我已经编写了一个基于网络的Tic Tac Toe游戏,并为AI选择其动作实施了Minimax算法。一切似乎都在起作用,但是在人类玩家点击第一个方格和玩家的标记(X或O)出现之间有很长的延迟。即使我在第一次调用minimax
之前调用updateBoard
函数,它似乎等待minimax
函数的所有递归在更新电路板之前完成。< / p>
这似乎是异步操作的一个问题,这意味着在它被minimax
的递归调用阻止之前,它没有时间更新HTML。我尝试在updateBoard
函数中添加一个回调参数,并在minimax
完成后执行updateBoard
,但我没有发现任何差异。
我不确定此问题是否与我的Minimax实施有关。这似乎工作正常......计算机永远不会丢失。我认为这可能是我对Javascript中同步操作的误解。
有人可以看看,尤其是$squares.click()
的处理程序,并告诉我你是否看错了什么?
您可以在Codepen上查看它,我已在下面复制了它:http://codepen.io/VAggrippino/pen/ZBqLxO
这很奇怪。当我&#34;运行Code Snippet&#34;在这里,它没有提出同样的问题。我偶然发现了CodePen的问题吗?
$(function(){
var humanToken = 'X';
var computerToken = 'O';
var gameFinished = false;
var $squares = $(".square");
/*
$squares.each(function() {
var id = $(this).attr("id");
$(this).html("<span style='font-size: 1rem;'>" + id + "</style>");
});
*/
var board = [
[null,null,null],
[null,null,null],
[null,null,null],
];
// Give the human player a choice between "X" and "O".
var chooseToken = function(e) {
var $button = $(e.target);
humanToken = $button.html();
computerToken = (humanToken === 'X' ? 'O' : 'X');
$(this).dialog("close");
gameFinished = false;
};
function reset(){
$squares.html("");
board = [
[null,null,null],
[null,null,null],
[null,null,null],
];
$("#tokenDialog").dialog("open");
}
function checkWinner(board) {
var allFull = true;
var tokens = [humanToken, computerToken];
for (var t in tokens) {
var diagonalWin1 = true;
var diagonalWin2 = true;
var token = tokens[t];
/* Since the squares are associated with a two-
dimensional array, these coordinates shouldn't be
thought of as x/y coordinates like a grid.
*/
for (var i = 0; i < 3; i++) {
// Checking 0,0; 1,1; 2,2... top left to bottom right
if (board[i][i] !== token) {
diagonalWin1 = false;
}
// Checking 2,0; 1,1; 0,2... bottom left to top right
if (board[2-i][i] !== token) {
diagonalWin2 = false;
}
var verticalWin = true;
var horizontalWin = true;
for (var j = 0; j < 3; j++) {
/* Checking:
0,0; 0,1; 0,2... horizontal top
1,0; 1,1; 1,2... horizontal middle
2,0; 2,1; 2,2... horizontal bottom
*/
if (board[i][j] !== token) {
horizontalWin = false;
}
/* Checking:
0,0; 1,0; 2,0... vertical left
0,1; 1,1; 2,1... vertical middle
0,2; 1,2; 2,2... vertical right
*/
if (board[j][i] !== token) {
verticalWin = false;
}
// If there are any empty squares, set allFull to
// false, indicating a tie.
if (board[i][j] === null) {
allFull = false;
}
}
if (horizontalWin || verticalWin) {
return token;
}
}
if (diagonalWin1 || diagonalWin2) {
return token;
}
}
// If all the squares are full and we didn't find a
// winner, it's a tie. Return -1.
if (allFull) return -1;
// No winner yet.
return null;
}
function updateBoard() {
// The layout of the board represents the layout of a
// Javascript array. So, these should thought of as row
// and column instead of x/y coordinates of a grid.
for (var r = 0; r < 3; r++) {
for (var c = 0; c < 3; c++) {
var squareId = "#s" + r + '' + c;
$(squareId).html(board[r][c]);
}
}
var result = "";
var winner = checkWinner(board);
if (winner !== null) {
if (winner === humanToken) {
result = "You Win!";
} else if (winner === computerToken) {
result = "The Computer Wins!";
} else if (winner === -1) {
result = "It's a tie!";
}
var $resultDialog = $("#resultDialog");
$resultDialog.dialog("option", "title", result);
$resultDialog.dialog("open");
}
}
$("#reset").click(reset);
$squares.click(function(){
// If the game is already finished, we're just looking at
// the results, so don't do anything.
if (gameFinished) return false;
var $square = $(this);
var boardPosition = $square.attr("id").match(/(\d)(\d)/);
var row = boardPosition[1];
var col = boardPosition[2];
var currentValue = board[row][col];
// If there's not already a token in the clicked square,
// place one and check for a winner.
if (currentValue === null) {
board[row][col] = humanToken;
updateBoard();
board = minimax(board, computerToken)[1];
updateBoard();
}
});
function minimax(board, token) {
// Check the current layout of the board for a winner.
var winner = checkWinner(board);
// The computer wins.
if (winner === computerToken) {
return [10, board];
// The human wins.
} else if (winner === humanToken) {
return [-10, board];
// It's a tie
} else if (winner === -1) {
return [0, board];
// There's no winner yet
} else if (winner === null) {
var nextScore = null;
var nextBoard = null;
// Add a token to the board and check it with a
// recursive call to minimax.
for (var r = 0; r < 3; r++) {
for (var c = 0; c < 3; c++) {
if (board[r][c] === null) {
// Play the current players token, then call
// minimax for the other player.
board[r][c] = token;
var score;
if (token === humanToken) {
score = minimax(board, computerToken)[0];
} else {
score = minimax(board, humanToken)[0];
}
/* This is the computer player trying to win.
If the current player is the computer and the
score is positive or the current player is
human and the score is negative assign a copy
of the board layout as the next board.
*/
if ((token === computerToken && (nextScore === null || score > nextScore)) ||
(token === humanToken && (nextScore === null || score < nextScore)) )
{
nextBoard = board.map(function(arr) {
return arr.slice();
});
nextScore = score;
}
board[r][c] = null;
}
}
}
return [nextScore, nextBoard];
} else {
console.log("Something bad happened.");
console.log("winner: ");
console.log(winner);
}
}
$("#tokenDialog").dialog({
"title": "Choose Your Weapon",
"position": {
"my": "center",
"at": "center",
"of": "#board"
},
"autoOpen": false,
"buttons": {
"X": chooseToken,
"O": chooseToken,
},
"closeOnEscape": false,
"draggable": false,
"modal": true,
"resizable": false,
"show": true,
"classes": {
"ui-dialog-buttonpane": "tokenDialog-buttonpane",
},
});
$("#resultDialog").dialog({
"position": {
"my": "center",
"at": "center",
"of": "#board"
},
"autoOpen": false,
"closeOnEscape": false,
"draggable": false,
"modal": true,
"resizable": false,
"show": true,
"buttons": {
"Play Again": function(){
$(this).dialog("close");
reset();
}
},
"classes": {
}
});
});
&#13;
body, html {
height: 100%;
margin: 0;
}
body {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
background: linear-gradient(45deg, #dca 12%, transparent 0, transparent 88%, #dca 0),
linear-gradient(135deg, transparent 37%, #a85 0, #a85 63%, transparent 0),
linear-gradient(45deg, transparent 37%, #dca 0, #dca 63%, transparent 0) #753;
background-size: 25px 25px;
}
div#board {
background-color: white;
background-image: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/29229/gimp_pattern_marble1.png);
background-repeat: repeat;
width: 21rem;
padding: 0.25rem;
margin: 1rem;
border: 10px solid transparent;
border-radius: 2rem;
position: relative;
background-clip: content-box;
box-shadow: 0px 5px 10px 3px rgba(0, 0, 0, 0.75);
}
div#board::after {
content: "";
z-index: -1;
position: absolute;
top: -10px;
right: -10px;
bottom: -10px;
left: -10px;
border-radius: 2rem;
background-image: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/29229/gimp_pattern_leather.png);
}
#reset {
font-size: 2em;
border-radius: 0.5rem;
}
div.square {
width: 7rem;
height: 7rem;
font-size: 7rem;
font-family: sans-serif;
float: left;
border-width: 0 2px 2px 0;
border-color: black;
border-style: solid;
box-sizing: border-box;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
}
#s02, #s12, #s22 {
border-right: none;
}
#s20, #s21, #s22 {
border-bottom: none;
}
.tokenDialog-buttonset {
width: 100%;
display: flex;
justify-content: center;
}
.ui-dialog-buttonpane.tokenDialog-buttonpane {
padding: 0.5em;
}
.no-close .ui-dialog-titlebar-close {
display: none;
}
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.0/jquery-ui.min.js"></script>
<link rel="stylesheet" href="https://code.jquery.com/ui/1.12.1/themes/ui-darkness/jquery-ui.css">
<!--
The board configuration represents the layout of a Javascript
array, not a typical grid. so, the square IDs should not be
thought of as x/y coordinates.
-->
<div id="board">
<div class="row">
<div class="square" id="s00"></div>
<div class="square" id="s01"></div>
<div class="square" id="s02"></div>
</div>
<div class="row">
<div class="square" id="s10"></div>
<div class="square" id="s11"></div>
<div class="square" id="s12"></div>
</div>
<div class="row">
<div class="square" id="s20"></div>
<div class="square" id="s21"></div>
<div class="square" id="s22"></div>
</div>
</div>
<div id="buttons">
<button id="reset">Reset Game</button>
</div>
<div id="tokenDialog"></div>
<div id="resultDialog"></div>
&#13;