{-# LANGUAGE CPP, LambdaCase #-}
{-
	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 <http://www.gnu.org/licenses/>.
-}
{- |
 [@AUTHOR@]	Dr. Alistair Ward

 [@DESCRIPTION@]

	* This data-type maintains the state of the board, but it doesn't know its history.
	In consequence it knows neither whether castling has occurred, nor which @Pawn@s have been promoted, nor whose turn it is.

	* It allows unvalidated access to the board, to place, move, or remove /piece/s.
	In consequence;
		it enforces neither a conventional layout for the /piece/s nor even that there is exactly one @King@ per side;
		it permits one to move into check or to take a @King@.

	* Two models of the board are simultaneously maintained; a square-centric model "State.MaybePieceByCoordinates" & a piece-centric model "State.CoordinatesByRankByLogicalColour".
	Though maintenance of two models is a burden, the duality permits alternative implementations of the required searches, & often one is more efficient than the other.
-}

module BishBosh.State.Board(
-- * Types
-- ** Type-synonyms
--	Transformation,
--	NDefendersByCoordinatesByLogicalColour,
-- ** Data-types
	Board(
--		MkBoard,
		getMaybePieceByCoordinates,
		getCoordinatesByRankByLogicalColour,
		getNDefendersByCoordinatesByLogicalColour,
		getNPiecesDifferenceByRank,
		getNPawnsByFileByLogicalColour,
		getNPieces,
		getPassedPawnCoordinatesByLogicalColour
	),
-- * Functions
	countDefendersByCoordinatesByLogicalColour,
	summariseNDefendersByLogicalColour,
	sumPieceSquareValueByLogicalColour,
	findAttackersOf,
	findAttacksBy,
-- ** Constructors
--	fromMaybePieceByCoordinates,
-- ** Mutators
	movePiece,
-- ** Predicates
	passesThroughCheck,
	isKingChecked,
	exposesKing
) where

import			Control.Arrow((&&&), (***))
import			Data.Array.IArray((!), (//))
import qualified	BishBosh.Attribute.MoveType				as Attribute.MoveType
import qualified	BishBosh.Attribute.Rank					as Attribute.Rank
import qualified	BishBosh.Cartesian.Coordinates				as Cartesian.Coordinates
import qualified	BishBosh.Cartesian.Vector				as Cartesian.Vector
import qualified	BishBosh.Colour.LogicalColour				as Colour.LogicalColour
import qualified	BishBosh.Component.Accountant				as Component.Accountant
import qualified	BishBosh.Component.Move					as Component.Move
import qualified	BishBosh.Component.Piece				as Component.Piece
import qualified	BishBosh.Component.PieceSquareValueByCoordinatesByRank	as Component.PieceSquareValueByCoordinatesByRank
import qualified	BishBosh.Data.Exception					as Data.Exception
import qualified	BishBosh.Direction.Direction				as Direction.Direction
import qualified	BishBosh.Property.Empty					as Property.Empty
import qualified	BishBosh.Property.ExtendedPositionDescription		as Property.ExtendedPositionDescription
import qualified	BishBosh.Property.FixedMembership			as Property.FixedMembership
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.Property.SelfValidating			as Property.SelfValidating
import qualified	BishBosh.State.CoordinatesByRankByLogicalColour		as State.CoordinatesByRankByLogicalColour
import qualified	BishBosh.State.MaybePieceByCoordinates			as State.MaybePieceByCoordinates
import qualified	BishBosh.StateProperty.Censor				as StateProperty.Censor
import qualified	BishBosh.StateProperty.Hashable				as StateProperty.Hashable
import qualified	BishBosh.StateProperty.Mutator				as StateProperty.Mutator
import qualified	BishBosh.StateProperty.Seeker				as StateProperty.Seeker
import qualified	BishBosh.StateProperty.View				as StateProperty.View
import qualified	BishBosh.Type.Count					as Type.Count
import qualified	BishBosh.Type.Mass					as Type.Mass
import qualified	Control.Arrow
import qualified	Control.DeepSeq
import qualified	Control.Exception
import qualified	Data.Array.IArray
import qualified	Data.Default
import qualified	Data.Foldable
import qualified	Data.List
import qualified	Data.Map.Strict						as Map
import qualified	Data.Maybe

-- | The type of a function which transforms a /board/.
type Transformation	= Board -> Board

-- | The number of defenders for each /piece/, belonging to each side.
type NDefendersByCoordinatesByLogicalColour	= Colour.LogicalColour.ArrayByLogicalColour (Map.Map Cartesian.Coordinates.Coordinates Type.Count.NPieces)

{- |
	* The board is modelled as two alternative structures representing the same data, but indexed by either /coordinates/ or /piece/.

	* For efficiency some ancillary structures are also maintained.
-}
data Board	= MkBoard {
	Board -> MaybePieceByCoordinates
getMaybePieceByCoordinates			:: State.MaybePieceByCoordinates.MaybePieceByCoordinates,			-- ^ Defines any /piece/ currently located at each /coordinate/.
	Board -> CoordinatesByRankByLogicalColour
getCoordinatesByRankByLogicalColour		:: State.CoordinatesByRankByLogicalColour.CoordinatesByRankByLogicalColour,	-- ^ The /coordinates/ of each /piece/.
	Board -> NDefendersByCoordinatesByLogicalColour
getNDefendersByCoordinatesByLogicalColour	:: NDefendersByCoordinatesByLogicalColour,					-- ^ The number of defenders of each /piece/, indexed by /logical colour/ & then by /coordinates/.
	Board -> NPiecesByRank
getNPiecesDifferenceByRank			:: StateProperty.Censor.NPiecesByRank,						-- ^ The difference in the number of /piece/s of each /rank/ held by either side. @White@ /piece/s are arbitrarily considered positive & @Black@ ones negative.
	Board -> NPiecesByFileByLogicalColour
getNPawnsByFileByLogicalColour			:: StateProperty.Seeker.NPiecesByFileByLogicalColour,				-- ^ The number of @Pawn@s of each /logical colour/, for each /file/.
	Board -> NPieces
getNPieces					:: Type.Count.NPieces,								-- ^ The total number of pieces on the board, including @Pawn@s.
	Board -> CoordinatesByLogicalColour
getPassedPawnCoordinatesByLogicalColour		:: State.CoordinatesByRankByLogicalColour.CoordinatesByLogicalColour		-- ^ The /coordinates/ of any /passed/ @Pawn@s.
}

instance Eq Board where
	MkBoard { getMaybePieceByCoordinates :: Board -> MaybePieceByCoordinates
getMaybePieceByCoordinates = MaybePieceByCoordinates
maybePieceByCoordinates } == :: Board -> Board -> Bool
== MkBoard { getMaybePieceByCoordinates :: Board -> MaybePieceByCoordinates
getMaybePieceByCoordinates = MaybePieceByCoordinates
maybePieceByCoordinates' }	= MaybePieceByCoordinates
maybePieceByCoordinates MaybePieceByCoordinates -> MaybePieceByCoordinates -> Bool
forall a. Eq a => a -> a -> Bool
== MaybePieceByCoordinates
maybePieceByCoordinates'	-- N.B.: the remaining fields are implied.

instance Control.DeepSeq.NFData Board where
	rnf :: Board -> ()
rnf MkBoard {
		getMaybePieceByCoordinates :: Board -> MaybePieceByCoordinates
getMaybePieceByCoordinates			= MaybePieceByCoordinates
maybePieceByCoordinates,
		getCoordinatesByRankByLogicalColour :: Board -> CoordinatesByRankByLogicalColour
getCoordinatesByRankByLogicalColour		= CoordinatesByRankByLogicalColour
coordinatesByRankByLogicalColour,
		getNDefendersByCoordinatesByLogicalColour :: Board -> NDefendersByCoordinatesByLogicalColour
getNDefendersByCoordinatesByLogicalColour	= NDefendersByCoordinatesByLogicalColour
nDefendersByCoordinatesByLogicalColour,
#ifndef UNBOX_TYPECOUNT_ARRAYS
		getNPiecesDifferenceByRank			= nPiecesDifferenceByRank,
#endif
		getNPawnsByFileByLogicalColour :: Board -> NPiecesByFileByLogicalColour
getNPawnsByFileByLogicalColour			= NPiecesByFileByLogicalColour
nPawnsByFileByLogicalColour,
		getNPieces :: Board -> NPieces
getNPieces					= NPieces
nPieces,
		getPassedPawnCoordinatesByLogicalColour :: Board -> CoordinatesByLogicalColour
getPassedPawnCoordinatesByLogicalColour		= CoordinatesByLogicalColour
passedPawnCoordinatesByLogicalColour
	} = (MaybePieceByCoordinates, CoordinatesByRankByLogicalColour,
 NDefendersByCoordinatesByLogicalColour,
 NPiecesByFileByLogicalColour, NPieces, CoordinatesByLogicalColour)
-> ()
forall a. NFData a => a -> ()
Control.DeepSeq.rnf (
		MaybePieceByCoordinates
maybePieceByCoordinates,
		CoordinatesByRankByLogicalColour
coordinatesByRankByLogicalColour,
		NDefendersByCoordinatesByLogicalColour
nDefendersByCoordinatesByLogicalColour,
#ifndef UNBOX_TYPECOUNT_ARRAYS
		nPiecesDifferenceByRank,
#endif
		NPiecesByFileByLogicalColour
nPawnsByFileByLogicalColour,
		NPieces
nPieces,
		CoordinatesByLogicalColour
passedPawnCoordinatesByLogicalColour
	 )

instance Read Board where
	readsPrec :: NPieces -> ReadS Board
readsPrec NPieces
_	= ReadS Board
forall a. ReadsFEN a => ReadS a
Property.ForsythEdwards.readsFEN

instance Show Board where
	showsPrec :: NPieces -> Board -> ShowS
showsPrec NPieces
_	= Board -> ShowS
forall a. ShowsFEN a => a -> ShowS
Property.ForsythEdwards.showsFEN

instance Property.ExtendedPositionDescription.ReadsEPD Board where
	readsEPD :: ReadS Board
readsEPD	= ((MaybePieceByCoordinates, String) -> (Board, String))
-> [(MaybePieceByCoordinates, String)] -> [(Board, String)]
forall a b. (a -> b) -> [a] -> [b]
map ((MaybePieceByCoordinates -> Board)
-> (MaybePieceByCoordinates, String) -> (Board, String)
forall (a :: * -> * -> *) b c d.
Arrow a =>
a b c -> a (b, d) (c, d)
Control.Arrow.first MaybePieceByCoordinates -> Board
fromMaybePieceByCoordinates) ([(MaybePieceByCoordinates, String)] -> [(Board, String)])
-> (String -> [(MaybePieceByCoordinates, String)]) -> ReadS Board
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> [(MaybePieceByCoordinates, String)]
forall a. ReadsEPD a => ReadS a
Property.ExtendedPositionDescription.readsEPD

instance Property.ExtendedPositionDescription.ShowsEPD Board where
	showsEPD :: Board -> ShowS
showsEPD MkBoard { getMaybePieceByCoordinates :: Board -> MaybePieceByCoordinates
getMaybePieceByCoordinates = MaybePieceByCoordinates
maybePieceByCoordinates }	= MaybePieceByCoordinates -> ShowS
forall a. ShowsEPD a => a -> ShowS
Property.ExtendedPositionDescription.showsEPD MaybePieceByCoordinates
maybePieceByCoordinates

instance Property.ForsythEdwards.ReadsFEN Board where
	readsFEN :: ReadS Board
readsFEN	= ReadS Board
forall a. ReadsEPD a => ReadS a
Property.ExtendedPositionDescription.readsEPD

instance Property.ForsythEdwards.ShowsFEN Board

instance Data.Default.Default Board where
	def :: Board
def	= MaybePieceByCoordinates -> Board
fromMaybePieceByCoordinates MaybePieceByCoordinates
forall a. Default a => a
Data.Default.def {-MaybePieceByCoordinates-}

instance Property.Empty.Empty Board where
	empty :: Board
empty	= MaybePieceByCoordinates -> Board
fromMaybePieceByCoordinates MaybePieceByCoordinates
forall a. Empty a => a
Property.Empty.empty {-MaybePieceByCoordinates-}

instance Property.Reflectable.ReflectableOnX Board where
	reflectOnX :: Board -> Board
reflectOnX MkBoard { getMaybePieceByCoordinates :: Board -> MaybePieceByCoordinates
getMaybePieceByCoordinates = MaybePieceByCoordinates
maybePieceByCoordinates }	= MaybePieceByCoordinates -> Board
fromMaybePieceByCoordinates (MaybePieceByCoordinates -> Board)
-> MaybePieceByCoordinates -> Board
forall a b. (a -> b) -> a -> b
$ MaybePieceByCoordinates -> MaybePieceByCoordinates
forall a. ReflectableOnX a => a -> a
Property.Reflectable.reflectOnX MaybePieceByCoordinates
maybePieceByCoordinates

instance Property.Reflectable.ReflectableOnY Board where
	reflectOnY :: Board -> Board
reflectOnY MkBoard { getMaybePieceByCoordinates :: Board -> MaybePieceByCoordinates
getMaybePieceByCoordinates = MaybePieceByCoordinates
maybePieceByCoordinates }	= MaybePieceByCoordinates -> Board
fromMaybePieceByCoordinates (MaybePieceByCoordinates -> Board)
-> MaybePieceByCoordinates -> Board
forall a b. (a -> b) -> a -> b
$ MaybePieceByCoordinates -> MaybePieceByCoordinates
forall a. ReflectableOnY a => a -> a
Property.Reflectable.reflectOnY MaybePieceByCoordinates
maybePieceByCoordinates

instance StateProperty.Hashable.Hashable Board where
	listRandoms :: Zobrist positionHash -> Board -> [positionHash]
listRandoms Zobrist positionHash
zobrist MkBoard { getCoordinatesByRankByLogicalColour :: Board -> CoordinatesByRankByLogicalColour
getCoordinatesByRankByLogicalColour = CoordinatesByRankByLogicalColour
coordinatesByRankByLogicalColour }	= Zobrist positionHash
-> CoordinatesByRankByLogicalColour -> [positionHash]
forall hashable positionHash.
Hashable hashable =>
Zobrist positionHash -> hashable -> [positionHash]
StateProperty.Hashable.listRandoms Zobrist positionHash
zobrist CoordinatesByRankByLogicalColour
coordinatesByRankByLogicalColour	-- Forward.

instance StateProperty.Mutator.Mutator Board where
	defineCoordinates :: Maybe Piece -> Coordinates -> Board -> Board
defineCoordinates Maybe Piece
maybePiece Coordinates
coordinates MkBoard { getMaybePieceByCoordinates :: Board -> MaybePieceByCoordinates
getMaybePieceByCoordinates = MaybePieceByCoordinates
maybePieceByCoordinates }	= MaybePieceByCoordinates -> Board
fromMaybePieceByCoordinates (MaybePieceByCoordinates -> Board)
-> MaybePieceByCoordinates -> Board
forall a b. (a -> b) -> a -> b
$ Maybe Piece
-> Coordinates
-> MaybePieceByCoordinates
-> MaybePieceByCoordinates
forall mutator.
Mutator mutator =>
Maybe Piece -> Coordinates -> mutator -> mutator
StateProperty.Mutator.defineCoordinates Maybe Piece
maybePiece Coordinates
coordinates MaybePieceByCoordinates
maybePieceByCoordinates
	movePiece :: Move -> MoveType -> Piece -> Board -> Board
movePiece Move
move MoveType
moveType Piece
sourcePiece MkBoard { getMaybePieceByCoordinates :: Board -> MaybePieceByCoordinates
getMaybePieceByCoordinates = MaybePieceByCoordinates
maybePieceByCoordinates }		= MaybePieceByCoordinates -> Board
fromMaybePieceByCoordinates (MaybePieceByCoordinates -> Board)
-> MaybePieceByCoordinates -> Board
forall a b. (a -> b) -> a -> b
$ Move
-> MoveType
-> Piece
-> MaybePieceByCoordinates
-> MaybePieceByCoordinates
forall mutator.
Mutator mutator =>
Move -> MoveType -> Piece -> mutator -> mutator
StateProperty.Mutator.movePiece Move
move MoveType
moveType Piece
sourcePiece MaybePieceByCoordinates
maybePieceByCoordinates

instance StateProperty.Seeker.Seeker Board where
	findProximateKnights :: Board -> LogicalColour -> Coordinates -> [Coordinates]
findProximateKnights MkBoard {
		getCoordinatesByRankByLogicalColour :: Board -> CoordinatesByRankByLogicalColour
getCoordinatesByRankByLogicalColour	= CoordinatesByRankByLogicalColour
coordinatesByRankByLogicalColour
	} = CoordinatesByRankByLogicalColour
-> LogicalColour -> Coordinates -> [Coordinates]
forall seeker.
Seeker seeker =>
seeker -> LogicalColour -> Coordinates -> [Coordinates]
StateProperty.Seeker.findProximateKnights CoordinatesByRankByLogicalColour
coordinatesByRankByLogicalColour	-- Forward the request.

	findPieces :: (Piece -> Bool) -> Board -> [LocatedPiece]
findPieces Piece -> Bool
predicate MkBoard {
		getCoordinatesByRankByLogicalColour :: Board -> CoordinatesByRankByLogicalColour
getCoordinatesByRankByLogicalColour	= CoordinatesByRankByLogicalColour
coordinatesByRankByLogicalColour
	} = (Piece -> Bool)
-> CoordinatesByRankByLogicalColour -> [LocatedPiece]
forall seeker.
Seeker seeker =>
(Piece -> Bool) -> seeker -> [LocatedPiece]
StateProperty.Seeker.findPieces Piece -> Bool
predicate CoordinatesByRankByLogicalColour
coordinatesByRankByLogicalColour	-- Forward the request.

	countPawnsByFileByLogicalColour :: Board -> NPiecesByFileByLogicalColour
countPawnsByFileByLogicalColour	MkBoard {
		getCoordinatesByRankByLogicalColour :: Board -> CoordinatesByRankByLogicalColour
getCoordinatesByRankByLogicalColour	= CoordinatesByRankByLogicalColour
coordinatesByRankByLogicalColour
	} = CoordinatesByRankByLogicalColour -> NPiecesByFileByLogicalColour
forall seeker.
Seeker seeker =>
seeker -> NPiecesByFileByLogicalColour
StateProperty.Seeker.countPawnsByFileByLogicalColour CoordinatesByRankByLogicalColour
coordinatesByRankByLogicalColour	-- Forward the request.

instance Property.SelfValidating.SelfValidating Board where
	findInvalidity :: Board -> [String]
findInvalidity	= (MaybePieceByCoordinates, CoordinatesByRankByLogicalColour)
-> [String]
forall a. SelfValidating a => a -> [String]
Property.SelfValidating.findInvalidity ((MaybePieceByCoordinates, CoordinatesByRankByLogicalColour)
 -> [String])
-> (Board
    -> (MaybePieceByCoordinates, CoordinatesByRankByLogicalColour))
-> Board
-> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Board -> MaybePieceByCoordinates
getMaybePieceByCoordinates (Board -> MaybePieceByCoordinates)
-> (Board -> CoordinatesByRankByLogicalColour)
-> Board
-> (MaybePieceByCoordinates, CoordinatesByRankByLogicalColour)
forall (a :: * -> * -> *) b c c'.
Arrow a =>
a b c -> a b c' -> a b (c, c')
&&& Board -> CoordinatesByRankByLogicalColour
getCoordinatesByRankByLogicalColour)

-- | Constructor.
fromMaybePieceByCoordinates :: State.MaybePieceByCoordinates.MaybePieceByCoordinates -> Board
fromMaybePieceByCoordinates :: MaybePieceByCoordinates -> Board
fromMaybePieceByCoordinates MaybePieceByCoordinates
maybePieceByCoordinates	= Board
board where
	board :: Board
board@MkBoard { getCoordinatesByRankByLogicalColour :: Board -> CoordinatesByRankByLogicalColour
getCoordinatesByRankByLogicalColour = CoordinatesByRankByLogicalColour
coordinatesByRankByLogicalColour }	= MkBoard :: MaybePieceByCoordinates
-> CoordinatesByRankByLogicalColour
-> NDefendersByCoordinatesByLogicalColour
-> NPiecesByRank
-> NPiecesByFileByLogicalColour
-> NPieces
-> CoordinatesByLogicalColour
-> Board
MkBoard {
		getMaybePieceByCoordinates :: MaybePieceByCoordinates
getMaybePieceByCoordinates			= MaybePieceByCoordinates
maybePieceByCoordinates,
		getCoordinatesByRankByLogicalColour :: CoordinatesByRankByLogicalColour
getCoordinatesByRankByLogicalColour		= MaybePieceByCoordinates -> CoordinatesByRankByLogicalColour
forall seeker view. (Seeker seeker, View view) => seeker -> view
StateProperty.View.translate MaybePieceByCoordinates
maybePieceByCoordinates,									-- Infer.
		getNDefendersByCoordinatesByLogicalColour :: NDefendersByCoordinatesByLogicalColour
getNDefendersByCoordinatesByLogicalColour	= Board -> NDefendersByCoordinatesByLogicalColour
countDefendersByCoordinatesByLogicalColour Board
board,									-- Infer.
		getNPiecesDifferenceByRank :: NPiecesByRank
getNPiecesDifferenceByRank			= CoordinatesByRankByLogicalColour -> NPiecesByRank
forall censor. Censor censor => censor -> NPiecesByRank
StateProperty.Censor.countPieceDifferenceByRank CoordinatesByRankByLogicalColour
coordinatesByRankByLogicalColour,					-- Infer.
		getNPawnsByFileByLogicalColour :: NPiecesByFileByLogicalColour
getNPawnsByFileByLogicalColour			= CoordinatesByRankByLogicalColour -> NPiecesByFileByLogicalColour
forall seeker.
Seeker seeker =>
seeker -> NPiecesByFileByLogicalColour
StateProperty.Seeker.countPawnsByFileByLogicalColour CoordinatesByRankByLogicalColour
coordinatesByRankByLogicalColour,				-- Infer.
		getNPieces :: NPieces
getNPieces					= CoordinatesByRankByLogicalColour -> NPieces
forall censor. Censor censor => censor -> NPieces
StateProperty.Censor.countPieces CoordinatesByRankByLogicalColour
coordinatesByRankByLogicalColour,							-- Infer.
		getPassedPawnCoordinatesByLogicalColour :: CoordinatesByLogicalColour
getPassedPawnCoordinatesByLogicalColour		= CoordinatesByRankByLogicalColour -> CoordinatesByLogicalColour
State.CoordinatesByRankByLogicalColour.findPassedPawnCoordinatesByLogicalColour CoordinatesByRankByLogicalColour
coordinatesByRankByLogicalColour	-- Infer.
	}

{- |
	* Moves the referenced /piece/.

	* CAVEAT: no validation is performed.

	* CAVEAT: /castling/ must be implemented by making two calls.
-}
movePiece
	:: Component.Move.Move			-- ^ N.B.: illegal moves are acceptable.
	-> Maybe Attribute.MoveType.MoveType	-- ^ N.B.: this may not be available to the caller, for example during the illegal moves required for rollback.
	-> Transformation
movePiece :: Move -> Maybe MoveType -> Board -> Board
movePiece Move
move Maybe MoveType
maybeMoveType board :: Board
board@MkBoard {
	getMaybePieceByCoordinates :: Board -> MaybePieceByCoordinates
getMaybePieceByCoordinates			= MaybePieceByCoordinates
maybePieceByCoordinates,
	getCoordinatesByRankByLogicalColour :: Board -> CoordinatesByRankByLogicalColour
getCoordinatesByRankByLogicalColour		= CoordinatesByRankByLogicalColour
coordinatesByRankByLogicalColour,
	getNDefendersByCoordinatesByLogicalColour :: Board -> NDefendersByCoordinatesByLogicalColour
getNDefendersByCoordinatesByLogicalColour	= NDefendersByCoordinatesByLogicalColour
nDefendersByCoordinatesByLogicalColour,
	getNPiecesDifferenceByRank :: Board -> NPiecesByRank
getNPiecesDifferenceByRank			= NPiecesByRank
nPiecesDifferenceByRank,
	getNPieces :: Board -> NPieces
getNPieces					= NPieces
nPieces
}
	| Just Piece
sourcePiece <- MaybePieceByCoordinates -> Coordinates -> Maybe Piece
State.MaybePieceByCoordinates.dereference MaybePieceByCoordinates
maybePieceByCoordinates Coordinates
source	= let
		(LogicalColour
logicalColour, (LogicalColour
opponentsLogicalColour, Piece
oppositePiece))	= Piece -> LogicalColour
Component.Piece.getLogicalColour (Piece -> LogicalColour)
-> (Piece -> (LogicalColour, Piece))
-> Piece
-> (LogicalColour, (LogicalColour, Piece))
forall (a :: * -> * -> *) b c c'.
Arrow a =>
a b c -> a b c' -> a b (c, c')
&&& ((Piece -> LogicalColour
Component.Piece.getLogicalColour (Piece -> LogicalColour)
-> (Piece -> Piece) -> Piece -> (LogicalColour, Piece)
forall (a :: * -> * -> *) b c c'.
Arrow a =>
a b c -> a b c' -> a b (c, c')
&&& Piece -> Piece
forall a. a -> a
id) (Piece -> (LogicalColour, Piece))
-> (Piece -> Piece) -> Piece -> (LogicalColour, Piece)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Piece -> Piece
forall a. Opposable a => a -> a
Property.Opposable.getOpposite) (Piece -> (LogicalColour, (LogicalColour, Piece)))
-> Piece -> (LogicalColour, (LogicalColour, Piece))
forall a b. (a -> b) -> a -> b
$ Piece
sourcePiece

