diff --git a/pkg/game/command.go b/pkg/game/command.go index 18173e7..4a196c4 100644 --- a/pkg/game/command.go +++ b/pkg/game/command.go @@ -6,19 +6,9 @@ import "github.com/google/uuid" type Command func() error // 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 { - // TODO: Calculate the move itself - - //_, 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) + _, err := w.MoveRover(id, bearing, duration) + return err } } diff --git a/pkg/game/command_test.go b/pkg/game/command_test.go index 0fd2580..99cb296 100644 --- a/pkg/game/command_test.go +++ b/pkg/game/command_test.go @@ -3,44 +3,31 @@ package game import ( "testing" - "github.com/google/uuid" "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) { world := NewWorld() - a := uuid.New() - assert.NoError(t, world.SpawnRover(a), "Failed to spawn") - + a := world.SpawnRover() pos := Vector{ X: 1.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") - // TODO: Test the bearing/duration movement - /* - // Try the move command - moveCommand := world.CommandMove(a, move) - assert.NoError(t, world.Execute(moveCommand), "Failed to execute move command") + bearing := 0.0 + duration := 1.0 + // Try the move command + moveCommand := world.CommandMove(a, bearing, duration) + assert.NoError(t, world.Execute(moveCommand), "Failed to execute move command") - newpos, err := world.GetPosition(a) - assert.NoError(t, err, "Failed to set position for rover") - pos.Add(move) - assert.Equal(t, pos, newpos, "Failed to correctly set position for rover") - */ + newpos, err := world.RoverPosition(a) + assert.NoError(t, err, "Failed to set position for rover") + 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") } diff --git a/pkg/game/world.go b/pkg/game/world.go index e8b0735..c4f560c 100644 --- a/pkg/game/world.go +++ b/pkg/game/world.go @@ -2,6 +2,7 @@ package game import ( "fmt" + "math" "github.com/google/uuid" ) @@ -12,6 +13,15 @@ type World struct { 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 type Rover struct { // 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 Vector `json:"pos"` - // 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"` + // Attributes represents the physical attributes of the rover + Attributes RoverAttributes `json:"attributes"` } // NewWorld creates a new world object @@ -35,20 +42,25 @@ func NewWorld() *World { } // SpawnRover adds an rover to the game -func (w *World) SpawnRover(id uuid.UUID) error { - if _, ok := w.Rovers[id]; ok { - return fmt.Errorf("rover with id %s already exists in world", id) - } - +func (w *World) SpawnRover() uuid.UUID { // Initialise the 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 - w.Rovers[id] = rover + w.Rovers[rover.Id] = rover - return nil + return rover.Id } // Removes an rover from the game @@ -61,8 +73,17 @@ func (w *World) DestroyRover(id uuid.UUID) error { return nil } -// GetPosition returns the position of a given rover -func (w World) GetPosition(id uuid.UUID) (Vector, error) { +// RoverAttributes returns the attributes of a requested rover +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 { return i.Pos, nil } else { @@ -70,8 +91,8 @@ func (w World) GetPosition(id uuid.UUID) (Vector, error) { } } -// SetPosition sets an rovers position -func (w *World) SetPosition(id uuid.UUID, pos Vector) error { +// WarpRover sets an rovers position +func (w *World) WarpRover(id uuid.UUID, pos Vector) error { if i, ok := w.Rovers[id]; ok { i.Pos = pos w.Rovers[id] = i @@ -82,9 +103,21 @@ func (w *World) SetPosition(id uuid.UUID, pos Vector) error { } // 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 { - 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 return i.Pos, nil } else { diff --git a/pkg/game/world_test.go b/pkg/game/world_test.go index b773b6c..154bed7 100644 --- a/pkg/game/world_test.go +++ b/pkg/game/world_test.go @@ -3,7 +3,6 @@ package game import ( "testing" - "github.com/google/uuid" "github.com/stretchr/testify/assert" ) @@ -17,10 +16,8 @@ func TestNewWorld(t *testing.T) { func TestWorld_CreateRover(t *testing.T) { world := NewWorld() - a := uuid.New() - b := uuid.New() - assert.NoError(t, world.SpawnRover(a), "Failed to spawn") - assert.NoError(t, world.SpawnRover(b), "Failed to spawn") + a := world.SpawnRover() + b := world.SpawnRover() // Basic duplicate check 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) { world := NewWorld() - a := uuid.New() - b := uuid.New() - assert.NoError(t, world.SpawnRover(a), "Failed to spawn") - assert.NoError(t, world.SpawnRover(b), "Failed to spawn") + a := world.SpawnRover() + b := world.SpawnRover() err := world.DestroyRover(a) 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) { world := NewWorld() - a := uuid.New() - assert.NoError(t, world.SpawnRover(a), "Failed to spawn") + a := world.SpawnRover() + attribs, err := world.RoverAttributes(a) + assert.NoError(t, err, "Failed to get rover attribs") pos := Vector{ X: 1.0, Y: 2.0, } - err := world.SetPosition(a, pos) + err = world.WarpRover(a, pos) 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.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") - 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") } diff --git a/pkg/server/api.go b/pkg/server/api.go index ba1095b..dc3f639 100644 --- a/pkg/server/api.go +++ b/pkg/server/api.go @@ -77,7 +77,7 @@ type Command struct { // Used for CommandMove 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 } // ================ diff --git a/pkg/server/routes_test.go b/pkg/server/routes_test.go index 022ef3c..4963bad 100644 --- a/pkg/server/routes_test.go +++ b/pkg/server/routes_test.go @@ -7,6 +7,7 @@ import ( "net/http/httptest" "testing" + "github.com/mdiluz/rove/pkg/game" "github.com/stretchr/testify/assert" ) @@ -80,6 +81,9 @@ func TestHandleCommands(t *testing.T) { // Spawn the rover rover for the account _, inst, err := s.SpawnRoverForAccount(a.Id) + pos, err := s.world.RoverPosition(inst) + assert.NoError(t, err, "Couldn't get rover position") + data := CommandsData{ Id: a.Id.String(), Commands: []Command{ @@ -106,9 +110,11 @@ func TestHandleCommands(t *testing.T) { t.Errorf("got false for /commands") } - if _, err := s.world.GetPosition(inst); err != nil { - t.Error("Couldn't get position for the rover rover") - } + attrib, err := s.world.RoverAttributes(inst) + 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") } diff --git a/pkg/server/server.go b/pkg/server/server.go index c67907a..490f720 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -163,9 +163,8 @@ func (s *Server) wrapHandler(method string, handler Handler) func(w http.Respons // SpawnRoverForAccount spawns the rover rover for an account func (s *Server) SpawnRoverForAccount(accountid uuid.UUID) (game.Vector, uuid.UUID, error) { - inst := uuid.New() - s.world.SpawnRover(inst) - if pos, err := s.world.GetPosition(inst); err != nil { + inst := s.world.SpawnRover() + if pos, err := s.world.RoverPosition(inst); err != nil { return game.Vector{}, uuid.UUID{}, fmt.Errorf("No position found for created rover") } else {