From d4d82c38e08057f6e43b409392b4451100248a71 Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Thu, 9 Jul 2020 22:05:12 +0100 Subject: [PATCH] Add "broadcast" command This will send a readable ascii triplet to all rovers in range --- pkg/game/command.go | 6 +++++ pkg/game/world.go | 47 +++++++++++++++++++++++++++++++++ pkg/game/world_test.go | 60 ++++++++++++++++++++++++++++++++++++++++++ proto/rove/rove.proto | 9 +++++-- 4 files changed, 120 insertions(+), 2 deletions(-) diff --git a/pkg/game/command.go b/pkg/game/command.go index 11428c3..96f95d8 100644 --- a/pkg/game/command.go +++ b/pkg/game/command.go @@ -12,6 +12,9 @@ const ( // CommandRecharge Will use one tick to charge the rover CommandRecharge = "recharge" + + // CommandBroadcast will broadcast a message to nearby rovers within range + CommandBroadcast = "broadcast" ) // Command represends a single command to execute @@ -20,6 +23,9 @@ type Command struct { // Used in the move command Bearing string `json:"bearing,omitempty"` + + // Used in the broadcast command + Message []byte `json:"message,omitempty"` } // CommandStream is a list of commands to execute in order diff --git a/pkg/game/world.go b/pkg/game/world.go index bd6d806..a48bea0 100644 --- a/pkg/game/world.go +++ b/pkg/game/world.go @@ -168,6 +168,40 @@ func (w *World) RoverRecharge(rover string) (int, error) { return i.Charge, nil } +// RoverBroadcast broadcasts a message to nearby rovers +func (w *World) RoverBroadcast(rover string, message []byte) (err error) { + w.worldMutex.Lock() + defer w.worldMutex.Unlock() + + i, ok := w.Rovers[rover] + if !ok { + return fmt.Errorf("Failed to find rover with name: %s", rover) + } + + // Use up a charge as needed, if available + if i.Charge == 0 { + return + } + i.Charge-- + + // Check all rovers + for r, rover := range w.Rovers { + if rover.Name == i.Name { + continue + } + + // Check if this rover is within range + if i.Pos.Distance(rover.Pos) < float64(i.Range) { + rover.AddLogEntryf("recieved %s from %s", string(message), i.Name) + w.Rovers[r] = rover + } + } + + i.AddLogEntryf("broadcasted %s", string(message)) + w.Rovers[rover] = i + return +} + // DestroyRover Removes an rover from the game func (w *World) DestroyRover(rover string) error { w.worldMutex.Lock() @@ -399,6 +433,15 @@ func (w *World) Enqueue(rover string, commands ...Command) error { if _, err := bearing.FromString(c.Bearing); err != nil { return fmt.Errorf("unknown bearing: %s", c.Bearing) } + case CommandBroadcast: + if len(c.Message) > 3 { + return fmt.Errorf("too many characters in message (limit 3): %d", len(c.Message)) + } + for _, b := range c.Message { + if b < 37 || b > 126 { + return fmt.Errorf("invalid message character: %c", b) + } + } case CommandStash: case CommandRepair: case CommandRecharge: @@ -494,6 +537,10 @@ func (w *World) ExecuteCommand(c *Command, rover string) (err error) { if err != nil { return err } + case CommandBroadcast: + if err := w.RoverBroadcast(rover, c.Message); err != nil { + return err + } default: return fmt.Errorf("unknown command: %s", c.Command) } diff --git a/pkg/game/world_test.go b/pkg/game/world_test.go index c67588a..c9654c5 100644 --- a/pkg/game/world_test.go +++ b/pkg/game/world_test.go @@ -365,3 +365,63 @@ func TestWorld_Daytime(t *testing.T) { world.ExecuteCommandQueues() } } + +func TestWorld_Broadcast(t *testing.T) { + world := NewWorld(8) + + a, err := world.SpawnRover() + assert.NoError(t, err) + + b, err := world.SpawnRover() + assert.NoError(t, err) + + // Warp rovers near to eachother + assert.NoError(t, world.WarpRover(a, vector.Vector{X: 0, Y: 0})) + assert.NoError(t, world.WarpRover(b, vector.Vector{X: 1, Y: 0})) + + // Broadcast from a + assert.NoError(t, world.RoverBroadcast(a, []byte{'A', 'B', 'C'})) + + // Check if b heard it + ra, err := world.GetRover(a) + assert.NoError(t, err) + assert.Equal(t, ra.MaximumCharge-1, ra.Charge, "A should have used a charge to broadcast") + assert.Contains(t, ra.Logs[len(ra.Logs)-1].Text, "ABC", "Rover B should have heard the broadcast") + + // Check if a logged it + rb, err := world.GetRover(b) + assert.NoError(t, err) + assert.Contains(t, rb.Logs[len(rb.Logs)-1].Text, "ABC", "Rover A should have logged it's broadcast") + + // Warp B outside of the range of A + assert.NoError(t, world.WarpRover(b, vector.Vector{X: ra.Range, Y: 0})) + + // Broadcast from a again + assert.NoError(t, world.RoverBroadcast(a, []byte{'X', 'Y', 'Z'})) + + // Check if b heard it + ra, err = world.GetRover(b) + assert.NoError(t, err) + assert.NotContains(t, ra.Logs[len(ra.Logs)-1].Text, "XYZ", "Rover B should not have heard the broadcast") + + // Check if a logged it + rb, err = world.GetRover(a) + assert.NoError(t, err) + assert.Contains(t, rb.Logs[len(rb.Logs)-1].Text, "XYZ", "Rover A should have logged it's broadcast") + + // Warp B outside of the range of A + assert.NoError(t, world.WarpRover(b, vector.Vector{X: ra.Range + 1, Y: 0})) + + // Broadcast from a again + assert.NoError(t, world.RoverBroadcast(a, []byte{'H', 'J', 'K'})) + + // Check if b heard it + ra, err = world.GetRover(b) + assert.NoError(t, err) + assert.NotContains(t, ra.Logs[len(ra.Logs)-1].Text, "HJK", "Rover B should have heard the broadcast") + + // Check if a logged it + rb, err = world.GetRover(a) + assert.NoError(t, err) + assert.Contains(t, rb.Logs[len(rb.Logs)-1].Text, "HJK", "Rover A should have logged it's broadcast") +} diff --git a/proto/rove/rove.proto b/proto/rove/rove.proto index 70b766c..d643e9c 100644 --- a/proto/rove/rove.proto +++ b/proto/rove/rove.proto @@ -66,10 +66,15 @@ message Command { // "stash" - Stashes item at current location in rover inventory // "repair" - Repairs the rover using an inventory object // "recharge" - Waits a tick to add more charge to the rover + // "broadcast" - Broadcasts a message to nearby rovers string command = 1; - // The bearing, example: NE + // A bearing, example: NE string bearing = 2; + + // A simple message, must be composed of printable ASCII glyphs (32-126) + // maximum of three characters + bytes message = 3; } message CommandRequest { @@ -135,7 +140,7 @@ message StatusResponse { // Position of the rover in world coordinates Vector position = 2; - // The range of this rover's radar + // The range of this rover's radar and broadcasting int32 range = 3; // The items in the rover inventory