Instantly share code, notes, and snippets.

# tkshill/Minesweeper.hs

Created April 19, 2021 00:48
Show Gist options
• Save tkshill/01e1551369a1a5ce6081eb5001fd792a to your computer and use it in GitHub Desktop.
Solving the exercism Minesweeper problem with Haskell

# Minesweeper

Add the mine counts to a completed Minesweeper board.

Minesweeper is a popular game where the user has to find the mines using numeric hints that indicate how many mines are directly adjacent (horizontally, vertically, diagonally) to a square.

In this exercise you have to create some code that counts the number of mines adjacent to a given empty square and replaces that square with the count.

The board is a rectangle composed of blank space (' ') characters. A mine is represented by an asterisk ('*') character.

If a given space has no adjacent mines at all, leave that square blank.

## Examples

For example you may receive a 5 x 4 board like this (empty spaces are represented here with the '·' character for display on screen):

``````·*·*·
··*··
··*··
·····
``````

And your code will transform it into this:

``````1*3*1
13*31
·2*2·
·111·
``````
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
 module Minesweeper (annotate) where import Data.Char (intToDigit) import Data.List.Split (chunksOf) annotate :: [String] -> [String] annotate [] = [] annotate [[]] = [[]] annotate board = toBoard \$ zipWith toOutput flatboard \$ bombCounts board where flatboard = concat board toBoard = chunksOf (length (head board)) -- convert board (list of strings) to list of bomb counts around each character bombCounts :: [String] -> [Int] bombCounts board = map (countBombsAround board) \$ getCoords board -- given a list of strings, convert to the appropriate array of characters getCoords :: Foldable t => [t a] -> [(Int, Int)] getCoords xs = [(x, y) | x <- [0 .. len -1], y <- [0 .. width -1]] where len = length xs width = length \$ head xs -- given a character and the number of bombs around it, convert to output character toOutput :: Char -> Int -> Char toOutput c n | c == '*' = c | n == 0 = ' ' | otherwise = intToDigit n -- given a grid, and a cell index, count the bombs around that cell countBombsAround :: [String] -> (Int, Int) -> Int countBombsAround grid index = foldr addBombsCount 0 neighbours where neighbours = getNeighbours len width index len = length grid width = length \$ head grid addBombsCount (x, y) n = if (grid !! x) !! y == '*' then n + 1 else n -- given the dimensions of a grid, and a point, calculate the valid cell indexes that surround that point getNeighbours :: Int -> Int -> (Int, Int) -> [(Int, Int)] getNeighbours l w (x, y) = filter isValidCoord potentialNeighbours where isValidCoord (rowi, coli) = rowi >= 0 && coli >= 0 && rowi < l && coli < w potentialNeighbours = [(i, j) | i <- [x -1 .. x + 1], j <- [y -1 .. y + 1]]