{-
	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@]

	* Defines the state of the game, without regard to how it arrived there; <https://www.chessprogramming.org/Chess_Position>.

	* Games with the same /position/ may be considered to have converged, since they now have equal opportunity.

	* N.B.: /piece/s are fungible, i.e. they lack identity, so the location of identical /piece/s may be exchanged, without altering the /position/.
-}

module BishBosh.State.Position(
-- * Types
-- ** Data-types
	Position(
--		MkPosition,
--		getNextLogicalColour,
--		getMaybePieceByCoordinates,
--		getCastleableRooksByLogicalColour,
		getMaybeEnPassantAbscissa
	),
-- * Functions
-- ** Constructors
	mkPosition
) where

import qualified	BishBosh.Attribute.LogicalColour		as Attribute.LogicalColour
import qualified	BishBosh.Component.Turn				as Component.Turn
import qualified	BishBosh.Component.Zobrist			as Component.Zobrist
import qualified	BishBosh.Property.Opposable			as Property.Opposable
import qualified	BishBosh.Property.Reflectable			as Property.Reflectable
import qualified	BishBosh.State.CastleableRooksByLogicalColour	as State.CastleableRooksByLogicalColour
import qualified	BishBosh.State.EnPassantAbscissa		as State.EnPassantAbscissa
import qualified	BishBosh.State.MaybePieceByCoordinates		as State.MaybePieceByCoordinates
import qualified	BishBosh.StateProperty.Hashable			as StateProperty.Hashable
import qualified	Control.DeepSeq
import qualified	Data.Maybe

-- | The state of the game, without regard to how it arrived there.
data Position	= MkPosition {
	Position -> LogicalColour
getNextLogicalColour			:: Attribute.LogicalColour.LogicalColour,	-- ^ The next player to move.
	Position -> MaybePieceByCoordinates
getMaybePieceByCoordinates		:: State.MaybePieceByCoordinates.MaybePieceByCoordinates,
	Position -> CastleableRooksByLogicalColour
getCastleableRooksByLogicalColour	:: State.CastleableRooksByLogicalColour.CastleableRooksByLogicalColour,
	Position -> Maybe EnPassantAbscissa
getMaybeEnPassantAbscissa		:: Maybe State.EnPassantAbscissa.EnPassantAbscissa
} deriving Position -> Position -> Bool
(Position -> Position -> Bool)
-> (Position -> Position -> Bool) -> Eq Position
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Position -> Position -> Bool
$c/= :: Position -> Position -> Bool
== :: Position -> Position -> Bool
$c== :: Position -> Position -> Bool
Eq

instance Ord Position where
	position :: Position
position@MkPosition {
		getNextLogicalColour :: Position -> LogicalColour
getNextLogicalColour		= LogicalColour
nextLogicalColour,
		getMaybePieceByCoordinates :: Position -> MaybePieceByCoordinates
getMaybePieceByCoordinates	= MaybePieceByCoordinates
maybePieceByCoordinates
	} compare :: Position -> Position -> Ordering
`compare` position' :: Position
position'@MkPosition {
		getNextLogicalColour :: Position -> LogicalColour
getNextLogicalColour		= LogicalColour
nextLogicalColour',
		getMaybePieceByCoordinates :: Position -> MaybePieceByCoordinates
getMaybePieceByCoordinates	= MaybePieceByCoordinates
maybePieceByCoordinates'
	} = (
		LogicalColour
nextLogicalColour,
		MaybePieceByCoordinates
maybePieceByCoordinates,
		Position -> CastleableRooksByLogicalColour
getCastleableRooksByLogicalColour Position
position,
		Position -> Maybe EnPassantAbscissa
getMaybeEnPassantAbscissa Position
position
	 ) (LogicalColour, MaybePieceByCoordinates,
 CastleableRooksByLogicalColour, Maybe EnPassantAbscissa)
-> (LogicalColour, MaybePieceByCoordinates,
    CastleableRooksByLogicalColour, Maybe EnPassantAbscissa)
-> Ordering
forall a. Ord a => a -> a -> Ordering
`compare` (
		LogicalColour
nextLogicalColour',
		MaybePieceByCoordinates
maybePieceByCoordinates',
		Position -> CastleableRooksByLogicalColour
getCastleableRooksByLogicalColour Position
position',
		Position -> Maybe EnPassantAbscissa
getMaybeEnPassantAbscissa Position
position'
	 )

