Merge pull request #28 from mdiluz/dormant-rovers

Dormant rovers
This commit is contained in:
Marc Di Luzio 2020-07-19 19:20:03 +01:00 committed by GitHub
commit 89123394cd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 246 additions and 153 deletions

View file

@ -22,6 +22,9 @@ const (
// GlyphRoverLive represents a live rover
GlyphRoverLive = Glyph('R')
// GlyphRoverDormant represents a dormant rover
GlyphRoverDormant = Glyph('r')
// GlyphRockSmall is a small stashable rock
GlyphRockSmall = Glyph('o')
@ -51,6 +54,8 @@ func ObjectGlyph(o roveapi.Object) Glyph {
return GlyphRoverLive
case roveapi.Object_RockSmall:
return GlyphRockSmall
case roveapi.Object_RoverDormant:
return GlyphRoverDormant
case roveapi.Object_RockLarge:
return GlyphRockLarge
}

View file

@ -1,4 +1,4 @@
package atlas
package rove
import (
"github.com/mdiluz/rove/pkg/maths"

View file

@ -1,4 +1,4 @@
package atlas
package rove
import (
"testing"

View file

@ -1,4 +1,4 @@
package atlas
package rove
import (
"log"
@ -6,7 +6,6 @@ import (
"github.com/mdiluz/rove/pkg/maths"
"github.com/mdiluz/rove/proto/roveapi"
"github.com/ojrac/opensimplex-go"
)
// chunk represents a fixed square grid of tiles
@ -34,29 +33,23 @@ type chunkBasedAtlas struct {
// ChunkSize is the x/y dimensions of each square chunk
ChunkSize int `json:"chunksize"`
// terrainNoise describes the noise function for the terrain
terrainNoise opensimplex.Noise
// terrainNoise describes the noise function for the terrain
objectNoise opensimplex.Noise
// worldGen is the internal world generator
worldGen WorldGen
}
const (
noiseSeed = 1024
terrainNoiseScale = 6
objectNoiseScale = 3
noiseSeed = 1024
)
// NewChunkAtlas creates a new empty atlas
func NewChunkAtlas(chunkSize int) Atlas {
// Start up with one chunk
a := chunkBasedAtlas{
ChunkSize: chunkSize,
Chunks: make([]chunk, 1),
LowerBound: maths.Vector{X: 0, Y: 0},
UpperBound: maths.Vector{X: chunkSize, Y: chunkSize},
terrainNoise: opensimplex.New(noiseSeed),
objectNoise: opensimplex.New(noiseSeed),
ChunkSize: chunkSize,
Chunks: make([]chunk, 1),
LowerBound: maths.Vector{X: 0, Y: 0},
UpperBound: maths.Vector{X: chunkSize, Y: chunkSize},
worldGen: NewNoiseWorldGen(noiseSeed),
}
// Initialise the first chunk
a.populate(0)
@ -105,31 +98,15 @@ func (a *chunkBasedAtlas) populate(chunk int) {
origin := a.chunkOriginInWorldSpace(chunk)
for i := 0; i < a.ChunkSize; i++ {
for j := 0; j < a.ChunkSize; j++ {
loc := maths.Vector{X: origin.X + i, Y: origin.Y + j}
// Get the terrain noise value for this location
t := a.terrainNoise.Eval2(float64(origin.X+i)/terrainNoiseScale, float64(origin.Y+j)/terrainNoiseScale)
var tile roveapi.Tile
switch {
case t > 0.5:
tile = roveapi.Tile_Gravel
case t > 0.05:
tile = roveapi.Tile_Sand
default:
tile = roveapi.Tile_Rock
}
c.Tiles[j*a.ChunkSize+i] = byte(tile)
// Set the tile
c.Tiles[j*a.ChunkSize+i] = byte(a.worldGen.GetTile(loc))
// Get the object noise value for this location
o := a.objectNoise.Eval2(float64(origin.X+i)/objectNoiseScale, float64(origin.Y+j)/objectNoiseScale)
var obj = roveapi.Object_ObjectUnknown
switch {
case o > 0.6:
obj = roveapi.Object_RockLarge
case o > 0.5:
obj = roveapi.Object_RockSmall
}
if obj != roveapi.Object_ObjectUnknown {
c.Objects[j*a.ChunkSize+i] = Object{Type: roveapi.Object(obj)}
// Set the object
obj := a.worldGen.GetObject(loc)
if obj.Type != roveapi.Object_ObjectUnknown {
c.Objects[j*a.ChunkSize+i] = obj
}
}
}
@ -236,12 +213,11 @@ func (a *chunkBasedAtlas) worldSpaceToChunkWithGrow(v maths.Vector) int {
// Create the new empty atlas
newAtlas := chunkBasedAtlas{
ChunkSize: a.ChunkSize,
LowerBound: lower,
UpperBound: upper,
Chunks: make([]chunk, size.X*size.Y),
terrainNoise: a.terrainNoise,
objectNoise: a.objectNoise,
ChunkSize: a.ChunkSize,
LowerBound: lower,
UpperBound: upper,
Chunks: make([]chunk, size.X*size.Y),
worldGen: a.worldGen,
}
// Log that we're resizing

View file

@ -1,4 +1,4 @@
package atlas
package rove
import (
"github.com/mdiluz/rove/proto/roveapi"
@ -8,12 +8,16 @@ import (
type Object struct {
// The type of the object
Type roveapi.Object `json:"type"`
// Data is an internal type used for certain types of object
Data []byte `json:"data"`
}
// IsBlocking checks if an object is a blocking object
func (o *Object) IsBlocking() bool {
var blocking = [...]roveapi.Object{
roveapi.Object_RoverLive,
roveapi.Object_RoverDormant,
roveapi.Object_RockLarge,
}

View file

@ -1,11 +1,14 @@
package rove
import (
"bufio"
"fmt"
"log"
"math/rand"
"os"
"time"
"github.com/mdiluz/rove/pkg/atlas"
"github.com/google/uuid"
"github.com/mdiluz/rove/pkg/maths"
)
@ -30,7 +33,7 @@ type Rover struct {
Range int `json:"range"`
// Inventory represents any items the rover is carrying
Inventory []atlas.Object `json:"inventory"`
Inventory []Object `json:"inventory"`
// Capacity is the maximum number of inventory items
Capacity int `json:"capacity"`
@ -51,6 +54,19 @@ type Rover struct {
Logs []RoverLogEntry `json:"logs"`
}
// DefaultRover returns a default rover object with default settings
func DefaultRover() Rover {
return Rover{
Range: 4,
Integrity: 10,
MaximumIntegrity: 10,
Capacity: 10,
Charge: 10,
MaximumCharge: 10,
Name: GenerateRoverName(),
}
}
// AddLogEntryf adds an entry to the rovers log
func (r *Rover) AddLogEntryf(format string, args ...interface{}) {
text := fmt.Sprintf(format, args...)
@ -62,3 +78,36 @@ func (r *Rover) AddLogEntryf(format string, args ...interface{}) {
},
)
}
var wordsFile = os.Getenv("WORDS_FILE")
var roverWords []string
// GenerateRoverName generates a new rover name
func GenerateRoverName() string {
// Try and load the rover words file
if len(roverWords) == 0 {
// Try and load the words file
if file, err := os.Open(wordsFile); err != nil {
log.Printf("Couldn't read words file [%s], running without words: %s\n", wordsFile, err)
} else {
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
roverWords = append(roverWords, scanner.Text())
}
if scanner.Err() != nil {
log.Printf("Failure during word file scan: %s\n", scanner.Err())
}
}
}
// Assign a random name if we have words
if len(roverWords) > 0 {
// Loop until we find a unique name
return fmt.Sprintf("%s-%s", roverWords[rand.Intn(len(roverWords))], roverWords[rand.Intn(len(roverWords))])
}
// Default to a unique string
return uuid.New().String()
}

View file

@ -1,15 +1,11 @@
package rove
import (
"bufio"
"fmt"
"log"
"math/rand"
"os"
"sync"
"github.com/google/uuid"
"github.com/mdiluz/rove/pkg/atlas"
"github.com/mdiluz/rove/pkg/maths"
"github.com/mdiluz/rove/proto/roveapi"
)
@ -26,7 +22,7 @@ type World struct {
Rovers map[string]Rover `json:"rovers"`
// Atlas represends the world map of chunks and tiles
Atlas atlas.Atlas `json:"atlas"`
Atlas Atlas `json:"atlas"`
// Commands is the set of currently executing command streams per rover
CommandQueue map[string]CommandStream `json:"commands"`
@ -37,36 +33,15 @@ type World struct {
worldMutex sync.RWMutex
// Mutex to lock around command operations
cmdMutex sync.RWMutex
// Set of possible words to use for names
words []string
}
var wordsFile = os.Getenv("WORDS_FILE")
// NewWorld creates a new world object
func NewWorld(chunkSize int) *World {
// Try and load the words file
var lines []string
if file, err := os.Open(wordsFile); err != nil {
log.Printf("Couldn't read words file [%s], running without words: %s\n", wordsFile, err)
} else {
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
lines = append(lines, scanner.Text())
}
if scanner.Err() != nil {
log.Printf("Failure during word file scan: %s\n", scanner.Err())
}
}
return &World{
Rovers: make(map[string]Rover),
CommandQueue: make(map[string]CommandStream),
CommandIncoming: make(map[string]CommandStream),
Atlas: atlas.NewChunkAtlas(chunkSize),
words: lines,
Atlas: NewChunkAtlas(chunkSize),
TicksPerDay: 24,
CurrentTicks: 0,
}
@ -78,27 +53,7 @@ func (w *World) SpawnRover() (string, error) {
defer w.worldMutex.Unlock()
// Initialise the rover
rover := Rover{
Range: 4,
Integrity: 10,
MaximumIntegrity: 10,
Capacity: 10,
Charge: 10,
MaximumCharge: 10,
Name: uuid.New().String(),
}
// Assign a random name if we have words
if len(w.words) > 0 {
for {
// Loop until we find a unique name
name := fmt.Sprintf("%s-%s", w.words[rand.Intn(len(w.words))], w.words[rand.Intn(len(w.words))])
if _, ok := w.Rovers[name]; !ok {
rover.Name = name
break
}
}
}
rover := DefaultRover()
// Spawn in a random place near the origin
rover.Pos = maths.Vector{
@ -240,7 +195,7 @@ func (w *World) SetRoverPosition(rover string, pos maths.Vector) error {
}
// RoverInventory returns the inventory of a requested rover
func (w *World) RoverInventory(rover string) ([]atlas.Object, error) {
func (w *World) RoverInventory(rover string) ([]Object, error) {
w.worldMutex.RLock()
defer w.worldMutex.RUnlock()
@ -346,7 +301,7 @@ func (w *World) RoverStash(rover string) (roveapi.Object, error) {
r.AddLogEntryf("stashed %c", obj.Type)
r.Inventory = append(r.Inventory, obj)
w.Rovers[rover] = r
w.Atlas.SetObject(r.Pos, atlas.Object{Type: roveapi.Object_ObjectUnknown})
w.Atlas.SetObject(r.Pos, Object{Type: roveapi.Object_ObjectUnknown})
return obj.Type, nil
}

View file

@ -3,7 +3,6 @@ package rove
import (
"testing"
"github.com/mdiluz/rove/pkg/atlas"
"github.com/mdiluz/rove/pkg/maths"
"github.com/mdiluz/rove/proto/roveapi"
"github.com/stretchr/testify/assert"
@ -90,7 +89,7 @@ func TestWorld_GetSetMovePosition(t *testing.T) {
assert.Contains(t, rover.Logs[len(rover.Logs)-1].Text, "moved", "Rover logs should contain the move")
// Place a tile in front of the rover
world.Atlas.SetObject(maths.Vector{X: 0, Y: 2}, atlas.Object{Type: roveapi.Object_RockLarge})
world.Atlas.SetObject(maths.Vector{X: 0, Y: 2}, Object{Type: roveapi.Object_RockLarge})
newPos, err = world.MoveRover(a, b)
assert.NoError(t, err, "Failed to move rover")
assert.Equal(t, pos, newPos, "Failed to correctly not move position for rover into wall")
@ -110,7 +109,9 @@ func TestWorld_RadarFromRover(t *testing.T) {
// Warp the rovers into position
bpos := maths.Vector{X: -3, Y: -3}
world.Atlas.SetObject(bpos, Object{Type: roveapi.Object_ObjectUnknown})
assert.NoError(t, world.WarpRover(b, bpos), "Failed to warp rover")
world.Atlas.SetObject(maths.Vector{X: 0, Y: 0}, Object{Type: roveapi.Object_ObjectUnknown})
assert.NoError(t, world.WarpRover(a, maths.Vector{X: 0, Y: 0}), "Failed to warp rover")
radar, objs, err := world.RadarFromRover(a)
@ -142,7 +143,7 @@ func TestWorld_RoverStash(t *testing.T) {
Y: 0.0,
}
world.Atlas.SetObject(pos, atlas.Object{Type: roveapi.Object_ObjectUnknown})
world.Atlas.SetObject(pos, Object{Type: roveapi.Object_ObjectUnknown})
err = world.WarpRover(a, pos)
assert.NoError(t, err, "Failed to set position for rover")
@ -151,7 +152,7 @@ func TestWorld_RoverStash(t *testing.T) {
for i := 0; i < rover.Capacity; i++ {
// Place an object
world.Atlas.SetObject(pos, atlas.Object{Type: roveapi.Object_RockSmall})
world.Atlas.SetObject(pos, Object{Type: roveapi.Object_RockSmall})
// Pick it up
o, err := world.RoverStash(a)
@ -166,7 +167,7 @@ func TestWorld_RoverStash(t *testing.T) {
inv, err := world.RoverInventory(a)
assert.NoError(t, err, "Failed to get inventory")
assert.Equal(t, i+1, len(inv))
assert.Equal(t, atlas.Object{Type: roveapi.Object_RockSmall}, inv[i])
assert.Equal(t, Object{Type: roveapi.Object_RockSmall}, inv[i])
// Check that this did reduce the charge
info, err := world.GetRover(a)
@ -183,7 +184,7 @@ func TestWorld_RoverStash(t *testing.T) {
}
// Place an object
world.Atlas.SetObject(pos, atlas.Object{Type: roveapi.Object_RockSmall})
world.Atlas.SetObject(pos, Object{Type: roveapi.Object_RockSmall})
// Try to pick it up
o, err := world.RoverStash(a)
@ -221,7 +222,7 @@ func TestWorld_RoverDamage(t *testing.T) {
info, err := world.GetRover(a)
assert.NoError(t, err, "couldn't get rover info")
world.Atlas.SetObject(maths.Vector{X: 0.0, Y: 1.0}, atlas.Object{Type: roveapi.Object_RockLarge})
world.Atlas.SetObject(maths.Vector{X: 0.0, Y: 1.0}, Object{Type: roveapi.Object_RockLarge})
vec, err := world.MoveRover(a, roveapi.Bearing_North)
assert.NoError(t, err, "Failed to move rover")
@ -243,7 +244,7 @@ func TestWorld_RoverRepair(t *testing.T) {
Y: 0.0,
}
world.Atlas.SetObject(pos, atlas.Object{Type: roveapi.Object_ObjectUnknown})
world.Atlas.SetObject(pos, Object{Type: roveapi.Object_ObjectUnknown})
err = world.WarpRover(a, pos)
assert.NoError(t, err, "Failed to set position for rover")
@ -252,12 +253,12 @@ func TestWorld_RoverRepair(t *testing.T) {
assert.NoError(t, err, "couldn't get rover info")
// Pick up something to repair with
world.Atlas.SetObject(pos, atlas.Object{Type: roveapi.Object_RockSmall})
world.Atlas.SetObject(pos, Object{Type: roveapi.Object_RockSmall})
o, err := world.RoverStash(a)
assert.NoError(t, err, "Failed to stash")
assert.Equal(t, roveapi.Object_RockSmall, o, "Failed to get correct object")
world.Atlas.SetObject(maths.Vector{X: 0.0, Y: 1.0}, atlas.Object{Type: roveapi.Object_RockLarge})
world.Atlas.SetObject(maths.Vector{X: 0.0, Y: 1.0}, Object{Type: roveapi.Object_RockLarge})
// Try and bump into the rock
vec, err := world.MoveRover(a, roveapi.Bearing_North)
@ -277,7 +278,7 @@ func TestWorld_RoverRepair(t *testing.T) {
assert.Contains(t, newinfo.Logs[len(newinfo.Logs)-1].Text, "repair", "Rover logs should contain the repair")
// Check again that it can't repair past the max
world.Atlas.SetObject(pos, atlas.Object{Type: roveapi.Object_RockSmall})
world.Atlas.SetObject(pos, Object{Type: roveapi.Object_RockSmall})
o, err = world.RoverStash(a)
assert.NoError(t, err, "Failed to stash")
assert.Equal(t, roveapi.Object_RockSmall, o, "Failed to get correct object")
@ -308,7 +309,7 @@ func TestWorld_Charge(t *testing.T) {
// Ensure the path ahead is empty
world.Atlas.SetTile(initialPos.Added(maths.BearingToVector(roveapi.Bearing_North)), roveapi.Tile_Rock)
world.Atlas.SetObject(initialPos.Added(maths.BearingToVector(roveapi.Bearing_North)), atlas.Object{Type: roveapi.Object_ObjectUnknown})
world.Atlas.SetObject(initialPos.Added(maths.BearingToVector(roveapi.Bearing_North)), Object{Type: roveapi.Object_ObjectUnknown})
// Try and move north (along unblocked path)
newPos, err := world.MoveRover(a, roveapi.Bearing_North)
@ -372,6 +373,8 @@ func TestWorld_Broadcast(t *testing.T) {
assert.NoError(t, err)
// Warp rovers near to eachother
world.Atlas.SetObject(maths.Vector{X: 0, Y: 0}, Object{Type: roveapi.Object_ObjectUnknown})
world.Atlas.SetObject(maths.Vector{X: 1, Y: 0}, Object{Type: roveapi.Object_ObjectUnknown})
assert.NoError(t, world.WarpRover(a, maths.Vector{X: 0, Y: 0}))
assert.NoError(t, world.WarpRover(b, maths.Vector{X: 1, Y: 0}))
@ -390,7 +393,7 @@ func TestWorld_Broadcast(t *testing.T) {
assert.Contains(t, rb.Logs[len(rb.Logs)-1].Text, "ABC", "Rover A should have logged it's broadcast")
// Warp B outside of the range of A
world.Atlas.SetObject(maths.Vector{X: ra.Range, Y: 0}, atlas.Object{Type: roveapi.Object_ObjectUnknown})
world.Atlas.SetObject(maths.Vector{X: ra.Range, Y: 0}, Object{Type: roveapi.Object_ObjectUnknown})
assert.NoError(t, world.WarpRover(b, maths.Vector{X: ra.Range, Y: 0}))
// Broadcast from a again
@ -407,7 +410,7 @@ func TestWorld_Broadcast(t *testing.T) {
assert.Contains(t, rb.Logs[len(rb.Logs)-1].Text, "XYZ", "Rover A should have logged it's broadcast")
// Warp B outside of the range of A
world.Atlas.SetObject(maths.Vector{X: ra.Range + 1, Y: 0}, atlas.Object{Type: roveapi.Object_ObjectUnknown})
world.Atlas.SetObject(maths.Vector{X: ra.Range + 1, Y: 0}, Object{Type: roveapi.Object_ObjectUnknown})
assert.NoError(t, world.WarpRover(b, maths.Vector{X: ra.Range + 1, Y: 0}))
// Broadcast from a again

93
pkg/rove/worldgen.go Normal file
View file

@ -0,0 +1,93 @@
package rove
import (
"encoding/json"
"log"
"github.com/mdiluz/rove/pkg/maths"
"github.com/mdiluz/rove/proto/roveapi"
"github.com/ojrac/opensimplex-go"
)
// WorldGen describes a world gen algorythm
type WorldGen interface {
// GetTile generates a tile for a location
GetTile(v maths.Vector) roveapi.Tile
// GetObject generates an object for a location
GetObject(v maths.Vector) Object
}
// NoiseWorldGen returns a noise based world generator
type NoiseWorldGen struct {
// noise describes the noise function
noise opensimplex.Noise
}
// NewNoiseWorldGen creates a new noise based world generator
func NewNoiseWorldGen(seed int64) WorldGen {
return &NoiseWorldGen{
noise: opensimplex.New(seed),
}
}
const (
terrainNoiseScale = 6
rockNoiseScale = 3
)
// GetTile returns the chosen tile at a location
func (g *NoiseWorldGen) GetTile(v maths.Vector) roveapi.Tile {
t := g.noise.Eval2(float64(v.X)/terrainNoiseScale, float64(v.Y)/terrainNoiseScale)
switch {
case t > 0.5:
return roveapi.Tile_Gravel
case t > 0.05:
return roveapi.Tile_Sand
default:
return roveapi.Tile_Rock
}
}
// GetObject returns the chosen object at a location
func (g *NoiseWorldGen) GetObject(v maths.Vector) (obj Object) {
o := g.noise.Eval2(float64(v.X)/rockNoiseScale, float64(v.Y)/rockNoiseScale)
switch {
case o > 0.6:
obj.Type = roveapi.Object_RockLarge
case o > 0.5:
obj.Type = roveapi.Object_RockSmall
}
// Very rarely spawn a dormant rover
if obj.Type == roveapi.Object_ObjectUnknown {
// TODO: Make this better, ideally with noise
if v.X%25 == 0 && v.Y%25 == 0 && v.X != 0 && v.Y != 0 {
obj.Type = roveapi.Object_RoverDormant
}
}
// Post process any spawned objects
switch obj.Type {
case roveapi.Object_RoverDormant:
// Create the rover
r := DefaultRover()
// Set the rover variables
r.Pos = v
// For now, mark the log as corrupted
r.AddLogEntryf("log corrupted")
// Marshal the rover data into the object data
b, err := json.Marshal(r)
if err != nil {
log.Fatalf("couldn't marshal rover, should never fail: %s", err)
}
// Store the bytes
obj.Data = b
}
return obj
}

View file

@ -162,10 +162,12 @@ const (
Object_ObjectUnknown Object = 0
// RoverLive represents a live rover
Object_RoverLive Object = 1
// RoverDormant describes a dormant rover
Object_RoverDormant Object = 2
// RockSmall is a small stashable rock
Object_RockSmall Object = 2
Object_RockSmall Object = 3
// RockLarge is a large blocking rock
Object_RockLarge Object = 3
Object_RockLarge Object = 4
)
// Enum value maps for Object.
@ -173,14 +175,16 @@ var (
Object_name = map[int32]string{
0: "ObjectUnknown",
1: "RoverLive",
2: "RockSmall",
3: "RockLarge",
2: "RoverDormant",
3: "RockSmall",
4: "RockLarge",
}
Object_value = map[string]int32{
"ObjectUnknown": 0,
"RoverLive": 1,
"RockSmall": 2,
"RockLarge": 3,
"RoverDormant": 2,
"RockSmall": 3,
"RockLarge": 4,
}
)
@ -1269,39 +1273,40 @@ var file_roveapi_roveapi_proto_rawDesc = []byte{
0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x4e, 0x6f, 0x72, 0x74, 0x68,
0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x45, 0x61, 0x73, 0x74, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05,
0x53, 0x6f, 0x75, 0x74, 0x68, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x57, 0x65, 0x73, 0x74, 0x10,
0x04, 0x2a, 0x48, 0x0a, 0x06, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x11, 0x0a, 0x0d, 0x4f,
0x04, 0x2a, 0x5a, 0x0a, 0x06, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x11, 0x0a, 0x0d, 0x4f,
0x62, 0x6a, 0x65, 0x63, 0x74, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00, 0x12, 0x0d,
0x0a, 0x09, 0x52, 0x6f, 0x76, 0x65, 0x72, 0x4c, 0x69, 0x76, 0x65, 0x10, 0x01, 0x12, 0x0d, 0x0a,
0x09, 0x52, 0x6f, 0x63, 0x6b, 0x53, 0x6d, 0x61, 0x6c, 0x6c, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09,
0x52, 0x6f, 0x63, 0x6b, 0x4c, 0x61, 0x72, 0x67, 0x65, 0x10, 0x03, 0x2a, 0x37, 0x0a, 0x04, 0x54,
0x69, 0x6c, 0x65, 0x12, 0x0f, 0x0a, 0x0b, 0x54, 0x69, 0x6c, 0x65, 0x55, 0x6e, 0x6b, 0x6e, 0x6f,
0x77, 0x6e, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x52, 0x6f, 0x63, 0x6b, 0x10, 0x01, 0x12, 0x0a,
0x0a, 0x06, 0x47, 0x72, 0x61, 0x76, 0x65, 0x6c, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x53, 0x61,
0x6e, 0x64, 0x10, 0x03, 0x32, 0xcf, 0x02, 0x0a, 0x04, 0x52, 0x6f, 0x76, 0x65, 0x12, 0x4d, 0x0a,
0x0c, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1c, 0x2e,
0x0a, 0x09, 0x52, 0x6f, 0x76, 0x65, 0x72, 0x4c, 0x69, 0x76, 0x65, 0x10, 0x01, 0x12, 0x10, 0x0a,
0x0c, 0x52, 0x6f, 0x76, 0x65, 0x72, 0x44, 0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x74, 0x10, 0x02, 0x12,
0x0d, 0x0a, 0x09, 0x52, 0x6f, 0x63, 0x6b, 0x53, 0x6d, 0x61, 0x6c, 0x6c, 0x10, 0x03, 0x12, 0x0d,
0x0a, 0x09, 0x52, 0x6f, 0x63, 0x6b, 0x4c, 0x61, 0x72, 0x67, 0x65, 0x10, 0x04, 0x2a, 0x37, 0x0a,
0x04, 0x54, 0x69, 0x6c, 0x65, 0x12, 0x0f, 0x0a, 0x0b, 0x54, 0x69, 0x6c, 0x65, 0x55, 0x6e, 0x6b,
0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x52, 0x6f, 0x63, 0x6b, 0x10, 0x01,
0x12, 0x0a, 0x0a, 0x06, 0x47, 0x72, 0x61, 0x76, 0x65, 0x6c, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04,
0x53, 0x61, 0x6e, 0x64, 0x10, 0x03, 0x32, 0xcf, 0x02, 0x0a, 0x04, 0x52, 0x6f, 0x76, 0x65, 0x12,
0x4d, 0x0a, 0x0c, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12,
0x1c, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72,
0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e,
0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74,
0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x72, 0x6f,
0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74,
0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x08,
0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x12, 0x18, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61,
0x70, 0x69, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x1a, 0x19, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x67,
0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12,
0x3e, 0x0a, 0x07, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x17, 0x2e, 0x72, 0x6f, 0x76,
0x65, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x6f,
0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12,
0x38, 0x0a, 0x05, 0x52, 0x61, 0x64, 0x61, 0x72, 0x12, 0x15, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61,
0x70, 0x69, 0x2e, 0x52, 0x61, 0x64, 0x61, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x16, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x61, 0x64, 0x61, 0x72, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3b, 0x0a, 0x06, 0x53, 0x74, 0x61,
0x74, 0x75, 0x73, 0x12, 0x16, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74,
0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x72, 0x6f,
0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x26, 0x5a, 0x24, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6d, 0x64, 0x69, 0x6c, 0x75, 0x7a, 0x2f, 0x72, 0x6f, 0x76, 0x65,
0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x62, 0x06,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x41,
0x0a, 0x08, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x12, 0x18, 0x2e, 0x72, 0x6f, 0x76,
0x65, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x52,
0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
0x00, 0x12, 0x3e, 0x0a, 0x07, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x17, 0x2e, 0x72,
0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e,
0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
0x00, 0x12, 0x38, 0x0a, 0x05, 0x52, 0x61, 0x64, 0x61, 0x72, 0x12, 0x15, 0x2e, 0x72, 0x6f, 0x76,
0x65, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x61, 0x64, 0x61, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x1a, 0x16, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x61, 0x64, 0x61,
0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3b, 0x0a, 0x06, 0x53,
0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x2e, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e,
0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e,
0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x26, 0x5a, 0x24, 0x67, 0x69, 0x74, 0x68,
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6d, 0x64, 0x69, 0x6c, 0x75, 0x7a, 0x2f, 0x72, 0x6f,
0x76, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x72, 0x6f, 0x76, 0x65, 0x61, 0x70, 0x69,
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (

View file

@ -151,11 +151,14 @@ enum Object {
// RoverLive represents a live rover
RoverLive = 1;
// RoverDormant describes a dormant rover
RoverDormant = 2;
// RockSmall is a small stashable rock
RockSmall = 2;
RockSmall = 3;
// RockLarge is a large blocking rock
RockLarge = 3;
RockLarge = 4;
}
enum Tile {