packages feed

chessica-0.1.0.0: src/Chess/Game/Command.hs

{-# LANGUAGE LambdaCase   #-}
{-# LANGUAGE TypeFamilies #-}
-----------------------------------------------------------------------------
-- |
-- Module      : Chess.Game.Command
-- Copyright   : (c) Michael Szvetits, 2023
-- License     : BSD-3-Clause (see the file LICENSE)
-- Maintainer  : typedbyte@qualified.name
-- Stability   : stable
-- Portability : portable
--
-- Types and functions for commands that can change chess game states.
-----------------------------------------------------------------------------
module Chess.Game.Command where

-- base
import GHC.Exts       (IsList(Item, fromList, toList))
import Prelude hiding (sequence)

import Chess.Board.PlacedPiece (PlacedPiece(..))
import Chess.Board.Position    (Position)
import Chess.Piece             (Piece)
import Chess.Some              (Some(Some))

-- | A command can be applied to a chess game state in order to obtain a new game state.
data Command
  = EndTurn
    -- ^ Ends the turn of the active player.
  | Move Position (Some PlacedPiece)
    -- ^ Moves a placed piece to the specified position.
  | Destroy (Some PlacedPiece)
    -- ^ Removes a placed piece.
  | Spawn Position (Some Piece)
    -- ^ Creates a chess piece on the specified position.
  | Promote (Some PlacedPiece) (Some Piece)
    -- ^ Converts a chess piece into another piece.
  | Sequence Command Command
    -- ^ Represents the consecutive execution of two commands.
  | Atomic Command
    -- ^ Denotes that a command and its sub-commands belong together and should
    -- be treated as single command (e.g., when recording the history of a chess game).
  deriving (Eq, Ord, Show)

instance IsList Command where
  type Item Command = Command
  
  fromList [cmd]    = cmd
  fromList (cmd:cs) = Sequence cmd (fromList cs)
  fromList []       = errorWithoutStackTrace "Command.fromList: empty list"
  
  toList (Sequence cmd1 cmd2) = cmd1 : toList cmd2
  toList cmd                  = [cmd]

-- | Smart constructor for 'EndTurn'.
endTurn :: Command
endTurn = EndTurn

-- | Smart constructor for 'Move'.
move :: Position -> PlacedPiece t -> Command
move position = Move position . Some

-- | Smart constructor for 'Destroy'.
destroy :: PlacedPiece t -> Command
destroy = Destroy . Some

-- | Smart constructor for 'Spawn'.
spawn :: Position -> Piece t -> Command
spawn position = Spawn position . Some

-- | Smart constructor for 'Promote'.
promote :: PlacedPiece a -> Piece b -> Command
promote placed piece = Promote (Some placed) (Some piece)

-- | Smart constructor for 'Sequence'.
sequence :: Command -> Command -> Command
sequence = Sequence

-- | Smart constructor for 'Atomic'.
atomic :: Command -> Command
atomic = Atomic

-- | Produces a command that has the opposite effect of the specified command.
undo :: Command -> Command
undo = \case
  EndTurn ->
    EndTurn
  Move dst (Some (PlacedPiece src piece)) ->
    move src (PlacedPiece dst piece)
  Destroy (Some placed) ->
    spawn placed.position placed.piece
  Spawn position (Some piece) ->
    destroy (PlacedPiece position piece)
  Promote (Some (PlacedPiece position old)) (Some new) ->
    promote (PlacedPiece position new) old
  Sequence cmd1 cmd2 ->
    sequence (undo cmd2) (undo cmd1)
  Atomic cmd ->
    atomic (undo cmd)