Forums Développement Multimédia

Les formations Mediabox
Les formations Mediabox

Exercice pratique : le BLACKJACK

Compatible ActionScript 3. Cliquer pour en savoir plus sur les compatibilités.Par Monsieur Spi, le 14 janvier 2013

Bonjour,

Voici le premier exercice de la série “Casinos”, il s'agit du Blackjack.

Tout d'abord le résultat :

L"extension Adobe Flash Plugin est nécessaire pour afficher ce contenu.

Les sources sont disponibles en fin d'exercice.

Etude préliminaire

Tout d'abord le BLACKJACK c'est quoi ? (merci Wikipedia)

Le blackjack a fait son apparition à la fin du XVIIIe siècle sous le nom de « 21 ». Ce jeu est à la base inspiré de deux jeux de cartes : le chemin de fer et la ferme française1. Il est à l’époque joué dans les casinos parisiens. Il est ensuite introduit aux États-Unis sans grand succès. Pour essayer d’attirer les joueurs, les casinotiers inventent des bonus : Les joueurs ayant un As de pique et un Valet noir gagnent un bonus qui offre alors un paiement de 10 contre 1. Le nom du jeu provient donc du « Valet Noir » de ce bonus soit « Black Jack » en anglais. On appelle encore ce jeu ainsi même si ce bonus a été modifié.

La partie oppose tous les joueurs contre la banque. Le but est de battre le croupier sans dépasser 21. Dès qu'un joueur fait plus que 21, on dit qu'il « saute » ou qu'il « crève » et il perd sa mise initiale. La valeur des cartes est établie comme suit :

  • de 2 à 10 → valeur nominale de la carte
  • chaque figure, surnommée “bûche” → 10 points
  • l'As → 1 ou 11 (au choix)

Un Blackjack est composé d'un As et d'une « buche » (carte ayant pour valeur 10, donc 10, J, Q ou K). Cependant, si le joueur atteint le point 21 en 3 cartes ou plus on compte le point 21 et non pas Blackjack; de même lorsque le joueur sépare deux as et qu'il reçoit une buche pour l'un d'eux.

Au début de la partie le croupier distribue une carte face visible à chaque joueur et tire une carte face visible également pour lui. Il tire ensuite pour chacun une seconde carte face visible et tire une seconde carte face cachée pour lui au Blackjack américain. Au blackjack européen, le croupier tire sa seconde carte après le tour de jeu des joueurs.

Puis il demande au premier joueur de la table (joueur situé à sa gauche) l'option qu'il désire choisir. Si le joueur veut une carte, il doit l'annoncer en disant « Carte ! ». Le joueur peut demander autant de cartes qu'il le souhaite pour approcher la valeur sans la dépasser. Si après le tirage d'une carte, il a dépassé 21, il perd sa mise et le croupier passe au joueur suivant. S'il décide de s'arrêter, en disant « Je reste », le croupier passe également au joueur suivant.

Le croupier répète cette opération jusqu'à ce que tous les joueurs soient servis.

Ensuite, il joue pour lui selon une règle simple et codifiée : « La banque tire à 16, reste à 17 ». Ainsi, le croupier tire des cartes jusqu'à atteindre un nombre compris entre 17 et 21 que l'on appelle un point. S'il fait plus de 21, tous les joueurs restants gagnent mais s'il fait son point, seuls gagnent ceux ayant un point supérieur au sien (sans avoir sauté). Dans cette situation, le joueur remporte l'équivalent de sa mise. En cas d'égalité le joueur garde sa mise mais n'empoche rien en plus. À noter que le blackjack (une bûche et un as en deux cartes) est plus fort que 21 fait en ayant tiré plus de deux cartes: Si un joueur fait blackjack et que le banquier fait 21 en 3 cartes ou plus, le joueur fait blackjack et remporte une fois et demie sa mise. Le banquier lui gagne contre tous les joueurs ayant 20 ou moins. Réciproquement si la banque a un as et une figure, elle gagne contre tout joueur ayant 21 en ayant tiré plus de deux cartes. Dans ce cas, si un joueur fait également blackjack, il peut récupérer sa mise mais n'est pas payé, le jeu étant à égalité.