-- CAVEAT: this is an incomplete implementation, since Castling isn't represented, but rather implemented via two sequential calls to 'movePiece'.
		moveType :: Attribute.MoveType.MoveType
		moveType :: MoveType
moveType -- CAVEAT: one can't call 'State.MaybePieceByCoordinates.inferMoveType', since that performs some move-validation, & therefore exceeds the remit of this module.
			| Just MoveType
explicitMoveType	<- Maybe MoveType
maybeMoveType					= MoveType
explicitMoveType
			| MaybePieceByCoordinates -> Move -> Bool
State.MaybePieceByCoordinates.isEnPassantMove MaybePieceByCoordinates
maybePieceByCoordinates Move
move	= MoveType
Attribute.MoveType.enPassant	-- N.B.: if this move is valid, then one's opponent must have just double-advanced an adjacent Pawn.
			| Bool
otherwise									= Maybe Rank -> Maybe Rank -> MoveType
Attribute.MoveType.mkNormalMoveType (
				Piece -> Rank
Component.Piece.getRank (Piece -> Rank) -> Maybe Piece -> Maybe Rank
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> MaybePieceByCoordinates -> Coordinates -> Maybe Piece
State.MaybePieceByCoordinates.dereference MaybePieceByCoordinates
maybePieceByCoordinates Coordinates
destination
			) (Maybe Rank -> MoveType) -> Maybe Rank -> MoveType
