very basic offline gaming capabilities on frontend

feature/modern-browsers
Inga 🏳‍🌈 1 month ago
parent ce53388d26
commit 709e573b99
  1. 8
      src/backend/components/boardgame.tsx
  2. 73
      src/frontend/components/board-game.ts
  3. 3
      src/frontend/components/index.ts

@ -61,12 +61,12 @@ export const getBoardgameHtml = (key: string, gameState: BoardgameStateType, rul
const currentOutcome = gameState.board ? rules.getBoardOutcome(gameState.board) : CurrentOutcome.Undecided;
return (
<board-game>
<form method="post">
<board-game track={key}>
<form method="post" is="progressive-form">
<p>Current player: {gameState.currentPlayerName}</p>
<table>
<tbody>
<table class="game-board">
<tbody class="game-board">
{sequence(gameState.rows).map((row) => (
<tr>
{sequence(gameState.columns).map((column) => (

@ -0,0 +1,73 @@
import { BoardgameState } from "../../shared/boardgame-state.ts";
import { CurrentOutcome, SquareState, formatSquareState } from "../../shared/datatypes.ts";
import { getDisplayStates } from "../../shared/display.ts";
import { gamesRules } from "../../shared/rules.ts";
import { TrackingTools } from "../lib/query-tracking-utils.ts";
export class BoardGameComponent extends HTMLElement {
private readonly trackingTools = new TrackingTools(this);
handleTrackedValueUpdate(newValue: string | null) {
const rules = gamesRules.tictactoe;
const gameState = BoardgameState.fromSerialized(newValue);
if (!gameState) {
throw new Error("Empty game state");
}
const currentOutcome = gameState.board ? rules.getBoardOutcome(gameState.board) : CurrentOutcome.Undecided;
const displayStates = getDisplayStates(gameState, currentOutcome);
for (const [className, shouldDisplay] of Object.entries(displayStates)) {
for (const element of this.querySelectorAll(`.${className}`)) {
(element as HTMLElement).style.display = shouldDisplay ? "" : "none";
}
}
for (const tbodyUntyped of this.querySelectorAll("tbody.game-board")) {
const tbody = tbodyUntyped as HTMLTableSectionElement;
for (let rowNumber = 0; rowNumber < tbody.rows.length; rowNumber++) {
const row = tbody.rows[rowNumber];
if (!row) {
continue;
}
for (let columnNumber = 0; columnNumber < row.cells.length; columnNumber++) {
const cell = row.cells[columnNumber];
if (!cell) {
continue;
}
for (const button of cell.querySelectorAll("button")) {
if (!gameState.board) {
button.value = gameState.serialize();
button.disabled = true;
button.innerText = " ";
} else {
const squareState = gameState.board.get(rowNumber, columnNumber);
const nextGameState =
squareState === SquareState.Unoccupied && currentOutcome === CurrentOutcome.Undecided
? gameState.withMove(rowNumber, columnNumber)
: gameState;
button.value = nextGameState.serialize();
button.disabled = nextGameState === gameState;
button.innerText = formatSquareState(squareState);
}
}
}
}
}
}
connectedCallback() {
this.trackingTools.connectedCallback();
}
attributeChangedCallback() {
this.trackingTools.attributeChangedCallback();
}
disconnectedCallback() {
this.trackingTools.disconnectedCallback();
}
}

@ -1,3 +1,4 @@
import { BoardGameComponent } from "./board-game.ts";
import { ProgressiveForm } from "./progressive-form.ts";
import { ReactiveButton } from "./reactive-button.ts";
import { ReactiveSpan } from "./reactive-span.ts";
@ -12,5 +13,7 @@ export const initializeWebComponents = () => {
customElements.define("progressive-form", ProgressiveForm, { extends: "form" });
customElements.define("reactive-button", ReactiveButton, { extends: "button" });
customElements.define("reactive-span", ReactiveSpan, { extends: "span" });
customElements.define("board-game", BoardGameComponent);
}
};

Loading…
Cancel
Save