Tic Tac Toe Parte 2: Deshacer / Rehacer sin estado

Tic Tac Toe Parte 0: Comparar Svelte y React
Tic Tac Toe Parte 1: Svelte y Canvas 2D
Tic Tac Toe Parte 2: Deshacer / Rehacer sin estado
Tic Tac Toe, parte 3: Deshacer / Rehacer con almacenamiento de comandos
Tic Tac Toe Parte 4: Interactuar con el backend de Flask usando HTTP

Continuaci贸n del art铆culo Tic Tac Toe, parte 1 , en el que comenzamos el desarrollo de este juego en Svelte . En esta parte terminaremos el juego hasta el final. Agregue equipos Deshacer / Rehacer , acceso aleatorio a cualquier paso del juego, alternar movimientos con el oponente, mostrar el estado del juego, determinar el ganador.


Comandos Deshacer / Rehacer

C贸digo REPL


En este punto, los comandos Deshacer / Rehacer se agregaron a la aplicaci贸n. Se agregaron m茅todos push y rehacer a la tienda de historial .


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

Los m茅todos push , redo , canUndo , canRedo se agregan a la clase 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++; } 

El m茅todo de historial se ha agregado al m茅todo push de eliminar todos los estados desde el actual hasta el 煤ltimo. Si ejecutamos el comando Deshacer varias veces y hacemos clic en el campo de juego, todos los estados a la derecha del actual hasta el 煤ltimo se eliminar谩n de la tienda y se agregar谩 un nuevo estado.


 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); } 

Los botones Deshacer y Rehacer se han agregado al componente Aplicaci贸n . Si la ejecuci贸n de comandos no es posible, se desactivan.


 <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> 

Cambio de rumbo

C贸digo REPL


Una apariencia alterna de una cruz o un dedo del pie despu茅s de hacer clic con el mouse.


El m茅todo clickCell () se ha eliminado de su repositorio de historial , todo el c贸digo del m茅todo se ha transferido al controlador handleClick () del componente 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); } 

Por lo tanto, se elimin贸 el error previamente cometido; el repositorio depend铆a de la l贸gica de este juego en particular. Ahora este error se ha solucionado y el repositorio se puede reutilizar en otros juegos y aplicaciones sin cambios.


Anteriormente, el estado de un paso del juego solo se describ铆a mediante una matriz de 9 valores. Ahora el estado del juego est谩 determinado por el objeto que contiene la matriz y la propiedad xIsNext. La inicializaci贸n de este objeto al comienzo del juego se ve as铆:


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

Y tambi茅n se puede observar que el almacenamiento del historial ahora puede percibir estados descritos de cualquier manera.


Acceso aleatorio al historial de movimientos

C贸digo REPL


En la tienda de historial , se agreg贸 el m茅todo setCurrent (actual) , con el que establecemos el estado actual seleccionado del juego.


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

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

En el componente de la aplicaci贸n , se agreg贸 la visualizaci贸n del historial de movimientos en forma de botones.


 <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> 

Determinaci贸n del ganador

C贸digo REPL


Se agreg贸 la funci贸n para determinar el ganador CalculateWinner () en un archivo helpers.js separado:


 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; } 

La derivada de estado se ha agregado para determinar el estado del juego, aqu铆 se determina el resultado del juego: ganador o sorteo:


 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 salida del estado del juego se ha agregado al componente de la aplicaci贸n :


 <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> 

En el componente Tablero , se han agregado limitaciones al controlador del controlador del clic del controlador () : es imposible hacer clic en la celda llena incluso despu茅s de que termine el juego.


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

El juego ha terminado! En el pr贸ximo art铆culo, consideraremos la implementaci贸n del mismo juego usando el patr贸n Command, es decir. con el almacenamiento de comandos Deshacer / Rehacer en lugar de almacenar estados individuales.


Repositorio de GitHub

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


Instalar el juego en la computadora local:


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

Lanzamos el juego en un navegador en la direcci贸n: http: // localhost: 5000 / .

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


All Articles