DEV 1676: Add maps helper functions (#111)
* add utility methods to Editor and BoardStateEditor * add Meta.Validate * allow setting Meta.MinPlayers to zero * remove uints in map sizes * use Meta.Validate in HazardPitsMap
This commit is contained in:
parent
c5810d8604
commit
c4247945ca
7 changed files with 701 additions and 83 deletions
|
|
@ -7,6 +7,110 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMetadataValidate(t *testing.T) {
|
||||
for label, test := range map[string]struct {
|
||||
metadata Metadata
|
||||
boardState *rules.BoardState
|
||||
expected error
|
||||
}{
|
||||
"unlimited": {
|
||||
Metadata{
|
||||
BoardSizes: AnySize(),
|
||||
},
|
||||
rules.NewBoardState(99, 99),
|
||||
nil,
|
||||
},
|
||||
"in sizes": {
|
||||
Metadata{
|
||||
BoardSizes: OddSizes(7, 25),
|
||||
},
|
||||
rules.NewBoardState(7, 7),
|
||||
nil,
|
||||
},
|
||||
"too small": {
|
||||
Metadata{
|
||||
BoardSizes: OddSizes(7, 25),
|
||||
},
|
||||
rules.NewBoardState(6, 6),
|
||||
rules.RulesetError("This map can only be played on these board sizes: 7x7, 9x9, 11x11, 13x13, 15x15, 17x17, 19x19, 21x21, 23x23, 25x25"),
|
||||
},
|
||||
"too large": {
|
||||
Metadata{
|
||||
BoardSizes: OddSizes(7, 25),
|
||||
},
|
||||
rules.NewBoardState(26, 26),
|
||||
rules.RulesetError("This map can only be played on these board sizes: 7x7, 9x9, 11x11, 13x13, 15x15, 17x17, 19x19, 21x21, 23x23, 25x25"),
|
||||
},
|
||||
"valid players": {
|
||||
Metadata{
|
||||
BoardSizes: AnySize(),
|
||||
MinPlayers: 4,
|
||||
MaxPlayers: 4,
|
||||
},
|
||||
&rules.BoardState{
|
||||
Snakes: []rules.Snake{
|
||||
{ID: "1"},
|
||||
{ID: "2"},
|
||||
{ID: "3"},
|
||||
{ID: "4"},
|
||||
},
|
||||
},
|
||||
nil,
|
||||
},
|
||||
"too few players": {
|
||||
Metadata{
|
||||
BoardSizes: AnySize(),
|
||||
MinPlayers: 3,
|
||||
MaxPlayers: 4,
|
||||
},
|
||||
&rules.BoardState{
|
||||
Snakes: []rules.Snake{
|
||||
{ID: "1"},
|
||||
{ID: "2"},
|
||||
},
|
||||
},
|
||||
rules.RulesetError("This map can only be played with 3-4 players"),
|
||||
},
|
||||
"too many players": {
|
||||
Metadata{
|
||||
BoardSizes: AnySize(),
|
||||
MinPlayers: 3,
|
||||
MaxPlayers: 4,
|
||||
},
|
||||
&rules.BoardState{
|
||||
Snakes: []rules.Snake{
|
||||
{ID: "1"},
|
||||
{ID: "2"},
|
||||
{ID: "3"},
|
||||
{ID: "4"},
|
||||
{ID: "5"},
|
||||
},
|
||||
},
|
||||
rules.RulesetError("This map can only be played with 3-4 players"),
|
||||
},
|
||||
} {
|
||||
t.Run(label, func(t *testing.T) {
|
||||
actual := test.metadata.Validate(test.boardState)
|
||||
require.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapSizes(t *testing.T) {
|
||||
s := FixedSizes(Dimensions{11, 12})
|
||||
require.Equal(t, s[0].Width, 11)
|
||||
require.Equal(t, s[0].Height, 12)
|
||||
|
||||
s = FixedSizes(Dimensions{11, 11}, Dimensions{19, 25})
|
||||
require.Len(t, s, 2)
|
||||
require.Equal(t, s[1].Width, 19)
|
||||
require.Equal(t, s[1].Height, 25)
|
||||
|
||||
s = AnySize()
|
||||
require.Len(t, s, 1, "unlimited maps should have just one dimensions")
|
||||
require.True(t, s.IsUnlimited())
|
||||
}
|
||||
|
||||
func TestBoardStateEditorInterface(t *testing.T) {
|
||||
var _ Editor = (*BoardStateEditor)(nil)
|
||||
}
|
||||
|
|
@ -18,7 +122,7 @@ func TestBoardStateEditor(t *testing.T) {
|
|||
Health: 100,
|
||||
})
|
||||
|
||||
editor := BoardStateEditor{BoardState: boardState}
|
||||
editor := BoardStateEditor{boardState: boardState}
|
||||
|
||||
editor.AddFood(rules.Point{X: 1, Y: 3})
|
||||
editor.AddFood(rules.Point{X: 3, Y: 6})
|
||||
|
|
@ -56,6 +160,26 @@ func TestBoardStateEditor(t *testing.T) {
|
|||
},
|
||||
}, boardState)
|
||||
|
||||
require.Equal(t, []rules.Point{
|
||||
{X: 1, Y: 3},
|
||||
{X: 3, Y: 7},
|
||||
}, editor.Food())
|
||||
|
||||
require.Equal(t, []rules.Point{
|
||||
{X: 1, Y: 3},
|
||||
{X: 3, Y: 7},
|
||||
}, editor.Hazards())
|
||||
|
||||
require.Equal(t, map[string][]rules.Point{
|
||||
"existing_snake": {
|
||||
{X: 5, Y: 2}, {X: 5, Y: 1}, {X: 5, Y: 0},
|
||||
},
|
||||
"new_snake": {
|
||||
|
||||
{X: 0, Y: 0}, {X: 1, Y: 0}, {X: 1, Y: 1},
|
||||
},
|
||||
}, editor.SnakeBodies())
|
||||
|
||||
editor.ClearFood()
|
||||
require.Equal(t, []rules.Point{}, boardState.Food)
|
||||
|
||||
|
|
@ -63,17 +187,317 @@ func TestBoardStateEditor(t *testing.T) {
|
|||
require.Equal(t, []rules.Point{}, boardState.Hazards)
|
||||
}
|
||||
|
||||
func TestMapSizes(t *testing.T) {
|
||||
s := FixedSizes(Dimensions{11, 12})
|
||||
require.Equal(t, s[0].Width, uint(11))
|
||||
require.Equal(t, s[0].Height, uint(12))
|
||||
func TestBoardStateEditorPlaceSnakesRandomlyAtPositions(t *testing.T) {
|
||||
for label, test := range map[string]struct {
|
||||
rand rules.Rand
|
||||
initialSnakes []rules.Snake
|
||||
heads []rules.Point
|
||||
bodyLength int
|
||||
expectedError error
|
||||
expectedSnakes []rules.Snake
|
||||
}{
|
||||
"empty": {
|
||||
rules.MinRand,
|
||||
[]rules.Snake{},
|
||||
[]rules.Point{},
|
||||
0,
|
||||
nil,
|
||||
[]rules.Snake{},
|
||||
},
|
||||
"too many snakes": {
|
||||
rules.MinRand,
|
||||
[]rules.Snake{
|
||||
{ID: "1"}, {ID: "2"}, {ID: "3"},
|
||||
},
|
||||
[]rules.Point{{X: 3, Y: 3}, {X: 6, Y: 2}},
|
||||
3,
|
||||
rules.ErrorTooManySnakes,
|
||||
nil,
|
||||
},
|
||||
"success unshuffled": {
|
||||
rules.MinRand,
|
||||
[]rules.Snake{
|
||||
{ID: "1"}, {ID: "2"},
|
||||
},
|
||||
[]rules.Point{{X: 3, Y: 3}, {X: 6, Y: 2}},
|
||||
3,
|
||||
nil,
|
||||
[]rules.Snake{
|
||||
{
|
||||
ID: "1",
|
||||
Body: []rules.Point{{X: 3, Y: 3}, {X: 3, Y: 3}, {X: 3, Y: 3}},
|
||||
Health: rules.SnakeMaxHealth,
|
||||
}, {
|
||||
ID: "2",
|
||||
Body: []rules.Point{{X: 6, Y: 2}, {X: 6, Y: 2}, {X: 6, Y: 2}},
|
||||
Health: rules.SnakeMaxHealth,
|
||||
},
|
||||
},
|
||||
},
|
||||
"success shuffled": {
|
||||
rules.MaxRand,
|
||||
[]rules.Snake{
|
||||
{ID: "1"}, {ID: "2"},
|
||||
},
|
||||
[]rules.Point{{X: 3, Y: 3}, {X: 6, Y: 2}},
|
||||
3,
|
||||
nil,
|
||||
[]rules.Snake{
|
||||
{
|
||||
ID: "1",
|
||||
Body: []rules.Point{{X: 6, Y: 2}, {X: 6, Y: 2}, {X: 6, Y: 2}},
|
||||
Health: rules.SnakeMaxHealth,
|
||||
}, {
|
||||
ID: "2",
|
||||
Body: []rules.Point{{X: 3, Y: 3}, {X: 3, Y: 3}, {X: 3, Y: 3}},
|
||||
Health: rules.SnakeMaxHealth,
|
||||
},
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(label, func(t *testing.T) {
|
||||
boardState := rules.NewBoardState(rules.BoardSizeSmall, rules.BoardSizeSmall)
|
||||
boardState.Snakes = test.initialSnakes
|
||||
editor := NewBoardStateEditor(boardState)
|
||||
|
||||
s = FixedSizes(Dimensions{11, 11}, Dimensions{19, 25})
|
||||
require.Len(t, s, 2)
|
||||
require.Equal(t, s[1].Width, uint(19))
|
||||
require.Equal(t, s[1].Height, uint(25))
|
||||
|
||||
s = AnySize()
|
||||
require.Len(t, s, 1, "unlimited maps should have just one dimensions")
|
||||
require.True(t, s.IsUnlimited())
|
||||
err := editor.PlaceSnakesRandomlyAtPositions(test.rand, test.initialSnakes, test.heads, test.bodyLength)
|
||||
if test.expectedError != nil {
|
||||
require.Equal(t, test.expectedError, err)
|
||||
} else {
|
||||
require.Equal(t, test.expectedSnakes, boardState.Snakes)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBoardStateEditorIsOccupied(t *testing.T) {
|
||||
for label, test := range map[string]struct {
|
||||
boardState *rules.BoardState
|
||||
point rules.Point
|
||||
snakes, hazards, food bool
|
||||
expected bool
|
||||
}{
|
||||
"empty board": {
|
||||
rules.NewBoardState(rules.BoardSizeSmall, rules.BoardSizeSmall),
|
||||
rules.Point{X: 3, Y: 3},
|
||||
true, true, true,
|
||||
false,
|
||||
},
|
||||
"unoccupied": {
|
||||
&rules.BoardState{
|
||||
Food: []rules.Point{{X: 1, Y: 1}},
|
||||
Hazards: []rules.Point{{X: 2, Y: 2}},
|
||||
Snakes: []rules.Snake{
|
||||
{
|
||||
ID: "1",
|
||||
Body: []rules.Point{{X: 3, Y: 3}},
|
||||
},
|
||||
},
|
||||
},
|
||||
rules.Point{X: 2, Y: 3},
|
||||
true, true, true,
|
||||
false,
|
||||
},
|
||||
"food": {
|
||||
&rules.BoardState{
|
||||
Food: []rules.Point{{X: 1, Y: 1}},
|
||||
},
|
||||
rules.Point{X: 1, Y: 1},
|
||||
false, false, true,
|
||||
true,
|
||||
},
|
||||
"ignored food": {
|
||||
&rules.BoardState{
|
||||
Food: []rules.Point{{X: 1, Y: 1}},
|
||||
},
|
||||
rules.Point{X: 1, Y: 1},
|
||||
false, false, false,
|
||||
false,
|
||||
},
|
||||
"hazard": {
|
||||
&rules.BoardState{
|
||||
Hazards: []rules.Point{{X: 1, Y: 1}},
|
||||
},
|
||||
rules.Point{X: 1, Y: 1},
|
||||
false, true, false,
|
||||
true,
|
||||
},
|
||||
"ignored hazard": {
|
||||
&rules.BoardState{
|
||||
Food: []rules.Point{{X: 1, Y: 1}},
|
||||
},
|
||||
rules.Point{X: 1, Y: 1},
|
||||
false, false, false,
|
||||
false,
|
||||
},
|
||||
"snake": {
|
||||
&rules.BoardState{
|
||||
Snakes: []rules.Snake{
|
||||
{
|
||||
ID: "1",
|
||||
Body: []rules.Point{{X: 1, Y: 1}},
|
||||
},
|
||||
},
|
||||
},
|
||||
rules.Point{X: 1, Y: 1},
|
||||
true, false, false,
|
||||
true,
|
||||
},
|
||||
"ignored snake": {
|
||||
&rules.BoardState{
|
||||
Snakes: []rules.Snake{
|
||||
{
|
||||
ID: "1",
|
||||
Body: []rules.Point{{X: 1, Y: 1}},
|
||||
},
|
||||
},
|
||||
},
|
||||
rules.Point{X: 1, Y: 1},
|
||||
false, false, false,
|
||||
false,
|
||||
},
|
||||
} {
|
||||
t.Run(label, func(t *testing.T) {
|
||||
editor := NewBoardStateEditor(test.boardState)
|
||||
|
||||
actual := editor.IsOccupied(test.point, test.snakes, test.hazards, test.food)
|
||||
|
||||
require.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBoardStateEditorOccupiedPoints(t *testing.T) {
|
||||
testBoardState := &rules.BoardState{
|
||||
Food: []rules.Point{{X: 1, Y: 1}},
|
||||
Hazards: []rules.Point{{X: 2, Y: 2}},
|
||||
Snakes: []rules.Snake{
|
||||
{
|
||||
ID: "1",
|
||||
Body: []rules.Point{{X: 3, Y: 3}},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for label, test := range map[string]struct {
|
||||
boardState *rules.BoardState
|
||||
snakes, hazards, food bool
|
||||
expected map[rules.Point]bool
|
||||
}{
|
||||
"empty board": {
|
||||
rules.NewBoardState(rules.BoardSizeSmall, rules.BoardSizeSmall),
|
||||
true, true, true,
|
||||
map[rules.Point]bool{},
|
||||
},
|
||||
"all types": {
|
||||
testBoardState,
|
||||
true, true, true,
|
||||
map[rules.Point]bool{
|
||||
{X: 1, Y: 1}: true,
|
||||
{X: 2, Y: 2}: true,
|
||||
{X: 3, Y: 3}: true,
|
||||
},
|
||||
},
|
||||
"ignore snakes": {
|
||||
testBoardState,
|
||||
false, true, true,
|
||||
map[rules.Point]bool{
|
||||
{X: 1, Y: 1}: true,
|
||||
{X: 2, Y: 2}: true,
|
||||
},
|
||||
},
|
||||
"ignore hazards": {
|
||||
testBoardState,
|
||||
true, false, true,
|
||||
map[rules.Point]bool{
|
||||
{X: 1, Y: 1}: true,
|
||||
{X: 3, Y: 3}: true,
|
||||
},
|
||||
},
|
||||
"ignore food": {
|
||||
testBoardState,
|
||||
true, true, false,
|
||||
map[rules.Point]bool{
|
||||
{X: 2, Y: 2}: true,
|
||||
{X: 3, Y: 3}: true,
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(label, func(t *testing.T) {
|
||||
editor := NewBoardStateEditor(test.boardState)
|
||||
|
||||
actual := editor.OccupiedPoints(test.snakes, test.hazards, test.food)
|
||||
|
||||
require.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBoardStateEditorFilterUnoccupiedPoints(t *testing.T) {
|
||||
testBoardState := &rules.BoardState{
|
||||
Food: []rules.Point{{X: 1, Y: 1}},
|
||||
Hazards: []rules.Point{{X: 2, Y: 2}},
|
||||
Snakes: []rules.Snake{
|
||||
{
|
||||
ID: "1",
|
||||
Body: []rules.Point{{X: 3, Y: 3}},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for label, test := range map[string]struct {
|
||||
boardState *rules.BoardState
|
||||
targets []rules.Point
|
||||
snakes, hazards, food bool
|
||||
expected []rules.Point
|
||||
}{
|
||||
"empty": {
|
||||
rules.NewBoardState(rules.BoardSizeSmall, rules.BoardSizeSmall),
|
||||
[]rules.Point{},
|
||||
true, true, true,
|
||||
[]rules.Point{},
|
||||
},
|
||||
"all types": {
|
||||
testBoardState,
|
||||
[]rules.Point{{X: 3, Y: 3}, {X: 1, Y: 1}, {X: 2, Y: 2}, {X: 2, Y: 1}},
|
||||
true, true, true,
|
||||
[]rules.Point{{X: 2, Y: 1}},
|
||||
},
|
||||
"ignore snakes": {
|
||||
testBoardState,
|
||||
[]rules.Point{{X: 3, Y: 3}, {X: 1, Y: 1}, {X: 2, Y: 2}, {X: 2, Y: 1}},
|
||||
false, true, true,
|
||||
[]rules.Point{{X: 3, Y: 3}, {X: 2, Y: 1}},
|
||||
},
|
||||
"ignore hazards": {
|
||||
testBoardState,
|
||||
[]rules.Point{{X: 3, Y: 3}, {X: 1, Y: 1}, {X: 2, Y: 2}, {X: 2, Y: 1}},
|
||||
true, false, true,
|
||||
[]rules.Point{{X: 2, Y: 2}, {X: 2, Y: 1}},
|
||||
},
|
||||
"ignore food": {
|
||||
testBoardState,
|
||||
[]rules.Point{{X: 3, Y: 3}, {X: 1, Y: 1}, {X: 2, Y: 2}, {X: 2, Y: 1}},
|
||||
true, true, false,
|
||||
[]rules.Point{{X: 1, Y: 1}, {X: 2, Y: 1}},
|
||||
},
|
||||
} {
|
||||
t.Run(label, func(t *testing.T) {
|
||||
editor := NewBoardStateEditor(test.boardState)
|
||||
|
||||
actual := editor.FilterUnoccupiedPoints(test.targets, test.snakes, test.hazards, test.food)
|
||||
|
||||
require.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBoardStateEditorShufflePoints(t *testing.T) {
|
||||
editor := NewBoardStateEditor(rules.NewBoardState(rules.BoardSizeSmall, rules.BoardSizeSmall))
|
||||
points := []rules.Point{{X: 4, Y: 0}, {X: 3, Y: 1}, {X: 2, Y: 2}, {X: 1, Y: 3}, {X: 0, Y: 4}}
|
||||
|
||||
editor.ShufflePoints(rules.MaxRand, points)
|
||||
expected := []rules.Point{{X: 3, Y: 1}, {X: 2, Y: 2}, {X: 1, Y: 3}, {X: 0, Y: 4}, {X: 4, Y: 0}}
|
||||
|
||||
require.Equal(t, expected, points)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue