Byte-snake-engine/maps/registry_test.go
Rob O'Dwyer 82e1999126
DEV-1761: New rules API (#118)
* DEV-1761: Clean up Ruleset interface (#115)

* remove legacy ruleset types and simplify ruleset interface

* remove unnecessary settings argument from Ruleset interface

* decouple rules.Settings from client API and store settings as strings

* DEV 1761: Add new BoardState and Point fields (#117)

* add Point.TTL, Point.Value, GameState and PointState to BoardState

* allow maps to access BoardState.GameState,PointState

* add PreUpdateBoard and refactor snail_mode with it

* fix bug where an extra turn was printed to the console

* fix formatting

* fix lint errors

Co-authored-by: JonathanArns <jonathan.arns@googlemail.com>
2022-10-28 16:49:49 -07:00

124 lines
5.2 KiB
Go

package maps
import (
"fmt"
"testing"
"github.com/BattlesnakeOfficial/rules"
"github.com/stretchr/testify/require"
)
const maxBoardWidth, maxBoardHeight = 25, 25
var testSettings rules.Settings = rules.NewSettings(map[string]string{
rules.ParamFoodSpawnChance: "25",
rules.ParamMinimumFood: "1",
rules.ParamHazardDamagePerTurn: "14",
rules.ParamShrinkEveryNTurns: "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)
meta := gameMap.Meta()
require.True(t, meta.Version > 0, fmt.Sprintf("registered maps must have a valid version (>= 1) - '%d' is invalid", meta.Version))
require.NotZero(t, meta.MaxPlayers, "registered maps must have maximum players declared")
require.LessOrEqual(t, meta.MaxPlayers, meta.MaxPlayers, "max players should always be >= min players")
require.NotEmpty(t, meta.BoardSizes, "registered maps must have at least one supported size declared")
require.NotNil(t, meta.Tags)
var setupBoardState *rules.BoardState
// "fuzz test" supported players
mapSize := pickSize(meta)
for i := meta.MinPlayers; i < meta.MaxPlayers; i++ {
t.Run(fmt.Sprintf("%d players", i), func(t *testing.T) {
initialBoardState := rules.NewBoardState(int(mapSize.Width), int(mapSize.Height))
for j := 0; j < i; j++ {
initialBoardState.Snakes = append(initialBoardState.Snakes, rules.Snake{ID: fmt.Sprint(j), Body: []rules.Point{}})
}
err := gameMap.SetupBoard(initialBoardState, testSettings, NewBoardStateEditor(initialBoardState))
require.NoError(t, err, fmt.Sprintf("%d players should be supported by this map", i))
})
}
// "fuzz test" supported map sizes
if !meta.BoardSizes.IsUnlimited() {
for _, mapSize := range meta.BoardSizes {
t.Run(fmt.Sprintf("%dx%d map size", mapSize.Width, mapSize.Height), func(t *testing.T) {
initialBoardState := rules.NewBoardState(int(mapSize.Width), int(mapSize.Height))
for i := 0; i < meta.MaxPlayers; i++ {
initialBoardState.Snakes = append(initialBoardState.Snakes, rules.Snake{ID: fmt.Sprint(i), Body: []rules.Point{}})
}
err := gameMap.SetupBoard(initialBoardState, testSettings, NewBoardStateEditor(initialBoardState))
require.NoError(t, err, "error setting up map")
})
}
}
// Check that at least one map size can be setup without error
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{}})
if meta.MaxPlayers > 1 {
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.PostUpdateBoard(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")
})
}
}
func pickSize(meta Metadata) Dimensions {
// For unlimited, we can pick any size
if meta.BoardSizes.IsUnlimited() {
return Dimensions{Width: 11, Height: 11}
}
// For fixed, just pick the first supported size
return meta.BoardSizes[0]
}
func TestListRegisteredMaps(t *testing.T) {
keys := globalRegistry.List()
mapCount := 0
for k := range globalRegistry {
// every registry key should exist in List results
require.Contains(t, keys, k)
mapCount++
}
// List should equal number of maps in the global registry
require.Equal(t, len(keys), mapCount)
}