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