Tic Tac Toe Partie 2: Annuler / Rétablir sans état

Tic Tac Toe Partie 0: Comparaison de Svelte et React
Tic Tac Toe Partie 1: Svelte et Canvas 2D
Tic Tac Toe Partie 2: Annuler / Rétablir sans état
Tic Tac Toe, partie 3: Annuler / Rétablir avec stockage des commandes
Tic Tac Toe Partie 4: Interaction avec le backend Flask en utilisant HTTP

Suite de l'article Tic Tac Toe, partie 1 , dans lequel nous avons commencé le développement de ce jeu sur Svelte . Dans cette partie, nous terminerons le jeu jusqu'à la fin. Ajoutez des équipes Annuler / Rétablir , un accès aléatoire à n'importe quelle étape du jeu, alterner les mouvements avec l'adversaire, afficher l'état du jeu, déterminer le gagnant.


Commandes Annuler / Rétablir

Code REPL


À ce stade, les commandes Annuler / Rétablir ont été ajoutées à l'application. Ajout de méthodes push et redo au magasin d' historique .


undo: () => update(h => { h.undo(); return h; }), redo: () => update(h => { h.redo(); return h; }), 

Les méthodes push , redo , canUndo , canRedo sont ajoutées à la classe History .


 canUndo() { return this.current > 0; } canRedo() { return this.current < this.history.length - 1; } undo() { if (this.canUndo()) this.current--; } redo() { if (this.canRedo()) this.current++; } 

La méthode d' historique a été ajoutée à la méthode push de suppression de tous les états du courant au dernier. Si nous exécutons la commande Annuler plusieurs fois et cliquons dans le champ de jeu, tous les états à droite du courant au dernier seront supprimés du magasin et un nouvel état sera ajouté.


 push(state) { // remove all redo states if (this.canRedo()) this.history.splice(this.current + 1); // add a new state this.current++; this.history.push(state); } 

Les boutons Annuler et Rétablir ont été ajoutés au composant App . Si l'exécution des commandes n'est pas possible, elles sont désactivées.


 <div> {#if $history.canUndo()} <button on:click={history.undo}>Undo</button> {:else} <button disabled>Undo</button> {/if} {#if $history.canRedo()} <button on:click={history.redo}>Redo</button> {:else} <button disabled>Redo</button> {/if} </div> 

Changement de cap

Code REPL


Apparition alternée d'une croix ou d'un orteil après un clic de souris.


La méthode clickCell () a été supprimée de leur référentiel d' historique , tout le code de la méthode a été transféré vers le gestionnaire handleClick () du composant Board .


 function handleClick(event) { let x = Math.trunc((event.offsetX + 0.5) / cellWidth); let y = Math.trunc((event.offsetY + 0.5) / cellHeight); let i = y * width + x; const state = $history.currentState(); const squares = state.squares.slice(); squares[i] = state.xIsNext ? 'X' : 'O'; let newState = { squares: squares, xIsNext: !state.xIsNext, }; history.push(newState); } 

Ainsi, l'erreur précédemment commise a été éliminée; le référentiel dépendait de la logique de ce jeu particulier. Maintenant, cette erreur a été corrigée et le référentiel peut être réutilisé dans d'autres jeux et applications sans modifications.


Auparavant, l'état d'une étape de jeu n'était décrit que par un tableau de 9 valeurs. Maintenant, l'état du jeu est déterminé par l'objet contenant le tableau et la propriété xIsNext. L'initialisation de cet objet au début du jeu ressemble à ceci:


 let state = { squares: Array(9).fill(''), xIsNext: true, }; 

Et on peut également noter que le stockage de l' historique peut désormais percevoir les états décrits de quelque manière que ce soit.


Accès aléatoire à l'historique des déplacements

Code REPL


Dans le magasin d' historique , la méthode setCurrent (current) a été ajoutée, avec laquelle nous définissons l'état actuel sélectionné du jeu.


 setCurrent(current) { if (current >= 0 && current < this.history.length) this.current = current; } 

 setCurrent: (current) => update(h => { h.setCurrent(current); return h; }), 

Dans le composant App , ajout de l'affichage de l'historique des déplacements sous forme de boutons.


 <ol> {#each $history.history as value, i} {#if i==0} <li><button on:click={() => history.setCurrent(i)}>Go to game start</button></li> {:else} <li><button on:click={() => history.setCurrent(i)}>Go to move #{i}</button></li> {/if} {/each} </ol> 

Déterminer le gagnant, afficher l'état du jeu

Code REPL


Ajout d'une fonction pour déterminer le gagnant CalculateWinner () dans un fichier helpers.js distinct:


 export function calculateWinner(squares) { const lines = [ [0, 1, 2], [3, 4, 5], [6, 7, 8], [0, 3, 6], [1, 4, 7], [2, 5, 8], [0, 4, 8], [2, 4, 6], ]; for (let i = 0; i < lines.length; i++) { const [a, b, c] = lines[i]; if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) { return squares[a]; } } return null; } 

Le dérivé de statut a été ajouté pour déterminer le statut du jeu, ici le résultat du jeu est déterminé: gagnant ou nul:


 export const status = derived( history, $history => { if ($history.currentState()) { if (calculateWinner($history.currentState().squares)) return 1; else if ($history.current == 9) return 2; } return 0; } ); 

La sortie de l'état du jeu a été ajoutée au composant App :


 <div class="status"> {#if $status === 1} <b>Winner: {!$history.currentState().xIsNext ? 'X' : 'O'}</b> {:else if $status === 2} <b>Draw</b> {:else} Next player: {$history.currentState().xIsNext ? 'X' : 'O'} {/if} </div> 

Dans le composant Board , des limitations ont été ajoutées au gestionnaire de poignées du clic handleClick () : il est impossible de cliquer dans la cellule remplie même après la fin du jeu.


 const state = $history.currentState(); if ($status == 1 || state.squares[i]) return; 

Le jeu est terminé! Dans le prochain article, nous considérerons l'implémentation du même jeu en utilisant le modèle Command, c'est-à-dire avec le stockage des commandes Annuler / Rétablir au lieu de stocker des états individuels.


Dépôt GitHub

https://github.com/nomhoi/tic-tac-toe-part2


Installation du jeu sur l'ordinateur local:


 git clone https://github.com/nomhoi/tic-tac-toe-part2.git cd tic-tac-toe-part2 npm install npm run dev 

Nous lançons le jeu dans un navigateur à l'adresse: http: // localhost: 5000 / .

Source: https://habr.com/ru/post/fr459630/


All Articles