Finished go/minesweeper

This commit is contained in:
2017-08-24 13:50:10 -05:00
parent 612b12d1e0
commit ce5ea92f79
8 changed files with 895 additions and 0 deletions

48
go/minesweeper/README.md Normal file
View File

@@ -0,0 +1,48 @@
# Minesweeper
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 the tests
To run the tests run the command `go test` from within the exercise directory.
If the test suite contains benchmarks, you can run these with the `-bench`
flag:
go test -bench .
Keep in mind that each reviewer will run benchmarks on a different machine, with
different specs, so the results from these benchmark tests may vary.
## Further information
For more detailed information about the Go track, including how to get help if
you're having trouble, please visit the exercism.io [Go language page](http://exercism.io/languages/go/about).
## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

112
go/minesweeper/board.go Normal file
View File

@@ -0,0 +1,112 @@
package minesweeper
import (
"bytes"
"errors"
"strconv"
)
const testVersion = 1
type Board [][]byte
// Count fills in the number of mines in adjacent squares on the board
func (b Board) Count() error {
if !b.isValid() {
return errors.New("Invalid Board")
}
for i := range b {
for j := range b[i] {
switch b[i][j] {
case '+', '-', '|', '*':
continue
case ' ':
num := b.countAdjacentMines(j, i)
if num != 0 {
b[i][j] = []byte(strconv.Itoa(num))[0]
}
default:
return errors.New("Invalid Board")
}
}
}
return nil
}
// countAdjacentMines returns the number of mines adjacent to x,y
// or -1 if x,y is invalid
func (b Board) countAdjacentMines(x, y int) int {
var numMines int
if b.isMine(x-1, y-1) {
numMines++
}
if b.isMine(x, y-1) {
numMines++
}
if b.isMine(x+1, y-1) {
numMines++
}
if b.isMine(x-1, y) {
numMines++
}
if b.isMine(x, y) {
numMines++
}
if b.isMine(x+1, y) {
numMines++
}
if b.isMine(x-1, y+1) {
numMines++
}
if b.isMine(x, y+1) {
numMines++
}
if b.isMine(x+1, y+1) {
numMines++
}
return numMines
}
func (b Board) isMine(x, y int) bool {
if x < 0 || y < 0 || x > len(b[0])-1 || y > len(b)-1 {
return false
}
return b[y][x] == '*'
}
func (b Board) isValid() bool {
if len(b) <= 0 {
// no size
return false
}
for i := range b {
if len(b[i]) != len(b[0]) {
// All rows must be same length
return false
}
for j := range b[i] {
// Check top & bottom row for border
if i == 0 || i == len(b)-1 {
// Check corners for border
if j == 0 || j == len(b[i])-1 {
if b[i][j] != '+' {
return false
}
} else if b[i][j] != '-' {
return false
}
} else {
if j == 0 || j == len(b[i])-1 {
if b[i][j] != '|' {
return false
}
}
}
}
}
return true
}
func (b Board) String() string {
return "\n" + string(bytes.Join(b, []byte{'\n'}))
}

View File

@@ -0,0 +1,198 @@
package minesweeper
import (
"bytes"
"testing"
)
const targetTestVersion = 1
var validBoards = []string{
// zero size board
`
++
++`,
// empty board
`
+---+
| |
| |
| |
+---+`,
// board full of mines
`
+---+
|***|
|***|
|***|
+---+`,
// surrounded
`
+---+
|***|
|*8*|
|***|
+---+`,
// horizontal line
`
+-----+
|1*2*1|
+-----+`,
// vertical line
`
+-+
|1|
|*|
|2|
|*|
|1|
+-+`,
// cross
`
+-----+
| 2*2 |
|25*52|
|*****|
|25*52|
| 2*2 |
+-----+`,
// Large 6x6 board with 8 bombs
`
+------+
|1*22*1|
|12*322|
| 123*2|
|112*4*|
|1*22*2|
|111111|
+------+`,
// Large 5x5 board with 7 bombs
`
+-----+
|1*2*1|
|11322|
| 12*2|
|12*4*|
|1*3*2|
+-----+`,
// Small 1x5 board with 2 bombs
`
+-+
|*|
|2|
|*|
|1|
| |
+-+`,
// 1x1 square with 1 bomb
`
+-+
|*|
+-+`,
// 2x2 square with 4 bombs
`
+--+
|**|
|**|
+--+`,
// 5x5 square with 2 bombs
`
+-----+
| 111|
| 1*1|
| 111|
|111 |
|1*1 |
+-----+`,
}
var badBoards = []string{
// Unaligned
`
+-+
| |
|* |
| |
+-+`,
// incomplete border
`
+-----+
* * |
+-- --+`,
// Unknown characters
`
+-----+
|X * |
+-----+`,
}
// constructor. take board as a string, return Board with digits cleared.
func clear(s string) Board {
b := []byte(s)
for i, c := range b {
if c >= '0' && c <= '9' {
b[i] = ' '
}
}
return bytes.Split(b, []byte{'\n'})[1:]
}
func TestTestVersion(t *testing.T) {
if testVersion != targetTestVersion {
t.Fatalf("Found testVersion = %v, want %v.", testVersion, targetTestVersion)
}
}
func TestValid(t *testing.T) {
for _, ref := range validBoards {
b := clear(ref)
t.Log(b)
if err := b.Count(); err != nil {
var _ error = err
t.Fatalf("Count() returned %q, want nil.", err)
}
if res := b.String(); res != ref {
t.Fatalf("Count() result: %s,\n want:%s.", res, ref)
}
}
}
func TestBad(t *testing.T) {
for _, ref := range badBoards {
b := clear(ref)
t.Log(b)
if b.Count() == nil {
t.Fatal("Count() returned nil, want error.")
}
}
}
func BenchmarkCount(b *testing.B) {
m := clear(`
+------+
|1*22*1|
|12*322|
| 123*2|
|112*4*|
|1*22*2|
|111111|
+------+`)
b.ResetTimer()
for i := 0; i < b.N; i++ {
m.Count()
}
}