{-
	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@]	Data on /move/s, gathered while searching.

-}

module BishBosh.Search.DynamicMoveData(
-- * Types
-- ** Type-synonyms
	Transformation,
-- ** Data-types
	KillerMoveKey(),
	DynamicMoveData(
--		MkDynamicMoveData,
		getKillerMoves,
		getTranspositions
	),
-- * Functions
-- ** Constructors
	mkKillerMoveKeyFromTurn,
-- ** Mutators
	updateKillerMoves,
	updateTranspositions
 ) where

import			Control.Arrow((&&&))
import qualified	BishBosh.Attribute.Rank			as Attribute.Rank
import qualified	BishBosh.Component.Move			as Component.Move
import qualified	BishBosh.Component.QualifiedMove	as Component.QualifiedMove
import qualified	BishBosh.Component.Turn			as Component.Turn
import qualified	BishBosh.Property.Empty			as Property.Empty
import qualified	BishBosh.Search.EphemeralData		as Search.EphemeralData
import qualified	BishBosh.Search.KillerMoves		as Search.KillerMoves
import qualified	BishBosh.Search.Transpositions		as Search.Transpositions
import qualified	BishBosh.Type.Count			as Type.Count
import qualified	Data.Maybe

{- |
	* Killer-moves are indexed by both the /move/ & the /rank/ of the piece which made it.

	* CAVEAT: there's still ambiguity in this /key/, since it may match either a different piece of the same /rank/ or have a different /move-type/ (though typically only quiet moves are recorded), in sibling games.
-}
data KillerMoveKey	= MkKillerMoveKey Component.Move.Move Attribute.Rank.Rank deriving (KillerMoveKey -> KillerMoveKey -> Bool
(KillerMoveKey -> KillerMoveKey -> Bool)
-> (KillerMoveKey -> KillerMoveKey -> Bool) -> Eq KillerMoveKey
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: KillerMoveKey -> KillerMoveKey -> Bool
$c/= :: KillerMoveKey -> KillerMoveKey -> Bool
== :: KillerMoveKey -> KillerMoveKey -> Bool
$c== :: KillerMoveKey -> KillerMoveKey -> Bool
Eq, Eq KillerMoveKey
Eq KillerMoveKey
-> (KillerMoveKey -> KillerMoveKey -> Ordering)
-> (KillerMoveKey -> KillerMoveKey -> Bool)
-> (KillerMoveKey -> KillerMoveKey -> Bool)
-> (KillerMoveKey -> KillerMoveKey -> Bool)
-> (KillerMoveKey -> KillerMoveKey -> Bool)
-> (KillerMoveKey -> KillerMoveKey -> KillerMoveKey)
-> (KillerMoveKey -> KillerMoveKey -> KillerMoveKey)
-> Ord KillerMoveKey
KillerMoveKey -> KillerMoveKey -> Bool
KillerMoveKey -> KillerMoveKey -> Ordering
KillerMoveKey -> KillerMoveKey -> KillerMoveKey
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: KillerMoveKey -> KillerMoveKey -> KillerMoveKey
$cmin :: KillerMoveKey -> KillerMoveKey -> KillerMoveKey
max :: KillerMoveKey -> KillerMoveKey -> KillerMoveKey
$cmax :: KillerMoveKey -> KillerMoveKey -> KillerMoveKey
>= :: KillerMoveKey -> KillerMoveKey -> Bool
$c>= :: KillerMoveKey -> KillerMoveKey -> Bool
> :: KillerMoveKey -> KillerMoveKey -> Bool
$c> :: KillerMoveKey -> KillerMoveKey -> Bool
<= :: KillerMoveKey -> KillerMoveKey -> Bool
$c<= :: KillerMoveKey -> KillerMoveKey -> Bool
< :: KillerMoveKey -> KillerMoveKey -> Bool
$c< :: KillerMoveKey -> KillerMoveKey -> Bool
compare :: KillerMoveKey -> KillerMoveKey -> Ordering
$ccompare :: KillerMoveKey -> KillerMoveKey -> Ordering
$cp1Ord :: Eq KillerMoveKey
Ord, Int -> KillerMoveKey -> ShowS
[KillerMoveKey] -> ShowS
KillerMoveKey -> String
(Int -> KillerMoveKey -> ShowS)
-> (KillerMoveKey -> String)
-> ([KillerMoveKey] -> ShowS)
-> Show KillerMoveKey
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [KillerMoveKey] -> ShowS
$cshowList :: [KillerMoveKey] -> ShowS
show :: KillerMoveKey -> String
$cshow :: KillerMoveKey -> String
showsPrec :: Int -> KillerMoveKey -> ShowS
$cshowsPrec :: Int -> KillerMoveKey -> ShowS
Show)

