DEV-1313: Add additional map types (#76)

* add helper to draw a ring of hazards

* refactor tests to not be internal tests

* add "hz_inner_wall" map

* add "hz_rings" map

* fix map registry

* fix: edge case bugs in drawRing

* remove println

* add "hz_columns"

* add "hz_rivers_bridges" map

* WIP: implementing spiral hazards map

* finish basic testing of 'hz_spiral'

* include first turn

* add "hz_hazards" map

* remove incorrect author

* add "hz_grow_box" map

* add "hz_expand_box" map

* add "hz_expand_scatter" map

* remove debug

* document the new "Range" method

* - use rules.RulesetError instead of generic error
- use a rules.Point for map rivers and bridgets map key

* use rules.RulesetError instead of errors.New

* provide more detail about boundar conditions

* fix documentation (max can be == min)

* add unit tests
This commit is contained in:
Torben 2022-06-01 11:39:31 -07:00 committed by GitHub
parent aa38bcd0eb
commit f0dc0bcb38
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 1129 additions and 24 deletions

217
maps/hazards_test.go Normal file
View file

@ -0,0 +1,217 @@
package maps_test
import (
"fmt"
"testing"
"github.com/BattlesnakeOfficial/rules"
"github.com/BattlesnakeOfficial/rules/maps"
"github.com/stretchr/testify/require"
)
func TestInnerBorderHazardsMap(t *testing.T) {
tests := []struct {
boardSize int
expectedHazards int
}{
{11, 32},
{19, 64},
{25, 88},
}
for _, tc := range tests {
t.Run(fmt.Sprintf("%dx%d", tc.boardSize, tc.boardSize), func(t *testing.T) {
m := maps.InnerBorderHazardsMap{}
state := rules.NewBoardState(tc.boardSize, tc.boardSize)
settings := rules.Settings{}
// ensure the ring of hazards is added to the board at setup
editor := maps.NewBoardStateEditor(state)
require.Empty(t, state.Hazards)
err := m.SetupBoard(state, settings, editor)
require.NoError(t, err)
require.NotEmpty(t, state.Hazards)
require.Len(t, state.Hazards, tc.expectedHazards)
})
}
}
func TestConcentricRingsHazardsMap(t *testing.T) {
tests := []struct {
boardSize int
expectedHazards int
}{
{11, 48},
}
for _, tc := range tests {
t.Run(fmt.Sprintf("%dx%d", tc.boardSize, tc.boardSize), func(t *testing.T) {
m := maps.ConcentricRingsHazardsMap{}
state := rules.NewBoardState(tc.boardSize, tc.boardSize)
settings := rules.Settings{}
// ensure the ring of hazards is added to the board at setup
editor := maps.NewBoardStateEditor(state)
require.Empty(t, state.Hazards)
err := m.SetupBoard(state, settings, editor)
require.NoError(t, err)
require.NotEmpty(t, state.Hazards)
require.Len(t, state.Hazards, tc.expectedHazards)
})
}
}
func TestColumnsHazardsMap(t *testing.T) {
m := maps.ColumnsHazardsMap{}
state := rules.NewBoardState(11, 11)
settings := rules.Settings{}
editor := maps.NewBoardStateEditor(state)
require.Empty(t, state.Hazards)
err := m.SetupBoard(state, settings, editor)
require.NoError(t, err)
require.NotEmpty(t, state.Hazards)
require.Len(t, state.Hazards, 25)
// a few spot checks
require.Contains(t, state.Hazards, rules.Point{X: 1, Y: 1})
require.Contains(t, state.Hazards, rules.Point{X: 1, Y: 5})
require.Contains(t, state.Hazards, rules.Point{X: 9, Y: 1})
require.Contains(t, state.Hazards, rules.Point{X: 9, Y: 9})
require.NotContains(t, state.Hazards, rules.Point{X: 0, Y: 1})
require.NotContains(t, state.Hazards, rules.Point{X: 8, Y: 4})
require.NotContains(t, state.Hazards, rules.Point{X: 2, Y: 2})
require.NotContains(t, state.Hazards, rules.Point{X: 4, Y: 9})
require.NotContains(t, state.Hazards, rules.Point{X: 1, Y: 0})
}
func TestRiversAndBridgetsHazardsMap(t *testing.T) {
// check error handling
m := maps.RiverAndBridgesHazardsMap{}
settings := rules.Settings{}
// check error for unsupported board sizes
state := rules.NewBoardState(9, 9)
editor := maps.NewBoardStateEditor(state)
err := m.SetupBoard(state, settings, editor)
require.Error(t, err)
// check all the supported sizes
for _, size := range []int{11, 19, 25} {
state = rules.NewBoardState(size, size)
editor = maps.NewBoardStateEditor(state)
require.Empty(t, state.Hazards)
err = m.SetupBoard(state, settings, editor)
require.NoError(t, err)
require.NotEmpty(t, state.Hazards)
}
}
func TestSpiralHazardsMap(t *testing.T) {
// check error handling
m := maps.SpiralHazardsMap{}
settings := rules.Settings{}
settings = settings.WithSeed(10)
state := rules.NewBoardState(11, 11)
editor := maps.NewBoardStateEditor(state)
err := m.SetupBoard(state, settings, editor)
require.NoError(t, err)
for i := 0; i < 1000; i++ {
state.Turn = i
err = m.UpdateBoard(state, settings, editor)
require.NoError(t, err)
}
require.NotEmpty(t, state.Hazards)
require.Equal(t, 11*11, len(state.Hazards), "hazards should eventually fille the entire map")
}
func TestScatterFillMap(t *testing.T) {
// check error handling
m := maps.ScatterFillMap{}
settings := rules.Settings{}
settings = settings.WithSeed(10)
state := rules.NewBoardState(11, 11)
editor := maps.NewBoardStateEditor(state)
err := m.SetupBoard(state, settings, editor)
require.NoError(t, err)
totalTurns := 11 * 11 * 2
for i := 0; i < totalTurns; i++ {
state.Turn = i
err = m.UpdateBoard(state, settings, editor)
require.NoError(t, err)
}
require.NotEmpty(t, state.Hazards)
require.Equal(t, 11*11, len(state.Hazards), "hazards should eventually fill the entire map")
}
func TestDirectionalExpandingBoxMap(t *testing.T) {
// check error handling
m := maps.DirectionalExpandingBoxMap{}
settings := rules.Settings{}
settings = settings.WithSeed(2)
state := rules.NewBoardState(11, 11)
editor := maps.NewBoardStateEditor(state)
err := m.SetupBoard(state, settings, editor)
require.NoError(t, err)
totalTurns := 1000
for i := 0; i < totalTurns; i++ {
state.Turn = i
err = m.UpdateBoard(state, settings, editor)
require.NoError(t, err)
}
require.NotEmpty(t, state.Hazards)
require.Equal(t, 11*11, len(state.Hazards), "hazards should eventually fill the entire map")
}
func TestExpandingBoxMap(t *testing.T) {
// check error handling
m := maps.ExpandingBoxMap{}
settings := rules.Settings{}
settings = settings.WithSeed(2)
state := rules.NewBoardState(11, 11)
editor := maps.NewBoardStateEditor(state)
err := m.SetupBoard(state, settings, editor)
require.NoError(t, err)
totalTurns := 1000
for i := 0; i < totalTurns; i++ {
state.Turn = i
err = m.UpdateBoard(state, settings, editor)
require.NoError(t, err)
}
require.NotEmpty(t, state.Hazards)
require.Equal(t, 11*11, len(state.Hazards), "hazards should eventually fill the entire map")
}
func TestExpandingScatterMap(t *testing.T) {
// check error handling
m := maps.ExpandingScatterMap{}
settings := rules.Settings{}
settings = settings.WithSeed(2)
state := rules.NewBoardState(11, 11)
editor := maps.NewBoardStateEditor(state)
err := m.SetupBoard(state, settings, editor)
require.NoError(t, err)
totalTurns := 1000
for i := 0; i < totalTurns; i++ {
state.Turn = i
err = m.UpdateBoard(state, settings, editor)
require.NoError(t, err)
}
require.NotEmpty(t, state.Hazards)
require.Equal(t, 11*11, len(state.Hazards), "hazards should eventually fill the entire map")
}