tic-tac-toe-optimal-turn
Version:
Tic-tac-toe optimal turn package based on alpha-beta algorithm.
174 lines (171 loc) • 4.85 kB
JavaScript
;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var src_exports = {};
__export(src_exports, {
getOptimalTurn: () => getOptimalTurn
});
module.exports = __toCommonJS(src_exports);
// src/functions.ts
function getOptimalTurn({ boardSize = 3, gameField, playerSymbol }) {
const winnerArray = generateWinnerCombination(boardSize);
if (!gameField.length || gameField.length < 9 || boardSize < 3) {
throw new Error("Too small values of Game field or board size! ");
}
if (gameField.length !== Math.pow(boardSize, 2)) {
throw new Error("Game field does not match board size!");
}
if (playerSymbol !== "X" && playerSymbol !== "O") {
throw new Error("Incorrect player symbol!");
}
if (gameField.every((cell) => cell !== null)) {
throw new Error("Game field is full!");
}
let bestScore = -Infinity;
let bestMove = 0;
for (let i = 0; i < gameField.length; i++) {
if (gameField[i] === null) {
gameField[i] = playerSymbol;
const score = alphaBeta({
gameField,
depth: 0,
isMaximizing: false,
winnerArray,
alpha: -Infinity,
beta: Infinity,
playerSymbol
});
gameField[i] = null;
if (score > bestScore) {
bestScore = score;
bestMove = i;
}
}
}
return bestMove;
}
function alphaBeta({
gameField,
alpha,
beta,
depth,
winnerArray,
isMaximizing,
playerSymbol
}) {
const opponentSymbol = playerSymbol === "O" ? "X" : "O";
const winner = calculateWinner(gameField, winnerArray);
if (winner === opponentSymbol) {
return -10;
}
if (winner === playerSymbol) {
return 10;
}
if (!gameField.includes(null)) {
return 0;
}
if (isMaximizing) {
let bestScore = -Infinity;
for (let i = 0; i < gameField.length; i++) {
if (gameField[i] === null) {
gameField[i] = playerSymbol;
const score = alphaBeta({
gameField,
depth: depth + 1,
isMaximizing: false,
winnerArray,
alpha,
beta,
playerSymbol
});
gameField[i] = null;
bestScore = Math.max(score, bestScore);
alpha = Math.max(alpha, score);
if (beta <= alpha) {
break;
}
}
}
return bestScore;
} else {
let bestScore = Infinity;
for (let i = 0; i < gameField.length; i++) {
if (gameField[i] === null) {
gameField[i] = opponentSymbol;
const score = alphaBeta({
gameField,
depth: depth + 1,
isMaximizing: true,
winnerArray,
alpha,
beta,
playerSymbol
});
gameField[i] = null;
bestScore = Math.min(score, bestScore);
beta = Math.min(beta, score);
if (beta <= alpha) {
break;
}
}
}
return bestScore;
}
}
function calculateWinner(gameField, winnerArray) {
if (winnerArray.length > 0) {
for (const line of winnerArray) {
const [first, ...rest] = line;
if (gameField[first] && rest.every((index) => gameField[index] === gameField[first])) {
return gameField[first];
}
}
}
return null;
}
function generateWinnerCombination(boardSize) {
const winnerArray = [];
for (let row = 0; row < boardSize; row++) {
const horizontalLine = [];
for (let col = 0; col < boardSize; col++) {
horizontalLine.push(row * boardSize + col);
}
winnerArray.push(horizontalLine);
}
for (let col = 0; col < boardSize; col++) {
const verticalLine = [];
for (let row = 0; row < boardSize; row++) {
verticalLine.push(row * boardSize + col);
}
winnerArray.push(verticalLine);
}
const diagonal1 = [];
const diagonal2 = [];
for (let i = 0; i < boardSize; i++) {
diagonal1.push(i * boardSize + i);
diagonal2.push((boardSize - 1 - i) * boardSize + i);
}
winnerArray.push(diagonal1);
winnerArray.push(diagonal2);
return winnerArray;
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
getOptimalTurn
});