reorganized code

main
Inga 🏳‍🌈 4 days ago
parent 339691baf4
commit ec1048e603
  1. 6
      src/backend/components/boardgame.tsx
  2. 22
      src/backend/components/counter.tsx
  3. 6
      src/backend/main/boardgame-handler.ts
  4. 42
      src/backend/main/index.tsx
  5. 14
      src/frontend/components/board-game.ts
  6. 4
      src/frontend/components/progressive-form.ts
  7. 2
      src/frontend/components/reactive-button.ts
  8. 2
      src/frontend/components/reactive-span.ts
  9. 0
      src/frontend/utils/navigation-utils.ts
  10. 0
      src/frontend/utils/query-tracking-utils.ts
  11. 0
      src/frontend/utils/url-utils.ts
  12. 2
      src/shared/datatypes/board.spec.ts
  13. 4
      src/shared/datatypes/board.ts
  14. 2
      src/shared/datatypes/boardgame-state.spec.ts
  15. 4
      src/shared/datatypes/boardgame-state.ts
  16. 2
      src/shared/datatypes/datatypes.spec.ts
  17. 2
      src/shared/datatypes/types.ts
  18. 2
      src/shared/display.ts
  19. 0
      src/shared/game-variants/index.ts
  20. 4
      src/shared/game-variants/tictactoe-rules.spec.ts
  21. 2
      src/shared/game-variants/tictactoe-rules.ts
  22. 8
      src/shared/game-variants/tictactoe.test.ts
  23. 4
      src/shared/gameplay/boardgame.ts
  24. 4
      src/shared/gameplay/opponent.spec.ts
  25. 2
      src/shared/gameplay/opponent.ts
  26. 2
      src/shared/gameplay/solver-cache.ts
  27. 2
      src/shared/gameplay/solver.spec.ts
  28. 4
      src/shared/gameplay/solver.ts
  29. 0
      src/shared/utils/array-utils.spec.ts
  30. 0
      src/shared/utils/array-utils.ts
  31. 0
      src/shared/utils/utils.ts

