module MoveTests where

import Chess.Internal.Piece
import Chess.Internal.Board
import Chess.Internal.Move
import TestUtils
import Test.Hspec

moveSpec :: IO ()
moveSpec = hspec $
        describe "Move" $ do
          isCorrectStartPieceSpec
          isRightPlayerMoveSpec
          areCoordinatesValidSpec
          generateAllRookMovesSpec
          generateAllBishopMovesSpec
          generateAllQueenMovesSpec
          generateAllKnightMovesSpec
          generateAllKingMovesSpec
          generateAllPawnMovesSpec
          generateAllPotentialMovesSpec
          boardAfterMoveSpec
          generateAllMovesSpec

isCorrectStartPieceSpec :: Spec
isCorrectStartPieceSpec =
        describe "isCorrectStartPiece" $
          it "should detect whether the piece in the coordinates matches the given piece information" $ do
             isCorrectStartPiece initialBoard (Piece White Pawn) (coord "e2") `shouldBe` True
             isCorrectStartPiece initialBoard (Piece White Knight) (coord "b1") `shouldBe` True
             isCorrectStartPiece initialBoard (Piece Black Pawn) (coord "e2") `shouldBe` False
             isCorrectStartPiece initialBoard (Piece White Bishop) (coord "e2") `shouldBe` False
             isCorrectStartPiece initialBoard (Piece White Pawn) (coord "e3") `shouldBe` False

isRightPlayerMoveSpec :: Spec
isRightPlayerMoveSpec =
        describe "isRightPlayerMove" $
          it "should detect whether the movement is by given player" $ do
             isRightPlayerMove White (Movement (Piece White Pawn) (coord "e2") (coord "e3")) `shouldBe` True
             isRightPlayerMove White (Movement (Piece Black Pawn) (coord "e7") (coord "e6")) `shouldBe` False
             isRightPlayerMove Black (Capture (Piece Black Queen) (coord "a1") (coord "a2")) `shouldBe` True
             isRightPlayerMove Black (Capture (Piece White Queen) (coord "a1") (coord "a2")) `shouldBe` False
             isRightPlayerMove White (Castling White Short) `shouldBe` True
             isRightPlayerMove Black (Castling White Long) `shouldBe` False
             isRightPlayerMove White (EnPassant (Piece White Pawn) (coord "e2") (coord "d3")) `shouldBe` True
             isRightPlayerMove Black (EnPassant (Piece White Pawn) (coord "e2") (coord "d3")) `shouldBe` False
             isRightPlayerMove Black (Promotion (Piece Black Pawn) (coord "e2") (coord "e1") Queen) `shouldBe` True
             isRightPlayerMove White (Promotion (Piece Black Pawn) (coord "e2") (coord "e1") Queen) `shouldBe` False
             isRightPlayerMove White (PawnDoubleMove (Piece White Pawn) (coord "e2") (coord "e4")) `shouldBe` True
             isRightPlayerMove Black (PawnDoubleMove (Piece White Pawn) (coord "e2") (coord "e4")) `shouldBe` False

areCoordinatesValidSpec :: Spec
areCoordinatesValidSpec =
        describe "areCoordinatesValid" $ do
          it "should return Nothing if both coordinates are inside bounds" $ do
             areCoordinatesValid (0, 0) (1, 2) `shouldBe` Nothing
             areCoordinatesValid (4, 4) (0, 7) `shouldBe` Nothing

          it "should return InvalidCoordinates error if at least one coordinate is out of bounds" $ do
             areCoordinatesValid (3, 3) (3, 3) `shouldBe` Just InvalidCoordinates
             areCoordinatesValid (-1, 0) (3, 3) `shouldBe` Just InvalidCoordinates
             areCoordinatesValid (7, 0) (7, 8) `shouldBe` Just InvalidCoordinates

generateAllRookMovesSpec :: Spec
generateAllRookMovesSpec =
        describe "generateAllRookMoves" $ do
          it "should return all movement and capture moves for a rook in a square" $
            generateAllRookMoves (game "k4b1r/p7/7p/4P3/1n2P2R/8/P2B1P2/3K2N1 w - - 6 9") (coord "h4") `shouldMatchList`
              [ Movement (Piece White Rook) (coord "h4") (coord "h5")
              , Movement (Piece White Rook) (coord "h4") (coord "g4")
              , Movement (Piece White Rook) (coord "h4") (coord "f4")
              , Movement (Piece White Rook) (coord "h4") (coord "h3")
              , Movement (Piece White Rook) (coord "h4") (coord "h2")
              , Movement (Piece White Rook) (coord "h4") (coord "h1")
              , Capture (Piece White Rook) (coord "h4") (coord "h6")]

          it "should return empty list of moves if no move is possible" $
            generateAllRookMoves (game "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1") (coord "a1") `shouldMatchList` []

generateAllBishopMovesSpec :: Spec
generateAllBishopMovesSpec =
        describe "generateAllBishopMoves" $ do
          it "should return all movement and capture moves for a bishop in a square" $
            generateAllBishopMoves (game "rnb1kbnr/pppp1ppp/8/5p2/8/3B4/PPPP3P/Rq1QKN2 w Qkq - 4 12") (coord "d3") `shouldMatchList`
              [ Movement (Piece White Bishop) (coord "d3") (coord "c4")
              , Movement (Piece White Bishop) (coord "d3") (coord "b5")
              , Movement (Piece White Bishop) (coord "d3") (coord "a6")
              , Movement (Piece White Bishop) (coord "d3") (coord "e4")
              , Movement (Piece White Bishop) (coord "d3") (coord "e2")
              , Capture (Piece White Bishop) (coord "d3") (coord "f5")]

          it "should return empty list of moves if no move is possible" $
            generateAllBishopMoves (game "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1") (coord "c1") `shouldMatchList` []