-- | Constructor.
mkKillerMoveKeyFromTurn :: Component.Turn.Turn -> KillerMoveKey
mkKillerMoveKeyFromTurn :: Turn -> KillerMoveKey
mkKillerMoveKeyFromTurn	= (Move -> Rank -> KillerMoveKey) -> (Move, Rank) -> KillerMoveKey
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry Move -> Rank -> KillerMoveKey
MkKillerMoveKey ((Move, Rank) -> KillerMoveKey)
-> (Turn -> (Move, Rank)) -> Turn -> KillerMoveKey
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (QualifiedMove -> Move
Component.QualifiedMove.getMove (QualifiedMove -> Move) -> (Turn -> QualifiedMove) -> Turn -> Move
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Turn -> QualifiedMove
Component.Turn.getQualifiedMove (Turn -> Move) -> (Turn -> Rank) -> Turn -> (Move, Rank)
forall (a :: * -> * -> *) b c c'.
Arrow a =>
a b c -> a b c' -> a b (c, c')
&&& Turn -> Rank
Component.Turn.getRank)

-- | The data on /move/s, gathered while searching.
data DynamicMoveData positionHash	= MkDynamicMoveData {
	DynamicMoveData positionHash -> KillerMoves KillerMoveKey
getKillerMoves		:: Search.KillerMoves.KillerMoves KillerMoveKey,
	DynamicMoveData positionHash
-> Transpositions QualifiedMove positionHash
getTranspositions	:: Search.Transpositions.Transpositions Component.QualifiedMove.QualifiedMove positionHash	-- ^ N.B. a qualifiedMove is used to additionally record any promotion-rank.
}

instance Property.Empty.Empty (DynamicMoveData positionHash) where
	empty :: DynamicMoveData positionHash
empty = MkDynamicMoveData :: forall positionHash.
KillerMoves KillerMoveKey
-> Transpositions QualifiedMove positionHash
-> DynamicMoveData positionHash
MkDynamicMoveData {
		getKillerMoves :: KillerMoves KillerMoveKey
getKillerMoves		= KillerMoves KillerMoveKey
forall a. Empty a => a
Property.Empty.empty,
		getTranspositions :: Transpositions QualifiedMove positionHash
getTranspositions	= Transpositions QualifiedMove positionHash
forall a. Empty a => a
Property.Empty.empty
	}

-- | The type of a function which transforms the dynamic move-data.
type Transformation positionHash	= DynamicMoveData positionHash -> DynamicMoveData positionHash

-- | Mutator.
updateKillerMoves :: Search.KillerMoves.Transformation KillerMoveKey -> Transformation positionHash
updateKillerMoves :: Transformation KillerMoveKey -> Transformation positionHash
updateKillerMoves Transformation KillerMoveKey
f dynamicMoveData :: DynamicMoveData positionHash
dynamicMoveData@MkDynamicMoveData { getKillerMoves :: forall positionHash.
DynamicMoveData positionHash -> KillerMoves KillerMoveKey
getKillerMoves = KillerMoves KillerMoveKey
killerMoves }	= DynamicMoveData positionHash
dynamicMoveData { getKillerMoves :: KillerMoves KillerMoveKey
getKillerMoves = Transformation KillerMoveKey
f KillerMoves KillerMoveKey
killerMoves }

-- | Mutator.
updateTranspositions :: Search.Transpositions.Transformation Component.QualifiedMove.QualifiedMove positionHash -> Transformation positionHash
updateTranspositions :: Transformation QualifiedMove positionHash
-> Transformation positionHash
updateTranspositions Transformation QualifiedMove positionHash
f dynamicMoveData :: DynamicMoveData positionHash
dynamicMoveData@MkDynamicMoveData { getTranspositions :: forall positionHash.
DynamicMoveData positionHash
-> Transpositions QualifiedMove positionHash
getTranspositions = Transpositions QualifiedMove positionHash
transpositions }	= DynamicMoveData positionHash
dynamicMoveData { getTranspositions :: Transpositions QualifiedMove positionHash
getTranspositions = Transformation QualifiedMove positionHash
f Transpositions QualifiedMove positionHash
transpositions }