forall a b. (a -> b) -> a -> b
$ if Piece -> Coordinates -> Bool
Component.Piece.isPawnPromotion Piece
sourcePiece Coordinates
destination
				then Rank -> Maybe Rank
forall a. a -> Maybe a
Just Rank
Attribute.Rank.defaultPromotionRank
				else Maybe Rank
forall a. Maybe a
Nothing

-- Derive the required values from moveType.
		(Piece
destinationPiece, (Maybe Piece
maybeExplicitlyTakenPiece, Bool
wasPawnTakenExplicitly))	= ((Piece -> Piece) -> Piece -> Piece
forall a b. (a -> b) -> a -> b
$ Piece
sourcePiece) ((Piece -> Piece) -> Piece)
-> (MoveType -> Piece -> Piece) -> MoveType -> Piece
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Piece -> Piece)
-> (Rank -> Piece -> Piece) -> Maybe Rank -> Piece -> Piece
forall b a. b -> (a -> b) -> Maybe a -> b
Data.Maybe.maybe Piece -> Piece
forall a. a -> a
id Rank -> Piece -> Piece
Component.Piece.promote (Maybe Rank -> Piece -> Piece)
-> (MoveType -> Maybe Rank) -> MoveType -> Piece -> Piece
forall b c a. (b -> c) -> (a -> b) -> a -> c
. MoveType -> Maybe Rank
forall a. Promotable a => a -> Maybe Rank
Attribute.Rank.getMaybePromotionRank (MoveType -> Piece)
-> (MoveType -> (Maybe Piece, Bool))
-> MoveType
-> (Piece, (Maybe Piece, Bool))
forall (a :: * -> * -> *) b c c'.
Arrow a =>
a b c -> a b c' -> a b (c, c')
&&& ((Rank -> Piece) -> Maybe Rank -> Maybe Piece
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (LogicalColour -> Rank -> Piece
Component.Piece.mkPiece LogicalColour
opponentsLogicalColour) (Maybe Rank -> Maybe Piece)
-> (Maybe Rank -> Bool) -> Maybe Rank -> (Maybe Piece, Bool)
forall (a :: * -> * -> *) b c c'.
Arrow a =>
a b c -> a b c' -> a b (c, c')
&&& (Maybe Rank -> Maybe Rank -> Bool
forall a. Eq a => a -> a -> Bool
== Rank -> Maybe Rank
forall a. a -> Maybe a
Just Rank
Attribute.Rank.Pawn)) (Maybe Rank -> (Maybe Piece, Bool))
-> (MoveType -> Maybe Rank) -> MoveType -> (Maybe Piece, Bool)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. MoveType -> Maybe Rank
Attribute.MoveType.getMaybeExplicitlyTakenRank (MoveType -> (Piece, (Maybe Piece, Bool)))
-> MoveType -> (Piece, (Maybe Piece, Bool))
forall a b. (a -> b) -> a -> b
$! MoveType
moveType

		movePiece' :: StateProperty.Mutator.Mutator mutator => mutator -> mutator
		movePiece' :: mutator -> mutator
movePiece'	= Move -> MoveType -> Piece -> mutator -> mutator
forall mutator.
Mutator mutator =>
Move -> MoveType -> Piece -> mutator -> mutator
StateProperty.Mutator.movePiece Move
move MoveType
moveType Piece
sourcePiece

		enpassantCaptureCoordinates :: Cartesian.Coordinates.Coordinates
		enpassantCaptureCoordinates :: Coordinates
enpassantCaptureCoordinates	= LogicalColour -> Transformation
Cartesian.Coordinates.retreat LogicalColour
logicalColour Coordinates
destination	-- CAVEAT: only valid when the move-type is En-passant.

		board' :: Board
