-- | -- Module: TickLeiste -- Description: Short Implementation of Splittermonds Tickleiste -- Stability: experimental module Data.TickLeiste ( TickLeiste, newTickLeiste, addTicksToPlayer, isAbwarten, isBereithalten, Player, getPlayerTick, getTickPlayers, getTickValue, setPlayerTick, toList, fromList, toMap, fromMap, Tick (..), ) where import qualified Data.Map.Strict as M import Data.Maybe (fromMaybe) import qualified Data.Text as T -- | A Tick is just a number data Tick = Abwarten | Bereithalten | Tick Int deriving (Show, Eq, Ord) -- | test if 'Tick' is constructed using 'Abwarten' isAbwarten :: Tick -> Bool isAbwarten Abwarten = True isAbwarten _ = False -- | test if 'Tick' is constructed using 'Bereithalten' isBereithalten :: Tick -> Bool isBereithalten Bereithalten = True isBereithalten _ = False -- | if 'Tick' is costructed with 'Tick' we get the 'Int' value, else 'Nothing' getTickValue :: Tick -> Maybe Int getTickValue (Tick n) = Just n getTickValue _ = Nothing -- | A Player has a name as identifier type Player = T.Text -- | The TickLeiste consists of a List of Ticks and for each Tick a -- Queue of players, that play at that turn. -- This is a bit wierd as it introduces redundancy, but I don't have a -- better idea. -- NOTE: the first player in the list is the first to move. data TickLeiste = TickLeiste {leiste :: M.Map Tick [Player], player :: M.Map Player Tick} deriving (Show) -- | Empty Tickleiste newTickLeiste :: TickLeiste newTickLeiste = TickLeiste M.empty M.empty -- | Adds a number of ticks to the player, if the player is not on -- the TickLeiste, Abwarten or Bereithalten then this function returns -- Nothing -- Would it be better to return the original 'TickLeiste' instead of 'Nothing'? addTicksToPlayer :: -- | the Player we want to add ticks to Player -> -- | the number of Ticks we want to add Int -> -- | the TickLeiste we want to modify TickLeiste -> -- | the result Maybe TickLeiste addTicksToPlayer p t tl = {- fromMaybe tl $ -} do pt <- getPlayerTick p tl ptv <- getTickValue pt let nt = Tick $ ptv + t return $ setPlayerTick p nt tl -- | We get the tick a 'Player' is on getPlayerTick :: Player -> TickLeiste -> Maybe Tick getPlayerTick p (TickLeiste _ pl) = pl M.!? p -- | get a list of 'Player' that move at a tick in order. getTickPlayers :: Tick -> TickLeiste -> [Player] getTickPlayers t (TickLeiste l _) = fromMaybe [] $ l M.!? t -- | set the tick of a 'Player', if the 'Player' is does not exist we add them. setPlayerTick :: Player -> Tick -> TickLeiste -> TickLeiste setPlayerTick p t tl@(TickLeiste l pl) = TickLeiste (insertPlayerToLeiste p t l') (setPlayerTickToPlayer p t pl) where l' :: M.Map Tick [Player] l' = fromMaybe l $ do ot <- getPlayerTick p tl return $ removePlayerFromLeiste p ot l -- | convert the Tick[eiste to a list of 'Tick' and 'Player' list pairs. These list are ordered toList :: TickLeiste -> [(Tick, [Player])] toList (TickLeiste l _) = M.toAscList l -- | convert from list to TickLeiste, if a player is at multiple 'Tick' this returns -- 'Nothing' -- TODO fromList :: [(Tick, [Player])] -> Maybe TickLeiste fromList = error "not implemented" -- | convert from 'TickLeiste' to a map from 'Tick' to list of 'Player' toMap :: TickLeiste -> M.Map Tick [Player] toMap = leiste -- TODO fromMap :: M.Map Tick [Player] -> Maybe TickLeiste fromMap = error "not implemented" -- these are just internal helpers -- it removes a player from a specific tick, if the player -- wasn't at the tick it is the identity. removePlayerFromLeiste :: Player -> Tick -> M.Map Tick [Player] -> M.Map Tick [Player] removePlayerFromLeiste p t l = fromMaybe l $ do list <- l M.!? t let list' = filter (/= p) list if null list' then return $ M.delete t l else return $ M.insert t list' l insertPlayerToLeiste :: Player -> Tick -> M.Map Tick [Player] -> M.Map Tick [Player] insertPlayerToLeiste p t l = M.insert t (M.findWithDefault [] t l ++ [p]) l setPlayerTickToPlayer :: Player -> Tick -> M.Map Player Tick -> M.Map Player Tick setPlayerTickToPlayer = M.insert