instance Search.EphemeralData.MaybeEphemeralData (DynamicMoveData positionHash) where
	maybeEuthanise :: Int
-> MaybeRetireAfterNMoves
-> MaybeRetireAfterNMoves
-> DynamicMoveData positionHash
-> DynamicMoveData positionHash
maybeEuthanise Int
nPlies MaybeRetireAfterNMoves
maybeRetireKillerMovesAfter MaybeRetireAfterNMoves
maybeRetireTranspositionsAfter MkDynamicMoveData {
		getKillerMoves :: forall positionHash.
DynamicMoveData positionHash -> KillerMoves KillerMoveKey
getKillerMoves		= KillerMoves KillerMoveKey
killerMoves,
		getTranspositions :: forall positionHash.
DynamicMoveData positionHash
-> Transpositions QualifiedMove positionHash
getTranspositions	= Transpositions QualifiedMove positionHash
transpositions
	} = MkDynamicMoveData :: forall positionHash.
KillerMoves KillerMoveKey
-> Transpositions QualifiedMove positionHash
-> DynamicMoveData positionHash
MkDynamicMoveData {
		getKillerMoves :: KillerMoves KillerMoveKey
getKillerMoves	= Transformation KillerMoveKey
-> (Int -> Transformation KillerMoveKey)
-> MaybeRetireAfterNMoves
-> Transformation KillerMoveKey
forall b a. b -> (a -> b) -> Maybe a -> b
Data.Maybe.maybe Transformation KillerMoveKey
forall a. a -> a
id (
			Int -> Transformation KillerMoveKey
forall a. EphemeralData a => Int -> a -> a
Search.EphemeralData.euthanise (Int -> Transformation KillerMoveKey)
-> (Int -> Int) -> Int -> Transformation KillerMoveKey
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> Int
reduceNPlies	-- When searching for a move at (nPlies + 1), matches with killer-moves from 'iterate (subtract 2) $ pred nPlies' are relevant up to a point. N.B. the opponent's killer-moves are useless.
		) MaybeRetireAfterNMoves
maybeRetireKillerMovesAfter KillerMoves KillerMoveKey
killerMoves,
		getTranspositions :: Transpositions QualifiedMove positionHash
getTranspositions	= (Transpositions QualifiedMove positionHash
 -> Transpositions QualifiedMove positionHash)
-> (Int
    -> Transpositions QualifiedMove positionHash
    -> Transpositions QualifiedMove positionHash)
-> MaybeRetireAfterNMoves
-> Transpositions QualifiedMove positionHash
-> Transpositions QualifiedMove positionHash
forall b a. b -> (a -> b) -> Maybe a -> b
Data.Maybe.maybe Transpositions QualifiedMove positionHash
-> Transpositions QualifiedMove positionHash
forall a. a -> a
id (
			Int
-> Transpositions QualifiedMove positionHash
-> Transpositions QualifiedMove positionHash
forall a. EphemeralData a => Int -> a -> a
Search.EphemeralData.euthanise (Int
 -> Transpositions QualifiedMove positionHash
 -> Transpositions QualifiedMove positionHash)
-> (Int -> Int)
-> Int
-> Transpositions QualifiedMove positionHash
-> Transpositions QualifiedMove positionHash
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> Int
reduceNPlies
		) MaybeRetireAfterNMoves
maybeRetireTranspositionsAfter Transpositions QualifiedMove positionHash
transpositions
	} where
		reduceNPlies :: Type.Count.NMoves -> Type.Count.NPlies
		reduceNPlies :: Int -> Int
reduceNPlies	= (Int -> Int -> Int
forall a. Num a => a -> a -> a
`subtract` Int
nPlies) (Int -> Int) -> (Int -> Int) -> Int -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
Component.Move.nPliesPerMove) (Int -> Int) -> (Int -> Int) -> Int -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral