🎉 Exercism Research is now launched. Help Exercism, help science and have some fun at research.exercism.io 🎉 # michaelkpfeifer's solution

## to Minesweeper in the Elixir Track

Published at Aug 09 2020 · 0 comments
Instructions
Test suite
Solution

Add the numbers to a 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 square and transforms boards like this (where `*` indicates a mine):

``````+-----+
| * * |
|  *  |
|  *  |
|     |
+-----+
``````

into this:

``````+-----+
|1*3*1|
|13*31|
| 2*2 |
| 111 |
+-----+
``````

## Running tests

Execute the tests with:

``````\$ mix test
``````

### Pending tests

In the test suites, all but the first test have been skipped.

Once you get a test passing, you can unskip the next one by commenting out the relevant `@tag :pending` with a `#` symbol.

For example:

``````# @tag :pending
test "shouting" do
assert Bob.hey("WATCH OUT!") == "Whoa, chill out!"
end
``````

Or, you can enable all the tests by commenting out the `ExUnit.configure` line in the test suite.

``````# ExUnit.configure exclude: :pending, trace: true
``````

If you're stuck on something, it may help to look at some of the available resources out there where answers might be found.

## Submitting Incomplete Solutions

It's possible to submit an incomplete solution so you can see how others have completed the exercise.

### minesweeper_test.exs

``````defmodule MinesweeperTest do
use ExUnit.Case

defp clean(b), do: Enum.map(b, &String.replace(&1, ~r/[^*]/, " "))

# @tag :pending
test "zero size board" do
b = []
assert Minesweeper.annotate(clean(b)) == b
end

@tag :pending
test "empty board" do
b = ["   ", "   ", "   "]
assert Minesweeper.annotate(clean(b)) == b
end

@tag :pending
test "board full of mines" do
b = ["***", "***", "***"]
assert Minesweeper.annotate(clean(b)) == b
end

@tag :pending
test "surrounded" do
b = ["***", "*8*", "***"]
assert Minesweeper.annotate(clean(b)) == b
end

@tag :pending
test "horizontal line" do
b = ["1*2*1"]
assert Minesweeper.annotate(clean(b)) == b
end

@tag :pending
test "vertical line" do
b = ["1", "*", "2", "*", "1"]
assert Minesweeper.annotate(clean(b)) == b
end

@tag :pending
test "cross" do
b = [" 2*2 ", "25*52", "*****", "25*52", " 2*2 "]
assert Minesweeper.annotate(clean(b)) == b
end
end``````

### test_helper.exs

``````ExUnit.start()
ExUnit.configure(exclude: :pending, trace: true)``````
``````defmodule Minesweeper do
@doc """
Annotate empty spots next to mines with the number of mines next to them.
"""
@spec annotate([String.t()]) :: [String.t()]

def annotate([]) do
[]
end

def annotate(board) do
nr_rows = length(board)
nr_cols = String.length(Enum.at(board, 0))
source = board_to_map(board)

Enum.reduce(
for r <- 0..(nr_rows - 1), c <- 0..(nr_cols - 1) do
{r, c}
end,
source,
fn {r, c}, acc ->
put_in(acc, [r, c], sum_for_coordinate(r, c, nr_rows, nr_cols, source))
end
)
|> Map.values()
|> Enum.map(fn row -> Map.values(row) |> Enum.join() end)
end

def board_to_map(board) do
Enum.map(board, fn row -> row_to_map(row) end)
|> Enum.with_index()
|> Enum.reduce(%{}, fn {row, index}, map -> Map.put_new(map, index, row) end)
end

def row_to_map(row) do
String.graphemes(row)
|> Enum.with_index()
|> Enum.reduce(%{}, fn {char, index}, map -> Map.put_new(map, index, char) end)
end

def coordinate_list(row, column, nr_rows, nr_cols) do
[
{row - 1, column - 1},
{row - 1, column},
{row - 1, column + 1},
{row, column - 1},
{row, column + 1},
{row + 1, column - 1},
{row + 1, column},
{row + 1, column + 1}
]
|> Enum.filter(fn {r, c} -> r >= 0 && r < nr_rows && c >= 0 && c < nr_cols end)
end

def sum_for_coordinate(row, column, nr_rows, nr_cols, source) do
if get_in(source, [row, column]) == "*" do
"*"
else
coordinate_list(row, column, nr_rows, nr_cols)
|> Enum.reduce(
0,
fn {r, c}, acc ->
if get_in(source, [r, c]) == " " do
acc
else
acc + 1
end
end
)
|> format_annotation()
end
end

def format_annotation(sum) do
if sum == 0 do
" "
else
Integer.to_string(sum)
end
end
end``````