packages feed

minesweeper-0.9: State/Functions.hs

{-# LANGUAGE PatternGuards #-}

module State.Functions where

import Configuration
import Preferences
import State
import Core.Square
--import Core.SquareConstraints
import Table

import Data.Maybe
--import Data.SetClass (toList)
import Control.Monad
import Random 
import Data.List
import Data.Function
--import qualified Data.ChangeMap as M
import qualified Data.Map as MM
--import qualified Data.ChangeSet as S

---------------------------------------------


initState :: String -> RandomSeed -> State
initState userName seed = loadState $ State
    { userName          = userName

    , preferences       = defaultPreferences
    , initialSeed       = seed

    , undo              = []
    , redo              = []

    , focus             = fromJust $ square (size defaultConfiguration) 1 1
    , mouseFocus        = Nothing
    , redness           = []

    , scores            = MM.empty
    , sortOrder         = [SA_Time, SA_Alive, SA_Success]

    , timer             = Nothing
    , revealing         = []
    , interrupt         = Nothing
    , hint              = Nothing
--    , hinting           = Nothing

    , game              = error "module State.Functions: game was not defined"
    }

loadState st = st
    { game = initGameState (configuration $ preferences st) (initialSeed st)
    }

stopState st = st { mouseFocus = Nothing }

revResult st
    | (x:_) <- revealing st
    = cAndG (gameStep $ revSquare x) st

----------------------- phases

-- the game is not yet started (new)
isNew 
    = null . undo

longInterrupt st
    | Just (b, _) <- interrupt st  = b
    | otherwise = False

-- the table is hidden
hiddenTable st
    =  longInterrupt st
    && isJust (timer st)
    && not (cAndG isFinished st)

cAndG f st = f (configuration $ preferences st) (game st)

-- the focus can be moved
focusMoves st
    =  not (longInterrupt st)
    && not (cAndG isFinished st)

-- the mouse focus is shown
mouseFocusShown st
    =  focusMoves st    
--    && isNothing (busyAnimation_ st)
    || interrupt st == Just (True, OtherApp)

-- reveal / mark / hint can be done
revealCanBeDone st
    =  isNothing (interrupt st)
    && not (cAndG isFinished st)
    && null (revealing st) 

-- mark can be done
markCanBeDone st
    =  isNothing (interrupt st)
    && cAndG mines_left st > 0
    && not (cAndG isFinished st)

-- the timer is active
isActive st 
    =  isNothing (busyAnimation_ st) 
    && isNothing (interrupt st) 
    && isJust (timer st)
    && not (cAndG isFinished st)


-------------- attributes

-- seed = currentSeed . game

-- allSolutions = numOfSolutions . configuration

busyAnimation_ = join . fmap busyAnimation . l2m . revealing

l2m (x:_) = Just x
l2m [] = Nothing

size_ = size . configuration . preferences
{-
msize g = xSize s * ySize s where s = size_ g

mines_left st 
    = mines (configuration st) - S.size (marked $ game st)

information :: State -> Double
information st 
    | all == 1  || s == 1  = 1
    | otherwise = 1 - logBase 2 (fromIntegral s) / logBase 2 (fromIntegral all)
 where
    all = allSolutions st
    s = solutions $ constraints $ game st


-- base 2 logarithm for large numbers
log2 :: (Ord a, Num a) => Int -> a -> Double
log2 prec n = f 0 1 1 1 n where

    f :: (Ord a, Num a) => Int -> Int -> a -> a -> a -> Double
    f a b twoa twoam1 nb = case twoa `compare` nb of
        LT              -> f (a+1) b (2*twoa) twoa nb
        GT | b < prec   -> f (2*a-1) (2*b) (twoam1 * twoa) (twoam1 * twoam1) (nb*nb)
        _ -> fromIntegral a / fromIntegral b


luck :: State -> Double
luck = luckFunction . alive . game

luckFunction :: Rational -> Double
luckFunction x 
    = max (- 1/2 * logBase 2 (realToFrac x)) 0


-------------- other

free g p 
    = not (isRevealed g p) && S.notMember p (marked g)

isRevealed g p  
    = M.member p (revealResults g)
-}
----------------------------------------


eventPos st m 
    = maybe (focus st) (calcSquare st) m

calcSquare = restrictSquare . size_ 

---------------------------------------
{-
showConfiguration c
    = unlines $ filter (not . null)
        [ unwords [show (xSize s) ++ "×" ++ show (ySize s), "table,", show $ mines c, "mines"]
        , showStrategy $ strategy c
        , showDPR $ deathProbRange c
        ]
 where
    s = size c

    showStrategy Random = ""
    showStrategy HighestProb = "least information game"

    showDPR (0,1) = ""
    showDPR (1,1) = "lucky game"
    showDPR _ = ""
            -- (a, b): ha p valséggel halnánk meg, akkor p>a esetén meghalunk, p<b esetén életben maradunk
-}
{-
        , c_allowedDeaths   :: Int
        , c_recursiveReveal :: Bool
        , c_undoAllowed     :: Bool
        , c_hintAllowed     :: Maybe HintType
-}


scoreEntry :: State -> Maybe ScoreEntry
scoreEntry st 
    | Just t <- timer st
    , cAndG playerWins st
    = Just $ ScoreEntry 
        { se_name    = userName st
        , se_time    = t + 1
        , se_alive   = aliveness $ game st
        , se_deaths  = 0
        , se_history = map (fmap (const ())) $ reverse $ undo st
        }
    | otherwise
    = Nothing


maxEntries = 10

currentScores :: State -> [ScoreEntry]
currentScores st 
    = ff $ MM.lookup (preferences st) (scores st)
 where
    ff (Just l) = l
    ff Nothing = []


addScore st
    | Just e <- scoreEntry st
    , st' <- st { scores = MM.alter (Just . maybe [e] (e:)) (preferences st) (scores st) }
    , e `elem` cutScores st'
    = Just $ st' { interrupt = Just (False, ViewScore) }
    | otherwise 
    = Nothing

 where
    cutScores :: State -> [ScoreEntry]
    cutScores st 
        = take maxEntries $ nub $ concat $ transpose [sortByAttr as $ currentScores st | as <- ass]
     where
        ass = [l2 ++ l1 | i <- [1..length (sortOrder st)], let (l1, l2)= splitAt (i-1) (sortOrder st)]

 

sortByAttr :: [ScoreAttr] -> [ScoreEntry] -> [ScoreEntry]
sortByAttr as = sortBy (flip compare `on` createRank as)
 where
    createRank :: [ScoreAttr] -> ScoreEntry -> [Double]
    createRank sl e 
        = [f | x <- sl, (a, f) <- l, a==x]
     where
        l :: [(ScoreAttr, Double)]
        l = [ (SA_Alive, realToFrac (se_alive e))
            , (SA_Time, - fromIntegral (se_time e))
            , (SA_Success, realToFrac $ successFunction (se_time e) (se_alive e))
            ]



-----------------------

-- megmondja hogy óránként várhatóan hányszor tudnánk teljesíteni egy klasszikus játékot
successFunction :: Int -> Rational -> Rational
successFunction time{-in sec-} alive{-probability-}
    = 3600 / fromIntegral time * alive  -- 1s 0.5valség   

-----------------
{-
forgetChanges x = x 
        { marked          = S.forget $ marked x
        , revealResults   = M.forget $ revealResults x
        , hiddenProbs     = M.forget $ hiddenProbs x
        }
-}