turncoat-1.0: src/Models/Move.hs
module Models.Move (Move(..), StorableMove(..), encodeMove, decodeMove, foldBoardAttacks, foldBoardMoves, foldBoardMovesConst, foldBoardPawnMovesConst, foldBoardSquares, foldlBoard, showBoard) where
import AppPrelude
import Models.Piece
import Utils.Board
import Data.Bits
import Data.List (iterate)
import qualified Data.Vector.Storable as Vector
import Test.QuickCheck (Arbitrary (..), chooseInt)
data Move = Move {
piece :: Piece,
promotion :: Promotion,
start :: Square,
end :: Square
} deriving (Eq, Ord, Generic)
instance Hashable Move
instance Arbitrary Move where
arbitrary = Move
<$> arbitrary <*> arbitrary
<*> chooseInt (0, 63) <*> chooseInt (0, 63)
newtype StorableMove = StorableMove Word32
deriving (Eq, Ord, Num, Bits, Generic, Storable)
encodeMove :: Maybe Move -> StorableMove
encodeMove Nothing = bit 31
encodeMove (Just Move {..}) = StorableMove
(fromIntegral start
.|. (fromIntegral end << 8)
.|. (fromIntegral pieceN << 16)
.|. (fromIntegral promotionN << 24))
where
Piece pieceN = piece
Promotion promotionN = promotion
decodeMove :: StorableMove -> Maybe Move
decodeMove (StorableMove n)
| testBit n 31 = Nothing
| otherwise = Just $! Move {
start = start,
end = end,
piece = piece,
promotion = promotion
}
where
start = fromIntegral n .&. 63
end = fromIntegral (n >> 8) .&. 63
piece = Piece $ fromIntegral (n >> 16)
promotion = Promotion $ fromIntegral ((n >> 24) .&. 7)
{-# INLINE foldBoardAttacks #-}
foldBoardAttacks :: (Square -> Board) -> Board -> Board
foldBoardAttacks !f !board = foldlBoard 0 (.|) f board
{-# INLINE foldBoardMoves #-}
foldBoardMoves :: Piece -> (Square -> Board) -> Board -> [Move] -> [Move]
foldBoardMoves !piece !f !board moves =
foldlBoard moves (foldBoardSquares piece f) id board
{-# INLINE foldBoardMovesConst #-}
foldBoardMovesConst :: Piece -> Square -> Board -> [Move] -> [Move]
foldBoardMovesConst !piece !end !board moves =
foldlBoard moves genMoves id board
where
genMoves xs start = Move piece NoProm start end : xs
{-# INLINE foldBoardPawnMovesConst #-}
foldBoardPawnMovesConst :: Square -> Board -> [Move] -> [Move]
foldBoardPawnMovesConst !end !board moves =
foldlBoard moves genMoves id board
where
genMoves xs start = Move Pawn promotion start end : xs
!promotion
| testSquare (rank_1 .| rank_8) end = QueenProm
| otherwise = NoProm
{-# INLINE foldBoardSquares #-}
foldBoardSquares :: Piece -> (Square -> Board) -> [Move] -> Square -> [Move]
foldBoardSquares Pawn !f moves !start =
foldlBoard moves (flip $ genPawnMoves start) id $! f start
foldBoardSquares piece !f moves !start =
foldlBoard moves (flip cons) (Move piece NoProm start) (f start)
{-# INLINE genPawnMoves #-}
genPawnMoves :: Square -> Square -> [Move] -> [Move]
genPawnMoves !start !end xs
| testSquare (rank_1 .| rank_8) end =
Move Pawn QueenProm start end
: Move Pawn KnightProm start end
: Move Pawn RookProm start end
: Move Pawn BishopProm start end
: xs
| otherwise =
Move Pawn NoProm start end : xs
{-# INLINE foldlBoard #-}
foldlBoard :: a -> (a -> b -> a) -> (Square -> b) -> Board -> a
foldlBoard !initial !foldFn !mapFn = go 0 initial
where
go _ acc 0 = acc
go !i !acc board = go (i' + 1) acc' board'
where
acc' = foldFn acc $! mapFn i'
i' = i + current
board' = (board >> current) >> 1
current = lsb board
showBoard :: Board -> String
showBoard board = unlines $ map showBin
$ reverse $ take 8
$ iterate (>> 8) board
where
showBin w = intersperse ' ' [sb (testSquare w i) | i <- [0 .. 7]]
sb False = '0'
sb True = 'X'
showSquare :: Square -> String
showSquare n = [fileChars !! toFile n, rankChars !! toRank n]
where
fileChars = Vector.fromList ['a' .. 'h']
rankChars = Vector.fromList ['1' .. '8']
instance Show Move where
show (Move {..}) = showSquare start <> showSquare end <> show promotion