Fully implement the bearing based movement

Instant, and without limit, for now
This commit is contained in:
Marc Di Luzio 2020-06-04 21:59:00 +01:00
parent 0fbad15c01
commit 6461b51c5c
7 changed files with 105 additions and 82 deletions

View file

@ -6,19 +6,9 @@ import "github.com/google/uuid"
type Command func() error type Command func() error
// CommandMove will move the rover in question // CommandMove will move the rover in question
func (w *World) CommandMove(id uuid.UUID, bearing float64, duration int64) Command { func (w *World) CommandMove(id uuid.UUID, bearing float64, duration float64) Command {
return func() error { return func() error {
// TODO: Calculate the move itself _, err := w.MoveRover(id, bearing, duration)
return err
//_, err := w.MovePosition(id, vec)
return nil
}
}
// CommandSpawn
// TODO: Two spawn commands with the same id could trigger a fail later on, we should prevent that somehow
func (w *World) CommandSpawn(id uuid.UUID) Command {
return func() error {
return w.SpawnRover(id)
} }
} }

View file

@ -3,44 +3,31 @@ package game
import ( import (
"testing" "testing"
"github.com/google/uuid"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func TestCommand_Spawn(t *testing.T) {
world := NewWorld()
a := uuid.New()
spawnCommand := world.CommandSpawn(a)
assert.NoError(t, world.Execute(spawnCommand), "Failed to execute spawn command")
rover, ok := world.Rovers[a]
assert.True(t, ok, "No new rover in world")
assert.Equal(t, a, rover.Id, "New rover has incorrect id")
}
func TestCommand_Move(t *testing.T) { func TestCommand_Move(t *testing.T) {
world := NewWorld() world := NewWorld()
a := uuid.New() a := world.SpawnRover()
assert.NoError(t, world.SpawnRover(a), "Failed to spawn")
pos := Vector{ pos := Vector{
X: 1.0, X: 1.0,
Y: 2.0, Y: 2.0,
} }
err := world.SetPosition(a, pos) attribs, err := world.RoverAttributes(a)
assert.NoError(t, err, "Failed to get rover attribs")
err = world.WarpRover(a, pos)
assert.NoError(t, err, "Failed to set position for rover") assert.NoError(t, err, "Failed to set position for rover")
// TODO: Test the bearing/duration movement bearing := 0.0
/* duration := 1.0
// Try the move command // Try the move command
moveCommand := world.CommandMove(a, move) moveCommand := world.CommandMove(a, bearing, duration)
assert.NoError(t, world.Execute(moveCommand), "Failed to execute move command") assert.NoError(t, world.Execute(moveCommand), "Failed to execute move command")
newpos, err := world.GetPosition(a) newpos, err := world.RoverPosition(a)
assert.NoError(t, err, "Failed to set position for rover") assert.NoError(t, err, "Failed to set position for rover")
pos.Add(move) pos.Add(Vector{0.0, float64(duration) * attribs.Speed}) // We should have moved duration*speed north
assert.Equal(t, pos, newpos, "Failed to correctly set position for rover") assert.Equal(t, pos, newpos, "Failed to correctly set position for rover")
*/
} }

View file

@ -2,6 +2,7 @@ package game
import ( import (
"fmt" "fmt"
"math"
"github.com/google/uuid" "github.com/google/uuid"
) )
@ -12,6 +13,15 @@ type World struct {
Rovers map[uuid.UUID]Rover `json:"rovers"` Rovers map[uuid.UUID]Rover `json:"rovers"`
} }
// RoverAttributes contains attributes of a rover
type RoverAttributes struct {
// Speed represents the Speed that the rover will move per second
Speed float64 `json:"speed"`
// Sight represents the distance the unit can see
Sight float64 `json:"sight"`
}
// Rover describes a single rover in the world // Rover describes a single rover in the world
type Rover struct { type Rover struct {
// Id is a unique ID for this rover // Id is a unique ID for this rover
@ -20,11 +30,8 @@ type Rover struct {
// Pos represents where this rover is in the world // Pos represents where this rover is in the world
Pos Vector `json:"pos"` Pos Vector `json:"pos"`
// Speed represents the Speed that the rover will move per second // Attributes represents the physical attributes of the rover
Speed float64 `json:"speed"` Attributes RoverAttributes `json:"attributes"`
// Sight represents the distance the unit can see
Sight float64 `json:"sight"`
} }
// NewWorld creates a new world object // NewWorld creates a new world object
@ -35,20 +42,25 @@ func NewWorld() *World {
} }
// SpawnRover adds an rover to the game // SpawnRover adds an rover to the game
func (w *World) SpawnRover(id uuid.UUID) error { func (w *World) SpawnRover() uuid.UUID {
if _, ok := w.Rovers[id]; ok {
return fmt.Errorf("rover with id %s already exists in world", id)
}
// Initialise the rover // Initialise the rover
rover := Rover{ rover := Rover{
Id: id, Id: uuid.New(),
// TODO: Set this somehow
Pos: Vector{},
// TODO: Stop these being random numbers
Attributes: RoverAttributes{
Speed: 1.0,
Sight: 20.0,
},
} }
// Append the rover to the list // Append the rover to the list
w.Rovers[id] = rover w.Rovers[rover.Id] = rover
return nil return rover.Id
} }
// Removes an rover from the game // Removes an rover from the game
@ -61,8 +73,17 @@ func (w *World) DestroyRover(id uuid.UUID) error {
return nil return nil
} }
// GetPosition returns the position of a given rover // RoverAttributes returns the attributes of a requested rover
func (w World) GetPosition(id uuid.UUID) (Vector, error) { func (w World) RoverAttributes(id uuid.UUID) (RoverAttributes, error) {
if i, ok := w.Rovers[id]; ok {
return i.Attributes, nil
} else {
return RoverAttributes{}, fmt.Errorf("no rover matching id")
}
}
// RoverPosition returns the position of a given rover
func (w World) RoverPosition(id uuid.UUID) (Vector, error) {
if i, ok := w.Rovers[id]; ok { if i, ok := w.Rovers[id]; ok {
return i.Pos, nil return i.Pos, nil
} else { } else {
@ -70,8 +91,8 @@ func (w World) GetPosition(id uuid.UUID) (Vector, error) {
} }
} }
// SetPosition sets an rovers position // WarpRover sets an rovers position
func (w *World) SetPosition(id uuid.UUID, pos Vector) error { func (w *World) WarpRover(id uuid.UUID, pos Vector) error {
if i, ok := w.Rovers[id]; ok { if i, ok := w.Rovers[id]; ok {
i.Pos = pos i.Pos = pos
w.Rovers[id] = i w.Rovers[id] = i
@ -82,9 +103,21 @@ func (w *World) SetPosition(id uuid.UUID, pos Vector) error {
} }
// SetPosition sets an rovers position // SetPosition sets an rovers position
func (w *World) MovePosition(id uuid.UUID, vec Vector) (Vector, error) { func (w *World) MoveRover(id uuid.UUID, bearing float64, duration float64) (Vector, error) {
if i, ok := w.Rovers[id]; ok { if i, ok := w.Rovers[id]; ok {
i.Pos.Add(vec) // Calculate the distance
distance := i.Attributes.Speed * float64(duration)
// Calculate the full movement based on the bearing
move := Vector{
X: math.Sin(bearing) * distance,
Y: math.Cos(bearing) * distance,
}
// Increment the position by the movement
i.Pos.Add(move)
// Set the rover values to the new ones
w.Rovers[id] = i w.Rovers[id] = i
return i.Pos, nil return i.Pos, nil
} else { } else {

View file

@ -3,7 +3,6 @@ package game
import ( import (
"testing" "testing"
"github.com/google/uuid"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -17,10 +16,8 @@ func TestNewWorld(t *testing.T) {
func TestWorld_CreateRover(t *testing.T) { func TestWorld_CreateRover(t *testing.T) {
world := NewWorld() world := NewWorld()
a := uuid.New() a := world.SpawnRover()
b := uuid.New() b := world.SpawnRover()
assert.NoError(t, world.SpawnRover(a), "Failed to spawn")
assert.NoError(t, world.SpawnRover(b), "Failed to spawn")
// Basic duplicate check // Basic duplicate check
if a == b { if a == b {
@ -30,12 +27,20 @@ func TestWorld_CreateRover(t *testing.T) {
} }
} }
func TestWorld_RoverAttributes(t *testing.T) {
world := NewWorld()
a := world.SpawnRover()
attribs, err := world.RoverAttributes(a)
assert.NoError(t, err, "Failed to get rover attribs")
assert.NotZero(t, attribs.Sight, "Rover should not be spawned blind")
assert.NotZero(t, attribs.Speed, "Rover should not be spawned unable to move")
}
func TestWorld_DestroyRover(t *testing.T) { func TestWorld_DestroyRover(t *testing.T) {
world := NewWorld() world := NewWorld()
a := uuid.New() a := world.SpawnRover()
b := uuid.New() b := world.SpawnRover()
assert.NoError(t, world.SpawnRover(a), "Failed to spawn")
assert.NoError(t, world.SpawnRover(b), "Failed to spawn")
err := world.DestroyRover(a) err := world.DestroyRover(a)
assert.NoError(t, err, "Error returned from rover destroy") assert.NoError(t, err, "Error returned from rover destroy")
@ -50,23 +55,26 @@ func TestWorld_DestroyRover(t *testing.T) {
func TestWorld_GetSetMovePosition(t *testing.T) { func TestWorld_GetSetMovePosition(t *testing.T) {
world := NewWorld() world := NewWorld()
a := uuid.New() a := world.SpawnRover()
assert.NoError(t, world.SpawnRover(a), "Failed to spawn") attribs, err := world.RoverAttributes(a)
assert.NoError(t, err, "Failed to get rover attribs")
pos := Vector{ pos := Vector{
X: 1.0, X: 1.0,
Y: 2.0, Y: 2.0,
} }
err := world.SetPosition(a, pos) err = world.WarpRover(a, pos)
assert.NoError(t, err, "Failed to set position for rover") assert.NoError(t, err, "Failed to set position for rover")
newpos, err := world.GetPosition(a) newpos, err := world.RoverPosition(a)
assert.NoError(t, err, "Failed to set position for rover") assert.NoError(t, err, "Failed to set position for rover")
assert.Equal(t, pos, newpos, "Failed to correctly set position for rover") assert.Equal(t, pos, newpos, "Failed to correctly set position for rover")
newpos, err = world.MovePosition(a, pos) bearing := 0.0
duration := 1.0
newpos, err = world.MoveRover(a, bearing, duration)
assert.NoError(t, err, "Failed to set position for rover") assert.NoError(t, err, "Failed to set position for rover")
pos.Add(pos) pos.Add(Vector{0, attribs.Speed * float64(duration)}) // We should have move one unit of the speed north
assert.Equal(t, pos, newpos, "Failed to correctly move position for rover") assert.Equal(t, pos, newpos, "Failed to correctly move position for rover")
} }

View file

@ -77,7 +77,7 @@ type Command struct {
// Used for CommandMove // Used for CommandMove
Bearing float64 `json:"bearing"` // The direction to move in degrees Bearing float64 `json:"bearing"` // The direction to move in degrees
Duration int64 `json:"duration"` // The duration of the move in seconds Duration float64 `json:"duration"` // The duration of the move in seconds
} }
// ================ // ================

View file

@ -7,6 +7,7 @@ import (
"net/http/httptest" "net/http/httptest"
"testing" "testing"
"github.com/mdiluz/rove/pkg/game"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -80,6 +81,9 @@ func TestHandleCommands(t *testing.T) {
// Spawn the rover rover for the account // Spawn the rover rover for the account
_, inst, err := s.SpawnRoverForAccount(a.Id) _, inst, err := s.SpawnRoverForAccount(a.Id)
pos, err := s.world.RoverPosition(inst)
assert.NoError(t, err, "Couldn't get rover position")
data := CommandsData{ data := CommandsData{
Id: a.Id.String(), Id: a.Id.String(),
Commands: []Command{ Commands: []Command{
@ -106,9 +110,11 @@ func TestHandleCommands(t *testing.T) {
t.Errorf("got false for /commands") t.Errorf("got false for /commands")
} }
if _, err := s.world.GetPosition(inst); err != nil { attrib, err := s.world.RoverAttributes(inst)
t.Error("Couldn't get position for the rover rover") assert.NoError(t, err, "Couldn't get rover attribs")
}
// TODO: Check position is correct pos2, err := s.world.RoverPosition(inst)
assert.NoError(t, err, "Couldn't get rover position")
pos.Add(game.Vector{X: 0.0, Y: attrib.Speed * 1}) // Should have moved north by the speed and duration
assert.Equal(t, pos, pos2, "Rover should have moved by bearing")
} }

View file

@ -163,9 +163,8 @@ func (s *Server) wrapHandler(method string, handler Handler) func(w http.Respons
// SpawnRoverForAccount spawns the rover rover for an account // SpawnRoverForAccount spawns the rover rover for an account
func (s *Server) SpawnRoverForAccount(accountid uuid.UUID) (game.Vector, uuid.UUID, error) { func (s *Server) SpawnRoverForAccount(accountid uuid.UUID) (game.Vector, uuid.UUID, error) {
inst := uuid.New() inst := s.world.SpawnRover()
s.world.SpawnRover(inst) if pos, err := s.world.RoverPosition(inst); err != nil {
if pos, err := s.world.GetPosition(inst); err != nil {
return game.Vector{}, uuid.UUID{}, fmt.Errorf("No position found for created rover") return game.Vector{}, uuid.UUID{}, fmt.Errorf("No position found for created rover")
} else { } else {