generateAllQueenMovesSpec :: Spec
generateAllQueenMovesSpec =
        describe "generateAllQueenMoves" $ do
          it "should return all movement and capture moves for a queen in a square" $ do
            generateAllQueenMoves (game "rnb1kbnr/pppp1ppp/8/5p2/8/3B4/PPPP3P/Rq1QKN2 b Qkq - 4 12") (coord "b1") `shouldMatchList`
              [ Movement (Piece Black Queen) (coord "b1") (coord "c1")
              , Capture (Piece Black Queen) (coord "b1") (coord "a1")
              , Capture (Piece Black Queen) (coord "b1") (coord "a2")
              , Capture (Piece Black Queen) (coord "b1") (coord "b2")
              , Capture (Piece Black Queen) (coord "b1") (coord "c2")
              , Capture (Piece Black Queen) (coord "b1") (coord "d1")]
            generateAllQueenMoves (game "r2qkb1r/ppnp2pp/4pp1n/4b3/2Q2P2/1P2P3/1P2BPPP/RPB1K1NR w KQkq - 2 13") (coord "c4") `shouldMatchList`
              [ Movement (Piece White Queen) (coord "c4") (coord "b5")
              , Movement (Piece White Queen) (coord "c4") (coord "a6")
              , Movement (Piece White Queen) (coord "c4") (coord "d5")
              , Movement (Piece White Queen) (coord "c4") (coord "d3")
              , Movement (Piece White Queen) (coord "c4") (coord "b4")
              , Movement (Piece White Queen) (coord "c4") (coord "a4")
              , Movement (Piece White Queen) (coord "c4") (coord "c5")
              , Movement (Piece White Queen) (coord "c4") (coord "c6")
              , Movement (Piece White Queen) (coord "c4") (coord "d4")
              , Movement (Piece White Queen) (coord "c4") (coord "e4")
              , Movement (Piece White Queen) (coord "c4") (coord "c3")
              , Movement (Piece White Queen) (coord "c4") (coord "c2")
              , Capture (Piece White Queen) (coord "c4") (coord "c7")
              , Capture (Piece White Queen) (coord "c4") (coord "e6")]

          it "should return empty list of moves if no move is possible" $
            generateAllQueenMoves (game "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1") (coord "d1") `shouldMatchList` []

generateAllKnightMovesSpec :: Spec
generateAllKnightMovesSpec =
        describe "generateAllKnightMoves" $
          it "should return all movement and capture moves for a knight in a square" $ do
            generateAllKnightMoves (game "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1") (coord "b1") `shouldMatchList`
              [ Movement (Piece White Knight) (coord "b1") (coord "a3")
              , Movement (Piece White Knight) (coord "b1") (coord "c3")]
            generateAllKnightMoves (game "r2qkbnr/2p1pppp/ppp5/bn6/3P4/2P5/PP2PPPP/RNBQKBNR b - - 3 7") (coord "b5") `shouldMatchList`
              [ Movement (Piece Black Knight) (coord "b5") (coord "d6")
              , Movement (Piece Black Knight) (coord "b5") (coord "a7")
              , Movement (Piece Black Knight) (coord "b5") (coord "a3")
              , Capture (Piece Black Knight) (coord "b5") (coord "c3")
              , Capture (Piece Black Knight) (coord "b5") (coord "d4")]

generateAllKingMovesSpec :: Spec
generateAllKingMovesSpec =
        describe "generateAllKingMoves" $ do
          it "should return empty list of moves if no move is possible" $
            generateAllKingMoves (game "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1") (coord "e1") `shouldMatchList` []

          it "should return all movement and capture moves for a king in a square" $
            generateAllKingMoves (game "2b5/pkp1prpp/8/rpn5/1K6/2P5/PP2N1PR/N1Q5 w - - 2 15") (coord "b4") `shouldMatchList`
              [ Movement (Piece White King) (coord "b4") (coord "a4")
              , Movement (Piece White King) (coord "b4") (coord "a3")
              , Movement (Piece White King) (coord "b4") (coord "b3")
              , Movement (Piece White King) (coord "b4") (coord "c4")
              , Capture (Piece White King) (coord "b4") (coord "a5")
              , Capture (Piece White King) (coord "b4") (coord "b5")
              , Capture (Piece White King) (coord "b4") (coord "c5")]

          it "should return castling moves when castling is possible" $
            generateAllKingMoves (game "1rq1kbr1/pppbnn1p/8/8/8/3N4/PPPP2PP/R3K2R w KQ - 0 10") (coord "e1") `shouldMatchList`
              [ Movement (Piece White King) (coord "e1") (coord "d1")
              , Movement (Piece White King) (coord "e1") (coord "e2")
              , Movement (Piece White King) (coord "e1") (coord "f2")
              , Movement (Piece White King) (coord "e1") (coord "f1")
              , Castling White Long
              , Castling White Short]

          it "should not return castling move if the castling has been invalidated" $ do
            generateAllKingMoves (game "r3k2r/ppppp1pp/2n2p2/3b4/8/2B2PPB/PPPPP2P/R3K2R w Qk - 5 14") (coord "e1") `shouldMatchList`
              [ Movement (Piece White King) (coord "e1") (coord "d1")
              , Movement (Piece White King) (coord "e1") (coord "f2")
              , Movement (Piece White King) (coord "e1") (coord "f1")
              , Castling White Long]
            generateAllKingMoves (game "r3k2r/ppppp1pp/2n2p2/3b4/8/2B2PPB/PPPPP2P/R3K2R w Qk - 5 14") (coord "e8") `shouldMatchList`
              [ Movement (Piece Black King) (coord "e8") (coord "d8")
              , Movement (Piece Black King) (coord "e8") (coord "f7")
              , Movement (Piece Black King) (coord "e8") (coord "f8")
              , Castling Black Short]

          it "should not return castling move if there is piece between castling line" $
            generateAllKingMoves (game "4k3/n7/8/8/8/5N2/P1N2PPP/R1n1K1R1 w Q - 0 16") (coord "e1") `shouldMatchList`
              [ Movement (Piece White King) (coord "e1") (coord "d1")
              , Movement (Piece White King) (coord "e1") (coord "d2")
              , Movement (Piece White King) (coord "e1") (coord "e2")
              , Movement (Piece White King) (coord "e1") (coord "f1")]

          it "should not return castling move if there is an opponent piece threatening castling end square" $
            generateAllKingMoves (game "3rk1r1/5p2/8/8/3b2N1/5P2/1PP1P1PP/4K2R w K - 2 20") (coord "e1") `shouldMatchList`
              [ Movement (Piece White King) (coord "e1") (coord "d1")
              , Movement (Piece White King) (coord "e1") (coord "d2")
              , Movement (Piece White King) (coord "e1") (coord "f1")
              , Movement (Piece White King) (coord "e1") (coord "f2")]

          it "should not return castling move if there is an opponent piece threatening a castling line square" $
            generateAllKingMoves (game "3rk3/5p2/5r2/8/3P2N1/8/1PP1P1PP/4K2R w K - 0 12") (coord "e1") `shouldMatchList`
              [ Movement (Piece White King) (coord "e1") (coord "d1")
              , Movement (Piece White King) (coord "e1") (coord "d2")
              , Movement (Piece White King) (coord "e1") (coord "f1")
              , Movement (Piece White King) (coord "e1") (coord "f2")]

          it "castling out of check should not be legal" $
            generateAllKingMoves (game "3rk3/5p2/4r3/8/3P2N1/8/1PP2PPP/4K2R w K - 0 12") (coord "e1") `shouldMatchList`
              [ Movement (Piece White King) (coord "e1") (coord "d1")
              , Movement (Piece White King) (coord "e1") (coord "d2")
              , Movement (Piece White King) (coord "e1") (coord "e2")
              , Movement (Piece White King) (coord "e1") (coord "f1")]

          it "castling when rook is threatened should be possible" $
            generateAllKingMoves (game "3rk3/5p2/7r/8/3P2N1/8/1PP2PP1/4K2R w K - 0 12") (coord "e1") `shouldMatchList`
              [ Movement (Piece White King) (coord "e1") (coord "d1")
              , Movement (Piece White King) (coord "e1") (coord "d2")
              , Movement (Piece White King) (coord "e1") (coord "e2")
              , Movement (Piece White King) (coord "e1") (coord "f1")
              , Castling White Short]

