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

	* Describes a /direction/.

	* The resolution of the measure is merely sufficient for Chess-moves.

	* The IO-format is based on 8 points of the compass.
-}

module BishBosh.Direction.Direction(
-- * Types
-- ** Type-synonyms
	ArrayByDirection,
-- ** Data-types
	Direction(),
-- * Constants
	parallels,
	s,
	n,
	w,
	e,
	diagonals,
	sw,
	se,
	nw,
	ne,
	opposites,
-- * Functions
	attackDirectionsForPawn,
	listArrayByDirection,
-- ** Predicates
	areAligned
) where

import			Control.Arrow((&&&), (|||), (+++))
import qualified	BishBosh.Colour.LogicalColour		as Colour.LogicalColour
import qualified	BishBosh.Direction.Diagonal		as Direction.Diagonal
import qualified	BishBosh.Direction.Parallel		as Direction.Parallel
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	Control.DeepSeq
import qualified	Data.Array.IArray
import qualified	Data.List.Extra

-- | Define the /direction/ in which a piece moves relative to the board's edges.
newtype Direction	= MkDirection (Either Direction.Parallel.Parallel Direction.Diagonal.Diagonal) deriving (Direction -> Direction -> Bool
(Direction -> Direction -> Bool)
-> (Direction -> Direction -> Bool) -> Eq Direction
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Direction -> Direction -> Bool
$c/= :: Direction -> Direction -> Bool
== :: Direction -> Direction -> Bool
$c== :: Direction -> Direction -> Bool
Eq, Eq Direction
Eq Direction
-> (Direction -> Direction -> Ordering)
-> (Direction -> Direction -> Bool)
-> (Direction -> Direction -> Bool)
-> (Direction -> Direction -> Bool)
-> (Direction -> Direction -> Bool)
-> (Direction -> Direction -> Direction)
-> (Direction -> Direction -> Direction)
-> Ord Direction
Direction -> Direction -> Bool
Direction -> Direction -> Ordering
Direction -> Direction -> Direction
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 :: Direction -> Direction -> Direction
$cmin :: Direction -> Direction -> Direction
max :: Direction -> Direction -> Direction
$cmax :: Direction -> Direction -> Direction
>= :: Direction -> Direction -> Bool
$c>= :: Direction -> Direction -> Bool
> :: Direction -> Direction -> Bool
$c> :: Direction -> Direction -> Bool
<= :: Direction -> Direction -> Bool
$c<= :: Direction -> Direction -> Bool
< :: Direction -> Direction -> Bool
$c< :: Direction -> Direction -> Bool
compare :: Direction -> Direction -> Ordering
$ccompare :: Direction -> Direction -> Ordering
$cp1Ord :: Eq Direction
Ord)

instance Control.DeepSeq.NFData Direction where
	rnf :: Direction -> ()
rnf (MkDirection Either Parallel Diagonal
d)	= Parallel -> ()
forall a. NFData a => a -> ()
Control.DeepSeq.rnf (Parallel -> ())
-> (Diagonal -> ()) -> Either Parallel Diagonal -> ()
forall (a :: * -> * -> *) b d c.
ArrowChoice a =>
a b d -> a c d -> a (Either b c) d
||| Diagonal -> ()
forall a. NFData a => a -> ()
Control.DeepSeq.rnf (Either Parallel Diagonal -> ()) -> Either Parallel Diagonal -> ()
forall a b. (a -> b) -> a -> b
$ Either Parallel Diagonal
d

instance Show Direction where
	showsPrec :: Int -> Direction -> ShowS
showsPrec Int
precedence (MkDirection Either Parallel Diagonal
d)	= Int -> Parallel -> ShowS
forall a. Show a => Int -> a -> ShowS
showsPrec Int
precedence (Parallel -> ShowS)
-> (Diagonal -> ShowS) -> Either Parallel Diagonal -> ShowS
forall (a :: * -> * -> *) b d c.
ArrowChoice a =>
a b d -> a c d -> a (Either b c) d
||| Int -> Diagonal -> ShowS
forall a. Show a => Int -> a -> ShowS
showsPrec Int
precedence (Either Parallel Diagonal -> ShowS)
-> Either Parallel Diagonal -> ShowS
forall a b. (a -> b) -> a -> b
$ Either Parallel Diagonal
d

instance Read Direction where
	readsPrec :: Int -> ReadS Direction
