import { App } from "./Components.js"; import { render } from "./SJDON.js"; import { State } from "./State.js"; /** * Represents a game. */ export class Game { /** * The number of chips required for a win. */ static #count = 4; /** * The width of the board. */ static #width = 7; /** * The height of the board. */ static #height = 6; /** * The state of the game. * * @type {State} */ #state; /** * The id of the element to add the board to. * * @type {string} */ id; /** * Initializes a new instance of the {@link Game `Game`} class. * * @param {string} id * The id of the element to add the board to. */ constructor(id) { this.id = id; this.#state = new State(Game.#width, Game.#height); } /** * Gets the state of the game. */ get state() { return this.#state; } /** * Gets the board of the game. */ get board() { return this.state.board; } /** * Gets the current player. */ get currentPlayer() { return this.state.currentPlayer; } /** * Gets the id of the player that is winning. * * @type {CellOwner} */ get winner() { for (let yOffset = 0; yOffset <= 1; yOffset++) { for (let xOffset = (yOffset === 1) ? -1 : 1; xOffset <= 1; xOffset++) { let lowerBound = Math.max(0, xOffset * (Game.#count - 1) * -1); let upperBound = Math.min(Game.#width, Game.#width - (xOffset * (Game.#count - 1))); for (let y = 0; y < (Game.#height - yOffset * (Game.#count - 1)); y++) { for (let x = lowerBound; x < upperBound; x++) { /** * @type {CellOwner[]} */ let tokens = []; for (let i = 0; i < Game.#count; i++) { tokens.push(this.board[y + i * yOffset][x + i * xOffset]); } let player = tokens[0]; if ( player !== "" && tokens.every((token) => token === player)) { return player; } } } } } return null; } /** * Dumps the state of the game. * * @returns {IState} * The JSON string representing the state. */ dump() { return { turnCount: this.state.turnCount, board: { ...this.state.board } }; } /** * Loads the game from the specified {@link data `data`}. * * @param {IState} data * The data to load. */ load(data) { this.state.turnCount = data.turnCount; this.state.board.splice(0); this.#state = new State(Game.#width, Game.#height); Object.assign(this.state.board, data.board); this.draw(); } /** * Initializes the game. */ initialize() { this.draw(); } /** * Resets the game. */ reset() { this.#state = new State(Game.#width, Game.#height); this.draw(); } /** * Replaces the content of the board with the current state. */ draw() { let container = document.getElementById(this.id); container.innerHTML = ""; render([App, this], container); } /** * Adds a chip to the board indicated by the {@link x `x`} and the {@link y `y`} coordinate. * * @param {number} x * The x coordinate to add the chip to. * * @param {number} y * The y coordinate to add the chip to. * * @returns {boolean} * A value indicating whether the chip could be added. */ addChip(x, y) { if (!this.winner) { for (let i = Game.#height - 1; i >= 0; i--) { if (this.board[i][x] === "") { this.board[i][x] = this.state.currentPlayer; return true; } } } return false; } }