board'@MkBoard { getMaybePieceByCoordinates :: Board -> MaybePieceByCoordinates
getMaybePieceByCoordinates = MaybePieceByCoordinates
maybePieceByCoordinates' }	= MkBoard :: MaybePieceByCoordinates
-> CoordinatesByRankByLogicalColour
-> NDefendersByCoordinatesByLogicalColour
-> NPiecesByRank
-> NPiecesByFileByLogicalColour
-> NPieces
-> CoordinatesByLogicalColour
-> Board
MkBoard {
			getMaybePieceByCoordinates :: MaybePieceByCoordinates
getMaybePieceByCoordinates			= MaybePieceByCoordinates -> MaybePieceByCoordinates
forall mutator. Mutator mutator => mutator -> mutator
movePiece' MaybePieceByCoordinates
maybePieceByCoordinates,
			getCoordinatesByRankByLogicalColour :: CoordinatesByRankByLogicalColour
getCoordinatesByRankByLogicalColour		= CoordinatesByRankByLogicalColour
-> CoordinatesByRankByLogicalColour
forall mutator. Mutator mutator => mutator -> mutator
movePiece' CoordinatesByRankByLogicalColour
coordinatesByRankByLogicalColour,
			getNDefendersByCoordinatesByLogicalColour :: NDefendersByCoordinatesByLogicalColour
getNDefendersByCoordinatesByLogicalColour	= (
				\(Map Coordinates NPieces
nBlackDefendersByCoordinates, Map Coordinates NPieces
nWhiteDefendersByCoordinates)	-> [Map Coordinates NPieces] -> NDefendersByCoordinatesByLogicalColour
forall (a :: * -> * -> *) e. IArray a e => [e] -> a LogicalColour e
Colour.LogicalColour.listArrayByLogicalColour [Map Coordinates NPieces
nBlackDefendersByCoordinates, Map Coordinates NPieces
nWhiteDefendersByCoordinates]
			) ((Map Coordinates NPieces, Map Coordinates NPieces)
 -> NDefendersByCoordinatesByLogicalColour)
-> ([LocatedPiece]
    -> (Map Coordinates NPieces, Map Coordinates NPieces))
-> [LocatedPiece]
-> NDefendersByCoordinatesByLogicalColour
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (LocatedPiece
 -> (Map Coordinates NPieces, Map Coordinates NPieces)
 -> (Map Coordinates NPieces, Map Coordinates NPieces))
-> (Map Coordinates NPieces, Map Coordinates NPieces)
-> [LocatedPiece]
-> (Map Coordinates NPieces, Map Coordinates NPieces)
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr (
				\(Coordinates
affectedCoordinates, Piece
affectedPiece) -> if Piece -> Bool
Component.Piece.isKing Piece
affectedPiece
					then (Map Coordinates NPieces, Map Coordinates NPieces)
-> (Map Coordinates NPieces, Map Coordinates NPieces)
forall a. a -> a
id	-- N.B.: defence of the King is irrelevant, since one can't get to a position where it can be taken.
					else let
						logicalColour' :: LogicalColour
logicalColour'	= Piece -> LogicalColour
Component.Piece.getLogicalColour Piece
affectedPiece
					in (
						if LogicalColour -> Bool
Colour.LogicalColour.isBlack LogicalColour
logicalColour'
							then (Map Coordinates NPieces -> Map Coordinates NPieces)
-> (Map Coordinates NPieces, Map Coordinates NPieces)
-> (Map Coordinates NPieces, Map Coordinates NPieces)
forall (a :: * -> * -> *) b c d.
Arrow a =>
a b c -> a (b, d) (c, d)
Control.Arrow.first
							else (Map Coordinates NPieces -> Map Coordinates NPieces)
-> (Map Coordinates NPieces, Map Coordinates NPieces)
-> (Map Coordinates NPieces, Map Coordinates NPieces)
forall (a :: * -> * -> *) b c d.
Arrow a =>
a b c -> a (d, b) (d, c)
Control.Arrow.second
					) ((Map Coordinates NPieces -> Map Coordinates NPieces)
 -> (Map Coordinates NPieces, Map Coordinates NPieces)
 -> (Map Coordinates NPieces, Map Coordinates NPieces))
-> ([(Coordinates, Rank)]
    -> Map Coordinates NPieces -> Map Coordinates NPieces)
-> [(Coordinates, Rank)]
-> (Map Coordinates NPieces, Map Coordinates NPieces)
-> (Map Coordinates NPieces, Map Coordinates NPieces)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Coordinates
-> NPieces -> Map Coordinates NPieces -> Map Coordinates NPieces
forall k a. Ord k => k -> a -> Map k a -> Map k a
Map.insert Coordinates
affectedCoordinates {-overwrite-} (NPieces -> Map Coordinates NPieces -> Map Coordinates NPieces)
-> ([(Coordinates, Rank)] -> NPieces)
-> [(Coordinates, Rank)]
-> Map Coordinates NPieces
-> Map Coordinates NPieces
forall b c a. (b -> c) -> (a -> b) -> a -> c
. NPieces -> NPieces
forall a b. (Integral a, Num b) => a -> b
fromIntegral (NPieces -> NPieces)
-> ([(Coordinates, Rank)] -> NPieces)
-> [(Coordinates, Rank)]
-> NPieces
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [(Coordinates, Rank)] -> NPieces
forall (t :: * -> *) a. Foldable t => t a -> NPieces
length ([(Coordinates, Rank)]
 -> (Map Coordinates NPieces, Map Coordinates NPieces)
 -> (Map Coordinates NPieces, Map Coordinates NPieces))
-> [(Coordinates, Rank)]
-> (Map Coordinates NPieces, Map Coordinates NPieces)
-> (Map Coordinates NPieces, Map Coordinates NPieces)
forall a b. (a -> b) -> a -> b
$! Board -> LogicalColour -> Coordinates -> [(Coordinates, Rank)]
findAttackersOf Board
board' (
						LogicalColour -> LogicalColour
forall a. Opposable a => a -> a
Property.Opposable.getOpposite LogicalColour
logicalColour'	-- Investigate an attack on the affected coordinates by the affected piece's own logical colour, i.e. defence.
					) Coordinates
affectedCoordinates
			) (
				(NDefendersByCoordinatesByLogicalColour
-> LogicalColour -> Map Coordinates NPieces
forall (a :: * -> * -> *) e i.
(IArray a e, Ix i) =>
a i e -> i -> e
! LogicalColour
Colour.LogicalColour.Black) (NDefendersByCoordinatesByLogicalColour -> Map Coordinates NPieces)
-> (NDefendersByCoordinatesByLogicalColour
    -> Map Coordinates NPieces)
-> NDefendersByCoordinatesByLogicalColour
-> (Map Coordinates NPieces, Map Coordinates NPieces)
forall (a :: * -> * -> *) b c c'.
Arrow a =>
a b c -> a b c' -> a b (c, c')
&&& (NDefendersByCoordinatesByLogicalColour
-> LogicalColour -> Map Coordinates NPieces
forall (a :: * -> * -> *) e i.
(IArray a e, Ix i) =>
a i e -> i -> e
! LogicalColour
Colour.LogicalColour.White) (NDefendersByCoordinatesByLogicalColour
 -> (Map Coordinates NPieces, Map Coordinates NPieces))
-> NDefendersByCoordinatesByLogicalColour
-> (Map Coordinates NPieces, Map Coordinates NPieces)
forall a b. (a -> b) -> a -> b
$ NDefendersByCoordinatesByLogicalColour
nDefendersByCoordinatesByLogicalColour NDefendersByCoordinatesByLogicalColour
-> [(LogicalColour, Map Coordinates NPieces)]
-> NDefendersByCoordinatesByLogicalColour
forall (a :: * -> * -> *) e i.
(IArray a e, Ix i) =>
a i e -> [(i, e)] -> a i e
// (
					let
						nDefendersByCoordinates :: Map Coordinates NPieces
nDefendersByCoordinates	= NDefendersByCoordinatesByLogicalColour
nDefendersByCoordinatesByLogicalColour NDefendersByCoordinatesByLogicalColour
-> LogicalColour -> Map Coordinates NPieces
forall (a :: * -> * -> *) e i.
(IArray a e, Ix i) =>
a i e -> i -> e
! LogicalColour
opponentsLogicalColour
					in (Bool
 -> [(LogicalColour, Map Coordinates NPieces)]
 -> [(LogicalColour, Map Coordinates NPieces)],
 [(LogicalColour, Map Coordinates NPieces)]
 -> [(LogicalColour, Map Coordinates NPieces)],
 (Maybe Rank, Maybe Rank)
 -> [(LogicalColour, Map Coordinates NPieces)]
 -> [(LogicalColour, Map Coordinates NPieces)])
-> MoveType
-> [(LogicalColour, Map Coordinates NPieces)]
-> [(LogicalColour, Map Coordinates NPieces)]
forall a.
(Bool -> a, a, (Maybe Rank, Maybe Rank) -> a) -> MoveType -> a
Attribute.MoveType.apply (
						([(LogicalColour, Map Coordinates NPieces)]
 -> [(LogicalColour, Map Coordinates NPieces)])
-> Bool
-> [(LogicalColour, Map Coordinates NPieces)]
-> [(LogicalColour, Map Coordinates NPieces)]
forall a b. a -> b -> a
const [(LogicalColour, Map Coordinates NPieces)]
-> [(LogicalColour, Map Coordinates NPieces)]
forall a. a -> a
id,	-- Castle.
						(:) ((LogicalColour, Map Coordinates NPieces)
 -> [(LogicalColour, Map Coordinates NPieces)]
 -> [(LogicalColour, Map Coordinates NPieces)])
-> (Map Coordinates NPieces
    -> (LogicalColour, Map Coordinates NPieces))
-> Map Coordinates NPieces
-> [(LogicalColour, Map Coordinates NPieces)]
-> [(LogicalColour, Map Coordinates NPieces)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (,) LogicalColour
opponentsLogicalColour (Map Coordinates NPieces
 -> [(LogicalColour, Map Coordinates NPieces)]
 -> [(LogicalColour, Map Coordinates NPieces)])
-> Map Coordinates NPieces
-> [(LogicalColour, Map Coordinates NPieces)]
-> [(LogicalColour, Map Coordinates NPieces)]
forall a b. (a -> b) -> a -> b
$ Coordinates -> Map Coordinates NPieces -> Map Coordinates NPieces
forall k a. Ord k => k -> Map k a -> Map k a
Map.delete Coordinates
enpassantCaptureCoordinates Map Coordinates NPieces
nDefendersByCoordinates,	-- Pawn taken En-passant.
						\case
							(Just Rank
_, Maybe Rank
_)	-> (:) ((LogicalColour, Map Coordinates NPieces)
 -> [(LogicalColour, Map Coordinates NPieces)]
 -> [(LogicalColour, Map Coordinates NPieces)])
-> (Map Coordinates NPieces
    -> (LogicalColour, Map Coordinates NPieces))
-> Map Coordinates NPieces
-> [(LogicalColour, Map Coordinates NPieces)]
-> [(LogicalColour, Map Coordinates NPieces)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (,) LogicalColour
opponentsLogicalColour (Map Coordinates NPieces
 -> [(LogicalColour, Map Coordinates NPieces)]
 -> [(LogicalColour, Map Coordinates NPieces)])
-> Map Coordinates NPieces
-> [(LogicalColour, Map Coordinates NPieces)]
-> [(LogicalColour, Map Coordinates NPieces)]
forall a b. (a -> b) -> a -> b
$ Coordinates -> Map Coordinates NPieces -> Map Coordinates NPieces
forall k a. Ord k => k -> Map k a -> Map k a
Map.delete Coordinates
destination Map Coordinates NPieces
nDefendersByCoordinates	-- Piece has been taken.
							(Maybe Rank, Maybe Rank)
_		-> [(LogicalColour, Map Coordinates NPieces)]
-> [(LogicalColour, Map Coordinates NPieces)]
forall a. a -> a
id
					) MoveType
moveType
				 ) [
					LogicalColour -> LogicalColour
forall a. a -> a
id (LogicalColour -> LogicalColour)
-> (LogicalColour -> Map Coordinates NPieces)
-> LogicalColour
-> (LogicalColour, Map Coordinates NPieces)
forall (a :: * -> * -> *) b c c'.
Arrow a =>
a b c -> a b c' -> a b (c, c')
&&& Coordinates -> Map Coordinates NPieces -> Map Coordinates NPieces
forall k a. Ord k => k -> Map k a -> Map k a
Map.delete Coordinates
source (Map Coordinates NPieces -> Map Coordinates NPieces)
-> (LogicalColour -> Map Coordinates NPieces)
-> LogicalColour
-> Map Coordinates NPieces
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (NDefendersByCoordinatesByLogicalColour
nDefendersByCoordinatesByLogicalColour NDefendersByCoordinatesByLogicalColour
-> LogicalColour -> Map Coordinates NPieces
forall (a :: * -> * -> *) e i.
(IArray a e, Ix i) =>
a i e -> i -> e
!) (LogicalColour -> (LogicalColour, Map Coordinates NPieces))
-> LogicalColour -> (LogicalColour, Map Coordinates NPieces)
forall a b. (a -> b) -> a -> b
$ LogicalColour
logicalColour	-- This piece has been moved.
				 ] -- Singleton.
			) ([LocatedPiece]
 -> (Map Coordinates NPieces, Map Coordinates NPieces))
-> ([LocatedPiece] -> [LocatedPiece])
-> [LocatedPiece]
-> (Map Coordinates NPieces, Map Coordinates NPieces)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (LocatedPiece -> LocatedPiece -> Bool)
-> [LocatedPiece] -> [LocatedPiece]
forall a. (a -> a -> Bool) -> [a] -> [a]
Data.List.nubBy (
				\LocatedPiece
l LocatedPiece
r -> LocatedPiece -> Coordinates
forall a b. (a, b) -> a
fst LocatedPiece
l Coordinates -> Coordinates -> Bool
forall a. Eq a => a -> a -> Bool
== LocatedPiece -> Coordinates
forall a b. (a, b) -> a
fst LocatedPiece
r	-- Compare coordinates.
			) ([LocatedPiece] -> NDefendersByCoordinatesByLogicalColour)
