{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}
module BishBosh.State.MaybePieceByCoordinates(
MaybePieceByCoordinates(),
inferMoveType,
findPieces,
findBlockingPiece,
findProximateKnights,
findAttackerInDirection,
sumPieceSquareValueByLogicalColour,
listDestinationsFor,
show2D,
dereference,
defineCoordinates,
movePiece,
isVacant,
isOccupied,
isClear,
isObstructed,
isEnPassantMove
) where
import Control.Applicative((<|>))
import Control.Arrow((&&&), (***))
import Data.Array.IArray((!), (//))
import qualified BishBosh.Attribute.ColourScheme as Attribute.ColourScheme
import qualified BishBosh.Attribute.Direction as Attribute.Direction
import qualified BishBosh.Attribute.LogicalColour as Attribute.LogicalColour
import qualified BishBosh.Attribute.LogicalColourOfSquare as Attribute.LogicalColourOfSquare
import qualified BishBosh.Attribute.MoveType as Attribute.MoveType
import qualified BishBosh.Attribute.PhysicalColour as Attribute.PhysicalColour
import qualified BishBosh.Attribute.Rank as Attribute.Rank
import qualified BishBosh.Cartesian.Abscissa as Cartesian.Abscissa
import qualified BishBosh.Cartesian.Coordinates as Cartesian.Coordinates
import qualified BishBosh.Cartesian.Ordinate as Cartesian.Ordinate
import qualified BishBosh.Component.Move as Component.Move
import qualified BishBosh.Component.Piece as Component.Piece
import qualified BishBosh.Component.PieceSquareArray as Component.PieceSquareArray
import qualified BishBosh.Component.Zobrist as Component.Zobrist
import qualified BishBosh.Data.Exception as Data.Exception
import qualified BishBosh.Property.Empty as Property.Empty
import qualified BishBosh.Property.ForsythEdwards as Property.ForsythEdwards
import qualified BishBosh.Property.Opposable as Property.Opposable
import qualified BishBosh.Property.Reflectable as Property.Reflectable
import qualified BishBosh.State.Censor as State.Censor
import qualified BishBosh.Text.ShowList as Text.ShowList
import qualified BishBosh.Types as T
import qualified Control.Arrow
import qualified Control.DeepSeq
import qualified Control.Exception
import qualified Data.Array.IArray
import qualified Data.Char
import qualified Data.Default
import qualified Data.List
import qualified Data.List.Extra
import qualified Data.Map
import qualified Data.Maybe
import qualified ToolShed.Data.List.Runlength
newtype MaybePieceByCoordinates x y = MkMaybePieceByCoordinates {
deconstruct :: Data.Array.IArray.Array (
Cartesian.Coordinates.Coordinates x y
) (
Maybe Component.Piece.Piece
)
} deriving (Eq, Ord)
rankSeparator :: Char
rankSeparator = '/'
listToRaster :: [a] -> [[a]]
listToRaster = Data.List.Extra.chunksOf $ fromIntegral Cartesian.Abscissa.xLength
instance (
Enum x,
Enum y,
Ord x,
Ord y
) => Read (MaybePieceByCoordinates x y) where
readsPrec _ = Property.ForsythEdwards.readsFEN
instance (
Enum x,
Enum y,
Ord x,
Ord y
) => Show (MaybePieceByCoordinates x y) where
showsPrec _ = Property.ForsythEdwards.showsFEN
instance (
Enum x,
Enum y,
Ord x,
Ord y
) => Property.ForsythEdwards.ReadsFEN (MaybePieceByCoordinates x y) where
readsFEN s
| length rows /= fromIntegral Cartesian.Ordinate.yLength || any (
(/= fromIntegral Cartesian.Abscissa.xLength) . length
) rows = []
| otherwise = [(MkMaybePieceByCoordinates . Cartesian.Coordinates.listArrayByCoordinates . concat $ reverse rows, remainder)]
where
(rows, remainder) = Control.Arrow.first (
map (
concatMap (
\c -> case reads [c] of
[(i, "")] -> replicate i Nothing
_ -> [Just piece | (piece, []) <- Property.ForsythEdwards.readsFEN [c]]
)
) . Text.ShowList.splitOn (== rankSeparator)
) . span (
`elem` rankSeparator : concatMap Property.ForsythEdwards.showFEN Component.Piece.range ++ concatMap show [1 .. Cartesian.Abscissa.xLength]
) $ Data.List.Extra.trimStart s
instance (
Enum x,
Enum y,
Ord x,
Ord y
) => Property.ForsythEdwards.ShowsFEN (MaybePieceByCoordinates x y) where
showsFEN MkMaybePieceByCoordinates { deconstruct = byCoordinates } = foldr1 (
flip (.)
) . Data.List.intersperse (
showChar rankSeparator
) . map (
foldr1 (.) . concatMap (
\(runLength, maybePiece) -> Data.Maybe.maybe [
shows runLength
] (
replicate runLength . Property.ForsythEdwards.showsFEN
) maybePiece
) . ToolShed.Data.List.Runlength.encode
) . listToRaster $ Data.Array.IArray.elems byCoordinates
instance (
Enum x,
Enum y,
Ord x,
Ord y
) => Data.Default.Default (MaybePieceByCoordinates x y) where
def = Property.ForsythEdwards.readFEN . Data.List.intercalate [rankSeparator] $ map ($ Attribute.LogicalColour.Black) [
showNobility,
showPawnRow
] ++ replicate 4 "8" ++ map ($ Attribute.LogicalColour.White) [
showPawnRow,
showNobility
] where
showPieces :: [Component.Piece.Piece] -> String
showPieces = concatMap Property.ForsythEdwards.showFEN
showPawnRow, showNobility :: Attribute.LogicalColour.LogicalColour -> String
showPawnRow logicalColour = showPieces . replicate (fromIntegral Cartesian.Abscissa.xLength) $ Component.Piece.mkPawn logicalColour
showNobility logicalColour = showPieces $ map (Component.Piece.mkPiece logicalColour) Attribute.Rank.nobility
instance (
Enum x,
Enum y,
Ord x,
Ord y
) => Property.Reflectable.ReflectableOnX (MaybePieceByCoordinates x y) where
reflectOnX MkMaybePieceByCoordinates { deconstruct = byCoordinates } = MkMaybePieceByCoordinates . Data.Array.IArray.array (minBound, maxBound) . map (
Property.Reflectable.reflectOnX *** fmap Property.Opposable.getOpposite
) $ Data.Array.IArray.assocs byCoordinates
instance (
Enum x,
Enum y,
Ord x,
Ord y
) => Property.Reflectable.ReflectableOnY (MaybePieceByCoordinates x y) where
reflectOnY MkMaybePieceByCoordinates { deconstruct = byCoordinates } = MkMaybePieceByCoordinates . Data.Array.IArray.array (minBound, maxBound) . map (
Control.Arrow.first Property.Reflectable.reflectOnY
) $ Data.Array.IArray.assocs byCoordinates
instance (
Enum x,
Enum y,
Ord x,
Ord y
) => Property.Empty.Empty (MaybePieceByCoordinates x y) where
empty = MkMaybePieceByCoordinates . Cartesian.Coordinates.listArrayByCoordinates $ repeat Nothing
instance (
Control.DeepSeq.NFData x,
Control.DeepSeq.NFData y
) => Control.DeepSeq.NFData (MaybePieceByCoordinates x y) where
rnf MkMaybePieceByCoordinates { deconstruct = byCoordinates } = Control.DeepSeq.rnf byCoordinates
instance (
Enum x,
Enum y,
Ord x,
Ord y
) => State.Censor.Censor (MaybePieceByCoordinates x y) where
countPiecesByLogicalColour = Data.List.foldl' (
\acc piece -> let
acc'@(nBlack, nWhite) = (
if Component.Piece.isBlack piece
then Control.Arrow.first
else Control.Arrow.second
) succ acc
in nBlack `seq` nWhite `seq` acc'
) (0, 0) . getPieces
countPieces = length . getPieces
countPieceDifferenceByRank = Data.Array.IArray.accumArray (+) 0 (minBound, maxBound) . map (
Component.Piece.getRank &&& (
\piece -> (
if Component.Piece.isBlack piece
then negate
else id
) 1
)
) . getPieces
hasInsufficientMaterial maybePieceByCoordinates = all (
(`notElem` Attribute.Rank.individuallySufficientMaterial) . Component.Piece.getRank . snd
) locatedPieces && case blackKnights ++ whiteKnights of
[] -> Cartesian.Coordinates.areSquaresIsochromatic bishops
[_] -> null bishops
_ -> False
where
locatedPieces = findPieces maybePieceByCoordinates
[blackKnights, blackBishops, whiteKnights, whiteBishops] = [
[
coordinates |
(coordinates, piece) <- locatedPieces,
piece == Component.Piece.mkPiece logicalColour rank
] |
logicalColour <- Attribute.LogicalColour.range,
rank <- [Attribute.Rank.Knight, Attribute.Rank.Bishop]
]
bishops = blackBishops ++ whiteBishops
hasBothKings maybePieceByCoordinates = case Data.List.partition Component.Piece.isBlack . filter Component.Piece.isKing $ getPieces maybePieceByCoordinates of
([_], [_]) -> True
_ -> False
instance (Enum x, Enum y, Ord x, Ord y) => Component.Zobrist.Hashable2D MaybePieceByCoordinates x y where
listRandoms2D MkMaybePieceByCoordinates { deconstruct = byCoordinates } zobrist = [
uncurry Component.Zobrist.dereferenceRandomByCoordinatesByRankByLogicalColour (Component.Piece.getLogicalColour &&& Component.Piece.getRank $ piece) coordinates zobrist |
(coordinates, Just piece) <- Data.Array.IArray.assocs byCoordinates
]
dereference :: (
Enum x,
Enum y,
Ord x,
Ord y
)
=> Cartesian.Coordinates.Coordinates x y
-> MaybePieceByCoordinates x y
-> Maybe Component.Piece.Piece
{-# INLINE dereference #-}
dereference coordinates MkMaybePieceByCoordinates { deconstruct = byCoordinates } = byCoordinates ! coordinates
inferMoveType :: (
Enum x,
Enum y,
Ord x,
Ord y,
Show x,
Show y
)
=> Component.Move.Move x y
-> Maybe Attribute.Rank.Rank
-> MaybePieceByCoordinates x y
-> Attribute.MoveType.MoveType
{-# SPECIALISE inferMoveType :: Component.Move.Move T.X T.Y -> Maybe Attribute.Rank.Rank -> MaybePieceByCoordinates T.X T.Y -> Attribute.MoveType.MoveType #-}
inferMoveType move maybePromotionRank maybePieceByCoordinates@MkMaybePieceByCoordinates { deconstruct = byCoordinates }
| Just sourcePiece <- byCoordinates ! Component.Move.getSource move = case [
moveType |
Component.Piece.isKing sourcePiece,
(moveType, kingsMove, _) <- Component.Move.castlingMovesByLogicalColour ! Component.Piece.getLogicalColour sourcePiece,
move == kingsMove
] of
moveType : _ -> moveType
_
| isEnPassantMove move maybePieceByCoordinates -> Attribute.MoveType.enPassant
| otherwise -> Attribute.MoveType.mkNormalMoveType (
fmap Component.Piece.getRank $ byCoordinates ! destination
) $ if Component.Piece.isPawnPromotion destination sourcePiece
then maybePromotionRank <|> Just Attribute.Rank.defaultPromotionRank
else Nothing
| otherwise = Control.Exception.throw . Data.Exception.mkSearchFailure . showString "BishBosh.State.MaybePieceByCoordinates.inferMoveType:\tno piece exists at " . shows (Component.Move.getSource move) . showString "; " $ shows maybePieceByCoordinates "."
where
destination = Component.Move.getDestination move
listDestinationsFor :: (
Enum x,
Enum y,
Ord x,
Ord y
)
=> Cartesian.Coordinates.Coordinates x y
-> Component.Piece.Piece
-> MaybePieceByCoordinates x y
-> [(Cartesian.Coordinates.Coordinates x y, Maybe Attribute.Rank.Rank)]
{-# SPECIALISE listDestinationsFor :: Cartesian.Coordinates.Coordinates T.X T.Y -> Component.Piece.Piece -> MaybePieceByCoordinates T.X T.Y -> [(Cartesian.Coordinates.Coordinates T.X T.Y, Maybe Attribute.Rank.Rank)] #-}
listDestinationsFor source piece maybePieceByCoordinates@MkMaybePieceByCoordinates { deconstruct = byCoordinates } = Control.Exception.assert (
byCoordinates ! source == Just piece
) $ if Component.Piece.getRank piece `elem` Attribute.Rank.fixedAttackRange
then let
findDestinations predicate = [
(destination, fmap Component.Piece.getRank maybeDestinationPiece) |
destination <- Component.Piece.findAttackDestinations source piece,
let maybeDestinationPiece = byCoordinates ! destination,
predicate maybeDestinationPiece
]
in if Component.Piece.isPawn piece
then findDestinations (
Data.Maybe.maybe False $ (/= logicalColour) . Component.Piece.getLogicalColour
) ++ let
advance :: (Enum y, Ord y) => Cartesian.Coordinates.Coordinates x y -> Cartesian.Coordinates.Coordinates x y
advance = Cartesian.Coordinates.advance logicalColour
advancedLocation = advance source
in if isVacant advancedLocation maybePieceByCoordinates
then (advancedLocation, Nothing) : [
(doubleAdvancedLocation, Nothing) |
Cartesian.Coordinates.isPawnsFirstRank logicalColour source,
let doubleAdvancedLocation = advance advancedLocation,
isVacant doubleAdvancedLocation maybePieceByCoordinates
]
else []
else findDestinations . Data.Maybe.maybe True $ (/= logicalColour) . Component.Piece.getLogicalColour
else let
takeUntil (destination : remainder)
| Just blockingPiece <- byCoordinates ! destination = [
(
destination,
Just $ Component.Piece.getRank blockingPiece
) | Component.Piece.getLogicalColour blockingPiece /= logicalColour
]
| otherwise = (destination, Nothing) : takeUntil remainder
takeUntil _ = []
in [
pairs |
direction <- Component.Piece.attackDirectionsByPiece Data.Map.! piece,
pairs <- takeUntil $ Cartesian.Coordinates.extrapolate direction source
]
where
logicalColour = Component.Piece.getLogicalColour piece
shows2D :: (
Enum x,
Enum y,
Integral column,
Ord x,
Ord y
)
=> column
-> Attribute.ColourScheme.ColourScheme
-> (Int, Int)
-> MaybePieceByCoordinates x y
-> ShowS
shows2D boardColumnMagnification colourScheme (xOrigin, yOrigin) MkMaybePieceByCoordinates { deconstruct = byCoordinates } = (
foldr (
\(y, pairs) showsRow -> showsRow . showString (
Attribute.PhysicalColour.selectGraphicsRendition True $ Attribute.PhysicalColour.mkFgColourCode Attribute.PhysicalColour.green
) . showChar y . foldr (
\(coordinates, c) acc' -> showString (
Attribute.PhysicalColour.selectGraphicsRendition False . Attribute.PhysicalColour.mkBgColourCode $ (
if Attribute.LogicalColourOfSquare.isBlack $ Cartesian.Coordinates.getLogicalColourOfSquare coordinates
then Attribute.ColourScheme.getDarkSquareColour
else Attribute.ColourScheme.getLightSquareColour
) colourScheme
) . showString (
Attribute.PhysicalColour.selectGraphicsRendition True . Attribute.PhysicalColour.mkFgColourCode $ (
if Data.Char.isLower c
then Attribute.ColourScheme.getDarkPieceColour
else Attribute.ColourScheme.getLightPieceColour
) colourScheme
) . let
showPadding = showString (fromIntegral (pred boardColumnMagnification) `replicate` ' ')
in showPadding . showChar c . showPadding . acc'
) showsReset pairs . showChar '\n'
) id . zip (
take (fromIntegral Cartesian.Ordinate.yLength) . enumFrom $ Data.Char.chr yOrigin
) . listToRaster . map (
Control.Arrow.second . Data.Maybe.maybe ' ' $ head . show
) $ Data.Array.IArray.assocs byCoordinates
) . showString (
replicate (fromIntegral boardColumnMagnification) ' '
) . showString (
Attribute.PhysicalColour.selectGraphicsRendition True $ Attribute.PhysicalColour.mkFgColourCode Attribute.PhysicalColour.green
) . foldr (.) showsReset (
Data.List.intersperse (
showString $ replicate (2 * fromIntegral (pred boardColumnMagnification)) ' '
) . map showChar . take (
fromIntegral Cartesian.Abscissa.xLength
) . enumFrom $ Data.Char.chr xOrigin
) where
showsReset :: ShowS
showsReset = showString $ Attribute.PhysicalColour.selectGraphicsRendition False 0
show2D :: (
Enum x,
Enum y,
Integral column,
Ord x,
Ord y
)
=> column
-> Attribute.ColourScheme.ColourScheme
-> (Int, Int)
-> MaybePieceByCoordinates x y
-> String
show2D boardColumnMagnification colourScheme (xOrigin, yOrigin) maybePieceByCoordinates = shows2D boardColumnMagnification colourScheme (xOrigin, yOrigin) maybePieceByCoordinates ""
getPieces :: (
Enum x,
Enum y,
Ord x,
Ord y
) => MaybePieceByCoordinates x y -> [Component.Piece.Piece]
getPieces MkMaybePieceByCoordinates { deconstruct = byCoordinates } = Data.Maybe.catMaybes $ Data.Array.IArray.elems byCoordinates
findPieces :: (
Enum x,
Enum y,
Ord x,
Ord y
) => MaybePieceByCoordinates x y -> [Component.Piece.LocatedPiece x y]
findPieces MkMaybePieceByCoordinates { deconstruct = byCoordinates } = [
(coordinates, piece) |
(coordinates, Just piece) <- Data.Array.IArray.assocs byCoordinates
]
findBlockingPiece :: (
Enum x,
Enum y,
Ord x,
Ord y
)
=> Attribute.Direction.Direction
-> Cartesian.Coordinates.Coordinates x y
-> MaybePieceByCoordinates x y
-> Maybe (Component.Piece.LocatedPiece x y)
{-# SPECIALISE findBlockingPiece :: Attribute.Direction.Direction -> Cartesian.Coordinates.Coordinates T.X T.Y -> MaybePieceByCoordinates T.X T.Y -> Maybe (Component.Piece.LocatedPiece T.X T.Y) #-}
findBlockingPiece direction source MkMaybePieceByCoordinates { deconstruct = byCoordinates } = Data.Maybe.listToMaybe [
(coordinates, piece) |
(coordinates, Just piece) <- map (id &&& (byCoordinates !)) $ Cartesian.Coordinates.extrapolate direction source
]
findProximateKnights :: (
Enum x,
Enum y,
Ord x,
Ord y
)
=> Attribute.LogicalColour.LogicalColour
-> Cartesian.Coordinates.Coordinates x y
-> MaybePieceByCoordinates x y
-> [Cartesian.Coordinates.Coordinates x y]
findProximateKnights logicalColour destination MkMaybePieceByCoordinates { deconstruct = byCoordinates } = filter (
(== Just knight) . (byCoordinates !)
) $ Component.Piece.findAttackDestinations destination knight where
knight = Component.Piece.mkKnight logicalColour
findAttackerInDirection :: (
Enum x,
Enum y,
Ord x,
Ord y
)
=> Attribute.LogicalColour.LogicalColour
-> Attribute.Direction.Direction
-> Cartesian.Coordinates.Coordinates x y
-> MaybePieceByCoordinates x y
-> Maybe (Cartesian.Coordinates.Coordinates x y, Attribute.Rank.Rank)
{-# SPECIALISE findAttackerInDirection :: Attribute.LogicalColour.LogicalColour -> Attribute.Direction.Direction -> Cartesian.Coordinates.Coordinates T.X T.Y -> MaybePieceByCoordinates T.X T.Y -> Maybe (Cartesian.Coordinates.Coordinates T.X T.Y, Attribute.Rank.Rank) #-}
findAttackerInDirection destinationLogicalColour direction destination = (=<<) (
\(source, sourcePiece) -> if Component.Piece.getLogicalColour sourcePiece /= destinationLogicalColour && Component.Piece.canAttackAlong source destination sourcePiece
then Just (source, Component.Piece.getRank sourcePiece)
else Nothing
) . findBlockingPiece direction destination
isVacant :: (
Enum x,
Enum y,
Ord x,
Ord y
)
=> Cartesian.Coordinates.Coordinates x y
-> MaybePieceByCoordinates x y
-> Bool
{-# INLINE isVacant #-}
isVacant coordinates MkMaybePieceByCoordinates { deconstruct = byCoordinates } = Data.Maybe.isNothing $ byCoordinates ! coordinates
isOccupied :: (
Enum x,
Enum y,
Ord x,
Ord y
)
=> Cartesian.Coordinates.Coordinates x y
-> MaybePieceByCoordinates x y
-> Bool
{-# INLINE isOccupied #-}
isOccupied coordinates = not . isVacant coordinates
isClear :: (
Enum x,
Enum y,
Ord x,
Ord y
)
=> Cartesian.Coordinates.Coordinates x y
-> Cartesian.Coordinates.Coordinates x y
-> MaybePieceByCoordinates x y
-> Bool
{-# SPECIALISE isClear :: Cartesian.Coordinates.Coordinates T.X T.Y -> Cartesian.Coordinates.Coordinates T.X T.Y -> MaybePieceByCoordinates T.X T.Y -> Bool #-}
isClear source destination maybePieceByCoordinates = Control.Exception.assert (source /= destination) . all (`isVacant` maybePieceByCoordinates) . init $ Cartesian.Coordinates.interpolate source destination
isObstructed :: (
Enum x,
Enum y,
Ord x,
Ord y
)
=> Cartesian.Coordinates.Coordinates x y
-> Cartesian.Coordinates.Coordinates x y
-> MaybePieceByCoordinates x y
-> Bool
{-# SPECIALISE isObstructed :: Cartesian.Coordinates.Coordinates T.X T.Y -> Cartesian.Coordinates.Coordinates T.X T.Y -> MaybePieceByCoordinates T.X T.Y -> Bool #-}
isObstructed source destination = not . isClear source destination
isEnPassantMove :: (
Enum x,
Enum y,
Ord x,
Ord y
)
=> Component.Move.Move x y
-> MaybePieceByCoordinates x y
-> Bool
{-# SPECIALISE isEnPassantMove :: Component.Move.Move T.X T.Y -> MaybePieceByCoordinates T.X T.Y -> Bool #-}
isEnPassantMove move maybePieceByCoordinates@MkMaybePieceByCoordinates { deconstruct = byCoordinates }
| Just piece <- byCoordinates ! source
, let logicalColour = Component.Piece.getLogicalColour piece
= Cartesian.Coordinates.isEnPassantRank logicalColour source && Component.Piece.isPawn piece && destination `elem` Component.Piece.findAttackDestinations source piece && isVacant destination maybePieceByCoordinates
| otherwise = False
where
(source, destination) = Component.Move.getSource &&& Component.Move.getDestination $ move
type Transformation x y = MaybePieceByCoordinates x y -> MaybePieceByCoordinates x y
defineCoordinates :: (
Enum x,
Enum y,
Ord x,
Ord y
)
=> Maybe Component.Piece.Piece
-> Cartesian.Coordinates.Coordinates x y
-> Transformation x y
defineCoordinates maybePiece coordinates MkMaybePieceByCoordinates { deconstruct = byCoordinates } = Control.Exception.assert (
Data.Maybe.isJust maybePiece || Data.Maybe.isJust (byCoordinates ! coordinates)
) . MkMaybePieceByCoordinates $ byCoordinates // [(coordinates, maybePiece)]
movePiece :: (
Enum x,
Enum y,
Ord x,
Ord y
)
=> Component.Move.Move x y
-> Component.Piece.Piece
-> Maybe (Cartesian.Coordinates.Coordinates x y)
-> Transformation x y
{-# SPECIALISE movePiece :: Component.Move.Move T.X T.Y -> Component.Piece.Piece -> Maybe (Cartesian.Coordinates.Coordinates T.X T.Y) -> Transformation T.X T.Y #-}
movePiece move destinationPiece maybeEnPassantDestination MkMaybePieceByCoordinates { deconstruct = byCoordinates } = MkMaybePieceByCoordinates $ byCoordinates // Data.Maybe.maybe id (
(:) . flip (,) Nothing
) maybeEnPassantDestination [
(
Component.Move.getSource move,
Nothing
), (
Component.Move.getDestination move,
Just destinationPiece
)
]
sumPieceSquareValueByLogicalColour :: (
Enum x,
Enum y,
Num pieceSquareValue,
Ord x,
Ord y
)
=> Component.PieceSquareArray.FindPieceSquareValue x y pieceSquareValue
-> MaybePieceByCoordinates x y
-> [pieceSquareValue]
{-# SPECIALISE sumPieceSquareValueByLogicalColour :: Component.PieceSquareArray.FindPieceSquareValue T.X T.Y T.PieceSquareValue -> MaybePieceByCoordinates T.X T.Y -> [T.PieceSquareValue] #-}
sumPieceSquareValueByLogicalColour findPieceSquareValue = (
\(b, w) -> [b, w]
) . Data.List.foldl' (
\(b, w) (coordinates, piece) -> let
logicalColour = Component.Piece.getLogicalColour piece
pieceSquareValue = findPieceSquareValue logicalColour (Component.Piece.getRank piece) coordinates
in if Attribute.LogicalColour.isBlack logicalColour
then let b' = b + pieceSquareValue in b' `seq` (b', w)
else let w' = w + pieceSquareValue in w' `seq` (b, w')
) (0, 0) . findPieces