Finished go/minesweeper
This commit is contained in:
parent
612b12d1e0
commit
ce5ea92f79
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()
|
||||||
|
}
|
||||||
|
}
|
52
go/robot-simulator/README.md
Normal file
52
go/robot-simulator/README.md
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
# Robot Simulator
|
||||||
|
|
||||||
|
Write a robot simulator.
|
||||||
|
|
||||||
|
A robot factory's test facility needs a program to verify robot movements.
|
||||||
|
|
||||||
|
The robots have three possible movements:
|
||||||
|
|
||||||
|
- turn right
|
||||||
|
- turn left
|
||||||
|
- advance
|
||||||
|
|
||||||
|
Robots are placed on a hypothetical infinite grid, facing a particular
|
||||||
|
direction (north, east, south, or west) at a set of {x,y} coordinates,
|
||||||
|
e.g., {3,8}, with coordinates increasing to the north and east.
|
||||||
|
|
||||||
|
The robot then receives a number of instructions, at which point the
|
||||||
|
testing facility verifies the robot's new position, and in which
|
||||||
|
direction it is pointing.
|
||||||
|
|
||||||
|
- The letter-string "RAALAL" means:
|
||||||
|
- Turn right
|
||||||
|
- Advance twice
|
||||||
|
- Turn left
|
||||||
|
- Advance once
|
||||||
|
- Turn left yet again
|
||||||
|
- Say a robot starts at {7, 3} facing north. Then running this stream
|
||||||
|
of instructions should leave it at {9, 4} facing west.
|
||||||
|
|
||||||
|
## 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).
|
||||||
|
|
||||||
|
## Source
|
||||||
|
|
||||||
|
Inspired by an interview question at a famous company.
|
||||||
|
|
||||||
|
## Submitting Incomplete Solutions
|
||||||
|
It's possible to submit an incomplete solution so you can see how others have completed the exercise.
|
32
go/robot-simulator/defs.go
Normal file
32
go/robot-simulator/defs.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package robot
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// definitions used in step 1
|
||||||
|
|
||||||
|
var Step1Robot struct {
|
||||||
|
X, Y int
|
||||||
|
Dir
|
||||||
|
}
|
||||||
|
|
||||||
|
type Dir int
|
||||||
|
|
||||||
|
var _ fmt.Stringer = Dir(1729)
|
||||||
|
|
||||||
|
// additional definitions used in step 2
|
||||||
|
|
||||||
|
type Command byte // valid values are 'R', 'L', 'A'
|
||||||
|
type RU int
|
||||||
|
type Pos struct{ Easting, Northing RU }
|
||||||
|
type Rect struct{ Min, Max Pos }
|
||||||
|
type Step2Robot struct {
|
||||||
|
Dir
|
||||||
|
Pos
|
||||||
|
}
|
||||||
|
|
||||||
|
// additional definition used in step 3
|
||||||
|
|
||||||
|
type Step3Robot struct {
|
||||||
|
Name string
|
||||||
|
Step2Robot
|
||||||
|
}
|
89
go/robot-simulator/robot_simulator_step2_test.go
Normal file
89
go/robot-simulator/robot_simulator_step2_test.go
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
// +build step2 !step1,!step3
|
||||||
|
|
||||||
|
package robot
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
// For step 1 you implement robot movements, but it's not much of a simulation.
|
||||||
|
// For example where in the source code is "the robot"? Where is "the grid"?
|
||||||
|
// Where are the computations that turn robot actions into grid positions,
|
||||||
|
// in the robot or in the grid? The physical world is different.
|
||||||
|
//
|
||||||
|
// Step 2 introduces a "room." It seems a small addition, but we'll make
|
||||||
|
// big changes to clarify the rolls of "room", "robot", and "test program"
|
||||||
|
// and begin to clarify the physics of the simulation. You will define Room
|
||||||
|
// and Robot as functions which the test program "brings into existence" by
|
||||||
|
// launching them as goroutines. Information moves between test program,
|
||||||
|
// robot, and room over Go channels.
|
||||||
|
//
|
||||||
|
// Think of Room as a "physics engine," something that models and simulates
|
||||||
|
// a physical room with walls and a robot. It should somehow model the
|
||||||
|
// coordinate space of the room, the location of the robot and the walls,
|
||||||
|
// and ensure for example that the robot doesn't walk through walls.
|
||||||
|
// We want Robot to be an agent that performs actions, but we want Room to
|
||||||
|
// maintain a coherent truth.
|
||||||
|
//
|
||||||
|
// Step 2 API:
|
||||||
|
//
|
||||||
|
// StartRobot(chan Command, chan Action)
|
||||||
|
// Room(extent Rect, robot Step2Robot, act chan Action, rep chan Step2Robot)
|
||||||
|
//
|
||||||
|
// You get to define Action; see defs.go for other definitions.
|
||||||
|
//
|
||||||
|
// The test program creates the channels and starts both Room and Robot.
|
||||||
|
// The test program then sends commands to Robot. When it is done sending
|
||||||
|
// commands, it closes the command channel. Robot must accept commands and
|
||||||
|
// inform Room of actions it is attempting. When it senses the command channel
|
||||||
|
// closing, it must shut down itself. The room must interpret the physical
|
||||||
|
// consequences of the robot actions. When it senses the robot shutting down,
|
||||||
|
// it sends a final report back to the test program, telling the robot's final
|
||||||
|
// position and direction.
|
||||||
|
|
||||||
|
var test2 = []struct {
|
||||||
|
Command
|
||||||
|
Step2Robot
|
||||||
|
}{
|
||||||
|
0: {' ', Step2Robot{N, Pos{1, 1}}}, // no command, this is the start DirAt
|
||||||
|
1: {'A', Step2Robot{N, Pos{1, 2}}},
|
||||||
|
2: {'R', Step2Robot{E, Pos{1, 2}}},
|
||||||
|
3: {'A', Step2Robot{E, Pos{2, 2}}},
|
||||||
|
4: {'L', Step2Robot{N, Pos{2, 2}}},
|
||||||
|
5: {'L', Step2Robot{W, Pos{2, 2}}},
|
||||||
|
6: {'L', Step2Robot{S, Pos{2, 2}}},
|
||||||
|
7: {'A', Step2Robot{S, Pos{2, 1}}},
|
||||||
|
8: {'R', Step2Robot{W, Pos{2, 1}}},
|
||||||
|
9: {'A', Step2Robot{W, Pos{1, 1}}},
|
||||||
|
10: {'A', Step2Robot{W, Pos{1, 1}}}, // bump W wall
|
||||||
|
11: {'L', Step2Robot{S, Pos{1, 1}}},
|
||||||
|
12: {'A', Step2Robot{S, Pos{1, 1}}}, // bump S wall
|
||||||
|
13: {'L', Step2Robot{E, Pos{1, 1}}},
|
||||||
|
14: {'A', Step2Robot{E, Pos{2, 1}}},
|
||||||
|
15: {'A', Step2Robot{E, Pos{2, 1}}}, // bump E wall
|
||||||
|
16: {'L', Step2Robot{N, Pos{2, 1}}},
|
||||||
|
17: {'A', Step2Robot{N, Pos{2, 2}}},
|
||||||
|
18: {'A', Step2Robot{N, Pos{2, 2}}}, // bump N wall
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStep2(t *testing.T) {
|
||||||
|
// run incrementally longer tests
|
||||||
|
for i := 1; i <= len(test2); i++ {
|
||||||
|
cmd := make(chan Command)
|
||||||
|
act := make(chan Action)
|
||||||
|
rep := make(chan Step2Robot)
|
||||||
|
go StartRobot(cmd, act)
|
||||||
|
go Room(Rect{Pos{1, 1}, Pos{2, 2}}, test2[0].Step2Robot, act, rep)
|
||||||
|
for j := 1; j < i; j++ {
|
||||||
|
cmd <- test2[j].Command
|
||||||
|
}
|
||||||
|
close(cmd)
|
||||||
|
da := <-rep
|
||||||
|
last := i - 1
|
||||||
|
want := test2[last].Step2Robot
|
||||||
|
if da.Pos != want.Pos {
|
||||||
|
t.Fatalf("Command #%d, Pos = %v, want %v", last, da.Pos, want.Pos)
|
||||||
|
}
|
||||||
|
if da.Dir != want.Dir {
|
||||||
|
t.Fatalf("Command #%d, Dir = %v, want %v", last, da.Dir, want.Dir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
293
go/robot-simulator/robot_simulator_step3_test.go
Normal file
293
go/robot-simulator/robot_simulator_step3_test.go
Normal file
@ -0,0 +1,293 @@
|
|||||||
|
// +build step3 !step1,!step2
|
||||||
|
|
||||||
|
package robot
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
// Step 3 has three major changes:
|
||||||
|
//
|
||||||
|
// * Robots run scripts rather than respond to individual commands.
|
||||||
|
// * A log channel allows robots and the room to log messages.
|
||||||
|
// * The room allows multiple robots to exist and operate concurrently.
|
||||||
|
//
|
||||||
|
// Step 3 API:
|
||||||
|
//
|
||||||
|
// StartRobot3(name, script string, action chan Action3, log chan string)
|
||||||
|
// Room3(extent Rect, robots []Step3Robot, action chan Action3, report chan []Step3Robot, log chan string)
|
||||||
|
//
|
||||||
|
// Again, you define Action3.
|
||||||
|
//
|
||||||
|
// For the final position report sent from StartRobot3, you can return the same slice
|
||||||
|
// received from the robots channel, just with updated positions and directions.
|
||||||
|
//
|
||||||
|
// Messages must be sent on the log channel for
|
||||||
|
// * A robot without a name
|
||||||
|
// * Duplicate robot names
|
||||||
|
// * Robots placed at the same place
|
||||||
|
// * A robot placed outside of the room
|
||||||
|
// * An undefined command in a script
|
||||||
|
// * An action from an unknown robot
|
||||||
|
// * A robot attempting to advance into a wall
|
||||||
|
// * A robot attempting to advance into another robot
|
||||||
|
|
||||||
|
var testOneRobot = []struct {
|
||||||
|
cmd byte
|
||||||
|
Step2Robot
|
||||||
|
nMsg int
|
||||||
|
}{
|
||||||
|
// (test2 data with message counts added)
|
||||||
|
{' ', Step2Robot{N, Pos{1, 1}}, 0},
|
||||||
|
{'A', Step2Robot{N, Pos{1, 2}}, 0},
|
||||||
|
{'R', Step2Robot{E, Pos{1, 2}}, 0},
|
||||||
|
{'A', Step2Robot{E, Pos{2, 2}}, 0},
|
||||||
|
{'L', Step2Robot{N, Pos{2, 2}}, 0},
|
||||||
|
{'L', Step2Robot{W, Pos{2, 2}}, 0},
|
||||||
|
{'L', Step2Robot{S, Pos{2, 2}}, 0},
|
||||||
|
{'A', Step2Robot{S, Pos{2, 1}}, 0},
|
||||||
|
{'R', Step2Robot{W, Pos{2, 1}}, 0},
|
||||||
|
{'A', Step2Robot{W, Pos{1, 1}}, 0},
|
||||||
|
{'A', Step2Robot{W, Pos{1, 1}}, 1}, // bump W wall
|
||||||
|
{'L', Step2Robot{S, Pos{1, 1}}, 1},
|
||||||
|
{'A', Step2Robot{S, Pos{1, 1}}, 2}, // bump S wall
|
||||||
|
{'L', Step2Robot{E, Pos{1, 1}}, 2},
|
||||||
|
{'A', Step2Robot{E, Pos{2, 1}}, 2},
|
||||||
|
{'A', Step2Robot{E, Pos{2, 1}}, 3}, // bump E wall
|
||||||
|
{'L', Step2Robot{N, Pos{2, 1}}, 3},
|
||||||
|
{'A', Step2Robot{N, Pos{2, 2}}, 3},
|
||||||
|
{'A', Step2Robot{N, Pos{2, 2}}, 4}, // bump N wall
|
||||||
|
}
|
||||||
|
|
||||||
|
func logMon(log chan string, nMsg chan int, t *testing.T) {
|
||||||
|
n := 0
|
||||||
|
for msg := range log {
|
||||||
|
t.Log("Sim:", msg)
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
nMsg <- n
|
||||||
|
}
|
||||||
|
|
||||||
|
// Same tests as step 2; single robot, but expect log messages on wall bumps.
|
||||||
|
func TestOneStep3(t *testing.T) {
|
||||||
|
for i := 1; i <= len(testOneRobot); i++ {
|
||||||
|
log := make(chan string)
|
||||||
|
nMsg := make(chan int)
|
||||||
|
go logMon(log, nMsg, t)
|
||||||
|
act := make(chan Action3)
|
||||||
|
rep := make(chan []Step3Robot)
|
||||||
|
go Room3(
|
||||||
|
Rect{Pos{1, 1}, Pos{2, 2}},
|
||||||
|
[]Step3Robot{{"Robbie", testOneRobot[0].Step2Robot}},
|
||||||
|
act, rep, log)
|
||||||
|
scr := ""
|
||||||
|
for j := 1; j < i; j++ {
|
||||||
|
scr += string(testOneRobot[j].cmd)
|
||||||
|
}
|
||||||
|
t.Logf("Script %q", scr)
|
||||||
|
go StartRobot3("Robbie", scr, act, log)
|
||||||
|
pls := <-rep
|
||||||
|
lastTest := testOneRobot[i-1]
|
||||||
|
if len(pls) != 1 {
|
||||||
|
t.Fatalf("Got report on %d robots, want 1.", len(pls))
|
||||||
|
}
|
||||||
|
pl := pls[0]
|
||||||
|
if pl.Name != "Robbie" {
|
||||||
|
t.Fatalf(`Got report for robot %q, want report for "Robbie".`,
|
||||||
|
pl.Name)
|
||||||
|
}
|
||||||
|
da := pl.Step2Robot
|
||||||
|
want := lastTest.Step2Robot
|
||||||
|
if da.Pos != want.Pos {
|
||||||
|
t.Fatalf("Script %q, Pos = %v, want %v", scr, da.Pos, want.Pos)
|
||||||
|
}
|
||||||
|
if da.Dir != want.Dir {
|
||||||
|
t.Fatalf("Script %q, Dir = %v, want %v", scr, da.Dir, want.Dir)
|
||||||
|
}
|
||||||
|
close(log)
|
||||||
|
if n := <-nMsg; n != lastTest.nMsg {
|
||||||
|
t.Errorf("%d sim messages logged, want %d.", n, lastTest.nMsg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNoName(t *testing.T) {
|
||||||
|
log := make(chan string)
|
||||||
|
nMsg := make(chan int)
|
||||||
|
go logMon(log, nMsg, t)
|
||||||
|
act := make(chan Action3)
|
||||||
|
rep := make(chan []Step3Robot)
|
||||||
|
go Room3(
|
||||||
|
Rect{Pos{1, 1}, Pos{1, 1}},
|
||||||
|
[]Step3Robot{{"", Step2Robot{N, Pos{1, 1}}}},
|
||||||
|
act, rep, log)
|
||||||
|
go StartRobot3("", "", act, log)
|
||||||
|
<-rep
|
||||||
|
close(log)
|
||||||
|
if n := <-nMsg; n != 1 {
|
||||||
|
t.Fatalf("Got %d messages, want 1.", n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSameName(t *testing.T) {
|
||||||
|
log := make(chan string)
|
||||||
|
nMsg := make(chan int)
|
||||||
|
go logMon(log, nMsg, t)
|
||||||
|
act := make(chan Action3)
|
||||||
|
rep := make(chan []Step3Robot)
|
||||||
|
go Room3(
|
||||||
|
Rect{Pos{1, 1}, Pos{2, 1}},
|
||||||
|
[]Step3Robot{
|
||||||
|
{"Daryl", Step2Robot{N, Pos{1, 1}}},
|
||||||
|
{"Daryl", Step2Robot{N, Pos{2, 1}}},
|
||||||
|
},
|
||||||
|
act, rep, log)
|
||||||
|
go StartRobot3("Daryl", "", act, log)
|
||||||
|
go StartRobot3("Daryl", "", act, log)
|
||||||
|
<-rep
|
||||||
|
close(log)
|
||||||
|
if n := <-nMsg; n != 1 {
|
||||||
|
t.Fatalf("Got %d messages, want 1.", n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSamePosition(t *testing.T) {
|
||||||
|
log := make(chan string)
|
||||||
|
nMsg := make(chan int)
|
||||||
|
go logMon(log, nMsg, t)
|
||||||
|
act := make(chan Action3)
|
||||||
|
rep := make(chan []Step3Robot)
|
||||||
|
go Room3(
|
||||||
|
Rect{Pos{1, 1}, Pos{100, 100}},
|
||||||
|
[]Step3Robot{
|
||||||
|
{"Matter", Step2Robot{N, Pos{23, 47}}},
|
||||||
|
{"Antimatter", Step2Robot{N, Pos{23, 47}}},
|
||||||
|
},
|
||||||
|
act, rep, log)
|
||||||
|
go StartRobot3("Matter", "", act, log)
|
||||||
|
go StartRobot3("Antimatter", "", act, log)
|
||||||
|
<-rep
|
||||||
|
close(log)
|
||||||
|
if n := <-nMsg; n != 1 {
|
||||||
|
t.Fatalf("Got %d messages, want 1.", n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOutsideRoom(t *testing.T) {
|
||||||
|
log := make(chan string)
|
||||||
|
nMsg := make(chan int)
|
||||||
|
go logMon(log, nMsg, t)
|
||||||
|
act := make(chan Action3)
|
||||||
|
rep := make(chan []Step3Robot)
|
||||||
|
go Room3(
|
||||||
|
Rect{Pos{1, 1}, Pos{1, 1}},
|
||||||
|
[]Step3Robot{{"Elvis", Step2Robot{N, Pos{2, 3}}}},
|
||||||
|
act, rep, log)
|
||||||
|
go StartRobot3("Elvis", "", act, log)
|
||||||
|
<-rep
|
||||||
|
close(log)
|
||||||
|
if n := <-nMsg; n != 1 {
|
||||||
|
t.Fatalf("Got %d messages, want 1.", n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBadCommand(t *testing.T) {
|
||||||
|
log := make(chan string)
|
||||||
|
nMsg := make(chan int)
|
||||||
|
go logMon(log, nMsg, t)
|
||||||
|
act := make(chan Action3)
|
||||||
|
rep := make(chan []Step3Robot)
|
||||||
|
go Room3(
|
||||||
|
Rect{Pos{0, 0}, Pos{0, 99}},
|
||||||
|
[]Step3Robot{{"Vgr", Step2Robot{N, Pos{0, 99}}}},
|
||||||
|
act, rep, log)
|
||||||
|
go StartRobot3("Vgr", "RET", act, log)
|
||||||
|
<-rep
|
||||||
|
close(log)
|
||||||
|
if n := <-nMsg; n != 1 {
|
||||||
|
t.Fatalf("Got %d messages, want 1.", n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBadRobot(t *testing.T) {
|
||||||
|
log := make(chan string)
|
||||||
|
nMsg := make(chan int)
|
||||||
|
go logMon(log, nMsg, t)
|
||||||
|
act := make(chan Action3)
|
||||||
|
rep := make(chan []Step3Robot)
|
||||||
|
go Room3(
|
||||||
|
Rect{Pos{0, 0}, Pos{0, 0}},
|
||||||
|
[]Step3Robot{{"Data", Step2Robot{N, Pos{0, 0}}}},
|
||||||
|
act, rep, log)
|
||||||
|
go StartRobot3("Lore", "A", act, log)
|
||||||
|
<-rep
|
||||||
|
close(log)
|
||||||
|
if n := <-nMsg; n != 1 {
|
||||||
|
t.Fatalf("Got %d messages, want 1.", n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestThree(t *testing.T) { // no bumping
|
||||||
|
log := make(chan string)
|
||||||
|
nMsg := make(chan int)
|
||||||
|
go logMon(log, nMsg, t)
|
||||||
|
act := make(chan Action3)
|
||||||
|
go StartRobot3("clutz", "LAAARALA", act, log)
|
||||||
|
go StartRobot3("sphero", "RRAAAAALA", act, log)
|
||||||
|
go StartRobot3("roomba", "LAAARRRALLLL", act, log)
|
||||||
|
rep := make(chan []Step3Robot)
|
||||||
|
go Room3(
|
||||||
|
Rect{Pos{-10, -10}, Pos{15, 10}},
|
||||||
|
[]Step3Robot{
|
||||||
|
{"clutz", Step2Robot{N, Pos{0, 0}}},
|
||||||
|
{"sphero", Step2Robot{E, Pos{2, -7}}},
|
||||||
|
{"roomba", Step2Robot{S, Pos{8, 4}}},
|
||||||
|
},
|
||||||
|
act, rep, log)
|
||||||
|
pls := <-rep
|
||||||
|
if len(pls) != 3 {
|
||||||
|
t.Fatalf("Got report on %d robots, want 3.", len(pls))
|
||||||
|
}
|
||||||
|
exp:
|
||||||
|
for _, exp := range []Step3Robot{
|
||||||
|
{"clutz", Step2Robot{W, Pos{-4, 1}}},
|
||||||
|
{"sphero", Step2Robot{S, Pos{-3, -8}}},
|
||||||
|
{"roomba", Step2Robot{N, Pos{11, 5}}},
|
||||||
|
} {
|
||||||
|
for _, pl := range pls {
|
||||||
|
if pl.Name != exp.Name {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if pl.Step2Robot.Pos != exp.Step2Robot.Pos {
|
||||||
|
t.Fatalf("%s at %v, want %v",
|
||||||
|
pl.Name, pl.Step2Robot.Pos, exp.Step2Robot.Pos)
|
||||||
|
}
|
||||||
|
if pl.Step2Robot.Dir != exp.Step2Robot.Dir {
|
||||||
|
t.Fatalf("%s facing %v, want %v",
|
||||||
|
pl.Name, pl.Step2Robot.Dir, exp.Step2Robot.Dir)
|
||||||
|
}
|
||||||
|
continue exp
|
||||||
|
}
|
||||||
|
t.Fatalf("Missing %s", exp.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBattle(t *testing.T) {
|
||||||
|
log := make(chan string)
|
||||||
|
nMsg := make(chan int)
|
||||||
|
go logMon(log, nMsg, t)
|
||||||
|
act := make(chan Action3)
|
||||||
|
go StartRobot3("Toro", "AAAAAAA", act, log)
|
||||||
|
go StartRobot3("Phere", "AAAAAAA", act, log)
|
||||||
|
rep := make(chan []Step3Robot)
|
||||||
|
go Room3(
|
||||||
|
Rect{Pos{1, 1}, Pos{9, 9}},
|
||||||
|
[]Step3Robot{
|
||||||
|
{"Toro", Step2Robot{E, Pos{1, 5}}},
|
||||||
|
{"Phere", Step2Robot{W, Pos{9, 5}}},
|
||||||
|
},
|
||||||
|
act, rep, log)
|
||||||
|
<-rep
|
||||||
|
close(log)
|
||||||
|
if n := <-nMsg; n != 7 {
|
||||||
|
t.Fatalf("Got %d messages, want 7.", n)
|
||||||
|
}
|
||||||
|
}
|
71
go/robot-simulator/robot_simulator_test.go
Normal file
71
go/robot-simulator/robot_simulator_test.go
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
// +build step1 !step2,!step3
|
||||||
|
|
||||||
|
package robot
|
||||||
|
|
||||||
|
// Tests are separated into 3 steps.
|
||||||
|
//
|
||||||
|
// Run all tests with `go test` or run specific tests with the -tags option.
|
||||||
|
// Examples,
|
||||||
|
//
|
||||||
|
// go test # run all tests
|
||||||
|
// go test -tags step1 # run just step 1 tests.
|
||||||
|
// go test -tags 'step1 step2' # run step1 and step2 tests
|
||||||
|
//
|
||||||
|
// This source file contains step 1 tests only. For other tests see
|
||||||
|
// robot_simulator_step2_test.go and robot_simulator_step3_test.go.
|
||||||
|
//
|
||||||
|
// You are given the source file defs.go which defines a number of things
|
||||||
|
// the test program requires. It is organized into three sections by step.
|
||||||
|
//
|
||||||
|
// To complete step 1 you will define Right, Left, Advance, N, S, E, W,
|
||||||
|
// and Dir.String. Complete step 1 before moving on to step 2.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
const targetTestVersion = 3
|
||||||
|
|
||||||
|
func TestTestVersion(t *testing.T) {
|
||||||
|
if testVersion != targetTestVersion {
|
||||||
|
t.Fatalf("Found testVersion = %v, want %v", testVersion, targetTestVersion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStep1(t *testing.T) {
|
||||||
|
|
||||||
|
want := func(x, y int, dir Dir) {
|
||||||
|
_, _, line, _ := runtime.Caller(1)
|
||||||
|
if Step1Robot.X != x || Step1Robot.Y != y {
|
||||||
|
t.Fatalf("(from line %d) robot at = %d, %d. Want %d, %d.",
|
||||||
|
line, Step1Robot.X, Step1Robot.Y, x, y)
|
||||||
|
}
|
||||||
|
if Step1Robot.Dir != dir {
|
||||||
|
t.Fatalf("(from line %d) robot facing %v, want %v.",
|
||||||
|
line, Step1Robot.Dir, dir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
want(0, 0, N)
|
||||||
|
|
||||||
|
Advance()
|
||||||
|
want(0, 1, N)
|
||||||
|
|
||||||
|
Right()
|
||||||
|
want(0, 1, E)
|
||||||
|
|
||||||
|
Advance()
|
||||||
|
want(1, 1, E)
|
||||||
|
|
||||||
|
Left()
|
||||||
|
want(1, 1, N)
|
||||||
|
|
||||||
|
Left()
|
||||||
|
Left()
|
||||||
|
Advance()
|
||||||
|
want(1, 0, S)
|
||||||
|
|
||||||
|
Right()
|
||||||
|
Advance()
|
||||||
|
want(0, 0, W)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user