instance Control.DeepSeq.NFData Position where
	rnf :: Position -> ()
rnf MkPosition {
		getNextLogicalColour :: Position -> LogicalColour
getNextLogicalColour			= LogicalColour
nextLogicalColour,
		getMaybePieceByCoordinates :: Position -> MaybePieceByCoordinates
getMaybePieceByCoordinates		= MaybePieceByCoordinates
maybePieceByCoordinates,
		getCastleableRooksByLogicalColour :: Position -> CastleableRooksByLogicalColour
getCastleableRooksByLogicalColour	= CastleableRooksByLogicalColour
castleableRooksByLogicalColour,
		getMaybeEnPassantAbscissa :: Position -> Maybe EnPassantAbscissa
getMaybeEnPassantAbscissa		= Maybe EnPassantAbscissa
maybeEnPassantAbscissa
	} = (LogicalColour, MaybePieceByCoordinates,
 CastleableRooksByLogicalColour, Maybe EnPassantAbscissa)
-> ()
forall a. NFData a => a -> ()
Control.DeepSeq.rnf (LogicalColour
nextLogicalColour, MaybePieceByCoordinates
maybePieceByCoordinates, CastleableRooksByLogicalColour
castleableRooksByLogicalColour, Maybe EnPassantAbscissa
maybeEnPassantAbscissa)

instance Property.Reflectable.ReflectableOnX Position where
	reflectOnX :: Position -> Position
reflectOnX position :: Position
position@MkPosition {
		getNextLogicalColour :: Position -> LogicalColour
getNextLogicalColour			= LogicalColour
nextLogicalColour,
		getMaybePieceByCoordinates :: Position -> MaybePieceByCoordinates
getMaybePieceByCoordinates		= MaybePieceByCoordinates
maybePieceByCoordinates,
		getCastleableRooksByLogicalColour :: Position -> CastleableRooksByLogicalColour
getCastleableRooksByLogicalColour	= CastleableRooksByLogicalColour
castleableRooksByLogicalColour
	} = Position
position {
		getNextLogicalColour :: LogicalColour
getNextLogicalColour			= LogicalColour -> LogicalColour
forall a. Opposable a => a -> a
Property.Opposable.getOpposite LogicalColour
nextLogicalColour,
		getMaybePieceByCoordinates :: MaybePieceByCoordinates
getMaybePieceByCoordinates		= MaybePieceByCoordinates -> MaybePieceByCoordinates
forall a. ReflectableOnX a => a -> a
Property.Reflectable.reflectOnX MaybePieceByCoordinates
maybePieceByCoordinates,
		getCastleableRooksByLogicalColour :: CastleableRooksByLogicalColour
getCastleableRooksByLogicalColour	= CastleableRooksByLogicalColour -> CastleableRooksByLogicalColour
forall a. ReflectableOnX a => a -> a
Property.Reflectable.reflectOnX CastleableRooksByLogicalColour
castleableRooksByLogicalColour
	}

instance StateProperty.Hashable.Hashable Position where
	listRandoms :: Position -> Zobrist positionHash -> [positionHash]
listRandoms MkPosition {
		getNextLogicalColour :: Position -> LogicalColour
getNextLogicalColour			= LogicalColour
nextLogicalColour,
		getMaybePieceByCoordinates :: Position -> MaybePieceByCoordinates
getMaybePieceByCoordinates		= MaybePieceByCoordinates
maybePieceByCoordinates,
		getCastleableRooksByLogicalColour :: Position -> CastleableRooksByLogicalColour
getCastleableRooksByLogicalColour	= CastleableRooksByLogicalColour
castleableRooksByLogicalColour,
		getMaybeEnPassantAbscissa :: Position -> Maybe EnPassantAbscissa
getMaybeEnPassantAbscissa		= Maybe EnPassantAbscissa
maybeEnPassantAbscissa
	} Zobrist positionHash
zobrist	= (
		if LogicalColour -> Bool
Attribute.LogicalColour.isBlack LogicalColour
nextLogicalColour
			then (Zobrist positionHash -> positionHash
forall positionHash. Zobrist positionHash -> positionHash
Component.Zobrist.getRandomForBlacksMove Zobrist positionHash
zobrist positionHash -> [positionHash] -> [positionHash]
forall a. a -> [a] -> [a]
:)
			else [positionHash] -> [positionHash]
forall a. a -> a
id
	 ) ([positionHash] -> [positionHash])