generateAllPawnMovesSpec :: Spec
generateAllPawnMovesSpec =
        describe "generateAllPawnMoves" $ do
          it "should return both normal movement and double move when possible" $ do
            generateAllPawnMoves (game "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1") (coord "e2") `shouldMatchList`
              [ Movement (Piece White Pawn) (coord "e2") (coord "e3")
              , PawnDoubleMove (Piece White Pawn) (coord "e2") (coord "e4")]
            generateAllPawnMoves (game "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR b KQkq - 0 1") (coord "a7") `shouldMatchList`
              [ Movement (Piece Black Pawn) (coord "a7") (coord "a6")
              , PawnDoubleMove (Piece Black Pawn) (coord "a7") (coord "a5")]

          it "should return empty list when no moves are possible" $ do
            generateAllPawnMoves (game "rnbqkbnr/pppp1ppp/8/4p3/4P3/8/PPP2PPP/RNBQKBNR w KQkq - 0 1") (coord "e4") `shouldMatchList` []
            generateAllPawnMoves (game "rnbqkbnr/pppp1ppp/8/4p3/4P3/8/PPP2PPP/RNBQKBNR b KQkq - 0 1") (coord "e5") `shouldMatchList` []

          it "should return both movement and capture moves when possible" $
            generateAllPawnMoves (game "rnbqkbnr/pppp1ppp/8/4p3/4PP2/8/PPP5/RNBQKBNR w KQkq - 0 1") (coord "f4") `shouldMatchList`
              [ Movement (Piece White Pawn) (coord "f4") (coord "f5")
              , Capture (Piece White Pawn) (coord "f4") (coord "e5")]

          it "should return only capture when normal movement is not possible" $
            generateAllPawnMoves (game "rnbqkbnr/pppp1ppp/8/4p3/4PP2/8/PPP5/RNBQKBNR w KQkq - 0 1") (coord "e5") `shouldMatchList`
              [ Capture (Piece Black Pawn) (coord "e5") (coord "f4")]

          it "should return normal movement when capture is not possible" $
            generateAllPawnMoves (game "rnbqkbnr/pppp1p1p/8/4pPp1/4P3/8/PPPP2PP/RNBQKBNR w KQkq - 0 4") (coord "f5") `shouldMatchList`
              [ Movement (Piece White Pawn) (coord "f5") (coord "f6")]

          it "should return en passant when it is possible" $ do
            generateAllPawnMoves (game "rnbqkbnr/ppp1p1pp/8/3pPp2/8/8/PPPP1PPP/RNBQKBNR w KQkq f6 0 3") (coord "e5") `shouldMatchList`
              [ Movement (Piece White Pawn) (coord "e5") (coord "e6")
              , EnPassant (Piece White Pawn) (coord "e5") (coord "f6")]
            generateAllPawnMoves (game "rnbqkbnr/ppp1pppp/8/8/2PpPP2/8/PP1P2PP/RNBQKBNR b KQkq c3 0 3") (coord "d4") `shouldMatchList`
              [ Movement (Piece Black Pawn) (coord "d4") (coord "d3")
              , EnPassant (Piece Black Pawn) (coord "d4") (coord "c3")]

          it "should not return en passant when en passant square is not one of the capture squares" $
            generateAllPawnMoves (game "rnbqkbnr/1pppp3/p5pp/3PPp2/8/8/PPP2PPP/RNBQKBNR w KQkq f6 0 5") (coord "d5") `shouldMatchList`
              [ Movement (Piece White Pawn) (coord "d5") (coord "d6")]

          it "should return all promotion moves when promotion with normal movement or by capture is possible" $ do
            generateAllPawnMoves (game "1r5k/2P5/8/8/8/8/8/4K2N w - - 0 1") (coord "c7") `shouldMatchList`
              [ Promotion (Piece White Pawn) (coord "c7") (coord "c8") Rook
              , Promotion (Piece White Pawn) (coord "c7") (coord "c8") Knight
              , Promotion (Piece White Pawn) (coord "c7") (coord "c8") Bishop
              , Promotion (Piece White Pawn) (coord "c7") (coord "c8") Queen
              , Promotion (Piece White Pawn) (coord "c7") (coord "b8") Rook
              , Promotion (Piece White Pawn) (coord "c7") (coord "b8") Knight
              , Promotion (Piece White Pawn) (coord "c7") (coord "b8") Bishop
              , Promotion (Piece White Pawn) (coord "c7") (coord "b8") Queen]
            generateAllPawnMoves (game "k7/8/8/8/8/8/p7/1N2K3 w - - 0 1") (coord "a2") `shouldMatchList`
              [ Promotion (Piece Black Pawn) (coord "a2") (coord "a1") Rook
              , Promotion (Piece Black Pawn) (coord "a2") (coord "a1") Knight
              , Promotion (Piece Black Pawn) (coord "a2") (coord "a1") Bishop
              , Promotion (Piece Black Pawn) (coord "a2") (coord "a1") Queen
              , Promotion (Piece Black Pawn) (coord "a2") (coord "b1") Rook
              , Promotion (Piece Black Pawn) (coord "a2") (coord "b1") Knight
              , Promotion (Piece Black Pawn) (coord "a2") (coord "b1") Bishop
              , Promotion (Piece Black Pawn) (coord "a2") (coord "b1") Queen]

          it "should not return any moves if no move is possible" $ do
            generateAllPawnMoves (game "k7/8/8/8/8/8/p7/N3K3 w - - 0 1") (coord "a2") `shouldMatchList` []
            generateAllPawnMoves (game "k7/8/8/8/8/4b3/4P3/K7 w - - 0 1") (coord "e2") `shouldMatchList` []

          it "should generate normal movement correctly" $
            generateAllPawnMoves (game "6k1/8/8/8/8/3P4/8/4K3 w - - 0 1") (coord "d3") `shouldMatchList`
              [ Movement (Piece White Pawn) (coord "d3") (coord "d4")]

