1 {-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, ScopedTypeVariables,
2 TypeSynonymInstances #-}
4 module RMCA.GUI.Board where
8 import Data.Array.MArray
9 import qualified Data.Bifunctor as BF
10 import Data.Board.GameBoardIO
14 import Data.ReactiveValue
16 import Game.Board.BasicTurnGame
17 import Graphics.UI.Gtk hiding (Action)
18 import Graphics.UI.Gtk.Board.BoardLink
19 import Graphics.UI.Gtk.Board.TiledBoard hiding (Board)
20 import qualified Graphics.UI.Gtk.Board.TiledBoard as BIO
23 data GUICell = GUICell { cellAction :: Action
28 newtype GUIBoard = GUIBoard { toGS :: GameState Int Tile Player GUICell }
31 data Player = Player deriving(Show)
33 -- Takes a GUI coordinate and give the corresponding coordinate on the
35 fromGUICoords :: (Int,Int) -> (Int,Int)
36 fromGUICoords (x,y) = (x,(x `mod` 2 - y) `quot` 2)
38 -- Takes coordinates from the point of view of the internal board and
39 -- translates them to GUI board coordinates.
40 toGUICoords :: (Int,Int) -> (Int,Int)
41 toGUICoords (x,y) = (x,2*(-y) + x `mod` 2)
47 tileH = round (sqrt 3 * fromIntegral tileW / 3)
50 hexW = round (4 * fromIntegral tileW / 3)
53 hexH = round (sqrt 3 * fromIntegral hexW / 2)
56 (xMax,yMax) = BF.second (*2) $ neighbor N nec
58 (xMin,yMin) = BF.second (*2) swc
60 boardToTile :: [(Int,Int,Tile)]
61 boardToTile = [(x,y,Tile) | (x,y) <- range ( (xMin-1,yMin)
66 boardToPiece :: [PlayHead] -> Board -> [(Int,Int,Player,GUICell)]
67 boardToPiece ph = map placePiece . filter (onBoard . fst) . assocs
68 where placePiece :: (Pos,Cell) -> (Int,Int,Player,GUICell)
69 placePiece ((x,y),(a,n)) = let y' = 2*(-y) + x `mod` 2
70 c = GUICell { cellAction = a
72 , asPh = (x,y) `elem` phPosS
77 validArea :: Board -> [(Int,Int)]
78 validArea = map (\(x,y,_,_) -> (x,y)) . boardToPiece []
83 naOrn = Ornaments Nothing [] NoSlide
86 initGUIBoard :: GUIBoard
87 initGUIBoard = GUIBoard GameState
89 , boardPos = boardToTile
90 , boardPieces' = boardToPiece [] $
91 makeBoard [((0,5), mkCell (ChDir True na NE))]
94 instance PlayableGame GUIBoard Int Tile Player GUICell where
96 allPos (GUIBoard game) = boardPos game
97 allPieces (GUIBoard game) = boardPieces' game
99 canMove (GUIBoard game) _ (x,y)
100 | Just (_,p) <- getPieceAt game (x,y)
101 , GUICell { cellAction = Inert } <- p = False
103 canMoveTo _ _ _ (x,y) = (x,y) `elem` validArea
104 where validArea = map (\(x',y',_,_) -> (x',y')) $ boardToPiece [] $
107 move (GUIBoard game) _ iPos@(_,yi) (xf,yf) = [ MovePiece iPos fPos'
108 , AddPiece iPos Player nCell]
110 | (xf `mod` 2 == 0 && yf `mod` 2 == 0)
111 || (xf `mod` 2 /= 0 && yf `mod` 2 /= 0) = (xf,yf)
112 | otherwise = (xf,yf+signum' (yf-yi))
115 | otherwise = signum x
117 | Just (_,GUICell { asPh = ph, repeatCount = n }) <-
118 getPieceAt game iPos = inertCell { repeatCount = n
121 | otherwise = inertCell
122 where inertCell = GUICell { cellAction = Inert
126 applyChange (GUIBoard game) (AddPiece pos@(x,y) Player piece) =
127 GUIBoard $ game { boardPieces' = bp' }
128 where bp' = (x,y,Player,piece):boardPieces' game
130 applyChange (GUIBoard game) (RemovePiece (x,y)) = GUIBoard $
131 game { boardPieces' = bp' }
132 where bp' = [p | p@(x',y',_,_) <- boardPieces' game
133 , x /= x' || y /= y']
135 applyChange guiBoard@(GUIBoard game) (MovePiece iPos fPos)
136 | Just (_,p) <- getPieceAt game iPos
137 = applyChanges guiBoard [ RemovePiece iPos
139 , AddPiece fPos Player p]
140 | otherwise = guiBoard
142 initGame :: IO (Game GUIBoard Int Tile Player GUICell)
144 pixbufs <- fileToPixbuf
145 tilePixbuf <- pixbufNew ColorspaceRgb False 8 tileW tileH
146 pixbufFill tilePixbuf 50 50 50 0
147 let pixPiece :: (Player,GUICell) -> Pixbuf
148 pixPiece (_,a) = fromJust $ lookup (actionToFile a) pixbufs
149 pixTile :: Tile -> Pixbuf
150 pixTile _ = tilePixbuf
151 visualA = VisualGameAspects { tileF = pixTile
153 , bgColor = (1000,1000,1000)
157 return $ Game visualA initGUIBoard
159 initBoardRV :: BIO.Board Int Tile (Player,GUICell)
160 -> IO ( ReactiveFieldRead IO Board
161 , ReactiveFieldReadWrite IO [PlayHead])
162 initBoardRV BIO.Board { boardPieces = GameBoard array } = do
163 phMVar <- newCBMVar []
164 let getterB :: IO Board
166 (boardArray :: [((Int,Int),Maybe (Player,GUICell))]) <- getAssocs array
167 let board = makeBoard $
168 map (BF.first fromGUICoords .
169 BF.second ((\(_,c) -> (cellAction c,repeatCount c)) .
171 filter (isJust . snd) boardArray
174 notifierB :: IO () -> IO ()
175 notifierB _ = return ()
177 getterP :: IO [PlayHead]
178 getterP = readCBMVar phMVar
180 setterP :: [PlayHead] -> IO ()
182 writeCBMVar phMVar lph
183 boardArray <- getAssocs array
184 let phPosS = map (toGUICoords . phPos) lph
185 updatePh :: ((Int,Int),Maybe (Player,GUICell)) -> IO ()
186 updatePh (i,c) = when (isJust c) $ do
187 let (_,c') = fromJust c
188 writeArray array i (Just (Player,c' { asPh = i `elem` phPosS }))
189 mapM_ updatePh boardArray
191 notifierP :: IO () -> IO ()
192 notifierP = installCallbackCBMVar phMVar
194 b = ReactiveFieldRead getterB notifierB
195 ph = ReactiveFieldReadWrite setterP getterP notifierP
198 fileToPixbuf :: IO [(FilePath,Pixbuf)]
199 fileToPixbuf = mapM (\f -> let f' = "img/" ++ f in uncurry (liftM2 (,))
201 , pixbufNewFromFile f' >>=
202 \p -> pixbufScaleSimple p hexW hexW InterpBilinear ))
203 (["hexOn.png","stop.svg","split.svg"] ++
204 concat [["start" ++ show d ++ ".svg","ric" ++ show d ++ ".svg"]
207 actionToFile :: GUICell -> FilePath
208 actionToFile GUICell { cellAction = a
212 (Inert,True) -> "img/hexOn.png"
213 (Inert,False) -> "img/hexOff.png"
214 (Absorb,_) -> "img/stop.svg"
215 (Stop _,_) -> "img/stop.svg"
216 (ChDir True _ d,_) -> "img/start" ++ show d ++ ".svg"
217 (ChDir False _ d,_) -> "img/ric" ++ show d ++ ".svg"
218 (Split _,_) -> "img/split.svg"