 # petertseng's solution

## to Minesweeper in the F# Track

Published at Jul 13 2018 · 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 |
+-----+
``````

## Submitting Incomplete Solutions

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

### MinesweeperTest.fs

``````// This file was auto-generated based on version 1.1.0 of the canonical data.

module MinesweeperTest

open FsUnit.Xunit
open Xunit

open Minesweeper

[<Fact>]
let ``No rows`` () =
let minefield: string list = []
let expected: string list = []
annotate minefield |> should equal expected

[<Fact(Skip = "Remove to run test")>]
let ``No columns`` () =
let minefield = [""]
let expected = [""]
annotate minefield |> should equal expected

[<Fact(Skip = "Remove to run test")>]
let ``No mines`` () =
let minefield =
[ "   ";
"   ";
"   " ]
let expected =
[ "   ";
"   ";
"   " ]
annotate minefield |> should equal expected

[<Fact(Skip = "Remove to run test")>]
let ``Minefield with only mines`` () =
let minefield =
[ "***";
"***";
"***" ]
let expected =
[ "***";
"***";
"***" ]
annotate minefield |> should equal expected

[<Fact(Skip = "Remove to run test")>]
let ``Mine surrounded by spaces`` () =
let minefield =
[ "   ";
" * ";
"   " ]
let expected =
[ "111";
"1*1";
"111" ]
annotate minefield |> should equal expected

[<Fact(Skip = "Remove to run test")>]
let ``Space surrounded by mines`` () =
let minefield =
[ "***";
"* *";
"***" ]
let expected =
[ "***";
"*8*";
"***" ]
annotate minefield |> should equal expected

[<Fact(Skip = "Remove to run test")>]
let ``Horizontal line`` () =
let minefield = [" * * "]
let expected = ["1*2*1"]
annotate minefield |> should equal expected

[<Fact(Skip = "Remove to run test")>]
let ``Horizontal line, mines at edges`` () =
let minefield = ["*   *"]
let expected = ["*1 1*"]
annotate minefield |> should equal expected

[<Fact(Skip = "Remove to run test")>]
let ``Vertical line`` () =
let minefield =
[ " ";
"*";
" ";
"*";
" " ]
let expected =
[ "1";
"*";
"2";
"*";
"1" ]
annotate minefield |> should equal expected

[<Fact(Skip = "Remove to run test")>]
let ``Vertical line, mines at edges`` () =
let minefield =
[ "*";
" ";
" ";
" ";
"*" ]
let expected =
[ "*";
"1";
" ";
"1";
"*" ]
annotate minefield |> should equal expected

[<Fact(Skip = "Remove to run test")>]
let ``Cross`` () =
let minefield =
[ "  *  ";
"  *  ";
"*****";
"  *  ";
"  *  " ]
let expected =
[ " 2*2 ";
"25*52";
"*****";
"25*52";
" 2*2 " ]
annotate minefield |> should equal expected

[<Fact(Skip = "Remove to run test")>]
let ``Large minefield`` () =
let minefield =
[ " *  * ";
"  *   ";
"    * ";
"   * *";
" *  * ";
"      " ]
let expected =
[ "1*22*1";
"12*322";
" 123*2";
"112*4*";
"1*22*2";
"111111" ]
annotate minefield |> should equal expected``````
``````module Minesweeper

// TODO: This was written for string -> string,
// then clumsily hacked to support string list -> string list
let annotate (boardRows: string list): string list =
// TODO: Remove this hack and just operate on the string list
if boardRows = [] then boardRows else
let board = String.concat "\n" boardRows in

let len = String.length board in
let inBounds x = x >= 0 && x < len in

let dys =
match Seq.tryFindIndex (fun c -> c = '\n') board with
| Some c -> [-c - 1; 0; c + 1]
| None   ->  in

let counts = Array.zeroCreate len in

board |> Seq.iteri (fun i c ->
if c = '*' then dys |> List.iter (fun dy ->
let row = i + dy in
if inBounds row then [-1; 0; 1] |> List.iter (fun dx ->
let pos = row + dx in
if inBounds pos then counts.[pos] <- counts.[pos] + 1
)
)
)

let newChar c n =
if c = '\n' || c = '*' then c
else if n = 0 then ' '
else char (int '0' + n) in

// TODO: Remove this hack and just return a string list directly
(System.String.Concat (Seq.map2 newChar board counts)).Split "\n" |> Array.toList``````