DEV 953: Add basic maps support to CLI (#74)
* remove squad support and switch to using pipelines only in RulesBuilder * remove spawn_food.standard from legacy ruleset definitions * bugfix: Royale map generates Standard food * add maps support to CLI * add automated tests for all registered GameMap implementations * update README
This commit is contained in:
parent
3bd1e47bb4
commit
1adbc79168
27 changed files with 565 additions and 1371 deletions
|
|
@ -19,11 +19,10 @@ func TestSetupBoard_Error(t *testing.T) {
|
|||
Id: t.Name(),
|
||||
Error: errors.New("bad map update"),
|
||||
}
|
||||
RegisterMap(testMap.ID(), testMap)
|
||||
|
||||
_, err := SetupBoard(testMap.ID(), rules.Settings{}, 10, 10, []string{})
|
||||
|
||||
require.EqualError(t, err, "bad map update")
|
||||
TestMap(testMap.ID(), testMap, func() {
|
||||
_, err := SetupBoard(testMap.ID(), rules.Settings{}, 10, 10, []string{})
|
||||
require.EqualError(t, err, "bad map update")
|
||||
})
|
||||
}
|
||||
|
||||
func TestSetupBoard(t *testing.T) {
|
||||
|
|
@ -42,26 +41,27 @@ func TestSetupBoard(t *testing.T) {
|
|||
{X: 2, Y: 2},
|
||||
},
|
||||
}
|
||||
RegisterMap(testMap.ID(), testMap)
|
||||
|
||||
boardState, err := SetupBoard(testMap.ID(), rules.Settings{}, 10, 10, []string{"1", "2"})
|
||||
TestMap(testMap.ID(), testMap, func() {
|
||||
boardState, err := SetupBoard(testMap.ID(), rules.Settings{}, 10, 10, []string{"1", "2"})
|
||||
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Len(t, boardState.Snakes, 2)
|
||||
require.Len(t, boardState.Snakes, 2)
|
||||
|
||||
require.Equal(t, rules.Snake{
|
||||
ID: "1",
|
||||
Body: []rules.Point{{X: 3, Y: 4}, {X: 3, Y: 4}, {X: 3, Y: 4}},
|
||||
Health: rules.SnakeMaxHealth,
|
||||
}, boardState.Snakes[0])
|
||||
require.Equal(t, rules.Snake{
|
||||
ID: "2",
|
||||
Body: []rules.Point{{X: 6, Y: 2}, {X: 6, Y: 2}, {X: 6, Y: 2}},
|
||||
Health: rules.SnakeMaxHealth,
|
||||
}, boardState.Snakes[1])
|
||||
require.Equal(t, []rules.Point{{X: 1, Y: 1}, {X: 5, Y: 3}}, boardState.Food)
|
||||
require.Equal(t, []rules.Point{{X: 3, Y: 5}, {X: 2, Y: 2}}, boardState.Hazards)
|
||||
require.Equal(t, rules.Snake{
|
||||
ID: "1",
|
||||
Body: []rules.Point{{X: 3, Y: 4}, {X: 3, Y: 4}, {X: 3, Y: 4}},
|
||||
Health: rules.SnakeMaxHealth,
|
||||
}, boardState.Snakes[0])
|
||||
require.Equal(t, rules.Snake{
|
||||
ID: "2",
|
||||
Body: []rules.Point{{X: 6, Y: 2}, {X: 6, Y: 2}, {X: 6, Y: 2}},
|
||||
Health: rules.SnakeMaxHealth,
|
||||
}, boardState.Snakes[1])
|
||||
require.Equal(t, []rules.Point{{X: 1, Y: 1}, {X: 5, Y: 3}}, boardState.Food)
|
||||
require.Equal(t, []rules.Point{{X: 3, Y: 5}, {X: 2, Y: 2}}, boardState.Hazards)
|
||||
})
|
||||
}
|
||||
|
||||
func TestUpdateBoard(t *testing.T) {
|
||||
|
|
@ -80,7 +80,6 @@ func TestUpdateBoard(t *testing.T) {
|
|||
{X: 2, Y: 2},
|
||||
},
|
||||
}
|
||||
RegisterMap(testMap.ID(), testMap)
|
||||
|
||||
previousBoardState := &rules.BoardState{
|
||||
Turn: 0,
|
||||
|
|
@ -98,17 +97,20 @@ func TestUpdateBoard(t *testing.T) {
|
|||
},
|
||||
},
|
||||
}
|
||||
boardState, err := UpdateBoard(testMap.ID(), previousBoardState, rules.Settings{})
|
||||
|
||||
require.NoError(t, err)
|
||||
TestMap(testMap.ID(), testMap, func() {
|
||||
boardState, err := UpdateBoard(testMap.ID(), previousBoardState, rules.Settings{})
|
||||
|
||||
require.Len(t, boardState.Snakes, 1)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, rules.Snake{
|
||||
ID: "1",
|
||||
Body: []rules.Point{{X: 6, Y: 4}, {X: 6, Y: 3}, {X: 6, Y: 2}},
|
||||
Health: rules.SnakeMaxHealth,
|
||||
}, boardState.Snakes[0])
|
||||
require.Equal(t, []rules.Point{{X: 0, Y: 1}, {X: 1, Y: 1}, {X: 5, Y: 3}}, boardState.Food)
|
||||
require.Equal(t, []rules.Point{{X: 3, Y: 4}, {X: 3, Y: 5}, {X: 2, Y: 2}}, boardState.Hazards)
|
||||
require.Len(t, boardState.Snakes, 1)
|
||||
|
||||
require.Equal(t, rules.Snake{
|
||||
ID: "1",
|
||||
Body: []rules.Point{{X: 6, Y: 4}, {X: 6, Y: 3}, {X: 6, Y: 2}},
|
||||
Health: rules.SnakeMaxHealth,
|
||||
}, boardState.Snakes[0])
|
||||
require.Equal(t, []rules.Point{{X: 0, Y: 1}, {X: 1, Y: 1}, {X: 5, Y: 3}}, boardState.Food)
|
||||
require.Equal(t, []rules.Point{{X: 3, Y: 4}, {X: 3, Y: 5}, {X: 2, Y: 2}}, boardState.Hazards)
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,3 +38,9 @@ func GetMap(id string) (GameMap, error) {
|
|||
func RegisterMap(id string, m GameMap) {
|
||||
globalRegistry.RegisterMap(id, m)
|
||||
}
|
||||
|
||||
func TestMap(id string, m GameMap, callback func()) {
|
||||
globalRegistry[id] = m
|
||||
callback()
|
||||
delete(globalRegistry, id)
|
||||
}
|
||||
|
|
|
|||
68
maps/registry_test.go
Normal file
68
maps/registry_test.go
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
package maps
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/BattlesnakeOfficial/rules"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const maxBoardWidth, maxBoardHeight = 25, 25
|
||||
|
||||
var testSettings rules.Settings = rules.Settings{
|
||||
FoodSpawnChance: 25,
|
||||
MinimumFood: 1,
|
||||
HazardDamagePerTurn: 14,
|
||||
RoyaleSettings: rules.RoyaleSettings{
|
||||
ShrinkEveryNTurns: 1,
|
||||
},
|
||||
}
|
||||
|
||||
func TestRegisteredMaps(t *testing.T) {
|
||||
for mapName, gameMap := range globalRegistry {
|
||||
t.Run(mapName, func(t *testing.T) {
|
||||
require.Equalf(t, mapName, gameMap.ID(), "%#v game map doesn't return its own ID", mapName)
|
||||
|
||||
var setupBoardState *rules.BoardState
|
||||
|
||||
for width := 0; width < maxBoardWidth; width++ {
|
||||
for height := 0; height < maxBoardHeight; height++ {
|
||||
initialBoardState := rules.NewBoardState(width, height)
|
||||
initialBoardState.Snakes = append(initialBoardState.Snakes, rules.Snake{ID: "1", Body: []rules.Point{}})
|
||||
initialBoardState.Snakes = append(initialBoardState.Snakes, rules.Snake{ID: "2", Body: []rules.Point{}})
|
||||
passedBoardState := initialBoardState.Clone()
|
||||
tempBoardState := initialBoardState.Clone()
|
||||
err := gameMap.SetupBoard(passedBoardState, testSettings, NewBoardStateEditor(tempBoardState))
|
||||
if err == nil {
|
||||
setupBoardState = tempBoardState
|
||||
require.Equal(t, initialBoardState, passedBoardState, "BoardState should not be modified directly by GameMap.SetupBoard")
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
require.NotNil(t, setupBoardState, "Map does not successfully setup the board at any supported combination of width and height")
|
||||
require.NotNil(t, setupBoardState.Food)
|
||||
require.NotNil(t, setupBoardState.Hazards)
|
||||
require.NotNil(t, setupBoardState.Snakes)
|
||||
for _, snake := range setupBoardState.Snakes {
|
||||
require.NotEmpty(t, snake.Body, "Map should place all snakes by initializing their body")
|
||||
}
|
||||
|
||||
previousBoardState := rules.NewBoardState(rules.BoardSizeMedium, rules.BoardSizeMedium)
|
||||
previousBoardState.Food = append(previousBoardState.Food, []rules.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}...)
|
||||
previousBoardState.Hazards = append(previousBoardState.Food, []rules.Point{{X: 4, Y: 3}, {X: 2, Y: 1}}...)
|
||||
previousBoardState.Snakes = append(previousBoardState.Snakes, rules.Snake{
|
||||
ID: "1",
|
||||
Body: []rules.Point{{X: 5, Y: 5}, {X: 5, Y: 4}, {X: 5, Y: 3}},
|
||||
Health: 100,
|
||||
})
|
||||
previousBoardState.Turn = 0
|
||||
|
||||
passedBoardState := previousBoardState.Clone()
|
||||
tempBoardState := previousBoardState.Clone()
|
||||
err := gameMap.UpdateBoard(passedBoardState, testSettings, NewBoardStateEditor(tempBoardState))
|
||||
require.NoError(t, err, "GameMap.UpdateBoard returned an error")
|
||||
require.Equal(t, previousBoardState, passedBoardState, "BoardState should not be modified directly by GameMap.UpdateBoard")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -29,6 +29,11 @@ func (m RoyaleHazardsMap) SetupBoard(lastBoardState *rules.BoardState, settings
|
|||
}
|
||||
|
||||
func (m RoyaleHazardsMap) UpdateBoard(lastBoardState *rules.BoardState, settings rules.Settings, editor Editor) error {
|
||||
// Use StandardMap to populate food
|
||||
if err := (StandardMap{}).UpdateBoard(lastBoardState, settings, editor); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Royale uses the current turn to generate hazards, not the previous turn that's in the board state
|
||||
turn := lastBoardState.Turn + 1
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue