2021-01-28 14:45:23 +00:00
|
|
|
module Minesweeper where
|
|
|
|
|
|
|
|
import System.Random
|
|
|
|
|
|
|
|
type Square = (Int, Int)
|
|
|
|
type Grid = [[Bool]]
|
|
|
|
|
|
|
|
data Board = Board { size :: Int
|
|
|
|
, mines :: Grid
|
|
|
|
, uncovered :: Grid
|
|
|
|
, flagged :: Grid
|
|
|
|
} deriving (Show)
|
|
|
|
|
|
|
|
-- Creates a board given a size (width/height), mine ratio and random generator
|
|
|
|
createBoard :: Int -> Float -> StdGen -> Board
|
|
|
|
createBoard size mineRatio rng = Board size (seedGrid rng mineRatio (createGrid size)) (createGrid size) (createGrid size)
|
|
|
|
|
|
|
|
-- Creates a 2D list of booleans of given size, initialised to False
|
|
|
|
createGrid :: Int -> Grid
|
|
|
|
createGrid size = replicate size (replicate size False)
|
|
|
|
|
|
|
|
-- Functions relating to seeding a grid with mines
|
2021-01-28 19:31:39 +00:00
|
|
|
|
2021-01-28 14:45:23 +00:00
|
|
|
seedGrid :: StdGen -> Float -> Grid -> Grid
|
|
|
|
seedGrid _ _ [] = []
|
|
|
|
seedGrid rng p (l:ls) = newL : seedGrid rng2 p ls
|
|
|
|
where (rng1, rng2) = split rng
|
|
|
|
(newL, _) = seedList rng1 p l
|
|
|
|
|
|
|
|
seedList :: StdGen -> Float -> [Bool] -> ([Bool], StdGen)
|
|
|
|
seedList rng p (l:ls) = (newBool : seedList2 newRng p ls, newRng)
|
|
|
|
where (newBool, newRng) = randomlyTrue rng p
|
|
|
|
|
|
|
|
seedList2 :: StdGen -> Float -> [Bool] -> [Bool]
|
|
|
|
seedList2 _ _ [] = []
|
|
|
|
seedList2 rng p (l:ls) = newBool : seedList2 newRng p ls
|
|
|
|
where (newBool, newRng) = randomlyTrue rng p
|
|
|
|
|
|
|
|
randomlyTrue :: StdGen -> Float -> (Bool, StdGen)
|
|
|
|
randomlyTrue rng p = (generatedFloat <= p, newRng)
|
|
|
|
where (generatedFloat, newRng) = randomR (0.0, 1.0) rng
|
|
|
|
|
2021-01-28 19:31:39 +00:00
|
|
|
-- Functions for determing status of a square
|
|
|
|
-- N.B. (r,c) = (row, column)
|
|
|
|
|
|
|
|
hasMine :: Board -> Square -> Bool
|
|
|
|
hasMine b (r,c) | validSquare b (r,c) = (mines b !! r) !! c
|
|
|
|
| otherwise = False
|
|
|
|
|
|
|
|
isUncovered :: Board -> Square -> Bool
|
|
|
|
isUncovered b (r,c) = (uncovered b !! r) !! c
|
|
|
|
|
|
|
|
isFlagged :: Board -> Square -> Bool
|
|
|
|
isFlagged b (r,c) = (flagged b !! r) !! c
|
|
|
|
|
|
|
|
validSquare :: Board -> Square -> Bool
|
|
|
|
validSquare b (r,c) = r >= 0 && c >= 0 && r < size b && c < size b
|
|
|
|
|
|
|
|
squareAscii :: Board -> Square -> String
|
|
|
|
squareAscii b (r,c) | hasMine b (r,c) = "X"
|
|
|
|
| otherwise = show $ adjacentBombs b (r,c)
|
|
|
|
|
|
|
|
squareColour :: Board -> Square -> String
|
|
|
|
squareColour b (r,c) | hasMine b (r,c) = "red"
|
|
|
|
| otherwise = "green"
|
|
|
|
|
|
|
|
adjacentBombs :: Board -> Square -> Int
|
|
|
|
adjacentBombs board (row,col) = let tl = boolToInt $ hasMine board (row-1,col-1)
|
|
|
|
t = boolToInt $ hasMine board (row-1,col)
|
|
|
|
tr = boolToInt $ hasMine board (row-1,col+1)
|
|
|
|
l = boolToInt $ hasMine board (row,col-1)
|
|
|
|
r = boolToInt $ hasMine board (row,col+1)
|
|
|
|
bl = boolToInt $ hasMine board (row+1,col-1)
|
|
|
|
b = boolToInt $ hasMine board (row+1,col)
|
|
|
|
br = boolToInt $ hasMine board (row+1,col+1)
|
|
|
|
in tl + t + tr + l + r + bl + b + br
|
|
|
|
|
|
|
|
boolToInt :: Bool -> Int
|
|
|
|
boolToInt x | x = 1
|
|
|
|
| otherwise = 0
|
|
|
|
|
2021-01-28 14:45:23 +00:00
|
|
|
-- Functions for turning a board into a string for debug purposes
|
2021-01-28 19:31:39 +00:00
|
|
|
|
2021-01-28 14:45:23 +00:00
|
|
|
printBoard :: Board -> String
|
|
|
|
printBoard b = printBoardGrid (mines b)
|
|
|
|
|
|
|
|
printBoardGrid :: Grid -> String
|
|
|
|
printBoardGrid [] = ""
|
|
|
|
printBoardGrid (l:ls) = printBoardLine l ++ "\n" ++ printBoardGrid ls
|
|
|
|
|
|
|
|
printBoardLine :: [Bool] -> String
|
|
|
|
printBoardLine [] = ""
|
|
|
|
printBoardLine (x:xs) = (if x then " x" else " .") ++ printBoardLine xs
|