Refactor radar to return a full set of the tiles within rover range
This commit is contained in:
parent
4e6bf385ad
commit
b0e076ac1a
7 changed files with 74 additions and 77 deletions
|
@ -5,6 +5,7 @@ import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"math"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
|
@ -173,9 +174,14 @@ 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 {
|
||||||
fmt.Printf("radar blips: %+v\n", response.Blips)
|
// Print the radar
|
||||||
|
num := int(math.Sqrt(float64(len(response.Tiles))))
|
||||||
// TODO: Do some art
|
for i := 0; i < num; i++ {
|
||||||
|
for j := num - 1; j >= 0; j-- {
|
||||||
|
fmt.Printf("%d", response.Tiles[i+num*j])
|
||||||
|
}
|
||||||
|
fmt.Print("\n")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case "rover":
|
case "rover":
|
||||||
|
|
|
@ -144,9 +144,17 @@ func (a *Atlas) ChunkOrigin(chunk int) Vector {
|
||||||
return v.Multiplied(a.ChunkSize)
|
return v.Multiplied(a.ChunkSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetWorldExtent gets the extent of the world
|
// GetWorldExtent gets the min and max valid coordinates of world
|
||||||
func (a *Atlas) GetWorldExtent() int {
|
func (a *Atlas) GetWorldExtents() (min Vector, max Vector) {
|
||||||
return (a.Size / 2) * a.ChunkSize
|
min = Vector{
|
||||||
|
-(a.Size / 2) * a.ChunkSize,
|
||||||
|
-(a.Size / 2) * a.ChunkSize,
|
||||||
|
}
|
||||||
|
max = Vector{
|
||||||
|
-min.X - 1,
|
||||||
|
-min.Y - 1,
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Grow will return a grown copy of the current atlas
|
// Grow will return a grown copy of the current atlas
|
||||||
|
|
|
@ -128,6 +128,7 @@ func (w *World) WarpRover(id uuid.UUID, pos Vector) error {
|
||||||
if i, ok := w.Rovers[id]; ok {
|
if i, ok := w.Rovers[id]; ok {
|
||||||
// Update the world tile
|
// Update the world tile
|
||||||
// TODO: Make this (and other things) transactional
|
// TODO: Make this (and other things) transactional
|
||||||
|
// TODO: Check this worldtile is free
|
||||||
if err := w.Atlas.SetTile(pos, TileRover); err != nil {
|
if err := w.Atlas.SetTile(pos, TileRover); err != nil {
|
||||||
return fmt.Errorf("coudln't set rover tile: %s", err)
|
return fmt.Errorf("coudln't set rover tile: %s", err)
|
||||||
} else if err := w.Atlas.SetTile(i.Attributes.Pos, TileEmpty); err != nil {
|
} else if err := w.Atlas.SetTile(i.Attributes.Pos, TileEmpty); err != nil {
|
||||||
|
@ -180,52 +181,55 @@ func (w *World) MoveRover(id uuid.UUID, bearing Direction) (RoverAttributes, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// RadarBlip represents a single blip on the radar
|
|
||||||
type RadarBlip struct {
|
|
||||||
Position Vector `json:"position"`
|
|
||||||
Tile Tile `json:"tile"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// RadarFromRover can be used to query what a rover can currently see
|
// RadarFromRover can be used to query what a rover can currently see
|
||||||
func (w *World) RadarFromRover(id uuid.UUID) ([]RadarBlip, error) {
|
func (w *World) RadarFromRover(id uuid.UUID) ([]Tile, error) {
|
||||||
w.worldMutex.RLock()
|
w.worldMutex.RLock()
|
||||||
defer w.worldMutex.RUnlock()
|
defer w.worldMutex.RUnlock()
|
||||||
|
|
||||||
if r, ok := w.Rovers[id]; ok {
|
if r, ok := w.Rovers[id]; ok {
|
||||||
var blips []RadarBlip
|
// The radar should span in range direction on each axis, plus the row/column the rover is currently on
|
||||||
|
radarSpan := (r.Attributes.Range * 2) + 1
|
||||||
|
roverPos := r.Attributes.Pos
|
||||||
|
|
||||||
extent := w.Atlas.GetWorldExtent()
|
// Get the radar min and max values
|
||||||
|
radarMin := Vector{
|
||||||
// Get min and max extents to query
|
X: roverPos.X - r.Attributes.Range,
|
||||||
min := Vector{
|
Y: roverPos.Y - r.Attributes.Range,
|
||||||
Max(-extent, r.Attributes.Pos.X-r.Attributes.Range),
|
|
||||||
Max(-extent, r.Attributes.Pos.Y-r.Attributes.Range),
|
|
||||||
}
|
}
|
||||||
max := Vector{
|
radarMax := Vector{
|
||||||
Min(extent-1, r.Attributes.Pos.X+r.Attributes.Range),
|
X: roverPos.X + r.Attributes.Range,
|
||||||
Min(extent-1, r.Attributes.Pos.Y+r.Attributes.Range),
|
Y: roverPos.Y + r.Attributes.Range,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure we only query within the actual world
|
||||||
|
worldMin, worldMax := w.Atlas.GetWorldExtents()
|
||||||
|
scanMin := Vector{
|
||||||
|
X: Max(radarMin.X, worldMin.X),
|
||||||
|
Y: Max(radarMin.Y, worldMin.Y),
|
||||||
|
}
|
||||||
|
scanMax := Vector{
|
||||||
|
X: Min(radarMax.X, worldMax.X),
|
||||||
|
Y: Min(radarMax.Y, worldMax.Y),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gather up all tiles within the range
|
// Gather up all tiles within the range
|
||||||
for i := min.X; i < max.X; i++ {
|
var radar = make([]Tile, radarSpan*radarSpan)
|
||||||
for j := min.Y; j < max.Y; j++ {
|
for i := scanMin.X; i < scanMax.X; i++ {
|
||||||
|
for j := scanMin.Y; j < scanMax.Y; j++ {
|
||||||
// Skip this rover
|
|
||||||
q := Vector{i, j}
|
q := Vector{i, j}
|
||||||
if q == r.Attributes.Pos {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if tile, err := w.Atlas.GetTile(q); err != nil {
|
if tile, err := w.Atlas.GetTile(q); err != nil {
|
||||||
return blips, fmt.Errorf("failed to query tile: %s", err)
|
return nil, fmt.Errorf("failed to query tile: %s", err)
|
||||||
|
|
||||||
} else if tile != TileEmpty {
|
} else {
|
||||||
blips = append(blips, RadarBlip{Position: q, Tile: tile})
|
// Get the position relative to the bottom left of the radar
|
||||||
|
relative := q.Added(radarMin.Negated())
|
||||||
|
radar[relative.X+relative.Y*radarSpan] = tile
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return blips, nil
|
return radar, nil
|
||||||
} else {
|
} else {
|
||||||
return nil, fmt.Errorf("no rover matching id")
|
return nil, fmt.Errorf("no rover matching id")
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,30 +96,24 @@ func TestWorld_RadarFromRover(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
b, err := world.SpawnRover()
|
b, err := world.SpawnRover()
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
c, err := world.SpawnRover()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
// Get a's attributes
|
// Get a's attributes
|
||||||
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")
|
||||||
|
|
||||||
// Warp the rovers so a can see b but not c
|
// Warp the rovers so a can see b
|
||||||
bpos := Vector{attrib.Range - 1, 0}
|
bpos := Vector{-attrib.Range, -attrib.Range}
|
||||||
cpos := Vector{attrib.Range + 1, 0}
|
|
||||||
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")
|
||||||
assert.NoError(t, world.WarpRover(c, cpos), "Failed to warp rover")
|
|
||||||
|
|
||||||
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")
|
||||||
assert.Equal(t, 1, len(radar), "Radar returned wrong number of rovers")
|
fullRange := attrib.Range + attrib.Range + 1
|
||||||
|
assert.Equal(t, fullRange*fullRange, len(radar), "Radar returned wrong number of rovers")
|
||||||
|
|
||||||
found := false
|
// bottom left should be a rover (we put one there with bpos)
|
||||||
for _, blip := range radar {
|
assert.Equal(t, radar[0], TileRover, "Rover not found on radar in expected position")
|
||||||
if blip.Position == bpos && blip.Tile == TileRover {
|
// Centre should be rover
|
||||||
found = true
|
assert.Equal(t, radar[fullRange*fullRange/2], TileRover, "Rover not found on radar in expected position")
|
||||||
}
|
|
||||||
}
|
|
||||||
assert.True(t, found, "Rover not found on radar in expected position")
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,7 +107,8 @@ type RadarResponse struct {
|
||||||
Error string `json:"error,omitempty"`
|
Error string `json:"error,omitempty"`
|
||||||
|
|
||||||
// The set of positions for nearby non-empty tiles
|
// The set of positions for nearby non-empty tiles
|
||||||
Blips []game.RadarBlip `json:"blips"`
|
Range int `json:"range"`
|
||||||
|
Tiles []game.Tile `json:"tiles"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ================
|
// ================
|
||||||
|
|
|
@ -97,12 +97,11 @@ func HandleRegister(s *Server, vars map[string]string, b io.ReadCloser, w io.Wri
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Save out the new accounts
|
// Save out the new accounts
|
||||||
fmt.Printf("New account registered\tname:%s\tid:%s\n", acc.Name, acc.Id)
|
|
||||||
|
|
||||||
response.Id = acc.Id.String()
|
response.Id = acc.Id.String()
|
||||||
response.Success = true
|
response.Success = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fmt.Printf("register response:%+v\n", response)
|
||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,19 +125,18 @@ func HandleSpawn(s *Server, vars map[string]string, b io.ReadCloser, w io.Writer
|
||||||
} else if id, err := uuid.Parse(id); err != nil {
|
} else if id, err := uuid.Parse(id); err != nil {
|
||||||
response.Error = "Provided account ID was invalid"
|
response.Error = "Provided account ID was invalid"
|
||||||
|
|
||||||
} else if attribs, rover, err := s.SpawnRoverForAccount(id); err != nil {
|
} else if attribs, _, err := s.SpawnRoverForAccount(id); err != nil {
|
||||||
response.Error = err.Error()
|
response.Error = err.Error()
|
||||||
|
|
||||||
} else if err := s.SaveWorld(); err != nil {
|
} else if err := s.SaveWorld(); err != nil {
|
||||||
response.Error = fmt.Sprintf("Internal server error when saving world: %s", err)
|
response.Error = fmt.Sprintf("Internal server error when saving world: %s", err)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("New rover spawned\taccount:%s\trover:%s\tattributes:%+v\n", id, rover, attribs)
|
|
||||||
|
|
||||||
response.Success = true
|
response.Success = true
|
||||||
response.Attributes = attribs
|
response.Attributes = attribs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fmt.Printf("spawn response \taccount:%s\tresponse:%+v\n", id, response)
|
||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,10 +167,10 @@ func HandleCommand(s *Server, vars map[string]string, b io.ReadCloser, w io.Writ
|
||||||
response.Error = fmt.Sprintf("Failed to execute commands: %s", err)
|
response.Error = fmt.Sprintf("Failed to execute commands: %s", err)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("Queued commands\taccount:%s\tcommands:%+v\n", id, data.Commands)
|
|
||||||
response.Success = true
|
response.Success = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fmt.Printf("command response \taccount:%s\tresponse:%+v\n", id, response)
|
||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,15 +190,19 @@ func HandleRadar(s *Server, vars map[string]string, b io.ReadCloser, w io.Writer
|
||||||
} else if inst, err := s.accountant.GetRover(id); err != nil {
|
} else if inst, err := s.accountant.GetRover(id); err != nil {
|
||||||
response.Error = fmt.Sprintf("Provided account has no rover: %s", err)
|
response.Error = fmt.Sprintf("Provided account has no rover: %s", err)
|
||||||
|
|
||||||
|
} else if attrib, err := s.world.RoverAttributes(inst); err != nil {
|
||||||
|
response.Error = fmt.Sprintf("Error getting rover attributes: %s", err)
|
||||||
|
|
||||||
} else if radar, err := s.world.RadarFromRover(inst); err != nil {
|
} else if radar, err := s.world.RadarFromRover(inst); err != nil {
|
||||||
response.Error = fmt.Sprintf("Error getting radar from rover: %s", err)
|
response.Error = fmt.Sprintf("Error getting radar from rover: %s", err)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("Responded with radar\taccount:%s\tradar:%+v\n", id, radar)
|
response.Tiles = radar
|
||||||
response.Blips = radar
|
response.Range = attrib.Range
|
||||||
response.Success = true
|
response.Success = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fmt.Printf("radar response \taccount:%s\tresponse:%+v\n", id, response)
|
||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,10 +226,10 @@ func HandleRover(s *Server, vars map[string]string, b io.ReadCloser, w io.Writer
|
||||||
response.Error = fmt.Sprintf("Error getting radar from rover: %s", err)
|
response.Error = fmt.Sprintf("Error getting radar from rover: %s", err)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("Responded with rover\taccount:%s\trover:%+v\n", id, attribs)
|
|
||||||
response.Attributes = attribs
|
response.Attributes = attribs
|
||||||
response.Success = true
|
response.Success = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fmt.Printf("rover response \taccount:%s\tresponse:%+v\n", id, response)
|
||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -146,13 +146,6 @@ func TestHandleRadar(t *testing.T) {
|
||||||
// Warp this rover to 0
|
// Warp this rover to 0
|
||||||
assert.NoError(t, s.world.WarpRover(id, game.Vector{}))
|
assert.NoError(t, s.world.WarpRover(id, game.Vector{}))
|
||||||
|
|
||||||
// Spawn another rover
|
|
||||||
id, err = s.world.SpawnRover()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
// Warp this rover to just above the other one
|
|
||||||
roverPos := game.Vector{X: 0, Y: 1}
|
|
||||||
assert.NoError(t, s.world.WarpRover(id, roverPos))
|
|
||||||
|
|
||||||
// Set a tile to wall below this rover
|
// Set a tile to wall below this rover
|
||||||
wallPos := game.Vector{X: 0, Y: -1}
|
wallPos := game.Vector{X: 0, Y: -1}
|
||||||
assert.NoError(t, s.world.Atlas.SetTile(wallPos, game.TileWall))
|
assert.NoError(t, s.world.Atlas.SetTile(wallPos, game.TileWall))
|
||||||
|
@ -170,18 +163,7 @@ func TestHandleRadar(t *testing.T) {
|
||||||
t.Errorf("got false for /radar: %s", status.Error)
|
t.Errorf("got false for /radar: %s", status.Error)
|
||||||
}
|
}
|
||||||
|
|
||||||
foundWall := false
|
assert.Equal(t, game.TileRover, status.Tiles[len(status.Tiles)/2])
|
||||||
foundRover := false
|
|
||||||
for _, b := range status.Blips {
|
|
||||||
if b.Position == wallPos && b.Tile == game.TileWall {
|
|
||||||
foundWall = true
|
|
||||||
} else if b.Position == roverPos && b.Tile == game.TileRover {
|
|
||||||
foundRover = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert.True(t, foundWall)
|
|
||||||
assert.True(t, foundRover)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHandleRover(t *testing.T) {
|
func TestHandleRover(t *testing.T) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue