Add "broadcast" command

This will send a readable ascii triplet to all rovers in range
This commit is contained in:
Marc Di Luzio 2020-07-09 22:05:12 +01:00
parent 30ca488890
commit d4d82c38e0
4 changed files with 120 additions and 2 deletions

View file

@ -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

View file

@ -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)
}

View file

@ -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")
}

View file

@ -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