{-# LANGUAGE CPP #-}

#ifdef USE_PRIMITIVE
{-# LANGUAGE MagicHash #-}
#endif

{-
	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@]	The location of a square on the board.
-}

module BishBosh.Cartesian.Coordinates(
-- * Types
-- ** Data-types
	Coordinates(
--		MkCoordinates,
		getX,
		getY
	),
-- ** Type-synonyms
	QualifiedStraightLine,
--	Transformation,
	ArrayByCoordinates,
	UArrayByCoordinates,
-- * Constants
	tag,
	topLeft,
	bottomRight,
	nSquares,
--	extrapolationsByDirectionByCoordinates,
--	interpolationsByDestinationBySource,
-- * Functions
--	extrapolate',
	extrapolate,
	applyAlongDirectionsFrom,
	interpolate,
	getLogicalColourOfSquare,
	kingsStartingCoordinates,
	rooksStartingCoordinates,
	measureDistance,
	translate,
	maybeTranslate,
	translateX,
	maybeTranslateX,
	translateY,
	maybeTranslateY,
	getAdjacents,
	advance,
--	maybeAdvance,
	retreat,
	maybeRetreat,
-- ** Constructors
	mkCoordinates,
	mkMaybeCoordinates,
	mkRelativeCoordinates,
	listArrayByCoordinates,
	arrayByCoordinates,
-- ** Predicates
--	inBounds,
	isBetween,
	isPawnsFirstRank,
	isEnPassantRank,
	areSquaresIsochromatic
) where

import			Control.Arrow((&&&), (***))
import			Data.Array.IArray((!))
import qualified	BishBosh.Cartesian.Abscissa		as Cartesian.Abscissa
import qualified	BishBosh.Cartesian.Ordinate		as Cartesian.Ordinate
import qualified	BishBosh.Colour.LogicalColour		as Colour.LogicalColour
import qualified	BishBosh.Colour.LogicalColourOfSquare	as Colour.LogicalColourOfSquare
import qualified	BishBosh.Direction.Direction		as Direction.Direction
import qualified	BishBosh.Property.FixedMembership	as Property.FixedMembership
import qualified	BishBosh.Property.Opposable		as Property.Opposable
import qualified	BishBosh.Property.Orientated		as Property.Orientated
import qualified	BishBosh.Property.Reflectable		as Property.Reflectable
import qualified	BishBosh.Property.Rotatable		as Property.Rotatable
import qualified	BishBosh.Type.Count			as Type.Count
import qualified	BishBosh.Type.Length			as Type.Length
import qualified	Control.Arrow
import qualified	Control.DeepSeq
import qualified	Control.Exception
import qualified	Data.Array.IArray
import qualified	Data.Array.Unboxed
import qualified	Data.Foldable
import qualified	Data.List
import qualified	Data.Map				as Map
import qualified	Data.Maybe

#ifdef USE_PRIMITIVE
import			GHC.Exts(Int(I#))
import			GHC.Prim((+#), (*#))
#endif

-- | Used to qualify XML.
tag :: String
tag :: String
tag	= String
"coordinates"

-- | The /coordinates/ of a square on the board.
data Coordinates	= MkCoordinates {
	Coordinates -> X
getX	:: ! Type.Length.X,	-- ^ Abscissa.
	Coordinates -> X
getY	:: ! Type.Length.Y	-- ^ Ordinate.
} deriving Coordinates -> Coordinates -> Bool
(Coordinates -> Coordinates -> Bool)
-> (Coordinates -> Coordinates -> Bool) -> Eq Coordinates
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Coordinates -> Coordinates -> Bool
$c/= :: Coordinates -> Coordinates -> Bool
== :: Coordinates -> Coordinates -> Bool
$c== :: Coordinates -> Coordinates -> Bool
Eq

instance Bounded Coordinates where
	minBound :: Coordinates
minBound = MkCoordinates :: X -> X -> Coordinates
MkCoordinates {
		getX :: X
getX	= X
Cartesian.Abscissa.xMin,
		getY :: X
getY	= X
Cartesian.Ordinate.yMin
	} -- Bottom Left.
	maxBound :: Coordinates
maxBound = MkCoordinates :: X -> X -> Coordinates
MkCoordinates {
		getX :: X
getX	= X
Cartesian.Abscissa.xMax,
		getY :: X
getY	= X
Cartesian.Ordinate.yMax
	} -- Top Right.

instance Control.DeepSeq.NFData Coordinates where
--	rnf MkCoordinates { getX = x, getY = y }	= Control.DeepSeq.rnf (x, y)
	rnf :: Coordinates -> ()
rnf Coordinates
_	= ()	-- N.B.: it's already strict.

instance Data.Array.IArray.Ix Coordinates where
	range :: (Coordinates, Coordinates) -> [Coordinates]
range (Coordinates
lower, Coordinates
upper)			= Bool -> [Coordinates] -> [Coordinates]
forall a. (?callStack::CallStack) => Bool -> a -> a
Control.Exception.assert (Coordinates
lower Coordinates -> Coordinates -> Bool
forall a. Eq a => a -> a -> Bool
== Coordinates
forall a. Bounded a => a
minBound Bool -> Bool -> Bool
&& Coordinates
upper Coordinates -> Coordinates -> Bool
forall a. Eq a => a -> a -> Bool
== Coordinates
forall a. Bounded a => a
maxBound) [Coordinates]
forall a. FixedMembership a => [a]
Property.FixedMembership.members
	inRange :: (Coordinates, Coordinates) -> Coordinates -> Bool
inRange (Coordinates
lower, Coordinates
upper) Coordinates
coordinates	= Bool -> Bool -> Bool
forall a. (?callStack::CallStack) => Bool -> a -> a
Control.Exception.assert (Coordinates
coordinates Coordinates -> Coordinates -> Bool
forall a. Ord a => a -> a -> Bool
>= Coordinates
lower Bool -> Bool -> Bool
&& Coordinates
coordinates Coordinates -> Coordinates -> Bool
forall a. Ord a => a -> a -> Bool
<= Coordinates
upper) Bool
True
	index :: (Coordinates, Coordinates) -> Coordinates -> X
index (Coordinates
lower, Coordinates
upper)			= Bool -> X -> X
forall a. (?callStack::CallStack) => Bool -> a -> a
Control.Exception.assert (Coordinates
lower Coordinates -> Coordinates -> Bool
forall a. Eq a => a -> a -> Bool
== Coordinates
forall a. Bounded a => a
minBound Bool -> Bool -> Bool
&& Coordinates
upper Coordinates -> Coordinates -> Bool
forall a. Eq a => a -> a -> Bool
== Coordinates
forall a. Bounded a => a
maxBound) (X -> X) -> (Coordinates -> X) -> Coordinates -> X
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Coordinates -> X
forall a. Enum a => a -> X
fromEnum

instance Enum Coordinates where
	toEnum :: X -> Coordinates
toEnum = (
		\(X
y, X
x) -> MkCoordinates :: X -> X -> Coordinates
MkCoordinates {
			getX :: X
getX	= X -> X
Cartesian.Abscissa.fromIx X
x,
			getY :: X
getY	= X -> X
Cartesian.Ordinate.fromIx X
y
		}
	 ) ((X, X) -> Coordinates) -> (X -> (X, X)) -> X -> Coordinates
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (X -> X -> (X, X)
forall a. Integral a => a -> a -> (a, a)
`divMod` X -> X
forall a b. (Integral a, Num b) => a -> b
fromIntegral X
Cartesian.Abscissa.xLength)

	fromEnum :: Coordinates -> X
fromEnum MkCoordinates {
#ifdef USE_PRIMITIVE
		getX :: Coordinates -> X
getX	= I# Int#
x,
		getY :: Coordinates -> X
getY	= I# Int#
y
	} = Int# -> X
I# (Int#
8# Int# -> Int# -> Int#
*# Int#
y Int# -> Int# -> Int#
+# Int#
x)	-- CAVEAT: bypasses modules 'Cartesian.{Abscissa, Ordinate}'.
#else
		getX	= x,
		getY	= y
	} = fromIntegral Cartesian.Abscissa.xLength * Cartesian.Ordinate.toIx y + Cartesian.Abscissa.toIx x
#endif

instance Ord Coordinates where
	MkCoordinates { getX :: Coordinates -> X
getX = X
x, getY :: Coordinates -> X
getY = X
y } compare :: Coordinates -> Coordinates -> Ordering
`compare` MkCoordinates { getX :: Coordinates -> X
getX = X
x', getY :: Coordinates -> X
getY = X
y' }	= (X
y, X
x) (X, X) -> (X, X) -> Ordering
forall a. Ord a => a -> a -> Ordering
`compare` (X
y', X
x')	-- N.B.: x is less significant than y.

instance Show Coordinates where
	showsPrec :: X -> Coordinates -> ShowS
showsPrec X
precedence MkCoordinates { getX :: Coordinates -> X
getX = X
x, getY :: Coordinates -> X
getY = X
y }	= X -> (X, X) -> ShowS
forall a. Show a => X -> a -> ShowS
showsPrec X
precedence (X
x, X
y)

instance Read Coordinates where
	readsPrec :: X -> ReadS Coordinates
readsPrec X
precedence String
s	= [
		(Coordinates
coordinates, String
remainder) |
			(Just Coordinates
coordinates, String
remainder)	<- ((X, X) -> Maybe Coordinates)
-> ((X, X), String) -> (Maybe Coordinates, String)
forall (a :: * -> * -> *) b c d.
Arrow a =>
a b c -> a (b, d) (c, d)
Control.Arrow.first ((X -> X -> Maybe Coordinates) -> (X, X) -> Maybe Coordinates
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry X -> X -> Maybe Coordinates
mkMaybeCoordinates) (((X, X), String) -> (Maybe Coordinates, String))
-> [((X, X), String)] -> [(Maybe Coordinates, String)]
forall a b. (a -> b) -> [a] -> [b]
`map` X -> ReadS (X, X)
forall a. Read a => X -> ReadS a
readsPrec X
precedence String
s
	 ] -- List-comprehension.

instance Property.Opposable.Opposable Coordinates where
	getOpposite :: Coordinates -> Coordinates
getOpposite MkCoordinates {
		getX :: Coordinates -> X
getX	= X
x,
		getY :: Coordinates -> X
getY	= X
y
	} = MkCoordinates :: X -> X -> Coordinates
MkCoordinates {
		getX :: X
getX	= X -> X
Cartesian.Abscissa.reflect X
x,
		getY :: X
getY	= X -> X
Cartesian.Ordinate.reflect X
y
	}

instance Property.Reflectable.ReflectableOnX Coordinates where
	reflectOnX :: Coordinates -> Coordinates
reflectOnX coordinates :: Coordinates
coordinates@MkCoordinates { getY :: Coordinates -> X
getY = X
y }	= Coordinates
coordinates { getY :: X
getY = X -> X
Cartesian.Ordinate.reflect X
y }

instance Property.Reflectable.ReflectableOnY Coordinates where
	reflectOnY :: Coordinates -> Coordinates
reflectOnY coordinates :: Coordinates
coordinates@MkCoordinates { getX :: Coordinates -> X
getX = X
x }	= Coordinates
coordinates { getX :: X
getX = X -> X
Cartesian.Abscissa.reflect X
x }

instance Property.Rotatable.Rotatable Coordinates where
	rotate90 :: Coordinates -> Coordinates
rotate90 MkCoordinates {
		getX :: Coordinates -> X
getX	= X
x,
		getY :: Coordinates -> X
getY	= X
y
	} = MkCoordinates :: X -> X -> Coordinates
MkCoordinates {
		getX :: X
getX	= X -> X
forall a b. (Integral a, Num b) => a -> b
fromIntegral (X -> X) -> X -> X
forall a b. (a -> b) -> a -> b
$! X -> X
Cartesian.Ordinate.reflect X
y,
		getY :: X
getY	= X -> X
forall a b. (Integral a, Num b) => a -> b
fromIntegral X
x
	} -- +90 degrees, i.e. anti-clockwise.

	rotate180 :: Coordinates -> Coordinates
rotate180	= Coordinates -> Coordinates
forall a. Opposable a => a -> a
Property.Opposable.getOpposite

	rotate270 :: Coordinates -> Coordinates
rotate270 MkCoordinates {
		getX :: Coordinates -> X
getX	= X
x,
		getY :: Coordinates -> X
getY	= X
y
	} = MkCoordinates :: X -> X -> Coordinates
MkCoordinates {
		getX :: X
getX	= X -> X
forall a b. (Integral a, Num b) => a -> b
fromIntegral X
y,
		getY :: X
getY	= X -> X
forall a b. (Integral a, Num b) => a -> b
fromIntegral (X -> X) -> X -> X
forall a b. (a -> b) -> a -> b
$! X -> X
Cartesian.Abscissa.reflect X
x
	} -- -90 degrees, i.e. clockwise.

-- | Constant.
topLeft :: Coordinates
topLeft :: Coordinates
topLeft = MkCoordinates :: X -> X -> Coordinates
MkCoordinates {
	getX :: X
getX	= X
Cartesian.Abscissa.xMin,
	getY :: X
getY	= X
Cartesian.Ordinate.yMax
}

-- | Constant.
bottomRight :: Coordinates
bottomRight :: Coordinates
bottomRight = MkCoordinates :: X -> X -> Coordinates
MkCoordinates {
	getX :: X
getX	= X
Cartesian.Abscissa.xMax,
	getY :: X
getY	= X
Cartesian.Ordinate.yMin
}

-- | The constant number of squares on the board.
nSquares :: Type.Count.NCoordinates
nSquares :: X
nSquares	= X -> X
forall a b. (Integral a, Num b) => a -> b
fromIntegral X
Cartesian.Abscissa.xLength X -> X -> X
forall a. Num a => a -> a -> a
* X -> X
forall a b. (Integral a, Num b) => a -> b
fromIntegral X
Cartesian.Ordinate.yLength

instance Property.FixedMembership.FixedMembership Coordinates where
	members :: [Coordinates]
members	= [
		MkCoordinates :: X -> X -> Coordinates
MkCoordinates {
			getX :: X
getX	= X
x,
			getY :: X
getY	= X
y
		} |
			X
y	<- [X]
Cartesian.Ordinate.yRange,
			X
x	<- [X]
Cartesian.Abscissa.xRange
	 ] -- List-comprehension.

-- | Predicate.
inBounds
	:: Type.Length.X	-- ^ Abscissa.
	-> Type.Length.Y	-- ^ Ordinate.
	-> Bool
inBounds :: X -> X -> Bool
inBounds X
x X
y	= X -> Bool
Cartesian.Abscissa.inBounds X
x Bool -> Bool -> Bool
&& X -> Bool
Cartesian.Ordinate.inBounds X
y

-- | Constructor.
mkCoordinates
	:: Type.Length.X	-- ^ Abscissa.
	-> Type.Length.Y	-- ^ Ordinate.
	-> Coordinates
mkCoordinates :: X -> X -> Coordinates
mkCoordinates X
x X
y	= Bool -> Coordinates -> Coordinates
forall a. (?callStack::CallStack) => Bool -> a -> a
Control.Exception.assert (X -> X -> Bool
inBounds X
x X
y) (Coordinates -> Coordinates) -> Coordinates -> Coordinates
forall a b. (a -> b) -> a -> b
$ X -> X -> Coordinates
MkCoordinates X
x X
y

-- | Safe constructor.
mkMaybeCoordinates
	:: Type.Length.X	-- ^ Abscissa.
	-> Type.Length.Y	-- ^ Ordinate.
	-> Maybe Coordinates
mkMaybeCoordinates :: X -> X -> Maybe Coordinates
mkMaybeCoordinates X
x X
y
	| X -> X -> Bool
inBounds X
x X
y	= Coordinates -> Maybe Coordinates
forall a. a -> Maybe a
Just MkCoordinates :: X -> X -> Coordinates
MkCoordinates { getX :: X
getX = X
x, getY :: X
getY = X
y }
	| Bool
otherwise	= Maybe Coordinates
forall a. Maybe a
Nothing

-- | The type of a function which changes one set of /coordinates/ to another.
type Transformation	= Coordinates -> Coordinates

{- |
	* Translate the specified /coordinates/ using the specified mapping.

	* CAVEAT: the caller must ensure that the results are legal.
-}
translate :: ((Type.Length.X, Type.Length.Y) -> (Type.Length.X, Type.Length.Y)) -> Transformation
translate :: ((X, X) -> (X, X)) -> Coordinates -> Coordinates
translate (X, X) -> (X, X)
transformation MkCoordinates {
	getX :: Coordinates -> X
getX	= X
x,
	getY :: Coordinates -> X
getY	= X
y
} = (X -> X -> Coordinates) -> (X, X) -> Coordinates
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry X -> X -> Coordinates
mkCoordinates ((X, X) -> Coordinates) -> (X, X) -> Coordinates
forall a b. (a -> b) -> a -> b
$ (X, X) -> (X, X)
transformation (X
x, X
y)

-- | Where legal, translate the specified /coordinates/.
maybeTranslate :: ((Type.Length.X, Type.Length.Y) -> (Type.Length.X, Type.Length.Y)) -> Coordinates -> Maybe Coordinates
maybeTranslate :: ((X, X) -> (X, X)) -> Coordinates -> Maybe Coordinates
maybeTranslate (X, X) -> (X, X)
transformation MkCoordinates {
	getX :: Coordinates -> X
getX	= X
x,
	getY :: Coordinates -> X
getY	= X
y
} = (X -> X -> Maybe Coordinates) -> (X, X) -> Maybe Coordinates
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry X -> X -> Maybe Coordinates
mkMaybeCoordinates ((X, X) -> Maybe Coordinates) -> (X, X) -> Maybe Coordinates
forall a b. (a -> b) -> a -> b
$ (X, X) -> (X, X)
transformation (X
x, X
y)

{- |
	* Translate the specified abscissa.

	* CAVEAT: the caller must ensure that the results are legal.
-}
translateX :: (Type.Length.X -> Type.Length.X) -> Transformation
translateX :: (X -> X) -> Coordinates -> Coordinates
translateX X -> X
transformation coordinates :: Coordinates
coordinates@MkCoordinates { getX :: Coordinates -> X
getX = X
x }	= Coordinates
coordinates { getX :: X
getX = (X -> X) -> X -> X
Cartesian.Abscissa.translate X -> X
transformation X
x }

-- | Where legal, translate the /x/-component of the specified /coordinates/.
maybeTranslateX
	:: (Type.Length.X -> Type.Length.X)	-- ^ Translation.
	-> Coordinates
	-> Maybe Coordinates
maybeTranslateX :: (X -> X) -> Coordinates -> Maybe Coordinates
maybeTranslateX X -> X
transformation coordinates :: Coordinates
coordinates@MkCoordinates { getX :: Coordinates -> X
getX = X
x }	= (\X
x' -> Coordinates
coordinates { getX :: X
getX = X
x' }) (X -> Coordinates) -> Maybe X -> Maybe Coordinates
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (X -> X) -> X -> Maybe X
Cartesian.Abscissa.maybeTranslate X -> X
transformation X
x

{- |
	* Translate the specified ordinate.

	* CAVEAT: the caller must ensure that the results are legal.
-}
translateY :: (Type.Length.Y -> Type.Length.Y) -> Transformation
translateY :: (X -> X) -> Coordinates -> Coordinates
translateY X -> X
transformation coordinates :: Coordinates
coordinates@MkCoordinates { getY :: Coordinates -> X
getY = X
y }	= Coordinates
coordinates { getY :: X
getY = (X -> X) -> X -> X
Cartesian.Ordinate.translate X -> X
transformation X
y }

-- | Where legal, translate the /y/-component of the specified /coordinates/.
maybeTranslateY
	:: (Type.Length.Y -> Type.Length.Y)	-- ^ Translation.
	-> Coordinates
	-> Maybe Coordinates
maybeTranslateY :: (X -> X) -> Coordinates -> Maybe Coordinates
maybeTranslateY X -> X
transformation coordinates :: Coordinates
coordinates@MkCoordinates { getY :: Coordinates -> X
getY = X
y }	= (\X
y' -> Coordinates
coordinates { getY :: X
getY = X
y' }) (X -> Coordinates) -> Maybe X -> Maybe Coordinates
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (X -> X) -> X -> Maybe X
Cartesian.Ordinate.maybeTranslate X -> X
transformation X
y

{- |
	* Construct /coordinates/ relative to 'minBound'.

	* CAVEAT: the caller must ensure that the results are legal.
-}
mkRelativeCoordinates :: ((Type.Length.X, Type.Length.Y) -> (Type.Length.X, Type.Length.Y)) -> Coordinates
mkRelativeCoordinates :: ((X, X) -> (X, X)) -> Coordinates
mkRelativeCoordinates	= (((X, X) -> (X, X)) -> Coordinates -> Coordinates
`translate` Coordinates
forall a. Bounded a => a
minBound)

{- |
	* Move one step towards the opponent.

	* CAVEAT: the caller must ensure that the results are legal.
-}
advance
	:: Colour.LogicalColour.LogicalColour	-- ^ The /logical colour/ of the /piece/ which is to advance.
	-> Transformation
advance :: LogicalColour -> Coordinates -> Coordinates
advance LogicalColour
logicalColour	= (X -> X) -> Coordinates -> Coordinates
translateY ((X -> X) -> Coordinates -> Coordinates)
-> (X -> X) -> Coordinates -> Coordinates
forall a b. (a -> b) -> a -> b
$ if LogicalColour -> Bool
Colour.LogicalColour.isBlack LogicalColour
logicalColour
	then X -> X
forall a. Enum a => a -> a
pred
	else X -> X
forall a. Enum a => a -> a
succ

-- | Where legal, move one step towards the opponent.
maybeAdvance
	:: Colour.LogicalColour.LogicalColour	-- ^ The /logical colour/ of the /piece/ which is to advance.
	-> Coordinates				-- ^ The location from which to advanced.
	-> Maybe Coordinates
maybeAdvance :: LogicalColour -> Coordinates -> Maybe Coordinates
maybeAdvance LogicalColour
logicalColour	= (X -> X) -> Coordinates -> Maybe Coordinates
maybeTranslateY ((X -> X) -> Coordinates -> Maybe Coordinates)
-> (X -> X) -> Coordinates -> Maybe Coordinates
forall a b. (a -> b) -> a -> b
$ if LogicalColour -> Bool
Colour.LogicalColour.isBlack LogicalColour
logicalColour
	then X -> X
forall a. Enum a => a -> a
pred
	else X -> X
forall a. Enum a => a -> a
succ

{- |
	* Move one step away from the opponent.

	* CAVEAT: the caller must ensure that the results are legal.
-}
retreat
	:: Colour.LogicalColour.LogicalColour	-- ^ The /logical colour/ of the /piece/ which is to retreat.
	-> Transformation
retreat :: LogicalColour -> Coordinates -> Coordinates
retreat	= LogicalColour -> Coordinates -> Coordinates
advance (LogicalColour -> Coordinates -> Coordinates)
-> (LogicalColour -> LogicalColour)
-> LogicalColour
-> Coordinates
-> Coordinates
forall b c a. (b -> c) -> (a -> b) -> a -> c
. LogicalColour -> LogicalColour
forall a. Opposable a => a -> a
Property.Opposable.getOpposite

-- | Where legal, move one step away from the opponent.
maybeRetreat
	:: Colour.LogicalColour.LogicalColour	-- ^ The /logical colour/ of the /piece/ which is to retreat.
	-> Coordinates				-- ^ The location from which to retreat.
	-> Maybe Coordinates
maybeRetreat :: LogicalColour -> Coordinates -> Maybe Coordinates
maybeRetreat	= LogicalColour -> Coordinates -> Maybe Coordinates
maybeAdvance (LogicalColour -> Coordinates -> Maybe Coordinates)
-> (LogicalColour -> LogicalColour)
-> LogicalColour
-> Coordinates
-> Maybe Coordinates
forall b c a. (b -> c) -> (a -> b) -> a -> c
. LogicalColour -> LogicalColour
forall a. Opposable a => a -> a
Property.Opposable.getOpposite

-- | Get the /coordinates/ immediately left & right.
getAdjacents :: Coordinates -> [Coordinates]
getAdjacents :: Coordinates -> [Coordinates]
getAdjacents coordinates :: Coordinates
coordinates@MkCoordinates { getX :: Coordinates -> X
getX = X
x }	= (X -> Coordinates) -> [X] -> [Coordinates]
forall a b. (a -> b) -> [a] -> [b]
map (\X
x' -> Coordinates
coordinates { getX :: X
getX = X
x' }) ([X] -> [Coordinates]) -> [X] -> [Coordinates]
forall a b. (a -> b) -> a -> b
$ X -> [X]
Cartesian.Abscissa.getAdjacents X
x

-- | Generates a line of /coordinates/, starting just after the specified source & proceeding in the specified /direction/ to the edge of the board.
extrapolate'
	:: Coordinates				-- ^ The point from which to start.
	-> Direction.Direction.Direction	-- ^ The direction in which to proceed.
	-> [Coordinates]
extrapolate' :: Coordinates -> Direction -> [Coordinates]
extrapolate' MkCoordinates {
	getX :: Coordinates -> X
getX	= X
x,
	getY :: Coordinates -> X
getY	= X
y
} Direction
direction = ([X] -> [X] -> [Coordinates]) -> ([X], [X]) -> [Coordinates]
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry ((X -> X -> Coordinates) -> [X] -> [X] -> [Coordinates]
forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith X -> X -> Coordinates
MkCoordinates) (([X], [X]) -> [Coordinates]) -> ([X], [X]) -> [Coordinates]
forall a b. (a -> b) -> a -> b
$ if Direction -> Bool
forall a. Orientated a => a -> Bool
Property.Orientated.isParallel Direction
direction
	then if Direction -> Bool
forall a. Orientated a => a -> Bool
Property.Orientated.isVertical Direction
direction
		then ([X], [X])
doVertical
		else ([X], [X])
doHorizontal
	else ([X], [X])
doDiagonal
	where
		xIncreasing, xDecreasing :: [Type.Length.X]
		xIncreasing :: [X]
xIncreasing	= [X -> X
forall a. Enum a => a -> a
succ X
x .. X
Cartesian.Abscissa.xMax]
		xDecreasing :: [X]
xDecreasing	= let startX :: X
startX = X -> X
forall a. Enum a => a -> a
pred X
x in X
startX X -> [X] -> [X]
`seq` [X
startX, X -> X
forall a. Enum a => a -> a
pred X
startX .. X
Cartesian.Abscissa.xMin]

		yIncreasing, yDecreasing :: [Type.Length.Y]
		yIncreasing :: [X]
yIncreasing	= [X -> X
forall a. Enum a => a -> a
succ X
y .. X
Cartesian.Ordinate.yMax]
		yDecreasing :: [X]
yDecreasing	= let startY :: X
startY = X -> X
forall a. Enum a => a -> a
pred X
y in X
startY X -> [X] -> [X]
`seq` [X
startY, X -> X
forall a. Enum a => a -> a
pred X
startY .. X
Cartesian.Ordinate.yMin]

		doVertical, doHorizontal, doDiagonal :: ([Type.Length.X], [Type.Length.Y])
		doVertical :: ([X], [X])
doVertical	= (
			X -> [X]
forall a. a -> [a]
repeat X
x,
			if Direction
direction Direction -> Direction -> Bool
forall a. Eq a => a -> a -> Bool
== Direction
Direction.Direction.s then [X]
yDecreasing else [X]
yIncreasing
		 ) -- Pair.
		doHorizontal :: ([X], [X])
doHorizontal	= (
			if Direction
direction Direction -> Direction -> Bool
forall a. Eq a => a -> a -> Bool
== Direction
Direction.Direction.w then [X]
xDecreasing else [X]
xIncreasing,
			X -> [X]
forall a. a -> [a]
repeat X
y
		 ) -- Pair.
		doDiagonal :: ([X], [X])
doDiagonal
			| Direction
direction Direction -> Direction -> Bool
forall a. Eq a => a -> a -> Bool
== Direction
Direction.Direction.sw	= ([X]
xDecreasing,	[X]
yDecreasing)
			| Direction
direction Direction -> Direction -> Bool
forall a. Eq a => a -> a -> Bool
== Direction
Direction.Direction.se	= ([X]
xIncreasing,	[X]
yDecreasing)
			| Direction
direction Direction -> Direction -> Bool
forall a. Eq a => a -> a -> Bool
== Direction
Direction.Direction.nw	= ([X]
xDecreasing,	[X]
yIncreasing)
			| Bool
otherwise {-NE-}			= ([X]
xIncreasing,	[X]
yIncreasing)

-- | A line of /coordinates/, qualified by an array-index into any 'ArrayByCoordinates'.
type QualifiedStraightLine	= [(Coordinates, Int)]

-- | The constant lists of (/coordinates/, array-index), extrapolated from every /coordinate/ in the /board/, in every /direction/.
extrapolationsByDirectionByCoordinates :: ArrayByCoordinates (Direction.Direction.ArrayByDirection QualifiedStraightLine)
extrapolationsByDirectionByCoordinates :: ArrayByCoordinates (ArrayByDirection QualifiedStraightLine)
extrapolationsByDirectionByCoordinates	= [ArrayByDirection QualifiedStraightLine]
-> ArrayByCoordinates (ArrayByDirection QualifiedStraightLine)
forall (a :: * -> * -> *) e. IArray a e => [e] -> a Coordinates e
listArrayByCoordinates ([ArrayByDirection QualifiedStraightLine]
 -> ArrayByCoordinates (ArrayByDirection QualifiedStraightLine))
-> [ArrayByDirection QualifiedStraightLine]
-> ArrayByCoordinates (ArrayByDirection QualifiedStraightLine)
forall a b. (a -> b) -> a -> b
$ (Coordinates -> ArrayByDirection QualifiedStraightLine)
-> [Coordinates] -> [ArrayByDirection QualifiedStraightLine]
forall a b. (a -> b) -> [a] -> [b]
map (
	\Coordinates
coordinates	-> [QualifiedStraightLine] -> ArrayByDirection QualifiedStraightLine
forall (a :: * -> * -> *) e. IArray a e => [e] -> a Direction e
Direction.Direction.listArrayByDirection ([QualifiedStraightLine] -> ArrayByDirection QualifiedStraightLine)
-> [QualifiedStraightLine]
-> ArrayByDirection QualifiedStraightLine
forall a b. (a -> b) -> a -> b
$ (Direction -> QualifiedStraightLine)
-> [Direction] -> [QualifiedStraightLine]
forall a b. (a -> b) -> [a] -> [b]
map (
		(Coordinates -> (Coordinates, X))
-> [Coordinates] -> QualifiedStraightLine
forall a b. (a -> b) -> [a] -> [b]
map (Coordinates -> Coordinates
forall a. a -> a
id (Coordinates -> Coordinates)
-> (Coordinates -> X) -> Coordinates -> (Coordinates, X)
forall (a :: * -> * -> *) b c c'.
Arrow a =>
a b c -> a b c' -> a b (c, c')
&&& Coordinates -> X
forall a. Enum a => a -> X
fromEnum) ([Coordinates] -> QualifiedStraightLine)
-> (Direction -> [Coordinates])
-> Direction
-> QualifiedStraightLine
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Coordinates -> Direction -> [Coordinates]
extrapolate' Coordinates
coordinates
	) [Direction]
forall a. FixedMembership a => [a]
Property.FixedMembership.members {-direction-}
 ) [Coordinates]
forall a. FixedMembership a => [a]
Property.FixedMembership.members {-coordinates-}

{- |
	* Generates a line of /coordinates/, starting just after the specified source & proceeding in the specified /direction/ to the edge of the board.

	* CAVEAT: this is a performance-hotspot (it's also responsible for the allocation of a third of the application's memory); refactor => re-profile.
	In consequence, it is typically automatically avoided using a rewrite-rule to lookup an array of the results from all possible calls.
-}
extrapolate
	:: Coordinates				-- ^ The point from which to start.
	-> Direction.Direction.Direction	-- ^ The direction in which to proceed.
	-> QualifiedStraightLine
extrapolate :: Coordinates -> Direction -> QualifiedStraightLine
extrapolate Coordinates
coordinates	Direction
direction	= ArrayByCoordinates (ArrayByDirection QualifiedStraightLine)
extrapolationsByDirectionByCoordinates ArrayByCoordinates (ArrayByDirection QualifiedStraightLine)
-> Coordinates -> ArrayByDirection QualifiedStraightLine
forall (a :: * -> * -> *) e i.
(IArray a e, Ix i) =>
a i e -> i -> e
! Coordinates
coordinates ArrayByDirection QualifiedStraightLine
-> Direction -> QualifiedStraightLine
forall (a :: * -> * -> *) e i.
(IArray a e, Ix i) =>
a i e -> i -> e
! Direction
direction

-- | Apply the specified function to each 'QualifiedStraightLine' extrapolated from the specified central hub, in each of the specified /direction/s.
applyAlongDirectionsFrom
	:: (QualifiedStraightLine -> [a])		-- ^ Individually map each 'QualifiedCoordinatesLine' extrapolated from the central hub.
	-> Coordinates					-- ^ The central hub from which to extrapolate.
	-> Maybe [Direction.Direction.Direction]	-- ^ The directions in which to extrapolate; 'Nothing' implies omnidirectional.
	-> [a]
applyAlongDirectionsFrom :: (QualifiedStraightLine -> [a])
-> Coordinates -> Maybe [Direction] -> [a]
applyAlongDirectionsFrom QualifiedStraightLine -> [a]
f Coordinates
from	= [a] -> ([Direction] -> [a]) -> Maybe [Direction] -> [a]
forall b a. b -> (a -> b) -> Maybe a -> b
Data.Maybe.maybe (
	(QualifiedStraightLine -> [a])
-> ArrayByDirection QualifiedStraightLine -> [a]
forall (t :: * -> *) m a.
(Foldable t, Monoid m) =>
(a -> m) -> t a -> m
Data.Foldable.foldMap QualifiedStraightLine -> [a]
f (ArrayByDirection QualifiedStraightLine -> [a])
-> ArrayByDirection QualifiedStraightLine -> [a]
forall a b. (a -> b) -> a -> b
$ ArrayByCoordinates (ArrayByDirection QualifiedStraightLine)
extrapolationsByDirectionByCoordinates ArrayByCoordinates (ArrayByDirection QualifiedStraightLine)
-> Coordinates -> ArrayByDirection QualifiedStraightLine
forall (a :: * -> * -> *) e i.
(IArray a e, Ix i) =>
a i e -> i -> e
! Coordinates
from -- Traverse all directions from this coordinate, without repeatedly converting direction to an array-index.
 ) (
	(Direction -> [a]) -> [Direction] -> [a]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap ((Direction -> [a]) -> [Direction] -> [a])
-> (Direction -> [a]) -> [Direction] -> [a]
forall a b. (a -> b) -> a -> b
$ QualifiedStraightLine -> [a]
f (QualifiedStraightLine -> [a])
-> (Direction -> QualifiedStraightLine) -> Direction -> [a]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (ArrayByCoordinates (ArrayByDirection QualifiedStraightLine)
extrapolationsByDirectionByCoordinates ArrayByCoordinates (ArrayByDirection QualifiedStraightLine)
-> Coordinates -> ArrayByDirection QualifiedStraightLine
forall (a :: * -> * -> *) e i.
(IArray a e, Ix i) =>
a i e -> i -> e
! Coordinates
from ArrayByDirection QualifiedStraightLine
-> Direction -> QualifiedStraightLine
forall (a :: * -> * -> *) e i.
(IArray a e, Ix i) =>
a i e -> i -> e
!)
 )

-- | The constant 'QualifiedStraightLine', between every permutation of source & valid destination on the /board/.
interpolationsByDestinationBySource :: ArrayByCoordinates (Map.Map Coordinates QualifiedStraightLine)
interpolationsByDestinationBySource :: ArrayByCoordinates (Map Coordinates QualifiedStraightLine)
interpolationsByDestinationBySource	= (ArrayByDirection QualifiedStraightLine
 -> Map Coordinates QualifiedStraightLine)
-> ArrayByCoordinates (ArrayByDirection QualifiedStraightLine)
-> ArrayByCoordinates (Map Coordinates QualifiedStraightLine)
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 (
	[(Coordinates, QualifiedStraightLine)]
-> Map Coordinates QualifiedStraightLine
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList ([(Coordinates, QualifiedStraightLine)]
 -> Map Coordinates QualifiedStraightLine)
-> (ArrayByDirection QualifiedStraightLine
    -> [(Coordinates, QualifiedStraightLine)])
-> ArrayByDirection QualifiedStraightLine
-> Map Coordinates QualifiedStraightLine
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (QualifiedStraightLine -> (Coordinates, QualifiedStraightLine))
-> [QualifiedStraightLine]
-> [(Coordinates, QualifiedStraightLine)]
forall a b. (a -> b) -> [a] -> [b]
map (
		(Coordinates, X) -> Coordinates
forall a b. (a, b) -> a
fst {-coordinates-} ((Coordinates, X) -> Coordinates)
-> (QualifiedStraightLine -> (Coordinates, X))
-> QualifiedStraightLine
-> Coordinates
forall b c a. (b -> c) -> (a -> b) -> a -> c
. QualifiedStraightLine -> (Coordinates, X)
forall a. [a] -> a
last {-destination-} (QualifiedStraightLine -> Coordinates)
-> (QualifiedStraightLine -> QualifiedStraightLine)
-> QualifiedStraightLine
-> (Coordinates, QualifiedStraightLine)
forall (a :: * -> * -> *) b c c'.
Arrow a =>
a b c -> a b c' -> a b (c, c')
&&& QualifiedStraightLine -> QualifiedStraightLine
forall a. a -> a
id {-interpolation-}
	) ([QualifiedStraightLine] -> [(Coordinates, QualifiedStraightLine)])
-> (ArrayByDirection QualifiedStraightLine
    -> [QualifiedStraightLine])
-> ArrayByDirection QualifiedStraightLine
-> [(Coordinates, QualifiedStraightLine)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (QualifiedStraightLine -> [QualifiedStraightLine])
-> [QualifiedStraightLine] -> [QualifiedStraightLine]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (
		[QualifiedStraightLine] -> [QualifiedStraightLine]
forall a. [a] -> [a]
tail {-remove null list-} ([QualifiedStraightLine] -> [QualifiedStraightLine])
-> (QualifiedStraightLine -> [QualifiedStraightLine])
-> QualifiedStraightLine
-> [QualifiedStraightLine]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. QualifiedStraightLine -> [QualifiedStraightLine]
forall a. [a] -> [[a]]
Data.List.inits	-- Generate all possible interpolations from this extrapolation.
	) ([QualifiedStraightLine] -> [QualifiedStraightLine])
-> (ArrayByDirection QualifiedStraightLine
    -> [QualifiedStraightLine])
-> ArrayByDirection QualifiedStraightLine
-> [QualifiedStraightLine]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ArrayByDirection QualifiedStraightLine -> [QualifiedStraightLine]
forall (t :: * -> *) a. Foldable t => t a -> [a]
Data.Foldable.toList
 ) ArrayByCoordinates (ArrayByDirection QualifiedStraightLine)
extrapolationsByDirectionByCoordinates	-- Derive from extrapolations.

{- |
	* Generates a 'QualifiedStraightLine' covering the half open interval @(source, destination]@.

	* CAVEAT: the destination-/coordinates/ must be a valid @Queen@'s /move/ from the source; so that all intermediate points lie on a square of the board.
-}
interpolate :: Coordinates -> Coordinates -> QualifiedStraightLine
interpolate :: Coordinates -> Coordinates -> QualifiedStraightLine
interpolate Coordinates
coordinatesSource Coordinates
coordinatesDestination	= ArrayByCoordinates (Map Coordinates QualifiedStraightLine)
interpolationsByDestinationBySource ArrayByCoordinates (Map Coordinates QualifiedStraightLine)
-> Coordinates -> Map Coordinates QualifiedStraightLine
forall (a :: * -> * -> *) e i.
(IArray a e, Ix i) =>
a i e -> i -> e
! Coordinates
coordinatesSource Map Coordinates QualifiedStraightLine
-> Coordinates -> QualifiedStraightLine
forall k a. Ord k => Map k a -> k -> a
Map.! Coordinates
coordinatesDestination

{- |
	* Measures the signed distance between source & destination /coordinates/.

	* N.B.: this isn't the typically /irrational/ distance a rational crow would fly, but rather the integral /x/ & /y/ components of that path.

	* CAVEAT: beware the potential fence-post error.
-}
measureDistance
	:: Coordinates	-- ^ Source.
	-> Coordinates	-- ^ Destination.
	-> (Type.Length.X, Type.Length.Y)
measureDistance :: Coordinates -> Coordinates -> (X, X)
measureDistance MkCoordinates {
	getX :: Coordinates -> X
getX	= X
x,
	getY :: Coordinates -> X
getY	= X
y
} MkCoordinates {
	getX :: Coordinates -> X
getX	= X
x',
	getY :: Coordinates -> X
getY	= X
y'
} = (X
x' X -> X -> X
forall a. Num a => a -> a -> a
- X
x, X
y' X -> X -> X
forall a. Num a => a -> a -> a
- X
y)

-- | The /logical colour/ of the specified square.
getLogicalColourOfSquare :: Coordinates -> Colour.LogicalColourOfSquare.LogicalColourOfSquare
getLogicalColourOfSquare :: Coordinates -> LogicalColourOfSquare
getLogicalColourOfSquare Coordinates
coordinates
	| (Bool -> Bool -> Bool) -> (Bool, Bool) -> Bool
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry Bool -> Bool -> Bool
forall a. Eq a => a -> a -> Bool
(==) ((Bool, Bool) -> Bool)
-> ((X, X) -> (Bool, Bool)) -> (X, X) -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (
		X -> Bool
forall a. Integral a => a -> Bool
even (X -> Bool) -> (X -> Bool) -> (X, X) -> (Bool, Bool)
forall (a :: * -> * -> *) b c b' c'.
Arrow a =>
a b c -> a b' c' -> a (b, b') (c, c')
*** X -> Bool
forall a. Integral a => a -> Bool
even
	) ((X, X) -> Bool) -> (X, X) -> Bool
forall a b. (a -> b) -> a -> b
$ Coordinates -> Coordinates -> (X, X)
measureDistance Coordinates
forall a. Bounded a => a
minBound Coordinates
coordinates	= LogicalColourOfSquare
Colour.LogicalColourOfSquare.black
	| Bool
otherwise					= LogicalColourOfSquare
Colour.LogicalColourOfSquare.white

-- | Whether the specified squares have the same /logical colour/.
areSquaresIsochromatic :: [Coordinates] -> Bool
areSquaresIsochromatic :: [Coordinates] -> Bool
areSquaresIsochromatic	= (Bool -> Bool -> Bool) -> (Bool, Bool) -> Bool
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry Bool -> Bool -> Bool
(||) ((Bool, Bool) -> Bool)
-> ([Coordinates] -> (Bool, Bool)) -> [Coordinates] -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((LogicalColourOfSquare -> Bool) -> [LogicalColourOfSquare] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all (LogicalColourOfSquare -> LogicalColourOfSquare -> Bool
forall a. Eq a => a -> a -> Bool
== LogicalColourOfSquare
forall a. Bounded a => a
minBound) ([LogicalColourOfSquare] -> Bool)
-> ([LogicalColourOfSquare] -> Bool)
-> [LogicalColourOfSquare]
-> (Bool, Bool)
forall (a :: * -> * -> *) b c c'.
Arrow a =>
a b c -> a b c' -> a b (c, c')
&&& (LogicalColourOfSquare -> Bool) -> [LogicalColourOfSquare] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all (LogicalColourOfSquare -> LogicalColourOfSquare -> Bool
forall a. Eq a => a -> a -> Bool
== LogicalColourOfSquare
forall a. Bounded a => a
maxBound)) ([LogicalColourOfSquare] -> (Bool, Bool))
-> ([Coordinates] -> [LogicalColourOfSquare])
-> [Coordinates]
-> (Bool, Bool)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Coordinates -> LogicalColourOfSquare)
-> [Coordinates] -> [LogicalColourOfSquare]
forall a b. (a -> b) -> [a] -> [b]
map Coordinates -> LogicalColourOfSquare
getLogicalColourOfSquare

{- |
	* Whether the specified coordinates lie within the open interval (source, destination); neither source nor destination lies in this interval.

	* CAVEAT: the source & destination must form a straight line of non-zero length (i.e. a valid Queen's move).
-}
isBetween
	:: Coordinates	-- ^ Source.
	-> Coordinates	-- ^ Destination.
	-> Coordinates	-- ^ Potential intermediary.
	-> Bool
isBetween :: Coordinates -> Coordinates -> Coordinates -> Bool
isBetween Coordinates
source Coordinates
destination Coordinates
intermediary	= Maybe (Coordinates, X) -> Bool
forall a. Maybe a -> Bool
Data.Maybe.isJust (Maybe (Coordinates, X) -> Bool)
-> (QualifiedStraightLine -> Maybe (Coordinates, X))
-> QualifiedStraightLine
-> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((Coordinates, X) -> Bool)
-> QualifiedStraightLine -> Maybe (Coordinates, X)
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
Data.List.find ((Coordinates -> Coordinates -> Bool
forall a. Eq a => a -> a -> Bool
== Coordinates
intermediary) (Coordinates -> Bool)
-> ((Coordinates, X) -> Coordinates) -> (Coordinates, X) -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Coordinates, X) -> Coordinates
forall a b. (a, b) -> a
fst {-coordinates-}) (QualifiedStraightLine -> Maybe (Coordinates, X))
-> (QualifiedStraightLine -> QualifiedStraightLine)
-> QualifiedStraightLine
-> Maybe (Coordinates, X)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. QualifiedStraightLine -> QualifiedStraightLine
forall a. [a] -> [a]
init {-drop the destination-} (QualifiedStraightLine -> Bool) -> QualifiedStraightLine -> Bool
forall a b. (a -> b) -> a -> b
$ Coordinates -> Coordinates -> QualifiedStraightLine
interpolate Coordinates
source Coordinates
destination

-- | The conventional starting /coordinates/ for the @King@ of the specified /logical colour/.
kingsStartingCoordinates :: Colour.LogicalColour.LogicalColour -> Coordinates
kingsStartingCoordinates :: LogicalColour -> Coordinates
kingsStartingCoordinates LogicalColour
logicalColour	= MkCoordinates :: X -> X -> Coordinates
MkCoordinates {
	getX :: X
getX	= X
Cartesian.Abscissa.kingsFile,
	getY :: X
getY	= LogicalColour -> X
Cartesian.Ordinate.firstRank LogicalColour
logicalColour
}

-- | The conventional starting /coordinates/ for each @Rook@.
rooksStartingCoordinates :: Colour.LogicalColour.LogicalColour -> [Coordinates]
rooksStartingCoordinates :: LogicalColour -> [Coordinates]
rooksStartingCoordinates LogicalColour
Colour.LogicalColour.Black	= [Coordinates
topLeft, Coordinates
forall a. Bounded a => a
maxBound]
rooksStartingCoordinates LogicalColour
_				= [Coordinates
forall a. Bounded a => a
minBound, Coordinates
bottomRight]

-- | Whether the specified /coordinates/ are where a @Pawn@ of the specified /logical colour/ starts.
isPawnsFirstRank :: Coordinates -> Colour.LogicalColour.LogicalColour -> Bool
isPawnsFirstRank :: Coordinates -> LogicalColour -> Bool
isPawnsFirstRank MkCoordinates { getY :: Coordinates -> X
getY = X
y }	= (X -> X -> Bool
forall a. Eq a => a -> a -> Bool
== X
y) (X -> Bool) -> (LogicalColour -> X) -> LogicalColour -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. LogicalColour -> X
Cartesian.Ordinate.pawnsFirstRank

-- | Whether a @Pawn@ is currently on the appropriate /rank/ to take an opponent's @Pawn@ /en-passant/.
isEnPassantRank :: Coordinates -> Colour.LogicalColour.LogicalColour -> Bool
isEnPassantRank :: Coordinates -> LogicalColour -> Bool
isEnPassantRank MkCoordinates { getY :: Coordinates -> X
getY = X
y }	= (X -> X -> Bool
forall a. Eq a => a -> a -> Bool
== X
y) (X -> Bool) -> (LogicalColour -> X) -> LogicalColour -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. LogicalColour -> X
Cartesian.Ordinate.enPassantRank

-- | A boxed array indexed by /coordinates/, of arbitrary elements.
type ArrayByCoordinates	= Data.Array.IArray.Array Coordinates

-- | An unboxed array indexed by /coordinates/, of fixed-size elements.
type UArrayByCoordinates	= Data.Array.Unboxed.UArray Coordinates

-- | Array-constructor from an ordered list of elements.
listArrayByCoordinates :: Data.Array.IArray.IArray a e => [e] -> a Coordinates e
listArrayByCoordinates :: [e] -> a Coordinates e
listArrayByCoordinates	= (Coordinates, Coordinates) -> [e] -> a Coordinates e
forall (a :: * -> * -> *) e i.
(IArray a e, Ix i) =>
(i, i) -> [e] -> a i e
Data.Array.IArray.listArray (Coordinates
forall a. Bounded a => a
minBound, Coordinates
forall a. Bounded a => a
maxBound)

-- | Array-constructor from an association-list.
arrayByCoordinates :: Data.Array.IArray.IArray a e => [(Coordinates, e)] -> a Coordinates e
arrayByCoordinates :: [(Coordinates, e)] -> a Coordinates e
arrayByCoordinates	= (Coordinates, Coordinates) -> [(Coordinates, e)] -> a Coordinates e
forall (a :: * -> * -> *) e i.
(IArray a e, Ix i) =>
(i, i) -> [(i, e)] -> a i e
Data.Array.IArray.array (Coordinates
forall a. Bounded a => a
minBound, Coordinates
forall a. Bounded a => a
maxBound)