readsPrec Int
precedence String
string	= let
		s' :: String
s'	= ShowS
Data.List.Extra.trimStart String
string
	 in case Int -> ReadS Diagonal
forall a. Read a => Int -> ReadS a
readsPrec Int
precedence String
s' of
		[(Diagonal
diagonal, String
s'')]	-> [(Either Parallel Diagonal -> Direction
MkDirection (Either Parallel Diagonal -> Direction)
-> Either Parallel Diagonal -> Direction
forall a b. (a -> b) -> a -> b
$ Diagonal -> Either Parallel Diagonal
forall a b. b -> Either a b
Right Diagonal
diagonal, String
s'')]
		[(Diagonal, String)]
_			-> case Int -> ReadS Parallel
forall a. Read a => Int -> ReadS a
readsPrec Int
precedence String
s' of
			[(Parallel
parallel, String
s'')]	-> [(Either Parallel Diagonal -> Direction
MkDirection (Either Parallel Diagonal -> Direction)
-> Either Parallel Diagonal -> Direction
forall a b. (a -> b) -> a -> b
$ Parallel -> Either Parallel Diagonal
forall a b. a -> Either a b
Left Parallel
parallel, String
s'')]
			[(Parallel, String)]
_			-> []	-- No parse.

instance Property.Opposable.Opposable Direction where
	getOpposite :: Direction -> Direction
getOpposite (MkDirection Either Parallel Diagonal
d)	= Either Parallel Diagonal -> Direction
MkDirection (Either Parallel Diagonal -> Direction)
-> Either Parallel Diagonal -> Direction
forall a b. (a -> b) -> a -> b
$ (Parallel -> Parallel
forall a. Opposable a => a -> a
Property.Opposable.getOpposite (Parallel -> Parallel)
-> (Diagonal -> Diagonal)
-> Either Parallel Diagonal
-> Either Parallel Diagonal
forall (a :: * -> * -> *) b c b' c'.
ArrowChoice a =>
a b c -> a b' c' -> a (Either b b') (Either c c')
+++ Diagonal -> Diagonal
forall a. Opposable a => a -> a
Property.Opposable.getOpposite) Either Parallel Diagonal
d

instance Property.Orientated.Orientated Direction where
	isVertical :: Direction -> Bool
isVertical (MkDirection (Left Parallel
p))	= Parallel -> Bool
forall a. Orientated a => a -> Bool
Property.Orientated.isVertical Parallel
p
	isVertical Direction
_				= Bool
False

	isHorizontal :: Direction -> Bool
isHorizontal (MkDirection (Left Parallel
p))	= Parallel -> Bool
forall a. Orientated a => a -> Bool
Property.Orientated.isHorizontal Parallel
p
	isHorizontal Direction
_				= Bool
False

	isParallel :: Direction -> Bool
isParallel (MkDirection (Left Parallel
_))	= Bool
True
	isParallel Direction
_				= Bool
False

	isDiagonal :: Direction -> Bool
isDiagonal (MkDirection (Right Diagonal
_))	= Bool
True
	isDiagonal Direction
_				= Bool
False

	isStraight :: Direction -> Bool
isStraight				= Bool -> Direction -> Bool
forall a b. a -> b -> a
const Bool
True

instance Property.Reflectable.ReflectableOnX Direction where
	reflectOnX :: Direction -> Direction
reflectOnX (MkDirection Either Parallel Diagonal
d)	= Either Parallel Diagonal -> Direction
MkDirection (Either Parallel Diagonal -> Direction)
-> Either Parallel Diagonal -> Direction
forall a b. (a -> b) -> a -> b
$ (Parallel -> Parallel
forall a. ReflectableOnX a => a -> a
Property.Reflectable.reflectOnX (Parallel -> Parallel)
-> (Diagonal -> Diagonal)
-> Either Parallel Diagonal
-> Either Parallel Diagonal
forall (a :: * -> * -> *) b c b' c'.
ArrowChoice a =>
a b c -> a b' c' -> a (Either b b') (Either c c')
+++ Diagonal -> Diagonal
forall a. ReflectableOnX a => a -> a
Property.Reflectable.reflectOnX) Either Parallel Diagonal
d

instance Property.Reflectable.ReflectableOnY Direction where
	reflectOnY :: Direction -> Direction
