packages feed

chessica-0.1.0.0: src/Chess/Rulebook/Standard.hs

{-# LANGUAGE OverloadedLists #-}
-----------------------------------------------------------------------------
-- |
-- Module      : Chess.Rulebook.Standard
-- 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 the standard rulebook for chess games.
-----------------------------------------------------------------------------
module Chess.Rulebook.Standard (standardRulebook) where

-- base
import Control.Monad  (guard)
import Prelude hiding (lookup)

import Chess.Board                      (Board, empty, lookup, replace)
import Chess.Board.PlacedPiece          (PlacedPiece(PlacedPiece), placedPiece)
import Chess.Board.Position             (boundedPosition)
import Chess.Color                      (Color(..))
import Chess.Exception                  (assumeRight)
import Chess.Game                       (Game(..), Update(..), execute)
import Chess.Game.Command               (atomic, endTurn)
import Chess.Piece                      (Piece(Piece), PieceType(..))
import Chess.Player                     (Player(Player))
import Chess.Rulebook                   (Rulebook(..))
import Chess.Rulebook.Standard.Check    (checked)
import Chess.Rulebook.Standard.Movement (movements)
import Chess.Rulebook.Standard.Status   (status)
import Chess.Some                       (Some(Some))

-- | The standard rulebook for chess games.
standardRulebook :: Rulebook
standardRulebook =
  Rulebook
  { newGame =
      Game
        { board         = newBoard
        , activePlayer  = Player White
        , passivePlayer = Player Black
        , lastUpdate    = Nothing
        }
  , status = status
  , updates =
      \position game -> do
        Some piece <- lookup position game.board
        move <- movements piece game
        let turn = atomic [move, endTurn]
        newGame <- assumeRight $ execute turn game
        guard $ not (checked newGame.passivePlayer newGame.board)
        pure (Update newGame turn)
  }

baseLine :: Color -> [Some PlacedPiece]
baseLine color =
  let
    rook   = Piece Rook color
    knight = Piece Knight color
    bishop = Piece Bishop color
    queen  = Piece Queen color
    king   = Piece King color
    row    = if color == White then 0 else 7
  in
    [ placedPiece (boundedPosition row 0) rook
    , placedPiece (boundedPosition row 1) knight
    , placedPiece (boundedPosition row 2) bishop
    , placedPiece (boundedPosition row 3) queen
    , placedPiece (boundedPosition row 4) king
    , placedPiece (boundedPosition row 5) bishop
    , placedPiece (boundedPosition row 6) knight
    , placedPiece (boundedPosition row 7) rook
    ]

pawnLine :: Color -> [Some PlacedPiece]
pawnLine color =
  let
    pawn = Piece Pawn color
    row  = if color == White then 1 else 6
  in
    fmap
      ( \column -> placedPiece (boundedPosition row column) pawn )
      [ 0..7 ]

newBoard :: Board
newBoard =
  let
    whiteBaseLine = baseLine White
    whitePawnLine = pawnLine White
    blackBaseLine = baseLine Black
    blackPawnLine = pawnLine Black
  in
    foldr
      ( \(Some (PlacedPiece position piece)) -> replace position piece )
      ( empty )
      ( whiteBaseLine ++ whitePawnLine ++ blackBaseLine ++ blackPawnLine )