Split out maths functions into maths, vector and bearing
This commit is contained in:
parent
aae668fb57
commit
51fe918090
14 changed files with 335 additions and 273 deletions
76
pkg/bearing/bearing.go
Normal file
76
pkg/bearing/bearing.go
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
package bearing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/mdiluz/rove/pkg/vector"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Direction describes a compass direction
|
||||||
|
type Direction int
|
||||||
|
|
||||||
|
const (
|
||||||
|
North Direction = iota
|
||||||
|
NorthEast
|
||||||
|
East
|
||||||
|
SouthEast
|
||||||
|
South
|
||||||
|
SouthWest
|
||||||
|
West
|
||||||
|
NorthWest
|
||||||
|
)
|
||||||
|
|
||||||
|
// DirectionString simply describes the strings associated with a direction
|
||||||
|
type DirectionString struct {
|
||||||
|
Long string
|
||||||
|
Short string
|
||||||
|
}
|
||||||
|
|
||||||
|
// DirectionStrings is the set of strings for each direction
|
||||||
|
var DirectionStrings = []DirectionString{
|
||||||
|
{"North", "N"},
|
||||||
|
{"NorthEast", "NE"},
|
||||||
|
{"East", "E"},
|
||||||
|
{"SouthEast", "SE"},
|
||||||
|
{"South", "S"},
|
||||||
|
{"SouthWest", "SW"},
|
||||||
|
{"West", "W"},
|
||||||
|
{"NorthWest", "NW"},
|
||||||
|
}
|
||||||
|
|
||||||
|
// String converts a Direction to a String
|
||||||
|
func (d Direction) String() string {
|
||||||
|
return DirectionStrings[d].Long
|
||||||
|
}
|
||||||
|
|
||||||
|
// ShortString converts a Direction to a short string version
|
||||||
|
func (d Direction) ShortString() string {
|
||||||
|
return DirectionStrings[d].Short
|
||||||
|
}
|
||||||
|
|
||||||
|
// DirectionFromString gets the Direction from a string
|
||||||
|
func DirectionFromString(s string) (Direction, error) {
|
||||||
|
for i, d := range DirectionStrings {
|
||||||
|
if strings.ToLower(d.Long) == strings.ToLower(s) || strings.ToLower(d.Short) == strings.ToLower(s) {
|
||||||
|
return Direction(i), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1, fmt.Errorf("Unknown direction: %s", s)
|
||||||
|
}
|
||||||
|
|
||||||
|
var DirectionVectors = []vector.Vector{
|
||||||
|
{X: 0, Y: 1}, // N
|
||||||
|
{X: 1, Y: 1}, // NE
|
||||||
|
{X: 1, Y: 0}, // E
|
||||||
|
{X: 1, Y: -1}, // SE
|
||||||
|
{X: 0, Y: -1}, // S
|
||||||
|
{X: -1, Y: 1}, // SW
|
||||||
|
{X: -1, Y: 0}, // W
|
||||||
|
{X: -1, Y: 1}, // NW
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vector converts a Direction to a Vector
|
||||||
|
func (d Direction) Vector() vector.Vector {
|
||||||
|
return DirectionVectors[d]
|
||||||
|
}
|
32
pkg/bearing/bearing_test.go
Normal file
32
pkg/bearing/bearing_test.go
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
package bearing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/mdiluz/rove/pkg/vector"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDirection(t *testing.T) {
|
||||||
|
dir := North
|
||||||
|
|
||||||
|
assert.Equal(t, "North", dir.String())
|
||||||
|
assert.Equal(t, "N", dir.ShortString())
|
||||||
|
assert.Equal(t, vector.Vector{X: 0, Y: 1}, dir.Vector())
|
||||||
|
|
||||||
|
dir, err := DirectionFromString("N")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, North, dir)
|
||||||
|
|
||||||
|
dir, err = DirectionFromString("n")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, North, dir)
|
||||||
|
|
||||||
|
dir, err = DirectionFromString("north")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, North, dir)
|
||||||
|
|
||||||
|
dir, err = DirectionFromString("NorthWest")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, NorthWest, dir)
|
||||||
|
}
|
|
@ -4,6 +4,9 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
|
||||||
|
"github.com/mdiluz/rove/pkg/maths"
|
||||||
|
"github.com/mdiluz/rove/pkg/vector"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Chunk represents a fixed square grid of tiles
|
// Chunk represents a fixed square grid of tiles
|
||||||
|
@ -55,7 +58,7 @@ func (a *Atlas) SpawnRocks() error {
|
||||||
for i := -extent; i < extent; i++ {
|
for i := -extent; i < extent; i++ {
|
||||||
for j := -extent; j < extent; j++ {
|
for j := -extent; j < extent; j++ {
|
||||||
if rand.Intn(16) == 0 {
|
if rand.Intn(16) == 0 {
|
||||||
if err := a.SetTile(Vector{i, j}, TileRock); err != nil {
|
if err := a.SetTile(vector.Vector{X: i, Y: j}, TileRock); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -72,13 +75,13 @@ func (a *Atlas) SpawnWalls() error {
|
||||||
// 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 { // N
|
if err := a.SetTile(vector.Vector{X: i, Y: extent - 1}, TileWall); err != nil { // N
|
||||||
return err
|
return err
|
||||||
} else if a.SetTile(Vector{extent - 1, i}, TileWall); err != nil { // E
|
} else if a.SetTile(vector.Vector{X: extent - 1, Y: i}, TileWall); err != nil { // E
|
||||||
return err
|
return err
|
||||||
} else if a.SetTile(Vector{i, -extent}, TileWall); err != nil { // S
|
} else if a.SetTile(vector.Vector{X: i, Y: -extent}, TileWall); err != nil { // S
|
||||||
return err
|
return err
|
||||||
} else if a.SetTile(Vector{-extent, i}, TileWall); err != nil { // W
|
} else if a.SetTile(vector.Vector{X: -extent, Y: i}, TileWall); err != nil { // W
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,7 +90,7 @@ func (a *Atlas) SpawnWalls() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetTile sets an individual tile's kind
|
// SetTile sets an individual tile's kind
|
||||||
func (a *Atlas) SetTile(v Vector, tile Tile) error {
|
func (a *Atlas) SetTile(v vector.Vector, tile Tile) error {
|
||||||
chunk := a.ToChunk(v)
|
chunk := a.ToChunk(v)
|
||||||
if chunk >= len(a.Chunks) {
|
if chunk >= len(a.Chunks) {
|
||||||
return fmt.Errorf("location outside of allocated atlas")
|
return fmt.Errorf("location outside of allocated atlas")
|
||||||
|
@ -103,7 +106,7 @@ func (a *Atlas) SetTile(v Vector, tile Tile) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTile will return an individual tile
|
// GetTile will return an individual tile
|
||||||
func (a *Atlas) GetTile(v Vector) (Tile, error) {
|
func (a *Atlas) GetTile(v vector.Vector) (Tile, error) {
|
||||||
chunk := a.ToChunk(v)
|
chunk := a.ToChunk(v)
|
||||||
if chunk >= len(a.Chunks) {
|
if chunk >= len(a.Chunks) {
|
||||||
return 0, fmt.Errorf("location outside of allocated atlas")
|
return 0, fmt.Errorf("location outside of allocated atlas")
|
||||||
|
@ -119,32 +122,32 @@ func (a *Atlas) GetTile(v Vector) (Tile, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToChunkLocal gets a chunk local coordinate for a tile
|
// ToChunkLocal gets a chunk local coordinate for a tile
|
||||||
func (a *Atlas) ToChunkLocal(v Vector) Vector {
|
func (a *Atlas) ToChunkLocal(v vector.Vector) vector.Vector {
|
||||||
return Vector{Pmod(v.X, a.ChunkSize), Pmod(v.Y, a.ChunkSize)}
|
return vector.Vector{X: maths.Pmod(v.X, a.ChunkSize), Y: maths.Pmod(v.Y, a.ChunkSize)}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetChunkLocal gets a chunk local coordinate for a tile
|
// GetChunkLocal gets a chunk local coordinate for a tile
|
||||||
func (a *Atlas) ToWorld(local Vector, chunk int) Vector {
|
func (a *Atlas) ToWorld(local vector.Vector, chunk int) vector.Vector {
|
||||||
return a.ChunkOrigin(chunk).Added(local)
|
return a.ChunkOrigin(chunk).Added(local)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetChunkID gets the chunk ID for a position in the world
|
// GetChunkID gets the chunk ID for a position in the world
|
||||||
func (a *Atlas) ToChunk(v Vector) int {
|
func (a *Atlas) ToChunk(v vector.Vector) int {
|
||||||
local := a.ToChunkLocal(v)
|
local := a.ToChunkLocal(v)
|
||||||
// Get the chunk origin itself
|
// Get the chunk origin itself
|
||||||
origin := v.Added(local.Negated())
|
origin := v.Added(local.Negated())
|
||||||
// Divided it by the number of chunks
|
// Divided it by the number of chunks
|
||||||
origin = origin.Divided(a.ChunkSize)
|
origin = origin.Divided(a.ChunkSize)
|
||||||
// Shift it by our size (our origin is in the middle)
|
// Shift it by our size (our origin is in the middle)
|
||||||
origin = origin.Added(Vector{a.Size / 2, a.Size / 2})
|
origin = origin.Added(vector.Vector{X: a.Size / 2, Y: a.Size / 2})
|
||||||
// Get the ID based on the final values
|
// Get the ID based on the final values
|
||||||
return (a.Size * origin.Y) + origin.X
|
return (a.Size * origin.Y) + origin.X
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChunkOrigin gets the chunk origin for a given chunk index
|
// ChunkOrigin gets the chunk origin for a given chunk index
|
||||||
func (a *Atlas) ChunkOrigin(chunk int) Vector {
|
func (a *Atlas) ChunkOrigin(chunk int) vector.Vector {
|
||||||
v := Vector{
|
v := vector.Vector{
|
||||||
X: Pmod(chunk, a.Size) - (a.Size / 2),
|
X: maths.Pmod(chunk, a.Size) - (a.Size / 2),
|
||||||
Y: (chunk / a.Size) - (a.Size / 2),
|
Y: (chunk / a.Size) - (a.Size / 2),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,14 +155,14 @@ func (a *Atlas) ChunkOrigin(chunk int) Vector {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetWorldExtent gets the min and max valid coordinates of world
|
// GetWorldExtent gets the min and max valid coordinates of world
|
||||||
func (a *Atlas) GetWorldExtents() (min Vector, max Vector) {
|
func (a *Atlas) GetWorldExtents() (min vector.Vector, max vector.Vector) {
|
||||||
min = Vector{
|
min = vector.Vector{
|
||||||
-(a.Size / 2) * a.ChunkSize,
|
X: -(a.Size / 2) * a.ChunkSize,
|
||||||
-(a.Size / 2) * a.ChunkSize,
|
Y: -(a.Size / 2) * a.ChunkSize,
|
||||||
}
|
}
|
||||||
max = Vector{
|
max = vector.Vector{
|
||||||
-min.X - 1,
|
X: -min.X - 1,
|
||||||
-min.Y - 1,
|
Y: -min.Y - 1,
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package game
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/mdiluz/rove/pkg/vector"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -28,13 +29,13 @@ func TestAtlas_ToChunk(t *testing.T) {
|
||||||
// Tiles should look like: 2 | 3
|
// Tiles should look like: 2 | 3
|
||||||
// -----
|
// -----
|
||||||
// 0 | 1
|
// 0 | 1
|
||||||
tile := a.ToChunk(Vector{0, 0})
|
tile := a.ToChunk(vector.Vector{X: 0, Y: 0})
|
||||||
assert.Equal(t, 3, tile)
|
assert.Equal(t, 3, tile)
|
||||||
tile = a.ToChunk(Vector{0, -1})
|
tile = a.ToChunk(vector.Vector{X: 0, Y: -1})
|
||||||
assert.Equal(t, 1, tile)
|
assert.Equal(t, 1, tile)
|
||||||
tile = a.ToChunk(Vector{-1, -1})
|
tile = a.ToChunk(vector.Vector{X: -1, Y: -1})
|
||||||
assert.Equal(t, 0, tile)
|
assert.Equal(t, 0, tile)
|
||||||
tile = a.ToChunk(Vector{-1, 0})
|
tile = a.ToChunk(vector.Vector{X: -1, Y: 0})
|
||||||
assert.Equal(t, 2, tile)
|
assert.Equal(t, 2, tile)
|
||||||
|
|
||||||
a = NewAtlas(2, 2)
|
a = NewAtlas(2, 2)
|
||||||
|
@ -43,13 +44,13 @@ func TestAtlas_ToChunk(t *testing.T) {
|
||||||
// 2 | 3
|
// 2 | 3
|
||||||
// -----
|
// -----
|
||||||
// 0 | 1
|
// 0 | 1
|
||||||
tile = a.ToChunk(Vector{1, 1})
|
tile = a.ToChunk(vector.Vector{X: 1, Y: 1})
|
||||||
assert.Equal(t, 3, tile)
|
assert.Equal(t, 3, tile)
|
||||||
tile = a.ToChunk(Vector{1, -2})
|
tile = a.ToChunk(vector.Vector{X: 1, Y: -2})
|
||||||
assert.Equal(t, 1, tile)
|
assert.Equal(t, 1, tile)
|
||||||
tile = a.ToChunk(Vector{-2, -2})
|
tile = a.ToChunk(vector.Vector{X: -2, Y: -2})
|
||||||
assert.Equal(t, 0, tile)
|
assert.Equal(t, 0, tile)
|
||||||
tile = a.ToChunk(Vector{-2, 1})
|
tile = a.ToChunk(vector.Vector{X: -2, Y: 1})
|
||||||
assert.Equal(t, 2, tile)
|
assert.Equal(t, 2, tile)
|
||||||
|
|
||||||
a = NewAtlas(4, 2)
|
a = NewAtlas(4, 2)
|
||||||
|
@ -62,13 +63,13 @@ func TestAtlas_ToChunk(t *testing.T) {
|
||||||
// 4 | 5 || 6 | 7
|
// 4 | 5 || 6 | 7
|
||||||
// ----------------
|
// ----------------
|
||||||
// 0 | 1 || 2 | 3
|
// 0 | 1 || 2 | 3
|
||||||
tile = a.ToChunk(Vector{1, 3})
|
tile = a.ToChunk(vector.Vector{X: 1, Y: 3})
|
||||||
assert.Equal(t, 14, tile)
|
assert.Equal(t, 14, tile)
|
||||||
tile = a.ToChunk(Vector{1, -3})
|
tile = a.ToChunk(vector.Vector{X: 1, Y: -3})
|
||||||
assert.Equal(t, 2, tile)
|
assert.Equal(t, 2, tile)
|
||||||
tile = a.ToChunk(Vector{-1, -1})
|
tile = a.ToChunk(vector.Vector{X: -1, Y: -1})
|
||||||
assert.Equal(t, 5, tile)
|
assert.Equal(t, 5, tile)
|
||||||
tile = a.ToChunk(Vector{-2, 2})
|
tile = a.ToChunk(vector.Vector{X: -2, Y: 2})
|
||||||
assert.Equal(t, 13, tile)
|
assert.Equal(t, 13, tile)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,14 +78,14 @@ func TestAtlas_GetSetTile(t *testing.T) {
|
||||||
assert.NotNil(t, a)
|
assert.NotNil(t, a)
|
||||||
|
|
||||||
// Set the origin tile to 1 and test it
|
// Set the origin tile to 1 and test it
|
||||||
assert.NoError(t, a.SetTile(Vector{0, 0}, 1))
|
assert.NoError(t, a.SetTile(vector.Vector{X: 0, Y: 0}, 1))
|
||||||
tile, err := a.GetTile(Vector{0, 0})
|
tile, err := a.GetTile(vector.Vector{X: 0, Y: 0})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, Tile(1), tile)
|
assert.Equal(t, Tile(1), tile)
|
||||||
|
|
||||||
// Set another tile to 1 and test it
|
// Set another tile to 1 and test it
|
||||||
assert.NoError(t, a.SetTile(Vector{5, -2}, 2))
|
assert.NoError(t, a.SetTile(vector.Vector{X: 5, Y: -2}, 2))
|
||||||
tile, err = a.GetTile(Vector{5, -2})
|
tile, err = a.GetTile(vector.Vector{X: 5, Y: -2})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, Tile(2), tile)
|
assert.Equal(t, Tile(2), tile)
|
||||||
}
|
}
|
||||||
|
@ -96,24 +97,24 @@ func TestAtlas_Grown(t *testing.T) {
|
||||||
assert.Equal(t, 4, len(a.Chunks))
|
assert.Equal(t, 4, len(a.Chunks))
|
||||||
|
|
||||||
// Set a few tiles to values
|
// Set a few tiles to values
|
||||||
assert.NoError(t, a.SetTile(Vector{0, 0}, 1))
|
assert.NoError(t, a.SetTile(vector.Vector{X: 0, Y: 0}, 1))
|
||||||
assert.NoError(t, a.SetTile(Vector{-1, -1}, 2))
|
assert.NoError(t, a.SetTile(vector.Vector{X: -1, Y: -1}, 2))
|
||||||
assert.NoError(t, a.SetTile(Vector{1, -2}, 3))
|
assert.NoError(t, a.SetTile(vector.Vector{X: 1, Y: -2}, 3))
|
||||||
|
|
||||||
// Grow once to just double it
|
// Grow once to just double it
|
||||||
err := a.Grow(4)
|
err := a.Grow(4)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, 16, len(a.Chunks))
|
assert.Equal(t, 16, len(a.Chunks))
|
||||||
|
|
||||||
tile, err := a.GetTile(Vector{0, 0})
|
tile, err := a.GetTile(vector.Vector{X: 0, Y: 0})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, Tile(1), tile)
|
assert.Equal(t, Tile(1), tile)
|
||||||
|
|
||||||
tile, err = a.GetTile(Vector{-1, -1})
|
tile, err = a.GetTile(vector.Vector{X: -1, Y: -1})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, Tile(2), tile)
|
assert.Equal(t, Tile(2), tile)
|
||||||
|
|
||||||
tile, err = a.GetTile(Vector{1, -2})
|
tile, err = a.GetTile(vector.Vector{X: 1, Y: -2})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, Tile(3), tile)
|
assert.Equal(t, Tile(3), tile)
|
||||||
|
|
||||||
|
@ -122,15 +123,15 @@ func TestAtlas_Grown(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, 100, len(a.Chunks))
|
assert.Equal(t, 100, len(a.Chunks))
|
||||||
|
|
||||||
tile, err = a.GetTile(Vector{0, 0})
|
tile, err = a.GetTile(vector.Vector{X: 0, Y: 0})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, Tile(1), tile)
|
assert.Equal(t, Tile(1), tile)
|
||||||
|
|
||||||
tile, err = a.GetTile(Vector{-1, -1})
|
tile, err = a.GetTile(vector.Vector{X: -1, Y: -1})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, Tile(2), tile)
|
assert.Equal(t, Tile(2), tile)
|
||||||
|
|
||||||
tile, err = a.GetTile(Vector{1, -2})
|
tile, err = a.GetTile(vector.Vector{X: 1, Y: -2})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, Tile(3), tile)
|
assert.Equal(t, Tile(3), tile)
|
||||||
}
|
}
|
||||||
|
@ -143,25 +144,25 @@ func TestAtlas_SpawnWorld(t *testing.T) {
|
||||||
assert.NoError(t, a.SpawnWalls())
|
assert.NoError(t, a.SpawnWalls())
|
||||||
|
|
||||||
for i := -4; i < 4; i++ {
|
for i := -4; i < 4; i++ {
|
||||||
tile, err := a.GetTile(Vector{i, -4})
|
tile, err := a.GetTile(vector.Vector{X: i, Y: -4})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, TileWall, tile)
|
assert.Equal(t, TileWall, tile)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := -4; i < 4; i++ {
|
for i := -4; i < 4; i++ {
|
||||||
tile, err := a.GetTile(Vector{-4, i})
|
tile, err := a.GetTile(vector.Vector{X: -4, Y: 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++ {
|
for i := -4; i < 4; i++ {
|
||||||
tile, err := a.GetTile(Vector{3, i})
|
tile, err := a.GetTile(vector.Vector{X: 3, Y: 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++ {
|
for i := -4; i < 4; i++ {
|
||||||
tile, err := a.GetTile(Vector{i, 3})
|
tile, err := a.GetTile(vector.Vector{X: i, Y: 3})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, TileWall, tile)
|
assert.Equal(t, TileWall, tile)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,8 @@ package game
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/mdiluz/rove/pkg/bearing"
|
||||||
|
"github.com/mdiluz/rove/pkg/vector"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -10,7 +12,7 @@ func TestCommand_Move(t *testing.T) {
|
||||||
world := NewWorld(2, 8)
|
world := NewWorld(2, 8)
|
||||||
a, err := world.SpawnRover()
|
a, err := world.SpawnRover()
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
pos := Vector{
|
pos := vector.Vector{
|
||||||
X: 1.0,
|
X: 1.0,
|
||||||
Y: 2.0,
|
Y: 2.0,
|
||||||
}
|
}
|
||||||
|
@ -21,7 +23,7 @@ func TestCommand_Move(t *testing.T) {
|
||||||
err = world.WarpRover(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")
|
||||||
|
|
||||||
bearing := North
|
bearing := bearing.North
|
||||||
duration := 1
|
duration := 1
|
||||||
// Try the move command
|
// Try the move command
|
||||||
moveCommand := Command{Command: CommandMove, Bearing: bearing.String(), Duration: duration}
|
moveCommand := Command{Command: CommandMove, Bearing: bearing.String(), Duration: duration}
|
||||||
|
@ -32,6 +34,6 @@ func TestCommand_Move(t *testing.T) {
|
||||||
|
|
||||||
newatributes, err := world.RoverAttributes(a)
|
newatributes, err := world.RoverAttributes(a)
|
||||||
assert.NoError(t, err, "Failed to set position for rover")
|
assert.NoError(t, err, "Failed to set position for rover")
|
||||||
pos.Add(Vector{0.0, duration * attribs.Speed}) // We should have moved duration*speed north
|
pos.Add(vector.Vector{X: 0.0, Y: duration * attribs.Speed}) // We should have moved duration*speed north
|
||||||
assert.Equal(t, pos, newatributes.Pos, "Failed to correctly set position for rover")
|
assert.Equal(t, pos, newatributes.Pos, "Failed to correctly set position for rover")
|
||||||
}
|
}
|
||||||
|
|
158
pkg/game/math.go
158
pkg/game/math.go
|
@ -1,158 +0,0 @@
|
||||||
package game
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"math"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TODO: Pull this out into math package and get more test coverage
|
|
||||||
|
|
||||||
// Abs gets the absolute value of an int
|
|
||||||
func Abs(x int) int {
|
|
||||||
if x < 0 {
|
|
||||||
return -x
|
|
||||||
}
|
|
||||||
return x
|
|
||||||
}
|
|
||||||
|
|
||||||
// pmod is a mositive modulo
|
|
||||||
// golang's % is a "remainder" function si misbehaves for negative modulus inputs
|
|
||||||
func Pmod(x, d int) int {
|
|
||||||
x = x % d
|
|
||||||
if x >= 0 {
|
|
||||||
return x
|
|
||||||
} else if d < 0 {
|
|
||||||
return x - d
|
|
||||||
} else {
|
|
||||||
return x + d
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Max returns the highest int
|
|
||||||
func Max(x int, y int) int {
|
|
||||||
if x < y {
|
|
||||||
return y
|
|
||||||
}
|
|
||||||
return x
|
|
||||||
}
|
|
||||||
|
|
||||||
// Min returns the lowest int
|
|
||||||
func Min(x int, y int) int {
|
|
||||||
if x > y {
|
|
||||||
return y
|
|
||||||
}
|
|
||||||
return x
|
|
||||||
}
|
|
||||||
|
|
||||||
// Vector desribes a 3D vector
|
|
||||||
type Vector struct {
|
|
||||||
X int `json:"x"`
|
|
||||||
Y int `json:"y"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add adds one vector to another
|
|
||||||
func (v *Vector) Add(v2 Vector) {
|
|
||||||
v.X += v2.X
|
|
||||||
v.Y += v2.Y
|
|
||||||
}
|
|
||||||
|
|
||||||
// Added calculates a new vector
|
|
||||||
func (v Vector) Added(v2 Vector) Vector {
|
|
||||||
v.Add(v2)
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
// Negated returns a negated vector
|
|
||||||
func (v Vector) Negated() Vector {
|
|
||||||
return Vector{-v.X, -v.Y}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Length returns the length of the vector
|
|
||||||
func (v Vector) Length() float64 {
|
|
||||||
return math.Sqrt(float64(v.X*v.X + v.Y*v.Y))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Distance returns the distance between two vectors
|
|
||||||
func (v Vector) Distance(v2 Vector) float64 {
|
|
||||||
// Negate the two vectors and calciate the length
|
|
||||||
return v.Added(v2.Negated()).Length()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Multiplied returns the vector multiplied by an int
|
|
||||||
func (v Vector) Multiplied(val int) Vector {
|
|
||||||
return Vector{v.X * val, v.Y * val}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Divided returns the vector divided by an int
|
|
||||||
func (v Vector) Divided(val int) Vector {
|
|
||||||
return Vector{v.X / val, v.Y / val}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Direction describes a compass direction
|
|
||||||
type Direction int
|
|
||||||
|
|
||||||
const (
|
|
||||||
North Direction = iota
|
|
||||||
NorthEast
|
|
||||||
East
|
|
||||||
SouthEast
|
|
||||||
South
|
|
||||||
SouthWest
|
|
||||||
West
|
|
||||||
NorthWest
|
|
||||||
)
|
|
||||||
|
|
||||||
// DirectionString simply describes the strings associated with a direction
|
|
||||||
type DirectionString struct {
|
|
||||||
Long string
|
|
||||||
Short string
|
|
||||||
}
|
|
||||||
|
|
||||||
// DirectionStrings is the set of strings for each direction
|
|
||||||
var DirectionStrings = []DirectionString{
|
|
||||||
{"North", "N"},
|
|
||||||
{"NorthEast", "NE"},
|
|
||||||
{"East", "E"},
|
|
||||||
{"SouthEast", "SE"},
|
|
||||||
{"South", "S"},
|
|
||||||
{"SouthWest", "SW"},
|
|
||||||
{"West", "W"},
|
|
||||||
{"NorthWest", "NW"},
|
|
||||||
}
|
|
||||||
|
|
||||||
// String converts a Direction to a String
|
|
||||||
func (d Direction) String() string {
|
|
||||||
return DirectionStrings[d].Long
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShortString converts a Direction to a short string version
|
|
||||||
func (d Direction) ShortString() string {
|
|
||||||
return DirectionStrings[d].Short
|
|
||||||
}
|
|
||||||
|
|
||||||
// DirectionFromString gets the Direction from a string
|
|
||||||
func DirectionFromString(s string) (Direction, error) {
|
|
||||||
for i, d := range DirectionStrings {
|
|
||||||
if strings.ToLower(d.Long) == strings.ToLower(s) || strings.ToLower(d.Short) == strings.ToLower(s) {
|
|
||||||
return Direction(i), nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1, fmt.Errorf("Unknown direction: %s", s)
|
|
||||||
}
|
|
||||||
|
|
||||||
var DirectionVectors = []Vector{
|
|
||||||
{0, 1}, // N
|
|
||||||
{1, 1}, // NE
|
|
||||||
{1, 0}, // E
|
|
||||||
{1, -1}, // SE
|
|
||||||
{0, -1}, // S
|
|
||||||
{-1, 1}, // SW
|
|
||||||
{-1, 0}, // W
|
|
||||||
{-1, 1}, // NW
|
|
||||||
}
|
|
||||||
|
|
||||||
// Vector converts a Direction to a Vector
|
|
||||||
func (d Direction) Vector() Vector {
|
|
||||||
return DirectionVectors[d]
|
|
||||||
}
|
|
|
@ -1,6 +1,9 @@
|
||||||
package game
|
package game
|
||||||
|
|
||||||
import "github.com/google/uuid"
|
import (
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/mdiluz/rove/pkg/vector"
|
||||||
|
)
|
||||||
|
|
||||||
// RoverAttributes contains attributes of a rover
|
// RoverAttributes contains attributes of a rover
|
||||||
type RoverAttributes struct {
|
type RoverAttributes struct {
|
||||||
|
@ -14,7 +17,7 @@ type RoverAttributes struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
|
||||||
// Pos represents where this rover is in the world
|
// Pos represents where this rover is in the world
|
||||||
Pos Vector `json:"pos"`
|
Pos vector.Vector `json:"pos"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rover describes a single rover in the world
|
// Rover describes a single rover in the world
|
||||||
|
|
|
@ -8,6 +8,9 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
"github.com/mdiluz/rove/pkg/bearing"
|
||||||
|
"github.com/mdiluz/rove/pkg/maths"
|
||||||
|
"github.com/mdiluz/rove/pkg/vector"
|
||||||
"github.com/tjarratt/babble"
|
"github.com/tjarratt/babble"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -70,9 +73,9 @@ func (w *World) SpawnRover() (uuid.UUID, error) {
|
||||||
strings.ReplaceAll(rover.Attributes.Name, "'s", "")
|
strings.ReplaceAll(rover.Attributes.Name, "'s", "")
|
||||||
|
|
||||||
// Spawn in a random place near the origin
|
// Spawn in a random place near the origin
|
||||||
rover.Attributes.Pos = Vector{
|
rover.Attributes.Pos = vector.Vector{
|
||||||
w.Atlas.ChunkSize/2 - rand.Intn(w.Atlas.ChunkSize),
|
X: w.Atlas.ChunkSize/2 - rand.Intn(w.Atlas.ChunkSize),
|
||||||
w.Atlas.ChunkSize/2 - rand.Intn(w.Atlas.ChunkSize),
|
Y: w.Atlas.ChunkSize/2 - rand.Intn(w.Atlas.ChunkSize),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Seach until we error (run out of world)
|
// Seach until we error (run out of world)
|
||||||
|
@ -84,7 +87,7 @@ func (w *World) SpawnRover() (uuid.UUID, error) {
|
||||||
break
|
break
|
||||||
} else {
|
} else {
|
||||||
// Try and spawn to the east of the blockage
|
// Try and spawn to the east of the blockage
|
||||||
rover.Attributes.Pos.Add(Vector{1, 0})
|
rover.Attributes.Pos.Add(vector.Vector{X: 1, Y: 0})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -146,7 +149,7 @@ func (w *World) SetRoverAttributes(id uuid.UUID, attributes RoverAttributes) err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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.Vector) error {
|
||||||
w.worldMutex.Lock()
|
w.worldMutex.Lock()
|
||||||
defer w.worldMutex.Unlock()
|
defer w.worldMutex.Unlock()
|
||||||
|
|
||||||
|
@ -176,7 +179,7 @@ func (w *World) WarpRover(id uuid.UUID, pos Vector) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetPosition sets an rovers position
|
// SetPosition sets an rovers position
|
||||||
func (w *World) MoveRover(id uuid.UUID, bearing Direction) (RoverAttributes, error) {
|
func (w *World) MoveRover(id uuid.UUID, bearing bearing.Direction) (RoverAttributes, error) {
|
||||||
w.worldMutex.Lock()
|
w.worldMutex.Lock()
|
||||||
defer w.worldMutex.Unlock()
|
defer w.worldMutex.Unlock()
|
||||||
|
|
||||||
|
@ -224,31 +227,31 @@ func (w *World) RadarFromRover(id uuid.UUID) ([]Tile, error) {
|
||||||
roverPos := r.Attributes.Pos
|
roverPos := r.Attributes.Pos
|
||||||
|
|
||||||
// Get the radar min and max values
|
// Get the radar min and max values
|
||||||
radarMin := Vector{
|
radarMin := vector.Vector{
|
||||||
X: roverPos.X - r.Attributes.Range,
|
X: roverPos.X - r.Attributes.Range,
|
||||||
Y: roverPos.Y - r.Attributes.Range,
|
Y: roverPos.Y - r.Attributes.Range,
|
||||||
}
|
}
|
||||||
radarMax := Vector{
|
radarMax := vector.Vector{
|
||||||
X: roverPos.X + r.Attributes.Range,
|
X: roverPos.X + r.Attributes.Range,
|
||||||
Y: roverPos.Y + r.Attributes.Range,
|
Y: roverPos.Y + r.Attributes.Range,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure we only query within the actual world
|
// Make sure we only query within the actual world
|
||||||
worldMin, worldMax := w.Atlas.GetWorldExtents()
|
worldMin, worldMax := w.Atlas.GetWorldExtents()
|
||||||
scanMin := Vector{
|
scanMin := vector.Vector{
|
||||||
X: Max(radarMin.X, worldMin.X),
|
X: maths.Max(radarMin.X, worldMin.X),
|
||||||
Y: Max(radarMin.Y, worldMin.Y),
|
Y: maths.Max(radarMin.Y, worldMin.Y),
|
||||||
}
|
}
|
||||||
scanMax := Vector{
|
scanMax := vector.Vector{
|
||||||
X: Min(radarMax.X, worldMax.X),
|
X: maths.Min(radarMax.X, worldMax.X),
|
||||||
Y: Min(radarMax.Y, worldMax.Y),
|
Y: maths.Min(radarMax.Y, worldMax.Y),
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 j := scanMin.Y; j <= scanMax.Y; j++ {
|
for j := scanMin.Y; j <= scanMax.Y; j++ {
|
||||||
for i := scanMin.X; i <= scanMax.X; i++ {
|
for i := scanMin.X; i <= scanMax.X; i++ {
|
||||||
q := Vector{i, j}
|
q := vector.Vector{X: i, Y: j}
|
||||||
|
|
||||||
if tile, err := w.Atlas.GetTile(q); err != nil {
|
if tile, err := w.Atlas.GetTile(q); err != nil {
|
||||||
return nil, fmt.Errorf("failed to query tile: %s", err)
|
return nil, fmt.Errorf("failed to query tile: %s", err)
|
||||||
|
@ -275,7 +278,7 @@ func (w *World) Enqueue(rover uuid.UUID, commands ...Command) error {
|
||||||
for _, c := range commands {
|
for _, c := range commands {
|
||||||
switch c.Command {
|
switch c.Command {
|
||||||
case "move":
|
case "move":
|
||||||
if _, err := DirectionFromString(c.Bearing); err != nil {
|
if _, err := bearing.DirectionFromString(c.Bearing); err != nil {
|
||||||
return fmt.Errorf("unknown direction: %s", c.Bearing)
|
return fmt.Errorf("unknown direction: %s", c.Bearing)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
@ -330,7 +333,7 @@ func (w *World) ExecuteCommand(c *Command, rover uuid.UUID) (finished bool, err
|
||||||
|
|
||||||
switch c.Command {
|
switch c.Command {
|
||||||
case "move":
|
case "move":
|
||||||
if dir, err := DirectionFromString(c.Bearing); err != nil {
|
if dir, err := bearing.DirectionFromString(c.Bearing); err != nil {
|
||||||
return true, fmt.Errorf("unknown direction in command %+v, skipping: %s\n", c, err)
|
return true, fmt.Errorf("unknown direction in command %+v, skipping: %s\n", c, err)
|
||||||
|
|
||||||
} else if _, err := w.MoveRover(rover, dir); err != nil {
|
} else if _, err := w.MoveRover(rover, dir); err != nil {
|
||||||
|
|
|
@ -3,6 +3,8 @@ package game
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/mdiluz/rove/pkg/bearing"
|
||||||
|
"github.com/mdiluz/rove/pkg/vector"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -65,7 +67,7 @@ func TestWorld_GetSetMovePosition(t *testing.T) {
|
||||||
attribs, err := world.RoverAttributes(a)
|
attribs, err := world.RoverAttributes(a)
|
||||||
assert.NoError(t, err, "Failed to get rover attribs")
|
assert.NoError(t, err, "Failed to get rover attribs")
|
||||||
|
|
||||||
pos := Vector{
|
pos := vector.Vector{
|
||||||
X: 0.0,
|
X: 0.0,
|
||||||
Y: 0.0,
|
Y: 0.0,
|
||||||
}
|
}
|
||||||
|
@ -77,15 +79,15 @@ func TestWorld_GetSetMovePosition(t *testing.T) {
|
||||||
assert.NoError(t, err, "Failed to set position for rover")
|
assert.NoError(t, err, "Failed to set position for rover")
|
||||||
assert.Equal(t, pos, newAttribs.Pos, "Failed to correctly set position for rover")
|
assert.Equal(t, pos, newAttribs.Pos, "Failed to correctly set position for rover")
|
||||||
|
|
||||||
bearing := North
|
bearing := bearing.North
|
||||||
duration := 1
|
duration := 1
|
||||||
newAttribs, err = world.MoveRover(a, bearing)
|
newAttribs, err = world.MoveRover(a, bearing)
|
||||||
assert.NoError(t, err, "Failed to set position for rover")
|
assert.NoError(t, err, "Failed to set position for rover")
|
||||||
pos.Add(Vector{0, attribs.Speed * duration}) // We should have move one unit of the speed north
|
pos.Add(vector.Vector{X: 0, Y: attribs.Speed * duration}) // We should have move one unit of the speed north
|
||||||
assert.Equal(t, pos, newAttribs.Pos, "Failed to correctly move position for rover")
|
assert.Equal(t, pos, newAttribs.Pos, "Failed to correctly move position for rover")
|
||||||
|
|
||||||
// Place a tile in front of the rover
|
// Place a tile in front of the rover
|
||||||
assert.NoError(t, world.Atlas.SetTile(Vector{0, 2}, TileWall))
|
assert.NoError(t, world.Atlas.SetTile(vector.Vector{X: 0, Y: 2}, TileWall))
|
||||||
newAttribs, err = world.MoveRover(a, bearing)
|
newAttribs, err = world.MoveRover(a, bearing)
|
||||||
assert.Equal(t, pos, newAttribs.Pos, "Failed to correctly not move position for rover into wall")
|
assert.Equal(t, pos, newAttribs.Pos, "Failed to correctly not move position for rover into wall")
|
||||||
}
|
}
|
||||||
|
@ -106,9 +108,9 @@ func TestWorld_RadarFromRover(t *testing.T) {
|
||||||
assert.NoError(t, err, "Failed to set rover attribs")
|
assert.NoError(t, err, "Failed to set rover attribs")
|
||||||
|
|
||||||
// Warp the rovers into position
|
// Warp the rovers into position
|
||||||
bpos := Vector{-3, -3}
|
bpos := vector.Vector{X: -3, Y: -3}
|
||||||
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(a, Vector{0, 0}), "Failed to warp rover")
|
assert.NoError(t, world.WarpRover(a, vector.Vector{X: 0, Y: 0}), "Failed to warp rover")
|
||||||
|
|
||||||
// Spawn the world wall
|
// Spawn the world wall
|
||||||
err = world.Atlas.SpawnWalls()
|
err = world.Atlas.SpawnWalls()
|
||||||
|
|
41
pkg/maths/maths.go
Normal file
41
pkg/maths/maths.go
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
package maths
|
||||||
|
|
||||||
|
// Abs gets the absolute value of an int
|
||||||
|
func Abs(x int) int {
|
||||||
|
if x < 0 {
|
||||||
|
return -x
|
||||||
|
}
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
// pmod is a mositive modulo
|
||||||
|
// golang's % is a "remainder" function si misbehaves for negative modulus inputs
|
||||||
|
func Pmod(x, d int) int {
|
||||||
|
if x == 0 || d == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
x = x % d
|
||||||
|
if x >= 0 {
|
||||||
|
return x
|
||||||
|
} else if d < 0 {
|
||||||
|
return x - d
|
||||||
|
} else {
|
||||||
|
return x + d
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Max returns the highest int
|
||||||
|
func Max(x int, y int) int {
|
||||||
|
if x < y {
|
||||||
|
return y
|
||||||
|
}
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
// Min returns the lowest int
|
||||||
|
func Min(x int, y int) int {
|
||||||
|
if x > y {
|
||||||
|
return y
|
||||||
|
}
|
||||||
|
return x
|
||||||
|
}
|
33
pkg/maths/maths_test.go
Normal file
33
pkg/maths/maths_test.go
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
package maths
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAbs(t *testing.T) {
|
||||||
|
assert.Equal(t, 0, Abs(0))
|
||||||
|
assert.Equal(t, 1, Abs(1))
|
||||||
|
assert.Equal(t, 1, Abs(-1))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPmod(t *testing.T) {
|
||||||
|
assert.Equal(t, 0, Pmod(0, 0))
|
||||||
|
assert.Equal(t, 2, Pmod(6, 4))
|
||||||
|
assert.Equal(t, 2, Pmod(-6, 4))
|
||||||
|
assert.Equal(t, 4, Pmod(-6, 10))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMax(t *testing.T) {
|
||||||
|
assert.Equal(t, 500, Max(100, 500))
|
||||||
|
assert.Equal(t, 1, Max(-4, 1))
|
||||||
|
assert.Equal(t, -2, Max(-4, -2))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMin(t *testing.T) {
|
||||||
|
assert.Equal(t, 100, Min(100, 500))
|
||||||
|
assert.Equal(t, -4, Min(-4, 1))
|
||||||
|
assert.Equal(t, -4, Min(-4, -2))
|
||||||
|
|
||||||
|
}
|
|
@ -10,6 +10,7 @@ import (
|
||||||
|
|
||||||
"github.com/mdiluz/rove/pkg/game"
|
"github.com/mdiluz/rove/pkg/game"
|
||||||
"github.com/mdiluz/rove/pkg/rove"
|
"github.com/mdiluz/rove/pkg/rove"
|
||||||
|
"github.com/mdiluz/rove/pkg/vector"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -91,7 +92,7 @@ func TestHandleCommand(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)
|
||||||
assert.NoError(t, s.world.WarpRover(inst, game.Vector{}))
|
assert.NoError(t, s.world.WarpRover(inst, vector.Vector{}))
|
||||||
|
|
||||||
attribs, err := s.world.RoverAttributes(inst)
|
attribs, err := s.world.RoverAttributes(inst)
|
||||||
assert.NoError(t, err, "Couldn't get rover position")
|
assert.NoError(t, err, "Couldn't get rover position")
|
||||||
|
@ -130,7 +131,7 @@ func TestHandleCommand(t *testing.T) {
|
||||||
|
|
||||||
attribs2, err := s.world.RoverAttributes(inst)
|
attribs2, err := s.world.RoverAttributes(inst)
|
||||||
assert.NoError(t, err, "Couldn't get rover position")
|
assert.NoError(t, err, "Couldn't get rover position")
|
||||||
attribs.Pos.Add(game.Vector{X: 0.0, Y: attrib.Speed * 1}) // Should have moved north by the speed and duration
|
attribs.Pos.Add(vector.Vector{X: 0.0, Y: attrib.Speed * 1}) // Should have moved north by the speed and duration
|
||||||
assert.Equal(t, attribs.Pos, attribs2.Pos, "Rover should have moved by bearing")
|
assert.Equal(t, attribs.Pos, attribs2.Pos, "Rover should have moved by bearing")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,13 +146,13 @@ func TestHandleRadar(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Warp this rover to 0,0
|
// Warp this rover to 0,0
|
||||||
assert.NoError(t, s.world.WarpRover(id, game.Vector{}))
|
assert.NoError(t, s.world.WarpRover(id, vector.Vector{}))
|
||||||
|
|
||||||
// Explicity set a few nearby tiles
|
// Explicity set a few nearby tiles
|
||||||
wallPos1 := game.Vector{X: 0, Y: -1}
|
wallPos1 := vector.Vector{X: 0, Y: -1}
|
||||||
wallPos2 := game.Vector{X: 1, Y: 1}
|
wallPos2 := vector.Vector{X: 1, Y: 1}
|
||||||
rockPos := game.Vector{X: 1, Y: 3}
|
rockPos := vector.Vector{X: 1, Y: 3}
|
||||||
emptyPos := game.Vector{X: -2, Y: -3}
|
emptyPos := vector.Vector{X: -2, Y: -3}
|
||||||
assert.NoError(t, s.world.Atlas.SetTile(wallPos1, game.TileWall))
|
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(wallPos2, game.TileWall))
|
||||||
assert.NoError(t, s.world.Atlas.SetTile(rockPos, game.TileRock))
|
assert.NoError(t, s.world.Atlas.SetTile(rockPos, game.TileRock))
|
||||||
|
@ -171,7 +172,7 @@ func TestHandleRadar(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
scope := attrib.Range*2 + 1
|
scope := attrib.Range*2 + 1
|
||||||
radarOrigin := game.Vector{X: -attrib.Range, Y: -attrib.Range}
|
radarOrigin := vector.Vector{X: -attrib.Range, Y: -attrib.Range}
|
||||||
|
|
||||||
// Make sure the rover tile is correct
|
// 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])
|
||||||
|
|
47
pkg/vector/vector.go
Normal file
47
pkg/vector/vector.go
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
package vector
|
||||||
|
|
||||||
|
import "math"
|
||||||
|
|
||||||
|
// Vector desribes a 3D vector
|
||||||
|
type Vector struct {
|
||||||
|
X int `json:"x"`
|
||||||
|
Y int `json:"y"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add adds one vector to another
|
||||||
|
func (v *Vector) Add(v2 Vector) {
|
||||||
|
v.X += v2.X
|
||||||
|
v.Y += v2.Y
|
||||||
|
}
|
||||||
|
|
||||||
|
// Added calculates a new vector
|
||||||
|
func (v Vector) Added(v2 Vector) Vector {
|
||||||
|
v.Add(v2)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Negated returns a negated vector
|
||||||
|
func (v Vector) Negated() Vector {
|
||||||
|
return Vector{-v.X, -v.Y}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Length returns the length of the vector
|
||||||
|
func (v Vector) Length() float64 {
|
||||||
|
return math.Sqrt(float64(v.X*v.X + v.Y*v.Y))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Distance returns the distance between two vectors
|
||||||
|
func (v Vector) Distance(v2 Vector) float64 {
|
||||||
|
// Negate the two vectors and calciate the length
|
||||||
|
return v.Added(v2.Negated()).Length()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Multiplied returns the vector multiplied by an int
|
||||||
|
func (v Vector) Multiplied(val int) Vector {
|
||||||
|
return Vector{v.X * val, v.Y * val}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Divided returns the vector divided by an int
|
||||||
|
func (v Vector) Divided(val int) Vector {
|
||||||
|
return Vector{v.X / val, v.Y / val}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package game
|
package vector
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math"
|
"math"
|
||||||
|
@ -180,15 +180,15 @@ func TestVector_Multiplied(t *testing.T) {
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "Basic multiply 1",
|
name: "Basic multiply 1",
|
||||||
vec: North.Vector(),
|
vec: Vector{0, 1},
|
||||||
arg: 2,
|
arg: 2,
|
||||||
want: Vector{0, 2},
|
want: Vector{0, 2},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Basic multiply 2",
|
name: "Basic multiply 2",
|
||||||
vec: NorthWest.Vector(),
|
vec: Vector{-1, 1},
|
||||||
arg: -1,
|
arg: -1,
|
||||||
want: SouthEast.Vector(),
|
want: Vector{1, -1},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
@ -203,27 +203,3 @@ func TestVector_Multiplied(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDirection(t *testing.T) {
|
|
||||||
dir := North
|
|
||||||
|
|
||||||
assert.Equal(t, "North", dir.String())
|
|
||||||
assert.Equal(t, "N", dir.ShortString())
|
|
||||||
assert.Equal(t, Vector{0, 1}, dir.Vector())
|
|
||||||
|
|
||||||
dir, err := DirectionFromString("N")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, North, dir)
|
|
||||||
|
|
||||||
dir, err = DirectionFromString("n")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, North, dir)
|
|
||||||
|
|
||||||
dir, err = DirectionFromString("north")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, North, dir)
|
|
||||||
|
|
||||||
dir, err = DirectionFromString("NorthWest")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, NorthWest, dir)
|
|
||||||
}
|
|
Loading…
Add table
Reference in a new issue