{-
Copyright (C) 2018 Dr. Alistair Ward
This file is part of BishBosh.
BishBosh is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
BishBosh is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with BishBosh. If not, see .
-}
{- |
[@AUTHOR@] Dr. Alistair Ward
[@DESCRIPTION@] Categorises /move/s, & provides ancillary information as required.
-}
module BishBosh.Attribute.MoveType(
-- * Types
-- ** Type-synonyms
-- IsShort,
-- ** Data-types
MoveType(
Castle,
EnPassant,
Normal
),
-- * Constants
tag,
shortCastle,
longCastle,
enPassant,
-- * Functions
nPiecesMutator,
-- ** Constructors
mkMaybeNormalMoveType,
mkNormalMoveType,
-- ** Predicates
isCastle,
isEnPassant,
-- isNormal,
isCapture,
isPromotion,
isQuiet,
isAcyclic,
-- ** Query
getMaybeExplicitlyTakenRank,
getMaybeImplicitlyTakenRank
) where
import qualified BishBosh.Attribute.Rank as Attribute.Rank
import qualified BishBosh.Text.ShowList as Text.ShowList
import qualified Control.Arrow
import qualified Control.DeepSeq
import qualified Control.Exception
import qualified Data.Default
import qualified Data.List.Extra
import qualified Data.Maybe
-- | Used to qualify output.
tag :: String
tag = "moveType"
-- | Self-documentation.
type IsShort = Bool
-- | Constant value required to denote a /short castle/.
shortCastle :: MoveType
shortCastle = Castle True
-- | Constant value required to denote a /long castle/.
longCastle :: MoveType
longCastle = Castle False
-- | Constant.
enPassant :: MoveType
enPassant = EnPassant
-- | Classifies the distinct types of /move/.
data MoveType
= Castle IsShort -- ^ Castling between the @King@ & one of its @Rook@s.
| EnPassant -- ^ Capture by a @Pawn@ of a @Pawn@ as it advanced two squares.
| Normal {
_getMaybeTakenRank :: Maybe Attribute.Rank.Rank, -- ^ The /rank/ of any opposing /piece/ which was just taken.
_getMaybePromotionRank :: Maybe Attribute.Rank.Rank -- ^ The /rank/ of any /piece/ to which a @Pawn@ was just promoted.
}
deriving Eq
instance Show MoveType where
showsPrec _ (Castle isShort) = showString "Castle (short" . Text.ShowList.showsAssociation . shows isShort . showChar ')'
showsPrec _ EnPassant = showString "En-passant"
showsPrec _ (Normal maybeTakenRank maybePromotionRank) = Text.ShowList.showsAssociationList' $ Data.Maybe.catMaybes [
fmap ((,) "takenRank" . shows) maybeTakenRank,
fmap ((,) "promotionRank" . shows) maybePromotionRank
]
instance Read MoveType where
readsPrec _ s = case Data.List.Extra.trimStart s of
'C' : 'a' : 's' : 't' : 'l' : 'e' : s1 -> [
(Castle isShort, remainder) |
("(", s2) <- lex s1,
("short", s3) <- lex s2,
("=", s4) <- lex s3,
(isShort, s5) <- reads s4,
(")", remainder) <- lex s5
] -- List-comprehension.
'E' : 'n' : '-' : 'p' : 'a' : 's' : 's' : 'a' : 'n' : 't' : remainder -> [(EnPassant, remainder)]
_ -> [
(normalMoveType, remainder) |
("{", s1) <- lex s,
(maybeTakenRank, s2) <- case [
pair |
("takenRank", s11) <- lex s1,
("=", s12) <- lex s11,
pair <- reads s12
] of
[] -> [(Nothing, s1)] -- Infer that nothing was taken.
parsed -> map (Control.Arrow.first Just) parsed,
s3 <- return $ case lex s2 of
[(",", s21)] -> s21
_ -> s2,
(maybePromotionRank, s4) <- case [
pair |
("promotionRank", s31) <- lex s3,
("=", s32) <- lex s31,
pair <- reads s32
] of
[] -> [(Nothing, s3)] -- Infer that there was no promotion.
parsed -> map (Control.Arrow.first Just) parsed,
("}", remainder) <- lex s4,
normalMoveType <- Data.Maybe.maybeToList $ mkMaybeNormalMoveType maybeTakenRank maybePromotionRank
] -- List-comprehension.
instance Control.DeepSeq.NFData MoveType where
rnf (Castle isShort) = Control.DeepSeq.rnf isShort
rnf (Normal t p) = Control.DeepSeq.rnf (t, p)
rnf _ = ()
instance Data.Default.Default MoveType where
def = Normal Nothing Nothing
instance Attribute.Rank.Promotable MoveType where
getMaybePromotionRank (Normal _ maybePromotionRank) = maybePromotionRank
getMaybePromotionRank _ = Nothing
-- | Smart-constructor for normal move-types.
mkMaybeNormalMoveType
:: Maybe Attribute.Rank.Rank -- ^ The /rank/ of any opposing /piece/ which was just taken.
-> Maybe Attribute.Rank.Rank -- ^ The /rank/ to which a @Pawn@ was just promoted.
-> Maybe MoveType -- ^ Maybe the required /move-type/.
mkMaybeNormalMoveType maybeTakenRank maybePromotionRank
| Data.Maybe.maybe True {-nothing taken-} (/= Attribute.Rank.King) maybeTakenRank
, Data.Maybe.maybe True {-nothing promoted-} (
`elem` Attribute.Rank.promotionProspects
) maybePromotionRank = Just $ Normal maybeTakenRank maybePromotionRank
| otherwise = Nothing
-- | Smart-constructor for normal move-types.
mkNormalMoveType
:: Maybe Attribute.Rank.Rank -- ^ The /rank/ of any opposing /piece/ which is to be taken.
-> Maybe Attribute.Rank.Rank -- ^ The /rank/ to which a @Pawn@ is to be promoted.
-> MoveType
mkNormalMoveType maybeTakenRank maybePromotionRank = Control.Exception.assert (
Data.Maybe.maybe True {-nothing taken-} (
/= Attribute.Rank.King
) maybeTakenRank && Data.Maybe.maybe True {-nothing promoted-} (
`elem` Attribute.Rank.promotionProspects
) maybePromotionRank
) $ Normal maybeTakenRank maybePromotionRank
-- | Predicate.
isCastle :: MoveType -> Bool
isCastle (Castle _) = True
isCastle _ = False
-- | Predicate.
isEnPassant :: MoveType -> Bool
isEnPassant EnPassant = True
isEnPassant _ = False
-- | Whether the /move/ was neither @EnPassant@ nor @Castle@.
isNormal :: MoveType -> Bool
isNormal (Normal _ _) = True
isNormal _ = False
-- | Whether a piece was captured, including @Pawn@s taken En-passant.
isCapture :: MoveType -> Bool
{-# INLINE isCapture #-}
isCapture (Normal (Just _) _) = True
isCapture moveType = isEnPassant moveType
-- | Whether the /move/ includes @Pawn@-promotion.
isPromotion :: MoveType -> Bool
isPromotion (Normal _ (Just _)) = True
isPromotion _ = False
-- | .
isQuiet :: MoveType -> Bool
isQuiet (Normal Nothing Nothing) = True
isQuiet moveType = isCastle moveType
{- |
* Whether the /move/ can't be a member of a repeated cycle.
* CAVEAT: one can't infer from a negative result that the move can be repeated, since the mover may have been a @Pawn@.
-}
isAcyclic :: MoveType -> Bool
isAcyclic (Normal Nothing Nothing) = False
isAcyclic _ = True
-- | Query whether a /piece/ was explicitly taken, excluding @Pawn@s taken En-passant.
getMaybeExplicitlyTakenRank :: MoveType -> Maybe Attribute.Rank.Rank
getMaybeExplicitlyTakenRank (Normal maybeTakenRank _) = maybeTakenRank
getMaybeExplicitlyTakenRank _ = Nothing
-- | Query whether a /piece/ was taken either explicitly, or implicitly during En-passant.
getMaybeImplicitlyTakenRank :: MoveType -> Maybe Attribute.Rank.Rank
getMaybeImplicitlyTakenRank EnPassant = Just Attribute.Rank.Pawn
getMaybeImplicitlyTakenRank moveType = getMaybeExplicitlyTakenRank moveType
-- | Returns the mutator required to adjust the number of pieces after a move.
nPiecesMutator :: Enum nPieces => MoveType -> (nPieces -> nPieces)
{-# INLINE nPiecesMutator #-}
nPiecesMutator moveType
| isCapture moveType = pred
| otherwise = id