hs2048-0.1.0: library/Hs2048/Game.hs
-- | Functions for playing the game.
module Hs2048.Game
( addRandomTile
, addRandomTiles
, hasWon
, isOver
, new
, randomEmptyIndex
, randomEmptyPoint
, randomTile
) where
import Data.Maybe (fromJust)
import qualified Hs2048.Board as B
import qualified Hs2048.Direction as D
import qualified Hs2048.Point as P
import qualified Hs2048.Settings as S
import qualified Hs2048.Tile as T
import qualified Hs2048.Vector as V
import qualified System.Random as R
{- |
Adds a random tile to the board.
>>> addRandomTile [[Nothing], [Nothing]] (R.mkStdGen 0)
([[Nothing],[Just 2]],1346387765 2103410263)
-}
addRandomTile :: R.RandomGen r => B.Board -> r -> (B.Board, r)
addRandomTile b r = case p of
Nothing -> (b, r)
_ -> (b', r'')
where
b' = B.set b t (fromJust p)
(p, r') = randomEmptyPoint b r
(t, r'') = randomTile r'
{- |
Adds some random tiles to the board.
>>> addRandomTiles 2 [[Nothing], [Nothing]] (R.mkStdGen 0)
([[Just 2],[Just 2]],2127568003 1780294415)
-}
addRandomTiles :: R.RandomGen r => Int -> B.Board -> r -> (B.Board, r)
addRandomTiles 0 b r = (b, r)
addRandomTiles n b r = addRandomTiles (n - 1) b' r'
where
(b', r') = addRandomTile b r
{- |
Determines if the game has been won. See 'Hs2048.Settings.maxTile'.
>>> hasWon [[Just 2048]]
True
-}
hasWon :: B.Board -> Bool
hasWon = any (any (maybe False (>= S.maxTile)))
{- |
Determines if the game is over. The game is over if there are no available
moves and no empty points.
>>> isOver [[Just 2]]
True
-}
isOver :: B.Board -> Bool
isOver b = cantMove && haveNoEmptyPoints
where
cantMove = not (any (B.canMove b) D.directions)
haveNoEmptyPoints = null (B.emptyPoints b)
{- |
Creates a new game by making an empty board and adding some random tiles to
it. See 'Hs2048.Settings.width', 'Hs2048.Settings.height', and
'Hs2048.Settings.tiles'.
>>> new (R.mkStdGen 0)
([[Just 2,Nothing,Nothing,Nothing],[Nothing,Nothing,Nothing,Nothing],[Nothing,Nothing,Nothing,Nothing],[Nothing,Just 2,Nothing,Nothing]],2127568003 1780294415)
-}
new :: R.RandomGen r => r -> (B.Board, r)
new = addRandomTiles S.tiles (B.empty S.width S.height)
{- |
Selects an empty index at random from a vector.
>>> randomEmptyIndex [Nothing, Nothing] (R.mkStdGen 0)
(Just 1,40014 40692)
-}
randomEmptyIndex :: R.RandomGen r => V.Vector -> r -> (Maybe Int, r)
randomEmptyIndex v r = if null is then (Nothing, r) else (Just i, r')
where
i = is !! x
(x, r') = R.randomR (0, length is - 1) r
is = V.emptyIndexes v
{- |
Selects an empty point at random from a board.
>>> randomEmptyPoint [[Nothing],[Nothing]] (R.mkStdGen 0)
(Just (1,0),40014 40692)
-}
randomEmptyPoint :: R.RandomGen r => B.Board -> r -> (Maybe P.Point, r)
randomEmptyPoint b r = if null ps then (Nothing, r) else (Just p, r')
where
p = ps !! x
(x, r') = R.randomR (0, length ps - 1) r
ps = B.emptyPoints b
{- |
Creates a random tile.
>>> randomTile (R.mkStdGen 0)
(Just 2,1601120196 1655838864)
-}
randomTile :: R.RandomGen r => r -> (T.Tile, r)
randomTile r = (Just n, r')
where
n = if (x :: Float) < 0.9 then 2 else 4
(x, r') = R.random r