Thread et asynchronicité

Technologie web 2

Johan Girod

Synchrone ou asynchrone

Synchrone ou asynchrone

Execution synchrone

  • Chaque tache est exécutées l'une après l'autre
  • Le déroulé est bloqué tant qu'une tache n'est pas terminée

Synchrone ou asynchrone

Execution asynchrone

  • Plusieurs tâches peuvent être lancées en même temps
  • Elles ne bloquent pas le déroulé, et d'autres événements peuvent être traités

Synchrone ou asynchrone

Asynchrone ou synchrone

Quelques exemples

  • Une conversation téléphonique ? synchrone
  • Une conversation par messagerie ? asynchrone
  • Un paiement à la caisse ? synchrone
  • Une préparation de repas ? asynchrone

Quelques exemples

Code synchrone ou asynchrone ?


			
						
					
				

Quelques exemples

Code synchrone ou asynchrone ?


			
						
					
				

Quelques exemples

Code synchrone ou asynchrone ?


			
						
					
				

Quelques exemples

Code synchrone ou asynchrone ?


			
						
					
				

Thread en JavaScript

Qu'est-ce qu'un thread ?

  • Un thread est un fil d'exécution
  • Un programme peut avoir plusieurs threads
  • Chaque thread ne peut exécuter qu'une instruction à la fois

Thread et JavaScript

  • Un programme JavaScript est executé dans un unique thread principal (« main thread »)
    Les instructions sont exécutées l'une après l'autre
  • Ce thread est non bloquant (asynchrone)
    Les instructions asynchrones ne bloquent pas le déroulé et sont exécutées en parallèle
  • Les fonctions fonction de callback sont placées dans la file d'attente (« event loop ») Elles sont exécutées dès que le thread principal est disponible, dans l'ordre d'arrivée

`setTimeout`


		
			

Le code est exécuté dans l'ordre A, D, C, B

`setTimeout`


		
			

Le code est exécuté dans l'ordre B, C, D, A

Perfomance

Si on effectue une tâche longue dans le thread principal, cela peut bloquer l'interface utilisateur, car les événements ne sont plus traités

Dans ce cas, il vaut mieux effectuer la tâche dans un autre thread : c'est le rôle des Web Workers

Callback hell

Lorsqu'on imbrique trop de fonctions de rappel, le code devient illisible


		
			

Pour cela, il existe une fonctionnalité du langage : les promesse et les fonctions async/await

...que vous verrez l'année prochaine

TP 5 : Balatro simplifié

Créer une version simplifiée du jeu de cartes Balatro en JavaScript, où le but est de former les meilleures combinaisons de poker possible.

Principes du jeu

  • Le joueur reçoit une main de 5 cartes piochées aléatoirement dans un deck de 52 cartes standard.
  • À chaque manche, le joueur dispose de 3 échanges pour améliorer sa main en remplaçant des cartes individuelles.
  • Une fois satisfait de sa main (ou après avoir utilisé tous ses échanges), le joueur valide sa main et un score est calculé en fonction de la combinaison de poker obtenue.

Calcul du score

Le score est calculé selon la formule : (jetons de base + jetons des cartes gagnantes) × multiplicateur

Chaque carte a une valeur en jetons :

  • As = 11 jetons
  • Têtes (Valet, Dame, Roi) = 10 jetons
  • Autres (2 à 10) = valeur faciale

Les combinaisons reconnues sont (du plus faible au plus fort) :

CombinaisonJetons de baseMultiplicateurCartes gagnantes
Carte Haute5×1la carte la plus haute
Paire10×2les 2 cartes de même valeur
Double Paire20×2les 4 cartes des 2 paires
Brelan30×3les 3 cartes de même valeur
Suite30×4les 5 cartes consécutives
Couleur35×4les 5 cartes de même couleur (♠, ♥, ♦, ♣)
Full40×4les 5 cartes (brelan + paire)
Carré60×7les 4 cartes de même valeur
Quinte Flush100×8les 5 cartes (suite + couleur)

Exemple : une paire de 7 donne (10 + 7 + 7) × 2 = 48 points.

Le score total s’accumule au fil des manches.

Installation

git clone https://sources.univ-jfc.fr/techno-web-2/tp8-balatro.git
cd tp8-balatro
npm install
npm run dev

Ce TP se rapproche d’un exercice en conditions réel. Vous arrivez sur un projet avec un certain nombre de fichiers / fonctions déjà définis. Le but est de savoir les réutiliser à bon escient. Prenez un petit temps pour examiner l’architecture des fichiers. Arrivez-vous à voir comment ils sont organisés ?

Ce TP utilise un bundler (vite) pour servir les fichiers de développement et faire de la compilation de contenu (comme du CSS).

A savoir : tous les projets javascript moderne utilisent un bundler, et vite est le plus moderne / populaire d’entre eux.

Partie 1 : le moteur de jeu

Nous allons créer un moteur de jeu qui simule les mécaniques de base de Balatro : tirage de cartes, formation de main de poker, et calcul de score.

