packages feed

tak-0.1.0.0: src/Tak/ApplyPlay.hs

module Tak.ApplyPlay where

import Data.Matrix hiding (trace)

import Tak.Types
import Tak.Win

play :: Play -> GameState -> Either GameState IllegalMove
play _ (GameState _ _ _ (Just finished) _ _) = Right (GameAlreadyWon finished)
play (Place stone loc) state
    | fst loc > nrows board = Right $ ExceededBounds PosX
    | fst loc < 1 = Right $ ExceededBounds NegX
    | snd loc >  ncols board = Right $ ExceededBounds PosY
    | snd loc < 1 = Right $ ExceededBounds NegY
    | otherwise = case square of
        [] -> Left $ GameState newBoard newWhite newBlack finished
            (oppositeColour $ stPlaysNext state) (stNextTurn state + 1)
        _ -> Right SquareNotEmpty
    where
        finished = won newBoard newWhite newBlack
        newBoard = setElem [(stone, colour)] loc board
        newWhite = case colour of
            White -> removeStone stone (stWhite state)
            Black -> stWhite state
        newBlack = case colour of
            White -> stBlack state
            Black -> removeStone stone (stBlack state)
        removeStone Flat player =
            player{stonesRemaing = stonesRemaing player - 1}
        removeStone Standing player = removeStone Flat player
        removeStone Cap player =
            player{capsRemaining = capsRemaining player - 1}
        square = board ! loc
        board = stBoard state
        colour = if stNextTurn state <= 2
            then oppositeColour $ stPlaysNext state
            else stPlaysNext state
play (Move loc dir drops) state
    | carry > carryLimit board = Right CarryLimitExceeded
    | carry > length square = Right StackTooSmall
    | length square == 0 = Right EmptySquare
    | snd (head square) /= colour = Right WrongColourStack
    | fst loc > nrows board = Right $ ExceededBounds PosX
    | fst loc < 1 = Right $ ExceededBounds NegX
    | snd loc > ncols board = Right $ ExceededBounds PosY
    | snd loc < 1 = Right $ ExceededBounds NegY
    | otherwise = case newBoardOrErr of
        Left newBoard -> Left state{stBoard = newBoard,
            stPlaysNext = oppositeColour colour,
            stNextTurn = stNextTurn state + 1,
            stFinished = won newBoard (stWhite state) (stBlack state)
        }
        Right err -> Right err
    where
        newBoardOrErr = foldr place (Left boardSub) (zip dropPieces dropLocs)
        dropPieces = snd $ foldl dropPieces' (carried, []) drops
        dropPieces' (carriedLeft, dropsSoFar) n =
            let (ds, carriedLeft') = splitAt n carriedLeft in 
            (carriedLeft', dropsSoFar ++ [reverse ds])
        dropLocs = map (step loc dir) [1..]
        boardSub = setElem (drop carry square) loc board
        carried = reverse $ take carry square
        carry = sum drops
        square = board ! loc
        board = stBoard state
        colour = stPlaysNext state

place :: ([Piece], Loc) -> (Either Board IllegalMove) -> Either Board IllegalMove
place stackLoc (Left board) = place' stackLoc board
place _ illegalMove = illegalMove

place' :: ([Piece], Loc) -> Board -> (Either Board IllegalMove)
place' (stack, loc) board
    | fst loc > nrows board = Right $ ExceededBounds PosX
    | fst loc < 1 = Right $ ExceededBounds NegX
    | snd loc > ncols board = Right $ ExceededBounds PosY
    | snd loc < 1 = Right $ ExceededBounds NegY
    | otherwise = case square of
        [] -> Left newBoard
        (Standing, _) : _-> case stack of
            (Cap, _) : [] -> Left $ newSquashedBoard
            _ -> Right PlaceOnStanding
        (Cap, _) : _ -> Right PlaceOnCapstone
        (Flat, _) : _ -> Left newBoard
    where
        newBoard = setElem newStack loc board
        newSquashedBoard = setElem newSquashedStack loc board
        newStack = stack ++ square
        newSquashedStack = stack ++ squashedSquare
        squashedSquare = case square of
            (Standing, colour) : ss -> (Flat, colour) : ss
            _ -> square
        square = board ! loc

carryLimit :: Board -> Int
carryLimit board = max (nrows board) (ncols board)

step :: Loc -> Dir -> Int -> Loc        
step (x, y) PosX n = (x + n, y)
step (x, y) NegX n = (x - n, y)
step (x, y) PosY n = (x, y + n)
step (x, y) NegY n = (x, y - n)

data IllegalMove =
    SquareNotEmpty
    | ExceededBounds Dir
    | CarryLimitExceeded
    | StackTooSmall
    | GameAlreadyWon Finished
    | PlaceOnStanding
    | PlaceOnCapstone
    | WrongColourStack
    | EmptySquare deriving Show