-> [LocatedPiece] -> NDefendersByCoordinatesByLogicalColour
forall a b. (a -> b) -> a -> b
$ [
				(Coordinates
affectedCoordinates, Piece
affectedPiece) |
					(Coordinates
knightsCoordinates, Piece
knight)	<- (Coordinates
source, Piece
sourcePiece) LocatedPiece -> [LocatedPiece] -> [LocatedPiece]
forall a. a -> [a] -> [a]
: (,) Coordinates
destination (Piece -> LocatedPiece) -> [Piece] -> [LocatedPiece]
forall a b. (a -> b) -> [a] -> [b]
`map` (
						Piece
destinationPiece Piece -> [Piece] -> [Piece]
forall a. a -> [a] -> [a]
: Maybe Piece -> [Piece]
forall a. Maybe a -> [a]
Data.Maybe.maybeToList Maybe Piece
maybeExplicitlyTakenPiece
					),
					Piece -> Bool
Component.Piece.isKnight Piece
knight,
					Coordinates
affectedCoordinates		<- (Vector -> Maybe Coordinates) -> [Vector] -> [Coordinates]
forall a b. (a -> Maybe b) -> [a] -> [b]
Data.Maybe.mapMaybe (Vector -> Coordinates -> Maybe Coordinates
`Cartesian.Vector.maybeTranslate` Coordinates
knightsCoordinates) [Vector]
Cartesian.Vector.attackVectorsForKnight,
					Piece
affectedPiece			<- Maybe Piece -> [Piece]
forall a. Maybe a -> [a]
Data.Maybe.maybeToList (Maybe Piece -> [Piece]) -> Maybe Piece -> [Piece]
forall a b. (a -> b) -> a -> b
$! MaybePieceByCoordinates -> Coordinates -> Maybe Piece
State.MaybePieceByCoordinates.dereference MaybePieceByCoordinates
maybePieceByCoordinates' Coordinates
affectedCoordinates,
					Piece -> Piece -> Bool
Component.Piece.isFriend Piece
knight Piece
affectedPiece
			] {-list-comprehension-} [LocatedPiece] -> [LocatedPiece] -> [LocatedPiece]
forall a. [a] -> [a] -> [a]
++ [
				(Coordinates
blockingCoordinates, Piece
blockingPiece) |
					Coordinates
passingPawnsDestination			<- [Coordinates
enpassantCaptureCoordinates | MoveType -> Bool
Attribute.MoveType.isEnPassant MoveType
moveType],
					(Direction
direction, Direction
antiParallelDirection)	<- [(Direction, Direction)]
Direction.Direction.opposites,
					(Coordinates
blockingCoordinates, Piece
blockingPiece)	<- case MaybePieceByCoordinates
-> Coordinates -> Maybe [Direction] -> [LocatedPiece]
State.MaybePieceByCoordinates.findBlockingPieces MaybePieceByCoordinates
maybePieceByCoordinates' Coordinates
passingPawnsDestination (Maybe [Direction] -> [LocatedPiece])
-> Maybe [Direction] -> [LocatedPiece]
forall a b. (a -> b) -> a -> b
$ [Direction] -> Maybe [Direction]
forall a. a -> Maybe a
Just [Direction
direction, Direction
antiParallelDirection] of
						[LocatedPiece
cp, LocatedPiece
cp'] -> [
							LocatedPiece
cp | (Bool -> Bool -> Bool) -> (Bool, Bool) -> Bool
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry Bool -> Bool -> Bool
(||) ((Bool, Bool) -> Bool)
-> ((LocatedPiece -> Bool) -> (Bool, Bool))
-> (LocatedPiece -> Bool)
-> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (
								((LocatedPiece -> Bool) -> LocatedPiece -> Bool
forall a b. (a -> b) -> a -> b
$ (Coordinates
passingPawnsDestination, Piece
oppositePiece)) ((LocatedPiece -> Bool) -> Bool)
-> ((LocatedPiece -> Bool) -> Bool)
-> (LocatedPiece -> Bool)
-> (Bool, Bool)
forall (a :: * -> * -> *) b c c'.
Arrow a =>
a b c -> a b c' -> a b (c, c')
&&& ((LocatedPiece -> Bool) -> LocatedPiece -> Bool
forall a b. (a -> b) -> a -> b
$ LocatedPiece
cp')
							) ((LocatedPiece -> Bool) -> Bool) -> (LocatedPiece -> Bool) -> Bool
forall a b. (a -> b) -> a -> b
$ \(Coordinates
from, Piece
piece) -> (Bool -> Bool -> Bool) -> (Bool, Bool) -> Bool
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry Bool -> Bool -> Bool
(&&) ((Bool, Bool) -> Bool) -> (Bool, Bool) -> Bool
forall a b. (a -> b) -> a -> b
$ ((Piece -> Bool) -> (Piece -> Bool) -> Piece -> (Bool, Bool))
-> (Piece -> Bool, Piece -> Bool) -> Piece -> (Bool, Bool)
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry (Piece -> Bool) -> (Piece -> Bool) -> Piece -> (Bool, Bool)
forall (a :: * -> * -> *) b c c'.
Arrow a =>
a b c -> a b c' -> a b (c, c')
(&&&) (
								Coordinates -> Coordinates -> Piece -> Bool
Component.Piece.canAttackAlong Coordinates
from (Coordinates -> Piece -> Bool)
-> (Piece -> Piece -> Bool)
-> LocatedPiece
-> (Piece -> Bool, Piece -> Bool)
forall (a :: * -> * -> *) b c b' c'.
Arrow a =>
a b c -> a b' c' -> a (b, b') (c, c')
*** Piece -> Piece -> Bool
Component.Piece.isFriend (LocatedPiece -> (Piece -> Bool, Piece -> Bool))
-> LocatedPiece -> (Piece -> Bool, Piece -> Bool)
forall a b. (a -> b) -> a -> b
$ LocatedPiece
cp
							) Piece
piece
						 ] {-list-comprehension-} [LocatedPiece] -> [LocatedPiece] -> [LocatedPiece]
forall a. [a] -> [a] -> [a]
++ [
							LocatedPiece
cp' | (Bool -> Bool -> Bool) -> (Bool, Bool) -> Bool
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry Bool -> Bool -> Bool
(||) ((Bool, Bool) -> Bool)
-> ((LocatedPiece -> Bool) -> (Bool, Bool))
-> (LocatedPiece -> Bool)
-> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (
								((LocatedPiece -> Bool) -> LocatedPiece -> Bool
forall a b. (a -> b) -> a -> b
$ (Coordinates
passingPawnsDestination, Piece
oppositePiece)) ((LocatedPiece -> Bool) -> Bool)
-> ((LocatedPiece -> Bool) -> Bool)
-> (LocatedPiece -> Bool)
-> (Bool, Bool)
forall (a :: * -> * -> *) b c c'.
Arrow a =>
a b c -> a b c' -> a b (c, c')
&&& ((LocatedPiece -> Bool) -> LocatedPiece -> Bool
forall a b. (a -> b) -> a -> b
$ LocatedPiece
cp)
							) ((LocatedPiece -> Bool) -> Bool) -> (LocatedPiece -> Bool) -> Bool
forall a b. (a -> b) -> a -> b
$ \(Coordinates
from, Piece
piece) -> (Bool -> Bool -> Bool) -> (Bool, Bool) -> Bool
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry Bool -> Bool -> Bool
(&&) ((Bool, Bool) -> Bool) -> (Bool, Bool) -> Bool
forall a b. (a -> b) -> a -> b
$ ((Piece -> Bool) -> (Piece -> Bool) -> Piece -> (Bool, Bool))
-> (Piece -> Bool, Piece -> Bool) -> Piece -> (Bool, Bool)
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry (Piece -> Bool) -> (Piece -> Bool) -> Piece -> (Bool, Bool)
forall (a :: * -> * -> *) b c c'.
Arrow a =>
a b c -> a b c' -> a b (c, c')
(&&&) (
								Coordinates -> Coordinates -> Piece -> Bool
Component.Piece.canAttackAlong Coordinates
from (Coordinates -> Piece -> Bool)
-> (Piece -> Piece -> Bool)
-> LocatedPiece
-> (Piece -> Bool, Piece -> Bool)
forall (a :: * -> * -> *) b c b' c'.
Arrow a =>
a b c -> a b' c' -> a (b, b') (c, c')
*** Piece -> Piece -> Bool
Component.Piece.isFriend (LocatedPiece -> (Piece -> Bool, Piece -> Bool))
-> LocatedPiece -> (Piece -> Bool, Piece -> Bool)
forall a b. (a -> b) -> a -> b
$ LocatedPiece
cp'
							) Piece
piece
						 ] -- List-comprehension.
						[LocatedPiece]
locatedPieces	-> (LocatedPiece -> Bool) -> [LocatedPiece] -> [LocatedPiece]
forall a. (a -> Bool) -> [a] -> [a]
filter (
							\LocatedPiece
locatedPiece -> (Bool -> Bool -> Bool) -> (Bool, Bool) -> Bool
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry Bool -> Bool -> Bool
(&&) ((Bool, Bool) -> Bool) -> (Bool, Bool) -> Bool
forall a b. (a -> b) -> a -> b
$ ((Piece -> Bool) -> (Piece -> Bool) -> Piece -> (Bool, Bool))
-> (Piece -> Bool, Piece -> Bool) -> Piece -> (Bool, Bool)
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry (Piece -> Bool) -> (Piece -> Bool) -> Piece -> (Bool, Bool)
forall (a :: * -> * -> *) b c c'.
Arrow a =>
a b c -> a b c' -> a b (c, c')
(&&&) (
								Coordinates -> Coordinates -> Piece -> Bool
Component.Piece.canAttackAlong Coordinates
passingPawnsDestination (Coordinates -> Piece -> Bool)
-> (Piece -> Piece -> Bool)
-> LocatedPiece
-> (Piece -> Bool, Piece -> Bool)
forall (a :: * -> * -> *) b c b' c'.
Arrow a =>
a b c -> a b' c' -> a (b, b') (c, c')
*** Piece -> Piece -> Bool
Component.Piece.isFriend (LocatedPiece -> (Piece -> Bool, Piece -> Bool))
-> LocatedPiece -> (Piece -> Bool, Piece -> Bool)
forall a b. (a -> b) -> a -> b
$ LocatedPiece
locatedPiece
							) Piece
oppositePiece
						 ) [LocatedPiece]
locatedPieces
			] {-list-comprehension-} [LocatedPiece] -> [LocatedPiece] -> [LocatedPiece]
