parent
e424772020
commit
2d03fc4006
@ -0,0 +1,345 @@ |
||||
import t from "tap"; |
||||
|
||||
import { Board } from "./board.ts"; |
||||
import { CurrentOutcome, SquareState } from "./datatypes.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,89 @@ |
||||
import { CurrentOutcome, GameRules, SquareState } from "./datatypes.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; |
||||
} |
||||
}; |
||||
|
||||
export const getBoardOutcome: GameRules["getBoardOutcome"] = (board) => { |
||||
for (let row = 0; board.hasRow(row); row++) { |
||||
for (let column = 2; board.hasSquare(row, column); column++) { |
||||
const tripleLeft = [ |
||||
board.get(row, column - 2), |
||||
board.get(row, column - 1), |
||||
board.get(row, column - 0), |
||||
]; |
||||
|
||||
const outcome = getSequenceOutcome(tripleLeft); |
||||
if (outcome) { |
||||
return outcome; |
||||
} |
||||
} |
||||
} |
||||
|
||||
for (let row = 2; board.hasRow(row); row++) { |
||||
for (let column = 0; board.hasSquare(row, column); column++) { |
||||
const tripleUp = [ |
||||
board.get(row - 2, column), |
||||
board.get(row - 1, column), |
||||
board.get(row - 0, column), |
||||
]; |
||||
|
||||
const outcome = getSequenceOutcome(tripleUp); |
||||
if (outcome) { |
||||
return outcome; |
||||
} |
||||
} |
||||
} |
||||
|
||||
for (let row = 2; board.hasRow(row); row++) { |
||||
for (let column = 2; board.hasSquare(row, column); column++) { |
||||
{ |
||||
const tripleUpLeft = [ |
||||
board.get(row - 2, column - 2), |
||||
board.get(row - 1, column - 1), |
||||
board.get(row - 0, column - 0), |
||||
]; |
||||
const upLeftOutcome = getSequenceOutcome(tripleUpLeft); |
||||
if (upLeftOutcome) { |
||||
return upLeftOutcome; |
||||
} |
||||
} |
||||
|
||||
{ |
||||
const tripleCross = [ |
||||
board.get(row - 0, column - 2), |
||||
board.get(row - 1, column - 1), |
||||
board.get(row - 2, column - 0), |
||||
]; |
||||
|
||||
const crossOutcome = getSequenceOutcome(tripleCross); |
||||
if (crossOutcome) { |
||||
return crossOutcome; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
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; |
||||
}; |
Loading…
Reference in new issue