parent
24fcb0ca1b
commit
a30663b268
@ -0,0 +1,14 @@ |
||||
import t from "tap"; |
||||
import { repeat, sequence } from "./array-utils.ts"; |
||||
|
||||
void t.test("repeat", async (t) => { |
||||
t.matchOnlyStrict(repeat("x", 0), []); |
||||
t.matchOnlyStrict(repeat("x", 1), ["x"]); |
||||
t.matchOnlyStrict(repeat("x", 5), ["x", "x", "x", "x", "x"]); |
||||
}); |
||||
|
||||
void t.test("sequence", async (t) => { |
||||
t.matchOnlyStrict(sequence(0), []); |
||||
t.matchOnlyStrict(sequence(1), [0]); |
||||
t.matchOnlyStrict(sequence(5), [0, 1, 2, 3, 4]); |
||||
}); |
@ -0,0 +1,238 @@ |
||||
import t, { type Test } from "tap"; |
||||
|
||||
import { BoardgameState } from "./boardgame-state.ts"; |
||||
import { Player, SquareState } from "./datatypes.ts"; |
||||
|
||||
const createAndCheckBoardgameState = (t: Test, serialized: string) => { |
||||
const boardgameState = BoardgameState.fromSerialized(serialized); |
||||
t.ok(boardgameState); |
||||
t.equal(boardgameState?.serialize(), serialized); |
||||
return boardgameState; |
||||
}; |
||||
|
||||
void t.test("Serialize / deserialize", async (t) => { |
||||
void t.test("Empty state", async (t) => { |
||||
t.equal(BoardgameState.fromSerialized(""), null); |
||||
t.equal(BoardgameState.fromSerialized(null), null); |
||||
t.equal(BoardgameState.fromSerialized(".."), null); |
||||
}); |
||||
|
||||
void t.test("1x1 state (incompletely serialized)", async (t) => { |
||||
const state = BoardgameState.fromSerialized("1x1"); |
||||
t.ok(state); |
||||
t.equal(state?.serialize(), "1x1.."); |
||||
t.equal(state?.rows, 1); |
||||
t.equal(state?.columns, 1); |
||||
t.equal(state?.currentPlayer, null); |
||||
t.equal(state?.currentPlayerName, ""); |
||||
t.equal(state?.board, null); |
||||
}); |
||||
|
||||
void t.test("1x1 state with empty board", async (t) => { |
||||
const state = createAndCheckBoardgameState(t, "1x1.."); |
||||
t.equal(state?.rows, 1); |
||||
t.equal(state?.columns, 1); |
||||
t.equal(state?.currentPlayer, null); |
||||
t.equal(state?.currentPlayerName, ""); |
||||
t.equal(state?.board, null); |
||||
}); |
||||
|
||||
void t.test("1x1 board with started game", async (t) => { |
||||
const state = createAndCheckBoardgameState(t, "1x1.X._"); |
||||
t.equal(state?.rows, 1); |
||||
t.equal(state?.columns, 1); |
||||
t.equal(state?.currentPlayer, Player.X); |
||||
t.equal(state?.currentPlayerName, "X"); |
||||
t.equal(state?.board?.hasRow(0), true); |
||||
t.equal(state?.board?.hasSquare(0, 0), true); |
||||
t.equal(state?.board?.get(0, 0), SquareState.Unoccupied); |
||||
t.equal(state?.board?.hasSquare(0, 1), false); |
||||
t.equal(state?.board?.hasRow(1), false); |
||||
}); |
||||
|
||||
void t.test("1x2 board with first move", async (t) => { |
||||
const state = createAndCheckBoardgameState(t, "1x2.O._X"); |
||||
t.equal(state?.rows, 1); |
||||
t.equal(state?.columns, 2); |
||||
t.equal(state?.currentPlayer, Player.O); |
||||
t.equal(state?.currentPlayerName, "O"); |
||||
t.equal(state?.board?.get(0, 0), SquareState.Unoccupied); |
||||
t.equal(state?.board?.get(0, 1), SquareState.X); |
||||
t.equal(state?.board?.hasSquare(0, 2), false); |
||||
t.equal(state?.board?.hasRow(1), false); |
||||
}); |
||||
|
||||
void t.test("2x1 board with first move", async (t) => { |
||||
const state = createAndCheckBoardgameState(t, "2x1.O._|X"); |
||||
t.equal(state?.rows, 2); |
||||
t.equal(state?.columns, 1); |
||||
t.equal(state?.currentPlayer, Player.O); |
||||
t.equal(state?.currentPlayerName, "O"); |
||||
t.equal(state?.board?.get(0, 0), SquareState.Unoccupied); |
||||
t.equal(state?.board?.get(1, 0), SquareState.X); |
||||
t.equal(state?.board?.hasSquare(0, 1), false); |
||||
t.equal(state?.board?.hasRow(2), false); |
||||
}); |
||||
}); |
||||
|
||||
void t.test("Empty state creation", async (t) => { |
||||
void t.test("0x0 board", async (t) => { |
||||
const state = BoardgameState.createWithoutBoard(0, 0); |
||||
t.equal(state.serialize(), "0x0.."); |
||||
t.equal(state.rows, 0); |
||||
t.equal(state.columns, 0); |
||||
t.equal(state.currentPlayer, null); |
||||
t.equal(state.currentPlayerName, ""); |
||||
t.equal(state.board, null); |
||||
}); |
||||
|
||||
void t.test("1x0 board", async (t) => { |
||||
const state = BoardgameState.createWithoutBoard(1, 0); |
||||
t.equal(state.serialize(), "1x0.."); |
||||
t.equal(state.rows, 1); |
||||
t.equal(state.columns, 0); |
||||
t.equal(state.currentPlayer, null); |
||||
t.equal(state.currentPlayerName, ""); |
||||
t.equal(state.board, null); |
||||
}); |
||||
|
||||
void t.test("1x1 board", async (t) => { |
||||
const state = BoardgameState.createWithoutBoard(1, 1); |
||||
t.equal(state.serialize(), "1x1.."); |
||||
t.equal(state.rows, 1); |
||||
t.equal(state.columns, 1); |
||||
t.equal(state.currentPlayer, null); |
||||
t.equal(state.currentPlayerName, ""); |
||||
t.equal(state.board, null); |
||||
}); |
||||
|
||||
void t.test("1x2 board", async (t) => { |
||||
const state = BoardgameState.createWithoutBoard(1, 2); |
||||
t.equal(state.serialize(), "1x2.."); |
||||
t.equal(state.rows, 1); |
||||
t.equal(state.columns, 2); |
||||
t.equal(state.currentPlayer, null); |
||||
t.equal(state.currentPlayerName, ""); |
||||
t.equal(state.board, null); |
||||
}); |
||||
|
||||
void t.test("Error handling", async (t) => { |
||||
t.throws(() => BoardgameState.fromSerialized("0.."), { |
||||
message: "Incorrect dimensions", |
||||
}); |
||||
}); |
||||
}); |
||||
|
||||
void t.test("withEmptyBoard", async (t) => { |
||||
void t.test("On 0x0 board", async (t) => { |
||||
const oldState = createAndCheckBoardgameState(t, "0x0.."); |
||||
const newState = oldState?.withEmptyBoard(); |
||||
t.equal(newState?.serialize(), "0x0.X."); |
||||
t.equal(newState?.rows, 0); |
||||
t.equal(newState?.columns, 0); |
||||
t.equal(newState?.currentPlayer, Player.X); |
||||
t.equal(newState?.currentPlayerName, "X"); |
||||
t.equal(newState?.board?.serialize(), ""); |
||||
}); |
||||
|
||||
void t.test("On 0x1 board", async (t) => { |
||||
const oldState = createAndCheckBoardgameState(t, "0x1.."); |
||||
const newState = oldState?.withEmptyBoard(); |
||||
t.equal(newState?.serialize(), "0x1.X."); |
||||
t.equal(newState?.rows, 0); |
||||
t.equal(newState?.columns, 1); |
||||
t.equal(newState?.currentPlayer, Player.X); |
||||
t.equal(newState?.currentPlayerName, "X"); |
||||
t.equal(newState?.board?.serialize(), ""); |
||||
}); |
||||
|
||||
void t.test("On 1x0 board", async (t) => { |
||||
const oldState = createAndCheckBoardgameState(t, "1x0.."); |
||||
const newState = oldState?.withEmptyBoard(); |
||||
t.equal(newState?.serialize(), "1x0.X."); |
||||
t.equal(newState?.rows, 1); |
||||
t.equal(newState?.columns, 0); |
||||
t.equal(newState?.currentPlayer, Player.X); |
||||
t.equal(newState?.currentPlayerName, "X"); |
||||
t.equal(newState?.board?.serialize(), ""); |
||||
}); |
||||
|
||||
void t.test("On 1x1 board", async (t) => { |
||||
const oldState = createAndCheckBoardgameState(t, "1x1.."); |
||||
const newState = oldState?.withEmptyBoard(); |
||||
t.equal(newState?.serialize(), "1x1.X._"); |
||||
t.equal(newState?.rows, 1); |
||||
t.equal(newState?.columns, 1); |
||||
t.equal(newState?.currentPlayer, Player.X); |
||||
t.equal(newState?.currentPlayerName, "X"); |
||||
t.equal(newState?.board?.serialize(), "_"); |
||||
}); |
||||
|
||||
void t.test("On 1x2 board", async (t) => { |
||||
const oldState = createAndCheckBoardgameState(t, "1x2.."); |
||||
const newState = oldState?.withEmptyBoard(); |
||||
t.equal(newState?.serialize(), "1x2.X.__"); |
||||
t.equal(newState?.rows, 1); |
||||
t.equal(newState?.columns, 2); |
||||
t.equal(newState?.currentPlayer, Player.X); |
||||
t.equal(newState?.currentPlayerName, "X"); |
||||
t.equal(newState?.board?.serialize(), "__"); |
||||
}); |
||||
|
||||
void t.test("On 2x1 board", async (t) => { |
||||
const oldState = createAndCheckBoardgameState(t, "2x1.."); |
||||
const newState = oldState?.withEmptyBoard(); |
||||
t.equal(newState?.serialize(), "2x1.X._|_"); |
||||
t.equal(newState?.rows, 2); |
||||
t.equal(newState?.columns, 1); |
||||
t.equal(newState?.currentPlayer, Player.X); |
||||
t.equal(newState?.currentPlayerName, "X"); |
||||
t.equal(newState?.board?.serialize(), "_|_"); |
||||
}); |
||||
}); |
||||
|
||||
void t.test("withMove", async (t) => { |
||||
void t.test("On 2x3 board", async (t) => { |
||||
const oldState = createAndCheckBoardgameState(t, "2x3.X.___|___"); |
||||
|
||||
const firstMoveState = oldState?.withMove(0, 1); |
||||
t.equal(firstMoveState?.serialize(), "2x3.O._X_|___"); |
||||
t.equal(firstMoveState?.rows, 2); |
||||
t.equal(firstMoveState?.columns, 3); |
||||
t.equal(firstMoveState?.currentPlayer, Player.O); |
||||
t.equal(firstMoveState?.currentPlayerName, "O"); |
||||
t.equal(firstMoveState?.board?.serialize(), "_X_|___"); |
||||
|
||||
const secondMoveState = firstMoveState?.withMove(1, 2); |
||||
t.equal(secondMoveState?.serialize(), "2x3.X._X_|__O"); |
||||
t.equal(secondMoveState?.rows, 2); |
||||
t.equal(secondMoveState?.columns, 3); |
||||
t.equal(secondMoveState?.currentPlayer, Player.X); |
||||
t.equal(secondMoveState?.currentPlayerName, "X"); |
||||
t.equal(secondMoveState?.board?.serialize(), "_X_|__O"); |
||||
}); |
||||
|
||||
void t.test("Error handling", async () => { |
||||
const unstartedBoard = createAndCheckBoardgameState(t, "1x1.."); |
||||
t.throws(() => unstartedBoard?.withMove(0, 0), { |
||||
message: "Game is not started", |
||||
}); |
||||
}); |
||||
}); |
||||
|
||||
void t.test("Sample usage scenario", async (t) => { |
||||
const noBoardState = BoardgameState.createWithoutBoard(2, 3); |
||||
t.equal(noBoardState.serialize(), "2x3.."); |
||||
|
||||
const gameStartedState = noBoardState.withEmptyBoard(); |
||||
t.equal(gameStartedState.serialize(), "2x3.X.___|___"); |
||||
|
||||
const firstMoveState = gameStartedState.withMove(0, 1); |
||||
t.equal(firstMoveState.serialize(), "2x3.O._X_|___"); |
||||
|
||||
const secondMoveState = firstMoveState.withMove(1, 2); |
||||
t.equal(secondMoveState.serialize(), "2x3.X._X_|__O"); |
||||
|
||||
const thirdMoveState = secondMoveState.withMove(1, 0); |
||||
t.equal(thirdMoveState.serialize(), "2x3.O._X_|X_O"); |
||||
}); |
Loading…
Reference in new issue