reflectOnY (MkDirection Either Parallel Diagonal
d)	= Either Parallel Diagonal -> Direction
MkDirection (Either Parallel Diagonal -> Direction)
-> Either Parallel Diagonal -> Direction
forall a b. (a -> b) -> a -> b
$ (Parallel -> Parallel
forall a. ReflectableOnY a => a -> a
Property.Reflectable.reflectOnY (Parallel -> Parallel)
-> (Diagonal -> Diagonal)
-> Either Parallel Diagonal
-> Either Parallel Diagonal
forall (a :: * -> * -> *) b c b' c'.
ArrowChoice a =>
a b c -> a b' c' -> a (Either b b') (Either c c')
+++ Diagonal -> Diagonal
forall a. ReflectableOnY a => a -> a
Property.Reflectable.reflectOnY) Either Parallel Diagonal
d

instance Property.Rotatable.Rotatable Direction where
	rotate90 :: Direction -> Direction
rotate90 Direction
direction
		| Direction -> Bool
forall a. Orientated a => a -> Bool
Property.Orientated.isParallel Direction
direction	= Direction
rotateParallel
		| Bool
otherwise					= Direction
rotateDiagonal
		where
			rotateParallel :: Direction
rotateParallel
				| Direction -> Bool
forall a. Orientated a => a -> Bool
Property.Orientated.isVertical Direction
direction	= Direction
rotateVertical
				| Bool
otherwise					= Direction
rotateHorizontal
				where
					rotateVertical :: Direction
rotateVertical
						| Direction
direction Direction -> Direction -> Bool
forall a. Eq a => a -> a -> Bool
== Direction
s	= Direction
e
						| Bool
otherwise {-n-}	= Direction
w

					rotateHorizontal :: Direction
rotateHorizontal
						| Direction
direction Direction -> Direction -> Bool
forall a. Eq a => a -> a -> Bool
== Direction
w	= Direction
s
						| Bool
otherwise {-e-}	= Direction
n
			rotateDiagonal :: Direction
rotateDiagonal
				| Direction
direction Direction -> Direction -> Bool
forall a. Eq a => a -> a -> Bool
== Direction
sw	= Direction
se
				| Direction
direction Direction -> Direction -> Bool
forall a. Eq a => a -> a -> Bool
== Direction
se	= Direction
ne
				| Direction
direction Direction -> Direction -> Bool
forall a. Eq a => a -> a -> Bool
== Direction
nw	= Direction
sw
				| Bool
otherwise {-ne-}	= Direction
nw

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

instance Property.FixedMembership.FixedMembership Direction where
	members :: [Direction]
members	= [Direction]
parallels [Direction] -> [Direction] -> [Direction]
forall a. [a] -> [a] -> [a]
++ [Direction]
diagonals

instance Bounded Direction where
	minBound :: Direction
minBound	= [Direction] -> Direction
forall a. [a] -> a
head [Direction]
forall a. FixedMembership a => [a]
Property.FixedMembership.members
	maxBound :: Direction
maxBound	= [Direction] -> Direction
forall a. [a] -> a
last [Direction]
forall a. FixedMembership a => [a]
Property.FixedMembership.members

instance Data.Array.IArray.Ix Direction where
	range :: (Direction, Direction) -> [Direction]
range (Direction, Direction)
_			= [Direction]
forall a. FixedMembership a => [a]
Property.FixedMembership.members
	inRange :: (Direction, Direction) -> Direction -> Bool
inRange (Direction, Direction)
_ Direction
_		= Bool
True
	index :: (Direction, Direction) -> Direction -> Int
index (Direction, Direction)
_ (MkDirection Either Parallel Diagonal
d)	= Parallel -> Int
forall a. Enum a => a -> Int
fromEnum (Parallel -> Int)
-> (Diagonal -> Int) -> Either Parallel Diagonal -> Int
forall (a :: * -> * -> *) b d c.
ArrowChoice a =>
a b d -> a c d -> a (Either b c) d
||| (Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
Direction.Parallel.nParallels) (Int -> Int) -> (Diagonal -> Int) -> Diagonal -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Diagonal -> Int
forall a. Enum a => a -> Int
fromEnum (Either Parallel Diagonal -> Int)
-> Either Parallel Diagonal -> Int
forall a b. (a -> b) -> a -> b
$ Either Parallel Diagonal
d

