diff --git a/src/components/NorthcottsGame.tsx b/src/components/NorthcottsGame.tsx index a9c859d..00e3616 100644 --- a/src/components/NorthcottsGame.tsx +++ b/src/components/NorthcottsGame.tsx @@ -16,6 +16,9 @@ interface GameState { rows: number; cols: number; misereMode: boolean; + cleanStart: boolean; + forwardOnly: boolean; + gameStarted: boolean; selectedPiece: { row: number; col: number } | null; } @@ -24,21 +27,38 @@ interface NorthcottsGameProps { } const NorthcottsGame: React.FC = ({ showSocialShare = false }) => { - const [gameState, setGameState] = useState(() => - initializeGame(3, 6, false) - ); + const [gameState, setGameState] = useState(() => ({ + board: Array(3).fill(null).map(() => Array(6).fill(null)), + currentPlayer: 'L', + gameOver: false, + winner: null, + rows: 3, + cols: 6, + misereMode: false, + cleanStart: false, + forwardOnly: true, + gameStarted: false, + selectedPiece: null + })); - function initializeGame(rows: number, cols: number, misereMode: boolean): GameState { + function initializeGame(rows: number, cols: number, misereMode: boolean, cleanStart: boolean, forwardOnly: boolean): GameState { const board: ('L' | 'R' | null)[][] = Array(rows).fill(null).map(() => Array(cols).fill(null)); - // Place L and R pieces in each row ensuring L is to the left of R - for (let row = 0; row < rows; row++) { - // Random positions ensuring L < R - const lPos = Math.floor(Math.random() * (cols - 1)); - const rPos = lPos + 1 + Math.floor(Math.random() * (cols - lPos - 1)); - - board[row][lPos] = 'L'; - board[row][rPos] = 'R'; + if (cleanStart) { + // Clean start: all L on left, all R on right + for (let row = 0; row < rows; row++) { + board[row][0] = 'L'; + board[row][cols - 1] = 'R'; + } + } else { + // Random positions ensuring L is to the left of R + for (let row = 0; row < rows; row++) { + const lPos = Math.floor(Math.random() * (cols - 1)); + const rPos = lPos + 1 + Math.floor(Math.random() * (cols - lPos - 1)); + + board[row][lPos] = 'L'; + board[row][rPos] = 'R'; + } } return { @@ -49,13 +69,16 @@ const NorthcottsGame: React.FC = ({ showSocialShare = false rows, cols, misereMode, + cleanStart, + forwardOnly, + gameStarted: true, selectedPiece: null }; } const getValidMoves = (row: number, col: number, player: 'L' | 'R'): number[] => { const moves: number[] = []; - const { board } = gameState; + const { board, forwardOnly } = gameState; if (player === 'L') { // L can move right (forward towards R) @@ -63,12 +86,28 @@ const NorthcottsGame: React.FC = ({ showSocialShare = false if (board[row][newCol] === 'R') break; // Can't jump over R if (board[row][newCol] === null) moves.push(newCol); } + + // L can also move left (backward) if forwardOnly is false + if (!forwardOnly) { + for (let newCol = col - 1; newCol >= 0; newCol--) { + if (board[row][newCol] === 'R') break; // Can't jump over R + if (board[row][newCol] === null) moves.push(newCol); + } + } } else { // R can move left (forward towards L) for (let newCol = col - 1; newCol >= 0; newCol--) { if (board[row][newCol] === 'L') break; // Can't jump over L if (board[row][newCol] === null) moves.push(newCol); } + + // R can also move right (backward) if forwardOnly is false + if (!forwardOnly) { + for (let newCol = col + 1; newCol < gameState.cols; newCol++) { + if (board[row][newCol] === 'L') break; // Can't jump over L + if (board[row][newCol] === null) moves.push(newCol); + } + } } return moves; @@ -89,7 +128,7 @@ const NorthcottsGame: React.FC = ({ showSocialShare = false }; const handleCellClick = (row: number, col: number) => { - if (gameState.gameOver) return; + if (gameState.gameOver || !gameState.gameStarted) return; const { board, currentPlayer, selectedPiece } = gameState; @@ -144,30 +183,61 @@ const NorthcottsGame: React.FC = ({ showSocialShare = false const getValidMovesForBoard = (board: ('L' | 'R' | null)[][], row: number, col: number, player: 'L' | 'R'): number[] => { const moves: number[] = []; + const { forwardOnly } = gameState; if (player === 'L') { + // L can move right (forward towards R) for (let newCol = col + 1; newCol < gameState.cols; newCol++) { if (board[row][newCol] === 'R') break; if (board[row][newCol] === null) moves.push(newCol); } + + // L can also move left (backward) if forwardOnly is false + if (!forwardOnly) { + for (let newCol = col - 1; newCol >= 0; newCol--) { + if (board[row][newCol] === 'R') break; + if (board[row][newCol] === null) moves.push(newCol); + } + } } else { + // R can move left (forward towards L) for (let newCol = col - 1; newCol >= 0; newCol--) { if (board[row][newCol] === 'L') break; if (board[row][newCol] === null) moves.push(newCol); } + + // R can also move right (backward) if forwardOnly is false + if (!forwardOnly) { + for (let newCol = col + 1; newCol < gameState.cols; newCol++) { + if (board[row][newCol] === 'L') break; + if (board[row][newCol] === null) moves.push(newCol); + } + } } return moves; }; const resetGame = () => { - setGameState(initializeGame(gameState.rows, gameState.cols, gameState.misereMode)); + setGameState(prev => ({ + ...prev, + board: Array(prev.rows).fill(null).map(() => Array(prev.cols).fill(null)), + currentPlayer: 'L', + gameOver: false, + winner: null, + gameStarted: false, + selectedPiece: null + })); + }; + + const startGame = () => { + setGameState(initializeGame(gameState.rows, gameState.cols, gameState.misereMode, gameState.cleanStart, gameState.forwardOnly)); }; const randomGame = () => { const rows = 3 + Math.floor(Math.random() * 8); // 3-10 const cols = 3 + Math.floor(Math.random() * 8); // 3-10 - setGameState(initializeGame(rows, cols, gameState.misereMode)); + setGameState(initializeGame(rows, cols, gameState.misereMode, false, gameState.forwardOnly)); }; const toggleMisereMode = () => { @@ -177,8 +247,31 @@ const NorthcottsGame: React.FC = ({ showSocialShare = false })); }; + const toggleCleanStart = () => { + setGameState(prev => ({ + ...prev, + cleanStart: !prev.cleanStart + })); + }; + + const toggleForwardOnly = () => { + setGameState(prev => ({ + ...prev, + forwardOnly: !prev.forwardOnly + })); + }; + const changeBoardSize = (rows: number, cols: number) => { - setGameState(initializeGame(rows, cols, gameState.misereMode)); + setGameState(prev => ({ + ...prev, + rows, + cols, + board: Array(rows).fill(null).map(() => Array(cols).fill(null)), + gameStarted: false, + gameOver: false, + winner: null, + selectedPiece: null + })); }; const getCellClass = (row: number, col: number) => { @@ -222,6 +315,8 @@ const NorthcottsGame: React.FC = ({ showSocialShare = false Northcott's Game + + {/* Mode Options Row */}
@@ -232,6 +327,27 @@ const NorthcottsGame: React.FC = ({ showSocialShare = false />
+
+ + +
+ +
+ + +
+
+ + {/* Board Size and Game Control Row */} +
-
@@ -311,11 +426,20 @@ const NorthcottsGame: React.FC = ({ showSocialShare = false -
+

Rules: Players alternate turns. L (blue) moves right, R (red) moves left.

Pieces cannot jump over the opponent and must stay in their row.

Click a piece to select it, then click a valid destination to move.

+ + {gameState.gameStarted && ( +
+ +
+ )}