Nous allons nous arrêter là pour les règles de base qui vont nous intéresser pour l'exercice, c'est déjà bien assez long comme ça ;-) Je vous encourage quand même à aller lire la définition complète sur Wikipedia si vous voulez faire un vrai jeu de Blackjack qui reprend toutes les options et subtilité, je l'avoue, le jeu que je vous propose est très sobre. Outre le respect des règles, ce qui va nous intéresser principalement ici c'est la manière dont on va organiser les tours de jeux, et comme vous allez le voir, ce genre de méthode peut s'appliquer à tous les jeux dits “tour par tour”, j'en vois qui relèvent la tête dans le fond, oui oui, comme les jeux de rôles par exemple.

Les pré-requis

Je vous recommande fortement d'avoir au moins lu les exercices suivants : DEMINEUR, PONG, SNAKE, TAQUIN
Les exercices sont courts mais de nombreuses astuces y sont proposées, je ne les expliquerai pas à chaque nouvel exercice.

Pour ce programme vous devez connaître :

Si vous souhaitez plus de précisions sur ces points, je vous encourage à parcourir le Wiki de Mediabox où vous trouverez de nombreux tutoriaux détaillés.

Le code

Voyons d'abord tout le code d'un coup.

var argent:int;
var mise:int;
var temps:Timer;
var tour:Array = 	[];
var paquet:Array = 	[];
var joueur:Array = 	[];
var banque:Array = 	[];
var hud:Hud = 		new Hud();
var carteCachee:Cartes;
 
hud.miser.addEventListener(MouseEvent.CLICK, miser);
hud.donner.addEventListener(MouseEvent.CLICK, donner);
hud.tirer.addEventListener(MouseEvent.CLICK, tirer);
hud.passer.addEventListener(MouseEvent.CLICK, passer);
hud.rejouer.addEventListener(MouseEvent.CLICK, depart);
hud.miser.buttonMode = true;
hud.donner.buttonMode = true;
hud.tirer.buttonMode = true;
hud.passer.buttonMode = true;
hud.rejouer.buttonMode = true;
 
// interface
var panneaux:Panneaux = new Panneaux();
panneaux.addEventListener(MouseEvent.MOUSE_DOWN, init);
panneaux.buttonMode = true;
addChild(panneaux);
 
// démarrer une partie
function init(e:Event):void{
 
	while(numChildren>0) removeChildAt(0);
 
	hud.miser.visible = false;
	hud.donner.visible = false;
	hud.tirer.visible = false;
	hud.passer.visible = false;
	hud.rejouer.visible = false;
	addChild(hud);
 
	argent = 100;
	afficheInfos();
	depart();
}
 
// lancer une manche
function depart(e:MouseEvent = null):void{
	while(numChildren>1) removeChildAt(1);
	joueur = [];
	banque = [];
	paquet = [];
	tour = [];
	hud.joueur.text = "";
	hud.banque.text = "";
	hud.mise.text = "";
	hud.infos.text = "Misez et jouez.";
	mise = 0;
	miser();
	hud.miser.visible = true;
	hud.donner.visible = true;
	hud.rejouer.visible = false;
	for (var j:int = 1; j<53;j++) paquet.push(j);
	melange();
}
 
// melanger le paquet
function melange():void{
    var n:int = paquet.length;
    while (n > 1) {
    	var k:int = Math.random()*n;
        n--;
        var t:int = paquet[k];
        paquet[k] = paquet[n];
        paquet[n] = t;
    }
}
 
// Miser
function miser(e:MouseEvent=null):void{
	if (argent >= 5) {
		argent -= 5;
		mise +=  5;
	}
	afficheInfos();
}
 
// Premier tour de jeu
function donner(e:MouseEvent):void{
	afficheInfos();
	hud.miser.visible=false;
	hud.donner.visible=false;
	tour.push("donne banque");
	tour.push("donne joueur");
	tour.push("donne banque");
	tour.push("donne joueur");
	tour.push("stoppe donne");
	lanceTour();
}
 