forall a. [a] -> [a] -> [a]
++ (Coordinates
destination, Piece
destinationPiece) LocatedPiece -> [LocatedPiece] -> [LocatedPiece]
forall a. a -> [a] -> [a]
: [
				(Coordinates
blockingCoordinates, Piece
blockingPiece) |
					(Direction
direction, Direction
antiParallelDirection)	<- [(Direction, Direction)]
Direction.Direction.opposites,
					(Coordinates
coordinates, Piece
piece)			<- [(Coordinates
source, Piece
sourcePiece), (Coordinates
destination, Piece
destinationPiece)],
					(Coordinates
blockingCoordinates, Piece
blockingPiece)	<- case MaybePieceByCoordinates
-> Coordinates -> Maybe [Direction] -> [LocatedPiece]
State.MaybePieceByCoordinates.findBlockingPieces MaybePieceByCoordinates
maybePieceByCoordinates' Coordinates
coordinates (Maybe [Direction] -> [LocatedPiece])
-> Maybe [Direction] -> [LocatedPiece]
forall a b. (a -> b) -> a -> b
$ [Direction] -> Maybe [Direction]
forall a. a -> Maybe a
Just [Direction
direction, Direction
antiParallelDirection] of
						[LocatedPiece
cp, LocatedPiece
cp']	-> [
							LocatedPiece
cp |
								let isDefendedBy :: Coordinates -> Piece -> Bool
isDefendedBy Coordinates
from	= (Bool -> Bool -> Bool) -> (Bool, Bool) -> Bool
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry Bool -> Bool -> Bool
(&&) ((Bool, Bool) -> Bool) -> (Piece -> (Bool, Bool)) -> Piece -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((Piece -> Bool) -> (Piece -> Bool) -> Piece -> (Bool, Bool))
-> (Piece -> Bool, Piece -> Bool) -> Piece -> (Bool, Bool)
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry (Piece -> Bool) -> (Piece -> Bool) -> Piece -> (Bool, Bool)
forall (a :: * -> * -> *) b c c'.
Arrow a =>
a b c -> a b c' -> a b (c, c')
(&&&) (Coordinates -> Coordinates -> Piece -> Bool
Component.Piece.canAttackAlong Coordinates
from (Coordinates -> Piece -> Bool)
-> (Piece -> Piece -> Bool)
-> LocatedPiece
-> (Piece -> Bool, Piece -> Bool)
forall (a :: * -> * -> *) b c b' c'.
Arrow a =>
a b c -> a b' c' -> a (b, b') (c, c')
*** Piece -> Piece -> Bool
Component.Piece.isFriend (LocatedPiece -> (Piece -> Bool, Piece -> Bool))
-> LocatedPiece -> (Piece -> Bool, Piece -> Bool)
forall a b. (a -> b) -> a -> b
$ LocatedPiece
cp),
								Coordinates -> Piece -> Bool
isDefendedBy Coordinates
coordinates Piece
piece Bool -> Bool -> Bool
|| Coordinates
coordinates Coordinates -> Coordinates -> Bool
forall a. Eq a => a -> a -> Bool
== Coordinates
destination Bool -> Bool -> Bool
&& Bool -> (Piece -> Bool) -> Maybe Piece -> Bool
forall b a. b -> (a -> b) -> Maybe a -> b
Data.Maybe.maybe Bool
False (Coordinates -> Piece -> Bool
isDefendedBy Coordinates
destination) Maybe Piece
maybeExplicitlyTakenPiece Bool -> Bool -> Bool
|| (Coordinates -> Piece -> Bool) -> LocatedPiece -> Bool
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry Coordinates -> Piece -> Bool
isDefendedBy LocatedPiece
cp'
						 ] {-list-comprehension-} [LocatedPiece] -> [LocatedPiece] -> [LocatedPiece]
forall a. [a] -> [a] -> [a]
++ [
							LocatedPiece
cp' |
								let isDefendedBy :: Coordinates -> Piece -> Bool
isDefendedBy Coordinates
from	= (Bool -> Bool -> Bool) -> (Bool, Bool) -> Bool
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry Bool -> Bool -> Bool
(&&) ((Bool, Bool) -> Bool) -> (Piece -> (Bool, Bool)) -> Piece -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((Piece -> Bool) -> (Piece -> Bool) -> Piece -> (Bool, Bool))
-> (Piece -> Bool, Piece -> Bool) -> Piece -> (Bool, Bool)
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry (Piece -> Bool) -> (Piece -> Bool) -> Piece -> (Bool, Bool)
forall (a :: * -> * -> *) b c c'.
Arrow a =>
a b c -> a b c' -> a b (c, c')
(&&&) (Coordinates -> Coordinates -> Piece -> Bool
Component.Piece.canAttackAlong Coordinates
from (Coordinates -> Piece -> Bool)
-> (Piece -> Piece -> Bool)
-> LocatedPiece
-> (Piece -> Bool, Piece -> Bool)
forall (a :: * -> * -> *) b c b' c'.
Arrow a =>
a b c -> a b' c' -> a (b, b') (c, c')
*** Piece -> Piece -> Bool
Component.Piece.isFriend (LocatedPiece -> (Piece -> Bool, Piece -> Bool))
-> LocatedPiece -> (Piece -> Bool, Piece -> Bool)
forall a b. (a -> b) -> a -> b
$ LocatedPiece
cp'),
								Coordinates -> Piece -> Bool
isDefendedBy Coordinates
coordinates Piece
piece Bool -> Bool -> Bool
|| Coordinates
coordinates Coordinates -> Coordinates -> Bool
forall a. Eq a => a -> a -> Bool
== Coordinates
destination Bool -> Bool -> Bool
&& Bool -> (Piece -> Bool) -> Maybe Piece -> Bool
forall b a. b -> (a -> b) -> Maybe a -> b
Data.Maybe.maybe Bool
False (Coordinates -> Piece -> Bool
isDefendedBy Coordinates
destination) Maybe Piece
maybeExplicitlyTakenPiece Bool -> Bool -> Bool
|| (Coordinates -> Piece -> Bool) -> LocatedPiece -> Bool
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry Coordinates -> Piece -> Bool
isDefendedBy LocatedPiece
cp
						 ] -- List-comprehension.
						[LocatedPiece]
locatedPieces	-> (LocatedPiece -> Bool) -> [LocatedPiece] -> [LocatedPiece]
forall a. (a -> Bool) -> [a] -> [a]
filter (
							\LocatedPiece
locatedPiece -> let
								isDefendedBy :: Piece -> Bool
isDefendedBy	= (Bool -> Bool -> Bool) -> (Bool, Bool) -> Bool
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry Bool -> Bool -> Bool
(&&) ((Bool, Bool) -> Bool) -> (Piece -> (Bool, Bool)) -> Piece -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((Piece -> Bool) -> (Piece -> Bool) -> Piece -> (Bool, Bool))
-> (Piece -> Bool, Piece -> Bool) -> Piece -> (Bool, Bool)
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry (Piece -> Bool) -> (Piece -> Bool) -> Piece -> (Bool, Bool)
forall (a :: * -> * -> *) b c c'.
Arrow a =>
a b c -> a b c' -> a b (c, c')
(&&&) (Coordinates -> Coordinates -> Piece -> Bool
Component.Piece.canAttackAlong Coordinates
coordinates (Coordinates -> Piece -> Bool)
-> (Piece -> Piece -> Bool)
-> LocatedPiece
-> (Piece -> Bool, Piece -> Bool)
forall (a :: * -> * -> *) b c b' c'.
Arrow a =>
a b c -> a b' c' -> a (b, b') (c, c')
*** Piece -> Piece -> Bool
Component.Piece.isFriend (LocatedPiece -> (Piece -> Bool, Piece -> Bool))
-> LocatedPiece -> (Piece -> Bool, Piece -> Bool)
forall a b. (a -> b) -> a -> b
$ LocatedPiece
locatedPiece)
							in Piece -> Bool
isDefendedBy Piece
piece Bool -> Bool -> Bool
|| Coordinates
coordinates Coordinates -> Coordinates -> Bool
forall a. Eq a => a -> a -> Bool
== Coordinates
destination Bool -> Bool -> Bool
&& Bool -> (Piece -> Bool) -> Maybe Piece -> Bool
forall b a. b -> (a -> b) -> Maybe a -> b
Data.Maybe.maybe Bool
False Piece -> Bool
isDefendedBy Maybe Piece
maybeExplicitlyTakenPiece
						 ) [LocatedPiece]
locatedPieces
			], -- List-comprehension. Define any pieces whose defence may be affected by the move.
			getNPiecesDifferenceByRank :: NPiecesByRank
getNPiecesDifferenceByRank	= (NPieces -> NPieces -> NPieces)
-> NPiecesByRank -> [(Rank, NPieces)] -> NPiecesByRank
forall (a :: * -> * -> *) e i e'.
(IArray a e, Ix i) =>
(e -> e' -> e) -> a i e -> [(i, e')] -> a i e
Data.Array.IArray.accum (
				if LogicalColour -> Bool
Colour.LogicalColour.isBlack LogicalColour
logicalColour
					then (-)	-- Since White pieces are arbitrarily counted as positive, negate the adjustment if the current player is Black.
					else NPieces -> NPieces -> NPieces
forall a. Num a => a -> a -> a
(+)
			) NPiecesByRank
nPiecesDifferenceByRank ([(Rank, NPieces)] -> NPiecesByRank)
-> [(Rank, NPieces)] -> NPiecesByRank
forall a b. (a -> b) -> a -> b
$ (Bool -> [(Rank, NPieces)], [(Rank, NPieces)],
 (Maybe Rank, Maybe Rank) -> [(Rank, NPieces)])
-> MoveType -> [(Rank, NPieces)]
forall a.
(Bool -> a, a, (Maybe Rank, Maybe Rank) -> a) -> MoveType -> a
Attribute.MoveType.apply (
				[(Rank, NPieces)] -> Bool -> [(Rank, NPieces)]
forall a b. a -> b -> a
const [],			-- Castle.
				[(Rank
Attribute.Rank.Pawn, NPieces
1)],	-- Enpassant => increment relative number of Pawns.
				(([(Rank, NPieces)] -> [(Rank, NPieces)])
 -> [(Rank, NPieces)] -> [(Rank, NPieces)])
-> ([(Rank, NPieces)] -> [(Rank, NPieces)], [(Rank, NPieces)])
-> [(Rank, NPieces)]
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry ([(Rank, NPieces)] -> [(Rank, NPieces)])
-> [(Rank, NPieces)] -> [(Rank, NPieces)]
forall a b. (a -> b) -> a -> b
($) (([(Rank, NPieces)] -> [(Rank, NPieces)], [(Rank, NPieces)])
 -> [(Rank, NPieces)])
-> ((Maybe Rank, Maybe Rank)
    -> ([(Rank, NPieces)] -> [(Rank, NPieces)], [(Rank, NPieces)]))
-> (Maybe Rank, Maybe Rank)
-> [(Rank, NPieces)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (
					([(Rank, NPieces)] -> [(Rank, NPieces)])
-> (Rank -> [(Rank, NPieces)] -> [(Rank, NPieces)])
-> Maybe Rank
-> [(Rank, NPieces)]
-> [(Rank, NPieces)]
forall b a. b -> (a -> b) -> Maybe a -> b
Data.Maybe.maybe [(Rank, NPieces)] -> [(Rank, NPieces)]
forall a. a -> a
id (
						(:) ((Rank, NPieces) -> [(Rank, NPieces)] -> [(Rank, NPieces)])
-> (Rank -> (Rank, NPieces))
-> Rank
-> [(Rank, NPieces)]
-> [(Rank, NPieces)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Rank -> NPieces -> (Rank, NPieces))
-> NPieces -> Rank -> (Rank, NPieces)
forall a b c. (a -> b -> c) -> b -> a -> c
flip (,) NPieces
1	-- Capture => Increment.
					) (Maybe Rank -> [(Rank, NPieces)] -> [(Rank, NPieces)])
-> (Maybe Rank -> [(Rank, NPieces)])
-> (Maybe Rank, Maybe Rank)
-> ([(Rank, NPieces)] -> [(Rank, NPieces)], [(Rank, NPieces)])
forall (a :: * -> * -> *) b c b' c'.
Arrow a =>
a b c -> a b' c' -> a (b, b') (c, c')
*** [(Rank, NPieces)]
-> (Rank -> [(Rank, NPieces)]) -> Maybe Rank -> [(Rank, NPieces)]
forall b a. b -> (a -> b) -> Maybe a -> b
Data.Maybe.maybe [] (
						\Rank
promotionRank -> [
							(
								Rank
promotionRank,
								NPieces
1		-- Increment.
							), (
								Rank
Attribute.Rank.Pawn,
								NPieces -> NPieces
forall a. Num a => a -> a
negate NPieces
1	-- Decrement relative number of Pawns.
							)
						]
					)
				) -- Normal.
			) MoveType
moveType,
			getNPawnsByFileByLogicalColour :: NPiecesByFileByLogicalColour
getNPawnsByFileByLogicalColour		= if Piece -> Bool
Component.Piece.isPawn Piece
sourcePiece Bool -> Bool -> Bool
&& Bool -> Bool
not (MoveType -> Bool
Attribute.MoveType.isQuiet MoveType
moveType) Bool -> Bool -> Bool
|| Bool
wasPawnTakenExplicitly
				then CoordinatesByRankByLogicalColour -> NPiecesByFileByLogicalColour
forall seeker.
Seeker seeker =>
seeker -> NPiecesByFileByLogicalColour
StateProperty.Seeker.countPawnsByFileByLogicalColour CoordinatesByRankByLogicalColour
coordinatesByRankByLogicalColour'	-- Recalculate.
				else Board -> NPiecesByFileByLogicalColour
getNPawnsByFileByLogicalColour Board
board,
			getNPieces :: NPieces
getNPieces				= MoveType -> NPieces -> NPieces
forall nPieces. Enum nPieces => MoveType -> nPieces -> nPieces
Attribute.MoveType.nPiecesMutator MoveType
moveType NPieces
nPieces,
			getPassedPawnCoordinatesByLogicalColour :: CoordinatesByLogicalColour
getPassedPawnCoordinatesByLogicalColour	= if Piece -> Bool
Component.Piece.isPawn Piece
sourcePiece Bool -> Bool -> Bool
|| Bool
wasPawnTakenExplicitly
				then CoordinatesByRankByLogicalColour -> CoordinatesByLogicalColour
State.CoordinatesByRankByLogicalColour.findPassedPawnCoordinatesByLogicalColour CoordinatesByRankByLogicalColour
coordinatesByRankByLogicalColour'	-- Recalculate.
				else Board -> CoordinatesByLogicalColour
getPassedPawnCoordinatesByLogicalColour Board
board
		}

		coordinatesByRankByLogicalColour' :: CoordinatesByRankByLogicalColour