generateAllPotentialMovesSpec :: Spec
generateAllPotentialMovesSpec =
        describe "generateAllPotentialMoves" $
          it "should return all potential moves for all pieces of the current player" $ do
            generateAllPotentialMoves (game "8/k7/8/8/6p1/7P/6PK/8 w - - 0 1") `shouldMatchList`
              [ Movement (Piece White Pawn) (coord "g2") (coord "g3")
              , Movement (Piece White Pawn) (coord "h3") (coord "h4")
              , Capture (Piece White Pawn) (coord "h3") (coord "g4")
              , Movement (Piece White King) (coord "h2") (coord "g3")
              , Movement (Piece White King) (coord "h2") (coord "h1")
              , Movement (Piece White King) (coord "h2") (coord "g1")]
            generateAllPotentialMoves (game "8/k7/8/8/6p1/7P/6PK/8 b - - 0 1") `shouldMatchList`
              [ Capture (Piece Black Pawn) (coord "g4") (coord "h3")
              , Movement (Piece Black Pawn) (coord "g4") (coord "g3")
              , Movement (Piece Black King) (coord "a7") (coord "a8")
              , Movement (Piece Black King) (coord "a7") (coord "a6")
              , Movement (Piece Black King) (coord "a7") (coord "b8")
              , Movement (Piece Black King) (coord "a7") (coord "b7")
              , Movement (Piece Black King) (coord "a7") (coord "b6")]

boardAfterMoveSpec :: Spec
boardAfterMoveSpec =
        describe "boardAfterMove" $ do
          it "should make pawn double move correctly" $ do
            boardAfterMove (fenBoard "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1") (PawnDoubleMove (Piece White Pawn) (coord "e2") (coord "e4")) `shouldBe` Just (fenBoard "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR w KQkq - 0 1")
            boardAfterMove (fenBoard "rnbqkbnr/p1pp1ppp/p7/4p3/4P3/5N2/PPPP1PPP/RNBQ1RK1 b kq - 1 1") (PawnDoubleMove (Piece Black Pawn) (coord "c7") (coord "c5")) `shouldBe` Just (fenBoard "rnbqkbnr/p2p1ppp/p7/2p1p3/4P3/5N2/PPPP1PPP/RNBQ1RK1 w kq c6 0 2")
            boardAfterMove (fenBoard "rnbqkbnr/p2p1ppp/p7/4N3/2p1P3/8/PPPP1PPP/RNBQ1RK1 w kq - 0 3") (PawnDoubleMove (Piece White Pawn) (coord "d2") (coord "d4")) `shouldBe` Just (fenBoard "rnbqkbnr/p2p1ppp/p7/4N3/2pPP3/8/PPP2PPP/RNBQ1RK1 b kq d3 0 3")

          it "should make pawn normal move correctly" $ do
            boardAfterMove (fenBoard "rnbqkbnr/pppp1ppp/8/4p3/4P3/8/PPPP1PPP/RNBQKBNR w KQkq - 0 1") (Movement (Piece White Knight) (coord "g1") (coord "f3")) `shouldBe` Just (fenBoard "rnbqkbnr/pppp1ppp/8/4p3/4P3/5N2/PPPP1PPP/RNBQKB1R w KQkq - 0 1")
            boardAfterMove (fenBoard "rnbqkbnr/p2p1ppp/p7/2p1N3/4P3/8/PPPP1PPP/RNBQ1RK1 b kq - 0 2") (Movement (Piece Black Pawn) (coord "c5") (coord "c4")) `shouldBe` Just (fenBoard "rnbqkbnr/p2p1ppp/p7/4N3/2p1P3/8/PPPP1PPP/RNBQ1RK1 w kq - 0 3")

          it "should make pawn capture move correctly" $
            boardAfterMove (fenBoard "rnbqkbnr/pppp1ppp/B7/4p3/4P3/5N2/PPPP1PPP/RNBQK2R w KQkq - 0 1") (Capture (Piece Black Pawn) (coord "b7") (coord "a6")) `shouldBe` Just (fenBoard "rnbqkbnr/p1pp1ppp/p7/4p3/4P3/5N2/PPPP1PPP/RNBQK2R w KQkq - 0 1")

          it "should move king and rook correctly in castling move" $
            boardAfterMove (fenBoard "rnbqkbnr/p1pp1ppp/p7/4p3/4P3/5N2/PPPP1PPP/RNBQK2R w KQkq - 0 1") (Castling White Short) `shouldBe` Just (fenBoard "rnbqkbnr/p1pp1ppp/p7/4p3/4P3/5N2/PPPP1PPP/RNBQ1RK1 b kq - 1 1")

          it "should make knight's capture move correctly" $
            boardAfterMove (fenBoard "rnbqkbnr/p2p1ppp/p7/2p1p3/4P3/5N2/PPPP1PPP/RNBQ1RK1 w kq c6 0 2") (Capture (Piece White Knight) (coord "f3") (coord "e5")) `shouldBe` Just (fenBoard "rnbqkbnr/p2p1ppp/p7/2p1N3/4P3/8/PPPP1PPP/RNBQ1RK1 b kq - 0 2")

          it "should make en passant move correctly" $
            boardAfterMove (fenBoard "rnbqkbnr/p2p1ppp/p7/4N3/2pPP3/8/PPP2PPP/RNBQ1RK1 b kq d3 0 3") (EnPassant (Piece Black Pawn) (coord "c4") (coord "d3")) `shouldBe` Just (fenBoard "rnbqkbnr/p2p1ppp/p7/4N3/4P3/3p4/PPP2PPP/RNBQ1RK1 w kq - 0 4")

generateAllMovesSpec :: Spec
generateAllMovesSpec =
        describe "generateAllMoves" $ do
          it "should generate all legal moves for the current player in a given game state" $
            generateAllMoves (game "rnbqkb1r/pp1p1Bpp/5n2/2p1p3/4P3/2P5/PP1P1PPP/RNBQK1NR b KQkq - 0 4") `shouldMatchList`
              [Movement (Piece Black King) (0,4) (1,4), Capture (Piece Black King) (0,4) (1,5)]

          it "should not give any legal moves in checkmate" $ do
            generateAllMoves (game "8/5r2/4K1q1/4p3/3k4/8/8/8 w - - 0 7") `shouldBe` []
            generateAllMoves (game "4r2r/p6p/1pnN2p1/kQp5/3pPq2/3P4/PPP3PP/R5K1 b - - 0 2") `shouldBe` []
            generateAllMoves (game "r3k2r/ppp2p1p/2n1p1p1/8/2B2P1q/2NPb1n1/PP4PP/R2Q3K w kq - 0 8") `shouldBe` []
            generateAllMoves (game "8/6R1/pp1r3p/6p1/P3R1Pk/1P4P1/7K/8 b - - 0 4") `shouldBe` []

          it "should not give any legal moves in stalemate" $ do
            generateAllMoves (game "1R6/8/8/8/8/8/7R/k6K b - - 0 1") `shouldBe` []
            generateAllMoves (game "8/8/5k2/p4p1p/P4K1P/1r6/8/8 w - - 0 2") `shouldBe` []

