У меня есть игра Tic Tac Toe 3D, которую я хочу оптимизировать, потому что в этот момент он разбивает мой веб-браузер. Он использует алгоритм альфа-бета-обрезки MiniMax, и я разделил код на 3 разных файла:
TTT3D
| -js
| ---init.js #, где я определяю все переменные (как winCombos, человек, компьютер и т.д.),
| ---worker.js # алгоритм miniMax и в конце, я отправляю в качестве сообщения переменную choice
| ---ticAlpha.js # рабочий построен и пытается получить ответ
| -index.html
Вот код:
index.html
<!DOCTYPE html>
<html>
<head>
<title>Tic Tac Toe 3D</title>
<link rel="stylesheet" type="text/css" href="css/main.css">
<link rel="stylesheet" type="text/css" href="css/bootstrap.min.css">
<meta charset="utf-8">
</head>
<body>
<section id="intro">
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2" style="text-align: center;">
<h1>Hi there! Let play <b>Tic Tac Toe</b>! Choose your player!</h1>
<button value="X" id="X" onclick="startGame('X')">X</button>
<button value="O" id="O" onclick="startGame('O')">O</button>
</div>
</div>
</div>
</section>
<section id="board" class="hidden">
<div class="container">
<div class="row bottom">
<div class="col-md-3"></div>
<div class="col-md-2 boxContent">
<ul>
<li>
<h2>X</h2>
</li>
<li style="float: right !important;">
<input type="text" name="xPlayerScore" id="xPlayerScore" value="-" readonly>
</li>
</ul>
</div>
<div class="col-md-2 boxContent">
<ul>
<li>
<h2>O</h2>
</li>
<li style="float: right !important;">
<input type="text" name="oPlayerScore" id="oPlayerScore" value="-" readonly>
</li>
</ul>
</div>
<button class="col-md-2" onclick="playAgain()" style="margin-left: 7px;">
<img src="images/again.png" width="16">
Play Again
</button>
<div class="col-md-3"></div>
</div>
</div>
<div class="canvas1">
<div class="row">
<div class="tile" boardcol="0" boardrow="0" boardlevel="0" onclick="makeMove(0)" id="0"></div>
<div class="tile" boardcol="1" boardrow="0" boardlevel="0" onclick="makeMove(1)" id="1"></div>
<div class="tile" boardcol="2" boardrow="0" boardlevel="0"onclick="makeMove(2)" id="2"></div>
</div>
<div class="row">
<div class="tile" boardcol="0" boardrow="1" boardlevel="0"onclick="makeMove(3)" id="3"></div>
<div class="tile" boardcol="1" boardrow="1" boardlevel="0"onclick="makeMove(4)" id="4"></div>
<div class="tile" boardcol="2" boardrow="1" boardlevel="0"onclick="makeMove(5)" id="5"></div>
</div>
<div class="row">
<div class="tile" boardcol="0" boardrow="2" boardlevel="0"onclick="makeMove(6)" id="6"></div>
<div class="tile" boardcol="1" boardrow="2" boardlevel="0"onclick="makeMove(7)" id="7"></div>
<div class="tile" boardcol="2" boardrow="2" boardlevel="0"onclick="makeMove(8)" id="8"></div>
</div>
</div>
<div class="canvas2">
<div class="row">
<div class="tile" boardcol="0" boardrow="0" boardlevel="1"onclick="makeMove(9)" id="9"></div>
<div class="tile" boardcol="1" boardrow="0" boardlevel="1"onclick="makeMove(10)" id="10"></div>
<div class="tile" boardcol="2" boardrow="0" boardlevel="1"onclick="makeMove(11)" id="11"></div>
</div>
<div class="row">
<div class="tile" boardcol="0" boardrow="1" boardlevel="1"onclick="makeMove(12)" id="12"></div>
<div class="tile" boardcol="1" boardrow="1" boardlevel="1"onclick="makeMove(13)" id="13"></div>
<div class="tile" boardcol="2" boardrow="1" boardlevel="1"onclick="makeMove(14)" id="14"></div>
</div>
<div class="row">
<div class="tile" boardcol="0" boardrow="2" boardlevel="1"onclick="makeMove(15)" id="15"></div>
<div class="tile" boardcol="1" boardrow="2" boardlevel="1"onclick="makeMove(16)" id="16"></div>
<div class="tile" boardcol="2" boardrow="2" boardlevel="1"onclick="makeMove(17)" id="17"></div>
</div>
</div>
<div class="canvas3">
<div class="row">
<div class="tile" boardcol="0" boardrow="0" boardlevel="2"onclick="makeMove(18)" id="18"></div>
<div class="tile" boardcol="1" boardrow="0" boardlevel="2"onclick="makeMove(19)" id="19"></div>
<div class="tile" boardcol="2" boardrow="0" boardlevel="2"onclick="makeMove(20)" id="20"></div>
</div>
<div class="row">
<div class="tile" boardcol="0" boardrow="1" boardlevel="2"onclick="makeMove(21)" id="21"></div>
<div class="tile" boardcol="1" boardrow="1" boardlevel="2"onclick="makeMove(22)" id="22"></div>
<div class="tile" boardcol="2" boardrow="1" boardlevel="2"onclick="makeMove(23)" id="23"></div>
</div>
<div class="row">
<div class="tile" boardcol="0" boardrow="2" boardlevel="2"onclick="makeMove(24)" id="24"></div>
<div class="tile" boardcol="1" boardrow="2" boardlevel="2"onclick="makeMove(25)" id="25"></div>
<div class="tile" boardcol="2" boardrow="2" boardlevel="2"onclick="makeMove(26)" id="26"></div>
</div>
</div>
</section>
<script src="https://code.jquery.com/jquery-3.1.1.js"
integrity="sha256-16cdPddA6VdVInumRGo6IbivbERE8p7CQR3HzTBuELA="
crossorigin="anonymous"></script>
<script src="js/init.js"></script>
<script src="js/ticAlpha.js"></script>
</body>
</html>
init.js
var winningCombos = new Array();
winningCombos[0] = [0, 1, 2];
winningCombos[1] = [3, 4, 5];
winningCombos[2] = [6, 7, 8];
// Per columns
winningCombos[3] = [0, 3, 6];
winningCombos[4] = [1, 4, 7];
winningCombos[5] = [2, 5, 8];
// Diagonals
winningCombos[6] = [0, 4, 8];
winningCombos[7] = [2, 4, 6];
// Second board
//Per lines
winningCombos[8] = [9, 10, 11];
winningCombos[9] = [12, 13, 14];
winningCombos[10] = [15, 16, 17];
// Per columns
winningCombos[11] = [9, 12, 15];
winningCombos[12] = [10, 13, 16];
winningCombos[13] = [11, 14, 17];
// Diagonals
winningCombos[14] = [9, 13, 17];
winningCombos[15] = [11, 13, 15];
// Third board
// Per lines
winningCombos[16] = [18, 19, 20];
winningCombos[17] = [21, 22, 23];
winningCombos[18] = [24, 25, 26];
// Per columns
winningCombos[19] = [18, 21, 24];
winningCombos[20] = [19, 22, 25];
winningCombos[21] = [20, 23, 26];
// Diagonals
winningCombos[22] = [18, 22, 26];
winningCombos[23] = [20, 22, 24];
// 3D Winning
winningCombos[24] = [0, 13, 26];
winningCombos[25] = [20, 13, 6];
// Per lines
winningCombos[26] = [0, 10, 20];
winningCombos[27] = [3, 13, 23];
winningCombos[28] = [6, 16, 26];
var free = ' ';
var boardSize = 27;
var board = new Array();
var activePlayer = 'Human';
var i;
var choice;
var human;
var humanTurn;
var computer;
var computerTurn;
var humanWin = 0;
var computerWin = 0;
$(document).ready(function() {
$("button").click(function(){
$("#intro").addClass("hidden");
$("#board").removeClass("hidden");
});
});
function startGame(player) {
for (i = 0; i < boardSize; i += 1) {
board[i] = free;
}
activePlayer = 'Human';
if (player == "X") {
human = "X";
humanTurn = "<p>X</p>";
computer = "O";
computerTurn = "<p>O</p>";
} else {
human = "O";
humanTurn = "<p>O</p>";
computer = "X";
computerTurn = "<p>X</p>";
}
}
ticAlpha.js
function makeMove(pos) {
if (board[pos] === free && !gameOver(board)) {
board[pos] = human;
document.getElementById(pos).innerHTML = humanTurn;
if (!gameOver(board)) {
activePlayer = 'Computer';
makeComputerMove();
}
}
}
function makeComputerMove() {
// miniMax(board, 0, -Infinity, +Infinity);
var worker = new Worker('worker.js');
worker.postMessage('its time');
worker.onmessage = function(event) {
var move = event.data;
};
board[move] = computer;
document.getElementById(move).innerHTML = computerTurn;
choice = [];
activePlayer = 'Human';
}
function score(possibleGame) {
var score = getWinner(possibleGame);
if (score === 3) {
return 0;
} else if (score === 1) {
return -1;
} else if (score === 2) {
return 1;
}
}
function miniMax(node, depth, alpha, beta) {
if (getWinner(node) !== 0) {
return score(node);
}
depth += 1;
// var scores = new Array();
// var moves = new Array();
var availableMoves = getAvailableMoves(node);
var move, result, possibleGame;
if (activePlayer === 'Computer'){
for (var i = 0; i < availableMoves.length; i += 1) {
move = availableMoves[i];
possibleGame = generateNewGame(move, node);
result = miniMax(possibleGame, depth, alpha, beta);
node = undoMove(node, move);
if (result > alpha) {
alpha = result;
if (depth === 1) {
choice = move;
} else if (alpha >= beta) {
return alpha;
}
}
}
return alpha;
} else if (activePlayer === 'Human') {
for (var i = 0; i < availableMoves.length; i += 1) {
move = availableMoves[i];
possibleGame = generateNewGame(move, node);
result = miniMax(possibleGame, depth, alpha, beta);
node = undoMove(node, move);
if (result < beta) {
beta = result;
if (depth === 1) {
choice = move;
} else if (alpha >= beta) {
return beta;
}
}
}
return beta;
}
}
function undoMove(possibleGame, move) {
possibleGame[move] = free;
changePlayerTurn();
return possibleGame;
}
function getAvailableMoves(tempBoard) {
var availableMoves = new Array();
for (var i = 0; i < boardSize; i += 1) {
if (board[i] === free) {
availableMoves.push(i);
}
}
return availableMoves;
}
function generateNewGame(move, possibleGame) {
var piece = changePlayerTurn();
possibleGame[move] = piece;
return possibleGame;
}
function changePlayerTurn() {
var turn;
if (activePlayer === 'Computer') {
turn = computer;
activePlayer = 'Human';
} else {
turn = human;
activePlayer = 'Computer';
}
return turn;
}
function gameOver(tempBoard) {
if (getWinner(tempBoard) === 0) {
return 0;
} else if (getWinner(tempBoard) === 1) {
alert("You won!");
humanWin += 1;
if (human === "X") {
document.getElementById("xPlayerScore").value = humanWin;
} else {
document.getElementById("oPlayerScore").value = humanWin;
}
} else if (getWinner(tempBoard) === 2) {
alert("Computer won!");
computerWin += 1;
if (computer === "X") {
document.getElementById("xPlayerScore").value = computerWin;
} else {
document.getElementById("oPlayerScore").value = computerWin;
}
} else if (getWinner(tempBoard) === 3) {
alert("The game was a draw!");
}
return 1;
}
function getWinner(tempBoard) {
for (i = 0; i < winningCombos.length; i += 1) {
if (tempBoard[winningCombos[i][0]] === human &&
tempBoard[winningCombos[i][1]] === human &&
tempBoard[winningCombos[i][2]] === human) {
return 1; // human won
} else if (tempBoard[winningCombos[i][0]] === computer &&
tempBoard[winningCombos[i][1]] === computer &&
tempBoard[winningCombos[i][2]] === computer) {
return 2; // computer won
}
}
if (tempBoard.indexOf(free) >= 0) {
return 0; // not finished yet
}
return 3; // the game was a draw
}
function gameOver(tempBoard) {
if (getWinner(tempBoard) === 0) {
return 0;
} else if (getWinner(tempBoard) === 1) {
alert("You won!");
humanWin += 1;
if (human === "X") {
document.getElementById("xPlayerScore").value = humanWin;
} else {
document.getElementById("oPlayerScore").value = humanWin;
}
} else if (getWinner(tempBoard) === 2) {
alert("Computer won!");
computerWin += 1;
if (computer === "X") {
document.getElementById("xPlayerScore").value = computerWin;
} else {
document.getElementById("oPlayerScore").value = computerWin;
}
} else if (getWinner(tempBoard) === 3) {
alert("The game was a draw!");
}
return 1;
}
function getWinner(tempBoard) {
for (i = 0; i < winningCombos.length; i += 1) {
if (tempBoard[winningCombos[i][0]] === human &&
tempBoard[winningCombos[i][1]] === human &&
tempBoard[winningCombos[i][2]] === human) {
return 1; // human won
} else if (tempBoard[winningCombos[i][0]] === computer &&
tempBoard[winningCombos[i][1]] === computer &&
tempBoard[winningCombos[i][2]] === computer) {
return 2; // computer won
}
}
if (tempBoard.indexOf(free) >= 0) {
return 0; // not finished yet
}
return 3; // the game was a draw
}
function playAgain() {
if (getWinner(board) != 0) {
for (var j = 0; j < boardSize; j += 1) {
document.getElementById(j).innerHTML = "";
}
resetBoard();
}
}
function resetBoard() {
for (i = 0; i < boardSize; i += 1) {
board[i] = free;
}
}
worker.js
function miniMax(node, depth, alpha, beta) {
if (getWinner(node) !== 0) {
return score(node);
}
depth += 1;
// var scores = new Array();
// var moves = new Array();
var availableMoves = getAvailableMoves(node);
var move, result, possibleGame;
if (activePlayer === 'Computer'){
for (var i = 0; i < availableMoves.length; i += 1) {
move = availableMoves[i];
possibleGame = generateNewGame(move, node);
result = miniMax(possibleGame, depth, alpha, beta);
node = undoMove(node, move);
if (result > alpha) {
alpha = result;
if (depth === 1) {
choice = move;
} else if (alpha >= beta) {
return alpha;
}
}
}
return alpha;
} else if (activePlayer === 'Human') {
for (var i = 0; i < availableMoves.length; i += 1) {
move = availableMoves[i];
possibleGame = generateNewGame(move, node);
result = miniMax(possibleGame, depth, alpha, beta);
node = undoMove(node, move);
if (result < beta) {
beta = result;
if (depth === 1) {
choice = move;
} else if (alpha >= beta) {
return beta;
}
}
}
return beta;
}
}
function undoMove(possibleGame, move) {
possibleGame[move] = free;
changePlayerTurn();
return possibleGame;
}
function getAvailableMoves(tempBoard) {
var availableMoves = new Array();
for (var i = 0; i < boardSize; i += 1) {
if (board[i] === free) {
availableMoves.push(i);
}
}
return availableMoves;
}
function generateNewGame(move, possibleGame) {
var piece = changePlayerTurn();
possibleGame[move] = piece;
return possibleGame;
}
function changePlayerTurn() {
var turn;
if (activePlayer === 'Computer') {
turn = computer;
activePlayer = 'Human';
} else {
turn = human;
activePlayer = 'Computer';
}
return turn;
}
function gameOver(tempBoard) {
if (getWinner(tempBoard) === 0) {
return 0;
} else if (getWinner(tempBoard) === 1) {
alert("You won!");
humanWin += 1;
if (human === "X") {
document.getElementById("xPlayerScore").value = humanWin;
} else {
document.getElementById("oPlayerScore").value = humanWin;
}
} else if (getWinner(tempBoard) === 2) {
alert("Computer won!");
computerWin += 1;
if (computer === "X") {
document.getElementById("xPlayerScore").value = computerWin;
} else {
document.getElementById("oPlayerScore").value = computerWin;
}
} else if (getWinner(tempBoard) === 3) {
alert("The game was a draw!");
}
return 1;
}
function getWinner(tempBoard) {
for (i = 0; i < winningCombos.length; i += 1) {
if (tempBoard[winningCombos[i][0]] === human &&
tempBoard[winningCombos[i][1]] === human &&
tempBoard[winningCombos[i][2]] === human) {
return 1; // human won
} else if (tempBoard[winningCombos[i][0]] === computer &&
tempBoard[winningCombos[i][1]] === computer &&
tempBoard[winningCombos[i][2]] === computer) {
return 2; // computer won
}
}
if (tempBoard.indexOf(free) >= 0) {
return 0; // not finished yet
}
return 3; // the game was a draw
}
onmessage = function(e) {
miniMax(board, 0, -Infinity, Infinity);
postMessage(choice);
};
Проблема в том, что переменная move
всегда не определена, и даже если я выполняю console.log()
внутри функции worker.onmessage
, она ничего не отображает.
Вероятнее всего, он неправильно понял концепцию использования веб-рабочего и должен использоваться, но я пытаюсь решить более 24 часов, и это похоже на прогресс 0. По достоинству оцените вашу помощь. Большое спасибо!
Ну, одна проблема заключается в том, что похоже, что вы ожидаете, что работник получит доступ ко всем переменным, которые доступны в основном потоке, и не будет. Вы должны поддерживать состояние платы как в основном потоке, так и в рабочем месте и синхронизировать их с помощью сообщений. Это еще одна причина, чтобы сделать работника впереди и сохранить один и тот же на протяжении всей жизни приложения.
Рассмотрите свой запрос на miniMax(board, 0, -Infinity, Infinity);
в worker.js
. Этот вызов, вероятно, вызывает ошибку в потоке, потому что board
не определена, поэтому miniMax
пытается получить к ней доступ в теле функции и бросает.
Кроме того, вы также вызываете postMessage(choice)
у своего работника, но choice
не определен нигде.
worker
в файле init.js
и также отправил board
из ticAlpha.js
в worker.js
с postMessage
. В worker.js
я определяю новую переменную var board = e.data;
, И choice
определен сейчас. Это все еще не хочет работать. Спасибо за помощь!
move
переменной всегда не определено. (ReferenceError: перемещение не определено). Даже когда я выполняю console.log () внутри функцииworker.onmessage
, он ничего не отображает.