La main sera représentée par un tableau de 5 cartes. Chaque carte est un objet avec une valeur (1 pour As, 13 pour Roi) et une couleur (♠, ♥, ♦, ♣).

Des tests sont disponibles, vous pouvez les lancer avec la commande npm test

1. constructor()

Complétez le constructeur en initialisant le deck

Corrigé

2. piocher(nombre)

Pioche nombre cartes du deck et les ajoute à la main (this.#hand). La méthode retourne true si le tirage a réussi, false s’il n’y a pas assez de cartes dans le deck.

Note Pour piocher une carte, il faut l’enlever de this.#deck et l’ajouter à this.#hand.

Corrigé

3. echanger(index)

Échange la carte à l’index donné (0-4) pour une nouvelle carte du deck. Le nombre d’échanges est limité à 3 par manche. Retourne true si l’échange a réussi, false sinon.

Corrigé

Partie 2 : le calcul du score (score.js)

Le calcul du score est séparé dans un module score.js. Ce module utilise la fonction calculMainGagnante du fichier fourni detecter-main/index.js et ajoute la logique de calcul du score.

Le fichier score.js contient deux fonctions à implémenter :

  1. valeurJetons(carte) : retourne la valeur en jetons d’une carte (As = 11, têtes = 10, autres = valeur faciale).
  2. calculerScore(main) : prend une main de 5 cartes et retourne un objet { score, multiplier, jetons, typeMain }.

valeurJetons(carte)

Corrigé

calculerScore(main)

Cette fonction doit :

  1. Appeler calculMainGagnante(main) pour obtenir le type de combinaison et les cartes gagnantes
  2. Calculer jetons de base et le multiplicateur
  3. Calculer les jetons totaux : jetons de base + somme des jetons des cartes gagnantes
  4. Calculer le score total (score = jetons totaux × multiplicateur)
  5. Retourner { score, multiplier, jetons, typeMain }
Corrigé

Implémenter jouerMain()

Maintenant que vous avez implémenté le calcul du score d’une main, vous pouvez l’utiliser dans le moteur de jeu. Dans le fichier balatro.js, implémentez la méthode jouerMain. Cette méthode est appelée lorsque le ou la joueuse clique sur « valider ».

La méthode :

  1. Calcule le score de la main via calculerScore() (du module score.js)
  2. Ajoute le score au total
  3. Vide la main, réinitialise les échanges, mélange et pioche une nouvelle main
Corrigé

Partie 3 : l’interface

Approche composant

Nous avons adopté une approche composant pour l’affichage. L’idée est de regrouper dans une fonction tout ce qui concerne l’affichage d’un élément : sa création DOM, son style, ses événements. L’approche composant est systématiquement utilisés dans les « vrais » projets, car elle permet d’organiser le code efficacement.

Un composant est une fonction qui prend des paramètre et retourne un noeud DOM, prêt à être inséré dans la page.

Dans ce TP, deux composants vous sont fournis : carte et score. Vous pouvez regarder le fichier main.js pour voir un exemple de leur utilisation.

Boucle de jeu

Maintenant que nous avons le moteur de jeu et les composants, assemblons le tout dans main.js. Le HTML est déjà fourni dans index.html.

L’interface doit :

  1. Afficher la main en utilisant le composant carte. Chaque carte a un onClick qui appelle jeu.echanger(index) puis rafraîchit l’affichage.
  2. Afficher la combinaison actuelle (sans le total) via le composant affichageScore.
  3. Au clic sur « Valider la main » :
    • Calculer le score et afficher le panneau avec le total (avecTotal: true)
    • Désactiver le bouton et les cartes
    • Après 2 secondes (setTimeout), appeler jeu.jouerMain(), incrémenter la manche, et rafraîchir l’interface
Corrigé

Partie 4 : Nouvelles règles !

1. Un Balatro plus grand

Dans le jeu Balatro, il y a deux mains :

  • Les cartes affichées face visible (main visible), au nombre de 8
  • Les cartes selectionnées pour être jouées (main jouée), au nombre de 5

Lors de l’échange, on selectionne le nombre de carte à changer (de 1 à la taille de la main visible, soit 8), puis on clique sur « échanger » pour confirmer l’échange.

Lorsqu’une main est jouée, elle est défaussé. Puis, on garde les cartes visibles non jouée dans la main, et on pioche pour compléter la main visible jusqu’à son nombre de base (8).

Modifier votre code pour implémenter ce nouveau comportement.

2. Conditions de fin

Le Balatro actuel n’a pas de conditions de fin, on peut continuer à jouer indéfiniment sans savoir si on a perdu ou gagné.

Nous allons modifier cela, en limitant le nombre d’échange possible sur plusieurs manches (plutôt que à chaque manche)

Par ailleurs nous allons ajouter un compteur de manches (compteur de main jouées).

Le nombre d’échange est limité à 4, et le nombre de main jouées à 3.

Quand le compteur d’échange est à zéro, on ne peut plus jouer de nouvelles cartes.

Quand le compteur de manche est à zéro, le jeu est terminé, on affiche le score.

3. Highscore

Faire en sorte de sauvegarder les highscore dans le localStorage, et de les afficher.