packages feed

hcheckers-0.1.0.2: src/Formats/Fen.hs

{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeFamilies #-}

module Formats.Fen where

import Control.Monad.State
import qualified Data.Text as T
import Text.Megaparsec hiding (Label)
import Text.Megaparsec.Char

import Core.Types
import Core.Board
import Formats.Types

pSide :: Parser Side
pSide = do
  l <- oneOf ['W', 'B'] :: Parser Char
  case l of
    'W' -> return First
    'B' -> return Second

pPiece :: SomeRules -> Parser (Label, PieceKind)
pPiece (SomeRules rules) = do
  kind <- do
          k <- optional $ try $ char 'K'
          case k of
            Nothing -> return Man
            Just _ -> return King
  not <- some digitChar
  lbl <- case parseNumericNotation (boardSize rules) (T.pack not) of
            Left err -> fail err
            Right lbl -> return lbl
  return (lbl, kind)

pFen :: SomeRules -> Parser Fen
pFen rules = do
    turn <- pSide
    char ':'
    side1 <- pSide
    pieces1 <- try (pPiece rules) `sepBy` char ','
    char ':'
    side2 <- pSide
    pieces2 <- try (pPiece rules) `sepBy` char ','
    if side1 == First
      then return $ Fen turn pieces1 pieces2
      else return $ Fen turn pieces2 pieces1

fenToBoardRep :: Fen -> BoardRep
fenToBoardRep fen =
  BoardRep $ [(lbl, Piece kind First) | (lbl, kind) <- fenFirst fen] ++
             [(lbl, Piece kind Second) | (lbl, kind) <- fenSecond fen]

parseFen :: SomeRules -> T.Text -> Either String (Side, BoardRep)
parseFen rules text =
  case evalState (runParserT (pFen rules) "FEN" text) Nothing of
    Left err -> Left $ errorBundlePretty err
    Right fen -> Right (fenNextMove fen, fenToBoardRep fen)

boardToFen :: Side -> Board -> Fen
boardToFen side b =
  Fen {
    fenNextMove = side,
    fenFirst = [(lbl, Man) | lbl <- myMen First b] ++
               [(lbl, King) | lbl <- myKings First b],
    fenSecond = [(lbl, Man) | lbl <- myMen Second b] ++
                [(lbl, King) | lbl <- myKings Second b]
  }

showFen :: BoardSize -> Fen -> T.Text
showFen bsize fen =
    showSide (fenNextMove fen) <> ":W" <>
    T.intercalate "," (map showPiece $ fenFirst fen) <> ":B" <>
    T.intercalate "," (map showPiece $ fenSecond fen)
  where
    showSide First = "W"
    showSide Second = "B"

    showPiece (lbl, Man) = numericNotation bsize lbl
    showPiece (lbl, King) = "K" <> numericNotation bsize lbl