Skip to content

Commit

Permalink
Update Tic-Tac-Toe-YZ.js
Browse files Browse the repository at this point in the history
  • Loading branch information
bochk0 authored Jul 18, 2024
1 parent 19499ff commit 82ed8c1
Showing 1 changed file with 143 additions and 34 deletions.
177 changes: 143 additions & 34 deletions games/Tic-Tac-Toe-YZ.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
/*
@title: Tic-Tac-Toe
@author: bochk0
@author: bochk0
@tags: ['classic']
@addedOn: 2024-07-17
*/

const PLAYER_Y = "Y";
const PLAYER_Z = "Z";
const POINTER = "P";
const POWERUP = "U";
const DOUBLE_MOVE = "D";

const EMPTY_TILE = "E";
const END_TILE = "T";
Expand Down Expand Up @@ -63,6 +65,40 @@ setLegend(
.....444444.....
................
................
................`],
[POWERUP, bitmap`
................
................
.......66.......
......6666......
......6666......
.....666666.....
..666666666666..
..666666666666..
...6666666666...
....66666666....
.....666666.....
....666..666....
...666....666...
..666......666..
................
................`],
[DOUBLE_MOVE, bitmap`
................
................
...HHHHH........
...H...HH.......
...H.H..HH......
...H.HH..HH.....
...H.H.H..HH....
...H.H..H..HH...
...H.H..H..HH...
...H.H.H..HH....
...H.HH..HH.....
...H.H..HH......
...H...HH.......
...HHHHH........
................
................`],
[EMPTY_TILE, bitmap`
3333333333333333
Expand Down Expand Up @@ -113,6 +149,10 @@ let gameBoard = [
[" ", " ", " "]
];
let gameFinished = false;
let aiLoseTurn = false;
let doubleMove = false;
let extraMove = false;
let aiSkipMoves = 0;

function hasWinner(board) {
const symbol = currentPlayer ? PLAYER_Y : PLAYER_Z;
Expand Down Expand Up @@ -156,6 +196,9 @@ TTT`);
function resetGame() {
gameFinished = false;
currentPlayer = true;
aiLoseTurn = false;
doubleMove = false;
extraMove = false;
gameBoard = [
[" ", " ", " "],
[" ", " ", " "],
Expand All @@ -169,44 +212,83 @@ EEE`);
addSprite(1, 1, POINTER);
}

function aiMove() {
const emptyCells = [];
function spawnPowerUp() {
if (Math.random() < 0.1) { // 10% chance to spawn a power-up after each move
const emptyCells = [];
for (let y = 0; y < 3; y++) {
for (let x = 0; x < 3; x++) {
if (gameBoard[y][x] === " ") {
emptyCells.push([y, x]);
}
}
}
if (emptyCells.length > 0) {
const [y, x] = emptyCells[Math.floor(Math.random() * emptyCells.length)];
const powerUpType = Math.random() < 0.5 ? POWERUP : DOUBLE_MOVE;
addSprite(x, y, powerUpType);
}
}
}

function minimax(board, depth, isMaximizing) {
if (hasWinner(board)) {
return isMaximizing ? -1 : 1;
}
if (isDraw(board)) {
return 0;
}

let bestScore = isMaximizing ? -Infinity : Infinity;
const symbol = isMaximizing ? PLAYER_Z : PLAYER_Y;

for (let y = 0; y < 3; y++) {
for (let x = 0; x < 3; x++) {
if (gameBoard[y][x] === " ") {
emptyCells.push([y, x]);
if (board[y][x] === " ") {
board[y][x] = symbol;
const score = minimax(board, depth + 1, !isMaximizing);
board[y][x] = " ";
bestScore = isMaximizing ? Math.max(score, bestScore) : Math.min(score, bestScore);
}
}
}
return bestScore;
}

// AI's simple strategy: winning move, blocking move, random move
for (let cell of emptyCells) {
const [y, x] = cell;
gameBoard[y][x] = PLAYER_Z;
if (hasWinner(gameBoard)) {
addSprite(x, y, PLAYER_Z);
checkGameEnd();
return;
function aiMove() {
if (aiLoseTurn || aiSkipMoves > 0) {
if (aiLoseTurn) {
aiLoseTurn = false;
} else {
aiSkipMoves--;
}
gameBoard[y][x] = " ";
addText("AI loses a turn!", { y: 1, color: color`2` });
setTimeout(() => clearText(), 2000);
currentPlayer = !currentPlayer;
return;
}

for (let cell of emptyCells) {
const [y, x] = cell;
gameBoard[y][x] = PLAYER_Y;
if (hasWinner(gameBoard)) {
gameBoard[y][x] = PLAYER_Z;
addSprite(x, y, PLAYER_Z);
checkGameEnd();
return;
let bestScore = -Infinity;
let move;

for (let y = 0; y < 3; y++) {
for (let x = 0; x < 3; x++) {
if (gameBoard[y][x] === " ") {
gameBoard[y][x] = PLAYER_Z;
const score = minimax(gameBoard, 0, false);
gameBoard[y][x] = " ";
if (score > bestScore) {
bestScore = score;
move = { y, x };
}
}
}
gameBoard[y][x] = " ";
}

const [y, x] = emptyCells[Math.floor(Math.random() * emptyCells.length)];
gameBoard[y][x] = PLAYER_Z;
addSprite(x, y, PLAYER_Z);
checkGameEnd();
if (move) {
gameBoard[move.y][move.x] = PLAYER_Z;
addSprite(move.x, move.y, PLAYER_Z);
checkGameEnd();
}
}

function checkGameEnd() {
Expand All @@ -216,7 +298,7 @@ function checkGameEnd() {
endGame("It's a draw!");
} else {
currentPlayer = !currentPlayer;
if (!currentPlayer) {
if (!currentPlayer && !extraMove) {
aiMove();
}
}
Expand All @@ -241,13 +323,40 @@ onInput("d", () => {
onInput("i", () => {
if (gameFinished || gameBoard[getFirst(POINTER).y][getFirst(POINTER).x] !== " ") return;

const symbol = currentPlayer ? PLAYER_Y : PLAYER_Z;
addSprite(getFirst(POINTER).x, getFirst(POINTER).y, symbol);
gameBoard[getFirst(POINTER).y][getFirst(POINTER).x] = symbol;

checkGameEnd();
const pointer = getFirst(POINTER);
const tiles = getTile(pointer.x, pointer.y);

if (tiles.some(tile => tile.type === POWERUP)) {
const powerUp = tiles.find(tile => tile.type === POWERUP);
if (powerUp) {
powerUp.remove();
}
aiLoseTurn = true;
addText("Power-up collected!", { y: 1, color: color`2` });
setTimeout(() => clearText(), 2000);
} else if (tiles.some(tile => tile.type === DOUBLE_MOVE)) {
const powerUp = tiles.find(tile => tile.type === DOUBLE_MOVE);
if (powerUp) {
powerUp.remove();
}
aiSkipMoves = 2; // AI skips 2 moves
addText("Double Move collected!", { y: 1, color: color`2` });
setTimeout(() => clearText(), 2000);
} else {
const symbol = currentPlayer ? PLAYER_Y : PLAYER_Z;
addSprite(pointer.x, pointer.y, symbol);
gameBoard[pointer.y][pointer.x] = symbol;
checkGameEnd();
if (!gameFinished) {
spawnPowerUp();
if (extraMove) {
extraMove = false;
currentPlayer = true;
}
}
}
});

onInput("k", () => {
resetGame();
});
});

0 comments on commit 82ed8c1

Please sign in to comment.