coordinatesByRankByLogicalColour'	= Board -> CoordinatesByRankByLogicalColour
getCoordinatesByRankByLogicalColour Board
board'
	in Board
board'
	| Bool
otherwise	= Exception -> Board
forall a e. Exception e => e -> a
Control.Exception.throw (Exception -> Board) -> (String -> Exception) -> String -> Board
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Exception
Data.Exception.mkSearchFailure (String -> Exception) -> ShowS -> String -> Exception
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> ShowS
showString String
"BishBosh.State.Board.movePiece:\tno piece exists at " ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Coordinates -> ShowS
forall a. Show a => a -> ShowS
shows Coordinates
source ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> ShowS
showString String
"; " (String -> Board) -> String -> Board
forall a b. (a -> b) -> a -> b
$ Board -> ShowS
forall a. Show a => a -> ShowS
shows Board
board String
"."
	where
		(Coordinates
source, Coordinates
destination)	= Move -> Coordinates
Component.Move.getSource (Move -> Coordinates)
-> (Move -> Coordinates) -> Move -> (Coordinates, Coordinates)
forall (a :: * -> * -> *) b c c'.
Arrow a =>
a b c -> a b c' -> a b (c, c')
&&& Move -> Coordinates
Component.Move.getDestination (Move -> (Coordinates, Coordinates))
-> Move -> (Coordinates, Coordinates)
forall a b. (a -> b) -> a -> b
$! Move
move	-- Deconstruct.

-- | Calculate the total value of the /coordinates/ occupied by the /piece/s of either side, at a stage in the game's life-span defined by the total number of pieces remaining.
sumPieceSquareValueByLogicalColour
	:: Component.PieceSquareValueByCoordinatesByRank.PieceSquareValueByCoordinatesByRank
	-> Board
	-> [Type.Mass.Base]	-- ^ Individual sums for each logical colours, of the PieceSquareValues.
sumPieceSquareValueByLogicalColour :: PieceSquareValueByCoordinatesByRank -> Board -> [Base]
sumPieceSquareValueByLogicalColour PieceSquareValueByCoordinatesByRank
pieceSquareValueByCoordinatesByRank MkBoard {
	getCoordinatesByRankByLogicalColour :: Board -> CoordinatesByRankByLogicalColour
getCoordinatesByRankByLogicalColour	= CoordinatesByRankByLogicalColour
coordinatesByRankByLogicalColour,
	getNPieces :: Board -> NPieces
getNPieces				= NPieces
nPieces
} = PieceSquareValueByCoordinatesByRank
-> CoordinatesByRankByLogicalColour -> NPieces -> [Base]
forall accountant.
Accountant accountant =>
PieceSquareValueByCoordinatesByRank
-> accountant -> NPieces -> [Base]
Component.Accountant.sumPieceSquareValueByLogicalColour PieceSquareValueByCoordinatesByRank
pieceSquareValueByCoordinatesByRank CoordinatesByRankByLogicalColour
coordinatesByRankByLogicalColour NPieces
nPieces	-- Forward the request.

{- |
	* Lists the source-/coordinates/ from which the referenced destination can be attacked.

	* N.B.: the algorithm is independent of whose turn it actually is.

	* CAVEAT: checks neither the /logical colour/ of the defender, nor that their /piece/ even exists.

	* CAVEAT: may return the /coordinates/ of a diagonally adjacent @Pawn@; which would be an illegal move if there's not actually any /piece/ at the referenced destination.

	* CAVEAT: can't detect an en-passant attack, since this depends both on whether the previous move was a double advance & that the defender is a @Pawn@.
-}
findAttackersOf
	:: Board
	-> Colour.LogicalColour.LogicalColour				-- ^ The defender's /logical colour/.
	-> Cartesian.Coordinates.Coordinates				-- ^ The defender's location.
	-> [(Cartesian.Coordinates.Coordinates, Attribute.Rank.Rank)]	-- ^ The locations from which the specified square can be attacked by the opposite /logical colour/.
findAttackersOf :: Board -> LogicalColour -> Coordinates -> [(Coordinates, Rank)]
findAttackersOf board :: Board
board@MkBoard { getMaybePieceByCoordinates :: Board -> MaybePieceByCoordinates
getMaybePieceByCoordinates = MaybePieceByCoordinates
maybePieceByCoordinates } LogicalColour
destinationLogicalColour Coordinates
destination	= [
	(Coordinates
coordinates, Rank
Attribute.Rank.Knight) |
		Coordinates
coordinates	<- Board -> LogicalColour -> Coordinates -> [Coordinates]
forall seeker.
Seeker seeker =>
seeker -> LogicalColour -> Coordinates -> [Coordinates]
StateProperty.Seeker.findProximateKnights Board
board (LogicalColour -> LogicalColour
forall a. Opposable a => a -> a
Property.Opposable.getOpposite LogicalColour
destinationLogicalColour) Coordinates
destination
 ] {-list-comprehension-} [(Coordinates, Rank)]
-> [(Coordinates, Rank)] -> [(Coordinates, Rank)]
forall a. [a] -> [a] -> [a]
++ MaybePieceByCoordinates
-> LogicalColour
-> Coordinates
-> Maybe [Direction]
-> [(Coordinates, Rank)]
State.MaybePieceByCoordinates.findAttackerInDirections MaybePieceByCoordinates
maybePieceByCoordinates LogicalColour
destinationLogicalColour Coordinates
destination Maybe [Direction]
forall a. Maybe a
Nothing {-omni-directional-}

{- |
	* Lists the source-/coordinates/ from which the referenced destination can be attacked by the specified type of /piece/.

	* N.B.: similar to 'findAttackersOf', but can be more efficient since the attacking /piece/ is known.

	* CAVEAT: can't detect an en-passant attack, since this depends both on whether the previous move was a double advance & that the defender is a @Pawn@.
-}
findAttacksBy
	:: Board
	-> Component.Piece.Piece		-- ^ The type of attacker.
	-> Cartesian.Coordinates.Coordinates	-- ^ The defender's location.
	-> [Cartesian.Coordinates.Coordinates]	-- ^ The sources from which the specified attacker could strike.
findAttacksBy :: Board -> Piece -> Coordinates -> [Coordinates]
findAttacksBy Board
board Piece
piece Coordinates
destination
	| Rank
rank Rank -> Rank -> Bool
forall a. Eq a => a -> a -> Bool
== Rank
Attribute.Rank.Knight	= Board -> LogicalColour -> Coordinates -> [Coordinates]
forall seeker.
Seeker seeker =>
seeker -> LogicalColour -> Coordinates -> [Coordinates]
StateProperty.Seeker.findProximateKnights Board
board LogicalColour
logicalColour Coordinates
destination
	| Bool
otherwise			= (Coordinates -> Bool) -> [Coordinates] -> [Coordinates]
forall a. (a -> Bool) -> [a] -> [a]
filter (
		\Coordinates
source -> Coordinates
source Coordinates -> Coordinates -> Bool
forall a. Eq a => a -> a -> Bool
/= Coordinates
destination Bool -> Bool -> Bool
&& Coordinates -> Coordinates -> Piece -> Bool
Component.Piece.canAttackAlong Coordinates
source Coordinates
destination Piece
piece Bool -> Bool -> Bool
&& MaybePieceByCoordinates -> Coordinates -> Coordinates -> Bool
State.MaybePieceByCoordinates.isClear (Board -> MaybePieceByCoordinates
getMaybePieceByCoordinates Board
board) Coordinates
source Coordinates
destination
	) ([Coordinates] -> [Coordinates]) -> [Coordinates] -> [Coordinates]
forall a b. (a -> b) -> a -> b
$ CoordinatesByRankByLogicalColour
-> LogicalColour -> Rank -> [Coordinates]
State.CoordinatesByRankByLogicalColour.dereference (Board -> CoordinatesByRankByLogicalColour
getCoordinatesByRankByLogicalColour Board
board) LogicalColour
logicalColour Rank
rank
	where
		(LogicalColour
logicalColour, Rank
rank)	= Piece -> LogicalColour
Component.Piece.getLogicalColour (Piece -> LogicalColour)
-> (Piece -> Rank) -> Piece -> (LogicalColour, Rank)
forall (a :: * -> * -> *) b c c'.
Arrow a =>
a b c -> a b c' -> a b (c, c')
&&& Piece -> Rank
Component.Piece.getRank (Piece -> (LogicalColour, Rank)) -> Piece -> (LogicalColour, Rank)
forall a b. (a -> b) -> a -> b
$ Piece
piece

{- |
	* Whether the @King@ of the specified /logical colour/ is currently /checked/.

	* N.B.: independent of whose turn it actually is.

	* CAVEAT: assumes there's exactly one @King@ of the specified /logical colour/.
-}
isKingChecked
	:: Board
	-> Colour.LogicalColour.LogicalColour	-- ^ The /logical colour/ of the @King@ in question.
	-> Bool