module MoveTests where

import Chess.Internal.Piece
import Chess.Internal.Board
import Chess.Internal.Move
import TestUtils
import Test.Hspec

moveSpec :: IO ()
moveSpec = hspec $
        describe "Move" $ do
          isCorrectStartPieceSpec
          isRightPlayerMoveSpec
          areCoordinatesValidSpec
          generateAllRookMovesSpec
          generateAllBishopMovesSpec
          generateAllQueenMovesSpec
          generateAllKnightMovesSpec
          generateAllKingMovesSpec
          generateAllPawnMovesSpec
          generateAllPotentialMovesSpec
          boardAfterMoveSpec
          generateAllMovesSpec

isCorrectStartPieceSpec :: Spec
isCorrectStartPieceSpec =
        describe "isCorrectStartPiece" $
          it "should detect whether the piece in the coordinates matches the given piece information" $ do
             isCorrectStartPiece initialBoard (Piece White Pawn) (coord "e2") `shouldBe` True
             isCorrectStartPiece initialBoard (Piece White Knight) (coord "b1") `shouldBe` True
             isCorrectStartPiece initialBoard (Piece Black Pawn) (coord "e2") `shouldBe` False
             isCorrectStartPiece initialBoard (Piece White Bishop) (coord "e2") `shouldBe` False
             isCorrectStartPiece initialBoard (Piece White Pawn) (coord "e3") `shouldBe` False

isRightPlayerMoveSpec :: Spec
isRightPlayerMoveSpec =
        describe "isRightPlayerMove" $
          it "should detect whether the movement is by given player" $ do
             isRightPlayerMove White (Movement (Piece White Pawn) (coord "e2") (coord "e3")) `shouldBe` True
             isRightPlayerMove White (Movement (Piece Black Pawn) (coord "e7") (coord "e6")) `shouldBe` False
             isRightPlayerMove Black (Capture (Piece Black Queen) (coord "a1") (coord "a2")) `shouldBe` True
             isRightPlayerMove Black (Capture (Piece White Queen) (coord "a1") (coord "a2")) `shouldBe` False
             isRightPlayerMove White (Castling White Short) `shouldBe` True
             isRightPlayerMove Black (Castling White Long) `shouldBe` False
             isRightPlayerMove White (EnPassant (Piece White Pawn) (coord "e2") (coord "d3")) `shouldBe` True
             isRightPlayerMove Black (EnPassant (Piece White Pawn) (coord "e2") (coord "d3")) `shouldBe` False
             isRightPlayerMove Black (Promotion (Piece Black Pawn) (coord "e2") (coord "e1") Queen) `shouldBe` True
             isRightPlayerMove White (Promotion (Piece Black Pawn) (coord "e2") (coord "e1") Queen) `shouldBe` False
             isRightPlayerMove White (PawnDoubleMove (Piece White Pawn) (coord "e2") (coord "e4")) `shouldBe` True
             isRightPlayerMove Black (PawnDoubleMove (Piece White Pawn) (coord "e2") (coord "e4")) `shouldBe` False

areCoordinatesValidSpec :: Spec
areCoordinatesValidSpec =
        describe "areCoordinatesValid" $ do
          it "should return Nothing if both coordinates are inside bounds" $ do
             areCoordinatesValid (0, 0) (1, 2) `shouldBe` Nothing
             areCoordinatesValid (4, 4) (0, 7) `shouldBe` Nothing

          it "should return InvalidCoordinates error if at least one coordinate is out of bounds" $ do
             areCoordinatesValid (3, 3) (3, 3) `shouldBe` Just InvalidCoordinates
             areCoordinatesValid (-1, 0) (3, 3) `shouldBe` Just InvalidCoordinates
             areCoordinatesValid (7, 0) (7, 8) `shouldBe` Just InvalidCoordinates

generateAllRookMovesSpec :: Spec
generateAllRookMovesSpec =
        describe "generateAllRookMoves" $ do
          it "should return all movement and capture moves for a rook in a square" $
            generateAllRookMoves (game "k4b1r/p7/7p/4P3/1n2P2R/8/P2B1P2/3K2N1 w - - 6 9") (coord "h4") `shouldMatchList`
              [ Movement (Piece White Rook) (coord "h4") (coord "h5")
              , Movement (Piece White Rook) (coord "h4") (coord "g4")
              , Movement (Piece White Rook) (coord "h4") (coord "f4")
              , Movement (Piece White Rook) (coord "h4") (coord "h3")
              , Movement (Piece White Rook) (coord "h4") (coord "h2")
              , Movement (Piece White Rook) (coord "h4") (coord "h1")
              , Capture (Piece White Rook) (coord "h4") (coord "h6")]

          it "should return empty list of moves if no move is possible" $
            generateAllRookMoves (game "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1") (coord "a1") `shouldMatchList` []

generateAllBishopMovesSpec :: Spec
generateAllBishopMovesSpec =
        describe "generateAllBishopMoves" $ do
          it "should return all movement and capture moves for a bishop in a square" $
            generateAllBishopMoves (game "rnb1kbnr/pppp1ppp/8/5p2/8/3B4/PPPP3P/Rq1QKN2 w Qkq - 4 12") (coord "d3") `shouldMatchList`
              [ Movement (Piece White Bishop) (coord "d3") (coord "c4")
              , Movement (Piece White Bishop) (coord "d3") (coord "b5")
              , Movement (Piece White Bishop) (coord "d3") (coord "a6")
              , Movement (Piece White Bishop) (coord "d3") (coord "e4")
              , Movement (Piece White Bishop) (coord "d3") (coord "e2")
              , Capture (Piece White Bishop) (coord "d3") (coord "f5")]

          it "should return empty list of moves if no move is possible" $
            generateAllBishopMoves (game "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1") (coord "c1") `shouldMatchList` []