// commencer le tour
function lanceTour():void{
	temps = new Timer(500);
	temps.addEventListener(TimerEvent.TIMER, actions);
	temps.start();
}
 
// arreter le tour
function stopTour():void{
	temps.stop();
	temps.removeEventListener(TimerEvent.TIMER, actions);
	temps = null;
}
 
// Gestion du tour
function actions(e:TimerEvent):void{
	var action:String = tour.shift();
	if (action == "donne banque")			donne("banque");
	if (action == "donne joueur")			donne("joueur");
	if (action == "montre carte") 			montreCarte();
	if (action == "banque joue")			banqueJoue();
	if (action == "stoppe donne" && !blackjack()) 	joueurJoue();
}
 
// Donner les cartes
function donne(qui:String):void{
	var carte:int = paquet.pop();
	var c:Cartes = new Cartes();
	c.gotoAndStop(carte+2);
	if (qui == "banque"){
		banque.push(carte)
		c.y = 100;
		c.x = 70 * (banque.length-1)+20;
		if (banque.length == 1)	{
			c.gotoAndStop(2);
			carteCachee = c;
		}
	} else if (qui == "joueur"){
		joueur.push(carte)
		c.y = 200;
		c.x = 70 * (joueur.length-1)+20;
	}
	addChild(c);
}
 
// Vérifie si le joueur sort un Blackjack
function blackjack():Boolean {
	if (joueur.length == 2 && score(joueur) == 21) {
		argent += mise*2.5;
		hud.infos.text = "Blackjack !";
		stopTour()
		afficheInfos();
		hud.rejouer.visible=true;
		return true;
	} else {
		return false;
	}
}
 
// calcule la valeur de la main
function score(main:Array):int{
	var total:int = 0;
	var ace:Boolean = false;
	for (var i:int=0; i<main.length; i++){
		var v:int = main[i]%13;
		if (v == 0) v = 13;
		if (v > 10) v = 10;
		total +=  v;
		if (v == 1) ace = true;
	}
	if (ace && total <= 11)	total +=  10;
	return total;
}
 
// A joueur de jouer
function joueurJoue(){
	hud.tirer.visible=true;
	hud.passer.visible=true;
	temps.stop();
}
 
// Tirer une nouvelle carte
function tirer(e:MouseEvent=null){
	donne("joueur");
	afficheInfos();
	if (score(joueur) >= 21) passer();
}
 
// A la banque de jouer
function passer(e:MouseEvent=null){
	hud.tirer.visible=false;
	hud.passer.visible=false;
	tour.push("montre carte");
	tour.push("banque joue");
	lanceTour();
}
 
// Affiche la carte masquée
function montreCarte(){
	carteCachee.gotoAndStop(banque[0]+2);
	afficheInfos();
}
 
// La banque joue
function banqueJoue(){
	if (score(banque) < 17)	{
		donne("banque");
		tour.push("banque joue");
	}else{
		gagne();
		stopTour();
		hud.rejouer.visible=true;
	}
	afficheInfos();
}
 
// Vérifie les résultats
function gagne():void{
	var P:int = score(joueur);
	var B:int = score(banque);
	if (P > 21){
		hud.infos.text = "Vous dépassez la limite !";
	}else if (B > 21){
		argent +=  mise * 2;
		hud.infos.text = "La Banque dépasse la limite !";
	}else if (B > P)	{
		hud.infos.text = "Vous perdez la manche !";
	}else if (B == P){
		argent +=  mise;
		hud.infos.text = "Egalité !";
	}else if (B < P)	{
		argent +=  mise * 2;
		hud.infos.text = "Vous gagnez la manche !";
	}
	if(argent==0) finPartie(2);
}
 
// Afficher les infos
function afficheInfos():void{
	hud.joueur.text = score(joueur).toString();
	hud.banque.text = score(banque).toString();
	hud.argent.text = "Argent : " + argent;
	hud.mise.text = "Mise : " + mise;
}
 
// fin de partie
function finPartie(G:int):void{
	addChild(panneaux);
	panneaux.gotoAndStop(G);
}

Etude du programme

