let board = [ ['', '', ''], ['', '', ''], ['', '', ''] ]; let w; // = width / 3; let h; // = height / 3; let ai = 'X'; let human = 'O'; let currentPlayer = human; function setup() { createCanvas(400, 400); w = width / 3; h = height / 3; bestMove(); } function equals3(a, b, c) { return a == b && b == c && a != ''; } function checkWinner() { let winner = null; // horizontal for (let i = 0; i < 3; i++) { if (equals3(board[i][0], board[i][1], board[i][2])) { winner = board[i][0]; } } // Vertical for (let i = 0; i < 3; i++) { if (equals3(board[0][i], board[1][i], board[2][i])) { winner = board[0][i]; } } // Diagonal if (equals3(board[0][0], board[1][1], board[2][2])) { winner = board[0][0]; } if (equals3(board[2][0], board[1][1], board[0][2])) { winner = board[2][0]; } let openSpots = 0; for (let i = 0; i < 3; i++) { for (let j = 0; j < 3; j++) { if (board[i][j] == '') { openSpots++; } } } if (winner == null && openSpots == 0) { return 'tie'; } else { return winner; } } function mousePressed() { if (currentPlayer == human) { // Human make turn let i = floor(mouseX / w); let j = floor(mouseY / h); // If valid turn if (board[i][j] == '') { board[i][j] = human; currentPlayer = ai; bestMove(); } } } function draw() { background(255); strokeWeight(4); line(w, 0, w, height); line(w * 2, 0, w * 2, height); line(0, h, width, h); line(0, h * 2, width, h * 2); for (let j = 0; j < 3; j++) { for (let i = 0; i < 3; i++) { let x = w * i + w / 2; let y = h * j + h / 2; let spot = board[i][j]; textSize(32); let r = w / 4; if (spot == human) { noFill(); ellipse(x, y, r * 2); } else if (spot == ai) { line(x - r, y - r, x + r, y + r); line(x + r, y - r, x - r, y + r); } } } let result = checkWinner(); if (result != null) { noLoop(); let resultP = createP(''); resultP.style('font-size', '32pt'); if (result == 'tie') { resultP.html('Tie!'); } else { resultP.html(`${result} wins!`); } } }
function bestMove() { // bestMove AI要下棋的位置 let bestScore = -Infinity; let move; for (let i = 0; i < 3; i++) { for (let j = 0; j < 3; j++) { if (board[i][j] == '') { // 随便找一个空位置下 board[i][j] = ai; // 使用minmax计算一下下这个空位置的得分怎么样 let score = minimax(board, 0, false); // 计算完得分后将位置还原 board[i][j] = ''; // 如果当前得分大于最佳得分 if (score > bestScore) { // 记录最佳得分 bestScore = score; // 记录最佳得分位置 move = { i, j }; } } } } // 移动到最近得分位置 board[move.i][move.j] = ai; // 切换角色 currentPlayer = human; } let scores = { X: 1, O: -1, tie: 0 }; function minimax(board, depth, isMaximizing, alpha = -Infinity, beta = Infinity) { let result = checkWinner(); if (result !== null) { // 如果是x赢了就返回1,如果是o赢了返回-1,如果是平局返回0 return scores[result]; } if (isMaximizing) { // max层假设是玩家下的时候 for (let i = 0; i < 3; i++) { for (let j = 0; j < 3; j++) { if (board[i][j] == '') { // 假设ai下的位置 board[i][j] = ai; let score = minimax(board, depth + 1, false, alpha, beta); board[i][j] = ''; // 1. 计算最低得分中最大的 alpha = max(score, alpha); // 2. min层只选最小的,如果比最小的还大直接剪枝 // beta是min的上限,当前节点的最低得分里面的最大(alpha)已经大于了之前最小得分,直接剪枝 if (alpha >= beta) return alpha } } } // 返回min里面最大的那个 return alpha } else { // min层假设是ai玩家下的时候 for (let i = 0; i < 3; i++) { for (let j = 0; j < 3; j++) { if (board[i][j] == '') { board[i][j] = human; let score = minimax(board, depth + 1, true, alpha, beta); board[i][j] = ''; // 1. 计算最低得分 beta = min(beta, score) // 2. max层只选最大的,比max层小的不用比较 // alpha代表的是max的下限,如果现在计算的最低得分比之前节点计算的最低得分要小直接剪枝 if (alpha >= beta) return beta } } } // 返回最低得分 return beta } }