packages feed

chessica-0.1.0.0: src/Chess/Board/Position.hs

-----------------------------------------------------------------------------
-- |
-- Module      : Chess.Board.Position
-- Copyright   : (c) Michael Szvetits, 2023
-- License     : BSD-3-Clause (see the file LICENSE)
-- Maintainer  : typedbyte@qualified.name
-- Stability   : stable
-- Portability : portable
--
-- Types and functions to represent and manipulate positions on a chess board.
-----------------------------------------------------------------------------
module Chess.Board.Position
  ( -- * Representing Positions
    Position(row, column)
  , mkPosition
  , boundedPosition
    -- * Manipulating Positions
  , offset
  , boundedOffset
  ) where

-- base
import Control.Applicative (Alternative, empty)

import Chess.Board.Direction (Direction(rowDelta, columnDelta))

-- | Represents a position on the chess board.
data Position = Position
  { row :: Int
    -- ^ The row of the position, where 0 is row 1.
  , column :: Int
    -- ^ The column of the position, where 0 is column A.
  }
  deriving (Eq, Ord, Read, Show)

-- | Creates a position from row and column indices.
mkPosition
  :: Alternative f
  => Int        -- ^ The row of the position, where 0 is the row labelled 1.
  -> Int        -- ^ The column of the position, where 0 is the column labelled A.
  -> f Position -- ^ The position, if it is within the bounds of the chess board.
mkPosition row column
  | between 0 7 row && between 0 7 column =
      pure $ Position row column
  | otherwise =
      empty
  where
    between low high value =
      low <= value && value <= high

-- | Creates a position from row and column indices.
boundedPosition
  :: Int      -- ^ The row of the position, where 0 is the row labelled 1.
  -> Int      -- ^ The column of the position, where 0 is the column labelled A.
  -> Position -- ^ The position, where out-of-bounds indices are limited to the valid range.
boundedPosition row column =
  Position
    ( clamp 0 7 row )
    ( clamp 0 7 column)
  where
    clamp lower upper value
      | value < lower = lower
      | value > upper = upper
      | otherwise     = value

-- | Adds an offset to a position, yielding a new position.
offset
  :: Alternative f
  => Direction  -- ^ The offset to be added to the position.
  -> Position   -- ^ The original position.
  -> f Position -- ^ The new position, if it is within the bounds of the chess board.
offset direction position =
  mkPosition
    ( position.row + direction.rowDelta )
    ( position.column + direction.columnDelta )

-- | Adds an offset to a position, yielding a new position.
boundedOffset
  :: Direction -- ^ The offset to be added to the position.
  -> Position  -- ^ The original position.
  -> Position  -- ^ The new position, where out-of-bounds indices are limited to the valid range.
boundedOffset direction position =
  boundedPosition
    ( position.row + direction.rowDelta )
    ( position.column + direction.columnDelta )