-> ([positionHash] -> [positionHash])
-> [positionHash]
-> [positionHash]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ([positionHash] -> [positionHash])
-> (EnPassantAbscissa -> [positionHash] -> [positionHash])
-> Maybe EnPassantAbscissa
-> [positionHash]
-> [positionHash]
forall b a. b -> (a -> b) -> Maybe a -> b
Data.Maybe.maybe [positionHash] -> [positionHash]
forall a. a -> a
id (
		[positionHash] -> [positionHash] -> [positionHash]
forall a. [a] -> [a] -> [a]
(++) ([positionHash] -> [positionHash] -> [positionHash])
-> (EnPassantAbscissa -> [positionHash])
-> EnPassantAbscissa
-> [positionHash]
-> [positionHash]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (EnPassantAbscissa -> Zobrist positionHash -> [positionHash]
forall hashable positionHash.
Hashable hashable =>
hashable -> Zobrist positionHash -> [positionHash]
`StateProperty.Hashable.listRandoms` Zobrist positionHash
zobrist)
	 ) Maybe EnPassantAbscissa
maybeEnPassantAbscissa ([positionHash] -> [positionHash])
-> [positionHash] -> [positionHash]
forall a b. (a -> b) -> a -> b
$ CastleableRooksByLogicalColour
-> Zobrist positionHash -> [positionHash]
forall hashable positionHash.
Hashable hashable =>
hashable -> Zobrist positionHash -> [positionHash]
StateProperty.Hashable.listRandoms CastleableRooksByLogicalColour
castleableRooksByLogicalColour Zobrist positionHash
zobrist [positionHash] -> [positionHash] -> [positionHash]
forall a. [a] -> [a] -> [a]
++ MaybePieceByCoordinates -> Zobrist positionHash -> [positionHash]
forall hashable positionHash.
Hashable hashable =>
hashable -> Zobrist positionHash -> [positionHash]
StateProperty.Hashable.listRandoms MaybePieceByCoordinates
maybePieceByCoordinates Zobrist positionHash
zobrist

-- | Constructor.
mkPosition
	:: Attribute.LogicalColour.LogicalColour	-- ^ The logical colour of the next player to move.
	-> State.MaybePieceByCoordinates.MaybePieceByCoordinates
	-> State.CastleableRooksByLogicalColour.CastleableRooksByLogicalColour
	-> Maybe Component.Turn.Turn			-- ^ The last /turn/ made.
	-> Position
mkPosition :: LogicalColour
-> MaybePieceByCoordinates
-> CastleableRooksByLogicalColour
-> Maybe Turn
-> Position
mkPosition LogicalColour
nextLogicalColour MaybePieceByCoordinates
maybePieceByCoordinates CastleableRooksByLogicalColour
castleableRooksByLogicalColour Maybe Turn
maybeLastTurn	= MkPosition :: LogicalColour
-> MaybePieceByCoordinates
-> CastleableRooksByLogicalColour
-> Maybe EnPassantAbscissa
-> Position
MkPosition {
	getNextLogicalColour :: LogicalColour
getNextLogicalColour			= LogicalColour
nextLogicalColour,
	getMaybePieceByCoordinates :: MaybePieceByCoordinates
getMaybePieceByCoordinates		= MaybePieceByCoordinates
maybePieceByCoordinates,	-- N.B.: one could have used 'State.CoordinatesByRankByLogicalColour.CoordinatesByRankByLogicalColour', except that the coordinates have an undefined order.
	getCastleableRooksByLogicalColour :: CastleableRooksByLogicalColour
getCastleableRooksByLogicalColour	= CastleableRooksByLogicalColour
castleableRooksByLogicalColour,
	getMaybeEnPassantAbscissa :: Maybe EnPassantAbscissa
getMaybeEnPassantAbscissa		= Maybe Turn
maybeLastTurn Maybe Turn
-> (Turn -> Maybe EnPassantAbscissa) -> Maybe EnPassantAbscissa
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= LogicalColour
-> MaybePieceByCoordinates -> Turn -> Maybe EnPassantAbscissa
State.EnPassantAbscissa.mkMaybeEnPassantAbscissa LogicalColour
nextLogicalColour MaybePieceByCoordinates
maybePieceByCoordinates
}