Fix world spawning and radar
Also expand test coverage a little to ensure it's correct
This commit is contained in:
parent
fba75960f8
commit
43588c0e4b
8 changed files with 130 additions and 45 deletions
|
@ -174,10 +174,10 @@ func InnerMain(command string) error {
|
||||||
return fmt.Errorf("Server returned failure: %s", response.Error)
|
return fmt.Errorf("Server returned failure: %s", response.Error)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Print the radar
|
// Print out the radar
|
||||||
num := int(math.Sqrt(float64(len(response.Tiles))))
|
num := int(math.Sqrt(float64(len(response.Tiles))))
|
||||||
for i := 0; i < num; i++ {
|
|
||||||
for j := num - 1; j >= 0; j-- {
|
for j := num - 1; j >= 0; j-- {
|
||||||
|
for i := 0; i < num; i++ {
|
||||||
fmt.Printf("%d", response.Tiles[i+num*j])
|
fmt.Printf("%d", response.Tiles[i+num*j])
|
||||||
}
|
}
|
||||||
fmt.Print("\n")
|
fmt.Print("\n")
|
||||||
|
|
|
@ -47,8 +47,8 @@ func NewAtlas(size int, chunkSize int) Atlas {
|
||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
|
|
||||||
// SpawnWorld spawns the current world
|
// SpawnRocks peppers the world with rocks
|
||||||
func (a *Atlas) SpawnWorld() error {
|
func (a *Atlas) SpawnRocks() error {
|
||||||
extent := a.ChunkSize * (a.Size / 2)
|
extent := a.ChunkSize * (a.Size / 2)
|
||||||
|
|
||||||
// Pepper the current world with rocks
|
// Pepper the current world with rocks
|
||||||
|
@ -62,16 +62,23 @@ func (a *Atlas) SpawnWorld() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SpawnWalls spawns the around the world
|
||||||
|
func (a *Atlas) SpawnWalls() error {
|
||||||
|
extent := a.ChunkSize * (a.Size / 2)
|
||||||
|
|
||||||
// Surround the atlas in walls
|
// Surround the atlas in walls
|
||||||
for i := -extent; i < extent; i++ {
|
for i := -extent; i < extent; i++ {
|
||||||
|
|
||||||
if err := a.SetTile(Vector{i, extent - 1}, TileWall); err != nil {
|
if err := a.SetTile(Vector{i, extent - 1}, TileWall); err != nil { // N
|
||||||
return err
|
return err
|
||||||
} else if a.SetTile(Vector{extent - 1, i}, TileWall); err != nil {
|
} else if a.SetTile(Vector{extent - 1, i}, TileWall); err != nil { // E
|
||||||
return err
|
return err
|
||||||
} else if a.SetTile(Vector{-extent, i}, TileWall); err != nil {
|
} else if a.SetTile(Vector{i, -extent}, TileWall); err != nil { // S
|
||||||
return err
|
return err
|
||||||
} else if a.SetTile(Vector{i, extent - 1}, TileWall); err != nil {
|
} else if a.SetTile(Vector{-extent, i}, TileWall); err != nil { // W
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -137,16 +137,32 @@ func TestAtlas_Grown(t *testing.T) {
|
||||||
|
|
||||||
func TestAtlas_SpawnWorld(t *testing.T) {
|
func TestAtlas_SpawnWorld(t *testing.T) {
|
||||||
// Start with a small example
|
// Start with a small example
|
||||||
a := NewAtlas(2, 2)
|
a := NewAtlas(2, 4)
|
||||||
assert.NotNil(t, a)
|
assert.NotNil(t, a)
|
||||||
assert.Equal(t, 4, len(a.Chunks))
|
assert.Equal(t, 4, len(a.Chunks))
|
||||||
|
assert.NoError(t, a.SpawnWalls())
|
||||||
|
|
||||||
assert.NoError(t, a.SpawnWorld())
|
for i := -4; i < 4; i++ {
|
||||||
tile, err := a.GetTile(Vector{1, 1})
|
tile, err := a.GetTile(Vector{i, -4})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, TileWall, tile)
|
assert.Equal(t, TileWall, tile)
|
||||||
|
}
|
||||||
|
|
||||||
tile, err = a.GetTile(Vector{-2, -2})
|
for i := -4; i < 4; i++ {
|
||||||
|
tile, err := a.GetTile(Vector{-4, i})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, TileWall, tile)
|
assert.Equal(t, TileWall, tile)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := -4; i < 4; i++ {
|
||||||
|
tile, err := a.GetTile(Vector{3, i})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, TileWall, tile)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := -4; i < 4; i++ {
|
||||||
|
tile, err := a.GetTile(Vector{i, 3})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, TileWall, tile)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCommand_Move(t *testing.T) {
|
func TestCommand_Move(t *testing.T) {
|
||||||
world := NewWorld()
|
world := NewWorld(2, 4)
|
||||||
a, err := world.SpawnRover()
|
a, err := world.SpawnRover()
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
pos := Vector{
|
pos := Vector{
|
||||||
|
|
|
@ -28,17 +28,20 @@ type World struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewWorld creates a new world object
|
// NewWorld creates a new world object
|
||||||
func NewWorld() *World {
|
func NewWorld(size int, chunkSize int) *World {
|
||||||
return &World{
|
return &World{
|
||||||
Rovers: make(map[uuid.UUID]Rover),
|
Rovers: make(map[uuid.UUID]Rover),
|
||||||
CommandQueue: make(map[uuid.UUID]CommandStream),
|
CommandQueue: make(map[uuid.UUID]CommandStream),
|
||||||
Atlas: NewAtlas(4, 8), // TODO: Choose an appropriate world size
|
Atlas: NewAtlas(size, chunkSize), // TODO: Choose an appropriate world size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SpawnWorld spawns a border at the edge of the world atlas
|
// SpawnWorld spawns a border at the edge of the world atlas
|
||||||
func (w *World) SpawnWorld() error {
|
func (w *World) SpawnWorld() error {
|
||||||
return w.Atlas.SpawnWorld()
|
if err := w.Atlas.SpawnRocks(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return w.Atlas.SpawnWalls()
|
||||||
}
|
}
|
||||||
|
|
||||||
// SpawnRover adds an rover to the game
|
// SpawnRover adds an rover to the game
|
||||||
|
@ -120,6 +123,20 @@ func (w *World) RoverAttributes(id uuid.UUID) (RoverAttributes, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetRoverAttributes sets the attributes of a requested rover
|
||||||
|
func (w *World) SetRoverAttributes(id uuid.UUID, attributes RoverAttributes) error {
|
||||||
|
w.worldMutex.Lock()
|
||||||
|
defer w.worldMutex.Unlock()
|
||||||
|
|
||||||
|
if i, ok := w.Rovers[id]; ok {
|
||||||
|
i.Attributes = attributes
|
||||||
|
w.Rovers[id] = i
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("no rover matching id")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// WarpRover sets an rovers position
|
// WarpRover sets an rovers position
|
||||||
func (w *World) WarpRover(id uuid.UUID, pos Vector) error {
|
func (w *World) WarpRover(id uuid.UUID, pos Vector) error {
|
||||||
w.worldMutex.Lock()
|
w.worldMutex.Lock()
|
||||||
|
@ -214,8 +231,8 @@ func (w *World) RadarFromRover(id uuid.UUID) ([]Tile, error) {
|
||||||
|
|
||||||
// Gather up all tiles within the range
|
// Gather up all tiles within the range
|
||||||
var radar = make([]Tile, radarSpan*radarSpan)
|
var radar = make([]Tile, radarSpan*radarSpan)
|
||||||
for i := scanMin.X; i < scanMax.X; i++ {
|
for j := scanMin.Y; j <= scanMax.Y; j++ {
|
||||||
for j := scanMin.Y; j < scanMax.Y; j++ {
|
for i := scanMin.X; i <= scanMax.X; i++ {
|
||||||
q := Vector{i, j}
|
q := Vector{i, j}
|
||||||
|
|
||||||
if tile, err := w.Atlas.GetTile(q); err != nil {
|
if tile, err := w.Atlas.GetTile(q); err != nil {
|
||||||
|
@ -224,7 +241,8 @@ func (w *World) RadarFromRover(id uuid.UUID) ([]Tile, error) {
|
||||||
} else {
|
} else {
|
||||||
// Get the position relative to the bottom left of the radar
|
// Get the position relative to the bottom left of the radar
|
||||||
relative := q.Added(radarMin.Negated())
|
relative := q.Added(radarMin.Negated())
|
||||||
radar[relative.X+relative.Y*radarSpan] = tile
|
index := relative.X + relative.Y*radarSpan
|
||||||
|
radar[index] = tile
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,14 +8,14 @@ import (
|
||||||
|
|
||||||
func TestNewWorld(t *testing.T) {
|
func TestNewWorld(t *testing.T) {
|
||||||
// Very basic for now, nothing to verify
|
// Very basic for now, nothing to verify
|
||||||
world := NewWorld()
|
world := NewWorld(4, 4)
|
||||||
if world == nil {
|
if world == nil {
|
||||||
t.Error("Failed to create world")
|
t.Error("Failed to create world")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWorld_CreateRover(t *testing.T) {
|
func TestWorld_CreateRover(t *testing.T) {
|
||||||
world := NewWorld()
|
world := NewWorld(2, 8)
|
||||||
a, err := world.SpawnRover()
|
a, err := world.SpawnRover()
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
b, err := world.SpawnRover()
|
b, err := world.SpawnRover()
|
||||||
|
@ -30,7 +30,7 @@ func TestWorld_CreateRover(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWorld_RoverAttributes(t *testing.T) {
|
func TestWorld_RoverAttributes(t *testing.T) {
|
||||||
world := NewWorld()
|
world := NewWorld(2, 4)
|
||||||
a, err := world.SpawnRover()
|
a, err := world.SpawnRover()
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ func TestWorld_RoverAttributes(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWorld_DestroyRover(t *testing.T) {
|
func TestWorld_DestroyRover(t *testing.T) {
|
||||||
world := NewWorld()
|
world := NewWorld(4, 1)
|
||||||
a, err := world.SpawnRover()
|
a, err := world.SpawnRover()
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
b, err := world.SpawnRover()
|
b, err := world.SpawnRover()
|
||||||
|
@ -59,7 +59,7 @@ func TestWorld_DestroyRover(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWorld_GetSetMovePosition(t *testing.T) {
|
func TestWorld_GetSetMovePosition(t *testing.T) {
|
||||||
world := NewWorld()
|
world := NewWorld(4, 4)
|
||||||
a, err := world.SpawnRover()
|
a, err := world.SpawnRover()
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
attribs, err := world.RoverAttributes(a)
|
attribs, err := world.RoverAttributes(a)
|
||||||
|
@ -91,29 +91,52 @@ func TestWorld_GetSetMovePosition(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWorld_RadarFromRover(t *testing.T) {
|
func TestWorld_RadarFromRover(t *testing.T) {
|
||||||
world := NewWorld()
|
// Create world that should have visible walls on the radar
|
||||||
|
world := NewWorld(4, 2)
|
||||||
a, err := world.SpawnRover()
|
a, err := world.SpawnRover()
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
b, err := world.SpawnRover()
|
b, err := world.SpawnRover()
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Get a's attributes
|
// Set the rover range to a predictable value
|
||||||
attrib, err := world.RoverAttributes(a)
|
attrib, err := world.RoverAttributes(a)
|
||||||
assert.NoError(t, err, "Failed to get rover attribs")
|
assert.NoError(t, err, "Failed to get rover attribs")
|
||||||
|
attrib.Range = 4 // Set the range to 4 so we can predict the radar fully
|
||||||
|
err = world.SetRoverAttributes(a, attrib)
|
||||||
|
assert.NoError(t, err, "Failed to set rover attribs")
|
||||||
|
|
||||||
// Warp the rovers so a can see b
|
// Warp the rovers into position
|
||||||
bpos := Vector{-attrib.Range, -attrib.Range}
|
bpos := Vector{-3, -3}
|
||||||
assert.NoError(t, world.WarpRover(a, Vector{0, 0}), "Failed to warp rover")
|
assert.NoError(t, world.WarpRover(a, Vector{0, 0}), "Failed to warp rover")
|
||||||
assert.NoError(t, world.WarpRover(b, bpos), "Failed to warp rover")
|
assert.NoError(t, world.WarpRover(b, bpos), "Failed to warp rover")
|
||||||
|
|
||||||
|
// Spawn the world wall
|
||||||
|
err = world.Atlas.SpawnWalls()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
radar, err := world.RadarFromRover(a)
|
radar, err := world.RadarFromRover(a)
|
||||||
assert.NoError(t, err, "Failed to get radar from rover")
|
assert.NoError(t, err, "Failed to get radar from rover")
|
||||||
fullRange := attrib.Range + attrib.Range + 1
|
fullRange := 4 + 4 + 1
|
||||||
assert.Equal(t, fullRange*fullRange, len(radar), "Radar returned wrong number of rovers")
|
assert.Equal(t, fullRange*fullRange, len(radar), "Radar returned wrong length")
|
||||||
|
|
||||||
// bottom left should be a rover (we put one there with bpos)
|
// It should look like:
|
||||||
assert.Equal(t, radar[0], TileRover, "Rover not found on radar in expected position")
|
// ---------
|
||||||
// Centre should be rover
|
// OOOOOOOO-
|
||||||
assert.Equal(t, radar[fullRange*fullRange/2], TileRover, "Rover not found on radar in expected position")
|
// O------O-
|
||||||
|
// O------O-
|
||||||
|
// O---R--O-
|
||||||
|
// O------O-
|
||||||
|
// O------O-
|
||||||
|
// OR-----O-
|
||||||
|
// OOOOOOOO-
|
||||||
|
|
||||||
|
// Test all expected values
|
||||||
|
assert.Equal(t, TileRover, radar[1+fullRange])
|
||||||
|
assert.Equal(t, TileRover, radar[4+4*fullRange])
|
||||||
|
for i := 0; i < 8; i++ {
|
||||||
|
assert.Equal(t, TileWall, radar[i])
|
||||||
|
assert.Equal(t, TileWall, radar[i+(7*9)])
|
||||||
|
assert.Equal(t, TileWall, radar[i*9])
|
||||||
|
assert.Equal(t, TileWall, radar[(i*9)+7])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -140,15 +140,21 @@ func TestHandleRadar(t *testing.T) {
|
||||||
assert.NoError(t, err, "Error registering account")
|
assert.NoError(t, err, "Error registering account")
|
||||||
|
|
||||||
// Spawn the rover rover for the account
|
// Spawn the rover rover for the account
|
||||||
_, id, err := s.SpawnRoverForAccount(a.Id)
|
attrib, id, err := s.SpawnRoverForAccount(a.Id)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Warp this rover to 0
|
// Warp this rover to 0,0
|
||||||
assert.NoError(t, s.world.WarpRover(id, game.Vector{}))
|
assert.NoError(t, s.world.WarpRover(id, game.Vector{}))
|
||||||
|
|
||||||
// Set a tile to wall below this rover
|
// Explicity set a few nearby tiles
|
||||||
wallPos := game.Vector{X: 0, Y: -1}
|
wallPos1 := game.Vector{X: 0, Y: -1}
|
||||||
assert.NoError(t, s.world.Atlas.SetTile(wallPos, game.TileWall))
|
wallPos2 := game.Vector{X: 1, Y: 1}
|
||||||
|
rockPos := game.Vector{X: 1, Y: 3}
|
||||||
|
emptyPos := game.Vector{X: -2, Y: -3}
|
||||||
|
assert.NoError(t, s.world.Atlas.SetTile(wallPos1, game.TileWall))
|
||||||
|
assert.NoError(t, s.world.Atlas.SetTile(wallPos2, game.TileWall))
|
||||||
|
assert.NoError(t, s.world.Atlas.SetTile(rockPos, game.TileRock))
|
||||||
|
assert.NoError(t, s.world.Atlas.SetTile(emptyPos, game.TileEmpty))
|
||||||
|
|
||||||
request, _ := http.NewRequest(http.MethodGet, path.Join("/", a.Id.String(), "/radar"), nil)
|
request, _ := http.NewRequest(http.MethodGet, path.Join("/", a.Id.String(), "/radar"), nil)
|
||||||
response := httptest.NewRecorder()
|
response := httptest.NewRecorder()
|
||||||
|
@ -163,7 +169,22 @@ func TestHandleRadar(t *testing.T) {
|
||||||
t.Errorf("got false for /radar: %s", status.Error)
|
t.Errorf("got false for /radar: %s", status.Error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scope := attrib.Range*2 + 1
|
||||||
|
radarOrigin := game.Vector{X: -attrib.Range, Y: -attrib.Range}
|
||||||
|
|
||||||
|
// Make sure the rover tile is correct
|
||||||
assert.Equal(t, game.TileRover, status.Tiles[len(status.Tiles)/2])
|
assert.Equal(t, game.TileRover, status.Tiles[len(status.Tiles)/2])
|
||||||
|
|
||||||
|
// Check our other tiles
|
||||||
|
wallPos1.Add(radarOrigin.Negated())
|
||||||
|
wallPos2.Add(radarOrigin.Negated())
|
||||||
|
rockPos.Add(radarOrigin.Negated())
|
||||||
|
emptyPos.Add(radarOrigin.Negated())
|
||||||
|
assert.Equal(t, game.TileWall, status.Tiles[wallPos1.X+wallPos1.Y*scope])
|
||||||
|
assert.Equal(t, game.TileWall, status.Tiles[wallPos2.X+wallPos2.Y*scope])
|
||||||
|
assert.Equal(t, game.TileRock, status.Tiles[rockPos.X+rockPos.Y*scope])
|
||||||
|
assert.Equal(t, game.TileEmpty, status.Tiles[emptyPos.X+emptyPos.Y*scope])
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHandleRover(t *testing.T) {
|
func TestHandleRover(t *testing.T) {
|
||||||
|
|
|
@ -98,7 +98,7 @@ func NewServer(opts ...ServerOption) *Server {
|
||||||
|
|
||||||
// Create the accountant
|
// Create the accountant
|
||||||
s.accountant = accounts.NewAccountant()
|
s.accountant = accounts.NewAccountant()
|
||||||
s.world = game.NewWorld()
|
s.world = game.NewWorld(4, 8) // TODO: Configure this
|
||||||
|
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue