ðŸŽ‰ Exercism Research is now launched. Help Exercism, help science and have some fun at research.exercism.io ðŸŽ‰

# danielpilon's solution

## to Minesweeper in the Elixir Track

Published at Jun 30 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: []

def annotate(board),
do:
board
|> map_coordinates()
|> annotate_coordinates()
|> to_board()

defp map_coordinates(board),
do:
board
|> Stream.with_index()
|> Stream.flat_map(&coordinates/1)
|> Enum.into(%{})

defp coordinates({row, row_index}) do
row
|> to_charlist()
|> Stream.with_index()
|> Stream.map(fn {char, column_index} -> {{row_index, column_index}, char} end)
end

defp annotate_coordinates(coordinates),
do:
coordinates

defp adjacent_mines({_coordinate, ?*}, coordinates), do: coordinates

defp adjacent_mines({{row, column} = coordinate, mines}, coordinates),
do:
mines
|> step_increment(coordinates[{row - 1, column}])
|> step_increment(coordinates[{row - 1, column + 1}])
|> step_increment(coordinates[{row - 1, column - 1}])
|> step_increment(coordinates[{row, column - 1}])
|> step_increment(coordinates[{row, column + 1}])
|> step_increment(coordinates[{row + 1, column - 1}])
|> step_increment(coordinates[{row + 1, column}])
|> step_increment(coordinates[{row + 1, column + 1}])
|> update_mine_count(coordinates, coordinate)

defp step_increment(?\s, ?*), do: ?1
defp step_increment(mines, ?*), do: mines + 1
defp step_increment(mines, _value), do: mines

defp update_mine_count(mines, coordinates, coordinate),
do: Map.put(coordinates, coordinate, mines)

defp to_board(coordinates),
do:
coordinates
|> Enum.group_by(fn {{row, _column}, _value} -> row end, fn {_coordinate, value} ->
value
end)
|> Enum.map(fn {_index, row} -> row |> to_string end)
end``````