Fix commit and reset actions crossing pointers by creating an action stack (will be used to send data to backend)
parent
0a0ca65e3b
commit
7c4a6f5603
|
|
@ -105,4 +105,8 @@ export class Cell {
|
||||||
throw Error("No adjacent exit or piece available to connect to");
|
throw Error("No adjacent exit or piece available to connect to");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public removePiece() {
|
||||||
|
this.placedPiece = undefined;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
"@testing-library/react": "^13.4.0",
|
"@testing-library/react": "^13.4.0",
|
||||||
"@testing-library/user-event": "^13.5.0",
|
"@testing-library/user-event": "^13.5.0",
|
||||||
"@types/jest": "^27.5.2",
|
"@types/jest": "^27.5.2",
|
||||||
"@types/node": "^16.18.119",
|
"@types/node": "^17.0.29",
|
||||||
"@types/react": "^18.3.12",
|
"@types/react": "^18.3.12",
|
||||||
"@types/react-dom": "^18.3.1",
|
"@types/react-dom": "^18.3.1",
|
||||||
"interface": "file:../interface",
|
"interface": "file:../interface",
|
||||||
|
|
@ -4202,9 +4202,9 @@
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "16.18.119",
|
"version": "17.0.29",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.119.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.29.tgz",
|
||||||
"integrity": "sha512-ia7V9a2FnhUFfetng4/sRPBMTwHZUkPFY736rb1cg9AgG7MZdR97q7/nLR9om+sq5f1la9C857E0l/nrI0RiFQ==",
|
"integrity": "sha512-tx5jMmMFwx7wBwq/V7OohKDVb/JwJU5qCVkeLMh1//xycAJ/ESuw9aJ9SEtlCZDYi2pBfe4JkisSoAtbOsBNAA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@types/node-forge": {
|
"node_modules/@types/node-forge": {
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
"@testing-library/react": "^13.4.0",
|
"@testing-library/react": "^13.4.0",
|
||||||
"@testing-library/user-event": "^13.5.0",
|
"@testing-library/user-event": "^13.5.0",
|
||||||
"@types/jest": "^27.5.2",
|
"@types/jest": "^27.5.2",
|
||||||
"@types/node": "^16.18.119",
|
"@types/node": "^17.0.29",
|
||||||
"@types/react": "^18.3.12",
|
"@types/react": "^18.3.12",
|
||||||
"@types/react-dom": "^18.3.1",
|
"@types/react-dom": "^18.3.1",
|
||||||
"interface": "file:../interface",
|
"interface": "file:../interface",
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import DiceSet from "./components/DiceSet";
|
||||||
import "./GamePage.scss";
|
import "./GamePage.scss";
|
||||||
import { buildBoard, PieceId } from "interface";
|
import { buildBoard, PieceId } from "interface";
|
||||||
import { DieViewProps } from "./types/DieViewProps";
|
import { DieViewProps } from "./types/DieViewProps";
|
||||||
|
import { PlacePieceActionStack } from "./types/PlacePieceActionStack";
|
||||||
|
|
||||||
const GamePage = () => {
|
const GamePage = () => {
|
||||||
const getRandomPieceId = () => {
|
const getRandomPieceId = () => {
|
||||||
|
|
@ -71,15 +72,19 @@ const GamePage = () => {
|
||||||
return dice.concat(specialDice).find((die) => matcher(die));
|
return dice.concat(specialDice).find((die) => matcher(die));
|
||||||
};
|
};
|
||||||
|
|
||||||
const [storedBoard, setStoredBoard] = useState(buildBoard());
|
|
||||||
const [board, setBoard] = useState(buildBoard());
|
const [board, setBoard] = useState(buildBoard());
|
||||||
const [id, setId] = useState(1);
|
const [id, setId] = useState(1);
|
||||||
const refreshBoardRender = () => {
|
const refreshBoardRender = () => {
|
||||||
setBoard(board);
|
setBoard(board);
|
||||||
setId(id + 1);
|
setId(id + 1);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const [placePieceActionStack, setPlacePieceActionStack] = useState(
|
||||||
|
new PlacePieceActionStack(),
|
||||||
|
);
|
||||||
const resetBoard = () => {
|
const resetBoard = () => {
|
||||||
setBoard(storedBoard);
|
placePieceActionStack.resetActions(board);
|
||||||
|
setBoard(board);
|
||||||
modifyDieState(
|
modifyDieState(
|
||||||
() => true,
|
() => true,
|
||||||
() => {
|
() => {
|
||||||
|
|
@ -90,7 +95,7 @@ const GamePage = () => {
|
||||||
};
|
};
|
||||||
const commitBoard = () => {
|
const commitBoard = () => {
|
||||||
if (dice.some((die) => !die.isDisabled)) return;
|
if (dice.some((die) => !die.isDisabled)) return;
|
||||||
setStoredBoard(board);
|
placePieceActionStack.commitActions();
|
||||||
setDice(getRandomDiceSet());
|
setDice(getRandomDiceSet());
|
||||||
setSpecialDieUsedInRound(false);
|
setSpecialDieUsedInRound(false);
|
||||||
};
|
};
|
||||||
|
|
@ -105,6 +110,8 @@ const GamePage = () => {
|
||||||
setSpecialDieUsedInRound={setSpecialDieUsedInRound}
|
setSpecialDieUsedInRound={setSpecialDieUsedInRound}
|
||||||
refreshBoardRender={refreshBoardRender}
|
refreshBoardRender={refreshBoardRender}
|
||||||
board={board}
|
board={board}
|
||||||
|
placePieceActionStack={placePieceActionStack}
|
||||||
|
setPlacePieceActionStack={setPlacePieceActionStack}
|
||||||
/>
|
/>
|
||||||
<div className="right-panel">
|
<div className="right-panel">
|
||||||
<DiceSet
|
<DiceSet
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { Cell, CellType, directions, Exit } from "interface";
|
import { Cell, CellType, directions, Exit, PieceId } from "interface";
|
||||||
import "./BoardCell.scss";
|
import "./BoardCell.scss";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { DieViewProps } from "../types/DieViewProps";
|
import { DieViewProps } from "../types/DieViewProps";
|
||||||
|
|
@ -14,6 +14,7 @@ export interface BoardCellProps {
|
||||||
matcher: (die: DieViewProps) => boolean,
|
matcher: (die: DieViewProps) => boolean,
|
||||||
) => DieViewProps | undefined;
|
) => DieViewProps | undefined;
|
||||||
setSpecialDieUsedInRound: React.Dispatch<React.SetStateAction<boolean>>;
|
setSpecialDieUsedInRound: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
|
placePieceActionHandler: (pieceId: PieceId, rotation: number) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const BoardCell = (props: BoardCellProps) => {
|
const BoardCell = (props: BoardCellProps) => {
|
||||||
|
|
@ -23,16 +24,14 @@ const BoardCell = (props: BoardCellProps) => {
|
||||||
modifyDieState,
|
modifyDieState,
|
||||||
fetchDie,
|
fetchDie,
|
||||||
setSpecialDieUsedInRound,
|
setSpecialDieUsedInRound,
|
||||||
|
placePieceActionHandler,
|
||||||
} = props;
|
} = props;
|
||||||
const [pieceRotationAngle, setPieceRotationAngle] = useState(0);
|
const [pieceRotationAngle, setPieceRotationAngle] = useState(0);
|
||||||
const handleBoardCellClick = () => {
|
const handleBoardCellClick = () => {
|
||||||
const selectedDie = fetchDie((die) => die.isSelected);
|
const selectedDie = fetchDie((die) => die.isSelected);
|
||||||
if (!selectedDie) return;
|
if (!selectedDie) return;
|
||||||
try {
|
try {
|
||||||
cell.placePiece(
|
placePieceActionHandler(selectedDie.pieceId, selectedDie.rotation);
|
||||||
selectedDie.pieceId,
|
|
||||||
selectedDie.rotation as 0 | 90 | 180 | 270,
|
|
||||||
);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@ import { Cell } from "interface";
|
||||||
import "./GameBoard.scss";
|
import "./GameBoard.scss";
|
||||||
import BoardCell from "./BoardCell";
|
import BoardCell from "./BoardCell";
|
||||||
import { DieViewProps } from "../types/DieViewProps";
|
import { DieViewProps } from "../types/DieViewProps";
|
||||||
|
import { PlacePieceActionStack } from "../types/PlacePieceActionStack";
|
||||||
|
import { PlacePieceAction } from "../types/PlacePieceAction";
|
||||||
|
|
||||||
export interface GameBoardProps {
|
export interface GameBoardProps {
|
||||||
modifyDieState: (
|
modifyDieState: (
|
||||||
|
|
@ -14,15 +16,31 @@ export interface GameBoardProps {
|
||||||
setSpecialDieUsedInRound: React.Dispatch<React.SetStateAction<boolean>>;
|
setSpecialDieUsedInRound: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
refreshBoardRender: () => void;
|
refreshBoardRender: () => void;
|
||||||
board: Cell[][];
|
board: Cell[][];
|
||||||
|
placePieceActionStack: PlacePieceActionStack;
|
||||||
|
setPlacePieceActionStack: React.Dispatch<
|
||||||
|
React.SetStateAction<PlacePieceActionStack>
|
||||||
|
>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const GameBoard = (props: GameBoardProps) => {
|
const GameBoard = (props: GameBoardProps) => {
|
||||||
const { board } = props;
|
const { board, placePieceActionStack, setPlacePieceActionStack } = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="game-board">
|
<div className="game-board">
|
||||||
{board.flatMap((row) =>
|
{board.flatMap((row, rowIndex) =>
|
||||||
row.map((cell) => <BoardCell {...props} cell={cell} />),
|
row.map((cell, colIndex) => (
|
||||||
|
<BoardCell
|
||||||
|
{...props}
|
||||||
|
cell={cell}
|
||||||
|
placePieceActionHandler={(pieceId, rotation) => {
|
||||||
|
placePieceActionStack.executeAction(
|
||||||
|
new PlacePieceAction(pieceId, rotation, rowIndex, colIndex),
|
||||||
|
board,
|
||||||
|
);
|
||||||
|
setPlacePieceActionStack(placePieceActionStack);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)),
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
import { Cell, PieceId } from "interface";
|
||||||
|
|
||||||
|
export class PlacePieceAction {
|
||||||
|
pieceId: PieceId;
|
||||||
|
rotation: number;
|
||||||
|
cell: {
|
||||||
|
row: number;
|
||||||
|
col: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(pieceId: PieceId, rotation: number, row: number, col: number) {
|
||||||
|
this.pieceId = pieceId;
|
||||||
|
this.rotation = rotation;
|
||||||
|
this.cell = {
|
||||||
|
row: row,
|
||||||
|
col: col,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
do(board: Cell[][]) {
|
||||||
|
const cell = board[this.cell.row][this.cell.col];
|
||||||
|
cell.placePiece(this.pieceId, this.rotation as 0 | 90 | 180 | 270);
|
||||||
|
}
|
||||||
|
|
||||||
|
undo(board: Cell[][]) {
|
||||||
|
const cell = board[this.cell.row][this.cell.col];
|
||||||
|
if (!cell.placedPiece || cell.placedPiece.id !== this.pieceId) {
|
||||||
|
throw Error("Un-doing action error");
|
||||||
|
}
|
||||||
|
cell.removePiece();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
import { Cell } from "interface";
|
||||||
|
import { PlacePieceAction } from "./PlacePieceAction";
|
||||||
|
|
||||||
|
export class PlacePieceActionStack {
|
||||||
|
readonly committedActions: PlacePieceAction[] = [];
|
||||||
|
inProgressActions: PlacePieceAction[] = [];
|
||||||
|
|
||||||
|
executeAction(action: PlacePieceAction, board: Cell[][]) {
|
||||||
|
action.do(board);
|
||||||
|
this.inProgressActions.push(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
undoLastAction(board: Cell[][]) {
|
||||||
|
const lastAction = this.inProgressActions.pop();
|
||||||
|
if (!lastAction) {
|
||||||
|
throw Error("No in progress action to undo");
|
||||||
|
}
|
||||||
|
lastAction.undo(board);
|
||||||
|
}
|
||||||
|
|
||||||
|
resetActions(board: Cell[][]) {
|
||||||
|
try {
|
||||||
|
while (true) {
|
||||||
|
this.undoLastAction(board);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (
|
||||||
|
!(error instanceof Error) ||
|
||||||
|
error.message !== "No in progress action to undo"
|
||||||
|
) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
commitActions() {
|
||||||
|
this.committedActions.push(...this.inProgressActions);
|
||||||
|
this.inProgressActions = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue