|
module Datatypes.Game (Game(..), Command(..), isCompleted, new, notifyCommand, powerCounterToScore, powerPhrasesAsCommands, commandsToString,stringToCommands) where -- FIXME exports
|
|
|
|
import Data.Hashable (hash)
|
|
import qualified Data.List as List
|
|
import Data.Map.Strict (Map)
|
|
import qualified Data.Map.Strict as Map
|
|
import Data.Set (Set)
|
|
import qualified Data.Set as Set
|
|
|
|
import Datatypes.Board (Board)
|
|
import Datatypes.Unit (Unit)
|
|
import qualified Datatypes.Unit as Unit
|
|
|
|
data Command = MoveW
|
|
| MoveSE
|
|
| MoveE
|
|
| MoveSW
|
|
| RotateClockwise
|
|
| RotateCounterclockwise
|
|
deriving (Show,Eq,Ord)
|
|
|
|
type UnitHash = Int
|
|
|
|
type PowerCounter = Map String Int
|
|
|
|
data Game = Game {
|
|
board :: Board,
|
|
units :: [Unit],
|
|
visitedUnits :: Set UnitHash,
|
|
oldLines :: Int,
|
|
score :: Int,
|
|
history :: [Command],
|
|
powerCounter :: PowerCounter
|
|
}
|
|
deriving Show
|
|
|
|
isCompleted :: Game -> Bool
|
|
isCompleted game = null $ units game
|
|
|
|
new :: Board -> [Unit] -> Game
|
|
new b us = Game {
|
|
board = b,
|
|
units = (c:cs),
|
|
visitedUnits = Set.singleton (hash c),
|
|
oldLines = 0,
|
|
score = 0,
|
|
history = [],
|
|
powerCounter = Map.empty
|
|
} where (c:cs) = map (flip Unit.centeredIn b) us
|
|
|
|
powerPhrases :: Map String [Command]
|
|
powerPhrases = Map.fromList $ map (\x -> (x, stringToCommands x)) keys where
|
|
keys = ["ei!", "ia! ia!", "ph'nglui mglw'nafh cthulhu r'lyeh wgah'nagl fhtagn.", " r'lyeh", "yogsothoth", "necronomicon", "yuggoth", "john bigboote"]
|
|
|
|
powerPhrasesAsCommands :: [[Command]]
|
|
powerPhrasesAsCommands = map snd $ Map.toList powerPhrases
|
|
|
|
reversedPowerPhrases :: [([Command], String)]
|
|
reversedPowerPhrases = swapTuples . Map.toList $ powerPhrases where
|
|
swapTuples = map (\(s, cs) -> (reverse cs, s))
|
|
|
|
notifyCommand :: Game -> Command -> Game
|
|
notifyCommand game command =
|
|
game { history = newHistory, powerCounter = newPowerCounter } where
|
|
newHistory = command:(history game)
|
|
newPowerCounter = updatePowerCounter (powerCounter game) newHistory
|
|
|
|
updatePowerCounter :: PowerCounter -> [Command] -> PowerCounter
|
|
updatePowerCounter counter history =
|
|
innerUpdate counter reversedPowerPhrases where
|
|
innerUpdate :: PowerCounter -> [([Command], String)] -> PowerCounter
|
|
innerUpdate c [] = c
|
|
innerUpdate c (p:ps) = innerUpdate (checkPrefix c p) ps
|
|
checkPrefix m (p, s) = if p `List.isPrefixOf` history
|
|
then incrementMember m s
|
|
else m
|
|
incrementMember m s = Map.insertWith (\_ old -> old + 1) s 1 m
|
|
|
|
powerCounterToScore :: PowerCounter -> Int
|
|
powerCounterToScore items = sum $ map evalScore (Map.toAscList items) where
|
|
evalScore (phrase, count) = 2 * (length phrase) * count + (if count > 0 then 300 else 0)
|
|
|
|
stringToCommands :: String -> [Command]
|
|
stringToCommands str = reverse (convert str []) where
|
|
convert [] acc = acc
|
|
convert ('p':cs) acc = convert cs (MoveW:acc)
|
|
convert ('\'':cs) acc = convert cs (MoveW:acc)
|
|
convert ('!':cs) acc = convert cs (MoveW:acc)
|
|
convert ('.':cs) acc = convert cs (MoveW:acc)
|
|
convert ('0':cs) acc = convert cs (MoveW:acc)
|
|
convert ('3':cs) acc = convert cs (MoveW:acc)
|
|
convert ('b':cs) acc = convert cs (MoveE:acc)
|
|
convert ('c':cs) acc = convert cs (MoveE:acc)
|
|
convert ('e':cs) acc = convert cs (MoveE:acc)
|
|
convert ('f':cs) acc = convert cs (MoveE:acc)
|
|
convert ('y':cs) acc = convert cs (MoveE:acc)
|
|
convert ('2':cs) acc = convert cs (MoveE:acc)
|
|
convert ('a':cs) acc = convert cs (MoveSW:acc)
|
|
convert ('g':cs) acc = convert cs (MoveSW:acc)
|
|
convert ('h':cs) acc = convert cs (MoveSW:acc)
|
|
convert ('i':cs) acc = convert cs (MoveSW:acc)
|
|
convert ('j':cs) acc = convert cs (MoveSW:acc)
|
|
convert ('4':cs) acc = convert cs (MoveSW:acc)
|
|
convert ('l':cs) acc = convert cs (MoveSE:acc)
|
|
convert ('m':cs) acc = convert cs (MoveSE:acc)
|
|
convert ('n':cs) acc = convert cs (MoveSE:acc)
|
|
convert ('o':cs) acc = convert cs (MoveSE:acc)
|
|
convert (' ':cs) acc = convert cs (MoveSE:acc)
|
|
convert ('5':cs) acc = convert cs (MoveSE:acc)
|
|
convert ('d':cs) acc = convert cs (RotateClockwise:acc)
|
|
convert ('q':cs) acc = convert cs (RotateClockwise:acc)
|
|
convert ('r':cs) acc = convert cs (RotateClockwise:acc)
|
|
convert ('v':cs) acc = convert cs (RotateClockwise:acc)
|
|
convert ('z':cs) acc = convert cs (RotateClockwise:acc)
|
|
convert ('1':cs) acc = convert cs (RotateClockwise:acc)
|
|
convert ('k':cs) acc = convert cs (RotateCounterclockwise:acc)
|
|
convert ('s':cs) acc = convert cs (RotateCounterclockwise:acc)
|
|
convert ('t':cs) acc = convert cs (RotateCounterclockwise:acc)
|
|
convert ('u':cs) acc = convert cs (RotateCounterclockwise:acc)
|
|
convert ('w':cs) acc = convert cs (RotateCounterclockwise:acc)
|
|
convert ('x':cs) acc = convert cs (RotateCounterclockwise:acc)
|
|
convert ('\t':cs) acc = convert cs acc
|
|
convert ('\n':cs) acc = convert cs acc
|
|
convert ('\r':cs) acc = convert cs acc
|
|
|
|
commandsToString :: [Command] -> String
|
|
commandsToString [] = ""
|
|
commandsToString cmds@(c:cs) =
|
|
case smartConvert cmds (map (\(a, b) -> (b, a)) $ Map.toList powerPhrases) of
|
|
Just word -> word ++ (commandsToString (drop (length word) cmds))
|
|
Nothing -> dumbConvert c : commandsToString cs
|
|
where
|
|
dumbConvert MoveW = 'p'
|
|
dumbConvert MoveE = 'b'
|
|
dumbConvert MoveSW = 'a'
|
|
dumbConvert MoveSE = 'l'
|
|
dumbConvert RotateClockwise = 'd'
|
|
dumbConvert RotateCounterclockwise = 'k'
|
|
smartConvert _ [] = Nothing
|
|
smartConvert cmds (p:ps) =
|
|
let (encoded, word) = p
|
|
in if encoded `List.isPrefixOf` cmds
|
|
then Just word
|
|
else smartConvert cmds ps
|