我在实施这个I found here的TicTacToe AI时遇到了麻烦。我对javascript相对较新,所以我确定我在变量范围内做错了。
代码不会在代码段中运行,而是heres my codepen
choices = {
0: '#ul',
1: '#um',
2: '#ur',
3: '#ml',
4: '#mm',
5: '#mr',
6: '#ll',
7: '#lm',
8: '#lr'
}
function getGrid() {
var divs = []
for (var i = 0; i < 9; i++) {
divs.push($(choices[i]).html())
}
return divs
}
function getGame() {
var divs = []
for (var i = 0; i < 9; i++) {
divs.push([$(choices[i]).html(), i])
}
return divs
}
function convertGameToGrid(game) {
var divs = []
for (var i = 0; i < game.length; i++) {
divs.push(game[i][0])
}
return divs
}
function checkGrid(divs) {
var options = [
[divs[0], divs[1], divs[2]],
[divs[3], divs[4], divs[5]],
[divs[6], divs[7], divs[8]],
[divs[0], divs[3], divs[6]],
[divs[1], divs[4], divs[7]],
[divs[2], divs[5], divs[8]],
[divs[0], divs[4], divs[8]],
[divs[2], divs[4], divs[6]]
]
for (var i = 0; i < options.length; i++) {
if (options[i][0] == 'X' && options[i][1] == 'X' && options[i][2] == 'X') {
return 'X'
} else if (options[i][0] == 'O' && options[i][1] == 'O' && options[i][2] == 'O') {
return 'O'
}
}
for (var i = 0; i < 9; i++) {
if (divs[i] == '') {
return false //still moves
}
}
return 'Tie' //no winner and no moves
}
var player = 'O'
var ai = 'X'
$(document).ready(function() {
function playerTurn(i) {
return function() {
var g = getGrid()
var cG = checkGrid(g)
if (!cG) {
if ($(choices[i]).html() == '') {
$(choices[i]).html(player)
var g = getGrid()
var cG = checkGrid(g)
if (cG == player) {
console.log('You win')
} else if (cG == 'Tie') {
console.log('Tie')
} else {
aiTurn()
}
}
}
}
}
for (var i = 0; i < 9; i++) {
$(choices[i]).on('click', playerTurn(i));
}
function score(g, depth) {
var cG = checkGrid(g)
console.log(cG, g)
if (cG == ai) {
return 10 - depth
} else if (cG == player) {
return depth - 10
} else {
return 0
}
}
function minimax(game, depth) {
var g = convertGameToGrid(game)
if (checkGrid(g)) {
return score(g, depth)
}
depth += 1
var scores = []
var moves = []
var availMoves = getAvailMoves(game)
console.log('moves', availMoves)
for (var i = 0; i < availMoves.length; i++) {
var possibleGame = game
if (depth % 2 == 0) {
possibleGame[availMoves[i]][0] = ai
} else {
possibleGame[availMoves[i]][0] = player
}
var m = minimax(possibleGame, depth)
scores.push(m)
console.log('mm: ', depth, i, scores)
moves.push(availMoves[i])
}
//even depths are ai, odd are player
if (depth % 2 == 0) {
var max_score_index = 0
var max_score = -100000000
for (var i = 0; i < scores.length; i++) {
if (scores[i] > max_score) {
max_score_index = i
max_score = scores[i]
}
}
if (depth == 0) { //we need the best move
return moves[max_score_index]
} else { //otherwise this function needs scores
return scores[max_score_index]
}
} else {
var min_score_index = 0
var min_score = 100000000
for (var i = 0; i < scores.length; i++) {
if (scores[i] < min_score) {
min_score_index = i
min_score = scores[i]
}
}
return scores[max_score_index]
}
}
function getAvailMoves(game) {
var moves = []
for (var i = 0; i < game.length; i++) {
if (game[i][0] == '') {
moves.push(game[i][1])
}
}
return moves
}
function aiTurn() {
//Dumb ai
// c = Math.floor(Math.random()*9)
// while ($(choices[c]).html()) {
// c = Math.floor(Math.random()*9)
// }
//new strategy taken from http://neverstopbuilding.com/minimax
console.log('ai')
var c;
game = getGame()
c = minimax(game, -1)
$(choices[c]).html('X')
var g = getGrid()
var cG = checkGrid(g)
if (cG == ai) {
console.log('You lose')
} else if (cG == 'Tie') {
console.log('Tie')
}
}
})
&#13;
#ttt-box {
position: relative;
height: 304px;
width: 304px;
margin: 30px auto;
background-color: #bbb;
border: solid #000 4px;
border-radius: 20%;
}
#l1,
#l2,
#l3,
#l4 {
position: absolute;
background-color: #000;
}
#l1 {
left: 99px;
width: 3px;
height: 296px;
}
#l2 {
left: 199px;
width: 3px;
height: 296px;
}
#l3 {
top: 99px;
width: 296px;
height: 3px;
}
#l4 {
top: 199px;
width: 296px;
height: 3px;
}
#ul,
#um,
#ur,
#ml,
#mm,
#mr,
#ll,
#lm,
#lr {
cursor: pointer;
position: absolute;
width: 99px;
height: 99px;
font-size: 70px;
text-align: center;
}
#ul {
top: 0;
left: 0;
}
#um {
top: 0;
left: 101px;
}
#ur {
top: 0;
left: 201px;
}
#ml {
top: 101px;
left: 0;
}
#mm {
top: 101px;
left: 101px;
}
#mr {
top: 101px;
left: 201px;
}
#ll {
top: 201px;
left: 0;
}
#lm {
top: 201px;
left: 101px;
}
#lr {
top: 201px;
left: 201px;
}
&#13;
<body>
<div class="container">
<div id="content">
<div id="ttt-box">
<div id="l1"></div>
<div id="l2"></div>
<div id="l3"></div>
<div id="l4"></div>
<div id="boxes">
<div id="ul"></div>
<div id="um"></div>
<div id="ur"></div>
<div id="ml"></div>
<div id="mm"></div>
<div id="mr"></div>
<div id="ll"></div>
<div id="lm"></div>
<div id="lr"></div>
</div>
</div>
</div>
</div>
</body>
&#13;
我认为特别突破的代码是以下部分。在第一个玩家移动之后,我认为console.log应打印出8个!时间因为应该采取的所有不同的路径,而是它只打印8次,就像它沿着单一路径一样。
var availMoves = getAvailMoves(game)
console.log('moves',availMoves)
for (var i=0;i<availMoves.length;i++) {
var possibleGame = game
if (depth%2==0) {
possibleGame[availMoves[i]][0] = ai
} else {
possibleGame[availMoves[i]][0] = player
}
var m = minimax(possibleGame,depth)
scores.push(m)
console.log('mm: ', depth,i, scores)
moves.push(availMoves[i])
}
编辑:我注意到的是,有时minimax递归返回未定义。我已经尝试找到原因(参见我的codepen),但我没有成功。
Edit2:它似乎返回undefined,因为它完全跳过这些递归。我仍然无法找到解决这个问题的方法。
答案 0 :(得分:2)
首先提出建议,因为您刚刚开始:学习如何使用调试器。在像这样的情况下它将是非常宝贵的,并且大多数现代浏览器都内置它们。
关于您的问题,我还没有查看您的所有代码,但我注意到有一件事可能导致您的minmax
功能出现问题。在该功能的底部,你有这个代码:
//even depths are ai, odd are player
if (depth % 2 == 0) {
var max_score_index = 0
// snip...
} else {
var min_score_index = 0
// snip...
return scores[max_score_index]
}
请注意,您在max_score_index
块中声明并分配if
,但也在else
块中使用它(不指定它)。这将导致它从else
块返回undefined。