{-
	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 relative value of a specific /rank/ of piece, occupying a specific square, at a each stage in the game's life-cycle.
-}

module BishBosh.Component.PieceSquareValueByCoordinatesByRank(
-- * Types
-- ** Type-synonyms
--	PieceSquareValueByCoordinatesByEitherNPieces,
-- ** Data-types
	PieceSquareValueByCoordinatesByRank(),
-- * Constants
	gnuPlotComment,
-- * Functions
--	unify,
	formatForGNUPlot,
-- ** Accessors
	getPieceSquareValueByCoordinates,
-- ** Constructor
	mkPieceSquareValueByCoordinatesByRank
) where

import			Control.Arrow((&&&), (|||))
import			Data.Array.IArray((!))
import qualified	BishBosh.Attribute.Rank					as Attribute.Rank
import qualified	BishBosh.Cartesian.Abscissa				as Cartesian.Abscissa
import qualified	BishBosh.Cartesian.Coordinates				as Cartesian.Coordinates
import qualified	BishBosh.Component.PieceSquareValueByCoordinates	as Component.PieceSquareValueByCoordinates
import qualified	BishBosh.Property.FixedMembership			as Property.FixedMembership
import qualified	BishBosh.Text.ShowList					as Text.ShowList
import qualified	BishBosh.Type.Count					as Type.Count
import qualified	BishBosh.Type.Mass					as Type.Mass
import qualified	Control.DeepSeq
import qualified	Data.Array.IArray
import qualified	Data.Foldable
import qualified	Data.List

-- | The piece-square value may vary with /coordinates/ & optionally also the number of pieces remaining.
type PieceSquareValueByCoordinatesByEitherNPieces	= Either Component.PieceSquareValueByCoordinates.PieceSquareValueByCoordinates (
	Data.Array.IArray.Array Type.Count.NPieces Component.PieceSquareValueByCoordinates.PieceSquareValueByCoordinates
 )

-- | The piece-square value for various /coordinates/, optionally the number of pieces remaining, & /rank/.
newtype PieceSquareValueByCoordinatesByRank	= MkPieceSquareValueByCoordinatesByRank (Attribute.Rank.ArrayByRank PieceSquareValueByCoordinatesByEitherNPieces)

instance Control.DeepSeq.NFData PieceSquareValueByCoordinatesByRank where
	rnf :: PieceSquareValueByCoordinatesByRank -> ()
rnf (MkPieceSquareValueByCoordinatesByRank ArrayByRank PieceSquareValueByCoordinatesByEitherNPieces
pieceSquareValueByCoordinatesByEitherNPiecesByRank)	= ArrayByRank PieceSquareValueByCoordinatesByEitherNPieces -> ()
forall a. NFData a => a -> ()
Control.DeepSeq.rnf ArrayByRank PieceSquareValueByCoordinatesByEitherNPieces
pieceSquareValueByCoordinatesByEitherNPiecesByRank	-- Forward.

-- | Constructor.
mkPieceSquareValueByCoordinatesByRank :: (Attribute.Rank.Rank -> PieceSquareValueByCoordinatesByEitherNPieces) -> PieceSquareValueByCoordinatesByRank
mkPieceSquareValueByCoordinatesByRank :: (Rank -> PieceSquareValueByCoordinatesByEitherNPieces)
-> PieceSquareValueByCoordinatesByRank
mkPieceSquareValueByCoordinatesByRank	= ArrayByRank PieceSquareValueByCoordinatesByEitherNPieces
-> PieceSquareValueByCoordinatesByRank
MkPieceSquareValueByCoordinatesByRank (ArrayByRank PieceSquareValueByCoordinatesByEitherNPieces
 -> PieceSquareValueByCoordinatesByRank)
-> ((Rank -> PieceSquareValueByCoordinatesByEitherNPieces)
    -> ArrayByRank PieceSquareValueByCoordinatesByEitherNPieces)
-> (Rank -> PieceSquareValueByCoordinatesByEitherNPieces)
-> PieceSquareValueByCoordinatesByRank
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [PieceSquareValueByCoordinatesByEitherNPieces]
-> ArrayByRank PieceSquareValueByCoordinatesByEitherNPieces
forall (a :: * -> * -> *) e. IArray a e => [e] -> a Rank e
Attribute.Rank.listArrayByRank ([PieceSquareValueByCoordinatesByEitherNPieces]
 -> ArrayByRank PieceSquareValueByCoordinatesByEitherNPieces)
-> ((Rank -> PieceSquareValueByCoordinatesByEitherNPieces)
    -> [PieceSquareValueByCoordinatesByEitherNPieces])
-> (Rank -> PieceSquareValueByCoordinatesByEitherNPieces)
-> ArrayByRank PieceSquareValueByCoordinatesByEitherNPieces
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((Rank -> PieceSquareValueByCoordinatesByEitherNPieces)
-> [Rank] -> [PieceSquareValueByCoordinatesByEitherNPieces]
forall a b. (a -> b) -> [a] -> [b]
`map` [Rank]
forall a. FixedMembership a => [a]
Property.FixedMembership.members)