Comme d'habitude j'utilise la bibliothèque de Flash pour gérer les graphismes, ce qui me permet d'alléger le code et de ne conserver que ce qui est utile pour l'exercice, si vous n'utilisez pas Flash voici les modifications à faire dans le programme :

Panneaux :

  • un clip qui regroupe tous les panneaux d'interface
  • un panneau différent par frame

Cartes :

  • un clip qui regroupe toutes les Cartes
  • une Carte sur chaque frame
  • chaque frame représente une couleur
  • deux premières frames représentent une carte vide et le dos des cartes

Hud :

  • contient toutes les informations pratiques du jeu
  • contient tous les boutons de l'interface

Bon, et bien cette fois on a du boulot, le code est assez long mais vous allez voir qu'il n'est pas bien compliqué.

Allez c'est parti pour l'étude pas à pas :

var argent:int;
var mise:int;
var temps:Timer;
var tour:Array = 	[];
var paquet:Array = 	[];
var joueur:Array = 	[];
var banque:Array = 	[];
var hud:Hud = 		new Hud();
var carteCachee:Cartes;
 
hud.miser.addEventListener(MouseEvent.CLICK, miser);
hud.donner.addEventListener(MouseEvent.CLICK, donner);
hud.tirer.addEventListener(MouseEvent.CLICK, tirer);
hud.passer.addEventListener(MouseEvent.CLICK, passer);
hud.rejouer.addEventListener(MouseEvent.CLICK, depart);
hud.miser.buttonMode = true;
hud.donner.buttonMode = true;
hud.tirer.buttonMode = true;
hud.passer.buttonMode = true;
hud.rejouer.buttonMode = true;
 
// interface
var panneaux:Panneaux = new Panneaux();
panneaux.addEventListener(MouseEvent.MOUSE_DOWN, init);
panneaux.buttonMode = true;
addChild(panneaux);

On se débarrasse tout de suite des déclarations globales.

argent = la somme de départ du joueur à chaque partie
mise = la mise du joueur à chaque manche
temps = la gestion du temps pour les tours de jeu
tour = le stockage des différents événements d'un tour de jeu
paquet = le paquet de cartes
joueur = les cartes dans la main du joueur
banque = les cartes dans la main de la banque
hud = l'affichage des informations
carteCachee = la carte de la banque qui est masquée en début de manche

Vous avez déjà beaucoup d'indices sur ce que nous allons faire dans ces simple déclarations, nous y reviendrons plus tard en détail, pour le moment on va se concentrer sur le “hud”. C'est la première fois que vous voyez ce terme dans mes exercices, si vous les avez fait dans l'ordre. Le “hud” dans un jeu est la zone d'affichage des informations, il existe dans pratiquement tous les jeux que ce soit pour compter le nombre de munitions, le score et tout ce qui peut donner des informations au joueur en cours de partie. Pour cette fois, je m'en suis servi également pour l'interface, c'est à dire que j'ai benné en vrac dedans tout ce que je n'ai pas envie de gérer et qui peut alourdir le code, c'est pourquoi il regroupe à la fois les infos comme le score et les résultats des manches, le fond du jeu et les boutons de l'interface. Vous avez donc, juste après les déclarations globales, l'interactivité des boutons contenus dans le hud, chacun lance une action précise que nous allons étudier. Et enfin pour terminer, comme d'habitude, le panneau d'interface global qui permet de lancer le jeu.

// démarrer une partie
function init(e:Event):void{
 
	while(numChildren>0) removeChildAt(0);
 
	hud.miser.visible = false;
	hud.donner.visible = false;
	hud.tirer.visible = false;
	hud.passer.visible = false;
	hud.rejouer.visible = false;
	addChild(hud);
 
	argent = 100;
	afficheInfos();
	depart();
}

