我想要一个显示历史记录按钮,在您重新启动游戏时显示最新的比赛,以及一个撤消按钮来撤消 1 个步骤。但显示不正确,撤消按钮总是显示“没有可撤消的动作”
我尝试检查控制台日志,但我不知道错误在哪里
这是我的js代码:
document.addEventListener('DOMContentLoaded', function () {
const dimensionButton = document.getElementById('dimension-button');
const dimensionElement = document.getElementById('dimension');
const statusElement = document.getElementById('status');
const restartButton = document.getElementById('restart-btn');
const singlePlayerToggle = document.getElementById('single-player-toggle');
const boardElement = document.getElementById('board');
const historyBtn = document.getElementById('history-button');
const undoButton = document.getElementById('undo-btn');
let dimension = 10;
dimensionButton.textContent = `${dimension}x${dimension}`;
let singlePlayerMode = false;
let squares = Array(dimension).fill(Array(dimension).fill(null));
let xIsNext = true;
let theWinner = null;
let winningLine = [];
const dimensions = [10, 12, 16, 20];
let dimensionIndex = 0;
dimensionButton.addEventListener('click', function () {
stopTimer();
dimensionIndex = (dimensionIndex + 1) % dimensions.length;
dimension = dimensions[dimensionIndex];
dimensionButton.textContent = `${dimension}x${dimension}`;
restartGame();
});
//restartButton.addEventListener('click', restartGame);
restartButton.addEventListener('click', function () {
stopTimer(); // Stop the timer before restarting the game
restartGame();
});
undoButton.addEventListener('click', function () {
undoLastMove();
});
historyBtn.addEventListener('click', function () {
console.log("History button clicked");
stopTimer();
displayLatestMatchMoves();
});
singlePlayerToggle.addEventListener('click', function () {
stopTimer();
toggleSinglePlayerMode();
restartGame();
if (singlePlayerMode && !xIsNext) {
makeComputerMove();
}
});
//UPDATE TIMING (10s each turn)
const timingElement = document.querySelector('.timing');
let timer;
function startTimer() {
resetTimer();
let timeLeft = 10;
timingElement.textContent = `Time left: ${timeLeft}s`;
timer = setInterval(() => {
if (theWinner) {
clearInterval(timer);
timingElement.textContent = '';
return;
}
//if (!xIsNext) return;
timeLeft--;
timingElement.textContent = `Time left: ${timeLeft}s`;
if (timeLeft === 0) {
clearInterval(timer);
if (singlePlayerMode && !xIsNext) {
makeComputerMove();
} else {
xIsNext = !xIsNext;
updateStatus();
}
//updateStatus();
timingElement.textContent = '';
}
}, 1000);
}
function resetTimer() {
clearInterval(timer);
timingElement.textContent = '';
}
function stopTimer(forceStop = false) {
if (forceStop) {
clearInterval(timer); // Clear the interval to stop the timer
timingElement.textContent = ''; // Clear the timing display
} else {
// Do nothing if forceStop is false
}
}
function handleClick(row, col) {
//update
console.log("Clicked on row:", row, "column:", col);
resetTimer();
if (theWinner || squares[row][col]) return;
const newSquares = squares.map((row) => [...row]);
newSquares[row][col] = xIsNext ? 'X' : 'O';
squares = newSquares;
//squares = newSquares;
xIsNext = !xIsNext;
const winner = calculateWinner(newSquares, row, col);
if (winner) {
theWinner = winner;
winningLine = findWinningLine(newSquares, row, col, winner);
}
//update match moves
currentMatchMoves.push([row, col]);
console.log("Current match moves:", currentMatchMoves);
renderBoard();
updateStatus();
//update
startTimer();
if (singlePlayerMode && !theWinner && !xIsNext) {
makeComputerMove();
} else {
startTimer();
}
if (theWinner) {
recordMatchHistory();
}
recordMatchHistory();
console.log("Move recorded:", row, col);
}
function calculateWinner(currentSquares, row, col) {
const currentPlayer = currentSquares[row][col];
// Check horizontally
let count = 1;
let leftCol = col - 1;
while (leftCol >= 0 && currentSquares[row][leftCol] === currentPlayer) {
count++;
leftCol--;
}
let rightCol = col + 1;
while (rightCol < dimension && currentSquares[row][rightCol] === currentPlayer) {
count++;
rightCol++;
}
if (count >= 5) {
return currentPlayer;
}
// Check vertically
count = 1;
let topRow = row - 1;
while (topRow >= 0 && currentSquares[topRow][col] === currentPlayer) {
count++;
topRow--;
}
let bottomRow = row + 1;
while (bottomRow < dimension && currentSquares[bottomRow][col] === currentPlayer) {
count++;
bottomRow++;
}
if (count >= 5) {
return currentPlayer;
}
// Check diagonally (top-left to bottom-right)
count = 1;
let topLeftRow = row - 1;
let topLeftCol = col - 1;
while (topLeftRow >= 0 && topLeftCol >= 0 && currentSquares[topLeftRow][topLeftCol] === currentPlayer) {
count++;
topLeftRow--;
topLeftCol--;
}
let bottomRightRow = row + 1;
let bottomRightCol = col + 1;
while (bottomRightRow < dimension && bottomRightCol < dimension && currentSquares[bottomRightRow][bottomRightCol] === currentPlayer) {
count++;
bottomRightRow++;
bottomRightCol++;
}
if (count >= 5) {
return currentPlayer;
}
// Check diagonally (top-right to bottom-left)
count = 1;
let topRightRow = row - 1;
let topRightCol = col + 1;
while (topRightRow >= 0 && topRightCol < dimension && currentSquares[topRightRow][topRightCol] === currentPlayer) {
count++;
topRightRow--;
topRightCol++;
}
let bottomLeftRow = row + 1;
let bottomLeftCol = col - 1;
while (bottomLeftRow < dimension && bottomLeftCol >= 0 && currentSquares[bottomLeftRow][bottomLeftCol] === currentPlayer) {
count++;
bottomLeftRow++;
bottomLeftCol--;
}
if (count >= 5) {
return currentPlayer;
}
return null;
}
function findWinningLine(currentSquares, row, col, winner) {
const currentPlayer = currentSquares[row][col];
const lines = [];
// Check horizontally
let leftCol = col - 1;
while (leftCol >= 0 && currentSquares[row][leftCol] === currentPlayer) {
lines.push([row, leftCol]);
leftCol--;
}
lines.push([row, col]);
let rightCol = col + 1;
while (rightCol < dimension && currentSquares[row][rightCol] === currentPlayer) {
lines.push([row, rightCol]);
rightCol++;
}
if (lines.length >= 5) {
return lines;
}
// Check vertically
let topRow = row - 1;
while (topRow >= 0 && currentSquares[topRow][col] === currentPlayer) {
lines.push([topRow, col]);
topRow--;
}
lines.push([row, col]);
let bottomRow = row + 1;
while (bottomRow < dimension && currentSquares[bottomRow][col] === currentPlayer) {
lines.push([bottomRow, col]);
bottomRow++;
}
if (lines.length >= 5) {
return lines;
}
// Check diagonally (top-left to bottom-right)
let topLeftRow = row - 1;
let topLeftCol = col - 1;
while (topLeftRow >= 0 && topLeftCol >= 0 && currentSquares[topLeftRow][topLeftCol] === currentPlayer) {
lines.push([topLeftRow, topLeftCol]);
topLeftRow--;
topLeftCol--;
}
lines.push([row, col]);
let bottomRightRow = row + 1;
let bottomRightCol = col + 1;
while (bottomRightRow < dimension && bottomRightCol < dimension && currentSquares[bottomRightRow][bottomRightCol] === currentPlayer) {
lines.push([bottomRightRow, bottomRightCol]);
bottomRightRow++;
bottomRightCol++;
}
if (lines.length >= 5) {
return lines;
}
// Check diagonally (top-right to bottom-left)
let topRightRow = row - 1;
let topRightCol = col + 1;
while (topRightRow >= 0 && topRightCol < dimension && currentSquares[topRightRow][topRightCol] === currentPlayer) {
lines.push([topRightRow, topRightCol]);
topRightRow--;
topRightCol++;
}
lines.push([row, col]);
let bottomLeftRow = row + 1;
let bottomLeftCol = col - 1;
while (bottomLeftRow < dimension && bottomLeftCol >= 0 && currentSquares[bottomLeftRow][bottomLeftCol] === currentPlayer) {
lines.push([bottomLeftRow, bottomLeftCol]);
bottomLeftRow++;
bottomLeftCol--;
}
if (lines.length >= 5) {
return lines;
}
return [];
}
function renderBoard() {
boardElement.innerHTML = '';
for (let row = 0; row < dimension; row++) {
const rowElement = document.createElement('div');
rowElement.className = 'board-row';
for (let col = 0; col < dimension; col++) {
const value = squares[row][col];
const isWinningSquare = winningLine.some(([winRow, winCol]) => winRow === row && winCol === col);
const squareButton = document.createElement('button');
squareButton.className = 'square';
squareButton.style.backgroundColor = isWinningSquare ? 'yellow' : 'white';
squareButton.style.color = value === 'X' ? 'blue' : 'red';
squareButton.style.fontWeight = isWinningSquare ? 'bold' : 'normal';
squareButton.textContent = value;
squareButton.addEventListener('click', () => {
if (!singlePlayerMode || (singlePlayerMode && xIsNext)) {
handleClick(row, col);
}
});
rowElement.appendChild(squareButton);
}
boardElement.appendChild(rowElement);
}
}
function updateStatus() {
if (theWinner) {
statusElement.textContent = `Chiến thắng: ${theWinner}`;
} else {
statusElement.textContent = `Người chơi: ${xIsNext ? 'X' : 'O'}`;
}
}
function restartGame() {
squares = Array(dimension).fill(Array(dimension).fill(null));
xIsNext = true;
theWinner = null;
winningLine = [];
currentMatchMoves = [];
renderBoard();
updateStatus();
//recordMatchHistory();
if (singlePlayerMode && !xIsNext) {
makeComputerMove();
}
}
function makeComputerMove() {
//update
console.log("makeComputerMove called");
console.log("singlePlayerMode:", singlePlayerMode);
console.log("xIsNext:", xIsNext);
console.log("theWinner:", theWinner);
if (!singlePlayerMode || theWinner) {
console.log("Exiting makeComputerMove early");
return;
}
const availableMoves = [];
const humanPlayer = xIsNext ? 'X' : 'O';
const computerPlayer = xIsNext ? 'O' : 'X';
squares.forEach((row, rowIndex) => {
row.forEach((col, colIndex) => {
if (!squares[rowIndex][colIndex]) {
availableMoves.push([rowIndex, colIndex]);
}
});
});
if (availableMoves.length > 0) {
// Check if the computer can win in the next move
for (let i = 0; i < availableMoves.length; i++) {
const [row, col] = availableMoves[i];
const newSquares = squares.map((row) => [...row]);
newSquares[row][col] = computerPlayer;
if (calculateWinner(newSquares, row, col) === computerPlayer) {
console.log("Computer winning move:", row, col);
handleClick(row, col);
return;
}
}
// Check if the human player can win in the next move
for (let i = 0; i < availableMoves.length; i++) {
const [row, col] = availableMoves[i];
const newSquares = squares.map((row) => [...row]);
newSquares[row][col] = humanPlayer;
if (calculateWinner(newSquares, row, col) === humanPlayer) {
console.log("Human winning move:", row, col);
handleClick(row, col);
return;
}
}
// Random move for normal difficulty
const randomIndex = Math.floor(Math.random() * availableMoves.length);
const [row, col] = availableMoves[randomIndex];
// Choose a winning move for hard difficulty
if (availableMoves.length >= 3) {
for (let i = 0; i < availableMoves.length; i++) {
const [row, col] = availableMoves[i];
const newSquares = squares.map((row) => [...row]);
newSquares[row][col] = computerPlayer;
if (isWinningMove(newSquares, computerPlayer)) {
handleClick(row, col);
return;
}
}
}
console.log("Computer random move:", row, col);
handleClick(row, col);
}
}
function isWinningMove(currentSquares, player) {
for (let row = 0; row < dimension; row++) {
for (let col = 0; col < dimension; col++) {
if (!currentSquares[row][col]) {
const newSquares = currentSquares.map((row) => [...row]);
newSquares[row][col] = player;
if (calculateWinner(newSquares, row, col) === player) {
return true;
}
}
}
}
return false;
}
function toggleSinglePlayerMode() {
singlePlayerMode = !singlePlayerMode;
if (singlePlayerMode) {
singlePlayerToggle.innerHTML = '💻';
} else {
singlePlayerToggle.innerHTML = '👷 ';
}
}
renderBoard();
updateStatus();
//UNDO BUTTON
function undoLastMove() {
console.log("Undo button clicked");
if (currentMatchMoves.length > 0) {
// Remove the last move from the current match moves
const [lastMoveRow, lastMoveCol] = currentMatchMoves.pop();
// Reset the game state to the state before the last move
squares[lastMoveRow][lastMoveCol] = null;
xIsNext = !xIsNext;
// Re-render the board and update the status
renderBoard();
updateStatus();
stopTimer(true); // Stop the timer after undoing the move
} else {
alert("No moves to undo");
}
}
//update history function
let matchHistory = [];
let currentMatchMoves = [];
function recordMatchHistory() {
console.log("Recording match history");
console.log("Current match moves:", currentMatchMoves);
if (currentMatchMoves.length > 0) {
// Check if the current match moves are already in the match history
const isDuplicate = matchHistory.some(historyMoves =>
JSON.stringify(historyMoves) === JSON.stringify(currentMatchMoves)
);
// If the current match moves are not a duplicate, record them
if (!isDuplicate) {
matchHistory.push([...currentMatchMoves]); // Push a copy of currentMatchMoves
console.log("Match history:", matchHistory);
}
// Reset current match moves after recording
currentMatchMoves = [];
}
}
function displayLatestMatchMoves() {
console.log("Displaying latest match moves...");
if (matchHistory.length > 0) {
const latestMatchMoves = matchHistory[matchHistory.length - 1];
console.log("Latest match moves:", latestMatchMoves);
// Replay the latest match moves
replayMatchMoves(latestMatchMoves);
} else {
alert("No match history available");
}
}
function replayMatchMoves(moves) {
// Reset the game state before replaying the moves
squares = Array.from({ length: dimension }, () => Array.from({ length: dimension }, () => null));
xIsNext = true;
theWinner = null;
winningLine = [];
moves.forEach((move, index) => {
const [row, col] = move;
console.log("Replaying move:", row, col); // Log each move being replayed
squares[row][col] = xIsNext ? 'X' : 'O'; // Update the squares array directly
xIsNext = !xIsNext; // Toggle the player after each move
// Update xIsNext based on the number of moves replayed
if (index === moves.length - 1) {
// If it's the last move, the next player depends on the total number of moves
xIsNext = moves.length % 2 === 0;
}
});
// Render the board after all moves are replayed
renderBoard();
// Update the status after replaying all moves
updateStatus();
}
});
这是我的js代码