-- | Unify the /Left/ & /Right/ sides of 'PieceSquareValueByCoordinatesByEitherNPieces'.
unify :: Type.Count.NPieces -> PieceSquareValueByCoordinatesByEitherNPieces -> Component.PieceSquareValueByCoordinates.PieceSquareValueByCoordinates
unify :: NPieces
-> PieceSquareValueByCoordinatesByEitherNPieces
-> PieceSquareValueByCoordinates
unify NPieces
nPieces	= PieceSquareValueByCoordinates -> PieceSquareValueByCoordinates
forall a. a -> a
id (PieceSquareValueByCoordinates -> PieceSquareValueByCoordinates)
-> (Array NPieces PieceSquareValueByCoordinates
    -> PieceSquareValueByCoordinates)
-> PieceSquareValueByCoordinatesByEitherNPieces
-> PieceSquareValueByCoordinates
forall (a :: * -> * -> *) b d c.
ArrowChoice a =>
a b d -> a c d -> a (Either b c) d
||| (Array NPieces PieceSquareValueByCoordinates
-> NPieces -> PieceSquareValueByCoordinates
forall (a :: * -> * -> *) e i.
(IArray a e, Ix i) =>
a i e -> i -> e
! NPieces
nPieces)

-- | Retrieve the appropriate /PieceSquareValueByCoordinates/ for the specified /rank/, @ the current stage of the game's life-cycle.
getPieceSquareValueByCoordinates
	:: PieceSquareValueByCoordinatesByRank
	-> Type.Count.NPieces	-- ^ An inverse proxy for the age of the game.
	-> Attribute.Rank.Rank	-- ^ The /piece/'s /rank/.
	-> Component.PieceSquareValueByCoordinates.PieceSquareValueByCoordinates
getPieceSquareValueByCoordinates :: PieceSquareValueByCoordinatesByRank
-> NPieces -> Rank -> PieceSquareValueByCoordinates
getPieceSquareValueByCoordinates (MkPieceSquareValueByCoordinatesByRank ArrayByRank PieceSquareValueByCoordinatesByEitherNPieces
pieceSquareValueByCoordinatesByEitherNPiecesByRank) NPieces
nPieces Rank
rank	= NPieces
-> PieceSquareValueByCoordinatesByEitherNPieces
-> PieceSquareValueByCoordinates
unify NPieces
nPieces (PieceSquareValueByCoordinatesByEitherNPieces
 -> PieceSquareValueByCoordinates)
-> PieceSquareValueByCoordinatesByEitherNPieces
-> PieceSquareValueByCoordinates
forall a b. (a -> b) -> a -> b
$ ArrayByRank PieceSquareValueByCoordinatesByEitherNPieces
pieceSquareValueByCoordinatesByEitherNPiecesByRank ArrayByRank PieceSquareValueByCoordinatesByEitherNPieces
-> Rank -> PieceSquareValueByCoordinatesByEitherNPieces
forall (a :: * -> * -> *) e i.
(IArray a e, Ix i) =>
a i e -> i -> e
! Rank
rank

-- | The character used in __GNUPlot__ to denote a comment.
gnuPlotComment :: Char
gnuPlotComment :: Char
gnuPlotComment	= Char
'#'

-- | Format the data for input to __GNUPlot__.
formatForGNUPlot
	:: PieceSquareValueByCoordinatesByRank
	-> (Type.Mass.PieceSquareValue -> ShowS)	-- ^ Format a /pieceSquareValue/.
	-> ShowS					-- ^ The column-delimiter.
	-> Type.Count.NPieces				-- ^ Select from interpolated values.
	-> ShowS
formatForGNUPlot :: PieceSquareValueByCoordinatesByRank
-> (PieceSquareValue -> ShowS) -> ShowS -> NPieces -> ShowS
formatForGNUPlot (MkPieceSquareValueByCoordinatesByRank ArrayByRank PieceSquareValueByCoordinatesByEitherNPieces
pieceSquareValueByCoordinatesByEitherNPiecesByRank) PieceSquareValue -> ShowS
pieceSquareValueFormatter ShowS
columnDelimiter NPieces
nPieces	= (
	[ShowS] -> ShowS
showsRow (
		Char -> ShowS
showChar Char
gnuPlotComment ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Char -> ShowS
showChar Char
'x' ShowS -> [ShowS] -> [ShowS]
forall a. a -> [a] -> [a]
: Char -> ShowS
showChar Char
'y' ShowS -> [ShowS] -> [ShowS]
forall a. a -> [a] -> [a]
: (Rank -> ShowS) -> [Rank] -> [ShowS]
forall a b. (a -> b) -> [a] -> [b]
map Rank -> ShowS
forall a. Show a => a -> ShowS
shows [Rank]
Attribute.Rank.range	-- Header comment.
	) ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
 ) (ShowS -> ShowS)