@ -1,7 +1,7 @@
import { sequence } from "../../shared/array-utils.ts"; import { sequence } from "../../shared/utils/array-utils.ts";
import { BoardgameStateType, CurrentOutcome } from "../../shared/datatypes.ts"; import { BoardgameStateType, CurrentOutcome } from "../../shared/datatypes/types.ts";
import { ButtonValues, getButtonValues, getCellDisplayData, getDisplayStates } from "../../shared/display.ts"; import { ButtonValues, getButtonValues, getCellDisplayData, getDisplayStates } from "../../shared/display.ts";
import { GameVariantName, gamesRules } from "../../shared/rules.ts"; import { GameVariantName, gamesRules } from "../../shared/game-variants/index.ts";
const getSubmitAttributes = (key: string, targetState: BoardgameStateType) => ({ const getSubmitAttributes = (key: string, targetState: BoardgameStateType) => ({
type: "submit" as const, type: "submit" as const,

@ -0,0 +1,22 @@
import type { Request } from "express";
import { safeGetQueryValue } from "../utils.ts";
export const getCounterHtml = (req: Request, key: string) => {
const counter = parseInt(safeGetQueryValue(req, "a") ?? "0", 10);
return (
<>
<form method="post" is="progressive-form">
<button type="submit" name={key} value={`${counter - 1}`} is="reactive-button" track={key} delta="-1">
-
</button>{" "}
<button type="submit" name={key} value={`${counter + 1}`} is="reactive-button" track={key} delta="+1">
+
</button>
</form>{" "}
Value of "{key}":{" "}
<span class="counter" is="reactive-span" track={key}>
{counter}
</span>
</>
);
};

@ -1,9 +1,9 @@
import type { Request, Response } from "express"; import type { Request, Response } from "express";
import { rewriteQueryParamsWith, safeGetQueryValue } from "../utils.ts"; import { rewriteQueryParamsWith, safeGetQueryValue } from "../utils.ts";
import { BoardgameState } from "../../shared/boardgame-state.ts"; import { BoardgameState } from "../../shared/datatypes/boardgame-state.ts";
import { getBoardgameHtml } from "../components/boardgame.tsx"; import { getBoardgameHtml } from "../components/boardgame.tsx";
import { GameVariantName, gamesRules } from "../../shared/rules.ts"; import { GameVariantName, gamesRules } from "../../shared/game-variants/index.ts";
import { getTargetGameState } from "../../shared/boardgame.ts"; import { getTargetGameState } from "../../shared/gameplay/boardgame.ts";
// Returns nothing if query parameter is uninitialized and a redirect was made, // Returns nothing if query parameter is uninitialized and a redirect was made,
// or component if query parameter is initialized. // or component if query parameter is initialized.

@ -1,15 +1,13 @@
import type { RequestHandler } from "express"; import type { RequestHandler } from "express";
import { safeGetQueryValue, sendHtml } from "../utils.ts"; import { sendHtml } from "../utils.ts";
import { handleBoardgame } from "./boardgame-handler.ts"; import { handleBoardgame } from "./boardgame-handler.ts";
import { getCounterHtml } from "../components/counter.tsx";
export const mainPageHandler: RequestHandler = (req, res) => { export const mainPageHandler: RequestHandler = (req, res) => {
const counters = {
a: parseInt(safeGetQueryValue(req, "a") ?? "0", 10),
b: parseInt(safeGetQueryValue(req, "b") ?? "0", 10),
};
const board1 = handleBoardgame(req, res, "tictactoe1", "tictactoe"); const board1 = handleBoardgame(req, res, "tictactoe1", "tictactoe");
if (!board1) { if (!board1) {
// No return value from handleBoardgame means that it redirected user to another URL,
// and we no longer need to render anything.
return; return;
} }
@ -40,36 +38,8 @@ export const mainPageHandler: RequestHandler = (req, res) => {
supports history navigation (you can go back & forward and see how the state changes). supports history navigation (you can go back & forward and see how the state changes).
</p> </p>
<ul> <ul>
{(["a", "b"] as const).map((key) => ( <li>{getCounterHtml(req, "a")}</li>
<li> <li>{getCounterHtml(req, "b")}</li>
<form method="post" is="progressive-form">
<button
type="submit"
name={key}
value={`${counters[key] - 1}`}
is="reactive-button"
track={key}
delta="-1"
>
-
</button>{" "}
<button
type="submit"
name={key}
value={`${counters[key] + 1}`}
is="reactive-button"
track={key}
delta="+1"
>
+
</button>
</form>{" "}
Value of "{key}":{" "}
<span class="counter" is="reactive-span" track={key}>
{counters[key]}
</span>
</li>
))}
</ul> </ul>
</section> </section>
<section class="game1">{board1}</section> <section class="game1">{board1}</section>

@ -1,11 +1,11 @@
import { BoardgameState } from "../../shared/boardgame-state.ts"; import { BoardgameState } from "../../shared/datatypes/boardgame-state.ts";
import { getTargetGameState } from "../../shared/boardgame.ts"; import { getTargetGameState } from "../../shared/gameplay/boardgame.ts";
import { CurrentOutcome } from "../../shared/datatypes.ts"; import { CurrentOutcome } from "../../shared/datatypes/types.ts";
import { ButtonValues, getButtonValues, getCellDisplayData, getDisplayStates } from "../../shared/display.ts"; import { ButtonValues, getButtonValues, getCellDisplayData, getDisplayStates } from "../../shared/display.ts";
import { GameVariantName, gamesRules } from "../../shared/rules.ts"; import { GameVariantName, gamesRules } from "../../shared/game-variants/index.ts";
import { replaceLocation } from "../lib/navigation-utils.ts"; import { replaceLocation } from "../utils/navigation-utils.ts";
import { TrackingTools } from "../lib/query-tracking-utils.ts"; import { TrackingTools } from "../utils/query-tracking-utils.ts";
import { updateWithQueryParams } from "../lib/url-utils.ts"; import { updateWithQueryParams } from "../utils/url-utils.ts";
export class BoardGameComponent extends HTMLElement { export class BoardGameComponent extends HTMLElement {
private readonly trackingTools = new TrackingTools(this); private readonly trackingTools = new TrackingTools(this);

@ -1,5 +1,5 @@
import { goToLocation } from "../lib/navigation-utils.ts"; import { goToLocation } from "../utils/navigation-utils.ts";
import { updateWithQueryParams } from "../lib/url-utils.ts"; import { updateWithQueryParams } from "../utils/url-utils.ts";
const submitListener = function (this: HTMLFormElement, e: SubmitEvent) { const submitListener = function (this: HTMLFormElement, e: SubmitEvent) {
goToLocation(updateWithQueryParams(new URL(this.action), new FormData(this, e.submitter))); goToLocation(updateWithQueryParams(new URL(this.action), new FormData(this, e.submitter)));

@ -1,4 +1,4 @@
import { TrackingTools } from "../lib/query-tracking-utils.ts"; import { TrackingTools } from "../utils/query-tracking-utils.ts";
export class ReactiveButton extends HTMLButtonElement { export class ReactiveButton extends HTMLButtonElement {
private readonly trackingTools = new TrackingTools(this); private readonly trackingTools = new TrackingTools(this);

@ -1,4 +1,4 @@
import { TrackingTools } from "../lib/query-tracking-utils.ts"; import { TrackingTools } from "../utils/query-tracking-utils.ts";
export class ReactiveSpan extends HTMLSpanElement { export class ReactiveSpan extends HTMLSpanElement {
private readonly trackingTools = new TrackingTools(this); private readonly trackingTools = new TrackingTools(this);

@ -1,7 +1,7 @@
import t, { type Test } from "tap"; import t, { type Test } from "tap";
import { Board } from "./board.ts"; import { Board } from "./board.ts";
import { SquareState } from "./datatypes.ts"; import { SquareState } from "./types.ts";
void t.test("Serialize / deserialize", async (t) => { void t.test("Serialize / deserialize", async (t) => {
const createAndCheckBoard = (t: Test, serialized: string) => { const createAndCheckBoard = (t: Test, serialized: string) => {

@ -1,5 +1,5 @@
import { repeat } from "./array-utils.ts"; import { repeat } from "../utils/array-utils.ts";
import { BoardType, SquareState, formatSquareState, parseSquareState } from "./datatypes.ts"; import { BoardType, SquareState, formatSquareState, parseSquareState } from "./types.ts";
export class Board implements BoardType { export class Board implements BoardType {
// State should be immutable // State should be immutable

@ -1,7 +1,7 @@
import t, { type Test } from "tap"; import t, { type Test } from "tap";
import { BoardgameState } from "./boardgame-state.ts"; import { BoardgameState } from "./boardgame-state.ts";
import { Player, SquareState } from "./datatypes.ts"; import { Player, SquareState } from "./types.ts";
const createAndCheckBoardgameState = (t: Test, serialized: string) => { const createAndCheckBoardgameState = (t: Test, serialized: string) => {
const boardgameState = BoardgameState.fromSerialized(serialized); const boardgameState = BoardgameState.fromSerialized(serialized);

@ -1,4 +1,4 @@
import { compact } from "./array-utils.ts"; import { compact } from "../utils/array-utils.ts";
import { Board } from "./board.ts"; import { Board } from "./board.ts";
import { import {
BoardType, BoardType,
@ -9,7 +9,7 @@ import {
getNextPlayer, getNextPlayer,
getOccupiedStateByPlayer, getOccupiedStateByPlayer,
parsePlayer, parsePlayer,
} from "./datatypes.ts"; } from "./types.ts";
const parseDimensions = (dimensionsSerialized: string | undefined) => { const parseDimensions = (dimensionsSerialized: string | undefined) => {
if (!dimensionsSerialized) { if (!dimensionsSerialized) {

@ -13,7 +13,7 @@ import {
getUndesiredFinalOutcomeByPlayer, getUndesiredFinalOutcomeByPlayer,
parsePlayer, parsePlayer,
parseSquareState, parseSquareState,
} from "./datatypes.ts"; } from "./types.ts";
// These unit tests are mostly useless because they're basically a tautology, // These unit tests are mostly useless because they're basically a tautology,
// a rephrasing of implementation in slightly different words. // a rephrasing of implementation in slightly different words.

@ -1,4 +1,4 @@
import { unreachableString } from "./utils.ts"; import { unreachableString } from "../utils/utils.ts";
export enum SquareState { export enum SquareState {
Unoccupied = 1, // so that all SquareState values are truthy Unoccupied = 1, // so that all SquareState values are truthy

@ -1,4 +1,4 @@
import { BoardgameStateType, CurrentOutcome, Player, SquareState, formatSquareState } from "./datatypes.ts"; import { BoardgameStateType, CurrentOutcome, Player, SquareState, formatSquareState } from "./datatypes/types.ts";
export const getDisplayStates = (gameState: BoardgameStateType, currentOutcome: CurrentOutcome) => ({ export const getDisplayStates = (gameState: BoardgameStateType, currentOutcome: CurrentOutcome) => ({
"outcome-winx": currentOutcome === CurrentOutcome.WinX, "outcome-winx": currentOutcome === CurrentOutcome.WinX,

@ -1,7 +1,7 @@
import t from "tap"; import t from "tap";
import { Board } from "./board.ts"; import { Board } from "../datatypes/board.ts";
import { CurrentOutcome, SquareState } from "./datatypes.ts"; import { CurrentOutcome, SquareState } from "../datatypes/types.ts";
import { getBoardOutcome, getSequenceOutcome } from "./tictactoe-rules.ts"; import { getBoardOutcome, getSequenceOutcome } from "./tictactoe-rules.ts";
void t.test("getSequenceOutcome", async (t) => { void t.test("getSequenceOutcome", async (t) => {

@ -1,4 +1,4 @@
import { CurrentOutcome, GameRules, SquareState } from "./datatypes.ts"; import { CurrentOutcome, GameRules, SquareState } from "../datatypes/types.ts";
export const getSequenceOutcome = (sequence: SquareState[]) => { export const getSequenceOutcome = (sequence: SquareState[]) => {
for (let i = 1; i < sequence.length; i++) { for (let i = 1; i < sequence.length; i++) {

@ -1,9 +1,9 @@
import t, { Test } from "tap"; import t, { Test } from "tap";
import { Board } from "./board.ts"; import { Board } from "../datatypes/board.ts";
import { ExpectedOutcome, FinalOutcome, Opponent, Player, getOccupiedStateByPlayer } from "./datatypes.ts"; import { ExpectedOutcome, FinalOutcome, Opponent, Player, getOccupiedStateByPlayer } from "../datatypes/types.ts";
import { createOpponent } from "./opponent.ts"; import { createOpponent } from "../gameplay/opponent.ts";
import { computeAllSolutions } from "./solver.ts"; import { computeAllSolutions } from "../gameplay/solver.ts";
import { rules } from "./tictactoe-rules.ts"; import { rules } from "./tictactoe-rules.ts";
void t.test("computeAllSolutions", async (t) => { void t.test("computeAllSolutions", async (t) => {

@ -1,5 +1,5 @@
import { BoardgameState } from "./boardgame-state.ts"; import { BoardgameState } from "../datatypes/boardgame-state.ts";
import { CurrentOutcome, GameRules } from "./datatypes.ts"; import { CurrentOutcome, GameRules } from "../datatypes/types.ts";
import { createOpponent } from "./opponent.ts"; import { createOpponent } from "./opponent.ts";
import { getAllSolutions } from "./solver-cache.ts"; import { getAllSolutions } from "./solver-cache.ts";

@ -1,6 +1,6 @@
import t from "tap"; import t from "tap";
import { Board } from "./board.ts"; import { Board } from "../datatypes/board.ts";
import { ExpectedOutcome, FinalOutcome, Player, getOccupiedStateByPlayer } from "./datatypes.ts"; import { ExpectedOutcome, FinalOutcome, Player, getOccupiedStateByPlayer } from "../datatypes/types.ts";
import { createOpponent } from "./opponent.ts"; import { createOpponent } from "./opponent.ts";
void t.test("createOpponent", async (t) => { void t.test("createOpponent", async (t) => {

@ -1,4 +1,4 @@
import { ExpectedOutcome, Opponent, SquareState, getOccupiedStateByPlayer } from "./datatypes.ts"; import { ExpectedOutcome, Opponent, SquareState, getOccupiedStateByPlayer } from "../datatypes/types.ts";
export const createOpponent = (outcomesByBoard: Map<string, ExpectedOutcome>): Opponent => { export const createOpponent = (outcomesByBoard: Map<string, ExpectedOutcome>): Opponent => {
const getNextMove: Opponent["getNextMove"] = (board, currentPlayer) => { const getNextMove: Opponent["getNextMove"] = (board, currentPlayer) => {

@ -1,4 +1,4 @@
import { ExpectedOutcome, GameRules } from "./datatypes.ts"; import { ExpectedOutcome, GameRules } from "../datatypes/types.ts";
import { computeAllSolutions } from "./solver.ts"; import { computeAllSolutions } from "./solver.ts";
const solverCache = new Map<GameRules, Map<string, Map<string, ExpectedOutcome>>>(); const solverCache = new Map<GameRules, Map<string, Map<string, ExpectedOutcome>>>();

@ -1,6 +1,6 @@
import t, { type Test } from "tap"; import t, { type Test } from "tap";
import { CurrentOutcome, ExpectedOutcome, FinalOutcome, GameRules, Player, SquareState } from "./datatypes.ts"; import { CurrentOutcome, ExpectedOutcome, FinalOutcome, GameRules, Player, SquareState } from "../datatypes/types.ts";
import { computeAllSolutions, getPreferredNextOutcome } from "./solver.ts"; import { computeAllSolutions, getPreferredNextOutcome } from "./solver.ts";
void t.test("getPreferredNextOutcome", async (t) => { void t.test("getPreferredNextOutcome", async (t) => {

@ -1,4 +1,4 @@
import { Board } from "./board.ts"; import { Board } from "../datatypes/board.ts";
import { import {
BoardType, BoardType,
CurrentOutcome, CurrentOutcome,
@ -13,7 +13,7 @@ import {
getNextPlayer, getNextPlayer,
getOccupiedStateByPlayer, getOccupiedStateByPlayer,
getUndesiredFinalOutcomeByPlayer, getUndesiredFinalOutcomeByPlayer,
} from "./datatypes.ts"; } from "../datatypes/types.ts";
export const getPreferredNextOutcome = ( export const getPreferredNextOutcome = (
possibleNextOutcomes: ExpectedOutcome[], possibleNextOutcomes: ExpectedOutcome[],
Loading…
Cancel
Save