parent
ec1048e603
commit
56216e904f
@ -1,7 +1,9 @@ |
||||
import { rules as tictactoeRules } from "./tictactoe-rules.ts"; |
||||
import { tictactoeAllRules } from "./tictactoe/tictactoe-all-rules.ts"; |
||||
import { tictactoeThreeRules } from "./tictactoe/tictactoe-three-rules.ts"; |
||||
|
||||
export const gamesRules = { |
||||
tictactoe: tictactoeRules, |
||||
"tictactoe-three": tictactoeThreeRules, |
||||
"tictactoe-all": tictactoeAllRules, |
||||
}; |
||||
|
||||
export type GameVariantName = keyof typeof gamesRules; |
||||
|
@ -1,194 +0,0 @@ |
||||
import t from "tap"; |
||||
|
||||
import { Board } from "../datatypes/board.ts"; |
||||
import { CurrentOutcome, SquareState } from "../datatypes/types.ts"; |
||||
import { getBoardOutcome, getSequenceOutcome } from "./tictactoe-rules.ts"; |
||||
|
||||
void t.test("getSequenceOutcome", async (t) => { |
||||
void t.test("empty sequence", async (t) => { |
||||
t.equal(getSequenceOutcome([]), null); |
||||
}); |
||||
|
||||
void t.test("all sequences of length 1", async (t) => { |
||||
t.equal(getSequenceOutcome([SquareState.Unoccupied]), null); |
||||
t.equal(getSequenceOutcome([SquareState.X]), CurrentOutcome.WinX); |
||||
t.equal(getSequenceOutcome([SquareState.O]), CurrentOutcome.WinO); |
||||
}); |
||||
|
||||
void t.test("all sequences of length 2", async (t) => { |
||||
t.equal(getSequenceOutcome([SquareState.Unoccupied, SquareState.Unoccupied]), null); |
||||
t.equal(getSequenceOutcome([SquareState.Unoccupied, SquareState.X]), null); |
||||
t.equal(getSequenceOutcome([SquareState.Unoccupied, SquareState.O]), null); |
||||
t.equal(getSequenceOutcome([SquareState.X, SquareState.Unoccupied]), null); |
||||
t.equal(getSequenceOutcome([SquareState.X, SquareState.X]), CurrentOutcome.WinX); |
||||
t.equal(getSequenceOutcome([SquareState.X, SquareState.O]), null); |
||||
t.equal(getSequenceOutcome([SquareState.O, SquareState.Unoccupied]), null); |
||||
t.equal(getSequenceOutcome([SquareState.O, SquareState.X]), null); |
||||
t.equal(getSequenceOutcome([SquareState.O, SquareState.O]), CurrentOutcome.WinO); |
||||
}); |
||||
|
||||
void t.test("sequences of length 7", async (t) => { |
||||
void t.test("all X except for the first element", async (t) => { |
||||
t.equal( |
||||
getSequenceOutcome([ |
||||
SquareState.Unoccupied, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
]), |
||||
null, |
||||
); |
||||
t.equal( |
||||
getSequenceOutcome([ |
||||
SquareState.O, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
]), |
||||
null, |
||||
); |
||||
}); |
||||
|
||||
void t.test("all X except for the last element", async (t) => { |
||||
t.equal( |
||||
getSequenceOutcome([ |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.Unoccupied, |
||||
]), |
||||
null, |
||||
); |
||||
t.equal( |
||||
getSequenceOutcome([ |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.O, |
||||
]), |
||||
null, |
||||
); |
||||
}); |
||||
|
||||
void t.test("all X except for the middle element", async (t) => { |
||||
t.equal( |
||||
getSequenceOutcome([ |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.Unoccupied, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
]), |
||||
null, |
||||
); |
||||
t.equal( |
||||
getSequenceOutcome([ |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.O, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
]), |
||||
null, |
||||
); |
||||
}); |
||||
|
||||
t.equal( |
||||
getSequenceOutcome([ |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
]), |
||||
CurrentOutcome.WinX, |
||||
); |
||||
}); |
||||
}); |
||||
|
||||
void t.test("getBoardOutcome", async (t) => { |
||||
void t.test("1x1 boards", async (t) => { |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X")), CurrentOutcome.Draw); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("O")), CurrentOutcome.Draw); |
||||
}); |
||||
|
||||
void t.test("2x2 boards", async (t) => { |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__|__")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX|X_")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX|XX")), CurrentOutcome.Draw); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("OO|OO")), CurrentOutcome.Draw); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XO|OX")), CurrentOutcome.Draw); |
||||
}); |
||||
|
||||
void t.test("3x3 boards", async (t) => { |
||||
t.equal(getBoardOutcome(Board.fromSerialized("___|___|___")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("___|_X_|___")), CurrentOutcome.Undecided); |
||||
|
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX_|___|___")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("___|___|_XX")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X__|X__|___")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("___|__X|__X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X__|_X_|___")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("___|_X_|__X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__X|_X_|___")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("___|_X_|X__")), CurrentOutcome.Undecided); |
||||
|
||||
t.equal(getBoardOutcome(Board.fromSerialized("XXX|O_O|O_O")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("O_O|O_O|XXX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XOO|X__|XOO")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("OOX|__X|OOX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XOO|OXO|OOX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("OOX|OXO|XOO")), CurrentOutcome.WinX); |
||||
|
||||
t.equal(getBoardOutcome(Board.fromSerialized("XXO|XOX|OXX")), CurrentOutcome.WinO); |
||||
|
||||
t.equal(getBoardOutcome(Board.fromSerialized("XOX|XXO|OXO")), CurrentOutcome.Draw); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XOX|XXO|OX_")), CurrentOutcome.Undecided); |
||||
}); |
||||
|
||||
void t.test("5x5 boards", async (t) => { |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_____|_____|_____|_____|_____")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XXX__|_____|_____|_____|_____")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X____|X____|X____|_____|_____")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X____|_X___|__X__|_____|_____")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__X__|_X___|X____|_____|_____")), CurrentOutcome.WinX); |
||||
|
||||
t.equal(getBoardOutcome(Board.fromSerialized("_____|_____|_____|_____|__XXX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_____|_____|____X|____X|____X")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_____|_____|__X__|___X_|____X")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_____|_____|____X|___X_|__X__")), CurrentOutcome.WinX); |
||||
}); |
||||
|
||||
void t.test("6x5 boards", async (t) => { |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_____|_____|_____|_____|_____|__XXX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_____|_____|_____|____X|____X|____X")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_____|_____|_____|__X__|___X_|____X")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_____|_____|_____|____X|___X_|__X__")), CurrentOutcome.WinX); |
||||
}); |
||||
|
||||
void t.test("5x6 boards", async (t) => { |
||||
t.equal(getBoardOutcome(Board.fromSerialized("______|______|______|______|___XXX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("______|______|_____X|_____X|_____X")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("______|______|___X__|____X_|_____X")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("______|______|_____X|____X_|___X__")), CurrentOutcome.WinX); |
||||
}); |
||||
}); |
@ -0,0 +1,253 @@ |
||||
import t from "tap"; |
||||
|
||||
import { Board } from "../../datatypes/board.ts"; |
||||
import { CurrentOutcome } from "../../datatypes/types.ts"; |
||||
import { getBoardOutcome } from "./tictactoe-all-rules.ts"; |
||||
|
||||
void t.test("getBoardOutcome", async (t) => { |
||||
void t.test("1x1 boards", async (t) => { |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("O")), CurrentOutcome.WinO); |
||||
}); |
||||
|
||||
void t.test("2x2 boards", async (t) => { |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__|__")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX|X_")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX|XX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("OO|OO")), CurrentOutcome.WinO); |
||||
|
||||
// Could be WinO as well, but we consider right-down diagonal before right-up one
|
||||
t.equal(getBoardOutcome(Board.fromSerialized("XO|OX")), CurrentOutcome.WinX); |
||||
}); |
||||
|
||||
void t.test("Enumerate all 2x2 boards without O", async (t) => { |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__|__")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__|_X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__|X_")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__|XX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X|__")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X|_X")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X|X_")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X|XX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_|__")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_|_X")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_|X_")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_|XX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX|__")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX|_X")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX|X_")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX|XX")), CurrentOutcome.WinX); |
||||
}); |
||||
|
||||
void t.test("Enumerate all 2x3 boards without O", async (t) => { |
||||
t.equal(getBoardOutcome(Board.fromSerialized("___|___")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("___|__X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("___|_X_")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("___|_XX")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("___|X__")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("___|X_X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("___|XX_")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("___|XXX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__X|___")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__X|__X")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__X|_X_")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__X|_XX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__X|X__")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__X|X_X")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__X|XX_")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__X|XXX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X_|___")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X_|__X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X_|_X_")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X_|_XX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X_|X__")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X_|X_X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X_|XX_")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X_|XXX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_XX|___")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_XX|__X")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_XX|_X_")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_XX|_XX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_XX|X__")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_XX|X_X")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_XX|XX_")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_XX|XXX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X__|___")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X__|__X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X__|_X_")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X__|_XX")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X__|X__")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X__|X_X")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X__|XX_")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X__|XXX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_X|___")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_X|__X")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_X|_X_")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_X|_XX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_X|X__")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_X|X_X")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_X|XX_")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_X|XXX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX_|___")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX_|__X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX_|_X_")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX_|_XX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX_|X__")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX_|X_X")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX_|XX_")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX_|XXX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XXX|___")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XXX|__X")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XXX|_X_")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XXX|_XX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XXX|X__")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XXX|X_X")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XXX|XX_")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XXX|XXX")), CurrentOutcome.WinX); |
||||
}); |
||||
|
||||
void t.test("Enumerate all 3x2 boards without O", async (t) => { |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__|__|__")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__|__|_X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__|__|X_")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__|__|XX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__|_X|__")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__|_X|_X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__|_X|X_")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__|_X|XX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__|X_|__")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__|X_|_X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__|X_|X_")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__|X_|XX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__|XX|__")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__|XX|_X")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__|XX|X_")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__|XX|XX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X|__|__")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X|__|_X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X|__|X_")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X|__|XX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X|_X|__")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X|_X|_X")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X|_X|X_")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X|_X|XX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X|X_|__")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X|X_|_X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X|X_|X_")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X|X_|XX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X|XX|__")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X|XX|_X")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X|XX|X_")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X|XX|XX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_|__|__")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_|__|_X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_|__|X_")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_|__|XX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_|_X|__")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_|_X|_X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_|_X|X_")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_|_X|XX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_|X_|__")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_|X_|_X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_|X_|X_")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_|X_|XX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_|XX|__")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_|XX|_X")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_|XX|X_")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_|XX|XX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX|__|__")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX|__|_X")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX|__|X_")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX|__|XX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX|_X|__")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX|_X|_X")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX|_X|X_")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX|_X|XX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX|X_|__")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX|X_|_X")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX|X_|X_")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX|X_|XX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX|XX|__")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX|XX|_X")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX|XX|X_")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX|XX|XX")), CurrentOutcome.WinX); |
||||
}); |
||||
|
||||
void t.test("3x3 boards", async (t) => { |
||||
t.equal(getBoardOutcome(Board.fromSerialized("___|___|___")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("___|_X_|___")), CurrentOutcome.Undecided); |
||||
|
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX_|___|___")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("___|___|_XX")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X__|X__|___")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("___|__X|__X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X__|_X_|___")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("___|_X_|__X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__X|_X_|___")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("___|_X_|X__")), CurrentOutcome.Undecided); |
||||
|
||||
t.equal(getBoardOutcome(Board.fromSerialized("XXX|O_O|O_O")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("O_O|O_O|XXX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XOO|X__|XOO")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("OOX|__X|OOX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XOO|OXO|OOX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("OOX|OXO|XOO")), CurrentOutcome.WinX); |
||||
|
||||
t.equal(getBoardOutcome(Board.fromSerialized("XXO|XOX|OXX")), CurrentOutcome.WinO); |
||||
|
||||
t.equal(getBoardOutcome(Board.fromSerialized("XOX|XXO|OXO")), CurrentOutcome.Draw); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XOX|XXO|OX_")), CurrentOutcome.Undecided); |
||||
}); |
||||
|
||||
void t.test("5x5 boards", async (t) => { |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_____|_____|_____|_____|_____")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XXXXX|_____|_____|_____|_____")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_____|_____|_____|_____|XXXXX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X____|X____|X____|X____|X____")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("____X|____X|____X|____X|____X")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X____|_X___|__X__|___X_|____X")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("____X|___X_|__X__|_X___|X____")), CurrentOutcome.WinX); |
||||
|
||||
t.equal(getBoardOutcome(Board.fromSerialized("XXXX_|_____|_____|_____|_____")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_____|_____|_____|_____|_XXXX")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X____|X____|X____|X____|_____")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_____|____X|____X|____X|____X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X____|_X___|__X__|___X_|_____")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_____|___X_|__X__|_X___|X____")), CurrentOutcome.Undecided); |
||||
}); |
||||
|
||||
void t.test("6x5 boards", async (t) => { |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_____|_____|_____|_____|_____|_____")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XXXXX|_____|_____|_____|_____|_____")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_____|_____|_____|_____|_____|XXXXX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X____|X____|X____|X____|X____|X____")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("____X|____X|____X|____X|____X|____X")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X____|_X___|__X__|___X_|____X|_____")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("____X|___X_|__X__|_X___|X____|_____")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_____|X____|_X___|__X__|___X_|____X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_____|____X|___X_|__X__|_X___|X____")), CurrentOutcome.Undecided); |
||||
|
||||
t.equal(getBoardOutcome(Board.fromSerialized("XXXX_|_____|_____|_____|_____|_____")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_____|_____|_____|_____|_____|_XXXX")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X____|X____|X____|X____|X____|_____")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_____|____X|____X|____X|____X|____X")), CurrentOutcome.Undecided); |
||||
}); |
||||
|
||||
void t.test("5x6 boards", async (t) => { |
||||
t.equal(getBoardOutcome(Board.fromSerialized("______|______|______|______|______")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XXXXXX|______|______|______|______")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("______|______|______|______|XXXXXX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_____|X_____|X_____|X_____|X_____")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_____X|_____X|_____X|_____X|_____X")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_____|_X____|__X___|___X__|____X_")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X____|__X___|___X__|____X_|_____X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("____X_|___X__|__X___|_X____|X_____")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_____X|____X_|___X__|__X___|_X____")), CurrentOutcome.Undecided); |
||||
|
||||
t.equal(getBoardOutcome(Board.fromSerialized("XXXXX_|______|______|______|______")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("______|______|______|______|_XXXXX")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_____|X_____|X_____|X_____|______")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("______|_____X|_____X|_____X|_____X")), CurrentOutcome.Undecided); |
||||
}); |
||||
}); |
@ -0,0 +1,66 @@ |
||||
import { CurrentOutcome, GameRules, SquareState } from "../../datatypes/types.ts"; |
||||
import { getSequenceOutcome } from "./tictactoe-generic.ts"; |
||||
|
||||
export const getBoardOutcome: GameRules["getBoardOutcome"] = (board) => { |
||||
for (let row = 0; board.hasRow(row); row++) { |
||||
const rowValues = []; |
||||
for (let column = 0; board.hasSquare(row, column); column++) { |
||||
rowValues.push(board.get(row, column)); |
||||
} |
||||
|
||||
const outcome = getSequenceOutcome(rowValues); |
||||
if (outcome) { |
||||
return outcome; |
||||
} |
||||
} |
||||
|
||||
for (let column = 0; board.hasSquare(0, column); column++) { |
||||
const columnValues = []; |
||||
for (let row = 0; board.hasSquare(row, column); row++) { |
||||
columnValues.push(board.get(row, column)); |
||||
} |
||||
|
||||
const outcome = getSequenceOutcome(columnValues); |
||||
if (outcome) { |
||||
return outcome; |
||||
} |
||||
} |
||||
|
||||
{ |
||||
const rightDownValues = []; |
||||
for (let index = 0; board.hasSquare(index, index); index++) { |
||||
rightDownValues.push(board.get(index, index)); |
||||
} |
||||
|
||||
if (!board.hasRow(rightDownValues.length) && !board.hasSquare(rightDownValues.length - 1, rightDownValues.length)) { |
||||
// It only makes sense to check diagonals on square boards
|
||||
const rightDownOutcome = getSequenceOutcome(rightDownValues); |
||||
if (rightDownOutcome) { |
||||
return rightDownOutcome; |
||||
} |
||||
|
||||
const rightUpValues = []; |
||||
for (let index = 0; board.hasSquare(rightDownValues.length - 1 - index, index); index++) { |
||||
rightUpValues.push(board.get(rightDownValues.length - 1 - index, index)); |
||||
} |
||||
const rightUpOutcome = getSequenceOutcome(rightUpValues); |
||||
if (rightUpOutcome) { |
||||
return rightUpOutcome; |
||||
} |
||||
} |
||||
} |
||||
|
||||
for (let row = 0; board.hasRow(row); row++) { |
||||
for (let column = 0; board.hasSquare(row, column); column++) { |
||||
if (board.get(row, column) === SquareState.Unoccupied) { |
||||
return CurrentOutcome.Undecided; |
||||
} |
||||
} |
||||
} |
||||
|
||||
return CurrentOutcome.Draw; |
||||
}; |
||||
|
||||
export const tictactoeAllRules: GameRules = { |
||||
getBoardOutcome, |
||||
}; |
@ -0,0 +1,124 @@ |
||||
import t from "tap"; |
||||
|
||||
import { CurrentOutcome, SquareState } from "../../datatypes/types.ts"; |
||||
import { getSequenceOutcome } from "./tictactoe-generic.ts"; |
||||
|
||||
void t.test("getSequenceOutcome", async (t) => { |
||||
void t.test("empty sequence", async (t) => { |
||||
t.equal(getSequenceOutcome([]), null); |
||||
}); |
||||
|
||||
void t.test("all sequences of length 1", async (t) => { |
||||
t.equal(getSequenceOutcome([SquareState.Unoccupied]), null); |
||||
t.equal(getSequenceOutcome([SquareState.X]), CurrentOutcome.WinX); |
||||
t.equal(getSequenceOutcome([SquareState.O]), CurrentOutcome.WinO); |
||||
}); |
||||
|
||||
void t.test("all sequences of length 2", async (t) => { |
||||
t.equal(getSequenceOutcome([SquareState.Unoccupied, SquareState.Unoccupied]), null); |
||||
t.equal(getSequenceOutcome([SquareState.Unoccupied, SquareState.X]), null); |
||||
t.equal(getSequenceOutcome([SquareState.Unoccupied, SquareState.O]), null); |
||||
t.equal(getSequenceOutcome([SquareState.X, SquareState.Unoccupied]), null); |
||||
t.equal(getSequenceOutcome([SquareState.X, SquareState.X]), CurrentOutcome.WinX); |
||||
t.equal(getSequenceOutcome([SquareState.X, SquareState.O]), null); |
||||
t.equal(getSequenceOutcome([SquareState.O, SquareState.Unoccupied]), null); |
||||
t.equal(getSequenceOutcome([SquareState.O, SquareState.X]), null); |
||||
t.equal(getSequenceOutcome([SquareState.O, SquareState.O]), CurrentOutcome.WinO); |
||||
}); |
||||
|
||||
void t.test("sequences of length 7", async (t) => { |
||||
void t.test("all X except for the first element", async (t) => { |
||||
t.equal( |
||||
getSequenceOutcome([ |
||||
SquareState.Unoccupied, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
]), |
||||
null, |
||||
); |
||||
t.equal( |
||||
getSequenceOutcome([ |
||||
SquareState.O, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
]), |
||||
null, |
||||
); |
||||
}); |
||||
|
||||
void t.test("all X except for the last element", async (t) => { |
||||
t.equal( |
||||
getSequenceOutcome([ |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.Unoccupied, |
||||
]), |
||||
null, |
||||
); |
||||
t.equal( |
||||
getSequenceOutcome([ |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.O, |
||||
]), |
||||
null, |
||||
); |
||||
}); |
||||
|
||||
void t.test("all X except for the middle element", async (t) => { |
||||
t.equal( |
||||
getSequenceOutcome([ |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.Unoccupied, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
]), |
||||
null, |
||||
); |
||||
t.equal( |
||||
getSequenceOutcome([ |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.O, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
]), |
||||
null, |
||||
); |
||||
}); |
||||
|
||||
t.equal( |
||||
getSequenceOutcome([ |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
SquareState.X, |
||||
]), |
||||
CurrentOutcome.WinX, |
||||
); |
||||
}); |
||||
}); |
@ -0,0 +1,18 @@ |
||||
import { CurrentOutcome, SquareState } from "../../datatypes/types.ts"; |
||||
|
||||
export const getSequenceOutcome = (sequence: SquareState[]) => { |
||||
for (let i = 1; i < sequence.length; i++) { |
||||
if (sequence[i - 1] != sequence[i]) { |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
switch (sequence[0]) { |
||||
case SquareState.X: |
||||
return CurrentOutcome.WinX; |
||||
case SquareState.O: |
||||
return CurrentOutcome.WinO; |
||||
default: |
||||
return null; |
||||
} |
||||
}; |
@ -0,0 +1,227 @@ |
||||
import t from "tap"; |
||||
|
||||
import { Board } from "../../datatypes/board.ts"; |
||||
import { CurrentOutcome } from "../../datatypes/types.ts"; |
||||
import { getBoardOutcome } from "./tictactoe-three-rules.ts"; |
||||
|
||||
void t.test("getBoardOutcome", async (t) => { |
||||
void t.test("1x1 boards", async (t) => { |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X")), CurrentOutcome.Draw); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("O")), CurrentOutcome.Draw); |
||||
}); |
||||
|
||||
void t.test("2x2 boards", async (t) => { |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__|__")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX|X_")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX|XX")), CurrentOutcome.Draw); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("OO|OO")), CurrentOutcome.Draw); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XO|OX")), CurrentOutcome.Draw); |
||||
}); |
||||
|
||||
void t.test("Enumerate all 2x2 boards without O", async (t) => { |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__|__")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__|_X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__|X_")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__|XX")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X|__")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X|_X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X|X_")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X|XX")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_|__")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_|_X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_|X_")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_|XX")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX|__")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX|_X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX|X_")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX|XX")), CurrentOutcome.Draw); |
||||
}); |
||||
|
||||
void t.test("Enumerate all 2x3 boards without O", async (t) => { |
||||
t.equal(getBoardOutcome(Board.fromSerialized("___|___")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("___|__X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("___|_X_")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("___|_XX")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("___|X__")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("___|X_X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("___|XX_")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("___|XXX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__X|___")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__X|__X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__X|_X_")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__X|_XX")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__X|X__")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__X|X_X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__X|XX_")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__X|XXX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X_|___")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X_|__X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X_|_X_")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X_|_XX")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X_|X__")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X_|X_X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X_|XX_")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X_|XXX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_XX|___")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_XX|__X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_XX|_X_")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_XX|_XX")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_XX|X__")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_XX|X_X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_XX|XX_")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_XX|XXX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X__|___")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X__|__X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X__|_X_")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X__|_XX")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X__|X__")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X__|X_X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X__|XX_")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X__|XXX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_X|___")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_X|__X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_X|_X_")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_X|_XX")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_X|X__")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_X|X_X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_X|XX_")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_X|XXX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX_|___")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX_|__X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX_|_X_")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX_|_XX")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX_|X__")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX_|X_X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX_|XX_")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX_|XXX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XXX|___")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XXX|__X")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XXX|_X_")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XXX|_XX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XXX|X__")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XXX|X_X")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XXX|XX_")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XXX|XXX")), CurrentOutcome.WinX); |
||||
}); |
||||
|
||||
void t.test("Enumerate all 3x2 boards without O", async (t) => { |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__|__|__")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__|__|_X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__|__|X_")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__|__|XX")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__|_X|__")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__|_X|_X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__|_X|X_")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__|_X|XX")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__|X_|__")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__|X_|_X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__|X_|X_")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__|X_|XX")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__|XX|__")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__|XX|_X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__|XX|X_")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__|XX|XX")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X|__|__")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X|__|_X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X|__|X_")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X|__|XX")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X|_X|__")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X|_X|_X")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X|_X|X_")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X|_X|XX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X|X_|__")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X|X_|_X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X|X_|X_")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X|X_|XX")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X|XX|__")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X|XX|_X")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X|XX|X_")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_X|XX|XX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_|__|__")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_|__|_X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_|__|X_")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_|__|XX")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_|_X|__")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_|_X|_X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_|_X|X_")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_|_X|XX")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_|X_|__")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_|X_|_X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_|X_|X_")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_|X_|XX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_|XX|__")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_|XX|_X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_|XX|X_")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X_|XX|XX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX|__|__")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX|__|_X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX|__|X_")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX|__|XX")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX|_X|__")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX|_X|_X")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX|_X|X_")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX|_X|XX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX|X_|__")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX|X_|_X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX|X_|X_")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX|X_|XX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX|XX|__")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX|XX|_X")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX|XX|X_")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX|XX|XX")), CurrentOutcome.WinX); |
||||
}); |
||||
|
||||
void t.test("3x3 boards", async (t) => { |
||||
t.equal(getBoardOutcome(Board.fromSerialized("___|___|___")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("___|_X_|___")), CurrentOutcome.Undecided); |
||||
|
||||
t.equal(getBoardOutcome(Board.fromSerialized("XX_|___|___")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("___|___|_XX")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X__|X__|___")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("___|__X|__X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X__|_X_|___")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("___|_X_|__X")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__X|_X_|___")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("___|_X_|X__")), CurrentOutcome.Undecided); |
||||
|
||||
t.equal(getBoardOutcome(Board.fromSerialized("XXX|O_O|O_O")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("O_O|O_O|XXX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XOO|X__|XOO")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("OOX|__X|OOX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XOO|OXO|OOX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("OOX|OXO|XOO")), CurrentOutcome.WinX); |
||||
|
||||
t.equal(getBoardOutcome(Board.fromSerialized("XXO|XOX|OXX")), CurrentOutcome.WinO); |
||||
|
||||
t.equal(getBoardOutcome(Board.fromSerialized("XOX|XXO|OXO")), CurrentOutcome.Draw); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XOX|XXO|OX_")), CurrentOutcome.Undecided); |
||||
}); |
||||
|
||||
void t.test("5x5 boards", async (t) => { |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_____|_____|_____|_____|_____")), CurrentOutcome.Undecided); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("XXX__|_____|_____|_____|_____")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X____|X____|X____|_____|_____")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("X____|_X___|__X__|_____|_____")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("__X__|_X___|X____|_____|_____")), CurrentOutcome.WinX); |
||||
|
||||
t.equal(getBoardOutcome(Board.fromSerialized("_____|_____|_____|_____|__XXX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_____|_____|____X|____X|____X")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_____|_____|__X__|___X_|____X")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_____|_____|____X|___X_|__X__")), CurrentOutcome.WinX); |
||||
}); |
||||
|
||||
void t.test("6x5 boards", async (t) => { |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_____|_____|_____|_____|_____|__XXX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_____|_____|_____|____X|____X|____X")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_____|_____|_____|__X__|___X_|____X")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("_____|_____|_____|____X|___X_|__X__")), CurrentOutcome.WinX); |
||||
}); |
||||
|
||||
void t.test("5x6 boards", async (t) => { |
||||
t.equal(getBoardOutcome(Board.fromSerialized("______|______|______|______|___XXX")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("______|______|_____X|_____X|_____X")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("______|______|___X__|____X_|_____X")), CurrentOutcome.WinX); |
||||
t.equal(getBoardOutcome(Board.fromSerialized("______|______|_____X|____X_|___X__")), CurrentOutcome.WinX); |
||||
}); |
||||
}); |
@ -0,0 +1,40 @@ |
||||
import { Test } from "tap"; |
||||
import { ExpectedOutcome, GameRules, Opponent, Player, getOccupiedStateByPlayer } from "../datatypes/types.ts"; |
||||
import { computeAllSolutions } from "../gameplay/solver.ts"; |
||||
import { Board } from "../datatypes/board.ts"; |
||||
|
||||
export const checkSolutionsComplete = ( |
||||
t: Test, |
||||
rules: GameRules, |
||||
rows: number, |
||||
columns: number, |
||||
expectedSolutions: Record<string, ExpectedOutcome>, |
||||
) => { |
||||
t.matchOnlyStrict(Object.fromEntries(computeAllSolutions(rows, columns, rules).entries()), expectedSolutions); |
||||
}; |
||||
|
||||
export const checkSolutionsIncomplete = ( |
||||
t: Test, |
||||
rules: GameRules, |
||||
rows: number, |
||||
columns: number, |
||||
expectedSolutionsCount: number, |
||||
expectedSolutionsIncomplete: Record<string, ExpectedOutcome>, |
||||
) => { |
||||
const allSolutions = computeAllSolutions(rows, columns, rules); |
||||
t.equal(allSolutions.size, expectedSolutionsCount); |
||||
t.matchStrict(Object.fromEntries(allSolutions.entries()), expectedSolutionsIncomplete); |
||||
}; |
||||
|
||||
export const checkNextMove = ( |
||||
t: Test, |
||||
opponent: Opponent, |
||||
currentBoardSerialized: string, |
||||
currentPlayer: Player, |
||||
expectedNextBoardSerialized: string, |
||||
) => { |
||||
const currentBoard = Board.fromSerialized(currentBoardSerialized); |
||||
const nextMove = opponent.getNextMove(currentBoard, currentPlayer); |
||||
const nextBoard = currentBoard.with(nextMove.row, nextMove.column, getOccupiedStateByPlayer(currentPlayer)); |
||||
t.equal(nextBoard.serialize(), expectedNextBoardSerialized); |
||||
}; |
@ -0,0 +1,59 @@ |
||||
import t from "tap"; |
||||
|
||||
import { FinalOutcome, Player } from "../datatypes/types.ts"; |
||||
import { createOpponent } from "../gameplay/opponent.ts"; |
||||
import { computeAllSolutions } from "../gameplay/solver.ts"; |
||||
import { checkNextMove, checkSolutionsIncomplete } from "./test-helpers.ts"; |
||||
import { gamesRules } from "../game-variants/index.ts"; |
||||
|
||||
for (const [gameVariant, rules] of Object.entries(gamesRules)) { |
||||
void t.test(`Checking 3x3 game for ${gameVariant}`, async (t) => { |
||||
void t.test("computeAllSolutions", async (t) => { |
||||
// number 5478 taken from https://math.stackexchange.com/a/613505
|
||||
checkSolutionsIncomplete(t, rules, 3, 3, 5478, { |
||||
"___|___|___": { finalOutcome: FinalOutcome.Draw, movesLeft: 9 }, |
||||
"X__|___|___": { finalOutcome: FinalOutcome.Draw, movesLeft: 8 }, |
||||
"_X_|___|___": { finalOutcome: FinalOutcome.Draw, movesLeft: 8 }, |
||||
"___|_X_|___": { finalOutcome: FinalOutcome.Draw, movesLeft: 8 }, |
||||
"XO_|___|___": { finalOutcome: FinalOutcome.WinX, movesLeft: 5 }, |
||||
"X__|___|_O_": { finalOutcome: FinalOutcome.WinX, movesLeft: 5 }, |
||||
"X_O|___|___": { finalOutcome: FinalOutcome.WinX, movesLeft: 5 }, |
||||
"X__|___|__O": { finalOutcome: FinalOutcome.WinX, movesLeft: 5 }, |
||||
"OO_|___|_XX": { finalOutcome: FinalOutcome.WinX, movesLeft: 1 }, |
||||
"OO_|__X|_XX": { finalOutcome: FinalOutcome.WinO, movesLeft: 1 }, |
||||
}); |
||||
|
||||
void t.test("createOpponent", async (t) => { |
||||
const outcomes = computeAllSolutions(3, 3, rules); |
||||
const opponent = createOpponent(outcomes); |
||||
|
||||
checkNextMove(t, opponent, "___|___|___", Player.X, "X__|___|___"); |
||||
checkNextMove(t, opponent, "X__|___|___", Player.O, "X__|_O_|___"); |
||||
checkNextMove(t, opponent, "X__|_O_|___", Player.X, "XX_|_O_|___"); |
||||
checkNextMove(t, opponent, "XX_|_O_|___", Player.O, "XXO|_O_|___"); |
||||
checkNextMove(t, opponent, "XXO|_O_|___", Player.X, "XXO|_O_|X__"); |
||||
checkNextMove(t, opponent, "XXO|_O_|X__", Player.O, "XXO|OO_|X__"); |
||||
checkNextMove(t, opponent, "XXO|OO_|X__", Player.X, "XXO|OOX|X__"); |
||||
checkNextMove(t, opponent, "XXO|OOX|X__", Player.O, "XXO|OOX|XO_"); |
||||
checkNextMove(t, opponent, "XXO|OOX|XO_", Player.X, "XXO|OOX|XOX"); |
||||
|
||||
checkNextMove(t, opponent, "___|___|__X", Player.O, "___|_O_|__X"); |
||||
checkNextMove(t, opponent, "___|_O_|__X", Player.X, "X__|_O_|__X"); |
||||
checkNextMove(t, opponent, "X__|_O_|__X", Player.O, "XO_|_O_|__X"); |
||||
checkNextMove(t, opponent, "XO_|_O_|__X", Player.X, "XO_|_O_|_XX"); |
||||
checkNextMove(t, opponent, "XO_|_O_|_XX", Player.O, "XO_|_O_|OXX"); |
||||
checkNextMove(t, opponent, "XO_|_O_|OXX", Player.X, "XOX|_O_|OXX"); |
||||
checkNextMove(t, opponent, "XOX|_O_|OXX", Player.O, "XOX|_OO|OXX"); |
||||
checkNextMove(t, opponent, "XOX|_OO|OXX", Player.X, "XOX|XOO|OXX"); |
||||
|
||||
checkNextMove(t, opponent, "XO_|___|___", Player.X, "XO_|X__|___"); |
||||
checkNextMove(t, opponent, "XO_|X__|___", Player.O, "XO_|X__|O__"); |
||||
checkNextMove(t, opponent, "XO_|X__|O__", Player.X, "XO_|XX_|O__"); |
||||
checkNextMove(t, opponent, "XO_|XX_|O__", Player.O, "XOO|XX_|O__"); |
||||
checkNextMove(t, opponent, "XOO|XX_|O__", Player.X, "XOO|XXX|O__"); |
||||
|
||||
checkNextMove(t, opponent, "XO_|XXO|O__", Player.X, "XO_|XXO|O_X"); |
||||
}); |
||||
}); |
||||
}); |
||||
} |
@ -1,33 +1,16 @@ |
||||
import t, { Test } from "tap"; |
||||
import t from "tap"; |
||||
|
||||
import { Board } from "../datatypes/board.ts"; |
||||
import { ExpectedOutcome, FinalOutcome, Opponent, Player, getOccupiedStateByPlayer } from "../datatypes/types.ts"; |
||||
import { FinalOutcome, Player } from "../datatypes/types.ts"; |
||||
import { createOpponent } from "../gameplay/opponent.ts"; |
||||
import { computeAllSolutions } from "../gameplay/solver.ts"; |
||||
import { rules } from "./tictactoe-rules.ts"; |
||||
import { tictactoeThreeRules } from "../game-variants/tictactoe/tictactoe-three-rules.ts"; |
||||
import { checkNextMove, checkSolutionsComplete } from "./test-helpers.ts"; |
||||
|
||||
void t.test("computeAllSolutions", async (t) => { |
||||
const checkSolutionsComplete = ( |
||||
rows: number, |
||||
columns: number, |
||||
expectedSolutions: Record<string, ExpectedOutcome>, |
||||
) => { |
||||
t.matchOnlyStrict(Object.fromEntries(computeAllSolutions(rows, columns, rules).entries()), expectedSolutions); |
||||
}; |
||||
|
||||
const checkSolutionsIncomplete = ( |
||||
rows: number, |
||||
columns: number, |
||||
expectedSolutionsCount: number, |
||||
expectedSolutionsIncomplete: Record<string, ExpectedOutcome>, |
||||
) => { |
||||
const allSolutions = computeAllSolutions(rows, columns, rules); |
||||
t.equal(allSolutions.size, expectedSolutionsCount); |
||||
t.matchStrict(Object.fromEntries(allSolutions.entries()), expectedSolutionsIncomplete); |
||||
}; |
||||
const rules = tictactoeThreeRules; |
||||
|
||||
void t.test("computeAllSolutions", async (t) => { |
||||
// smallest possible board where X can win
|
||||
checkSolutionsComplete(1, 5, { |
||||
checkSolutionsComplete(t, rules, 1, 5, { |
||||
|