-> ([PieceSquareValueByCoordinatesByEitherNPieces] -> ShowS)
-> [PieceSquareValueByCoordinatesByEitherNPieces]
-> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((Coordinates, [PieceSquareValue]) -> ShowS -> ShowS)
-> ShowS -> [(Coordinates, [PieceSquareValue])] -> ShowS
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr (
	\(Coordinates
coordinates, [PieceSquareValue]
byRank') ShowS
showS	-> let
		(NPieces
x, NPieces
y)	= Coordinates -> NPieces
Cartesian.Coordinates.getX (Coordinates -> NPieces)
-> (Coordinates -> NPieces) -> Coordinates -> (NPieces, NPieces)
forall (a :: * -> * -> *) b c c'.
Arrow a =>
a b c -> a b c' -> a b (c, c')
&&& Coordinates -> NPieces
Cartesian.Coordinates.getY (Coordinates -> (NPieces, NPieces))
-> Coordinates -> (NPieces, NPieces)
forall a b. (a -> b) -> a -> b
$ Coordinates
coordinates
	in [ShowS] -> ShowS
showsRow (
		NPieces -> ShowS
forall a. Show a => a -> ShowS
shows NPieces
x ShowS -> [ShowS] -> [ShowS]
forall a. a -> [a] -> [a]
: NPieces -> ShowS
forall a. Show a => a -> ShowS
shows NPieces
y ShowS -> [ShowS] -> [ShowS]
forall a. a -> [a] -> [a]
: (PieceSquareValue -> ShowS) -> [PieceSquareValue] -> [ShowS]
forall a b. (a -> b) -> [a] -> [b]
map PieceSquareValue -> ShowS
pieceSquareValueFormatter [PieceSquareValue]
byRank'
	) ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (
		if NPieces
x NPieces -> NPieces -> Bool
forall a. Eq a => a -> a -> Bool
== NPieces
Cartesian.Abscissa.xMax
			then ShowS
terminateRow	-- Separate isolines.
			else ShowS
forall a. a -> a
id
	) ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ShowS
showS
 ) ShowS
forall a. a -> a
id ([(Coordinates, [PieceSquareValue])] -> ShowS)
-> ([PieceSquareValueByCoordinatesByEitherNPieces]
    -> [(Coordinates, [PieceSquareValue])])
-> [PieceSquareValueByCoordinatesByEitherNPieces]
-> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Coordinates]
-> [[PieceSquareValue]] -> [(Coordinates, [PieceSquareValue])]
forall a b. [a] -> [b] -> [(a, b)]
zip [Coordinates]
forall a. FixedMembership a => [a]
Property.FixedMembership.members ([[PieceSquareValue]] -> [(Coordinates, [PieceSquareValue])])
-> ([PieceSquareValueByCoordinatesByEitherNPieces]
    -> [[PieceSquareValue]])
-> [PieceSquareValueByCoordinatesByEitherNPieces]
-> [(Coordinates, [PieceSquareValue])]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [[PieceSquareValue]] -> [[PieceSquareValue]]
forall a. [[a]] -> [[a]]
Data.List.transpose ([[PieceSquareValue]] -> [[PieceSquareValue]])
-> ([PieceSquareValueByCoordinatesByEitherNPieces]
    -> [[PieceSquareValue]])
-> [PieceSquareValueByCoordinatesByEitherNPieces]
-> [[PieceSquareValue]]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (PieceSquareValueByCoordinatesByEitherNPieces
 -> [PieceSquareValue])
-> [PieceSquareValueByCoordinatesByEitherNPieces]
-> [[PieceSquareValue]]
forall a b. (a -> b) -> [a] -> [b]
map (
	PieceSquareValueByCoordinates -> [PieceSquareValue]
Component.PieceSquareValueByCoordinates.toList (PieceSquareValueByCoordinates -> [PieceSquareValue])
-> (PieceSquareValueByCoordinatesByEitherNPieces
    -> PieceSquareValueByCoordinates)
-> PieceSquareValueByCoordinatesByEitherNPieces
-> [PieceSquareValue]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. NPieces
-> PieceSquareValueByCoordinatesByEitherNPieces
-> PieceSquareValueByCoordinates
unify NPieces
nPieces
 ) ([PieceSquareValueByCoordinatesByEitherNPieces] -> ShowS)
-> [PieceSquareValueByCoordinatesByEitherNPieces] -> ShowS
forall a b. (a -> b) -> a -> b
$ ArrayByRank PieceSquareValueByCoordinatesByEitherNPieces
-> [PieceSquareValueByCoordinatesByEitherNPieces]
forall (t :: * -> *) a. Foldable t => t a -> [a]
Data.Foldable.toList {-ByRank-} ArrayByRank PieceSquareValueByCoordinatesByEitherNPieces
pieceSquareValueByCoordinatesByEitherNPiecesByRank where
	terminateRow :: ShowS
terminateRow	= Char -> ShowS
showChar Char
'\n'
	showsRow :: [ShowS] -> ShowS
showsRow	= ShowS -> ShowS -> ShowS -> [ShowS] -> ShowS
Text.ShowList.showsDelimitedList ShowS
columnDelimiter ShowS
forall a. a -> a
id ShowS
terminateRow