isKingChecked :: Board -> LogicalColour -> Bool
isKingChecked board :: Board
board@MkBoard { getCoordinatesByRankByLogicalColour :: Board -> CoordinatesByRankByLogicalColour
getCoordinatesByRankByLogicalColour = CoordinatesByRankByLogicalColour
coordinatesByRankByLogicalColour }	= Bool -> Bool
not (Bool -> Bool) -> (LogicalColour -> Bool) -> LogicalColour -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [(Coordinates, Rank)] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null ([(Coordinates, Rank)] -> Bool)
-> (LogicalColour -> [(Coordinates, Rank)])
-> LogicalColour
-> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((Coordinates -> [(Coordinates, Rank)])
 -> Coordinates -> [(Coordinates, Rank)])
-> (Coordinates -> [(Coordinates, Rank)], Coordinates)
-> [(Coordinates, Rank)]
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry (Coordinates -> [(Coordinates, Rank)])
-> Coordinates -> [(Coordinates, Rank)]
forall a b. (a -> b) -> a -> b
($!) ((Coordinates -> [(Coordinates, Rank)], Coordinates)
 -> [(Coordinates, Rank)])
-> (LogicalColour
    -> (Coordinates -> [(Coordinates, Rank)], Coordinates))
-> LogicalColour
-> [(Coordinates, Rank)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (
	Board -> LogicalColour -> Coordinates -> [(Coordinates, Rank)]
findAttackersOf Board
board (LogicalColour -> Coordinates -> [(Coordinates, Rank)])
-> (LogicalColour -> Coordinates)
-> LogicalColour
-> (Coordinates -> [(Coordinates, Rank)], Coordinates)
forall (a :: * -> * -> *) b c c'.
Arrow a =>
a b c -> a b c' -> a b (c, c')
&&& CoordinatesByRankByLogicalColour -> LogicalColour -> Coordinates
State.CoordinatesByRankByLogicalColour.getKingsCoordinates CoordinatesByRankByLogicalColour
coordinatesByRankByLogicalColour
 )

-- | Confirm whether the specified move passes through check.
passesThroughCheck
	:: Board
	-> Colour.LogicalColour.LogicalColour	-- ^ The mover's /logical colour/.
	-> Component.Move.Move
	-> Bool
passesThroughCheck :: Board -> LogicalColour -> Move -> Bool
passesThroughCheck Board
board LogicalColour
logicalColour	= Bool -> Bool
not (Bool -> Bool) -> (Move -> Bool) -> Move -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((Coordinates, NPieces) -> Bool)
-> [(Coordinates, NPieces)] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all (
	[(Coordinates, Rank)] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null ([(Coordinates, Rank)] -> Bool)
-> ((Coordinates, NPieces) -> [(Coordinates, Rank)])
-> (Coordinates, NPieces)
-> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Board -> LogicalColour -> Coordinates -> [(Coordinates, Rank)]
findAttackersOf Board
board LogicalColour
logicalColour (Coordinates -> [(Coordinates, Rank)])
-> ((Coordinates, NPieces) -> Coordinates)
-> (Coordinates, NPieces)
-> [(Coordinates, Rank)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Coordinates, NPieces) -> Coordinates
forall a b. (a, b) -> a
fst {-coordinates-}
 ) ([(Coordinates, NPieces)] -> Bool)
-> (Move -> [(Coordinates, NPieces)]) -> Move -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Move -> [(Coordinates, NPieces)]
Component.Move.interpolate

{- |
	* Whether one's own @King@ has become exposed in the proposed /board/.

	* CAVEAT: assumes that one's @King@ wasn't already checked.

	* CAVEAT: this function is a performance-hotspot.
-}
exposesKing
	:: Board				-- ^ The original /board/, i.e. prior to the /move/.
	-> Colour.LogicalColour.LogicalColour	-- ^ The /logical colour/ of the player proposing to move.
	-> Component.Move.Move			-- ^ The /move/.
	-> Bool
exposesKing :: Board -> LogicalColour -> Move -> Bool
exposesKing board :: Board
board@MkBoard { getCoordinatesByRankByLogicalColour :: Board -> CoordinatesByRankByLogicalColour
getCoordinatesByRankByLogicalColour = CoordinatesByRankByLogicalColour
coordinatesByRankByLogicalColour } LogicalColour
logicalColour Move
move
	| Coordinates
source Coordinates -> Coordinates -> Bool
forall a. Eq a => a -> a -> Bool
== Coordinates
kingsCoordinates	= Bool -> Bool
not (Bool -> Bool) -> (Coordinates -> Bool) -> Coordinates -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [(Coordinates, Rank)] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null ([(Coordinates, Rank)] -> Bool)
-> (Coordinates -> [(Coordinates, Rank)]) -> Coordinates -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Board -> LogicalColour -> Coordinates -> [(Coordinates, Rank)]
findAttackersOf Board
board LogicalColour
logicalColour (Coordinates -> Bool) -> Coordinates -> Bool
forall a b. (a -> b) -> a -> b
$! Move -> Coordinates
Component.Move.getDestination Move
move	-- CAVEAT: expensive, since all directions from the King may have to be explored.
	| Just Direction
directionFromKing	<- Vector -> Maybe Direction
Cartesian.Vector.toMaybeDirection (Vector -> Maybe Direction) -> Vector -> Maybe Direction
forall a b. (a -> b) -> a -> b
$! Coordinates -> Coordinates -> Vector
Cartesian.Vector.measureDistance Coordinates
kingsCoordinates Coordinates
source	-- Check whether one's own King is on a straight line with the start of the move.
	, let maybePieceByCoordinates :: MaybePieceByCoordinates
maybePieceByCoordinates	= Board -> MaybePieceByCoordinates
getMaybePieceByCoordinates Board
board
	, MaybePieceByCoordinates -> Coordinates -> Coordinates -> Bool
State.MaybePieceByCoordinates.isClear MaybePieceByCoordinates
maybePieceByCoordinates Coordinates
kingsCoordinates Coordinates
source	-- Check whether the straight line from one's own King to the start of the move, is clear.
	, Bool -> (Direction -> Bool) -> Maybe Direction -> Bool
forall b a. b -> (a -> b) -> Maybe a -> b
Data.Maybe.maybe Bool
True {-Knight's move-} (
		Bool -> Bool
not (Bool -> Bool) -> (Direction -> Bool) -> Direction -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Direction -> Direction -> Bool
Direction.Direction.areAligned Direction
directionFromKing	-- The blocking piece has revealed any attacker.
	) (Maybe Direction -> Bool)
-> (Vector -> Maybe Direction) -> Vector -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Vector -> Maybe Direction
Cartesian.Vector.toMaybeDirection (Vector -> Bool) -> Vector -> Bool
forall a b. (a -> b) -> a -> b
$! Move -> Vector
Component.Move.measureDistance Move
move
	, Just (Coordinates
_, Rank
attackersRank)	<- MaybePieceByCoordinates
-> LogicalColour
-> Coordinates
-> Direction
-> Maybe (Coordinates, Rank)
State.MaybePieceByCoordinates.findAttackerInDirection MaybePieceByCoordinates
maybePieceByCoordinates LogicalColour
logicalColour Coordinates
source Direction
directionFromKing	-- Confirm the existence of an obscured attacker.
					= Rank
attackersRank Rank -> [Rank] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem` [Rank]
Attribute.Rank.plodders	-- Check the attacker's strike-range is sufficient to bridge the vacated space.
	| Bool
otherwise			= Bool
False
	where
		source :: Coordinates
source			= Move -> Coordinates
Component.Move.getSource Move
move
		kingsCoordinates :: Coordinates
kingsCoordinates	= CoordinatesByRankByLogicalColour -> LogicalColour -> Coordinates
State.CoordinatesByRankByLogicalColour.getKingsCoordinates CoordinatesByRankByLogicalColour
coordinatesByRankByLogicalColour LogicalColour
logicalColour

-- | Count the number of defenders of each /piece/ on the /board/.
countDefendersByCoordinatesByLogicalColour :: Board -> NDefendersByCoordinatesByLogicalColour
countDefendersByCoordinatesByLogicalColour :: Board -> NDefendersByCoordinatesByLogicalColour
countDefendersByCoordinatesByLogicalColour board :: Board
board@MkBoard { getCoordinatesByRankByLogicalColour :: Board -> CoordinatesByRankByLogicalColour
getCoordinatesByRankByLogicalColour = CoordinatesByRankByLogicalColour
coordinatesByRankByLogicalColour }	= [Map Coordinates NPieces] -> NDefendersByCoordinatesByLogicalColour
forall (a :: * -> * -> *) e. IArray a e => [e] -> a LogicalColour e
Colour.LogicalColour.listArrayByLogicalColour [
	[(Coordinates, NPieces)] -> Map Coordinates NPieces
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList [
		(
			Coordinates
coordinates,
			NPieces -> NPieces
forall a b. (Integral a, Num b) => a -> b
fromIntegral (NPieces -> NPieces)
-> ([(Coordinates, Rank)] -> NPieces)
-> [(Coordinates, Rank)]
-> NPieces
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [(Coordinates, Rank)] -> NPieces
forall (t :: * -> *) a. Foldable t => t a -> NPieces
length ([(Coordinates, Rank)] -> NPieces)
-> [(Coordinates, Rank)] -> NPieces
forall a b. (a -> b) -> a -> b
$ Board -> LogicalColour -> Coordinates -> [(Coordinates, Rank)]
findAttackersOf Board
board (
				LogicalColour -> LogicalColour
forall a. Opposable a => a -> a
Property.Opposable.getOpposite LogicalColour
logicalColour	-- Investigate an attack on these coordinates by one's own logical colour.
			) Coordinates
coordinates
		) |
			Rank
rank		<- [Rank]
Attribute.Rank.expendable,	-- CAVEAT: there's no point defending one's own King.
			Coordinates
coordinates	<- CoordinatesByRankByLogicalColour
-> LogicalColour -> Rank -> [Coordinates]
State.CoordinatesByRankByLogicalColour.dereference CoordinatesByRankByLogicalColour
coordinatesByRankByLogicalColour LogicalColour
logicalColour Rank
rank
	] {-list-comprehension-} | LogicalColour
logicalColour <- [LogicalColour]
forall a. FixedMembership a => [a]
Property.FixedMembership.members
 ] -- List-comprehension.

-- | Collapses 'NDefendersByCoordinatesByLogicalColour' into the total number of defenders on either side.
summariseNDefendersByLogicalColour :: Board -> Colour.LogicalColour.ArrayByLogicalColour Type.Count.NPieces
summariseNDefendersByLogicalColour :: Board -> ArrayByLogicalColour NPieces
summariseNDefendersByLogicalColour MkBoard { getNDefendersByCoordinatesByLogicalColour :: Board -> NDefendersByCoordinatesByLogicalColour
getNDefendersByCoordinatesByLogicalColour = NDefendersByCoordinatesByLogicalColour
nDefendersByCoordinatesByLogicalColour }	= (Map Coordinates NPieces -> NPieces)
-> NDefendersByCoordinatesByLogicalColour
-> ArrayByLogicalColour NPieces
forall (a :: * -> * -> *) e' e i.
(IArray a e', IArray a e, Ix i) =>
(e' -> e) -> a i e' -> a i e
Data.Array.IArray.amap (
	(NPieces -> NPieces -> NPieces)
-> NPieces -> Map Coordinates NPieces -> NPieces
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
Data.Foldable.foldl' NPieces -> NPieces -> NPieces
forall a. Num a => a -> a -> a
(+) NPieces
0	-- CAVEAT: 'Data.Foldable.sum' is too slow.
 ) NDefendersByCoordinatesByLogicalColour
nDefendersByCoordinatesByLogicalColour