macbeth-lib-0.0.12: src/Macbeth/Wx/BoardState.hs
module Macbeth.Wx.BoardState (
BoardState(..),
DraggedPiece(..),
Origin(..),
PieceMove (..),
pickUpPieceFromHolding,
discardDraggedPiece,
setPromotion,
togglePromotion,
pointToSquare,
movePiece,
update,
setResult,
resize,
invertPerspective,
performPreMoves,
cancelLastPreMove,
setPieceSet,
getPieceHolding,
getSelectedSquare,
initBoardState
) where
import Macbeth.Fics.Api.Api
import Macbeth.Fics.Api.Move
import Macbeth.Fics.Api.Game
import Macbeth.Wx.PieceSet
import Control.Monad
import Control.Concurrent.STM
import Data.Maybe
import Data.List
import Graphics.UI.WX hiding (position, update, resize, when)
import Safe
import System.IO
data BoardState = BoardState {
lastMove :: Move
, gameResult :: Maybe GameResult
, pieceMove :: [PieceMove]
, moves :: [Move]
, _position :: Position
, preMoves :: [PieceMove]
, perspective :: PColor
, mousePt :: Point
, promotion :: PType
, draggedPiece :: Maybe DraggedPiece
, isWaiting :: Bool
, psize :: Int
, scale :: Double
, pieceSet :: PieceSet
, phW :: [PType]
, phB :: [PType] }
data DraggedPiece = DraggedPiece Point Piece Origin deriving (Show)
data Origin = FromHolding | FromBoard Square deriving (Show)
data PieceMove = PieceMove { piece :: Piece, from :: Square, to :: Square } | DropMove Piece Square
instance Show PieceMove where
show (PieceMove _ s1 s2) = show s1 ++ show s2
show (DropMove (Piece p _) s) = show p ++ "@" ++ show s
pickUpPieceFromHolding :: TVar BoardState -> PType -> IO ()
pickUpPieceFromHolding vState p = atomically $ modifyTVar vState
(\s -> let color = colorUser (lastMove s)
isAllowed = isGameUser (lastMove s) && p `elem` getPieceHolding color s
in if isAllowed
then s{draggedPiece = Just $ DraggedPiece (mousePt s) (Piece p color) FromHolding }
else s)
discardDraggedPiece :: TVar BoardState -> IO ()
discardDraggedPiece vState = atomically $ modifyTVar vState (\s -> s {
draggedPiece = Nothing
, _position = movePieces (preMoves s) (position $ lastMove s)})
invertPerspective :: TVar BoardState -> IO ()
invertPerspective vState = atomically $ modifyTVar vState (\s -> s{perspective = invert $ perspective s})
setResult :: TVar BoardState -> GameResult -> IO ()
setResult vState r = atomically $ modifyTVar vState (\s -> s{
gameResult = Just r
, _position = position $ lastMove s
, preMoves = []
, draggedPiece = Nothing})
setPromotion :: PType -> TVar BoardState -> IO ()
setPromotion p vState = atomically $ modifyTVar vState (\s -> s{promotion = p})
togglePromotion :: TVar BoardState -> IO PType
togglePromotion vState = atomically $ do
modifyTVar vState (\s -> s{promotion = togglePromotion' (promotion s)})
promotion `fmap` readTVar vState
where
togglePromotion' :: PType -> PType
togglePromotion' p = let px = [Queen, Rook, Knight, Bishop]
in px !! ((fromJust (p `elemIndex` px) + 1) `mod` length px)
update :: TVar BoardState -> Move -> MoveModifier -> IO ()
update vBoardState move ctx = atomically $ modifyTVar vBoardState (\s -> s {
isWaiting = isNextMoveUser move
, pieceMove = diffPosition (position $ lastMove s) (position move)
, moves = addtoHistory move ctx (moves s)
, lastMove = move
, _position = movePieces (preMoves s) (position move)})
diffPosition :: Position -> Position -> [PieceMove]
diffPosition before after =
let from = before \\ after
to = after \\ before
in [PieceMove piece1 s1 s2 | (s1, piece1) <- from, (s2, piece2) <- to, piece1 == piece2, s1 /= s2 ]
addtoHistory :: Move -> MoveModifier -> [Move] -> [Move]
addtoHistory _ Illegal mx = mx
addtoHistory m (Takeback _) mx = m : tail (dropWhile (not . equal m) mx)
where
equal :: Move -> Move -> Bool
equal m1 m2 = (moveNumber m1 == moveNumber m2) && (turn m1 == turn m2)
addtoHistory m _ mx = m : mx
resize :: Panel () -> TVar BoardState -> IO ()
resize p vState = do
(Size x _) <- get p size
let psize' = pieceSetFindSize x
atomically $ modifyTVar vState (\s -> s { psize = psize', scale = fromIntegral x / 8 / fromIntegral psize'})
cancelLastPreMove :: TVar BoardState -> IO ()
cancelLastPreMove vBoardState = atomically $ modifyTVar vBoardState (\s ->
let preMoves' = fromMaybe [] $ initMay (preMoves s) in s {
preMoves = preMoves'
, _position = movePieces preMoves' (position $ lastMove s)})
performPreMoves :: TVar BoardState -> Handle -> IO ()
performPreMoves vBoardState h = do
preMoves' <- preMoves `fmap` readTVarIO vBoardState
unless (null preMoves') $ do
atomically $ modifyTVar vBoardState (\s -> s {
isWaiting = False,
preMoves = tail preMoves'})
hPutStrLn h $ "6 " ++ show (head preMoves' )
setPieceSet :: TVar BoardState -> PieceSet -> IO ()
setPieceSet vState ps = atomically (modifyTVar vState (\s -> s { pieceSet = ps }))
getPieceHolding :: PColor -> BoardState -> [PType]
getPieceHolding White bs = phW bs
getPieceHolding Black bs = phB bs
getSelectedSquare :: BoardState -> Square
getSelectedSquare state = pointToSquare state (mousePt state)
pointToSquare :: BoardState -> Point -> Square
pointToSquare state (Point x y) = Square
(intToCol (perspective state) (floor $ fromIntegral x / fieldSize state))
(intToRow (perspective state) (floor $ fromIntegral y / fieldSize state))
where
intToRow :: PColor -> Int -> Row
intToRow White = toEnum . abs . (7-)
intToRow Black = toEnum
intToCol :: PColor -> Int -> Column
intToCol White = toEnum
intToCol Black = toEnum . abs . (7-)
fieldSize :: BoardState -> Double
fieldSize bs = scale bs * fromIntegral (psize bs)
movePieces :: [PieceMove] -> Position -> Position
movePieces moves pos = foldl (flip movePiece) pos moves
movePiece :: PieceMove -> Position -> Position
movePiece (PieceMove piece from to) position = filter (\(s, _) -> s /= from && s /= to) position ++ [(to, piece)]
movePiece (DropMove piece sq) pos = filter (\(s, _) -> s /= sq) pos ++ [(sq, piece)]
initBoardState :: Move -> BoardState
initBoardState move = BoardState {
lastMove = move
, gameResult = Nothing
, pieceMove = []
, moves = [move] -- ^ accept empty positions, filter when creating pgn
, _position = Macbeth.Fics.Api.Move.position move
, preMoves = []
, perspective = if relation move == Observing then White else colorUser move
, mousePt = Point 0 0
, promotion = Queen
, draggedPiece = Nothing
, isWaiting = relation move == MyMove
, psize = 40
, scale = 1.0
, pieceSet = head pieceSets
, phW = []
, phB = []}