Implement current wind direction and rover wind movement
This commit is contained in:
parent
c94ac68f44
commit
c89c5f6e74
7 changed files with 174 additions and 119 deletions
|
@ -107,3 +107,13 @@ func BearingToVector(b roveapi.Bearing) Vector {
|
|||
|
||||
return Vector{}
|
||||
}
|
||||
|
||||
// Dot returns the dot product of two vectors
|
||||
func Dot(a Vector, b Vector) int {
|
||||
return a.X*b.X + a.Y*b.Y
|
||||
}
|
||||
|
||||
// AngleCos returns the cosine of the angle between two vectors
|
||||
func AngleCos(a Vector, b Vector) float64 {
|
||||
return float64(Dot(a, b)) / a.Length() * b.Length()
|
||||
}
|
||||
|
|
|
@ -57,6 +57,9 @@ type Rover struct {
|
|||
// SailPosition is the current position of the sails
|
||||
SailPosition roveapi.SailPosition
|
||||
|
||||
// Current number of ticks in this move, used for sailing speeds
|
||||
MoveTicks int
|
||||
|
||||
// Logs Stores log of information
|
||||
Logs []RoverLogEntry
|
||||
}
|
||||
|
|
|
@ -10,11 +10,16 @@ import (
|
|||
"github.com/mdiluz/rove/proto/roveapi"
|
||||
)
|
||||
|
||||
const (
|
||||
TicksPerNormalMove = 4
|
||||
)
|
||||
|
||||
// CommandStream is a list of commands to execute in order
|
||||
type CommandStream []*roveapi.Command
|
||||
|
||||
// World describes a self contained universe and everything in it
|
||||
type World struct {
|
||||
|
||||
// TicksPerDay is the amount of ticks in a single day
|
||||
TicksPerDay int
|
||||
|
||||
|
@ -27,6 +32,9 @@ type World struct {
|
|||
// Atlas represends the world map of chunks and tiles
|
||||
Atlas Atlas
|
||||
|
||||
// Wind is the current wind direction
|
||||
Wind roveapi.Bearing
|
||||
|
||||
// Commands is the set of currently executing command streams per rover
|
||||
CommandQueue map[string]CommandStream
|
||||
// Incoming represents the set of commands to add to the queue at the end of the current tick
|
||||
|
@ -230,8 +238,8 @@ func (w *World) WarpRover(rover string, pos maths.Vector) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// MoveRover attempts to move a rover in a specific direction
|
||||
func (w *World) MoveRover(rover string, b roveapi.Bearing) (maths.Vector, error) {
|
||||
// TryMoveRover attempts to move a rover in a specific direction
|
||||
func (w *World) TryMoveRover(rover string, b roveapi.Bearing) (maths.Vector, error) {
|
||||
w.worldMutex.Lock()
|
||||
defer w.worldMutex.Unlock()
|
||||
|
||||
|
@ -240,12 +248,6 @@ func (w *World) MoveRover(rover string, b roveapi.Bearing) (maths.Vector, error)
|
|||
return maths.Vector{}, fmt.Errorf("no rover matching id")
|
||||
}
|
||||
|
||||
// Ensure the rover has energy
|
||||
if i.Charge <= 0 {
|
||||
return i.Pos, nil
|
||||
}
|
||||
i.Charge--
|
||||
|
||||
// Try the new move position
|
||||
newPos := i.Pos.Added(maths.BearingToVector(b))
|
||||
|
||||
|
@ -318,7 +320,9 @@ func (w *World) RoverToggle(rover string) (roveapi.SailPosition, error) {
|
|||
r.SailPosition = roveapi.SailPosition_CatchingWind
|
||||
}
|
||||
|
||||
w.Rovers[rover] = r
|
||||
// Reset the movement ticks
|
||||
r.MoveTicks = 0
|
||||
|
||||
return r.SailPosition, nil
|
||||
}
|
||||
|
||||
|
@ -334,6 +338,8 @@ func (w *World) RoverTurn(rover string, bearing roveapi.Bearing) (roveapi.Bearin
|
|||
|
||||
// Set the new bearing
|
||||
r.Bearing = bearing
|
||||
// Reset the movement ticks
|
||||
r.MoveTicks = 0
|
||||
|
||||
return r.Bearing, nil
|
||||
}
|
||||
|
@ -502,6 +508,64 @@ func (w *World) Tick() {
|
|||
// Add any incoming commands from this tick and clear that queue
|
||||
w.EnqueueAllIncoming()
|
||||
|
||||
// Change the wind every day
|
||||
if (w.CurrentTicks % w.TicksPerDay) == 0 {
|
||||
w.Wind = roveapi.Bearing((rand.Int() % 8) + 1) // Random cardinal bearing
|
||||
}
|
||||
|
||||
// Move all the rovers based on current wind and sails
|
||||
for n, r := range w.Rovers {
|
||||
// Skip if we're not catching the wind
|
||||
if r.SailPosition != roveapi.SailPosition_CatchingWind {
|
||||
continue
|
||||
}
|
||||
|
||||
// Increment the current move ticks
|
||||
r.MoveTicks++
|
||||
|
||||
// Get the difference between the two bearings
|
||||
// Normalise, we don't care about clockwise/anticlockwise
|
||||
diff := maths.Abs(int(w.Wind - r.Bearing))
|
||||
if diff > 4 {
|
||||
diff = 8 - diff
|
||||
}
|
||||
|
||||
// Calculate the travel "ticks"
|
||||
var ticksToMove int
|
||||
switch diff {
|
||||
case 0:
|
||||
// Going with the wind, travel at base speed of once every 4 ticks
|
||||
ticksToMove = TicksPerNormalMove
|
||||
case 1:
|
||||
// At a slight angle, we can go a little faster
|
||||
ticksToMove = TicksPerNormalMove / 2
|
||||
case 2:
|
||||
// Perpendicular to wind, max speed
|
||||
ticksToMove = 1
|
||||
case 3:
|
||||
// Heading at 45 degrees into the wind, back to min speed
|
||||
ticksToMove = TicksPerNormalMove
|
||||
case 4:
|
||||
// Heading durectly into the wind, no movement at all
|
||||
default:
|
||||
log.Fatalf("bearing difference of %d should be impossible", diff)
|
||||
}
|
||||
|
||||
// If we've incremented over the current move ticks on the rover, we can try and make the move
|
||||
if r.MoveTicks >= ticksToMove {
|
||||
_, err := w.TryMoveRover(n, r.Bearing)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
// TODO: Report this error somehow
|
||||
}
|
||||
|
||||
// Reset the move ticks
|
||||
r.MoveTicks = 0
|
||||
}
|
||||
|
||||
log.Print(ticksToMove)
|
||||
}
|
||||
|
||||
// Increment the current tick count
|
||||
w.CurrentTicks++
|
||||
}
|
||||
|
|
|
@ -78,25 +78,20 @@ func TestWorld_GetSetMovePosition(t *testing.T) {
|
|||
assert.Equal(t, pos, newPos, "Failed to correctly set position for rover")
|
||||
|
||||
b := roveapi.Bearing_North
|
||||
newPos, err = world.MoveRover(a, b)
|
||||
newPos, err = world.TryMoveRover(a, b)
|
||||
assert.NoError(t, err, "Failed to set position for rover")
|
||||
pos.Add(maths.Vector{X: 0, Y: 1})
|
||||
assert.Equal(t, pos, newPos, "Failed to correctly move position for rover")
|
||||
|
||||
rover, err := world.GetRover(a)
|
||||
assert.NoError(t, err, "Failed to get rover information")
|
||||
assert.Equal(t, rover.MaximumCharge-1, rover.Charge, "Rover should have lost charge for moving")
|
||||
assert.Contains(t, rover.Logs[len(rover.Logs)-1].Text, "moved", "Rover logs should contain the move")
|
||||
|
||||
// Place a tile in front of the rover
|
||||
world.Atlas.SetObject(maths.Vector{X: 0, Y: 2}, Object{Type: roveapi.Object_RockLarge})
|
||||
newPos, err = world.MoveRover(a, b)
|
||||
newPos, err = world.TryMoveRover(a, b)
|
||||
assert.NoError(t, err, "Failed to move rover")
|
||||
assert.Equal(t, pos, newPos, "Failed to correctly not move position for rover into wall")
|
||||
|
||||
rover, err = world.GetRover(a)
|
||||
assert.NoError(t, err, "Failed to get rover information")
|
||||
assert.Equal(t, rover.MaximumCharge-2, rover.Charge, "Rover should have lost charge for move attempt")
|
||||
}
|
||||
|
||||
func TestWorld_RadarFromRover(t *testing.T) {
|
||||
|
@ -224,7 +219,7 @@ func TestWorld_RoverDamage(t *testing.T) {
|
|||
|
||||
world.Atlas.SetObject(maths.Vector{X: 0.0, Y: 1.0}, Object{Type: roveapi.Object_RockLarge})
|
||||
|
||||
vec, err := world.MoveRover(a, roveapi.Bearing_North)
|
||||
vec, err := world.TryMoveRover(a, roveapi.Bearing_North)
|
||||
assert.NoError(t, err, "Failed to move rover")
|
||||
assert.Equal(t, pos, vec, "Rover managed to move into large rock")
|
||||
|
||||
|
@ -261,7 +256,7 @@ func TestWorld_RoverRepair(t *testing.T) {
|
|||
world.Atlas.SetObject(maths.Vector{X: 0.0, Y: 1.0}, Object{Type: roveapi.Object_RockLarge})
|
||||
|
||||
// Try and bump into the rock
|
||||
vec, err := world.MoveRover(a, roveapi.Bearing_North)
|
||||
vec, err := world.TryMoveRover(a, roveapi.Bearing_North)
|
||||
assert.NoError(t, err, "Failed to move rover")
|
||||
assert.Equal(t, pos, vec, "Rover managed to move into large rock")
|
||||
|
||||
|
@ -291,39 +286,6 @@ func TestWorld_RoverRepair(t *testing.T) {
|
|||
assert.Equal(t, originalInfo.Integrity, newinfo.Integrity, "rover should have kept the same integrity")
|
||||
}
|
||||
|
||||
func TestWorld_Charge(t *testing.T) {
|
||||
world := NewWorld(4)
|
||||
a, err := world.SpawnRover()
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Get the rover information
|
||||
rover, err := world.GetRover(a)
|
||||
assert.NoError(t, err, "Failed to get rover information")
|
||||
assert.Equal(t, rover.MaximumCharge, rover.Charge, "Rover should start with maximum charge")
|
||||
|
||||
// Use up all the charge
|
||||
for i := 0; i < rover.MaximumCharge; i++ {
|
||||
// Get the initial position
|
||||
initialPos, err := world.RoverPosition(a)
|
||||
assert.NoError(t, err, "Failed to get position for rover")
|
||||
|
||||
// Ensure the path ahead is empty
|
||||
world.Atlas.SetTile(initialPos.Added(maths.BearingToVector(roveapi.Bearing_North)), roveapi.Tile_Rock)
|
||||
world.Atlas.SetObject(initialPos.Added(maths.BearingToVector(roveapi.Bearing_North)), Object{Type: roveapi.Object_ObjectUnknown})
|
||||
|
||||
// Try and move north (along unblocked path)
|
||||
newPos, err := world.MoveRover(a, roveapi.Bearing_North)
|
||||
assert.NoError(t, err, "Failed to set position for rover")
|
||||
assert.Equal(t, initialPos.Added(maths.BearingToVector(roveapi.Bearing_North)), newPos, "Failed to correctly move position for rover")
|
||||
|
||||
// Ensure rover lost charge
|
||||
rover, err := world.GetRover(a)
|
||||
assert.NoError(t, err, "Failed to get rover information")
|
||||
assert.Equal(t, rover.MaximumCharge-(i+1), rover.Charge, "Rover should have lost charge")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestWorld_Daytime(t *testing.T) {
|
||||
world := NewWorld(1)
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue