module GameStrategy where
import Text.Regex
import Control.Monad.State
import qualified Data.Map as Map
import qualified Data.Set as S
import Data.List
import Types
import WordsOp
import Hangman
import Guess
{-
TODO: 1. failure at when define
`class (Guess g, HangmanC h) => GuessAction g h | g -> h where ...`
-}
data SimpleStrategy = SimpleStrategy { candidateLetters :: [Letter]
, candidateWords :: [EnglishWord]
, lastLetter :: Letter
, mapAlphabet :: MapPerAlphabet
}
-- | Build a Strategy base on words which have same length to secret word.
newSimpleStrategy :: [EnglishWord] -> SimpleStrategy
newSimpleStrategy xs = SimpleStrategy letters words ' ' map
where map = buildMapWordsFrequency xs
letters = buildListLetterFrequency map
words = []
instance Show SimpleStrategy where
show (SimpleStrategy ls ws ll _) = ll : "->" ++ show ls ++ show (take 5 ws)
instance GameStategy SimpleStrategy where
nextGuess hg = do
s1 <- get
modify (updateNextGuessWords hg)
ss@(SimpleStrategy cl cw ll map) <- get
-- FIXME: complicated if-else thus need lang ext DoAndIfThenElse. can be simple??
liftIO $ print $ take 5 cw
if (length cw > 0) && ((gameWrongGuessesMade hg + length cw <= 5) || gameWrongGuessesMade hg >= 3) then
put (SimpleStrategy cl (tail cw) ll map) >> return (NextGuess $ GuessWord $ head cw)
else
return (NextGuess $ GuessLetter ll)
-- | Narrow down possible words for guessing.
updateNextGuessWords :: Hangman -> SimpleStrategy -> SimpleStrategy
updateNextGuessWords (Hangman _ _ gsf ils cls iws) (SimpleStrategy cl cw ll map) =
let possibles = fetchWordsPerLetter ll map
i = S.toList ils
c = S.toList cls
cw1 = correctGuess ll c i cw possibles
cw2 = filterPossiblesPerGuessed i c gsf cw1
map1 = if length cw2 > 0 then buildMapWordsFrequency cw2 else map
ls = if (length cw2 > 0) then ((buildListLetterFrequency map1) \\ i) \\ c else cl
in
(SimpleStrategy (tail ls) cw2 (head ls) map1)
fetchWordsPerLetter :: Char -> Map.Map Char [a] -> [a]
fetchWordsPerLetter lastLetter m
| Map.null m = []
| lastLetter == ' ' = []
| otherwise = case Map.lookup lastLetter m of
Just xs -> xs
Nothing -> []
correctGuess lastLetter corrects incorrects origins possibles
| lastLetter `elem` corrects && length origins > 0 = origins `union` possibles
| lastLetter `elem` corrects && length origins == 0 = possibles
| lastLetter `elem` incorrects = origins \\ possibles
| otherwise = origins
filterPossiblesPerGuessed [] [] _ p = p
filterPossiblesPerGuessed incorrects corrects guessedSoFar p =
let neg = if length incorrects > 0 then ("[^" ++ incorrects ++ "]") else "."
reg = subRegex (mkRegex "-") guessedSoFar neg in
filter (match reg) p
match :: String -- ^ reg exp
-> EnglishWord -- ^ test word
-> Bool -- ^ <==> Ture if match
match s w = case matchRegex (mkRegex s) w of
Just _ -> True
Nothing -> False