Finished go/minesweeper
This commit is contained in:
48
go/minesweeper/README.md
Normal file
48
go/minesweeper/README.md
Normal 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
112
go/minesweeper/board.go
Normal 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'}))
|
||||
}
|
198
go/minesweeper/minesweeper_test.go
Normal file
198
go/minesweeper/minesweeper_test.go
Normal 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()
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user