packages feed

labyrinth-0.1.5.1: src/LabyrinthServer/Data.hs

{-# Language DeriveDataTypeable, TemplateHaskell, TypeFamilies, Rank2Types #-}

module LabyrinthServer.Data where

import Control.Lens hiding (Action)
import Control.Monad.State
import Control.Monad.Reader (ask)

import Data.Acid (Query, Update, makeAcidic)
import Data.DeriveTH
import Data.Derive.Typeable
import qualified Data.Map as M
import Data.SafeCopy (base, deriveSafeCopy)
import Data.Typeable

import Text.JSON

import Labyrinth hiding (performMove)
import qualified Labyrinth as L

deriveSafeCopy 0 'base ''Direction
deriveSafeCopy 0 'base ''Wall
deriveSafeCopy 0 'base ''CellType
deriveSafeCopy 0 'base ''Cell
deriveSafeCopy 0 'base ''Position
deriveSafeCopy 0 'base ''Treasure
deriveSafeCopy 0 'base ''Health
deriveSafeCopy 0 'base ''Player
deriveSafeCopy 0 'base ''Labyrinth

deriveSafeCopy 0 'base ''Action
deriveSafeCopy 0 'base ''MoveDirection
deriveSafeCopy 0 'base ''QueryType
deriveSafeCopy 0 'base ''Move

deriveSafeCopy 0 'base ''CellTypeResult
deriveSafeCopy 0 'base ''TreasureResult
deriveSafeCopy 0 'base ''CellEvents
deriveSafeCopy 0 'base ''GoResult
deriveSafeCopy 0 'base ''GrenadeResult
deriveSafeCopy 0 'base ''ShootResult
deriveSafeCopy 0 'base ''ActionResult
deriveSafeCopy 0 'base ''ChoosePositionResult
deriveSafeCopy 0 'base ''ReorderCellResult
deriveSafeCopy 0 'base ''QueryResult
deriveSafeCopy 0 'base ''StartResult
deriveSafeCopy 0 'base ''MoveResult

derive makeTypeable ''Labyrinth
derive makeTypeable ''Move
derive makeTypeable ''MoveResult

type GameId = String

data MoveRecord = MoveRecord { _rplayer :: PlayerId
                             , _rmove :: Move
                             , _rresult :: MoveResult
                             }

makeLenses ''MoveRecord

deriveSafeCopy 0 'base ''MoveRecord

derive makeTypeable ''MoveRecord

type MoveLog = [MoveRecord]

logMoveResult :: MoveRecord -> State MoveLog ()
logMoveResult m = modify (++ [m])

data Game = Game { _labyrinth :: Labyrinth
                 , _moves :: MoveLog
                 }

newGame :: Labyrinth -> Game
newGame l = Game l []

makeLenses ''Game

deriveSafeCopy 0 'base ''Game

derive makeTypeable ''Game

data Games = Games { _games :: M.Map GameId Game }

noGames :: Games
noGames = Games M.empty

makeLenses ''Games

game :: GameId -> Simple Traversal Games Game
game gid = games . ix gid

getGames :: Query Games Games
getGames = ask

stateUpdate :: State x y -> Update x y
stateUpdate f = do
    st <- get
    let (r, st') = runState f st
    put st'
    return r

addGame :: GameId -> Labyrinth -> Update Games Bool
addGame gid lab = stateUpdate $ zoom games $ do
    existing <- gets (M.member gid)
    if existing
        then return False
        else do
            modify $ M.insert gid $ newGame lab
            return True

getGame :: GameId -> Query Games Game
getGame = view . singular . game

performMove :: GameId -> PlayerId -> Move -> Update Games MoveResult
performMove g p m = stateUpdate $ zoom (singular $ game g) $ do
    r <- zoom labyrinth $ L.performMove p m
    zoom moves $ logMoveResult $ MoveRecord p m r
    return r

deriveSafeCopy 0 'base ''Games

derive makeTypeable ''Games

makeAcidic ''Games [ 'getGames
                   , 'addGame
                   , 'getGame
                   , 'performMove
                   ]

logJSON :: MoveLog -> JSValue
logJSON g = JSArray $ map moveJSON g
    where moveJSON l = jsObject [ ("player", jsInt $ l ^. rplayer)
                                , ("move", jsShow $ l ^. rmove)
                                , ("result", jsShow $ l ^. rresult)
                                ]

gameInfoJSON :: Game -> JSValue
gameInfoJSON g = jsObject prop
    where prop = [ ("width", jsInt $ l ^. labWidth)
                 , ("height", jsInt $ l ^. labHeight)
                 , ("players", jsInt $ playerCount l)
                 , ("currentTurn", jsInt $ l ^. currentTurn)
                 , ("gameEnded", jsBool $ l ^. gameEnded)
                 ] ++ mapProp
          mapProp = [("map", jsShow l) | l ^. gameEnded]
          l = g ^. labyrinth

gameListJSON :: Games -> JSValue
gameListJSON = jsObject . M.toList . M.map gameInfoJSON . view games

gameJSON :: Game -> JSValue
gameJSON g = jsObject [("game", gameInfoJSON g), ("log", logJSON m)]
    where m = g ^. moves

jsObject :: [(String, JSValue)] -> JSValue
jsObject = JSObject . toJSObject

jsInt :: Int -> JSValue
jsInt = JSRational False . fromIntegral

jsBool :: Bool -> JSValue
jsBool = JSBool

jsShow :: (Show a) => a -> JSValue
jsShow = JSString . toJSString . show