所以我的问题在于 playAI 和 miniMaxAlgorithm。
首先,我制作了实例 myTicTacToe 的副本,我将其称为 tempgame,然后我在该副本上调用 miniMaxAlgorithm。所有这些都是为了确保 myTicTacToe 保持不变。
问题是 myTicTacToe 之后具有与 tempgame 相同的值。 我不明白为什么。
/****************************TicTacToe 类******************** ****/
class TicTacToe {
constructor(playField = [
['E', 'E', 'E'],
['E', 'E', 'E'],
['E', 'E', 'E']
], human = 'X', computer = 'O', gameStatus = "playing", currentPlayer = 'X') {
this.playField = playField;
this.human = human;
this.computer = computer;
this.gameStatus = gameStatus;
this.bestMove;
this.startingPlayer = human;
this.currentPlayer = currentPlayer;
}
reset() {
this.playField = [
['E', 'E', 'E'],
['E', 'E', 'E'],
['E', 'E', 'E']
];
this.gameStatus = 'playing';
this.currentPlayer = this.startingPlayer;
$('#gamestate').text('');
}
checkGameState() {
/******************** Win conditions******************************/
if (this.currentPlayer === 'X' || this.currentPlayer === 'O') {
if (this.winOrNot()) {
$('#gamestate').text('Player ' + this.currentPlayer + ' won.');
this.gameStatus = 'over';
}
//********************** Check if it is a draw***************/
else {
/*which results in a draw*/
let arrayOfFreePlaces = this.freePositions();
if (!(arrayOfFreePlaces.length > 0)) {
$('#gamestate').text('It is a draw');
this.gameStatus = 'over';
}
}
}
}
/***********************/
winOrNot() {
if ((this.playField[0][0] === this.currentPlayer && this.playField[0][1] === this.currentPlayer && this.playField[0][2] === this.currentPlayer) ||
(this.playField[1][0] === this.currentPlayer && this.playField[1][1] === this.currentPlayer && this.playField[1][2] === this.currentPlayer) ||
(this.playField[2][0] === this.currentPlayer && this.playField[2][1] === this.currentPlayer && this.playField[2][2] === this.currentPlayer) ||
(this.playField[0][0] === this.currentPlayer && this.playField[1][0] === this.currentPlayer && this.playField[2][0] === this.currentPlayer) ||
(this.playField[0][1] === this.currentPlayer && this.playField[1][1] === this.currentPlayer && this.playField[2][1] === this.currentPlayer) ||
(this.playField[0][2] === this.currentPlayer && this.playField[1][2] === this.currentPlayer && this.playField[2][2] === this.currentPlayer) ||
(this.playField[0][0] === this.currentPlayer && this.playField[1][1] === this.currentPlayer && this.playField[2][2] === this.currentPlayer) ||
(this.playField[0][2] === this.currentPlayer && this.playField[1][1] === this.currentPlayer && this.playField[2][0] === this.currentPlayer)
) {
return true;
} else {
return false;
}
}
freePositions() {
let emptyPositions = [];
for (let i = 0; i < this.playField.length; i++) {
for (let j = 0; j < this.playField[i].length; j++) {
if (this.playField[i][j] === 'E') {
emptyPositions.push([i, j]);
}
}
}
return emptyPositions;
}
// rate gamestate
rateField() {
// if computer wins +10
if (this.winOrNot(this.computer)) {
return 10;
}
// if human wins -10
else if (this.winOrNot(this.human)) {
return -10;
}
// if no one wins +0, aka drawm or not finished yet
else {
return 0;
}
}
}
//Prototypes of TicTacToe
TicTacToe.prototype.placeSign = function(row, column) {
// check if field is empty
if (this.playField[row][column] === 'E') {
if (this.currentPlayer === "X") {
this.playField[row][column] = 'X';
} else if (this.currentPlayer === "O") {
this.playField[row][column] = 'O';
}
this.checkGameState();
this.currentPlayer === this.computer ? this.currentPlayer = this.human : this.currentPlayer = this.computer;
} else {
console.log("Select an empty field!!");
}
};
/********************声明**************************** *****/
let myTicTacToe = new TicTacToe();
let tempgame = new TicTacToe();
let bestMove;
/********************函数****************************/
//迷你最大算法
function miniMaxAlgorithm(TicTacToe1) {
/****************base case********************************/
// if the game is over , return rating
if (TicTacToe1.gameStatus === 'over') {
return TicTacToe1.rateField();
}
/******************************************/
//contains the rating of each move
let scores = [];
// containing the equivalent moves
let moves = [];
//fill the scores array
/**************************recursive case*******************************/
// create on array with containing all possible moves of the current tictactoe instance
let freeFields = TicTacToe1.freePositions();
for (let i = 0; i < freeFields.length; i++) {
//make a copy of the current tictactoe instance
let possibleTicTacToe = new TicTacToe(TicTacToe1.playField, TicTacToe1.human, TicTacToe1.computer, TicTacToe1.gameStatus, TicTacToe1.currentPlayer);
//play one of the possible moves
possibleTicTacToe.placeSign(freeFields[i][0], freeFields[i][1]);
// calling the function recursively until game is over
scores.push(miniMaxAlgorithm(possibleTicTacToe));
// adding place sign parameters ass an array inside moves
moves.push([freeFields[i][0], freeFields[i][1]]);
}
// Min Max Calculation
if (TicTacToe1.currentPlayer === TicTacToe1.computer) {
// search for the largest score and save its index in maxScoreIndex
let maxScoreIndex = 0;
for (let j = 1; j < scores.length; j++) {
if (scores[j] > scores[maxScoreIndex]) {
maxScoreIndex = j;
}
}
bestMove = moves[maxScoreIndex];
return scores[maxScoreIndex];
}
// tests best possible opponent moves (human)
else {
//
let minScoreIndex = 0;
for (let j = 1; j < scores.length; j++) {
if (scores[j] < scores[minScoreIndex]) {
minScoreIndex = j;
}
}
bestMove = moves[minScoreIndex];
return scores[minScoreIndex];
}
/**********************************************************************/
}
函数 updateFields() {
document.getElementById('field1').innerHTML = myTicTacToe.playField[0][0];
document.getElementById('field2').innerHTML = myTicTacToe.playField[0][1];
document.getElementById('field3').innerHTML = myTicTacToe.playField[0][2];
document.getElementById('field4').innerHTML = myTicTacToe.playField[1][0];
document.getElementById('field5').innerHTML = myTicTacToe.playField[1][1];
document.getElementById('field6').innerHTML = myTicTacToe.playField[1][2];
document.getElementById('field7').innerHTML = myTicTacToe.playField[2][0];
document.getElementById('field8').innerHTML = myTicTacToe.playField[2][1];
document.getElementById('field9').innerHTML = myTicTacToe.playField[2][2];
}
/**********************************************************/
//playAI
function playAI() {
//AI miniMaxEnd
tempgame = new TicTacToe (myTicTacToe.playField,myTicTacToe.human,myTicTacToe.computer,myTicTacToe.gameStatus,myTicTacToe.currentPlayer)
console.dir(myTicTacToe);
console.dir(tempgame);
miniMaxAlgorithm(tempgame);
console.dir(myTicTacToe);
console.dir(tempgame);
myTicTacToe.placeSign(bestMove[0],bestMove[1]);
//AI miniMaxEnd
updateFields();
}
最佳答案
我知道可能导致此问题的原因。
在 javascript 中,数组(或列表)是通过引用而不是值传递的。
这意味着,如果您向函数传递一个数组作为参数,它将传递一个指向列表的指针,而不是列表的副本。
在您的 TicTacToe 类中,您有一个名为 playField 的变量,它是一个列表。
您可以像这样创建 TicTacToe 对象的克隆:
tempgame = new TicTacToe (myTicTacToe.playField, ... )
在这里,您将引用传递到现有的playField列表,而不是克隆列表。即传递列表的地址而不是其内容。
您的myTicTacToe和tempgame都将使用playField数据的相同副本。一者的改变会导致另一者的改变。
为了创建数组的新副本,标准做法是使用 javascript slice 操作:
tempgame = new TicTacToe (myTicTacToe.playField.slice(0), ... )
此操作将从第一个元素 (0) 开始创建数组的新副本。
有关使用切片操作克隆数组的更多信息可以在此处找到:
关于javascript - 为什么同一类的不同实例会互相覆盖?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46403639/