Rien de bien compliqué si vous avez lu les pré-requis (et comme vous êtes consciencieux je suis sur que vous l'avez fait, sinon tant pis pour vous ;-) ). Lorsqu'on lance une nouvelle partie, on supprime tout ce qui est affiché, on rend tous les boutons du hud invisible, et on fixe l'argent de départ à 100. Puis on met à jour les champs textes visibles du hud, cette fonction étant appelée souvent on va s'en débarrasser tout de suite avant de voir ce qu'il se passe lorsqu'on lance une manche (départ) .

// Afficher les infos
function afficheInfos():void{
	hud.joueur.text = score(joueur).toString();
	hud.banque.text = score(banque).toString();
	hud.argent.text = "Argent : " + argent;
	hud.mise.text = "Mise : " + mise;
}

C'est tout bête, chaque champ texte prend une valeur, notez simplement que le score du joueur et de la banque sont calculés grâce à une petite fonction sur laquelle nous reviendront en temps voulu.

Ok, la partie est commencée, voyons ce qu'il se passe quand on lance une manche, d'ailleurs faites bien la différence entre “partie” et “manche”, une partie comporte autant de manches que le joueur à d'argent pour miser, lorsque le joueur n'a plus d'argent on relance une partie et il repart avec une somme de départ.

// lancer une manche
function depart(e:MouseEvent = null):void{
	while(numChildren>1) removeChildAt(1);
	joueur = [];
	banque = [];
	paquet = [];
	tour = [];
	hud.joueur.text = "";
	hud.banque.text = "";
	hud.mise.text = "";
	hud.infos.text = "Misez et jouez.";
	mise = 0;
	miser();
	hud.miser.visible = true;
	hud.donner.visible = true;
	hud.rejouer.visible = false;
	for (var j:int = 1; j<53;j++) paquet.push(j);
	melange();
}

A chaque manche, on supprime tout ce qui est affiché, au cas où il resterait des choses de la manche précédente, sauf le hud qui est le premier objet à avoir été affiché donc le premier présent dans la liste d'affichage, lui inutile de le supprimer puisqu'on va le réafficher tout de suite, il suffit de mettre ses informations à jour, nous venons de voir comment.

A chaque début de manche on vide également tout ce qui sert en cours de partie, la main de la banque, du joueur, le paquet de cartes, les événements des tours, la mise, etc…. On affiche également les deux premiers boutons qui vont être utile au joueur, miser ou donner les cartes.

On va également recréer le paquet, pour cela une simple boucle suffit, mais attention c'est là que l'exercice diffère un peu de la réalité, puisqu'en recréant un paquet à chaque manche le joueur ne peut pas compter les cartes (pour en savoir plus à ce sujet lisez l'article de wikipedia). Et enfin on mélange le paquet.

// melanger le paquet
function melange():void{
    var n:int = paquet.length;
    while (n > 1) {
        var k:int = Math.random()*n;
        n--; 
        var t:int = paquet[k];
        paquet[k] = paquet[n];
        paquet[n] = t;
    }
}

J'ai collé le mélange du tableau dans une fonction à part car je pense que c'est quelque chose que vous allez souvent réutiliser, le principe est simple, on boucle sur l'ensemble du paquet et on intervertit les index aléatoirement tant qu'on a pas atteint le nombre de cartes du paquet, et hop on a un tableau mélangé aléatoirement.

Bien, tout est prêt on va pouvoir commencer à jouer, et c'est le joueur qui commence, il a deux options représentées par les deux boutons que nous venons d'afficher, soit il mise, soit il demande à la banque de donner les cartes. Voyons ce qu'il se passe si il mise.

// Miser
function miser(e:MouseEvent=null):void{
	if (argent >= 5) {
		argent -= 5;
		mise +=  5;
	}
	afficheInfos();
}

Tant que le joueur à de l'argent il peut ajouter 5 à la mise à chaque fois qu'il clique sur le bouton correspondant, on s'en resservira par la suite je met donc ça dans une petite fonction à part, et j'affiche les infos dans le hud pour mettre à jour la mise et la somme d'argent restante.

Une fois que le joueur à fini de miser il demande à la banque de donner les cartes, et c'est parti pour le premier tour de jeu …

// Premier tour de jeu
function donner(e:MouseEvent):void{
	afficheInfos();
	hud.miser.visible=false;
	hud.donner.visible=false;
	tour.push("donne banque");
	tour.push("donne joueur");
	tour.push("donne banque");
	tour.push("donne joueur");
	tour.push("stoppe donne");
	lanceTour();
}

