module Chess.Internal.Notation (parseMove, parseCoordinateNotation, parseCoordinateStringWithPromotion) where import Chess.Internal.Move import Chess.Internal.Board import Chess.Internal.Piece import Data.List import Data.Char import Data.Attoparsec.Text import qualified Data.Text as T import Control.Applicative parseMove :: GameState -> String -> Maybe Move parseMove = parseCoordinateNotation parseCoordinateNotation :: GameState -> String -> Maybe Move parseCoordinateNotation game moveString = case parseOnly parseCoordinateStringWithPromotion (T.pack moveString) of Left _ -> Nothing Right (start, end, promotion) -> findMoveForCoordinates game start end promotion findMoveForCoordinates :: GameState -> Coordinates -> Coordinates -> Maybe PieceType -> Maybe Move findMoveForCoordinates game start end Nothing | length moves == 1 = Just $ head moves where moves = findMovesMatchingCoordinates game start end findMoveForCoordinates game start end (Just promotion) = findPromotionMove start end promotion moves where moves = findMovesMatchingCoordinates game start end findMoveForCoordinates _ _ _ _ = Nothing findPromotionMove :: Coordinates -> Coordinates -> PieceType -> [Move] -> Maybe Move findPromotionMove start end promotion = find matchPromotionMove where matchPromotionMove (Promotion _ s e p) = s == start && e == end && p == promotion matchPromotionMove _ = False findMovesMatchingCoordinates :: GameState -> Coordinates -> Coordinates -> [Move] findMovesMatchingCoordinates game start end = filter (coordinatesMatch start end) allMoves where allMoves = generateAllMoves game coordinatesMatch :: Coordinates -> Coordinates -> Move -> Bool coordinatesMatch start end (Movement _ from to) = from == start && to == end coordinatesMatch start end (Capture _ from to) = from == start && to == end coordinatesMatch start end (EnPassant _ from to) = from == start && to == end coordinatesMatch start end (PawnDoubleMove _ from to) = from == start && to == end coordinatesMatch start end (Promotion _ from to _) = from == start && to == end coordinatesMatch start end (Castling White Short) = start == (7, 4) && end == (7, 6) coordinatesMatch start end (Castling White Long) = start == (7, 4) && end == (7, 2) coordinatesMatch start end (Castling Black Short) = start == (0, 4) && end == (0, 6) coordinatesMatch start end (Castling Black Long) = start == (0, 4) && end == (0, 2) parseCoordinateStringWithPromotion :: Parser (Coordinates, Coordinates, Maybe PieceType) parseCoordinateStringWithPromotion = do (coord1, coord2) <- parseCoordinateString promotion <- parsePromotion endOfInput return (coord1, coord2, promotion) parseCoordinateString :: Parser (Coordinates, Coordinates) parseCoordinateString = do coord1 <- parseCoordinates _ <- char '-' coord2 <- parseCoordinates return (coord1, coord2) parseCoordinates :: Parser Coordinates parseCoordinates = do column <- letter row <- digit case parseCoordinate [toLower column, row] of Just coordinates -> return coordinates Nothing -> fail "Could not parse coordinate" parsePromotion :: Parser (Maybe PieceType) parsePromotion = (Just <$> parsePromotionEqualSign) <|> (Just <$> parsePromotionParenthesis) <|> return Nothing parsePromotionEqualSign :: Parser PieceType parsePromotionEqualSign = do _ <- char '=' promotionChar <- satisfy (`elem` "NBRQ") case parsePieceType promotionChar of Just piece -> return piece Nothing -> fail "Invalid promotion piecetype" parsePromotionParenthesis :: Parser PieceType parsePromotionParenthesis = do _ <- char '(' promotionChar <- satisfy (`elem` "NBRQ") _ <- char ')' case parsePieceType promotionChar of Just piece -> return piece Nothing -> fail "Invalid promotion piecetype"