Skip to content

Chess Logic

Chess.hs
module Chess where -- (3)!

import Data.Char (toUpper)
import Data.List (intercalate, intersperse)
import Data.Text qualified as T
import Witch (into)


data PieceType = Bishop | Rook | Knight | Pawn | King | Queen -- (1)!
  deriving (Eq, Show) -- (4)!

data Color = Black | White
  deriving (Eq, Show)

data Piece = Piece PieceType Color -- (2)!
  deriving (Eq, Show)

data File = A | B | C | D | E | F | G | H
  deriving (Eq, Ord, Show, Enum) -- (5)!

data Rank = One | Two | Three | Four | Five | Six | Seven | Eight
  deriving (Eq, Ord, Show, Enum)

data SquareState where -- (7)!
  Empty :: SquareState
  HasPiece :: Piece -> SquareState
  deriving (Show, Eq)

-- (15)!
newtype Board where -- (14)!
  Board :: (File -> Rank -> SquareState) -> Board

initBoard :: Board -- (6)!
initBoard = Board $ \f r -> Empty

getSquare :: Board -> (File, Rank) -> SquareState
getSquare (Board board) (f,r) = board f r

display :: Board -> T.Text
display (Board boardFunc) =
  into @T.Text $ -- (9)!
    intercalate "\n" $
      map (intersperse '|') $
        group 8 flatBoard
  where

    -- (10)!
    flatBoard =
      [ showSquare (boardFunc file rank)
        | file <- [A .. H],
          rank <- [One .. Eight] -- (8)!
      ]

    showPiece (Piece pieceType color) = letterCase color $ case pieceType of
      Bishop -> 'b'
      King -> 'k'
      Knight -> 'n'
      Queen -> 'q'
      Rook -> 'r'
      Pawn -> 'p'

    showSquare = \case
      -- (11)!
      Empty -> '_'
      HasPiece p -> showPiece p

    letterCase = \case
      Black -> toUpper -- (12)!
      White -> id -- (13)!

-- helper function: split list into chunks of size n
group :: Int -> [a] -> [[a]]
group _ [] = []
group n l = (take n l) : (group n (drop n l))
  1. A sum type.
  2. A product type.
  3. Module should have same name as file.
  4. Automatically derive Eq and Show typeclasses
  5. Enum allows for writing e.g. [A .. H]
  6. Alternative approaches include
  7. Another syntax for custom datatypes, know as GADT
  8. A list comprehension, as in Python.
  9. into is from the witch package, for type coercions. @Text indicates the type to coerce to.
  10. No need to write type signatures (although it's good practice) - Haskell will infer them for you.
  11. \case requires the LambdaCase extension which has been globally enabled in the project's .cabal file.
  12. If Black, return function to uppercase the character.
  13. If White, don't change the letter case. id can be useful.
  14. newtype is like the data keyword. See more about newtype here
  15. Alternative approaches to the Board type include.

Analysis

Because custom types are so easily made and expressive, it is typical in Haskell to create types that model your problem domain (here various chess-related types).

The central type is Board, which represents the state of a chessboard. We have chosen to directly represent the board's state as a function from a square (specified by file and rank) to the square's state.

A good example of type-based refactoring in Haskell is to change Board to an alternative representation and then fix the series of type errors that Haskell will show you in order to make the new Board type work across your project.

Alternative approaches

initBoard

initBoard :: Board
initBoard = Board $ \f r -> Empty
initBoard :: Board
initBoard = Board $ \_ _ -> Empty
initBoard :: Board
initBoard = Board $ curry $ const Empty

Tip

To understand how this works, lookup the types of const and curry on Hoogle (both are common and useful functions).

Then ascertain the type of const Empty with VSCode, namely:

  • (const Empty) :: (File, Rank) -> SquareState

Convince yourself that this is an appropriate input type for curry, and an appropriate output type for const.

Board

data SquareState where 
    Empty :: SquareState
    HasPiece :: Piece -> SquareState

newtype Board where
    Board :: (File -> Rank -> SquareState) -> Board
import qualified Data.Map as M
type Board = M.Map (File, Rank) Piece

Last update: February 14, 2023
Created: August 18, 2022

Comments