-- | Constant directions.
parallels	:: [Direction]
s, n, w, e	:: Direction
parallels :: [Direction]
parallels@[Direction
s, Direction
n, Direction
w, Direction
e]	= (Parallel -> Direction) -> [Parallel] -> [Direction]
forall a b. (a -> b) -> [a] -> [b]
map (Either Parallel Diagonal -> Direction
MkDirection (Either Parallel Diagonal -> Direction)
-> (Parallel -> Either Parallel Diagonal) -> Parallel -> Direction
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Parallel -> Either Parallel Diagonal
forall a b. a -> Either a b
Left) [Parallel]
forall a. FixedMembership a => [a]
Property.FixedMembership.members {-parallels-}

-- | Constant directions.
diagonals	:: [Direction]
sw, se, nw, ne	:: Direction
diagonals :: [Direction]
diagonals@[Direction
sw, Direction
se, Direction
nw, Direction
ne]	= (Diagonal -> Direction) -> [Diagonal] -> [Direction]
forall a b. (a -> b) -> [a] -> [b]
map (Either Parallel Diagonal -> Direction
MkDirection (Either Parallel Diagonal -> Direction)
-> (Diagonal -> Either Parallel Diagonal) -> Diagonal -> Direction
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Diagonal -> Either Parallel Diagonal
forall a b. b -> Either a b
Right) [Diagonal]
forall a. FixedMembership a => [a]
Property.FixedMembership.members

{- |
	* Returns a list of /direction/s, each paired with its anti-parallel.

	* CAVEAT: each /direction/ only appears once in the list, on an arbitrary side of a pair.
-}
opposites :: [(Direction, Direction)]
opposites :: [(Direction, Direction)]
opposites	= (Direction -> (Direction, Direction))
-> [Direction] -> [(Direction, Direction)]
forall a b. (a -> b) -> [a] -> [b]
map (Direction -> Direction
forall a. a -> a
id (Direction -> Direction)
-> (Direction -> Direction) -> Direction -> (Direction, Direction)
forall (a :: * -> * -> *) b c c'.
Arrow a =>
a b c -> a b c' -> a b (c, c')
&&& Direction -> Direction
forall a. Opposable a => a -> a
Property.Opposable.getOpposite) [Direction
n, Direction
ne, Direction
e, Direction
se]

-- | The /direction/s in which a @Pawn@ can attack.
attackDirectionsForPawn :: Colour.LogicalColour.LogicalColour -> [Direction]
attackDirectionsForPawn :: LogicalColour -> [Direction]
attackDirectionsForPawn LogicalColour
Colour.LogicalColour.Black	= [Direction
sw, Direction
se]
attackDirectionsForPawn LogicalColour
_				= [Direction
nw, Direction
ne]

-- | Whether the two /direction/s specified, are either parallel or anti-parallel.
areAligned :: Direction -> Direction -> Bool
areAligned :: Direction -> Direction -> Bool
areAligned Direction
l	= (Bool -> Bool -> Bool) -> (Bool, Bool) -> Bool
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry Bool -> Bool -> Bool
(||) ((Bool, Bool) -> Bool)
-> (Direction -> (Bool, Bool)) -> Direction -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((Direction -> Direction -> Bool
forall a. Eq a => a -> a -> Bool
== Direction
l) (Direction -> Bool)
-> (Direction -> Bool) -> Direction -> (Bool, Bool)
forall (a :: * -> * -> *) b c c'.
Arrow a =>
a b c -> a b c' -> a b (c, c')
&&& (Direction -> Direction -> Bool
forall a. Eq a => a -> a -> Bool
== Direction
l) (Direction -> Bool)
-> (Direction -> Direction) -> Direction -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Direction -> Direction
forall a. Opposable a => a -> a
Property.Opposable.getOpposite)

-- | A boxed array indexed by /direction/, of arbitrary elements.
type ArrayByDirection	= Data.Array.IArray.Array {-Boxed-} Direction

-- | Array-constructor.
listArrayByDirection :: Data.Array.IArray.IArray a e => [e] -> a Direction e
listArrayByDirection :: [e] -> a Direction e
listArrayByDirection	= (Direction, Direction) -> [e] -> a Direction e
forall (a :: * -> * -> *) e i.
(IArray a e, Ix i) =>
(i, i) -> [e] -> a i e
Data.Array.IArray.listArray (Direction
forall a. Bounded a => a
minBound, Direction
forall a. Bounded a => a
maxBound)