269 lines
5.6 KiB
JavaScript
269 lines
5.6 KiB
JavaScript
var cellWidth = 0;
|
|
var gameOver = false;
|
|
var humanFirst;
|
|
var usePruning;
|
|
var gameBoard = [
|
|
['', '', ''],
|
|
['', '', ''],
|
|
['', '', '']
|
|
];
|
|
var currentPlayer = 0;
|
|
var players = ['X', 'O'];
|
|
var scores = {
|
|
X: 1,
|
|
Tie: 0,
|
|
O: -1
|
|
};
|
|
var eh = true;
|
|
|
|
function setup() {
|
|
var canvas = createCanvas(500, 500);
|
|
cellWidth = floor(width / 3);
|
|
canvas.parent('sketch-holder');
|
|
humanFirst = document.getElementById("humanFirst").checked;
|
|
usePruning = document.getElementById("usePruning").checked;
|
|
}
|
|
|
|
function draw() {
|
|
background(220);
|
|
|
|
strokeWeight(4);
|
|
line(0, height / 3, width, height / 3);
|
|
line(0, height - height / 3, width, height - height / 3);
|
|
line(width / 3, 0, width / 3, height);
|
|
line(width - width / 3, 0, width - width / 3, height);
|
|
|
|
if(!humanFirst) nextTurn();
|
|
|
|
noLoop();
|
|
}
|
|
|
|
function nextTurn() {
|
|
let bestScore = players[currentPlayer] == 'X' ? -Infinity : Infinity;
|
|
let move;
|
|
|
|
for(let i = 0; i < 3; i++)
|
|
for(let j = 0; j < 3; j++) {
|
|
if(gameBoard[i][j] == ''){
|
|
gameBoard[i][j] = players[currentPlayer];
|
|
let score;
|
|
|
|
if(!usePruning) score = minimax(gameBoard, !(players[currentPlayer] == 'X'));
|
|
else score = minimaxPrune(gameBoard, -Infinity, Infinity, !(players[currentPlayer] == 'X'));
|
|
gameBoard[i][j] = '';
|
|
|
|
if(players[currentPlayer] == 'X') {
|
|
if(score > bestScore){
|
|
bestScore = score;
|
|
|
|
move = {i, j};
|
|
}
|
|
}
|
|
else {
|
|
if(score < bestScore){
|
|
bestScore = score;
|
|
|
|
move = {i, j};
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
gameBoard[move.i][move.j] = players[currentPlayer];
|
|
drawPlayer(move.j, move.i, cellWidth, players[currentPlayer]);
|
|
currentPlayer = (currentPlayer + 1) % players.length;
|
|
}
|
|
|
|
function minimaxPrune(board, alpha, beta, isMaximizing){
|
|
let result = checkWinner(board);
|
|
|
|
if(result != null) {
|
|
return scores[result];
|
|
}
|
|
|
|
if(isMaximizing){
|
|
let bestScore = -Infinity;
|
|
|
|
for(let i = 0; i < 3; i++)
|
|
for(let j = 0; j < 3; j++) {
|
|
if(board[i][j] == ''){
|
|
board[i][j] = 'X';
|
|
let score = minimaxPrune(board, alpha, beta, false);
|
|
board[i][j] = '';
|
|
|
|
bestScore = max(score, bestScore);
|
|
alpha = max(alpha, bestScore);
|
|
|
|
if(beta <= alpha)
|
|
return bestScore;
|
|
}
|
|
}
|
|
return bestScore;
|
|
}
|
|
else {
|
|
let bestScore = Infinity;
|
|
|
|
for(let i = 0; i < 3; i++)
|
|
for(let j = 0; j < 3; j++) {
|
|
if(board[i][j] == ''){
|
|
board[i][j] = 'O';
|
|
let score = minimaxPrune(board, alpha, beta, true);
|
|
board[i][j] = '';
|
|
|
|
bestScore = min(score, bestScore);
|
|
beta = min(beta, bestScore);
|
|
|
|
if(beta <= alpha)
|
|
return bestScore;
|
|
}
|
|
}
|
|
return bestScore;
|
|
}
|
|
}
|
|
|
|
function minimax(board, isMaximizing){
|
|
let result = checkWinner(board);
|
|
|
|
if(result != null) {
|
|
return scores[result];
|
|
}
|
|
|
|
if(isMaximizing){
|
|
let bestScore = -Infinity;
|
|
|
|
for(let i = 0; i < 3; i++)
|
|
for(let j = 0; j < 3; j++) {
|
|
if(board[i][j] == ''){
|
|
board[i][j] = 'X';
|
|
let score = minimax(board, false);
|
|
board[i][j] = '';
|
|
|
|
bestScore = max(score, bestScore);
|
|
}
|
|
}
|
|
return bestScore;
|
|
}
|
|
else {
|
|
let bestScore = Infinity;
|
|
|
|
for(let i = 0; i < 3; i++)
|
|
for(let j = 0; j < 3; j++) {
|
|
if(board[i][j] == ''){
|
|
board[i][j] = 'O';
|
|
let score = minimax(board, true);
|
|
board[i][j] = '';
|
|
|
|
bestScore = min(score, bestScore);
|
|
}
|
|
}
|
|
return bestScore;
|
|
}
|
|
}
|
|
|
|
function checkWinner(board, showEnd = false){
|
|
let winner = null;
|
|
|
|
for(let i = 0; i < 3; i++){
|
|
if(board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][0] != ''){
|
|
winner = board[i][0];
|
|
let y = 0;
|
|
|
|
if(i == 0) y = cellWidth / 2;
|
|
else y = cellWidth * i + cellWidth / 2;
|
|
|
|
if(showEnd) line(0, y, width, y);
|
|
}
|
|
}
|
|
|
|
for(let i = 0; i < 3; i++){
|
|
if(board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ''){
|
|
winner = board[0][i];
|
|
let x = 0;
|
|
|
|
if(i == 0) x = cellWidth / 2;
|
|
else x = cellWidth * i + cellWidth / 2;
|
|
|
|
if(showEnd) line(x, 0, x, width);
|
|
}
|
|
}
|
|
|
|
if(board[0][0] != '' && board[0][0] == board[1][1] && board[2][2] == board[0][0]){
|
|
winner = board[0][0];
|
|
if(showEnd) line(0, 0, width, width);
|
|
}
|
|
else if(board[2][0] != '' && board[2][0] == board[1][1] && board[0][2] == board[2][0]){
|
|
winner = board[2][0];
|
|
if(showEnd) line(width, 0, 0, width);
|
|
}
|
|
|
|
let openMoves = 0;
|
|
|
|
for(let i = 0; i < 3; i++)
|
|
for(let j = 0; j < 3; j++)
|
|
if(board[i][j] == '')
|
|
openMoves++;
|
|
|
|
if(!winner && openMoves == 0)
|
|
return "Tie";
|
|
|
|
return winner;
|
|
}
|
|
|
|
function mouseClicked(){
|
|
if(gameOver) {
|
|
print("Game over!");
|
|
return;
|
|
}
|
|
|
|
let x = floor(mouseX / cellWidth);
|
|
let y = floor(mouseY / cellWidth);
|
|
|
|
if(gameBoard[y][x] == ''){
|
|
drawPlayer(x, y, cellWidth, players[currentPlayer]);
|
|
gameBoard[y][x] = players[currentPlayer];
|
|
|
|
let result = checkWinner(gameBoard, true);
|
|
|
|
if(result != null) {
|
|
showWinner(result);
|
|
return;
|
|
}
|
|
|
|
currentPlayer = (currentPlayer + 1) % players.length;
|
|
|
|
nextTurn();
|
|
|
|
result = checkWinner(gameBoard, true);
|
|
|
|
if(result != null)
|
|
showWinner(result);
|
|
}
|
|
}
|
|
|
|
function showWinner(player) {
|
|
gameOver = true;
|
|
|
|
if(player == "Tie")
|
|
document.getElementById("statusLabel").innerHTML = `<h1>Its a tie. (Pruning? ${usePruning})</h1>`;
|
|
else
|
|
document.getElementById("statusLabel").innerHTML = `<h1>Game over ${player} wins! (Pruning? ${usePruning})</h1>`;
|
|
}
|
|
|
|
function drawPlayer(x, y, w, player) {
|
|
strokeWeight(2);
|
|
|
|
if(player == 'O'){
|
|
fill(220);
|
|
ellipse(w / 2 + w * x, w / 2 + w * y, floor(width / 4));
|
|
}
|
|
else if (player == 'X'){
|
|
let newX = w * x;
|
|
let newY = w * y;
|
|
let trim = w * 0.90;
|
|
line(newX + trim * 0.15, newY + trim * 0.15, newX + trim, newY + trim);
|
|
line(newX + trim * 0.15, newY + trim, newX + trim, newY + trim * 0.15);
|
|
}
|
|
|
|
strokeWeight(10);
|
|
}
|