A chaque nouvelle donne, on met à jour les informations du hud, et on fait disparaître les boutons d'interface qui ne servent plus à rien, cela empêche le joueur de faire une bêtise en cliquant sur le mauvais bouton à un moment où il ne faut pas. Puis on va remplir le tableau qui stocke les événements du tour, faites bien attention, l'astuce pour créer des jeux en tour par tour est ici, ne la ratez pas.

J'ajoute, dans l'ordre, les différents événements qui composent le tour de jeu (ici le premier tour), on donne une carte à la banque, on donne une carte au joueur, on donne une autre carte à la banque et une autre au joueur, puis on stoppe la donne. Vous remarquerez que chaque étape d'un tour est une chaîne de caractère, j'aurais très bien pu utiliser des entier ou des ours en peluche de couleurs différentes si j'avais voulu, mais ça me semble plus clair avec des phrases, on obtient donc le tableau suivant :

tour = [“donne banque”, “donne joueur”, “donne banque”, “donne joueur”, “stoppe donne”]

Remarquez qu'ici j'aurais très bien pu écrire tout sur une seule ligne comme je viens de le faire puisqu'il s'agit du premier tour et que donc mon tableau est encore vide, mais par la suite on va pousser nos événements au fur et à mesure et en fonction de ce qu'il doit se passer, alors autant vous le montrer clairement dès le départ.

Une fois la séquence d'événement prête je lance le tour.

// commencer le tour
function lanceTour():void{
	temps = new Timer(500);
	temps.addEventListener(TimerEvent.TIMER, actions);
	temps.start();
}

Chaque événement est cadencé, ceci plus pour que ce soit agréable pour le joueur qui à l'impression qu'il y a une latence logique dans la donne des carte et le déroulement des événements. Chaque action va donc se produire à l'aide d'un Timer à une demi seconde d'intervalle.

Pour arrêter le tour c'est très simple, on stoppe le timer.

// arreter le tour
function stopTour():void{
	temps.stop();
	temps.removeEventListener(TimerEvent.TIMER, actions);
	temps = null;
}

Voyons à présent comment on gère le tour.

// Gestion du tour
function actions(e:TimerEvent):void{
	var action:String = tour.shift();
	if (action == "donne banque")					donne("banque");
	if (action == "donne joueur")					donne("joueur");
	if (action == "montre carte") 					montreCarte();
	if (action == "banque joue")					banqueJoue();
	if (action == "stoppe donne" && !blackjack()) 			joueurJoue();
}

A chaque passage du Timer, la variable “action” prend la valeur du premier index du tableau, que l'on retire immédiatement du tableau, ceci se fait facilement avec la méthode “shift()” dont c'est la fonction (voir aide de Flash à propos des tableaux si besoin).

Ensuite je regarde à quoi correspond l'action qui doit se dérouler et je lance la fonction correspondante, c'est aussi simple que ça, mais fort utile pour gérer une séquence d'événements.

Bon, c'est bien joli tout ça mais maintenant il faut voir ce qu'il se passe à chaque phase d'un tour, on commence par voir du côté de la donne des cartes.

// Donner les cartes
function donne(qui:String):void{
	var carte:int = paquet.pop();
	var c:Cartes = new Cartes();
	c.gotoAndStop(carte+2);
	if (qui == "banque"){
		banque.push(carte)
		c.y = 100;
		c.x = 70 * (banque.length-1)+20;
		if (banque.length == 1)	{
			c.gotoAndStop(2);
			carteCachee = c;
		}
	} else if (qui == "joueur"){
		joueur.push(carte)
		c.y = 200;
		c.x = 70 * (joueur.length-1)+20;
	}
	addChild(c);
}