generateAllQueenMovesSpec :: Spec
generateAllQueenMovesSpec =
        describe "generateAllQueenMoves" $ do
          it "should return all movement and capture moves for a queen in a square" $ do
            generateAllQueenMoves (game "rnb1kbnr/pppp1ppp/8/5p2/8/3B4/PPPP3P/Rq1QKN2 b Qkq - 4 12") (coord "b1") `shouldMatchList`
              [ Movement (Piece Black Queen) (coord "b1") (coord "c1")
              , Capture (Piece Black Queen) (coord "b1") (coord "a1")
              , Capture (Piece Black Queen) (coord "b1") (coord "a2")
              , Capture (Piece Black Queen) (coord "b1") (coord "b2")
              , Capture (Piece Black Queen) (coord "b1") (coord "c2")
              , Capture (Piece Black Queen) (coord "b1") (coord "d1")]
            generateAllQueenMoves (game "r2qkb1r/ppnp2pp/4pp1n/4b3/2Q2P2/1P2P3/1P2BPPP/RPB1K1NR w KQkq - 2 13") (coord "c4") `shouldMatchList`
              [ Movement (Piece White Queen) (coord "c4") (coord "b5")
              , Movement (Piece White Queen) (coord "c4") (coord "a6")
              , Movement (Piece White Queen) (coord "c4") (coord "d5")
              , Movement (Piece White Queen) (coord "c4") (coord "d3")
              , Movement (Piece White Queen) (coord "c4") (coord "b4")
              , Movement (Piece White Queen) (coord "c4") (coord "a4")
              , Movement (Piece White Queen) (coord "c4") (coord "c5")
              , Movement (Piece White Queen) (coord "c4") (coord "c6")
              , Movement (Piece White Queen) (coord "c4") (coord "d4")
              , Movement (Piece White Queen) (coord "c4") (coord "e4")
              , Movement (Piece White Queen) (coord "c4") (coord "c3")
              , Movement (Piece White Queen) (coord "c4") (coord "c2")
              , Capture (Piece White Queen) (coord "c4") (coord "c7")
              , Capture (Piece White Queen) (coord "c4") (coord "e6")]

          it "should return empty list of moves if no move is possible" $
            generateAllQueenMoves (game "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1") (coord "d1") `shouldMatchList` []

generateAllKnightMovesSpec :: Spec
generateAllKnightMovesSpec =
        describe "generateAllKnightMoves" $
          it "should return all movement and capture moves for a knight in a square" $ do
            generateAllKnightMoves (game "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1") (coord "b1") `shouldMatchList`
              [ Movement (Piece White Knight) (coord "b1") (coord "a3")
              , Movement (Piece White Knight) (coord "b1") (coord "c3")]
            generateAllKnightMoves (game "r2qkbnr/2p1pppp/ppp5/bn6/3P4/2P5/PP2PPPP/RNBQKBNR b - - 3 7") (coord "b5") `shouldMatchList`
              [ Movement (Piece Black Knight) (coord "b5") (coord "d6")
              , Movement (Piece Black Knight) (coord "b5") (coord "a7")
              , Movement (Piece Black Knight) (coord "b5") (coord "a3")
              , Capture (Piece Black Knight) (coord "b5") (coord "c3")
              , Capture (Piece Black Knight) (coord "b5") (coord "d4")]

generateAllKingMovesSpec :: Spec
generateAllKingMovesSpec =
        describe "generateAllKingMoves" $ do
          it "should return empty list of moves if no move is possible" $
            generateAllKingMoves (game "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1") (coord "e1") `shouldMatchList` []

          it "should return all movement and capture moves for a king in a square" $
            generateAllKingMoves (game "2b5/pkp1prpp/8/rpn5/1K6/2P5/PP2N1PR/N1Q5 w - - 2 15") (coord "b4") `shouldMatchList`
              [ Movement (Piece White King) (coord "b4") (coord "a4")
              , Movement (Piece White King) (coord "b4") (coord "a3")
              , Movement (Piece White King) (coord "b4") (coord "b3")
              , Movement (Piece White King) (coord "b4") (coord "c4")
              , Capture (Piece White King) (coord "b4") (coord "a5")
              , Capture (Piece White King) (coord "b4") (coord "b5")
              , Capture (Piece White King) (coord "b4") (coord "c5")]

          it "should return castling moves when castling is possible" $
            generateAllKingMoves (game "1rq1kbr1/pppbnn1p/8/8/8/3N4/PPPP2PP/R3K2R w KQ - 0 10") (coord "e1") `shouldMatchList`
              [ Movement (Piece White King) (coord "e1") (coord "d1")
              , Movement (Piece White King) (coord "e1") (coord "e2")
              , Movement (Piece White King) (coord "e1") (coord "f2")
              , Movement (Piece White King) (coord "e1") (coord "f1")
              , Castling White Long
              , Castling White Short]

          it "should not return castling move if the castling has been invalidated" $ do
            generateAllKingMoves (game "r3k2r/ppppp1pp/2n2p2/3b4/8/2B2PPB/PPPPP2P/R3K2R w Qk - 5 14") (coord "e1") `shouldMatchList`
              [ Movement (Piece White King) (coord "e1") (coord "d1")
              , Movement (Piece White King) (coord "e1") (coord "f2")
              , Movement (Piece White King) (coord "e1") (coord "f1")
              , Castling White Long]
            generateAllKingMoves (game "r3k2r/ppppp1pp/2n2p2/3b4/8/2B2PPB/PPPPP2P/R3K2R w Qk - 5 14") (coord "e8") `shouldMatchList`
              [ Movement (Piece Black King) (coord "e8") (coord "d8")
              , Movement (Piece Black King) (coord "e8") (coord "f7")
              , Movement (Piece Black King) (coord "e8") (coord "f8")
              , Castling Black Short]

          it "should not return castling move if there is piece between castling line" $
            generateAllKingMoves (game "4k3/n7/8/8/8/5N2/P1N2PPP/R1n1K1R1 w Q - 0 16") (coord "e1") `shouldMatchList`
              [ Movement (Piece White King) (coord "e1") (coord "d1")
              , Movement (Piece White King) (coord "e1") (coord "d2")
              , Movement (Piece White King) (coord "e1") (coord "e2")
              , Movement (Piece White King) (coord "e1") (coord "f1")]

          it "should not return castling move if there is an opponent piece threatening castling end square" $
            generateAllKingMoves (game "3rk1r1/5p2/8/8/3b2N1/5P2/1PP1P1PP/4K2R w K - 2 20") (coord "e1") `shouldMatchList`
              [ Movement (Piece White King) (coord "e1") (coord "d1")
              , Movement (Piece White King) (coord "e1") (coord "d2")
              , Movement (Piece White King) (coord "e1") (coord "f1")
              , Movement (Piece White King) (coord "e1") (coord "f2")]

          it "should not return castling move if there is an opponent piece threatening a castling line square" $
            generateAllKingMoves (game "3rk3/5p2/5r2/8/3P2N1/8/1PP1P1PP/4K2R w K - 0 12") (coord "e1") `shouldMatchList`
              [ Movement (Piece White King) (coord "e1") (coord "d1")
              , Movement (Piece White King) (coord "e1") (coord "d2")
              , Movement (Piece White King) (coord "e1") (coord "f1")
              , Movement (Piece White King) (coord "e1") (coord "f2")]

          it "castling out of check should not be legal" $
            generateAllKingMoves (game "3rk3/5p2/4r3/8/3P2N1/8/1PP2PPP/4K2R w K - 0 12") (coord "e1") `shouldMatchList`
              [ Movement (Piece White King) (coord "e1") (coord "d1")
              , Movement (Piece White King) (coord "e1") (coord "d2")
              , Movement (Piece White King) (coord "e1") (coord "e2")
              , Movement (Piece White King) (coord "e1") (coord "f1")]

          it "castling when rook is threatened should be possible" $
            generateAllKingMoves (game "3rk3/5p2/7r/8/3P2N1/8/1PP2PP1/4K2R w K - 0 12") (coord "e1") `shouldMatchList`
              [ Movement (Piece White King) (coord "e1") (coord "d1")
              , Movement (Piece White King) (coord "e1") (coord "d2")
              , Movement (Piece White King) (coord "e1") (coord "e2")
              , Movement (Piece White King) (coord "e1") (coord "f1")
              , Castling White Short]

