(tic-tac-toe): Board is now locked once game is finished.
This commit is contained in:
parent
7f412c8fb5
commit
9653b0cbe6
1 changed files with 32 additions and 6 deletions
|
|
@ -2,9 +2,15 @@ import { addRoute, setTitle, type RouteHandlerReturn } from "@app/routing";
|
||||||
import tttPage from "./ttt.html?raw";
|
import tttPage from "./ttt.html?raw";
|
||||||
import { showError, showInfo, showSuccess } from "@app/toast";
|
import { showError, showInfo, showSuccess } from "@app/toast";
|
||||||
|
|
||||||
|
// Represents the possible states of a cell on the board.
|
||||||
|
// `null` means that the cell is empty.
|
||||||
type CellState = 'O' | 'X' | null
|
type CellState = 'O' | 'X' | null
|
||||||
|
|
||||||
|
// Encapsulates the game logic.
|
||||||
class TTC {
|
class TTC {
|
||||||
|
|
||||||
|
private isGameOver: boolean;
|
||||||
|
|
||||||
private board: [
|
private board: [
|
||||||
CellState, CellState, CellState,
|
CellState, CellState, CellState,
|
||||||
CellState, CellState, CellState,
|
CellState, CellState, CellState,
|
||||||
|
|
@ -13,6 +19,7 @@ class TTC {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.board = [null,null,null,null,null,null,null,null,null];
|
this.board = [null,null,null,null,null,null,null,null,null];
|
||||||
|
this.isGameOver = false;
|
||||||
this.currentPlayer = 'X';
|
this.currentPlayer = 'X';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -24,12 +31,13 @@ class TTC {
|
||||||
this.currentPlayer = 'X';
|
this.currentPlayer = 'X';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Analyzes the current board to determine if the game has ended.
|
||||||
private checkState(): 'winX' | 'winO' | 'draw' | 'ongoing'
|
private checkState(): 'winX' | 'winO' | 'draw' | 'ongoing'
|
||||||
{
|
{
|
||||||
const checkRow = (row: number): ('X' | 'O' | null) => {
|
const checkRow = (row: number): ('X' | 'O' | null) => {
|
||||||
if (this.board[row * 3] === null)
|
if (this.board[row * 3] === null)
|
||||||
return null;
|
return null;
|
||||||
if (this.board[row * 3] === this.board[row * 3+1] && this.board[row + 1] === this.board[row * 3 + 2])
|
if (this.board[row * 3] === this.board[row * 3+1] && this.board[row * 3 + 1] === this.board[row * 3 + 2])
|
||||||
return this.board[row * 3];
|
return this.board[row * 3];
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -67,14 +75,29 @@ class TTC {
|
||||||
return 'ongoing';
|
return 'ongoing';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Attempts to place the current player's mark on the specified cell.
|
||||||
|
// @param idx - The index of the board (0-8) to place the mark.
|
||||||
|
// @returns The resulting game state, or `invalidMove` if the move is illegal.
|
||||||
public makeMove(idx: number): 'winX' | 'winO' | 'draw' | 'ongoing' | 'invalidMove' {
|
public makeMove(idx: number): 'winX' | 'winO' | 'draw' | 'ongoing' | 'invalidMove' {
|
||||||
if (idx < 0 || idx >= this.board.length)
|
if (this.isGameOver) {
|
||||||
return 'invalidMove';
|
return 'invalidMove';
|
||||||
if (this.board[idx] !== null)
|
}
|
||||||
|
if (idx < 0 || idx >= this.board.length) {
|
||||||
return 'invalidMove';
|
return 'invalidMove';
|
||||||
|
}
|
||||||
|
if (this.board[idx] !== null) {
|
||||||
|
return 'invalidMove';
|
||||||
|
}
|
||||||
this.board[idx] = this.currentPlayer;
|
this.board[idx] = this.currentPlayer;
|
||||||
this.changePlayer();
|
this.changePlayer();
|
||||||
return this.checkState();
|
|
||||||
|
const result = this.checkState();
|
||||||
|
|
||||||
|
if (result !== 'ongoing') {
|
||||||
|
this.isGameOver = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getBoard(): [
|
public getBoard(): [
|
||||||
|
|
@ -86,9 +109,11 @@ class TTC {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Route handler for the Tic-Tac-Toe page.
|
||||||
|
// Instantiates the game logic and binds UI events.
|
||||||
async function handleTTT(): Promise<RouteHandlerReturn>
|
async function handleTTT(): Promise<RouteHandlerReturn>
|
||||||
{
|
{
|
||||||
|
// Create a fresh instance for every page load.
|
||||||
let board = new TTC();
|
let board = new TTC();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
@ -123,6 +148,7 @@ async function handleTTT(): Promise<RouteHandlerReturn>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sync UI with Game State
|
||||||
const board_state = board.getBoard();
|
const board_state = board.getBoard();
|
||||||
board_state.forEach( function (cell_state, cell_idx) {
|
board_state.forEach( function (cell_state, cell_idx) {
|
||||||
cells[cell_idx].innerText = cell_state !== null ? cell_state : " ";
|
cells[cell_idx].innerText = cell_state !== null ? cell_state : " ";
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue