packages feed

minesweeper (empty) → 0.4

raw patch · 14 files changed

+2210/−0 lines, 14 filesdep +basedep +cairodep +containerssetup-changed

Dependencies added: base, cairo, containers, glade, gtk, random, time

Files

+ Core.hs view
@@ -0,0 +1,247 @@+-- | Core data types and functions for the game.+module Core+    ( MMap+    , emptyMMap+    , setSum+    , solutions'+    ) where++-------------------------------------------++import Place+import PlaceSet+import qualified PlaceMap as M++import Data.Function+import Data.Maybe+import Data.List hiding ((\\), insert, delete)+import qualified Data.List as List+++-------------------------------------------++data Constraint+    = Constraint+        { places_      :: PlaceSet+        , value        :: Int+        }++    deriving Eq++data ConstraintView+    = BigConstraint PlaceSet Int+    | SmallConstraint PlaceSet Int+    | Clear Place+    | Mine Place++view :: Constraint -> ConstraintView+view (Constraint ps v)+    | size ps == 1 && v == 1    = Mine $ head $ placeSetToList ps+    | size ps == 1 && v == 0    = Clear $ head $ placeSetToList ps+    | size ps > big = BigConstraint ps v+    | otherwise     = SmallConstraint ps v++big :: Int+big = 10+++---------------------------------------++constraint :: [Place] -> Int -> Constraint+constraint ps v +    = Constraint (listToPlaceSet ps) v++subConstraint :: Constraint -> Constraint -> Bool+subConstraint a b = places_ a `isSubsetOf` places_ b++similarConstraint :: Constraint -> Constraint -> Bool+similarConstraint a b = places_ a == places_ b+++-- csak akkor, ha subConstraint!+(.-.) :: Constraint -> Constraint -> Constraint+Constraint ps v .-. Constraint ps' v' = Constraint (ps \\ ps') (v-v')++connectionStrength :: Constraint -> Constraint -> [Constraint]+connectionStrength (Constraint ps v) (Constraint ps' v') = [Constraint l i | i<-[min_ .. max_]]     where ++    min_ = maximum [0, v - (size ps - m), v' - (size ps' - m)]+    max_ = minimum [m, v, v']++    m = size l+    l = intersection ps ps'++constraintSolutions :: Constraint -> Integer+constraintSolutions (Constraint ps v) = binom (size ps) v+++binom :: Int -> Int -> Integer+binom m n = binoms !! m !! min n (m-n)++binoms :: [[Integer]]+binoms = iterate f [1]  where f l = zipWith (+) (l++[0]) ([0]++l)+++-------------------------------------------+++data Constraints+    = Unsolvable+    | Solvable +        [Constraint]        -- big constraints (usually one) +        PlaceSet            -- cleared places+        PlaceSet            -- places with mines+        (M.PlaceMap [Constraint])   -- other constraints++---------------------------------------++addConstraintSimple :: Constraint -> Constraints -> Constraints+addConstraintSimple _ Unsolvable = Unsolvable +addConstraintSimple c (Solvable bc cl mi m) = case view c of+    Clear p             -> Solvable bc (insert p cl) mi m+    Mine  p             -> Solvable bc cl (insert p mi) m+    BigConstraint _ _   -> Solvable (c:bc) cl mi m+    SmallConstraint ps _-> Solvable bc cl mi $ foldr f m $ placeSetToList ps+ where++    f p m' = case M.lookup p m' of+        Nothing     -> M.insert p [c] m'+        Just cs     -> M.insert p (c:cs) m'++deleteConstraint :: Constraint -> Constraints -> Constraints+deleteConstraint _ Unsolvable = Unsolvable +deleteConstraint c (Solvable bc cl mi m) = case view c of+    Clear p             -> Solvable bc (delete p cl) mi m+    Mine  p             -> Solvable bc cl (delete p mi) m+    BigConstraint _ _   -> Solvable (deleteBy similarConstraint c bc) cl mi m+    SmallConstraint ps _-> Solvable bc cl mi $ foldr f m $ placeSetToList ps  + where++    f p m' = case M.lookup p m' of+        Just cs -> case deleteBy similarConstraint c cs of+            []      -> M.delete p m'+            l       -> M.insert p l m'++deleteConstraints :: [Constraint] -> Constraints -> Constraints+deleteConstraints cs m = foldr deleteConstraint m cs++dependentConstraints :: Constraint -> Constraints -> [Constraint]+dependentConstraints c@(Constraint ps _) (Solvable bc cl mi m)+    = List.delete c $ l ++ filter (not . disjunct ps . places_) bc+ where  +    l    = map toClear (placeSetToList $ intersection cl ps) +        ++ map toMine  (placeSetToList $ intersection mi ps) +        ++ nub (concatMap f $ placeSetToList ps) ++    toClear p = constraint [p] 0+    toMine  p = constraint [p] 1++    f p = case M.lookup p m of+        Just cs     -> cs+        Nothing     -> []++---------------------------------------++-- calculate number of different solutions+solutions :: Constraints -> Integer+solutions Unsolvable = 0+solutions (Solvable bc _ _ cs) = product (map (constraintSolutions . fst) indep) * case dep of+        []  -> 1+        ((c, xs): _)   -> let+                        cs' = minimumBy (compare `on` length) $ map (connectionStrength c) xs+                    in sum [solutions (addConstraint y s'') | y<-cs']+ where+    s' = Solvable bc empty empty cs+    s'' = deleteConstraints (map fst indep) s'++    (indep, dep) = partition (null . snd) $ map (`dependentConstraints_` s') $ nub (concatMap snd $ M.toList cs) ++ bc++dependentConstraints_ c cs = (c, dependentConstraints c cs)++solvable :: Constraints -> Bool+solvable Unsolvable = False+solvable (Solvable bc _ _ cs) = case concatMap snd (M.toList cs) ++ bc of+    []                         -> True+    (c: _) -> case dependentConstraints c s' of+        []    -> solvable (deleteConstraint c s')+        (x:_)   -> let+                        cs' = connectionStrength c x+                    in or [solvable (addConstraint y s') | y<-cs']+ where+    s' = Solvable bc empty empty cs++-- may be faster?+omittable :: Constraint -> Constraints -> Bool+omittable c@(Constraint ps v) cs = all (not . solvable) [addConstraint (Constraint ps i) cs | i <- [v-1,v-2..0] ++ [v+1..size ps]]+{-+    - if  p1 + ... + pI = n  has no solution except for  n = n0,  then  p1 + ... + pI = n0  is not needed:+             pA + pB = 1, pB + pC = 1, pC + pD = 1, pD + pA = 1      +        -->  pA + pB = 1, pB + pC = 1, pC + pD = 1+    - ...+-}++---------------------------------------++addConstraintSmart :: Constraint -> Constraints -> Constraints+addConstraintSmart c cs+    | not (solvable cs') = Unsolvable+--    | omittable c cs     = cs     -- not really needed+    | otherwise          = cs'+ where+    cs' = addConstraint c cs+++addConstraint :: Constraint -> Constraints -> Constraints+addConstraint _ Unsolvable = Unsolvable +addConstraint n@(Constraint ps v) c+    | v<0 || v>s                = Unsolvable+    | s==0                      = c+    | s>1 && v==0               = addConstraints [constraint [p] 0 | p<- placeSetToList ps] c+    | s>1 && v==s               = addConstraints [constraint [p] 1 | p<- placeSetToList ps] c+    | any ((/= v) . value) similar  = Unsolvable+    | not (null similar)        = c+    | any (==0) str             = Unsolvable+    | ((x, [y]): _) <- oneStrength+            = addConstraints [n .-. y, x .-. y, y] $ deleteConstraint x c+    | otherwise = addConstraints [x .-. n | x <- supDomains] $ addConstraintSimple n $ deleteConstraints supDomains c + where+    s = size ps++    supDomains  = [x | x <- dep, n `subConstraint` x]++    oneStrength  = filter ((==1) . length . snd) strength+    str = map (length . snd) strength++    strength = [(x, connectionStrength n x) | x <- dep, x `subConstraint` n]++    (similar, dep) = partition (similarConstraint n) $ dependentConstraints n c+++addConstraints :: [Constraint] -> Constraints -> Constraints+addConstraints l c = foldr addConstraint c l++++-----------------------------------------------++data MMap = MMap Constraints Integer++emptyMMap :: MMap+emptyMMap = mkMMap $ Solvable [] empty empty M.empty++mkMMap :: Constraints -> MMap+mkMMap c = MMap (if s==0 then Unsolvable else c) s+ where+    s = solutions c++solutions' :: MMap -> Integer+solutions' (MMap _ i) = i++setSum  :: PlaceSet -> Int -> MMap -> MMap+setSum ps v (MMap c _) = mkMMap $ addConstraint (Constraint ps v) c++---------------------------------------++++
+ Delay.hs view
@@ -0,0 +1,35 @@+module Delay +    ( doLater+    , cancelAction+    , newCancelVar+    , CancelVar+    ) where++import Control.Concurrent+import Control.Concurrent.MVar++--------------------++newtype CancelVar = C (MVar (Maybe ThreadId))++newCancelVar :: IO CancelVar+newCancelVar = fmap C $ newMVar Nothing++cancelAction :: CancelVar -> IO ()+cancelAction (C v) = do+    swapMVar v Nothing+    return ()++doLater :: RealFrac t => CancelVar -> t -> IO () -> IO ()+doLater (C v) t action = do+    tid <- forkIO $ do+        threadDelay $ round $ 1000000 * t+        mytid <- myThreadId+        b <- readMVar v+        case b of+            Just tid | tid == mytid -> action+            _                       -> return ()+    swapMVar v (Just tid)+    return ()++
+ Game.hs view
@@ -0,0 +1,253 @@++module Game+    ( GState+    , size+    , board+    , Board+    , initGState+    , deservesUndo+    , revealRec+    , flag+    , info+    , isEnd+    , isWin+    , State (..)+    , getP+    , mines+    , reveal'+--    , diff+    ) where++import Place +import PlaceSet hiding (size, empty, insert, delete)+import qualified PlaceSet+import Core++import Numeric+import Data.Maybe+import Data.List hiding (delete, insert)+import qualified Data.List as List+import Data.Map hiding (size, singleton, map)+import qualified Data.Map as Map++import System.Random++----------------------------------------++isNotMineAt :: Place -> MMap -> (Rational, MMap)+isNotMineAt p m+   = (fromIntegral (solutions' m') / fromIntegral (solutions' m), m')+  where+        m' = setSum (singleton p) 0 m++degree :: Size -> Place -> MMap -> StdGen -> (Int, MMap, StdGen)+degree s p m r = (x, l !! x, r')+ where+    ps = listToPlaceSet $ neighbours s p++    (x, r') = integerDomino (map solutions' l) r++    l = [setSum ps i m | i<-[0..PlaceSet.size ps]]++-----------------------------++data State +    = Hidden !Bool (Maybe Rational)+    | Death+    | Clear Rational !Int      -- veszélyesség; szomszédos aknák száma+        deriving (Eq, Show)++-----++type Board = Map Place State++get :: Place -> Board -> State+get p b = case Map.lookup p b of+    Just s  -> s+--    _       -> Hidden++set :: Place -> State -> Board -> Board+set p s b = insert p s b++-----------------------------++data GState = GS +    { field     :: MMap+    , board_     :: Board+    , flagged   :: Int+    , cleared   :: Int+    , alive     :: Rational +    , mines     :: Int          +    , size      :: Size+-----------+    , revMod    :: Maybe Rational+    }++deservesUndo g (g':_) | board_ g == board_ g' = 1+deservesUndo g (_:g':_) | board_ g == board_ g' = 2+deservesUndo _ _ = 0++initGState :: Size -> Int -> GState+initGState s mines_ = GS +    { field     = setSum (places s) mines_ emptyMMap+    , board_     = Map.fromList $ zip (placeSetToList $ places s) $ repeat $ Hidden False Nothing+    , flagged   = 0+    , cleared   = 0+    , alive     = 1+    , mines     = mines_+    , size      = s+    , revMod    = Nothing+    }++reveal' :: Bool -> GState -> GState+reveal' all gs +    = case revMod gs of+        Just 0 | all -> unreveal gs+        Just i | i > 0 && not all -> unreveal gs+        _ -> revealBoard all gs++unreveal gs | isJust (revMod gs)+    = gs { revMod = Nothing, board_ = Map.map f $ board_ gs }  where+ +    f (Hidden fl _) = Hidden fl Nothing+    f x = x+unreveal gs = gs++revealBoard all gs -- | revMod gs+    | solutions' (field gs) == 0 = gs+    | otherwise+        = gs { board_ = foldr (uncurry Map.insert) (board_ gs) b, revMod = Just $ if all then 0 else mi }  where+ +    b = concatMap h $ Map.toList $ board_ gs++    mi = maximum $ map (k . snd) b  where    k (Hidden _ (Just i)) = i++    h (p, Hidden fl _) = [(p, Hidden fl $ Just $ fst $ isNotMineAt p (field gs))]+    h _ = []++board gs +    = case revMod gs of+        Just i | i /= 0 -> Map.map (f i) $ board_ gs+        _   -> board_ gs+ where+    f i (Hidden False (Just j)) | j /= 0 && j < i  = Hidden False Nothing+    f _ x = x+++isHidden (Hidden False x) = True+isHidden _ = False++reveal :: Place -> GState -> StdGen -> (GState, StdGen)+reveal p g r+    | not $ isHidden (get p (board_ g))     = (g, r)+    | pr == 0 = (g { field = f, alive = 0, board_ = insert p Death (board_ g) }, r)+    | otherwise =  (g { field = f', board_ = insert p (Clear pr d) (board_ g), alive = pr * alive g, cleared = 1 + cleared g }, r')+ where+    (pr, f) = isNotMineAt p (field g)+    (d, f', r')= degree (size g) p f r++flag :: Place -> GState -> ([(Place, State)], GState)+flag p g = case get p $ board_ g of+    Hidden True r   -> h (Hidden False r) (-1)+    Hidden False r  | flagged g < mines g   -> h (Hidden True r) 1+    _           -> ([], g)+ where+    h x c = ([(p, x)], g { flagged = flagged g + c, board_ = set p x $ board_ g })++getP :: Place -> GState -> State+getP p g = get p (board_ g)+++-------------++data Report +    = Report Int Int Rational Double+        deriving Eq++instance Show Report where+    show (Report _ _ 0 _)        = "Sorry, you died of necessity."+    show (Report 0 0 x y)        = "Congratulations! You won with " ++ show_ 2 (luckinessFunction x) ++ " luckyness."+    show (Report i _ x y)        = "Mines left: " ++ show i ++ "  Information: " ++ show_ 1 (100*y) ++ "%" ++ "  Luckyness: " ++ show_ 2 (luckinessFunction x)++luckinessFunction :: Rational -> Double+luckinessFunction x = max (- log (realToFrac x) / log 4) 0++show_ :: Int -> Double -> [Char]+show_ i x = showFFloat (Just i) x ""++eval :: GState -> Report+eval g@(GS {size= s@(xS, yS)}) +    = Report (mines g - flagged g) (xS*yS - cleared g - flagged g) (alive g) y+ where+    i = solutions' $ setSum (places s) (mines g) emptyMMap++    a = fromIntegral (solutions' $ field g) / fromIntegral i+    b = 1 / fromIntegral i++    y+        | b == 1    = 1+        | otherwise = luckinessFunction a / luckinessFunction b++isEnd :: GState -> Bool+isEnd g = case eval g of+    Report _ _ 0 _ -> True+    Report 0 0 _ _ -> True+    _              -> False++isWin :: GState -> Bool+isWin g = case eval g of+    Report 0 0 _ _ -> True+    _              -> False++info :: GState -> String+info = show . eval++-----------------------------+fff ~(x,g,r) = (x, unreveal g,r)++isFlagged (Hidden True _) = True+isFlagged _ = False++revealRec :: Place -> GState -> StdGen -> ([(Place, State)], GState, StdGen)+revealRec p g r = fff $ case getP p g of+    Clear _ x | x == sum [1 | q <- neighbours (size g) p, isFlagged (getP q g) ]  +            -> revRecL (neighbours (size g) p) g r+    _       -> revealRecS p g r++revealRecS :: Place -> GState -> StdGen -> ([(Place, State)], GState, StdGen)+revealRecS p g r = case getP p g of+    Hidden False _    -> revRec p $ reveal p g r+    _           -> ([], g, r)+++revRec :: Place -> (GState, StdGen) -> ([(Place, State)], GState, StdGen)+revRec p (g, r) = strictT2 p gp  .: case gp of+    Clear _ 0   -> revRecL (neighbours (size g) p) g r+    _           -> ([], g, r)+ where+    gp = getP p g++revRecL :: [Place] -> GState -> StdGen -> ([(Place, State)], GState, StdGen)+revRecL [] g r    = ([], g, r)+revRecL (p:ps) g r = l .++ revRecL ps g' r'  where ++    ~(l, g', r') = revealRecS p g r++strictT2 a b = a `seq` b `seq` (a, b)++p .: ~(ps, g, r) = (p: ps, g, r)++l .++ ~(l', g, r) = (l ++ l', g, r)++----------++-- | Get a value from a discrete distribution with the domino algorithm.+integerDomino :: [Integer] -> StdGen -> (Int, StdGen)+integerDomino [_] r = (0, r)+integerDomino l r = (j, r')+ where+    (x, r') = randomR (1, sum l) r++    j = length $ takeWhile (<x) $ scanl1 (+) l++
+ LICENSE view
@@ -0,0 +1,31 @@+Copyright Péter Diviánszky 2008++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Péter Diviánszky nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+
+ Minesweeper.hs view
@@ -0,0 +1,254 @@++import Table+import Delay+import Timer+import Game+import Place++import Paths_minesweeper++import Graphics.UI.Gtk hiding (Clear)+import Graphics.UI.Gtk.Glade++import Control.Concurrent+import Control.Concurrent.MVar+import Control.Monad (when)++import System.Random++--------------------------------------++type UGState = ([GState], [GState]) ++-- | Program state+data ProgramState = ProgramState+    { timer  :: Tim             -- ^ timer state+    , label  :: Label           -- ^ label component (constant)+    , gstate :: MVar UGState    -- ^ game state (model)+    , table  :: GameTable       -- ^ table state (view)+    , seed   :: MVar StdGen+    }+++msglade :: IO GladeXML+msglade = do+    f <- getDataFileName "ms.glade" +    Just xml <- xmlNew f+    return xml++main :: IO ()+main = mdo+    initGUI+    timeoutAddFull (yield >> return True) priorityDefaultIdle 50++    xml <- msglade+    window      <- xmlGetWidget xml castToWindow "window1"+    lab         <- xmlGetWidget xml castToLabel "label1"+    timeLabel   <- xmlGetWidget xml castToLabel "label7"+    asp_        <- xmlGetWidget xml castToAspectFrame "aspectframe1"++    asp     <- newTable asp_ (checkWin pst . flag') (checkWin pst . revealRec)++    se <- newStdGen+    ss <- newMVar se++    let pst = ProgramState tim lab gst asp ss++    mapM_ (addMenuItem xml)+        [ ("imagemenuitem5",  widgetDestroy window)+        , ("imagemenuitem1",  newGame pst)+        , ("imagemenuitem6",  preferences pst)+        , ("imagemenuitem3",  helpDialog)+        , ("imagemenuitem10", aboutDialog)+        , ("imagemenuitem9",  clear pst $ checkWin pst . revealRec)+        , ("imagemenuitem11", clear pst $ checkWin pst . flag')+        , ("imagemenuitem12", undo pst)+        , ("imagemenuitem15", redo pst)+        , ("menuitem2",  reveal False pst)+        , ("menuitem6",  reveal True pst)+        ]++    onDestroy window mainQuit++    gst     <- newMVar ([initGState (10,10) 20], [])+    tim     <- newTimer (labelSetText timeLabel) ++    hideTableVar <- newCancelVar+    showTableVar <- newCancelVar++    onFocusOut window $ \_ -> do+        when' (isActiveTimer tim) $ do+            stopTimer True tim+            doLater hideTableVar (1::Double) (setTableSt asp $ \s -> if s == Fixed then s else Stopped)+        return False++    onFocusIn window $ \_ -> do+        when' (fmap not $ isActiveTimer tim) $ do+            cancelAction hideTableVar+            setTableSt asp $ \s -> if s == Fixed then s else Normal Nothing+            startTimer True tim+        return False++    widgetShowAll window+    resizeTable_ pst++    mainGUI++flag' a b r = (c, d, r) where (c, d) = flag a b++clear :: ProgramState -> (Place -> IO ()) -> IO ()+clear pst m = do+    p <- getFocusPos (table pst)+    m p ++reveal :: Bool -> ProgramState -> IO ()+reveal all pst = withUState pst f where+    f s@(g: _, _) = do+        startTimer False (timer pst)+        stopTimer False (timer pst)+        return $ smartUndo (reveal' all g) s++undo :: ProgramState -> IO ()+undo pst = withUState pst f where+    f (g:g':gs, redos) = do+        stopTimer False (timer pst)+        return (g':gs, g:redos)+    f x = return x++redo :: ProgramState -> IO ()+redo pst = withUState pst f where+    f (gs, g:redos) = return (g:gs, redos)+    f x = return x++newGame :: ProgramState -> IO ()+newGame pst = withUState pst f where+    f (as, _) = do+        resetTimer (timer pst)+        return ([last as], [])++resizeTable_ :: ProgramState -> IO ()+resizeTable_ pst = do++    (g:_, _) <- readMVar (gstate pst)+    resizeTable (size g) (table pst)+    newGame pst+++addMenuItem +    :: GladeXML+   -> (String, IO ())+   -> IO (ConnectId MenuItem)++addMenuItem xml (str, action) = do+    m   <- xmlGetWidget xml castToMenuItem str+    onActivateLeaf m action+++preferences :: ProgramState -> IO ()+preferences pst = do+    xml <- msglade+    a   <- xmlGetWidget xml castToDialog "dialog1"+    sx  <- xmlGetWidget xml castToSpinButton "spinbutton3"+    sy  <- xmlGetWidget xml castToSpinButton "spinbutton1"+    ms  <- xmlGetWidget xml castToSpinButton "spinbutton4"++    al@(g:_, _) <- takeMVar $ gstate pst+    let (xS,yS) = size g+    set sx [spinButtonValue := fromIntegral xS]+    set sy [spinButtonValue := fromIntegral yS]+    set ms [spinButtonValue := fromIntegral $ mines g]++    let setRange = do+        x <- get sx spinButtonValue+        y <- get sy spinButtonValue+        spinButtonSetRange ms 0 (x*y)++    onValueSpinned sx setRange+    onValueSpinned sy setRange++    setRange++    r <- dialogRun a+    case r of+        ResponseOk  -> do+            x   <- get sx spinButtonValue+            y   <- get sy spinButtonValue+            mines_   <- get ms spinButtonValue+            putMVar (gstate pst) ([initGState (round x, round y) (round mines_)], [])+            resizeTable_ pst++        _ -> +            putMVar (gstate pst) al++    widgetDestroy a+    return ()++++checkWin :: ProgramState -> (GState -> StdGen -> ([(Place, State)], GState, StdGen)) -> IO ()+checkWin pst f = withUState pst $ \x -> case x of+    s@(g:gs, redo)   | not (isEnd g) ->  do+        startTimer False $ timer pst+        r <- takeMVar $ seed pst+        let (ps, g', r') = f g r+        adjustButtsInc (table pst) ps+        putMVar (seed pst) r' +        when (isEnd g') $ do+            _ti <- stopTimer False $ timer pst+            return ()+        return $ smartUndo g' s++    _   -> return x++smartUndo g' (gs, redo) = (g': drop x gs, if x==1 then redo else [])+ where+   x = deservesUndo g' gs+++--------------------------++aboutDialog :: IO ()+aboutDialog =  do+    xml <- msglade+    a   <- xmlGetWidget xml castToAboutDialog "aboutdialog1"+    dialogRun a+    widgetDestroy a+    return ()++helpDialog :: IO ()+helpDialog =  do+    xml <- msglade+    a   <- xmlGetWidget xml castToDialog "dialog2"+    dialogRun a+    widgetDestroy a+    return ()+++withUState pst f = modifyMVarInSeparateThread (gstate pst) (\x -> f x >>= h)  where+    h s@(g: gs, redos) = do+        adjustButts (table pst) (board g)+        setTableSt (table pst) $ if isEnd g then const Fixed else ff+        labelSetText (label pst) $ info g+        return s++    ff (Normal x) = Normal x+    ff _ = Normal Nothing+-------------------++when' :: Monad m => m Bool -> m a -> m ()+when' f g = f >>= \b -> if b then g >> return () else return ()++-- | modifies an mvar if it is not taken already+modifyMVarInSeparateThread :: MVar a -> (a -> IO a) -> IO ()+modifyMVarInSeparateThread v f = do+    b <- isEmptyMVar v+    if not b +        then do+            forkIO (modifyMVar_ v f)+            return () +        else +            return ()+++------------+
+ Place.hs view
@@ -0,0 +1,76 @@+{-# LANGUAGE ViewPatterns #-}+-- | Places.+module Place +    (+    -- * Size+      Size++    -- * Place+    , Place+    , place+    , coords++    -- * For performance+    , hashPlace+    , unHashPlace+    , placesInAColumn++    -- * Useful functions+    , neighbours++    ) where++import Data.Bits++-----------++-- | Size of a board.+type Size = (Int, Int)+++-- | Place on a board (with fixed width).+--+-- Simpler but slower implementation: +--+-- > data Place = P !Int !Int +-- >    deriving (Eq, Ord, Show)++newtype Place = P Int deriving (Eq, Ord)++instance Show Place where+    show (coords -> (x, y)) = "place " ++ show x ++ " " ++ show y++place :: Int -> Int -> Place+place i j = P $ shiftL i 8 .|. j++coords :: Place -> (Int, Int)+coords (P i) = (shiftR i 8, i .&. 255)++-- | Perfect hash function.+hashPlace :: Place -> Int+hashPlace (P i) = i++unHashPlace :: Int -> Place+unHashPlace = P++placesInAColumn :: Int -> (Int, Int) -> [Place]+placesInAColumn i (j1, j2) = map P $ [i' + j1 .. i' + j2]  where i' = shiftL i 8++-- | Neighbours of a place.+--+-- Examples:+--+-- > neighbours (8, 8) (place 2 3) +-- >  == [place 1 2, place 1 3, place 1 4, place 2 2, place 2 4, place 3 2, place 3 3, place 3 4]+--+-- > neighbours (8, 8) (place 2 1) +-- >  == [place 1 1, place 1 2, place 2 2, place 3 1, place 3 2]+neighbours :: Size -> Place -> [Place]+neighbours (xS, yS) p@(P q) = f' q ++ (if j > 1 then f (q-1) else []) ++ (if j < yS then f (q+1) else [])+ where+    (i, j) = coords p++    f  r = P r: (if i > 1 then [P (r - 256)] else []) ++ (if i < xS then [P (r + 256)] else [])+    f' r =      (if i > 1 then [P (r - 256)] else []) ++ (if i < xS then [P (r + 256)] else [])++
+ PlaceMap.hs view
@@ -0,0 +1,39 @@+-- | Maps of places+module PlaceMap+    ( PlaceMap+    , fromList+    , toList+    , empty+    , lookup+    , insert+    , delete+    ) where++import Place++import qualified Data.IntMap as M+import Prelude hiding (lookup)++-------------++-- | Map of places.+type PlaceMap = M.IntMap++fromList :: [(Place, a)] -> PlaceMap a+fromList = M.fromList . map (\(p, a) -> (hashPlace p, a))++toList :: PlaceMap a -> [(Place, a)]+toList = map (\(p, a)-> (unHashPlace p, a)) . M.toList++empty :: PlaceMap a+empty = M.empty++lookup :: Place -> PlaceMap a -> Maybe a+lookup p m = M.lookup (hashPlace p) m++insert :: Place -> a -> PlaceMap a -> PlaceMap a+insert p a m = M.insert (hashPlace p) a m++delete :: Place -> PlaceMap a -> PlaceMap a+delete p m = M.delete (hashPlace p) m+
+ PlaceSet.hs view
@@ -0,0 +1,86 @@+-- | Sets of places+module PlaceSet+    ( PlaceSet+    , listToPlaceSet+    , placeSetToList+    , singleton+    , empty+    , nullPS+    , insert+    , delete+    , member+    , size+    , (\\)+    , unions+    , intersection+    , disjunct+    , isSubsetOf++    -- * Useful functions+    , places++    ) where++import Place++import qualified Data.IntSet as S++-------------++-- | Set of places.+type PlaceSet = S.IntSet++listToPlaceSet :: [Place] -> PlaceSet+listToPlaceSet = S.fromList . map hashPlace++placeSetToList :: PlaceSet -> [Place]+placeSetToList = map unHashPlace . S.toList++singleton :: Place -> PlaceSet+singleton = S.singleton . hashPlace++-- | The empty PlaceSet.+empty :: PlaceSet+empty = S.empty++insert :: Place -> PlaceSet -> PlaceSet+insert p ps = S.insert (hashPlace p) ps++delete :: Place -> PlaceSet -> PlaceSet+delete p ps = S.delete (hashPlace p) ps++-- | True if the PlaceSet is empty.+nullPS :: PlaceSet -> Bool+nullPS = S.null++member :: Place -> PlaceSet -> Bool+member p = S.member (hashPlace p)++size :: PlaceSet -> Int+size = S.size++-- | Difference.+(\\) :: PlaceSet -> PlaceSet -> PlaceSet+(\\) = (S.\\)++unions :: [PlaceSet] -> PlaceSet+unions = S.unions++disjunct :: PlaceSet -> PlaceSet -> Bool+disjunct a b = S.null (S.intersection a b)++intersection :: PlaceSet -> PlaceSet -> PlaceSet+intersection = S.intersection++isSubsetOf :: PlaceSet -> PlaceSet -> Bool+isSubsetOf = S.isSubsetOf++----------------------++-- | All places at a board.+places :: Size -> PlaceSet+places (xS, yS) = listToPlaceSet [p | x<-[1..xS], p <- placesInAColumn x (1, yS)]++++
+ Setup.hs view
@@ -0,0 +1,5 @@++import Distribution.Simple++main = defaultMainWithHooks defaultUserHooks+
+ Table.hs view
@@ -0,0 +1,275 @@+{-# LANGUAGE ViewPatterns #-}+module Table +    ( GameTable+    , newTable+    , St (Stopped, Normal, Fixed)+    , setTableSt+    , resizeTable+    , adjustButts+    , adjustButtsInc+    , getFocusPos +    ) where++import TableGraphics+import Place+import PlaceSet+import Game++import Graphics.Rendering.Cairo+import Graphics.UI.Gtk.Gdk.EventM++import Graphics.UI.Gtk hiding (Clear, fill)++import Control.Monad (when)+import Control.Concurrent.MVar++import qualified Data.Map as M++import Data.Maybe++---------------------++-- ez van kivetítve+data IState  = IState+    { size_     :: (Int, Int)+    , board_    :: Board+    , focusPos  :: Place+    , hidden    :: St+    } ++data St +    = Stopped +    | Fixed +    | Normal (Maybe Place)+    deriving Eq++data GameTable +    = T +        { frame     :: AspectFrame+        , area      :: DrawingArea +        , states    :: MVar IState+        , flag_     :: Place -> IO ()+        , reveal    :: Place -> IO ()+        }++---------------------++newTable :: AspectFrame -> (Place -> IO ()) -> (Place -> IO ()) -> IO GameTable+newTable asp fA rA = do+    canvas <- drawingAreaNew+    m <- newMVar undefined+    let g = T asp canvas m fA rA++    containerAdd asp canvas+    set canvas [widgetCanFocus := True]+    widgetAddEvents canvas [LeaveNotifyMask, PointerMotionMask, KeyPressMask]++    canvas `on` exposeEvent       $ updateCanvas  g+    canvas `on` leaveNotifyEvent  $ changeCanvas_ g+    canvas `on` motionNotifyEvent $ changeCanvas  g+    canvas `on` buttonPressEvent  $ changeCanvas' g+    canvas `on` keyPressEvent     $ changeCanvasKey g+    return g++getFocusPos :: GameTable -> IO Place+getFocusPos g = fmap focusPos $ readMVar (states g)++setTableSt :: GameTable -> (St -> St) -> IO ()+setTableSt g fx = redrawButts_ g f where+    f ss+        | x == hh   = ([], ss)+        | Stopped `elem` [x, hh]+            = (M.toList $ board_ ss, ss')+        | otherwise +            = (map h $ focusPos ss: ff hh ++ ff x, ss')+      where+        ss' = ss { hidden = x }+        h p = (p, board_ ss M.! p)+        x = fx hh+        hh = hidden ss+        ff (Normal (Just x)) = [x]+        ff _  = []+++resizeTable :: (Int, Int) -> GameTable ->  IO ()+resizeTable s@(xS, yS) g = do++    set (frame g) [aspectFrameRatio := fromIntegral xS / fromIntegral yS]++    swapMVar (states g) $ IState+        { size_     = s+        , board_    = M.fromList $ zip (placeSetToList $ places s) $ repeat $ Hidden False Nothing+        , focusPos  = place 1 1+        , hidden    = Normal Nothing+        } ++    win <- drawingAreaGetDrawWindow' g+    drawWindowClear win+    updateCanvas' g++------------------++updateCanvas :: GameTable -> EventM EExpose Bool+updateCanvas g = do+    liftIO $ updateCanvas' g+    return True++updateCanvas' g = do+    redrawButts_ g $ \ss -> (M.toList $ board_ ss, ss)++adjustButts :: GameTable -> Board -> IO ()+adjustButts g b =+    redrawButts_ g $ \ss -> ([p | (p,y) <- zip (M.assocs b) (M.elems $ board_ ss), snd p /= y], ss { board_ = b })+++adjustButtsInc :: GameTable -> [(Place, State)] -> IO ()+adjustButtsInc g [] = return ()+adjustButtsInc g ((p, s):ps) = do++    redrawButts_ g $ \ss -> ([(p, s)], ss { board_ = M.insert p s $ board_ ss })+    adjustButtsInc g ps++redrawButts :: GameTable -> [Place] -> IO ()+redrawButts g l +    = redrawButts_ g $ \ss -> ([(p, board_ ss M.! p)| p <- l], ss)+++redrawButts_ :: GameTable -> (IState -> ([(Place, State)], IState)) -> IO ()+redrawButts_ g f = do++    ss <- takeMVar (states g)+    let (ps, ss') = f ss+    redrawButts__ g ss' ps+    putMVar (states g) $ ss'+++redrawButts__ :: GameTable -> IState -> [(Place, State)] -> IO ()+redrawButts__ gg g l = do+    win <- drawingAreaGetDrawWindow' gg+    let+        size = size_ g+        m   = board_ g+        b   = hidden g+        pp = case b of+            Normal x    -> x+            _           -> Nothing+        foc = focusPos g+    let f (p, s) = do+            renderWithDrawable' p size win $ drawPlace (b == Stopped) (isNormal b && pp == Just p) (p == foc && isNormal b) p s+    mapM_ f l+    +isNormal (Normal _) = True+isNormal _ = False++renderWithDrawable' (coords -> (x,y)) (xx, yy) win m = do+    (width, height) <- drawableGetSize_ win+    let+        a = width/fromIntegral xx+        b = height/fromIntegral yy+    drawWindowBeginPaintRect win $ Rectangle (round $ a*fromIntegral (x-1)) (round $ b*fromIntegral (y-1)) (round a) (round b)+    renderWithDrawable win $ do+        scale a b+        translate (fromIntegral x - 0.5) (fromIntegral y - 0.5)+        m+    drawWindowEndPaint win+++drawableGetSize_ win = do +    (width, height) <- drawableGetSize win+    return (realToFrac width, realToFrac height)++changeCanvas :: GameTable -> EventM EMotion Bool+changeCanvas g = do+    pos <- eventCoordinates+    liftIO $ do+        n <- calcPos_ pos g+        changeC n g+++changeCanvas_ :: GameTable -> EventM ECrossing Bool+changeCanvas_ g = liftIO $ do+    let n = Nothing+    changeC n g++changeC n g = do+    ss <- readMVar (states g)+    case hidden ss of+        Normal m -> do+            ss <- takeMVar (states g)+            putMVar (states g) $ ss { hidden = Normal n }++            if m /= n  +                then redrawButts g $ catMaybes [m, n]+                else return ()+        _ -> return ()+    return True+++changeCanvas' :: GameTable -> EventM EButton Bool+changeCanvas' g = do+    pos <- eventCoordinates+    b <- eventButton+    liftIO $ do+        ss <- readMVar (states g)+        when (isNormal (hidden ss)) $ do+            n <- calcPos_ pos g+            case (n, b) of+                (Just p, LeftButton)    -> reveal g p+                (Just p, RightButton)   -> flag_  g p+                _                       -> return ()+    return True++++changeCanvasKey :: GameTable -> EventM EKey Bool+changeCanvasKey g = do+    k <- eventKeyName+    liftIO $ do+        case k of+            "Right" -> moveFocus ( 1, 0)+            "Left"  -> moveFocus (-1, 0)+            "Down"  -> moveFocus ( 0, 1)+            "Up"    -> moveFocus ( 0,-1)+            _        -> return False+ where+    moveFocus (dx, dy) = do+        ss <- readMVar (states g)+        when (isNormal (hidden ss)) $ do+            ss <- takeMVar (states g)+            let +                foc@(coords -> (x, y)) = focusPos ss+                (sx, sy)    = size_ ss+                foc' = place (max 1 $ min sx $ dx + x) (max 1 $ min sy $ dy + y)+            putMVar (states g) $ ss { focusPos = foc' }+            if foc /= foc'+                then redrawButts g [foc, foc']+                else return ()+        return True+++drawingAreaGetDrawWindow' = widgetGetDrawWindow . area++calcPos_ pos g = do+    win <- drawingAreaGetDrawWindow' g+    ss <- readMVar (states g)++    (width, height) <- drawableGetSize_ win+    return $ calcPos (size_ ss) (width, height) pos++calcPos (xx, yy) (width, height) (x,y)+    | i + 1 `between` (1, xx) && j + 1 `between` (1, yy)  {- && ii>0 && ii<9 && jj>0 && jj<9 -}     +        = Just $ place (i+1) (j+1)+    | otherwise = Nothing+ where+    a = floor (x/width*10* fromIntegral xx)+    b = floor (y/height*10* fromIntegral yy)++    (i,_ii) = a `divMod` 10+    (j,_jj) = b `divMod` 10++infix 4 `between`+a `between` (b, c) = b <= a && a <= c++++
+ TableGraphics.hs view
@@ -0,0 +1,168 @@+{-# LANGUAGE ViewPatterns #-}+module TableGraphics+    ( drawPlace+    ) where++import Place+import Game (State (..))++import Graphics.Rendering.Cairo++import Control.Monad (when)++-----------------------------------------+++drawPlace +    :: Bool     -- ^ signs are not shown+    -> Bool     -- ^ highlighted+    -> Bool     -- ^ focused+    -> Place+    -> State +    -> Render ()++drawPlace bb b foc (coords -> (i,j)) st = do++++    rectangle (-0.4) (-0.4) 0.8 0.8+    setLineWidth 0.03+    setSourceRGB' bg+    strokePreserve+    setSourceRGB' $ case st of+            Clear _  _  -> bg+            Hidden True _ -> bg+            Death       -> bg+            _           -> if b then bg else blue++    fill+    setSourceRGB' black+    if foc +        then do+            rectangle (-0.35) (-0.35) 0.7 0.7+            setLineWidth 0.02+            setDash [0.1,0.1] (if even (i + j) then 0 else 0.1)+            stroke+            setDash [] 0+        else return ()+    setLineCap LineCapRound+    setLineWidth 0.1++    if bb   +        then return ()+        else case st of+            Death -> do+                setLineCap LineCapSquare+                moveTo (-0.25) (-0.25)+                lineTo   0.25    0.25+                stroke+                moveTo   0.25  (-0.25)+                lineTo (-0.25)   0.25+                stroke++            Hidden False (Just d) -> do+                setLineWidth 0.03+                arc 0 0 0.25 0 (2*pi*(1- realToFrac d))+                setSourceRGB' $ Color_ 0.3 0.4 0.5+                stroke+                arc 0 0 0.25 (2*pi*(1- realToFrac d)) (2*pi)+                setSourceRGB' $ Color_ 1 1 1+                stroke+                +            Hidden True bbb -> do+                setLineWidth 0.03+                arc 0 0 0.25 0 (2*pi)+                setSourceRGB' $ if b then bg else blue+                fillPreserve+                setSourceRGB' black+                stroke+                when (maybe False (/=0) bbb) $ do+                    setLineCap LineCapSquare+                    moveTo (-0.25) (-0.25)+                    lineTo   0.25    0.25+                    stroke+                    moveTo   0.25  (-0.25)+                    lineTo (-0.25)   0.25+                    stroke+            Clear _ n -> do+                case n of +                    0   -> do+                        setLineCap LineCapSquare+                        setLineWidth 0.03+                        moveTo (-0.2) 0+                        lineTo   0.2  0+                        stroke+                    1   -> do+                        point 0 0+                    2   -> do+                        point (-0.2) 0+                        point   0.2  0+                    3   -> do+                        point   0    (-0.20)+                        point (-0.2)   0.14+                        point   0.2    0.14+                    4   -> do+                        point (-0.2)   0.2+                        point   0.2    0.2+                        point (-0.2) (-0.2)+                        point   0.2  (-0.2)+                    5   -> do+                        point   0      0+                        point (-0.2)   0.2+                        point   0.2    0.2+                        point (-0.2) (-0.2)+                        point   0.2  (-0.2)+                    6   -> do+                        point (-0.2)   0+                        point   0.2    0+                        point (-0.2)   0.2+                        point   0.2    0.2+                        point (-0.2) (-0.2)+                        point   0.2  (-0.2)+                    7   -> do+                        point   0      0+                        point (-0.2)   0+                        point   0.2    0+                        point (-0.2)   0.2+                        point   0.2    0.2+                        point (-0.2) (-0.2)+                        point   0.2  (-0.2)+                    8   -> do+                        point (-0.2)   0+                        point   0.2    0+                        point   0    (-0.2)+                        point   0      0.2 +                        point (-0.2)   0.2+                        point   0.2    0.2+                        point (-0.2) (-0.2)+                        point   0.2  (-0.2)+                return ()+            _ -> return ()+++point :: Double -> Double -> Render ()+point x y = do+    moveTo x y+    relLineTo 0 0+    stroke+++data Color_ = Color_ Double Double Double++bg, blue, black, white :: Color_+blue = Color_ 0.65 0.8 1+black = Color_ 0 0 0+white = Color_ 1 1 1+bg = Color_ 0.9 0.9 0.9++setSourceRGB' :: Color_ -> Render ()+setSourceRGB' (Color_ r g b) = setSourceRGB r g b+++betw ::+    Double -> Color_ -> Color_ -> Color_+betw pr (Color_ a b c) (Color_ a' b' c') = Color_ (f a a') (f b b') (f c c') where++    f i j = max 0 $ min 1 $ i + pr * (j - i)++
+ Timer.hs view
@@ -0,0 +1,117 @@+module Timer+    ( Tim+    , newTimer+    , startTimer+    , stopTimer+    , resetTimer+    , isActiveTimer++    , showSeconds+    ) where++import Control.Concurrent+import Control.Concurrent.MVar++import Data.Time.Clock++-------------------------------------------++data StoppedTimerState +    = Initial | Stopped | Halted+        deriving Eq++data Timer+    = ActiveTimer +        ThreadId    -- which thread is responsible for the updating of the timer+        UTCTime+    | StoppedTimer +        StoppedTimerState+        NominalDiffTime++type Action = String -> IO ()++type Tim = MVar +    ( Action        -- this is global for this module, but Haskell has no parameterized modules..+    , Timer)++-------------------++newTimer :: Action -> IO Tim+newTimer action +    = newMVar (action, StoppedTimer Initial 0)++startTimer :: Bool -> Tim -> IO ()+startTimer b tim = do+    a@(timeL, xx) <- takeMVar tim+    case xx of+        StoppedTimer x s | b && x == Stopped || not b && x /= Halted -> do+            tid <- forkIO $ modTime tim+            ti <- getCurrentTime+            putMVar tim (timeL, ActiveTimer tid $ addUTCTime (-s) ti)+        _ -> do+            putMVar tim a++stopTimer :: Bool -> Tim -> IO NominalDiffTime+stopTimer b tim = do+    a@(timeL, x) <- takeMVar tim+    case x of+        ActiveTimer _ ti -> do+            ti' <- getCurrentTime+            let d =  diffUTCTime ti' ti+            putMVar tim (timeL, StoppedTimer xx d)+            timeL $ (if b then "Stopped at " else "Halted at ") ++ showSeconds (round d)+            return d+        StoppedTimer _ d -> do+            putMVar tim (timeL, StoppedTimer xx d)+            return d+ where+    xx = if b then Stopped else Halted++resetTimer :: Tim -> IO ()+resetTimer tim = do+    (timeL, _) <- takeMVar tim+    timeL "Timer will start"+    putMVar tim (timeL, StoppedTimer Initial 0)+    return ()++isActiveTimer :: Tim -> IO Bool+isActiveTimer tim = do+    (_, x) <- readMVar tim+    return $ case x of+        ActiveTimer _ _ -> True+        _               -> False+++-----------------++modTime ::  Tim -> IO ()+modTime tim = do+    tid <- myThreadId+    (timeL, x) <- readMVar tim+    case x of+        ActiveTimer tid' ti | tid == tid'  -> do+            ti' <- getCurrentTime+            let diff = diffUTCTime ti' ti+            timeL $ "Time: " ++ showSeconds (round diff)+            threadDelay (computeWaitTime diff)+            modTime tim+        _   ->+            return ()++computeWaitTime :: NominalDiffTime -> Int {-milliseconds-}+computeWaitTime x = 1000000 * (1 + round y) - round (1000000 * y) - 100000   where (_, y) = properFraction x :: (Integer, NominalDiffTime)++-------------++showSeconds :: Integer -> String+showSeconds s = f h ++ ":" ++ f m' ++ ":" ++ f s' where++    (m, s') = divMod s 60+    (h, m') = divMod m 60++    f :: Integer -> String+    f i = reverse $ take 2 $ reverse $ "0" ++ show i++++
+ minesweeper.cabal view
@@ -0,0 +1,49 @@+name:                minesweeper+version:             0.4+category:            Game+synopsis:            Minesweeper game which is always solvable without guessing+description:         +	Minesweeper game which is always solvable without guessing. +    .+    TODO list:+    .+    * Source code documentation and improvement+    .+    * Implement the score table.+stability:          alpha+license:            BSD3+license-file:       LICENSE+author:             Péter Diviánszky+maintainer:         divip@aszt.inf.elte.hu+data-files:         ms.glade+cabal-version:      >=1.4+build-type:         Simple++executable          minesweeper+  ghc-options:      -Wall -fno-warn-incomplete-patterns -fno-warn-name-shadowing+  build-depends:    base >= 3.0, +                    containers, +                    random, +                    time, +                    glade, +                    gtk >= 0.10, +                    cairo >= 0.10++  main-is:          Minesweeper.hs++  other-modules:    Place,+                    PlaceSet,+                    PlaceMap,+                    Delay,+                    Core, +                    Game,+                    Table,+                    TableGraphics,+                    Timer++  extensions:       RecursiveDo,+                    NamedFieldPuns,+                    ViewPatterns,+                    PatternGuards++
+ ms.glade view
@@ -0,0 +1,575 @@+<?xml version="1.0"?>+<glade-interface>+  <!-- interface-requires gtk+ 2.16 -->+  <requires lib="gnome"/>+  <!-- interface-requires gnome 2475.30408 -->+  <!-- interface-naming-policy toplevel-contextual -->+  <widget class="GtkWindow" id="window1">+    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>+    <property name="title" translatable="yes">The Lucky Minesweeper</property>+    <property name="window_position">center</property>+    <property name="default_width">500</property>+    <property name="default_height">590</property>+    <child>+      <widget class="GtkVBox" id="vbox1">+        <property name="visible">True</property>+        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>+        <child>+          <widget class="GtkMenuBar" id="menubar1">+            <property name="visible">True</property>+            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>+            <child>+              <widget class="GtkMenuItem" id="menuitem1">+                <property name="visible">True</property>+                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>+                <property name="label" translatable="yes">_File</property>+                <property name="use_underline">True</property>+                <child>+                  <widget class="GtkMenu" id="menu1">+                    <property name="visible">True</property>+                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>+                    <child>+                      <widget class="GtkImageMenuItem" id="imagemenuitem1">+                        <property name="label">gtk-new</property>+                        <property name="visible">True</property>+                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>+                        <property name="use_underline">True</property>+                        <property name="use_stock">True</property>+                      </widget>+                    </child>+                    <child>+                      <widget class="GtkSeparatorMenuItem" id="separatormenuitem1">+                        <property name="visible">True</property>+                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>+                      </widget>+                    </child>+                    <child>+                      <widget class="GtkSeparatorMenuItem" id="separatormenuitem3">+                        <property name="visible">True</property>+                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>+                      </widget>+                    </child>+                    <child>+                      <widget class="GtkImageMenuItem" id="imagemenuitem5">+                        <property name="label">gtk-quit</property>+                        <property name="visible">True</property>+                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>+                        <property name="use_underline">True</property>+                        <property name="use_stock">True</property>+                      </widget>+                    </child>+                  </widget>+                </child>+              </widget>+            </child>+            <child>+              <widget class="GtkMenuItem" id="menuitem5">+                <property name="visible">True</property>+                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>+                <property name="label" translatable="yes">_Edit</property>+                <property name="use_underline">True</property>+                <child>+                  <widget class="GtkMenu" id="menu5">+                    <property name="visible">True</property>+                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>+                    <child>+                      <widget class="GtkMenuItem" id="imagemenuitem9">+                        <property name="visible">True</property>+                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>+                        <property name="label" translatable="yes">_Clear</property>+                        <property name="use_underline">True</property>+                        <accelerator key="space" signal="activate"/>+                      </widget>+                    </child>+                    <child>+                      <widget class="GtkMenuItem" id="imagemenuitem11">+                        <property name="visible">True</property>+                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>+                        <property name="label" translatable="yes">_Flag</property>+                        <property name="use_underline">True</property>+                        <accelerator key="Return" signal="activate"/>+                      </widget>+                    </child>+                    <child>+                      <widget class="GtkMenuItem" id="menuitem2">+                        <property name="visible">True</property>+                        <property name="label" translatable="yes">_Hint</property>+                        <property name="use_underline">True</property>+                        <accelerator key="h" signal="activate" modifiers="GDK_CONTROL_MASK"/>+                      </widget>+                    </child>+                    <child>+                      <widget class="GtkMenuItem" id="menuitem6">+                        <property name="visible">True</property>+                        <property name="label" translatable="yes">_Probabilities</property>+                        <property name="use_underline">True</property>+                        <accelerator key="p" signal="activate" modifiers="GDK_CONTROL_MASK"/>+                      </widget>+                    </child>+                    <child>+                      <widget class="GtkSeparatorMenuItem" id="separatormenuitem6">+                        <property name="visible">True</property>+                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>+                      </widget>+                    </child>+                    <child>+                      <widget class="GtkImageMenuItem" id="imagemenuitem12">+                        <property name="label">gtk-undo</property>+                        <property name="visible">True</property>+                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>+                        <property name="use_underline">True</property>+                        <property name="use_stock">True</property>+                        <accelerator key="z" signal="activate" modifiers="GDK_CONTROL_MASK"/>+                      </widget>+                    </child>+                    <child>+                      <widget class="GtkImageMenuItem" id="imagemenuitem15">+                        <property name="label">gtk-redo</property>+                        <property name="visible">True</property>+                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>+                        <property name="use_underline">True</property>+                        <property name="use_stock">True</property>+                        <accelerator key="z" signal="activate" modifiers="GDK_SHIFT_MASK | GDK_CONTROL_MASK"/>+                      </widget>+                    </child>+                  </widget>+                </child>+              </widget>+            </child>+            <child>+              <widget class="GtkMenuItem" id="menuitem3">+                <property name="visible">True</property>+                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>+                <property name="label" translatable="yes">_Preferences</property>+                <property name="use_underline">True</property>+                <child>+                  <widget class="GtkMenu" id="menu4">+                    <property name="visible">True</property>+                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>+                    <child>+                      <widget class="GtkImageMenuItem" id="imagemenuitem6">+                        <property name="label">gtk-preferences</property>+                        <property name="visible">True</property>+                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>+                        <property name="use_underline">True</property>+                        <property name="use_stock">True</property>+                      </widget>+                    </child>+                  </widget>+                </child>+              </widget>+            </child>+            <child>+              <widget class="GtkMenuItem" id="menuitem4">+                <property name="visible">True</property>+                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>+                <property name="label" translatable="yes">_Help</property>+                <property name="use_underline">True</property>+                <child>+                  <widget class="GtkMenu" id="menu3">+                    <property name="visible">True</property>+                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>+                    <child>+                      <widget class="GtkImageMenuItem" id="imagemenuitem3">+                        <property name="label">gtk-help</property>+                        <property name="visible">True</property>+                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>+                        <property name="use_underline">True</property>+                        <property name="use_stock">True</property>+                      </widget>+                    </child>+                    <child>+                      <widget class="GtkImageMenuItem" id="imagemenuitem10">+                        <property name="label">gtk-about</property>+                        <property name="visible">True</property>+                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>+                        <property name="use_underline">True</property>+                        <property name="use_stock">True</property>+                      </widget>+                    </child>+                  </widget>+                </child>+              </widget>+            </child>+          </widget>+          <packing>+            <property name="expand">False</property>+            <property name="position">0</property>+          </packing>+        </child>+        <child>+          <widget class="GtkLabel" id="label1">+            <property name="visible">True</property>+            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>+            <property name="label" translatable="yes">x</property>+            <property name="justify">center</property>+            <property name="single_line_mode">True</property>+          </widget>+          <packing>+            <property name="expand">False</property>+            <property name="padding">5</property>+            <property name="position">3</property>+          </packing>+        </child>+        <child>+          <widget class="GtkHSeparator" id="hseparator1">+            <property name="visible">True</property>+            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>+          </widget>+          <packing>+            <property name="expand">False</property>+            <property name="position">4</property>+          </packing>+        </child>+        <child>+          <widget class="GtkAspectFrame" id="aspectframe1">+            <property name="visible">True</property>+            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>+            <property name="label_xalign">0</property>+            <property name="shadow_type">none</property>+            <property name="obey_child">False</property>+            <child>+              <placeholder/>+            </child>+          </widget>+          <packing>+            <property name="padding">5</property>+            <property name="position">5</property>+          </packing>+        </child>+        <child>+          <widget class="GtkHSeparator" id="hseparator2">+            <property name="visible">True</property>+            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>+          </widget>+          <packing>+            <property name="expand">False</property>+            <property name="pack_type">end</property>+            <property name="position">2</property>+          </packing>+        </child>+        <child>+          <widget class="GtkLabel" id="label7">+            <property name="visible">True</property>+            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>+            <property name="label" translatable="yes">x</property>+          </widget>+          <packing>+            <property name="expand">False</property>+            <property name="padding">5</property>+            <property name="pack_type">end</property>+            <property name="position">1</property>+          </packing>+        </child>+      </widget>+    </child>+  </widget>+  <widget class="GtkAboutDialog" id="aboutdialog1">+    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>+    <property name="border_width">5</property>+    <property name="title" translatable="yes">About The Lucky Minesweeper</property>+    <property name="resizable">False</property>+    <property name="window_position">center-on-parent</property>+    <property name="type_hint">dialog</property>+    <property name="has_separator">False</property>+    <property name="program_name">The Lucky Minesweeper</property>+    <property name="version">0.1</property>+    <property name="copyright" translatable="yes">P&#xE9;ter Divi&#xE1;nszky &#xA9; 2008</property>+    <property name="comments" translatable="yes">Written in Haskell with GHC, Gtk2Hs and Glade</property>+    <property name="license" translatable="yes">Copyright P&#xE9;ter Divi&#xE1;nszky 2008++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of P&#xE9;ter Divi&#xE1;nszky nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+</property>+    <property name="authors">P&#xE9;ter Divi&#xE1;szky, divip aszt inf elte hu</property>+    <property name="documenters"></property>+    <child internal-child="vbox">+      <widget class="GtkVBox" id="dialog-vbox1">+        <property name="visible">True</property>+        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>+        <property name="spacing">2</property>+        <child internal-child="action_area">+          <widget class="GtkHButtonBox" id="dialog-action_area1">+            <property name="visible">True</property>+            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>+            <property name="layout_style">end</property>+          </widget>+          <packing>+            <property name="expand">False</property>+            <property name="pack_type">end</property>+            <property name="position">0</property>+          </packing>+        </child>+      </widget>+    </child>+  </widget>+  <widget class="GtkDialog" id="dialog1">+    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>+    <property name="border_width">5</property>+    <property name="title" translatable="yes">Preferences</property>+    <property name="window_position">center-on-parent</property>+    <property name="type_hint">dialog</property>+    <child internal-child="vbox">+      <widget class="GtkVBox" id="dialog-vbox3">+        <property name="visible">True</property>+        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>+        <property name="spacing">2</property>+        <child>+          <widget class="GtkTable" id="table1">+            <property name="visible">True</property>+            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>+            <property name="n_rows">3</property>+            <property name="n_columns">2</property>+            <child>+              <widget class="GtkLabel" id="label2">+                <property name="visible">True</property>+                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>+                <property name="label" translatable="yes">Rows</property>+              </widget>+            </child>+            <child>+              <widget class="GtkSpinButton" id="spinbutton1">+                <property name="visible">True</property>+                <property name="can_focus">True</property>+                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>+                <property name="adjustment">10 1 15 1 10 0</property>+              </widget>+              <packing>+                <property name="left_attach">1</property>+                <property name="right_attach">2</property>+              </packing>+            </child>+            <child>+              <widget class="GtkSpinButton" id="spinbutton3">+                <property name="visible">True</property>+                <property name="can_focus">True</property>+                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>+                <property name="adjustment">10 1 15 1 10 0</property>+              </widget>+              <packing>+                <property name="left_attach">1</property>+                <property name="right_attach">2</property>+                <property name="top_attach">1</property>+                <property name="bottom_attach">2</property>+              </packing>+            </child>+            <child>+              <widget class="GtkLabel" id="label3">+                <property name="visible">True</property>+                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>+                <property name="label" translatable="yes">Columns</property>+              </widget>+              <packing>+                <property name="top_attach">1</property>+                <property name="bottom_attach">2</property>+              </packing>+            </child>+            <child>+              <widget class="GtkLabel" id="label4">+                <property name="visible">True</property>+                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>+                <property name="label" translatable="yes">Mines</property>+              </widget>+              <packing>+                <property name="top_attach">2</property>+                <property name="bottom_attach">3</property>+              </packing>+            </child>+            <child>+              <widget class="GtkSpinButton" id="spinbutton4">+                <property name="visible">True</property>+                <property name="can_focus">True</property>+                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>+                <property name="adjustment">20 0 100 1 10 0</property>+                <property name="snap_to_ticks">True</property>+              </widget>+              <packing>+                <property name="left_attach">1</property>+                <property name="right_attach">2</property>+                <property name="top_attach">2</property>+                <property name="bottom_attach">3</property>+              </packing>+            </child>+          </widget>+          <packing>+            <property name="position">2</property>+          </packing>+        </child>+        <child internal-child="action_area">+          <widget class="GtkHButtonBox" id="dialog-action_area3">+            <property name="visible">True</property>+            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>+            <property name="layout_style">end</property>+            <child>+              <widget class="GtkButton" id="button1">+                <property name="label" translatable="yes">gtk-cancel</property>+                <property name="response_id">-6</property>+                <property name="visible">True</property>+                <property name="can_focus">True</property>+                <property name="receives_default">True</property>+                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>+                <property name="use_stock">True</property>+              </widget>+              <packing>+                <property name="expand">False</property>+                <property name="fill">False</property>+                <property name="position">0</property>+              </packing>+            </child>+            <child>+              <widget class="GtkButton" id="button2">+                <property name="label" translatable="yes">gtk-ok</property>+                <property name="response_id">-5</property>+                <property name="visible">True</property>+                <property name="can_focus">True</property>+                <property name="receives_default">True</property>+                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>+                <property name="use_stock">True</property>+              </widget>+              <packing>+                <property name="expand">False</property>+                <property name="fill">False</property>+                <property name="position">1</property>+              </packing>+            </child>+          </widget>+          <packing>+            <property name="expand">False</property>+            <property name="pack_type">end</property>+            <property name="position">0</property>+          </packing>+        </child>+      </widget>+    </child>+  </widget>+  <widget class="GtkDialog" id="dialog2">+    <property name="width_request">450</property>+    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>+    <property name="border_width">5</property>+    <property name="window_position">center-on-parent</property>+    <property name="type_hint">dialog</property>+    <property name="has_separator">False</property>+    <child internal-child="vbox">+      <widget class="GtkVBox" id="dialog-vbox4">+        <property name="visible">True</property>+        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>+        <property name="spacing">2</property>+        <child>+          <widget class="GtkTextView" id="textview1">+            <property name="visible">True</property>+            <property name="can_focus">True</property>+            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>+            <property name="pixels_above_lines">10</property>+            <property name="editable">False</property>+            <property name="wrap_mode">word</property>+            <property name="left_margin">10</property>+            <property name="right_margin">10</property>+            <property name="cursor_visible">False</property>+            <property name="text" translatable="yes">Minesweeper game which is always solvable without guessing. +The luck of the gamer is measured with 'luckiness'.+Luckiness = -1/2 * log2 p, where p is the probability of that the player would be alive if the game was a normal minesweeper game.+For example, luckiness = 10 means that since the game start the player cleared 20 positions where a mine was with 50% probability, so in a normal game the player probably would die 10 times.++Known bugs: +* There is no score table.+* The game may slow down at larger tables.+</property>+          </widget>+          <packing>+            <property name="position">1</property>+          </packing>+        </child>+        <child>+          <widget class="GtkVBox" id="vbox3">+            <property name="visible">True</property>+            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>+            <property name="border_width">5</property>+            <child>+              <widget class="GtkLabel" id="label6">+                <property name="visible">True</property>+                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>+                <property name="label" translatable="yes">See also:</property>+              </widget>+              <packing>+                <property name="position">0</property>+              </packing>+            </child>+            <child>+              <widget class="GnomeHRef" id="href1">+                <property name="label" translatable="yes">_Minesweeper (Wikipedia)</property>+                <property name="visible">True</property>+                <property name="can_focus">True</property>+                <property name="receives_default">True</property>+                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>+                <property name="relief">none</property>+                <property name="use_underline">True</property>+                <property name="url">http://en.wikipedia.org/wiki/Minesweeper_(computer_game)</property>+                <property name="text"></property>+              </widget>+              <packing>+                <property name="position">2</property>+              </packing>+            </child>+          </widget>+          <packing>+            <property name="expand">False</property>+            <property name="position">2</property>+          </packing>+        </child>+        <child internal-child="action_area">+          <widget class="GtkHButtonBox" id="dialog-action_area4">+            <property name="visible">True</property>+            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>+            <property name="layout_style">end</property>+            <child>+              <widget class="GtkButton" id="button3">+                <property name="label" translatable="yes">gtk-ok</property>+                <property name="visible">True</property>+                <property name="can_focus">True</property>+                <property name="receives_default">True</property>+                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>+                <property name="use_stock">True</property>+              </widget>+              <packing>+                <property name="expand">False</property>+                <property name="fill">False</property>+                <property name="position">0</property>+              </packing>+            </child>+          </widget>+          <packing>+            <property name="expand">False</property>+            <property name="pack_type">end</property>+            <property name="position">1</property>+          </packing>+        </child>+      </widget>+    </child>+  </widget>+</glade-interface>