generateAllPawnMovesSpec :: Spec
generateAllPawnMovesSpec =
        describe "generateAllPawnMoves" $ do
          it "should return both normal movement and double move when possible" $ do
            generateAllPawnMoves (game "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1") (coord "e2") `shouldMatchList`
              [ Movement (Piece White Pawn) (coord "e2") (coord "e3")
              , PawnDoubleMove (Piece White Pawn) (coord "e2") (coord "e4")]
            generateAllPawnMoves (game "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR b KQkq - 0 1") (coord "a7") `shouldMatchList`
              [ Movement (Piece Black Pawn) (coord "a7") (coord "a6")
              , PawnDoubleMove (Piece Black Pawn) (coord "a7") (coord "a5")]

          it "should return empty list when no moves are possible" $ do
            generateAllPawnMoves (game "rnbqkbnr/pppp1ppp/8/4p3/4P3/8/PPP2PPP/RNBQKBNR w KQkq - 0 1") (coord "e4") `shouldMatchList` []
            generateAllPawnMoves (game "rnbqkbnr/pppp1ppp/8/4p3/4P3/8/PPP2PPP/RNBQKBNR b KQkq - 0 1") (coord "e5") `shouldMatchList` []

          it "should return both movement and capture moves when possible" $
            generateAllPawnMoves (game "rnbqkbnr/pppp1ppp/8/4p3/4PP2/8/PPP5/RNBQKBNR w KQkq - 0 1") (coord "f4") `shouldMatchList`
              [ Movement (Piece White Pawn) (coord "f4") (coord "f5")
              , Capture (Piece White Pawn) (coord "f4") (coord "e5")]

          it "should return only capture when normal movement is not possible" $
            generateAllPawnMoves (game "rnbqkbnr/pppp1ppp/8/4p3/4PP2/8/PPP5/RNBQKBNR w KQkq - 0 1") (coord "e5") `shouldMatchList`
              [ Capture (Piece Black Pawn) (coord "e5") (coord "f4")]

          it "should return normal movement when capture is not possible" $
            generateAllPawnMoves (game "rnbqkbnr/pppp1p1p/8/4pPp1/4P3/8/PPPP2PP/RNBQKBNR w KQkq - 0 4") (coord "f5") `shouldMatchList`
              [ Movement (Piece White Pawn) (coord "f5") (coord "f6")]

          it "should return en passant when it is possible" $ do
            generateAllPawnMoves (game "rnbqkbnr/ppp1p1pp/8/3pPp2/8/8/PPPP1PPP/RNBQKBNR w KQkq f6 0 3") (coord "e5") `shouldMatchList`
              [ Movement (Piece White Pawn) (coord "e5") (coord "e6")
              , EnPassant (Piece White Pawn) (coord "e5") (coord "f6")]
            generateAllPawnMoves (game "rnbqkbnr/ppp1pppp/8/8/2PpPP2/8/PP1P2PP/RNBQKBNR b KQkq c3 0 3") (coord "d4") `shouldMatchList`
              [ Movement (Piece Black Pawn) (coord "d4") (coord "d3")
              , EnPassant (Piece Black Pawn) (coord "d4") (coord "c3")]

          it "should not return en passant when en passant square is not one of the capture squares" $
            generateAllPawnMoves (game "rnbqkbnr/1pppp3/p5pp/3PPp2/8/8/PPP2PPP/RNBQKBNR w KQkq f6 0 5") (coord "d5") `shouldMatchList`
              [ Movement (Piece White Pawn) (coord "d5") (coord "d6")]

          it "should return all promotion moves when promotion with normal movement or by capture is possible" $ do
            generateAllPawnMoves (game "1r5k/2P5/8/8/8/8/8/4K2N w - - 0 1") (coord "c7") `shouldMatchList`
              [ Promotion (Piece White Pawn) (coord "c7") (coord "c8") Rook
              , Promotion (Piece White Pawn) (coord "c7") (coord "c8") Knight
              , Promotion (Piece White Pawn) (coord "c7") (coord "c8") Bishop
              , Promotion (Piece White Pawn) (coord "c7") (coord "c8") Queen
              , Promotion (Piece White Pawn) (coord "c7") (coord "b8") Rook
              , Promotion (Piece White Pawn) (coord "c7") (coord "b8") Knight
              , Promotion (Piece White Pawn) (coord "c7") (coord "b8") Bishop
              , Promotion (Piece White Pawn) (coord "c7") (coord "b8") Queen]
            generateAllPawnMoves (game "k7/8/8/8/8/8/p7/1N2K3 w - - 0 1") (coord "a2") `shouldMatchList`
              [ Promotion (Piece Black Pawn) (coord "a2") (coord "a1") Rook
              , Promotion (Piece Black Pawn) (coord "a2") (coord "a1") Knight
              , Promotion (Piece Black Pawn) (coord "a2") (coord "a1") Bishop
              , Promotion (Piece Black Pawn) (coord "a2") (coord "a1") Queen
              , Promotion (Piece Black Pawn) (coord "a2") (coord "b1") Rook
              , Promotion (Piece Black Pawn) (coord "a2") (coord "b1") Knight
              , Promotion (Piece Black Pawn) (coord "a2") (coord "b1") Bishop
              , Promotion (Piece Black Pawn) (coord "a2") (coord "b1") Queen]

          it "should not return any moves if no move is possible" $ do
            generateAllPawnMoves (game "k7/8/8/8/8/8/p7/N3K3 w - - 0 1") (coord "a2") `shouldMatchList` []
            generateAllPawnMoves (game "k7/8/8/8/8/4b3/4P3/K7 w - - 0 1") (coord "e2") `shouldMatchList` []

          it "should generate normal movement correctly" $
            generateAllPawnMoves (game "6k1/8/8/8/8/3P4/8/4K3 w - - 0 1") (coord "d3") `shouldMatchList`
              [ Movement (Piece White Pawn) (coord "d3") (coord "d4")]

