作为个人练习,我试图实现基于极小极大的井字游戏。我一直在研究我在网上找到的各种语言的例子。我的实现似乎在它的工作,但AI在某些边缘情况下失败。您可以播放我的here
版本如果您选择3个角落,然后选择中心,您将获胜。否则它似乎正确执行。我可以手动运行具有不同游戏状态的minmax()函数,并且它似乎在人工智能的第一步中得分不正确。我担心我实现算法的方式存在根本性的问题。
这是我的代码:
// Board state 'object'
function State(old) {
// Prior board states can be loaded in during minmax recursion
if (typeof old !== 'undefined') {
this.board = old.board.slice(0);
} else {
// Otherwise start with empty board
this.board = ['E','E','E','E','E','E','E','E','E'];
}
// Terminal game flag
this.result = 'active';
// Current player flag
this.turn = "X";
// Label to identify move that results in this state during recursion
this.element = "";
// Function to switch active player for minmax scoring
this.advanceTurn = function() {
this.turn = this.turn === "X" ? "O" : "X";
}
// Function to determine if game is complete
this.isTerminal = function() {
const lines = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6],
];
for (let i = 0; i < lines.length; i++) {
const [a, b, c] = lines[i];
if (this.board[a] !== 'E' && this.board[a] === this.board[b] && this.board[a] === this.board[c]) {
this.result = this.board[a];
return true;
}
}
if (this.moves().length < 1) {
this.result = 'DRAW';
return true;
}
return false;
}
// Function to find all possible moves
this.moves = function() {
arr = this.board.reduce(function(array,el,index){
if (el === 'E') {
array.push(index);
}
return array;
},[]);
return arr;
}
}
// Recursive minmax function
function minmax(state) {
// 1) If the state is terminal, return the score from O's perspective
if (state.isTerminal() === true) {
if (state.result === 'X') {
return -10;
} else if (state.result === 'O') {
return 10;
} else {
return 0;
}
}
// Generate list of possible new game states (moves)
newStatesSet = state.moves().map(function (el) {
var newState = new State(state);
newState.board[el] = state.turn.slice(0);
newState.advanceTurn();
newState.element = el;
return newState;
});
// Array to hold all child scores
var newStateScores = [];
// For each of these states, add the minmax score of
// that state to the scoreList
newStatesSet.forEach(function(newState) {
var newStateScore = minmax(newState);
newStateScores.push(newStateScore);
});
stateScore = Math.min(...newStateScores);
return stateScore;
}
function aiMove(state) {
var possibleScores = [];
var possibleMoves = [];
var possibleStates = state.moves().map(function(el) {
var newState = new State(state);
possibleMoves.push(el);
newState.board[el] = 'O';
possibleScores.push(minmax(newState));
return newState;
});
if (possibleMoves.length < 1) {
return -1;
}
console.log(possibleStates);
console.log(possibleScores);
function indexOfMax(arr) {
var max = arr.reduce(function(a,b) {
return b > a ? b : a;
});
return arr.indexOf(max);
}
return possibleMoves[indexOfMax(possibleScores)];
}
var game = new State();
game.board = ['E','E','E',
'O','E','E',
'X','E','X']
game.turn = 'O';
//console.log(minmax(game));
console.log(aiMove(game));
答案 0 :(得分:1)
我认为你的递归minmax函数存在问题:
1 2
这总是计算最小值,所以你实际上在运行一个算法,在递归中X和O都在合作使X赢!
(在你的顶级你使用max,但不在递归中。)
您可以通过根据state.turn选择是最大还是最小来解决此问题。
答案 1 :(得分:0)
也许this implementation in Typescript可以帮助指导您。
它使用Board
和MiniMaxItem
,如:
interface Board {
gameOver: boolean;
score: number;
playerX: boolean;
val: GridEntry[];
}
interface MinMaxItem {
score: number;
board: Board;
}
MiniMaxItem
是递归元素,其中保存状态及其递归分数。要在JavaScript中实现,您必须通过函数更改BoardManager
类的方法。