From 300847c825088d00767b6c18240b45d18e6f551b Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Wed, 18 Oct 2023 11:39:47 +0200 Subject: [PATCH] specs2->munit WIP --- test-kit/src/test/scala/BerserkTest.scala | 87 +++--- test-kit/src/test/scala/BishopTest.scala | 28 +- test-kit/src/test/scala/BoardTest.scala | 61 ++-- .../src/test/scala/CastlingKingSideTest.scala | 102 +++++++ .../test/scala/CastlingQueenSideTest.scala | 112 +++++++ test-kit/src/test/scala/CastlingTest.scala | 281 ++---------------- test-kit/src/test/scala/ChessTest.scala | 12 + 7 files changed, 343 insertions(+), 340 deletions(-) create mode 100644 test-kit/src/test/scala/CastlingKingSideTest.scala create mode 100644 test-kit/src/test/scala/CastlingQueenSideTest.scala diff --git a/test-kit/src/test/scala/BerserkTest.scala b/test-kit/src/test/scala/BerserkTest.scala index 57988453a..61fc5b9c1 100644 --- a/test-kit/src/test/scala/BerserkTest.scala +++ b/test-kit/src/test/scala/BerserkTest.scala @@ -2,53 +2,48 @@ package chess import scala.language.implicitConversions -class BerserkTest extends ChessSpecs: +class BerserkTest extends ChessTest: + + import clockConv.given def whiteBerserk(minutes: Int, seconds: Int) = Clock(minutes * 60, seconds).goBerserk(White).remainingTime(White).centis * .01 - given Conversion[Int, Clock.LimitSeconds] = Clock.LimitSeconds(_) - given Conversion[Int, Clock.IncrementSeconds] = Clock.IncrementSeconds(_) - - "berserkable" should: - "yep" in: - Clock.Config(60 * 60, 0).berserkable must_== true - Clock.Config(1 * 60, 0).berserkable must_== true - Clock.Config(60 * 60, 60).berserkable must_== true - Clock.Config(1 * 60, 0).berserkable must_== true - "nope" in: - Clock.Config(0 * 60, 1).berserkable must_== false - Clock.Config(0 * 60, 10).berserkable must_== false - "berserk flags" should: - "white" in: - Clock(60, 0).berserked(White) must_== false - Clock(60, 0).goBerserk(White).berserked(White) must_== true - "black" in: - Clock(60, 0).berserked(Black) must_== false - Clock(60, 0).goBerserk(Black).berserked(Black) must_== true - "initial time penalty, no increment" should: - "10+0" in: - whiteBerserk(10, 0) must_== 5 * 60 - "5+0" in: - whiteBerserk(5, 0) must_== 2.5 * 60 - "3+0" in: - whiteBerserk(3, 0) must_== 1.5 * 60 - "1+0" in: - whiteBerserk(1, 0) must_== 0.5 * 60 - "initial time penalty, with increment" should: - "4+4" in: - whiteBerserk(4, 4) must_== 2 * 60 - "3+2" in: - whiteBerserk(3, 2) must_== 1.5 * 60 - "2+10" in: - whiteBerserk(2, 10) must_== 2 * 60 - "10+5" in: - whiteBerserk(10, 5) must_== 5 * 60 - "10+2" in: - whiteBerserk(10, 2) must_== 5 * 60 - "1+1" in: - whiteBerserk(1, 1) must_== 0.5 * 60 - "1+3" in: - whiteBerserk(1, 3) must_== 1 * 60 - "1+5" in: - whiteBerserk(1, 5) must_== 1 * 60 + test("berserkable: yep"): + assertEquals(Clock.Config(60 * 60, 0).berserkable , true) + assertEquals(Clock.Config(1 * 60, 0).berserkable , true) + assertEquals(Clock.Config(60 * 60, 60).berserkable , true) + assertEquals(Clock.Config(1 * 60, 0).berserkable , true) + test("berserkable: nope"): + assertEquals(Clock.Config(0 * 60, 1).berserkable , false) + assertEquals(Clock.Config(0 * 60, 10).berserkable , false) + test("berserk flags: white"): + assertEquals(Clock(60, 0).berserked(White) , false) + assertEquals(Clock(60, 0).goBerserk(White).berserked(White) , true) + test("berserk flags: black"): + assertEquals(Clock(60, 0).berserked(Black) , false) + assertEquals(Clock(60, 0).goBerserk(Black).berserked(Black) , true) + test("initial time penalty, no increment: 10+0"): + assertEquals(whiteBerserk(10, 0) , 5 * 60d) + test("initial time penalty, no increment: 5+0"): + assertEquals(whiteBerserk(5, 0) , 2.5 * 60d) + test("initial time penalty, no increment: 3+0"): + assertEquals(whiteBerserk(3, 0) , 1.5 * 60d) + test("initial time penalty, no increment: 1+0"): + assertEquals(whiteBerserk(1, 0) , 0.5 * 60d) + test("initial time penalty, with increment: 4+4"): + assertEquals(whiteBerserk(4, 4) , 2 * 60d) + test("initial time penalty, with increment: 3+2"): + assertEquals(whiteBerserk(3, 2) , 1.5 * 60d) + test("initial time penalty, with increment: 2+10"): + assertEquals(whiteBerserk(2, 10) , 2 * 60d) + test("initial time penalty, with increment: 10+5"): + assertEquals(whiteBerserk(10, 5) , 5 * 60d) + test("initial time penalty, with increment: 10+2"): + assertEquals(whiteBerserk(10, 2) , 5 * 60d) + test("initial time penalty, with increment: 1+1"): + assertEquals(whiteBerserk(1, 1) , 0.5 * 60d) + test("initial time penalty, with increment: 1+3"): + assertEquals(whiteBerserk(1, 3) , 1 * 60d) + test("initial time penalty, with increment: 1+5"): + assertEquals(whiteBerserk(1, 5) , 1 * 60d) diff --git a/test-kit/src/test/scala/BishopTest.scala b/test-kit/src/test/scala/BishopTest.scala index 29788f591..f4fec28bc 100644 --- a/test-kit/src/test/scala/BishopTest.scala +++ b/test-kit/src/test/scala/BishopTest.scala @@ -3,12 +3,10 @@ package chess import scala.language.implicitConversions import Square.* -class BishopTest extends ChessSpecs: +class BishopTest extends ChessTest: - "a bishop" should: - - "not move to positions that are occupied by the same colour" in: - val board = """ + test("not move to positions that are occupied by the same colour"): + val board = """ k B @@ -18,9 +16,9 @@ N B P PPPPPPPP NBQKBNR """ - board destsFrom C4 must bePoss( - board, - """ + assertEquals( + visualDests(board, board destsFrom C4), + """ k B x x x x @@ -30,10 +28,10 @@ N B P PPPPPPPP NBQKBNR """ - ) + ) - "capture opponent pieces" in: - val board = """ + test("capture opponent pieces"): + val board = """ k B q p @@ -43,9 +41,9 @@ N B P PPPPPPPP NBQKBNR """ - board destsFrom C4 must bePoss( - board, - """ + assertEquals( + visualDests(board, board destsFrom C4), + """ k B x x x @@ -55,4 +53,4 @@ N B P PPPPPPPP NBQKBNR """ - ) + ) diff --git a/test-kit/src/test/scala/BoardTest.scala b/test-kit/src/test/scala/BoardTest.scala index b75dcca73..c95ebb01b 100644 --- a/test-kit/src/test/scala/BoardTest.scala +++ b/test-kit/src/test/scala/BoardTest.scala @@ -3,14 +3,14 @@ package chess import scala.language.implicitConversions import Square.* -class BoardTest extends ChessSpecs: +class BoardTest extends ChessTest: val board = makeBoard - "a board" should: - - "position pieces correctly" in: - board.pieces must havePairs( + test("position pieces correctly"): + assertEquals( + board.pieces, + Map( A1 -> (White - Rook), B1 -> (White - Knight), C1 -> (White - Bishop), @@ -44,42 +44,47 @@ class BoardTest extends ChessSpecs: G8 -> (Black - Knight), H8 -> (Black - Rook) ) + ) - "have pieces by default" in: - board.allPieces must not beEmpty + test("have pieces by default"): + assertNot(board.allPieces.isEmpty) - "have castling rights by default" in: - board.history.castles == Castles.init + test("have castling rights by default"): + assertEquals(board.history.castles, Castles.init) - "allow a piece to be placed" in: - board.place(White - Rook, E3) must beSome: - (_: Board)(E3) must_== Option(White - Rook) + test("allow a piece to be placed"): + assertEquals(board.place(White - Rook, E3).get.apply(E3), Option(White - Rook)) - "allow a piece to be taken" in: - board take A1 must beSome: - (_: Board)(A1) must beNone + test("allow a piece to be taken"): + board take A1 assertSome: b => + assertEquals(b(A1), None) - "allow a piece to move" in: - board.move(E2, E4) must beSome: - (_: Board)(E4) must_== Option(White - Pawn) + test("allow a piece to move"): + board.move(E2, E4) assertSome: b => + assertEquals(b(E4), Option(White - Pawn)) - "not allow an empty position to move" in: - board.move(E5, E6) must beNone + test("not allow an empty position to move"): + assertEquals(board.move(E5, E6), None) - "not allow a piece to move to an occupied position" in: - board.move(A1, A2) must beNone + test("not allow a piece to move to an occupied position"): + assertEquals(board.move(A1, A2), None) - "allow chaining actions" in: - makeEmptyBoard.seq( + test("allow chaining actions"): + makeEmptyBoard + .seq( _.place(White - Pawn, A2), _.place(White - Pawn, A3), _.move(A2, A4) - ) must beSome: - (_: Board)(A4) must_== Option(White - Pawn) + ) + .assertSome: b => + assertEquals(b(A4), Option(White - Pawn)) - "fail on bad actions chain" in: + test("fail on bad actions chain"): + assertEquals( makeEmptyBoard.seq( _.place(White - Pawn, A2), _.place(White - Pawn, A3), _.move(B2, B4) - ) must beNone + ), + None + ) diff --git a/test-kit/src/test/scala/CastlingKingSideTest.scala b/test-kit/src/test/scala/CastlingKingSideTest.scala new file mode 100644 index 000000000..7add35920 --- /dev/null +++ b/test-kit/src/test/scala/CastlingKingSideTest.scala @@ -0,0 +1,102 @@ +package chess + +import scala.language.implicitConversions +import Square.* +import variant.FromPosition + +class CastlingKingSideTest extends ChessTest: + + import compare.dests + + val goodHist = """ +PPPPPPPP +R QK R""" + val badHist = goodHist updateHistory (_ withoutCastles White) + test("impossible"): + assertEquals(goodHist.place(White.bishop, F1).flatMap(_ destsFrom E1), Set()) + assertEquals(goodHist.place(White.knight, G1).flatMap(_ destsFrom E1), Set(F1)) + assertEquals(badHist destsFrom E1, Set(F1)) + val board960 = """ +PPPPPPPP +RQK R """.chess960 withHistory History.castle(White, kingSide = true, queenSide = true) + assertEquals(board960.place(White.bishop, D1).flatMap(_ destsFrom C1), Set()) + assertEquals(board960.place(White.knight, F1).flatMap(_ destsFrom C1), Set(D1)) + test("possible"): + val game = Game(goodHist, White) + assertEquals(game.board destsFrom E1, Set(F1, G1, H1)) + assertGame( + game.playMove(E1, G1).get, + """ +PPPPPPPP +R Q RK """ + ) + val board: Board = """ + PPPPP +B KR""".chess960 + val g2 = Game(board, White) + assertEquals(board destsFrom G1, Set(F1, H1)) + assertGame( + g2.playMove(G1, H1).get, + """ + PPPPP +B RK """ + ) + test("chess960 close kingside with 2 rooks around"): + val board: Board = """ +PPPPPPPP +RKRBB """.chess960 + assertEquals(board destsFrom B1, Set()) + test("chess960 close queenside"): + val board: Board = """ +PPPPPPPP +RK B""".chess960 + val game = Game(board, White) + assertEquals(board destsFrom B1, Set(A1, C1)) + assertGame( + game.playMove(B1, A1).get, + """ +PPPPPPPP + KR B""" + ) + test("chess960 close queenside as black"): + val game = Game( + """ + b rkr q +p pppppp + p n + + + + + K""".chess960, + Black + ) + assertEquals(game.board destsFrom E8, Set(D8, F8)) + assertGame( + game.playMove(E8, D8).get, + """ + bkr r q +p pppppp + p n + + + + + K""" + ) + test("from position with chess960 castling"): + val game = Game( + makeBoard( + """rk r +pppbnppp + p n +P Pp + P q +R NP +PP PP +KNQRB""", + FromPosition + ), + Black + ) + assertEquals(game.board destsFrom B8, Set(A8, C8, E8)) diff --git a/test-kit/src/test/scala/CastlingQueenSideTest.scala b/test-kit/src/test/scala/CastlingQueenSideTest.scala new file mode 100644 index 000000000..0e172edba --- /dev/null +++ b/test-kit/src/test/scala/CastlingQueenSideTest.scala @@ -0,0 +1,112 @@ +package chess + +import scala.language.implicitConversions +import Square.* +import variant.Chess960 +import format.EpdFen + +class CastlingQueenSideTest extends ChessTest: + + import compare.dests + + val goodHist = """ +PPPPPPPP +R KB R""" + val badHist = goodHist updateHistory (_ withoutCastles White) + test("impossible: near queen in the way"): + assertEquals(goodHist place (White.queen, D1) flatMap (_ destsFrom E1), Set()) + test("impossible: bishop in the way"): + assertEquals(goodHist place (White.bishop, C1) flatMap (_ destsFrom E1), Set(D1)) + test("impossible: distant knight in the way"): + assertEquals(goodHist place (White.knight, C1) flatMap (_ destsFrom E1), Set(D1)) + test("impossible: not allowed by history"): + assertEquals(badHist destsFrom E1, Set(D1)) + test("possible: viable moves"): + val game = Game(goodHist, White) + assertEquals(game.board destsFrom E1, Set(A1, C1, D1)) + test("possible: correct new board"): + val game = Game(goodHist, White) + assertGame( + game.playMove(E1, C1).get, + """ +PPPPPPPP + KR B R""" + ) + + val board = """ +PPPPPPPP +R K R""" withHistory History.castle(White, kingSide = true, queenSide = true) + test("impact history: if king castles kingside"): + val game = Game(board, White) + val g2 = game.playMove(E1, G1).get + assertGame( + g2, + """ +PPPPPPPP +R RK """ + ) + assertEquals(g2.board destsFrom G1, Set(H1)) + assertEquals( + g2.board + .seq( + _ move (F1, H1), + _ move (G1, E1) + ) + .flatMap(_ destsFrom E1), + Set(D1, F1) + ) + test("impact history: if king castles queenside"): + val game = Game(board, White) + val g2 = game.playMove(E1, C1).get + assertGame( + g2, + """ +PPPPPPPP + KR R""" + ) + assertEquals(g2.board destsFrom C1, Set(B1)) + assertEquals( + g2.board + .seq( + _ move (D1, A1), + _ move (C1, E1) + ) + .flatMap(_ destsFrom E1), + Set(D1, F1) + ) + test("impact history: if king moves to the right"): + val game = Game(board, White) + val g2 = game.playMove(E1, F1).get as White + assertEquals(g2.board destsFrom F1, Set(E1, G1)) + val g3 = g2.playMove(F1, E1).get as White + assertEquals(g3.board destsFrom E1, Set(D1, F1)) + test("impact history: if king moves to the left"): + val game = Game(board, White) + val g2 = game.playMove(E1, D1).get as White + assertEquals(g2.board destsFrom D1, Set(C1, E1)) + val g3 = g2.playMove(D1, E1).get as White + assertEquals(g3.board destsFrom E1, Set(D1, F1)) + test("impact history: if kingside rook moves"): + val game = Game(board, White) + val g2 = game.playMove(H1, G1).get as White + assertEquals(g2.board destsFrom E1, Set(C1, D1, F1, A1)) + val g3 = g2.playMove(A1, B1).get + assertEquals(g3.board destsFrom E1, Set(D1, F1)) + test("impact history: if queenside rook moves"): + val game = Game(board, White) + val g2 = game.playMove(A1, B1).get as White + assertEquals(g2.board destsFrom E1, Set(D1, F1, G1, H1)) + val g3 = g2.playMove(H1, G1).get + assertEquals(g3.board destsFrom E1, Set(D1, F1)) + + test("chess960"): + val fenPosition = EpdFen("r3k2r/8/8/8/8/8/8/1R2K2R b KQk - 1 1") + val init = fenToGame(fenPosition, Chess960) + val game = init.playMoves((A8, A1), (H1, H2), (A1, A7)).get + assert(game.situation.legalMoves.exists(_.castles)) + + test("test games"): + val fenPosition = EpdFen("1rnk1bqr/pppp1bpp/1n2p3/5p2/5P2/1N1N4/PPPPPBPP/1R1K1BQR w KQkq - 0 5") + val init = fenToGame(fenPosition, Chess960) + assert(init.situation.legalMoves.exists(_.castles)) + assert(init.situation.legalMoves.exists(_.castle.exists(_.side == QueenSide))) diff --git a/test-kit/src/test/scala/CastlingTest.scala b/test-kit/src/test/scala/CastlingTest.scala index dee5c48fd..07ff1ba15 100644 --- a/test-kit/src/test/scala/CastlingTest.scala +++ b/test-kit/src/test/scala/CastlingTest.scala @@ -2,255 +2,34 @@ package chess import scala.language.implicitConversions import Square.* -import variant.FromPosition -import variant.Chess960 -import format.EpdFen -class CastlingTest extends ChessSpecs: - - "king side" should: - val goodHist = """ -PPPPPPPP -R QK R""" - val badHist = goodHist updateHistory (_ withoutCastles White) - "impossible" in: - "standard chess" in: - "near bishop in the way" in: - goodHist place (White.bishop, F1) flatMap (_ destsFrom E1) must bePoss() - "distant knight in the way" in: - goodHist place (White.knight, G1) flatMap (_ destsFrom E1) must bePoss(F1) - "not allowed by history" in: - badHist destsFrom E1 must bePoss(F1) - "chess960" in: - val board960 = """ -PPPPPPPP -RQK R """.chess960 withHistory History.castle(White, kingSide = true, queenSide = true) - "near bishop in the way" in: - board960 place (White.bishop, D1) flatMap (_ destsFrom C1) must bePoss() - "distant knight in the way" in: - board960 place (White.knight, F1) flatMap (_ destsFrom C1) must bePoss(D1) - "possible" in: - "standard" in: - val game = Game(goodHist, White) - "viable moves" in: - game.board destsFrom E1 must bePoss(F1, G1, H1) - "correct new board" in: - game.playMove(E1, G1) must beGame(""" -PPPPPPPP -R Q RK """) - "chess960 close kingside" in: - val board: Board = """ - PPPPP -B KR""".chess960 - val game = Game(board, White) - "viable moves" in: - board destsFrom G1 must bePoss(F1, H1) - "correct new board" in: - game.playMove(G1, H1) must beGame(""" - PPPPP -B RK """) - "chess960 close kingside with 2 rooks around" in: - val board: Board = """ -PPPPPPPP -RKRBB """.chess960 - "viable moves" in: - board destsFrom B1 must bePoss() - "chess960 close queenside" in: - val board: Board = """ -PPPPPPPP -RK B""".chess960 - val game = Game(board, White) - "viable moves" in: - board destsFrom B1 must bePoss(A1, C1) - "correct new board" in: - game.playMove(B1, A1) must beGame(""" -PPPPPPPP - KR B""") - "chess960 close queenside as black" in: - val game = Game( - """ - b rkr q -p pppppp - p n - - - - - K""".chess960, - Black - ) - "viable moves" in: - game.board destsFrom E8 must bePoss(D8, F8) - "correct new board" in: - game.playMove(E8, D8) must beGame(""" - bkr r q -p pppppp - p n - - - - - K""") - "from position with chess960 castling" in: - val game = Game( - makeBoard( - """rk r -pppbnppp - p n -P Pp - P q -R NP - PP PP - KNQRB""", - FromPosition - ), - Black - ) - "dests" in: - game.board destsFrom B8 must bePoss(A8, C8, E8) - - "queen side" should: - val goodHist = """ -PPPPPPPP -R KB R""" - val badHist = goodHist updateHistory (_ withoutCastles White) - "impossible" in: - "near queen in the way" in: - goodHist place (White.queen, D1) flatMap (_ destsFrom E1) must bePoss() - "bishop in the way" in: - goodHist place (White.bishop, C1) flatMap (_ destsFrom E1) must bePoss(D1) - "distant knight in the way" in: - goodHist place (White.knight, C1) flatMap (_ destsFrom E1) must bePoss(D1) - "not allowed by history" in: - badHist destsFrom E1 must bePoss(D1) - "possible" in: - val game = Game(goodHist, White) - "viable moves" in: - game.board destsFrom E1 must bePoss(A1, C1, D1) - "correct new board" in: - game.playMove(E1, C1) must beGame(""" -PPPPPPPP - KR B R""") - - "impact history" in: - val board = """ -PPPPPPPP -R K R""" withHistory History.castle(White, kingSide = true, queenSide = true) - val game = Game(board, White) - "if king castles kingside" in: - val g2 = game.playMove(E1, G1) - "correct new board" in: - g2 must beGame(""" -PPPPPPPP -R RK """) - "cannot castle queenside anymore" in: - g2.toOption flatMap (_.board destsFrom G1) must bePoss(H1) - "cannot castle kingside anymore even if the position looks good" in: - g2.toOption flatMap (_.board.seq( - _ move (F1, H1), - _ move (G1, E1) - )) flatMap (_ destsFrom E1) must bePoss(D1, F1) - "if king castles queenside" in: - val g2 = game.playMove(E1, C1) - "correct new board" in: - g2 must beGame(""" -PPPPPPPP - KR R""") - "cannot castle kingside anymore" in: - g2.toOption flatMap (_.board destsFrom C1) must bePoss(B1) - "cannot castle queenside anymore even if the position looks good" in: - g2.toOption flatMap (_.board.seq( - _ move (D1, A1), - _ move (C1, E1) - )) flatMap (_ destsFrom E1) must bePoss(D1, F1) - "if king moves" in: - "to the right" in: - val g2 = game.playMove(E1, F1) map (_ as White) - "cannot castle anymore" in: - g2.toOption flatMap (_.board destsFrom F1) must bePoss(E1, G1) - "neither if the king comes back" in: - val g3 = g2 flatMap (_.playMove(F1, E1)) map (_ as White) - g3.toOption flatMap (_.board destsFrom E1) must bePoss(D1, F1) - "to the left" in: - val g2 = game.playMove(E1, D1) map (_ as White) - "cannot castle anymore" in: - g2.toOption flatMap (_.board destsFrom D1) must bePoss(C1, E1) - "neither if the king comes back" in: - val g3 = g2 flatMap (_.playMove(D1, E1)) map (_ as White) - g3.toOption flatMap (_.board destsFrom E1) must bePoss(D1, F1) - "if kingside rook moves" in: - val g2 = game.playMove(H1, G1) map (_ as White) - "can only castle queenside" in: - g2.toOption flatMap (_.board destsFrom E1) must bePoss(C1, D1, F1, A1) - "if queenside rook moves" in: - val g3 = g2 flatMap (_.playMove(A1, B1)) - "can not castle at all" in: - g3.toOption flatMap (_.board destsFrom E1) must bePoss(D1, F1) - "if queenside rook moves" in: - val g2 = game.playMove(A1, B1) map (_ as White) - "can only castle kingside" in: - g2.toOption flatMap (_.board destsFrom E1) must bePoss(D1, F1, G1, H1) - "if kingside rook moves" in: - val g3 = g2 flatMap (_.playMove(H1, G1)) - "can not castle at all" in: - g3.toOption flatMap (_.board destsFrom E1) must bePoss(D1, F1) - - "chess960" in: - val fenPosition = EpdFen("r3k2r/8/8/8/8/8/8/1R2K2R b KQk - 1 1") - val init = fenToGame(fenPosition, Chess960).toOption.get - val game = init.playMoves((A8, A1), (H1, H2), (A1, A7)).toOption.get - game.situation.legalMoves.exists(_.castles) must beTrue - - "test games" in: - val fenPosition = EpdFen("1rnk1bqr/pppp1bpp/1n2p3/5p2/5P2/1N1N4/PPPPPBPP/1R1K1BQR w KQkq - 0 5") - val init = fenToGame(fenPosition, Chess960).toOption.get - init.situation.legalMoves.exists(_.castles) must beTrue - init.situation.legalMoves.exists(_.castle.exists(_.side == QueenSide)) must beTrue - - "threat on king prevents castling" in: - val board: Board = """R K R""" - "by a rook" in: - board place (Black.rook, E3) flatMap (_ destsFrom E1) must bePoss(D1, D2, F2, F1) - "by a knight" in: - board place (Black.knight, D3) flatMap (_ destsFrom E1) must bePoss(D1, D2, E2, F1) - "threat on castle trip prevents castling" in: - "king side" in: - val board: Board = """R QK R""" - "close" in: - board place (Black.rook, F3) flatMap (_ destsFrom E1) must bePoss(D2, E2) - "far" in: - board place (Black.rook, G3) flatMap (_ destsFrom E1) must bePoss(D2, E2, F2, F1) - "queen side" in: - val board: Board = """R KB R""" - "close" in: - board place (Black.rook, D3) flatMap (_ destsFrom E1) must bePoss(E2, F2) - "far" in: - board place (Black.rook, C3) flatMap (_ destsFrom E1) must bePoss(D1, D2, E2, F2) - "chess 960" in: - "far kingside" in: - val board: Board = """BK R""" - "rook threat" in: - board place (Black.rook, F3) flatMap (_ destsFrom B1) must bePoss(A2, B2, C2, C1) - "enemy king threat" in: - board place (Black.king, E2) flatMap (_ destsFrom B1) must bePoss(A2, B2, C2, C1) - "threat on rook does not prevent castling" in: - "king side" in: - val board: Board = """R QK R""" - board place (Black.rook, H3) flatMap (_ destsFrom E1) must bePoss( - D2, - E2, - F1, - F2, - G1, - H1 - ) - "queen side" in: - val board: Board = """R KB R""" - board place (Black.rook, A3) flatMap (_ destsFrom E1) must bePoss( - A1, - C1, - D1, - D2, - E2, - F2 - ) +class CastlingTest extends ChessTest: + + import compare.dests + + val board: Board = """R K R""" + test("threat on king prevents castling: by a rook"): + assertEquals( + board.place(Black.rook, E3).flatMap(_ destsFrom E1), + Set(D1, D2, F2, F1) + ) + test("threat on king prevents castling: by a knight"): + assertEquals(board.place(Black.knight, D3).flatMap(_ destsFrom E1), Set(D1, D2, E2, F1)) + test("threat on castle trip prevents castling: king side"): + val board: Board = """R QK R""" + assertEquals(board.place(Black.rook, F3).flatMap(_ destsFrom E1), Set(D2, E2)) + assertEquals(board.place(Black.rook, G3).flatMap(_ destsFrom E1), Set(D2, E2, F2, F1)) + test("threat on castle trip prevents castling: queen side"): + val board: Board = """R KB R""" + assertEquals(board.place(Black.rook, D3).flatMap(_ destsFrom E1), Set(E2, F2)) + assertEquals(board.place(Black.rook, C3).flatMap(_ destsFrom E1), Set(D1, D2, E2, F2)) + test("threat on castle trip prevents castling: chess 960"): + val board: Board = """BK R""" + assertEquals(board.place(Black.rook, F3).flatMap(_ destsFrom B1), Set(A2, B2, C2, C1)) + assertEquals(board.place(Black.king, E2).flatMap(_ destsFrom B1), Set(A2, B2, C2, C1)) + test("threat on rook does not prevent castling king side"): + val board: Board = """R QK R""" + assertEquals(board.place(Black.rook, H3).flatMap(_ destsFrom E1), Set(D2, E2, F1, F2, G1, H1)) + test("threat on rook does not prevent castling king side"): + val board: Board = """R KB R""" + assertEquals(board.place(Black.rook, A3).flatMap(_ destsFrom E1), Set(A1, C1, D1, D2, E2, F2)) diff --git a/test-kit/src/test/scala/ChessTest.scala b/test-kit/src/test/scala/ChessTest.scala index 37589d129..c7c855617 100644 --- a/test-kit/src/test/scala/ChessTest.scala +++ b/test-kit/src/test/scala/ChessTest.scala @@ -146,9 +146,21 @@ trait ChessTest extends munit.FunSuite with ChessTestCommon with MunitExtensions given Conversion[Int, Clock.LimitSeconds] = Clock.LimitSeconds(_) given Conversion[Int, Clock.IncrementSeconds] = Clock.IncrementSeconds(_) + object compare: + given dests: munit.Compare[Option[List[Square]], Set[Square]] = new: + def isEqual(obtained: Option[List[Square]], expected: Set[Square]): Boolean = + obtained.fold(Set.empty)(_.toSet) == expected + def fenToGame(positionString: EpdFen, variant: Variant)(using Location): Game = fenToGameEither(positionString, variant).get + def visualDests(board: Board, p: Iterable[Square]): String = + Visual.addNewLines(Visual.>>|(board, Map(p -> 'x'))) + def visualDests(board: Board, p: Option[Iterable[Square]]): String = visualDests(board, p | Nil) + + def assertGame(game: Game, visual: String)(using Location) = + assertEquals(game.board.visual, (Visual << visual).visual) + trait ChessSpecs extends Specification with EitherMatchers with ChessTestCommon: def fenToGame(positionString: EpdFen, variant: Variant): Either[String, Game] =