bishbosh-0.1.3.0: src-lib/BishBosh/Direction/Direction.hs
{-
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 (Eq, Ord)
instance Control.DeepSeq.NFData Direction where
rnf (MkDirection d) = Control.DeepSeq.rnf ||| Control.DeepSeq.rnf $ d
instance Show Direction where
showsPrec precedence (MkDirection d) = showsPrec precedence ||| showsPrec precedence $ d
instance Read Direction where
readsPrec precedence string = let
s' = Data.List.Extra.trimStart string
in case readsPrec precedence s' of
[(diagonal, s'')] -> [(MkDirection $ Right diagonal, s'')]
_ -> case readsPrec precedence s' of
[(parallel, s'')] -> [(MkDirection $ Left parallel, s'')]
_ -> [] -- No parse.
instance Property.Opposable.Opposable Direction where
getOpposite (MkDirection d) = MkDirection $ (Property.Opposable.getOpposite +++ Property.Opposable.getOpposite) d
instance Property.Orientated.Orientated Direction where
isVertical (MkDirection (Left p)) = Property.Orientated.isVertical p
isVertical _ = False
isHorizontal (MkDirection (Left p)) = Property.Orientated.isHorizontal p
isHorizontal _ = False
isParallel (MkDirection (Left _)) = True
isParallel _ = False
isDiagonal (MkDirection (Right _)) = True
isDiagonal _ = False
isStraight = const True
instance Property.Reflectable.ReflectableOnX Direction where
reflectOnX (MkDirection d) = MkDirection $ (Property.Reflectable.reflectOnX +++ Property.Reflectable.reflectOnX) d
instance Property.Reflectable.ReflectableOnY Direction where
reflectOnY (MkDirection d) = MkDirection $ (Property.Reflectable.reflectOnY +++ Property.Reflectable.reflectOnY) d
instance Property.Rotatable.Rotatable Direction where
rotate90 direction
| Property.Orientated.isParallel direction = rotateParallel
| otherwise = rotateDiagonal
where
rotateParallel
| Property.Orientated.isVertical direction = rotateVertical
| otherwise = rotateHorizontal
where
rotateVertical
| direction == s = e
| otherwise {-n-} = w
rotateHorizontal
| direction == w = s
| otherwise {-e-} = n
rotateDiagonal
| direction == sw = se
| direction == se = ne
| direction == nw = sw
| otherwise {-ne-} = nw
rotate180 = Property.Opposable.getOpposite
instance Property.FixedMembership.FixedMembership Direction where
members = parallels ++ diagonals
instance Bounded Direction where
minBound = head Property.FixedMembership.members
maxBound = last Property.FixedMembership.members
instance Data.Array.IArray.Ix Direction where
range _ = Property.FixedMembership.members
inRange _ _ = True
index _ (MkDirection d) = fromEnum ||| (+ fromIntegral Direction.Parallel.nParallels) . fromEnum $ d
-- | Constant directions.
parallels :: [Direction]
s, n, w, e :: Direction
parallels@[s, n, w, e] = map (MkDirection . Left) Property.FixedMembership.members {-parallels-}
-- | Constant directions.
diagonals :: [Direction]
sw, se, nw, ne :: Direction
diagonals@[sw, se, nw, ne] = map (MkDirection . Right) 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 = map (id &&& Property.Opposable.getOpposite) [n, ne, e, se]
-- | The /direction/s in which a @Pawn@ can attack.
attackDirectionsForPawn :: Colour.LogicalColour.LogicalColour -> [Direction]
attackDirectionsForPawn Colour.LogicalColour.Black = [sw, se]
attackDirectionsForPawn _ = [nw, ne]
-- | Whether the two /direction/s specified, are either parallel or anti-parallel.
areAligned :: Direction -> Direction -> Bool
areAligned l = uncurry (||) . ((== l) &&& (== l) . 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 = Data.Array.IArray.listArray (minBound, maxBound)