英文:
Recursion does not seem to work in my minimax implementation
问题
The miniMax
函数返回一个包含索引和分数的对象,但它没有访问预期的状态。
以下是代码的翻译部分:
const human = 'X'
const ai = 'O'
const winOptions = [[0, 1, 2], [3, 4, 5], [6, 7, 8], [0, 3, 6], [1, 4, 7], [2, 5, 8], [0, 4, 8], [2, 4, 6]]
const emptyCells = (layout) => {
return layout.map((item, index) => {
return (item === '' ? index : null)
}).filter((item) => item !== null)
}
const checkWin = (layout) => {
for (let i = 0; i < winOptions.length; i += 1) {
const [a, b, c] = winOptions[i]
if (layout[a] === 'X' && layout[a] === layout[b] && layout[b] === layout[c]) {
return -1
}
if (layout[a] === 'O' && layout[a] === layout[b] && layout[b] === layout[c]) {
return 1
}
}
return false
}
function miniMax (gameLayout, player) {
const empty = emptyCells(gameLayout)
if (checkWin(gameLayout) === 1) {
return { score: 10 }
} else if (checkWin(gameLayout) === -1) {
return { score: -10 }
} else if (empty.length === 0) {
return { score: 0 }
}
const moves = []
for (let i = 0; i < empty.length; i++) {
const move = {}
move.index = empty[i]
player === 'O' ? gameLayout[empty[i]] = 'O' : gameLayout[empty[i]] = 'X'
player === 'O' ? move.score = miniMax(gameLayout, human).score : move.score = miniMax(gameLayout, ai).score
moves.push(move)
}
let bestMove
if (player === 'O') {
bestMove = moves.reduce((acc, curr, i) => moves[acc].score > curr.score ? acc : i, 0)
} else {
bestMove = moves.reduce((acc, curr, i) => moves[acc].score < curr.score ? acc : i, 0)
}
console.log(JSON.stringify(moves))
return moves[bestMove]
}
// 示例运行:
const layout = ["X", "", "",
"", "", "",
"", "", "O"]
miniMax(layout, "X")
希望这有助于理解你的代码并找到问题。
英文:
The miniMax
function returns an object consisting of an index and score as intended, but it does not visit as many states as expected.
Here is the code:
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
const human = 'X'
const ai = 'O'
const winOptions = [[0, 1, 2], [3, 4, 5], [6, 7, 8], [0, 3, 6], [1, 4, 7], [2, 5, 8], [0, 4, 8], [2, 4, 6]]
const emptyCells = (layout) => {
return layout.map((item, index) => {
return (item === '' ? index : null)
}).filter((item) => item !== null)
}
const checkWin = (layout) => {
for (let i = 0; i < winOptions.length; i += 1) {
const [a, b, c] = winOptions[i]
if (layout[a] === 'X' && layout[a] === layout[b] && layout[b] === layout[c]) {
return -1
}
if (layout[a] === 'O' && layout[a] === layout[b] && layout[b] === layout[c]) {
return 1
}
}
return false
}
function miniMax (gameLayout, player) {
const empty = emptyCells(gameLayout)
if (checkWin(gameLayout) === 1) {
return { score: 10 }
} else if (checkWin(gameLayout) === -1) {
return { score: -10 }
} else if (empty.length === 0) {
return { score: 0 }
}
const moves = []
for (let i = 0; i < empty.length; i++) {
const move = {}
move.index = empty[i]
player === 'O' ? gameLayout[empty[i]] = 'O' : gameLayout[empty[i]] = 'X'
player === 'O' ? move.score = miniMax(gameLayout, human).score : move.score = miniMax(gameLayout, ai).score
moves.push(move)
}
let bestMove
if (player === 'O') {
bestMove = moves.reduce((acc, curr, i) => moves[acc].score > curr.score ? acc : i, 0)
} else {
bestMove = moves.reduce((acc, curr, i) => moves[acc].score < curr.score ? acc : i, 0)
}
console.log(JSON.stringify(moves))
return moves[bestMove]
}
// Example run:
const layout = ["X", "", "" ,
"" , "", "" ,
"" , "", "O"]
miniMax(layout, "X")
<!-- end snippet -->
Here is how it looks, with the console output as produced in the above snippet:
Clearly my minimax implementation for Tic Tac Toe doesn't work as expected. The console should be flooded with output, since there are many options for enumeration. But it displays only 6 lines of output, as if the recursion does not work and does not go into every possible option.
I've been trying to find a bug for days... What is wrong with this implementation?
答案1
得分: 2
问题在于播放的移动没有被撤销。函数按照预期递归调用,但之后棋盘已满,之后不再更改。因此,对于所有执行上下文,empty.length
都将为 0,并且递归将在不进行其他移动的情况下解开。
为了解决这个问题,在push
之后添加以下行:
gameLayout[empty[i]] = ''; // 撤销
英文:
The problem is that the played moves are not taken back. The function is called recursively as intended, but then the board is full and it never changes after that. By consequence empty.length
will be 0 for all execution contexts, and the recursion will unwind without making other moves.
To fix this, add this line after the push
:
gameLayout[empty[i]] = ''; // take back
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论