From ce5ea92f7977a0caadc9be7629f602e4fc2b03bc Mon Sep 17 00:00:00 2001 From: Brian Buller Date: Thu, 24 Aug 2017 13:50:10 -0500 Subject: [PATCH] Finished go/minesweeper --- go/minesweeper/README.md | 48 +++ go/minesweeper/board.go | 112 +++++++ go/minesweeper/minesweeper_test.go | 198 ++++++++++++ go/robot-simulator/README.md | 52 ++++ go/robot-simulator/defs.go | 32 ++ .../robot_simulator_step2_test.go | 89 ++++++ .../robot_simulator_step3_test.go | 293 ++++++++++++++++++ go/robot-simulator/robot_simulator_test.go | 71 +++++ 8 files changed, 895 insertions(+) create mode 100644 go/minesweeper/README.md create mode 100644 go/minesweeper/board.go create mode 100644 go/minesweeper/minesweeper_test.go create mode 100644 go/robot-simulator/README.md create mode 100644 go/robot-simulator/defs.go create mode 100644 go/robot-simulator/robot_simulator_step2_test.go create mode 100644 go/robot-simulator/robot_simulator_step3_test.go create mode 100644 go/robot-simulator/robot_simulator_test.go diff --git a/go/minesweeper/README.md b/go/minesweeper/README.md new file mode 100644 index 0000000..71d384f --- /dev/null +++ b/go/minesweeper/README.md @@ -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. diff --git a/go/minesweeper/board.go b/go/minesweeper/board.go new file mode 100644 index 0000000..b47531d --- /dev/null +++ b/go/minesweeper/board.go @@ -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'})) +} diff --git a/go/minesweeper/minesweeper_test.go b/go/minesweeper/minesweeper_test.go new file mode 100644 index 0000000..394edcd --- /dev/null +++ b/go/minesweeper/minesweeper_test.go @@ -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() + } +} diff --git a/go/robot-simulator/README.md b/go/robot-simulator/README.md new file mode 100644 index 0000000..2c21272 --- /dev/null +++ b/go/robot-simulator/README.md @@ -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. diff --git a/go/robot-simulator/defs.go b/go/robot-simulator/defs.go new file mode 100644 index 0000000..7b7e0d5 --- /dev/null +++ b/go/robot-simulator/defs.go @@ -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 +} diff --git a/go/robot-simulator/robot_simulator_step2_test.go b/go/robot-simulator/robot_simulator_step2_test.go new file mode 100644 index 0000000..539da87 --- /dev/null +++ b/go/robot-simulator/robot_simulator_step2_test.go @@ -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) + } + } +} diff --git a/go/robot-simulator/robot_simulator_step3_test.go b/go/robot-simulator/robot_simulator_step3_test.go new file mode 100644 index 0000000..c29803e --- /dev/null +++ b/go/robot-simulator/robot_simulator_step3_test.go @@ -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) + } +} diff --git a/go/robot-simulator/robot_simulator_test.go b/go/robot-simulator/robot_simulator_test.go new file mode 100644 index 0000000..fcbc4e7 --- /dev/null +++ b/go/robot-simulator/robot_simulator_test.go @@ -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) +}