bishbosh-0.1.1.0: src-lib/BishBosh/Cartesian/Ordinate.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 the /y/-axis by which the /board/ is indexed.
* AKA the /rank/ of a piece.
* N.B. this coordinate-system is for internal use only, and doesn't attempt to replicate any standard chess-notation.
-}
module BishBosh.Cartesian.Ordinate(
-- * Types
-- ArrayByOrdinate,
-- * Constants
yOrigin,
yLength,
yMin,
yMax,
yBounds,
yRange,
-- * Functions
toIx,
fromIx,
firstRank,
lastRank,
pawnsFirstRank,
enPassantRank,
reflect,
translate,
maybeTranslate,
-- ** Constructors
-- listArrayByOrdinate,
-- ** Predicates
inBounds
) where
import qualified BishBosh.Attribute.LogicalColour as Attribute.LogicalColour
import qualified BishBosh.Cartesian.Abscissa as Cartesian.Abscissa
import qualified BishBosh.Data.Enum as Data.Enum
import qualified BishBosh.Property.Opposable as Property.Opposable
import qualified BishBosh.Type.Length as Type.Length
import qualified Control.Exception
import qualified Data.Array.IArray
-- | The position of the origin on the /y/-axis.
yOrigin :: Int
yOrigin = Cartesian.Abscissa.xOrigin -- N.B. it doesn't need to be the same.
-- | The constant length of the /y/-axis.
yLength :: Type.Length.Distance
yLength = Cartesian.Abscissa.xLength -- Because the board is square.
-- | The constant lower bound of ordinates.
yMin :: Enum y => y
yMin = toEnum yOrigin
-- | The constant upper bound of ordinates.
yMax :: Enum y => y
yMax = toEnum $ yOrigin + fromIntegral (pred {-fence-post-} yLength)
-- | The constant bounds of ordinates.
yBounds :: Enum y => (y, y)
yBounds = (yMin, yMax)
-- | The constant list of all ordinates.
yRange :: Enum y => [y]
yRange = uncurry enumFromTo yBounds
-- | Convert to an array-index.
toIx :: Enum y => y -> Int
{-# INLINE toIx #-}
toIx = subtract yOrigin . fromEnum
-- | Convert from an array-index.
fromIx :: Enum y => Int -> y
{-# INLINE fromIx #-}
fromIx = toEnum . (+ yOrigin)
-- | The /rank/ from which /piece/s conventionally start.
firstRank :: Enum y => Attribute.LogicalColour.LogicalColour -> y
firstRank Attribute.LogicalColour.Black = yMax
firstRank _ = yMin
-- | The final /rank/; i.e. the one on which a @Pawn@ is promoted.
lastRank :: Enum y => Attribute.LogicalColour.LogicalColour -> y
lastRank = firstRank . Property.Opposable.getOpposite
-- | The /rank/ from which @Pawn@s conventionally start.
pawnsFirstRank :: Enum y => Attribute.LogicalColour.LogicalColour -> y
{-# INLINE pawnsFirstRank #-}
pawnsFirstRank Attribute.LogicalColour.Black = pred yMax
pawnsFirstRank _ = succ yMin
-- | The /rank/ from which a @Pawn@ may capture /en-passant/.
enPassantRank :: Enum y => Attribute.LogicalColour.LogicalColour -> y
{-# INLINE enPassantRank #-}
enPassantRank Attribute.LogicalColour.Black = toEnum $ yOrigin + 3
enPassantRank _ = toEnum $ yOrigin + 4
-- | Reflects about the mid-point of the axis.
reflect :: Enum y => y -> y
reflect = Data.Enum.translate $ (
+ (2 * yOrigin + fromIntegral (pred yLength))
) . negate
-- | Predicate.
inBounds :: (Enum y, Ord y) => y -> Bool
{-# INLINE inBounds #-}
inBounds y = y >= yMin && y <= yMax
-- | Translate the specified ordinate.
translate :: (Enum y, Ord y) => (y -> y) -> y -> y
translate transformation = (\y -> Control.Exception.assert (inBounds y) y) . transformation
-- | Where legal, translate the specified ordinate.
maybeTranslate :: (Enum y, Ord y) => (y -> y) -> y -> Maybe y
maybeTranslate transformation = (
\y -> if inBounds y
then Just y
else Nothing
) . transformation
-- | A boxed array indexed by /coordinates/, of arbitrary elements.
type ArrayByOrdinate y = Data.Array.IArray.Array {-Boxed-} y
-- | Array-constructor.
listArrayByOrdinate :: (
Data.Array.IArray.IArray a e,
Data.Array.IArray.Ix y,
Enum y
) => [e] -> a y e
listArrayByOrdinate = Data.Array.IArray.listArray yBounds