我正在尝试用人工智能制作井字游戏。到目前为止,我已经应用了游戏逻辑、人工智能逻辑、玩家和游戏板处理。一切正常,但选择角色有一个问题,如果我选择按钮 X 或 O,它应该将其分配给人类玩家,另一个分配给人工智能。然而,虽然 X 按钮按预期工作,但我的 O 按钮仍然让人类玩家选择 X。我需要能够选择 O 并玩第二个,但游戏总是以我作为第一个玩家和第二个 AI 开始..
const chooseXObtn = document.querySelector(".chooseXO-btn");
const chooseXO = document.querySelector(".chooseXO");
function displayXO() {
chooseXO.classList.toggle("closed");
console.log("display work")
}
chooseXObtn.addEventListener("click", displayXO);
const gameBoard = (() => {
let winner;
let getWinner = () => winner;
let gameboard = [
["", "", ""],
["", "", ""],
["", "", ""]
];
const getBoard = () => gameboard;
const getPosition = (row, column) => gameboard[row][column];
const setPosition = (row, column, el) => {
gameboard[row][column] = el;
render();
}
const resetBoard = () => {
gameboard = [
["", "", ""],
["", "", ""],
["", "", ""]
];
};
const getEmptyFieldsId = () => {
let fields = [];
for (let row = 0; row < gameboard.length; row++) {
for (let col = 0; col < gameboard[row].length; col++) {
const field = gameboard[row][col];
if (field == "") {
fields.push([row, col]);
}
}
}
return fields;
};
const setFieldForAiLogic = (row, column, player) => {
if (player == undefined) {
board[row][column] = "";
return;
}
board[row][column] = player.getMark();
}
return {
getBoard,
getWinner,
getPosition,
setPosition,
resetBoard,
getEmptyFieldsId
}
})();
const Player = (name, mark) => {
const getName = () => name;
const getMark = () => mark;
const setMark = (mark) => this.mark = mark;
return {
getName,
getMark,
setMark
}
}
const gameController = (() => {
let humanPlayer = Player("Player1", "X");
let aiPlayer = Player("AI", "O");
let turn = true;
let board;
let winner = null;
const setTurn = (falseortrue) => turn = falseortrue;
const getWinner = () => winner;
const whoseTurn = () => turn ? humanPlayer : aiPlayer;
const switchTurn = () => turn = turn ? false : true;
const startGame = () => {
gameBoard.resetBoard();
board = gameBoard.getBoard();
render();
winner = null;
// Check who has the 'X' mark and let that player start
if (humanPlayer.getMark() === 'X') {
console.log("x called turn true");
turn = true; // Human starts
} else {
console.log("o called turn = false");
switchTurn(); // AI starts
}
// If AI starts, make a move for AI
if (!turn) {
let arr = _getbestMove(gameBoard.getBoard());
console.log(arr)
gameBoard.setPosition(arr[0], arr[1], whoseTurn().getMark());
switchTurn();
}
// If Human starts, wait for their move
else {
// Wait for human to make a move
}
}
const handleMove = (moveRow, moveColumn) => {
if (!isGameOver(gameBoard.getBoard()) && whoseTurn().getMark() === "X") {
gameBoard.setPosition(moveRow, moveColumn, whoseTurn().getMark());
switchTurn();
// If it's now AI's turn and the game is not over, make a move for AI
if (!isGameOver(gameBoard.getBoard()) && whoseTurn().getMark() === "O") {
let arr = _getbestMove(gameBoard.getBoard());
console.log(arr)
gameBoard.setPosition(arr[0], arr[1], whoseTurn().getMark());
switchTurn();
}
}
}
const changeMark = (mark) => {
if (mark == "X") {
humanPlayer.setMark('X');
aiPlayer.setMark("O");
turn = true;
} else if (mark == "O") {
humanPlayer.setMark("O");
aiPlayer.setMark("X");
turn = false;
} else throw 'incorrect sign';
}
const isGameOver = (board) => {
//check row and columns
for (let i = 0; i < 3; i++) {
if (board[i][0] !== "" && board[i][0] === board[i][1] && board[i][1] === board[i][2]) {
winner = board[i][0];
return winner;
} else if (board[0][i] !== "" && board[0][i] === board[1][i] && board[1][i] === board[2][i]) {
winner = board[0][i];
return winner;
}
}
//check diagonals
if (board[0][0] !== "" && board[0][0] === board[1][1] && board[1][1] === board[2][2]) {
winner = board[0][0];
return winner;
} else if (board[2][0] !== "" && board[2][0] === board[1][1] && board[1][1] === board[0][2]) {
winner = board[2][0];
return winner;
}
//if draw
let isDraw = true;
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
if (board[i][j] === "") {
isDraw = false;
}
}
}
if (isDraw) {
return "draw";
}
// game still continues
return null;
}
return {
whoseTurn,
startGame,
handleMove,
isGameOver,
getWinner,
changeMark
}
}
)();
function render() {
const tiles = document.querySelectorAll(".tile");
tiles.forEach(
(tile) => {
tile.innerHTML = gameBoard.getPosition(tile.getAttribute("data-row"), tile.getAttribute("data-col"))
}
);
};
// Announce the winner or draw
function announceResult(result) {
const message = result === 'draw' ? 'It\'s a draw!' : `${result} wins!`;
// Display the message in an alert or a div
setTimeout(() => {
alert(message);
}, 100);
}
const tiles = document.querySelectorAll(".tile");
tiles.forEach((tile) => {
const row = tile.getAttribute("data-row");
const col = tile.getAttribute("data-col");
});
// Add this event listener to the "Restart" button to start a new game
const restart = document.querySelector(".restart");
restart.addEventListener("click", () => {
gameController.startGame();
render();
});
// Add an event listener to each tile
tiles.forEach((tile) => {
tile.addEventListener("click", () => {
// Get the row and column of the clicked tile
const row = tile.getAttribute("data-row");
const col = tile.getAttribute("data-col");
// Make a move on the game board
if (tile.innerHTML == "") {
gameController.handleMove(row, col);
}
// Check if the game is over
const result = gameController.isGameOver(gameBoard.getBoard());
if (result) {
// Announce the result of the game
announceResult(result);
}
});
});
function _getbestMove(currentBoard) {
let bestVal = +Infinity
let bestMove = [-1, -1]
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
if (currentBoard[i][j] !== "X" & currentBoard[i][j] !== "O") {
let temp = currentBoard[i][j]
// console.log(temp)
currentBoard[i][j] = 'O'
let moveVal = _minimax(currentBoard, 9, true)
// console.log("move val", moveVal, bestVal)
currentBoard[i][j] = temp
if (moveVal < bestVal) {
bestMove = [i, j]
bestVal = moveVal
}
}
}
}
// console.log("best Val:", bestVal)
return bestMove
}
function _minimax(currentBoard, depth, isMaximixingPlayer) {
// console.log(currentBoard, depth, isMaximixingPlayer)
let winSymbol = gameController.isGameOver(currentBoard)
if (depth === 0 | winSymbol === "X" | winSymbol === "O" | winSymbol === "draw") {
let gameOutcome = gameController.isGameOver(currentBoard)
if (gameOutcome === 'X') {
return 10
} else if (gameOutcome === 'O') {
return -10
} else {
return 0
}
}
if (isMaximixingPlayer) {
let maxEval = -Infinity
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
if (currentBoard[i][j] !== "X" & currentBoard[i][j] !== "O") {
let temp = currentBoard[i][j]
currentBoard[i][j] = 'X'
let val = _minimax(currentBoard, depth - 1, false)
// console.log(val)
currentBoard[i][j] = temp
if (val > maxEval) {
maxEval = val;
}
}
}
}
return maxEval;
} else {
let minEval = +Infinity
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
if (currentBoard[i][j] !== "X" & currentBoard[i][j] !== "O") {
let temp = currentBoard[i][j]
currentBoard[i][j] = 'O'
let val = _minimax(currentBoard, depth - 1, true)
currentBoard[i][j] = temp
if (val < minEval) {
minEval = val;
}
}
}
}
return minEval
}
}
const O_btn = document.querySelector("#O-btn");
const X_btn = document.querySelector("#X-btn");
X_btn.addEventListener("click", function() {
gameController.changeMark(this.innerHTML);
gameController.startGame();
console.log("X button clicked");
});
O_btn.addEventListener("click", function() {
gameController.changeMark(this.innerHTML);
gameController.startGame();
console.log("O button clicked");
});
html,
body,
div,
span,
applet,
object,
iframe,
h1,
h2,
h3,
h4,
h5,
h6,
p,
blockquote,
pre,
a,
abbr,
acronym,
address,
big,
cite,
code,
del,
dfn,
em,
img,
ins,
kbd,
q,
s,
samp,
small,
strike,
strong,
sub,
sup,
tt,
var,
b,
u,
i,
center,
dl,
dt,
dd,
ol,
ul,
li,
fieldset,
form,
label,
legend,
table,
caption,
tbody,
tfoot,
thead,
tr,
th,
td,
article,
aside,
canvas,
details,
embed,
figure,
figcaption,
footer,
header,
hgroup,
menu,
nav,
output,
ruby,
section,
summary,
time,
mark,
audio,
video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
body {
display: flex;
flex-direction: column;
align-items: center;
gap: 15px;
font-family: 'Open Sans', "Helvetica";
}
.title {
margin-top: 100px;
flex: 1;
display: flex;
justify-content: center;
border: 1px solid black;
font-size: 40px;
}
.game-board-container {
width: 50vh;
height: 50vh;
display: flex;
justify-content: center;
align-items: center;
}
.game-board {
display: grid;
grid-template-columns: repeat(3, minmax(100px, 1fr));
grid-template-rows: repeat(3, minmax(100px, 1fr));
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
grid-gap: 1px;
/* This creates a 1px gap between cells */
}
.game-board div {
width: 100%;
height: 100%;
outline: 1px solid black;
display: flex;
justify-content: center;
align-items: center;
font-size: 100px;
}
.game-board div:hover {
cursor: pointer;
}
.bottom-menu {
margin-top: 100px;
;
display: flex;
width: 50vh;
height: 50px;
text-align: center;
}
.bottom-menu div {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
}
button {
width: 100%;
height: 100%;
border: none;
box-shadow: 1px 1px 1px grey;
outline: 1px solid black;
}
button:hover {
box-shadow: 3px 3px 2px grey;
cursor: pointer
}
span {
outline: 1px solid black;
height: 100%;
display: flex;
align-items: center;
}
.chooseXO {
height: 50px;
}
.closed {
display: none !important;
}
#X-btn {
background-color: green;
}
#O-btn {
background-color: red;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="style.css">
<script src="script.js" defer></script>
</head>
<body>
<div class="title">
<h1>Tic-tac-toe</h1>
</div>
<div class="game-board-container">
<div class="game-board">
<div class="tile" data-row="0" data-col="0">X</div>
<div class="tile" data-row="0" data-col="1">O</div>
<div class="tile" data-row="0" data-col="2">X</div>
<div class="tile" data-row="1" data-col="0">O</div>
<div class="tile" data-row="1" data-col="1">X</div>
<div class="tile" data-row="1" data-col="2">O </div>
<div class="tile" data-row="2" data-col="0">x</div>
<div class="tile" data-row="2" data-col="1">O</div>
<div class="tile" data-row="2" data-col="2">X</div>
</div>
</div>
<div class="bottom-menu">
<div class="menu-left">
<button class="restart">Restart</button>
</div>
<div class="menu-right">
<button class=chooseXO-btn>Choose your letter</button>
<div class="chooseXO closed">
<button id="X-btn">X</button>
<button id="O-btn">O</button>
</div>
</div>
</div>
</body>
</html>
到目前为止,我尝试使用 console.log 调用来调试代码,它在两个按钮上都返回“X 调用变为 true”,并且按钮说 X 单击了,O也单击了。所以按钮可以工作,游戏开始,但是为玩家分配标记似乎没有按预期工作。我必须犯一些根本性的错误。谢谢你
工作
const chooseXObtn = document.querySelector(".chooseXO-btn");
const chooseXO = document.querySelector(".chooseXO");
function displayXO() {
chooseXO.classList.toggle("closed");
console.log("display work")
}
chooseXObtn.addEventListener("click", displayXO);
const gameBoard = (() => {
let winner;
let getWinner = () => winner;
let gameboard = [
["", "", ""],
["", "", ""],
["", "", ""]
];
const getBoard = () => gameboard;
const getPosition = (row, column) => gameboard[row][column];
const setPosition = (row, column, el) => {
gameboard[row][column] = el;
render();
}
const resetBoard = () => {
gameboard = [
["", "", ""],
["", "", ""],
["", "", ""]
];
};
const getEmptyFieldsId = () => {
let fields = [];
for (let row = 0; row < gameboard.length; row++) {
for (let col = 0; col < gameboard[row].length; col++) {
const field = gameboard[row][col];
if (field == "") {
fields.push([row, col]);
}
}
}
return fields;
};
const setFieldForAiLogic = (row, column, player) => {
if (player == undefined) {
board[row][column] = "";
return;
}
board[row][column] = player.getMark();
}
return {
getBoard,
getWinner,
getPosition,
setPosition,
resetBoard,
getEmptyFieldsId
}
})();
const Player = (name, mark) => {
return {
name,
mark,
getName(){
return this.name;
},
getMark(){
debugger;
return this.mark;
},
setMark(mark){
this.mark = mark;
}
}
}
const gameController = (() => {
let humanPlayer = Player("Player1", "X");
let aiPlayer = Player("AI", "O");
let turn = true;
let board;
let winner = null;
const setTurn = (falseortrue) => turn = falseortrue;
const getWinner = () => winner;
const whoseTurn = () => turn ? humanPlayer : aiPlayer;
const switchTurn = () => turn = turn ? false : true;
const startGame = () => {
gameBoard.resetBoard();
board = gameBoard.getBoard();
render();
winner = null;
// Check who has the 'X' mark and let that player start
if (humanPlayer.getMark() === 'X') {
console.log("x called turn true");
turn = true; // Human starts
} else {
console.log("o called turn = false");
turn = false;
switchTurn(); // AI starts
}
debugger;
// If AI starts, make a move for AI
if (!turn) {
let arr = _getbestMove(gameBoard.getBoard());
console.log(arr)
gameBoard.setPosition(arr[0], arr[1], whoseTurn().getMark());
switchTurn();
}
// If Human starts, wait for their move
else {
// Wait for human to make a move
}
}
const handleMove = (moveRow, moveColumn) => {
if (!isGameOver(gameBoard.getBoard()) && whoseTurn().getMark() === "X") {
gameBoard.setPosition(moveRow, moveColumn, whoseTurn().getMark());
switchTurn();
// If it's now AI's turn and the game is not over, make a move for AI
if (!isGameOver(gameBoard.getBoard()) && whoseTurn().getMark() === "O") {
let arr = _getbestMove(gameBoard.getBoard());
console.log(arr)
gameBoard.setPosition(arr[0], arr[1], whoseTurn().getMark());
switchTurn();
}
}
}
const changeMark = (mark) => {
if (mark == "X") {
humanPlayer.setMark('X');
aiPlayer.setMark("O");
turn = true;
} else if (mark == "O") {
humanPlayer.setMark("O");
aiPlayer.setMark("X");
turn = false;
} else throw 'incorrect sign';
}
const isGameOver = (board) => {
//check row and columns
for (let i = 0; i < 3; i++) {
if (board[i][0] !== "" && board[i][0] === board[i][1] && board[i][1] === board[i][2]) {
winner = board[i][0];
return winner;
} else if (board[0][i] !== "" && board[0][i] === board[1][i] && board[1][i] === board[2][i]) {
winner = board[0][i];
return winner;
}
}
//check diagonals
if (board[0][0] !== "" && board[0][0] === board[1][1] && board[1][1] === board[2][2]) {
winner = board[0][0];
return winner;
} else if (board[2][0] !== "" && board[2][0] === board[1][1] && board[1][1] === board[0][2]) {
winner = board[2][0];
return winner;
}
//if draw
let isDraw = true;
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
if (board[i][j] === "") {
isDraw = false;
}
}
}
if (isDraw) {
return "draw";
}
// game still continues
return null;
}
return {
whoseTurn,
startGame,
handleMove,
isGameOver,
getWinner,
changeMark
}
}
)();
function render() {
const tiles = document.querySelectorAll(".tile");
tiles.forEach(
(tile) => {
tile.innerHTML = gameBoard.getPosition(tile.getAttribute("data-row"), tile.getAttribute("data-col"))
}
);
};
// Announce the winner or draw
function announceResult(result) {
const message = result === 'draw' ? 'It\'s a draw!' : `${result} wins!`;
// Display the message in an alert or a div
setTimeout(() => {
alert(message);
}, 100);
}
const tiles = document.querySelectorAll(".tile");
tiles.forEach((tile) => {
const row = tile.getAttribute("data-row");
const col = tile.getAttribute("data-col");
});
// Add this event listener to the "Restart" button to start a new game
const restart = document.querySelector(".restart");
restart.addEventListener("click", () => {
gameController.startGame();
render();
});
// Add an event listener to each tile
tiles.forEach((tile) => {
tile.addEventListener("click", () => {
// Get the row and column of the clicked tile
const row = tile.getAttribute("data-row");
const col = tile.getAttribute("data-col");
// Make a move on the game board
if (tile.innerHTML == "") {
gameController.handleMove(row, col);
}
// Check if the game is over
const result = gameController.isGameOver(gameBoard.getBoard());
if (result) {
// Announce the result of the game
announceResult(result);
}
});
});
function _getbestMove(currentBoard) {
let bestVal = +Infinity
let bestMove = [-1, -1]
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
if (currentBoard[i][j] !== "X" & currentBoard[i][j] !== "O") {
let temp = currentBoard[i][j]
// console.log(temp)
currentBoard[i][j] = 'O'
let moveVal = _minimax(currentBoard, 9, true)
// console.log("move val", moveVal, bestVal)
currentBoard[i][j] = temp
if (moveVal < bestVal) {
bestMove = [i, j]
bestVal = moveVal
}
}
}
}
// console.log("best Val:", bestVal)
return bestMove
}
function _minimax(currentBoard, depth, isMaximixingPlayer) {
// console.log(currentBoard, depth, isMaximixingPlayer)
let winSymbol = gameController.isGameOver(currentBoard)
if (depth === 0 | winSymbol === "X" | winSymbol === "O" | winSymbol === "draw") {
let gameOutcome = gameController.isGameOver(currentBoard)
if (gameOutcome === 'X') {
return 10
} else if (gameOutcome === 'O') {
return -10
} else {
return 0
}
}
if (isMaximixingPlayer) {
let maxEval = -Infinity
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
if (currentBoard[i][j] !== "X" & currentBoard[i][j] !== "O") {
let temp = currentBoard[i][j]
currentBoard[i][j] = 'X'
let val = _minimax(currentBoard, depth - 1, false)
// console.log(val)
currentBoard[i][j] = temp
if (val > maxEval) {
maxEval = val;
}
}
}
}
return maxEval;
} else {
let minEval = +Infinity
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
if (currentBoard[i][j] !== "X" & currentBoard[i][j] !== "O") {
let temp = currentBoard[i][j]
currentBoard[i][j] = 'O'
let val = _minimax(currentBoard, depth - 1, true)
currentBoard[i][j] = temp
if (val < minEval) {
minEval = val;
}
}
}
}
return minEval
}
}
const O_btn = document.querySelector("#O-btn");
const X_btn = document.querySelector("#X-btn");
X_btn.addEventListener("click", function() {
gameController.changeMark(this.innerHTML);
gameController.startGame();
console.log("X button clicked");
});
O_btn.addEventListener("click", function() {
gameController.changeMark(this.innerHTML);
gameController.startGame();
console.log("O button clicked");
});
html,
body,
div,
span,
applet,
object,
iframe,
h1,
h2,
h3,
h4,
h5,
h6,
p,
blockquote,
pre,
a,
abbr,
acronym,
address,
big,
cite,
code,
del,
dfn,
em,
img,
ins,
kbd,
q,
s,
samp,
small,
strike,
strong,
sub,
sup,
tt,
var,
b,
u,
i,
center,
dl,
dt,
dd,
ol,
ul,
li,
fieldset,
form,
label,
legend,
table,
caption,
tbody,
tfoot,
thead,
tr,
th,
td,
article,
aside,
canvas,
details,
embed,
figure,
figcaption,
footer,
header,
hgroup,
menu,
nav,
output,
ruby,
section,
summary,
time,
mark,
audio,
video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
body {
display: flex;
flex-direction: column;
align-items: center;
gap: 15px;
font-family: 'Open Sans', "Helvetica";
}
.title {
margin-top: 100px;
flex: 1;
display: flex;
justify-content: center;
border: 1px solid black;
font-size: 40px;
}
.game-board-container {
width: 50vh;
height: 50vh;
display: flex;
justify-content: center;
align-items: center;
}
.game-board {
display: grid;
grid-template-columns: repeat(3, minmax(100px, 1fr));
grid-template-rows: repeat(3, minmax(100px, 1fr));
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
grid-gap: 1px;
/* This creates a 1px gap between cells */
}
.game-board div {
width: 100%;
height: 100%;
outline: 1px solid black;
display: flex;
justify-content: center;
align-items: center;
font-size: 100px;
}
.game-board div:hover {
cursor: pointer;
}
.bottom-menu {
margin-top: 100px;
;
display: flex;
width: 50vh;
height: 50px;
text-align: center;
}
.bottom-menu div {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
}
button {
width: 100%;
height: 100%;
border: none;
box-shadow: 1px 1px 1px grey;
outline: 1px solid black;
}
button:hover {
box-shadow: 3px 3px 2px grey;
cursor: pointer
}
span {
outline: 1px solid black;
height: 100%;
display: flex;
align-items: center;
}
.chooseXO {
height: 50px;
}
.closed {
display: none !important;
}
#X-btn {
background-color: green;
}
#O-btn {
background-color: red;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="style.css">
<script src="script.js" defer></script>
</head>
<body>
<div class="title">
<h1>Tic-tac-toe</h1>
</div>
<div class="game-board-container">
<div class="game-board">
<div class="tile" data-row="0" data-col="0">X</div>
<div class="tile" data-row="0" data-col="1">O</div>
<div class="tile" data-row="0" data-col="2">X</div>
<div class="tile" data-row="1" data-col="0">O</div>
<div class="tile" data-row="1" data-col="1">X</div>
<div class="tile" data-row="1" data-col="2">O </div>
<div class="tile" data-row="2" data-col="0">x</div>
<div class="tile" data-row="2" data-col="1">O</div>
<div class="tile" data-row="2" data-col="2">X</div>
</div>
</div>
<div class="bottom-menu">
<div class="menu-left">
<button class="restart">Restart</button>
</div>
<div class="menu-right">
<button class=chooseXO-btn>Choose your letter</button>
<div class="chooseXO closed">
<button id="X-btn">X</button>
<button id="O-btn">O</button>
</div>
</div>
</div>
</body>
</html>