On commence par récupérer la valeur de la carte, pour cela on récupère la dernière carte du paquet, que l'on retire aussitôt du paquet, cela se fait d'un coup avec la méthode “pop()” (voir aide de Flash à propos des tableaux si besoin). Puis on crée tout de suite une nouvelle carte qui servira à l'affichage, on affiche la frame correspondant à la valeur qu'on vient de trouver à laquelle on ajoute 2 (les deux première frames étant utilisées pour autre chose). Puis on regarde à qui on donne la carte, si c'est la banque on ajoute la carte à sa main, on place la carte à la bonne position sur les deux axes, et on vérifie si il s'agit de la première carte de la main de la banque, auquel cas on affiche le dos de la carte. Si on donne la carte au joueur, même topo, sauf qu'on ne masque aucune carte, et enfin on affiche la carte.

Faites un petit retour en arrière, selon le planning des événements que nous avons établis lors du premier tour de jeu, une fois qu'on à donné deux carte à la banque et au joueur on stoppe le tour de jeu, ceci se passe en deux temps. Selon les règles, à ce stade le joueur peut avoir tiré un blackjack, on va donc devoir le vérifier car si c'est le cas il gagne la partie, sinon c'est au joueur de jouer.

// Vérifie si le joueur sort un Blackjack
function blackjack():Boolean {
	if (joueur.length == 2 && score(joueur) == 21) {
		argent += mise*2.5;
		hud.infos.text = "Blackjack !";
		stopTour()
		afficheInfos();
		hud.rejouer.visible=true;
		return true;
	} else {
		return false;
	}
}

La condition pour que le joueur sorte un blackjack est la suivante : il n'a que deux cartes et le total fait 21 points. Si c'est le cas, il gagne 2.5 fois sa mise, on met à jour le hud, on stoppe le tour et on affiche le bouton qui permet de relancer une manche. C'est pas plus compliqué que ça ;-)

Vous noterez qu'une fois de plus on vérifie le score à l'aide d'une fonction, débarrassons-nous en tout de suite comme ça ce sera fait.

// calcule la valeur de la main
function score(main:Array):int{
	var total:int = 0;
	var ace:Boolean = false;
	for (var i:int=0; i<main.length; i++){
		var v:int = main[i]%13;
		if (v == 0) v = 13;
		if (v > 10) v = 10;
		total +=  v;
		if (v == 1) ace = true;
	}
	if (ace && total <= 11)	total +=  10;
	return total;
}

On regarde la valeur de toutes le cartes contenues dans la main du joueur testé (la banque ou le joueur), la valeur de la carte est obtenue à l'aide du modulo, si le reste de la division est égal à zéro c'est que c'est la dernière carte, je corrige donc la valeur tout de suite. Ici il vous faut une petite précision, lorsque je remplis mon paquet je le fais sans distinction de classe de carte, j'ai juste une suite de nombre de 1 à 52, or il y a 13 cartes dans chaque couleur (pic, cœur, carreau, trèfle). La référence des cartes contenues dans les mains est donc un nombre qui ne correspond pas à la valeur de la carte pour chaque couleur. Pour trouver la bonne valeur j'utilise tout simplement le modulo, ainsi j'obtiens la valeur de chaque carte sur une base de 52, les couleurs n'ayant pas réellement d'importance dans ce jeu.

Toutes les cartes au dessus de 9 ont une valeur de 10, on fixe donc leur valeur tout de suite, et enfin on prend en compte les AS. Alors non ce n'est pas une faute d'orthographe, j'utilise “ace” (signifiant “as” en anglais) plutôt que “as” qui est une méthode intégrée à Flash, ceci pour éviter les confusions. Donc, si le joueur a en main un ou plusieurs as il faut le prendre en compte, car un as peut avoir comme valeur 1 ou 10 au choix. Si le total des cartes en main est inférieur à 11 et que le joueur concerné a au moins un as dans son jeu, l'as prend une valeur de 10, l'objectif étant de se rapprocher le plus de 21 sans jamais dépasser cette valeur.

Allez, revenons à nous moutons, si le joueur n'a pas fait de blackjack c'est à lui de jouer.

// A joueur de jouer
function joueurJoue(){
	hud.tirer.visible=true;
	hud.passer.visible=true;
	temps.stop();
}

