fuzzyworld

dao-game / commit

More refactoring and getting undo to work.

e946b6c8203542eb531e42c9ed1f7e0505be73fd · Pavel Safronov · 2022-03-01 01:06:22 -0800 · parents cc5cd8495d4c

More refactoring and getting undo to work.
 game.js | 169 ++++++++++++++++++++++++++++++++++++++++------------------------
 1 file changed, 107 insertions(+), 62 deletions(-)

diff --git a/game.js b/game.js
index 5f4b8ac..19547fc 100644
--- a/game.js
+++ b/game.js
@@ -6,7 +6,9 @@ class Coord {
      * @memberof Coord
      */
     constructor(row, col) {
+        /** @type {number} */
         this.row = row;
+        /** @type {number} */
         this.col = col;
     }
 
@@ -95,11 +97,25 @@ const gameBoard = {
     }
 }
 
+class Move {
+    /**
+     * Creates an instance of Move.
+     * @param {number} sourceIndex
+     * @param {number} destIndex
+     * @memberof Move
+     */
+    constructor(sourceIndex, destIndex) {
+        /** @type {number} */
+        this.sourceIndex = sourceIndex;
+        /** @type {number} */
+        this.destIndex = destIndex;
+    }
+}
 const player = {
     isRedTurn: false,
     /** @type {number | undefined} */
     selectedPieceId: undefined,
-    /** @type {Coord[]} */
+    /** @type {Move[]} */
     moveHistory: [],
 }
 
@@ -112,37 +128,7 @@ const bluePieces = () => Array.from(document.querySelectorAll('.blue-piece:not(#
 const redTurnText = () => Array.from(document.querySelectorAll(".red-turn-text"));
 const blueTurnText = () => Array.from(document.querySelectorAll(".blue-turn-text"));
 const resetButton = document.getElementById('reset-button');
-resetButton.addEventListener('click', () => {
-    if (confirm('Reset?')) {
-        window.location.reload();
-    }
-});
-
-// create board and pieces
-(() => {
-    let isWhite = true;
-    const table = document.getElementById('table');
-    for (let r = 0; r < constants.rows; r++) {
-        const tr = document.createElement('tr');
-        table.appendChild(tr);
-        for (let c = 0; c < constants.cols; c++) {
-            const td = document.createElement('td');
-            tr.appendChild(td);
-            td.className = isWhite ? 'white' : 'black';
-            isWhite = !isWhite;
-        }
-        isWhite = !isWhite;
-    }
-    gameBoard.board.forEach((value, index) => {
-        if (value !== null) {
-            const isRed = value < 4;
-            const defaultPiece = isRed ? defaultRedPiece : defaultBluePiece;
-            const newPiece = defaultPiece.cloneNode(true);
-            newPiece.id = value;
-            cells()[index].appendChild(newPiece);
-        }
-    });
-})();
+const undoButton = document.getElementById('undo-button');
 
 const listenerManager = {
     /** @type {{element: Element, type: string, listener: (event: Event) => void}[]} */
@@ -179,6 +165,44 @@ const resetBoard = () => {
     listenerManager.removeListeners();
 };
 
+/**
+ * @param {number} sourceIndex
+ * @param {number} destIndex
+ */
+const movePiece = async (sourceIndex, destIndex) => {
+    const pieceId = gameBoard.board[sourceIndex];
+    // find current piece
+    const piece = document.getElementById(pieceId);
+    // unselect piece
+    piece.classList.remove(constants.selectedPieceClassName);
+
+    // create clone for new position
+    const destPiece = piece.cloneNode(true);
+    // add to destination cell
+    cells()[destIndex].appendChild(destPiece);
+
+    // start animations
+    const destAnimation = cells()[destIndex].animate([{ opacity: 0 }, { opacity: 1 }], { duration: 1000, iterations: 1 })
+    const sourceAnimation = piece.animate([{
+        opacity: 1,
+        width: '45px',
+        height: '45px'
+    }, {
+        opacity: 0,
+        width: '5px',
+        height: '5px'
+    },], { duration: 1000, iterations: 1 });
+
+    // await animations
+    await Promise.all([sourceAnimation.finished, destAnimation.finished]);
+
+    // remove old piece
+    piece.parentNode.removeChild(piece);
+    gameBoard.board[sourceIndex] = null;
+    // set new piece
+    gameBoard.board[destIndex] = pieceId;
+};
+
 const nextMove = () => {
     const setActivePlayerPieces = () => {
         /** @param {Event} event */
@@ -260,36 +284,10 @@ const nextMove = () => {
         const makeMove = async (destIndex) => {
             listenerManager.removeListeners();
 
-            // find current piece
-            const piece = document.getElementById(player.selectedPieceId);
-            // unselect piece
-            piece.classList.remove(constants.selectedPieceClassName);
-
-            // create clone for new position
-            const destPiece = piece.cloneNode(true);
-            // add to destination cell
-            cells()[destIndex].appendChild(destPiece);
-
-            // start animations
-            const destAnimation = cells()[destIndex].animate([{ opacity: 0 }, { opacity: 1 }], { duration: 1000, iterations: 1 })
-            const sourceAnimation = piece.animate([{
-                opacity: 1,
-                width: '45px',
-                height: '45px'
-            }, {
-                opacity: 0,
-                width: '5px',
-                height: '5px'
-            },], { duration: 1000, iterations: 1 });
-
-            // await animations
-            await Promise.all([sourceAnimation.finished, destAnimation.finished]);
-
-            // remove old piece
-            piece.parentNode.removeChild(piece);
-            gameBoard.board[gameBoard.board.indexOf(player.selectedPieceId)] = null;
-            // set new piece
-            gameBoard.board[destIndex] = player.selectedPieceId;
+            const sourceIndex = gameBoard.board.indexOf(player.selectedPieceId);
+            player.moveHistory.push(new Move(sourceIndex, destIndex));
+
+            await movePiece(sourceIndex, destIndex);
 
             checkForWin();
             nextMove();
@@ -415,5 +413,52 @@ const checkForWin = () => {
     }
 };
 
+const init = () => {
+    // create board and pieces
+    let isWhite = true;
+    const table = document.getElementById('table');
+    for (let r = 0; r < constants.rows; r++) {
+        const tr = document.createElement('tr');
+        table.appendChild(tr);
+        for (let c = 0; c < constants.cols; c++) {
+            const td = document.createElement('td');
+            tr.appendChild(td);
+            td.className = isWhite ? 'white' : 'black';
+            isWhite = !isWhite;
+        }
+        isWhite = !isWhite;
+    }
+    gameBoard.board.forEach((value, index) => {
+        if (value !== null) {
+            const isRed = value < 4;
+            const defaultPiece = isRed ? defaultRedPiece : defaultBluePiece;
+            const newPiece = defaultPiece.cloneNode(true);
+            newPiece.id = value;
+            cells()[index].appendChild(newPiece);
+        }
+    });
+
+    // hook up buttons
+    resetButton.addEventListener('click', () => {
+        if (player.moveHistory.length === 0) {
+            return;
+        }
+
+        if (confirm('Reset?')) {
+            window.location.reload();
+        }
+    });
+    undoButton.addEventListener('click', () => {
+        if (player.moveHistory.length === 0) {
+            return;
+        }
+
+        const { destIndex, sourceIndex } = player.moveHistory.pop();
+        movePiece(destIndex, sourceIndex);
+        nextMove();
+    });
+};
+
 // start!
+init();
 nextMove();
\ No newline at end of file