generateAllPotentialMovesSpec :: Spec
generateAllPotentialMovesSpec =
        describe "generateAllPotentialMoves" $
          it "should return all potential moves for all pieces of the current player" $ do
            generateAllPotentialMoves (game "8/k7/8/8/6p1/7P/6PK/8 w - - 0 1") `shouldMatchList`
              [ Movement (Piece White Pawn) (coord "g2") (coord "g3")
              , Movement (Piece White Pawn) (coord "h3") (coord "h4")
              , Capture (Piece White Pawn) (coord "h3") (coord "g4")
              , Movement (Piece White King) (coord "h2") (coord "g3")
              , Movement (Piece White King) (coord "h2") (coord "h1")
              , Movement (Piece White King) (coord "h2") (coord "g1")]
            generateAllPotentialMoves (game "8/k7/8/8/6p1/7P/6PK/8 b - - 0 1") `shouldMatchList`
              [ Capture (Piece Black Pawn) (coord "g4") (coord "h3")
              , Movement (Piece Black Pawn) (coord "g4") (coord "g3")
              , Movement (Piece Black King) (coord "a7") (coord "a8")
              , Movement (Piece Black King) (coord "a7") (coord "a6")
              , Movement (Piece Black King) (coord "a7") (coord "b8")
              , Movement (Piece Black King) (coord "a7") (coord "b7")
              , Movement (Piece Black King) (coord "a7") (coord "b6")]

boardAfterMoveSpec :: Spec
boardAfterMoveSpec =
        describe "boardAfterMove" $ do
          it "should make pawn double move correctly" $ do
            boardAfterMove (fenBoard "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1") (PawnDoubleMove (Piece White Pawn) (coord "e2") (coord "e4")) `shouldBe` Just (fenBoard "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR w KQkq - 0 1")
            boardAfterMove (fenBoard "rnbqkbnr/p1pp1ppp/p7/4p3/4P3/5N2/PPPP1PPP/RNBQ1RK1 b kq - 1 1") (PawnDoubleMove (Piece Black Pawn) (coord "c7") (coord "c5")) `shouldBe` Just (fenBoard "rnbqkbnr/p2p1ppp/p7/2p1p3/4P3/5N2/PPPP1PPP/RNBQ1RK1 w kq c6 0 2")
            boardAfterMove (fenBoard "rnbqkbnr/p2p1ppp/p7/4N3/2p1P3/8/PPPP1PPP/RNBQ1RK1 w kq - 0 3") (PawnDoubleMove (Piece White Pawn) (coord "d2") (coord "d4")) `shouldBe` Just (fenBoard "rnbqkbnr/p2p1ppp/p7/4N3/2pPP3/8/PPP2PPP/RNBQ1RK1 b kq d3 0 3")

          it "should make pawn normal move correctly" $ do
            boardAfterMove (fenBoard "rnbqkbnr/pppp1ppp/8/4p3/4P3/8/PPPP1PPP/RNBQKBNR w KQkq - 0 1") (Movement (Piece White Knight) (coord "g1") (coord "f3")) `shouldBe` Just (fenBoard "rnbqkbnr/pppp1ppp/8/4p3/4P3/5N2/PPPP1PPP/RNBQKB1R w KQkq - 0 1")
            boardAfterMove (fenBoard "rnbqkbnr/p2p1ppp/p7/2p1N3/4P3/8/PPPP1PPP/RNBQ1RK1 b kq - 0 2") (Movement (Piece Black Pawn) (coord "c5") (coord "c4")) `shouldBe` Just (fenBoard "rnbqkbnr/p2p1ppp/p7/4N3/2p1P3/8/PPPP1PPP/RNBQ1RK1 w kq - 0 3")

          it "should make pawn capture move correctly" $
            boardAfterMove (fenBoard "rnbqkbnr/pppp1ppp/B7/4p3/4P3/5N2/PPPP1PPP/RNBQK2R w KQkq - 0 1") (Capture (Piece Black Pawn) (coord "b7") (coord "a6")) `shouldBe` Just (fenBoard "rnbqkbnr/p1pp1ppp/p7/4p3/4P3/5N2/PPPP1PPP/RNBQK2R w KQkq - 0 1")

          it "should move king and rook correctly in castling move" $
            boardAfterMove (fenBoard "rnbqkbnr/p1pp1ppp/p7/4p3/4P3/5N2/PPPP1PPP/RNBQK2R w KQkq - 0 1") (Castling White Short) `shouldBe` Just (fenBoard "rnbqkbnr/p1pp1ppp/p7/4p3/4P3/5N2/PPPP1PPP/RNBQ1RK1 b kq - 1 1")

          it "should make knight's capture move correctly" $
            boardAfterMove (fenBoard "rnbqkbnr/p2p1ppp/p7/2p1p3/4P3/5N2/PPPP1PPP/RNBQ1RK1 w kq c6 0 2") (Capture (Piece White Knight) (coord "f3") (coord "e5")) `shouldBe` Just (fenBoard "rnbqkbnr/p2p1ppp/p7/2p1N3/4P3/8/PPPP1PPP/RNBQ1RK1 b kq - 0 2")

          it "should make en passant move correctly" $
            boardAfterMove (fenBoard "rnbqkbnr/p2p1ppp/p7/4N3/2pPP3/8/PPP2PPP/RNBQ1RK1 b kq d3 0 3") (EnPassant (Piece Black Pawn) (coord "c4") (coord "d3")) `shouldBe` Just (fenBoard "rnbqkbnr/p2p1ppp/p7/4N3/4P3/3p4/PPP2PPP/RNBQ1RK1 w kq - 0 4")

generateAllMovesSpec :: Spec
generateAllMovesSpec =
        describe "generateAllMoves" $ do
          it "should generate all legal moves for the current player in a given game state" $
            generateAllMoves (game "rnbqkb1r/pp1p1Bpp/5n2/2p1p3/4P3/2P5/PP1P1PPP/RNBQK1NR b KQkq - 0 4") `shouldMatchList`
              [Movement (Piece Black King) (0,4) (1,4), Capture (Piece Black King) (0,4) (1,5)]

          it "should not give any legal moves in checkmate" $ do
            generateAllMoves (game "8/5r2/4K1q1/4p3/3k4/8/8/8 w - - 0 7") `shouldBe` []
            generateAllMoves (game "4r2r/p6p/1pnN2p1/kQp5/3pPq2/3P4/PPP3PP/R5K1 b - - 0 2") `shouldBe` []
            generateAllMoves (game "r3k2r/ppp2p1p/2n1p1p1/8/2B2P1q/2NPb1n1/PP4PP/R2Q3K w kq - 0 8") `shouldBe` []
            generateAllMoves (game "8/6R1/pp1r3p/6p1/P3R1Pk/1P4P1/7K/8 b - - 0 4") `shouldBe` []

          it "should not give any legal moves in stalemate" $ do
            generateAllMoves (game "1R6/8/8/8/8/8/7R/k6K b - - 0 1") `shouldBe` []
            generateAllMoves (game "8/8/5k2/p4p1p/P4K1P/1r6/8/8 w - - 0 2") `shouldBe` []
