Never spawn food within one move of any snake.

This commit is contained in:
bvanvugt 2020-11-10 13:00:13 -08:00
parent f5aec61e04
commit c6d9ba12ab
3 changed files with 48 additions and 16 deletions

1
go.sum
View file

@ -2,6 +2,7 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=

View file

@ -157,7 +157,7 @@ func (r *StandardRuleset) placeFoodFixed(b *BoardState) error {
// Finally, always place 1 food in center of board for dramatic purposes // Finally, always place 1 food in center of board for dramatic purposes
isCenterOccupied := true isCenterOccupied := true
centerCoord := Point{(b.Width - 1) / 2, (b.Height - 1) / 2} centerCoord := Point{(b.Width - 1) / 2, (b.Height - 1) / 2}
unoccupiedPoints := r.getUnoccupiedPoints(b) unoccupiedPoints := r.getUnoccupiedPoints(b, true)
for _, point := range unoccupiedPoints { for _, point := range unoccupiedPoints {
if point == centerCoord { if point == centerCoord {
isCenterOccupied = false isCenterOccupied = false
@ -526,7 +526,7 @@ func (r *StandardRuleset) maybeSpawnFood(b *BoardState) error {
func (r *StandardRuleset) spawnFood(b *BoardState, n int) error { func (r *StandardRuleset) spawnFood(b *BoardState, n int) error {
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
unoccupiedPoints := r.getUnoccupiedPoints(b) unoccupiedPoints := r.getUnoccupiedPoints(b, false)
if len(unoccupiedPoints) > 0 { if len(unoccupiedPoints) > 0 {
newFood := unoccupiedPoints[rand.Intn(len(unoccupiedPoints))] newFood := unoccupiedPoints[rand.Intn(len(unoccupiedPoints))]
b.Food = append(b.Food, newFood) b.Food = append(b.Food, newFood)
@ -535,7 +535,7 @@ func (r *StandardRuleset) spawnFood(b *BoardState, n int) error {
return nil return nil
} }
func (r *StandardRuleset) getUnoccupiedPoints(b *BoardState) []Point { func (r *StandardRuleset) getUnoccupiedPoints(b *BoardState, includePossibleMoves bool) []Point {
pointIsOccupied := map[int32]map[int32]bool{} pointIsOccupied := map[int32]map[int32]bool{}
for _, p := range b.Food { for _, p := range b.Food {
if _, xExists := pointIsOccupied[p.X]; !xExists { if _, xExists := pointIsOccupied[p.X]; !xExists {
@ -544,11 +544,26 @@ func (r *StandardRuleset) getUnoccupiedPoints(b *BoardState) []Point {
pointIsOccupied[p.X][p.Y] = true pointIsOccupied[p.X][p.Y] = true
} }
for _, snake := range b.Snakes { for _, snake := range b.Snakes {
for _, p := range snake.Body { for i, p := range snake.Body {
if _, xExists := pointIsOccupied[p.X]; !xExists { if _, xExists := pointIsOccupied[p.X]; !xExists {
pointIsOccupied[p.X] = map[int32]bool{} pointIsOccupied[p.X] = map[int32]bool{}
} }
pointIsOccupied[p.X][p.Y] = true pointIsOccupied[p.X][p.Y] = true
if i == 0 && !includePossibleMoves {
nextMovePoints := []Point{
{X: p.X - 1, Y: p.Y},
{X: p.X + 1, Y: p.Y},
{X: p.X, Y: p.Y - 1},
{X: p.X, Y: p.Y + 1},
}
for _, nextP := range nextMovePoints {
if _, xExists := pointIsOccupied[nextP.X]; !xExists {
pointIsOccupied[nextP.X] = map[int32]bool{}
}
pointIsOccupied[nextP.X][nextP.Y] = true
}
}
} }
} }
@ -570,7 +585,7 @@ func (r *StandardRuleset) getUnoccupiedPoints(b *BoardState) []Point {
func (r *StandardRuleset) getEvenUnoccupiedPoints(b *BoardState) []Point { func (r *StandardRuleset) getEvenUnoccupiedPoints(b *BoardState) []Point {
// Start by getting unoccupied points // Start by getting unoccupied points
unoccupiedPoints := r.getUnoccupiedPoints(b) unoccupiedPoints := r.getUnoccupiedPoints(b, true)
// Create a new array to hold points that are even // Create a new array to hold points that are even
evenUnoccupiedPoints := []Point{} evenUnoccupiedPoints := []Point{}

View file

@ -44,17 +44,18 @@ func TestCreateInitialBoardState(t *testing.T) {
Err error Err error
}{ }{
{1, 1, []string{"one"}, 0, nil}, {1, 1, []string{"one"}, 0, nil},
{1, 2, []string{"one"}, 1, nil}, {1, 2, []string{"one"}, 0, nil},
{1, 4, []string{"one"}, 1, nil},
{2, 2, []string{"one"}, 1, nil},
{9, 8, []string{"one"}, 1, nil}, {9, 8, []string{"one"}, 1, nil},
{2, 2, []string{"one", "two"}, 2, nil}, {2, 2, []string{"one", "two"}, 0, nil},
{2, 2, []string{"one", "two"}, 2, nil},
{1, 1, []string{"one", "two"}, 2, errors.New("not enough space to place snake")}, {1, 1, []string{"one", "two"}, 2, errors.New("not enough space to place snake")},
{1, 2, []string{"one", "two"}, 2, errors.New("not enough space to place snake")}, {1, 2, []string{"one", "two"}, 2, errors.New("not enough space to place snake")},
{BoardSizeSmall, BoardSizeSmall, []string{"one", "two"}, 3, nil}, {BoardSizeSmall, BoardSizeSmall, []string{"one", "two"}, 3, nil},
} }
r := StandardRuleset{} r := StandardRuleset{}
for _, test := range tests { for testNum, test := range tests {
state, err := r.CreateInitialBoardState(test.Width, test.Height, test.IDs) state, err := r.CreateInitialBoardState(test.Width, test.Height, test.IDs)
require.Equal(t, test.Err, err) require.Equal(t, test.Err, err)
if err != nil { if err != nil {
@ -68,7 +69,7 @@ func TestCreateInitialBoardState(t *testing.T) {
for i, id := range test.IDs { for i, id := range test.IDs {
require.Equal(t, id, state.Snakes[i].ID) require.Equal(t, id, state.Snakes[i].ID)
} }
require.Len(t, state.Food, test.ExpectedNumFood) require.Len(t, state.Food, test.ExpectedNumFood, testNum)
} }
} }
@ -212,7 +213,7 @@ func TestPlaceSnakes(t *testing.T) {
r := StandardRuleset{} r := StandardRuleset{}
for _, test := range tests { for _, test := range tests {
require.Equal(t, test.BoardState.Width*test.BoardState.Height, int32(len(r.getUnoccupiedPoints(test.BoardState)))) require.Equal(t, test.BoardState.Width*test.BoardState.Height, int32(len(r.getUnoccupiedPoints(test.BoardState, true))))
err := r.placeSnakes(test.BoardState) err := r.placeSnakes(test.BoardState)
require.Equal(t, test.Err, err, "Snakes: %d", len(test.BoardState.Snakes)) require.Equal(t, test.Err, err, "Snakes: %d", len(test.BoardState.Snakes))
if err == nil { if err == nil {
@ -1732,7 +1733,7 @@ func TestGetUnoccupiedPoints(t *testing.T) {
r := StandardRuleset{} r := StandardRuleset{}
for _, test := range tests { for _, test := range tests {
unoccupiedPoints := r.getUnoccupiedPoints(test.Board) unoccupiedPoints := r.getUnoccupiedPoints(test.Board, true)
require.Equal(t, len(test.Expected), len(unoccupiedPoints)) require.Equal(t, len(test.Expected), len(unoccupiedPoints))
for i, e := range test.Expected { for i, e := range test.Expected {
require.Equal(t, e, unoccupiedPoints[i]) require.Equal(t, e, unoccupiedPoints[i])
@ -1834,12 +1835,12 @@ func TestMaybeSpawnFood(t *testing.T) {
ExpectedFood []Point ExpectedFood []Point
}{ }{
// Use pre-tested seeds and results // Use pre-tested seeds and results
{123, []Point{}, []Point{{2, 2}}}, {123, []Point{}, []Point{{2, 3}}},
{456, []Point{{4, 4}}, []Point{{4, 4}}}, {456, []Point{{4, 4}}, []Point{{4, 4}}},
{789, []Point{{4, 4}}, []Point{{4, 4}}}, {789, []Point{{4, 4}}, []Point{{4, 4}}},
{1024, []Point{}, []Point{{4, 1}}}, {1024, []Point{}, []Point{{1, 2}}},
{511, []Point{{4, 4}}, []Point{{4, 4}, {2, 0}}}, {511, []Point{{4, 4}}, []Point{{4, 4}, {4, 1}}},
{165, []Point{{4, 4}}, []Point{{4, 4}, {3, 1}}}, {165, []Point{{4, 4}}, []Point{{4, 4}, {4, 2}}},
} }
r := StandardRuleset{} r := StandardRuleset{}
@ -1864,6 +1865,21 @@ func TestMaybeSpawnFood(t *testing.T) {
} }
} }
func TestSpawnFood(t *testing.T) {
b := &BoardState{
Height: 1,
Width: 3,
Snakes: []Snake{
{Body: []Point{{1, 0}}},
},
}
// Food should never spawn, no room
r := StandardRuleset{}
err := r.spawnFood(b, 99)
require.NoError(t, err)
require.Equal(t, len(b.Food), 0)
}
func TestIsGameOver(t *testing.T) { func TestIsGameOver(t *testing.T) {
tests := []struct { tests := []struct {
Snakes []Snake Snakes []Snake