From 5d80cb2596848ea6d745d33dfb5deae4b40bcf5c Mon Sep 17 00:00:00 2001
From: Marc Di Luzio <marc.diluzio@gmail.com>
Date: Wed, 22 Jul 2020 23:50:42 +0100
Subject: [PATCH] Implement sailing tests and fix into-the-wind bug

---
 pkg/rove/world.go      |  2 +-
 pkg/rove/world_test.go | 78 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 79 insertions(+), 1 deletion(-)

diff --git a/pkg/rove/world.go b/pkg/rove/world.go
index 0265951..b8464aa 100644
--- a/pkg/rove/world.go
+++ b/pkg/rove/world.go
@@ -552,7 +552,7 @@ func (w *World) Tick() {
 		}
 
 		// If we've incremented over the current move ticks on the rover, we can try and make the move
-		if r.MoveTicks >= ticksToMove {
+		if ticksToMove != 0 && r.MoveTicks >= ticksToMove {
 			_, err := w.TryMoveRover(n, r.Bearing)
 			if err != nil {
 				log.Println(err)
diff --git a/pkg/rove/world_test.go b/pkg/rove/world_test.go
index 2dbcc86..a3c2f47 100644
--- a/pkg/rove/world_test.go
+++ b/pkg/rove/world_test.go
@@ -388,3 +388,81 @@ func TestWorld_Broadcast(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Contains(t, rb.Logs[len(rb.Logs)-1].Text, "HJK", "Rover A should have logged it's broadcast")
 }
+
+func TestWorld_Sailing(t *testing.T) {
+	world := NewWorld(8)
+	world.Tick()                       // One initial tick to set the wind direction the first time
+	world.Wind = roveapi.Bearing_North // Set the wind direction to north
+
+	name, err := world.SpawnRover()
+	assert.NoError(t, err)
+
+	// Warp the rover to 0,0 after clearing it
+	world.Atlas.SetObject(maths.Vector{X: 0, Y: 0}, Object{Type: roveapi.Object_ObjectUnknown})
+	assert.NoError(t, world.WarpRover(name, maths.Vector{X: 0, Y: 0}))
+
+	s, err := world.RoverToggle(name)
+	assert.NoError(t, err)
+	assert.Equal(t, roveapi.SailPosition_CatchingWind, s)
+
+	b, err := world.RoverTurn(name, roveapi.Bearing_North)
+	assert.NoError(t, err)
+	assert.Equal(t, roveapi.Bearing_North, b)
+
+	// Clear the space to the north
+	world.Atlas.SetObject(maths.Vector{X: 0, Y: 1}, Object{Type: roveapi.Object_ObjectUnknown})
+
+	// Tick the world and check we've moved not moved
+	world.Tick()
+	info, err := world.GetRover(name)
+	assert.NoError(t, err)
+	assert.Equal(t, maths.Vector{Y: 0}, info.Pos)
+
+	// Loop a few more times
+	for i := 0; i < TicksPerNormalMove-2; i++ {
+		world.Tick()
+		info, err := world.GetRover(name)
+		assert.NoError(t, err)
+		assert.Equal(t, maths.Vector{Y: 0}, info.Pos)
+	}
+
+	// Now check we've moved (after the TicksPerNormalMove number of ticks)
+	world.Tick()
+	info, err = world.GetRover(name)
+	assert.NoError(t, err)
+	assert.Equal(t, maths.Vector{Y: 1}, info.Pos)
+
+	// Reset the world ticks back to stop any wind changes etc.
+	world.CurrentTicks = 1
+
+	// Face the rover south, into the wind
+	b, err = world.RoverTurn(name, roveapi.Bearing_South)
+	assert.NoError(t, err)
+	assert.Equal(t, roveapi.Bearing_South, b)
+
+	// Tick a bunch, we should never move
+	for i := 0; i < TicksPerNormalMove*2; i++ {
+		world.Tick()
+		info, err := world.GetRover(name)
+		assert.NoError(t, err)
+		assert.Equal(t, maths.Vector{Y: 1}, info.Pos)
+	}
+
+	// Reset the world ticks back to stop any wind changes etc.
+	world.CurrentTicks = 1
+	world.Wind = roveapi.Bearing_SouthEast // Set up a south easternly wind
+
+	// Turn the rover perpendicular
+	b, err = world.RoverTurn(name, roveapi.Bearing_NorthEast)
+	assert.NoError(t, err)
+	assert.Equal(t, roveapi.Bearing_NorthEast, b)
+
+	// Clear a space
+	world.Atlas.SetObject(maths.Vector{X: 1, Y: 2}, Object{Type: roveapi.Object_ObjectUnknown})
+
+	// Now check we've moved immediately
+	world.Tick()
+	info, err = world.GetRover(name)
+	assert.NoError(t, err)
+	assert.Equal(t, maths.Vector{X: 1, Y: 2}, info.Pos)
+}