Il peut soit demander une nouvelle carte à la banque (autant de fois qu'il veut), soit passer et laisser donc la banque tirer ses cartes pour tenter d'atteindre 21. Au passage on stoppe la gestion du tour.

Si le joueur choisi de tirer une nouvelle carte :

// Tirer une nouvelle carte
function tirer(e:MouseEvent=null){
	donne("joueur");
	afficheInfos();
	if (score(joueur) >= 21) passer();
}

Pas de gestion des événement pour cette fois, on donne tout de suite une nouvelle carte au joueur, on met à jour le hud et on regarde si le score du joueur atteint ou dépasse 21, auquel cas soit il a gagné soit il a perdu, mais ça ne sert plus à rien qu'il joue, il passe donc son tour et laisse la banque jouer.

// A la banque de jouer
function passer(e:MouseEvent=null){
	hud.tirer.visible=false;
	hud.passer.visible=false;
	tour.push("montre carte");
	tour.push("banque joue");
	lanceTour();
}

Lorsque le joueur passe son tour, soit par choix, soit par obligation, c'est à la banque de jouer. On retire donc les boutons qui sont devenus inutiles, et on ajoute de nouveaux événements à notre planning, la banque montre sa carte cachée et joue son tour. Alors c'est vrai qu'ici ça ne sert à rien de gérer le retournement de la carte cachée de la banque via un gestionnaire d'événement, à par pour cadencer l'événement et que ce soit plus agréable pour le joueur, vu que la banque ne retournera qu'une seule fois sa carte, mais j'ai envie de jouer avec ce planning ;-)

// Affiche la carte masquée
function montreCarte(){
	carteCachee.gotoAndStop(banque[0]+2);
	afficheInfos();
}

La banque dévoile donc sa carte, la première de sa main, on met à jour le hud avec cette nouvelle donnée, et enfin la banque joue.

// La banque joue
function banqueJoue(){
	if (score(banque) < 17)	{
		donne("banque");
		tour.push("banque joue");
	}else{
		gagne();
		stopTour();
		hud.rejouer.visible=true;
	}
	afficheInfos();
}

Selon les règles du jeu, la banque tire des carte tant que son score est inférieur à 17, sinon on vérifie qui gagne la partie, on stoppe la gestion du tour et on propose au joueur de rejouer.

Allez, c'est la dernière ligne droite, voyons qui va gagner cette partie.

// Vérifie les résultats
function gagne():void{
	var P:int = score(joueur);
	var B:int = score(banque);
	if (P > 21){
		hud.infos.text = "Vous dépassez la limite !";
	}else if (B > 21){
		argent +=  mise * 2;
		hud.infos.text = "La Banque dépasse la limite !";
	}else if (B > P)	{
		hud.infos.text = "Vous perdez la manche !";
	}else if (B == P){
		argent +=  mise;
		hud.infos.text = "Egalité !";
	}else if (B < P)	{
		argent +=  mise * 2;
		hud.infos.text = "Vous gagnez la manche !";
	}
	if(argent==0) finPartie(2);
}

Avez-vous vraiment besoin d'explications à ce stade ? Vous qui avez survécu à tout le reste, cette petite fonction devrait vous paraître bien fade, on compare les score et on vérifie qui gagne la manche. Attention, là aussi on s'éloigne un peu de la réalité du Blackjack tel qu'il est joué en casino, normalement il y aurait plus d'options à prendre en compte, mais pour cet exercice ça ira bien comme ça.

// fin de partie
function finPartie(G:int):void{
	addChild(panneaux);
	panneaux.gotoAndStop(G);
}

C'est la fin de la partie lorsque le joueur n'a plus de sous, on affiche le panneau de fin.

Conclusion

Le blackjack est un des jeux de carte de casino les plus simples à mettre en oeuvre, mais il nous a permis d'aborder une notion au combien importante dans beaucoup de jeux, la planification d'événements. Avec ce procédé vous allez pouvoir gérer pas mal de choses dites “en tour par tour”, et ce n'est pas une mince affaire quand on ne sait pas par quel bout prendre la chose.

Les sources

blackjack_mb.fla version CS6
blackjack_mb_cs5.fla version CS5