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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
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/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
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
isCenterOccupied := true
centerCoord := Point{(b.Width - 1) / 2, (b.Height - 1) / 2}
unoccupiedPoints := r.getUnoccupiedPoints(b)
unoccupiedPoints := r.getUnoccupiedPoints(b, true)
for _, point := range unoccupiedPoints {
if point == centerCoord {
isCenterOccupied = false
@ -526,7 +526,7 @@ func (r *StandardRuleset) maybeSpawnFood(b *BoardState) error {
func (r *StandardRuleset) spawnFood(b *BoardState, n int) error {
for i := 0; i < n; i++ {
unoccupiedPoints := r.getUnoccupiedPoints(b)
unoccupiedPoints := r.getUnoccupiedPoints(b, false)
if len(unoccupiedPoints) > 0 {
newFood := unoccupiedPoints[rand.Intn(len(unoccupiedPoints))]
b.Food = append(b.Food, newFood)
@ -535,7 +535,7 @@ func (r *StandardRuleset) spawnFood(b *BoardState, n int) error {
return nil
}
func (r *StandardRuleset) getUnoccupiedPoints(b *BoardState) []Point {
func (r *StandardRuleset) getUnoccupiedPoints(b *BoardState, includePossibleMoves bool) []Point {
pointIsOccupied := map[int32]map[int32]bool{}
for _, p := range b.Food {
if _, xExists := pointIsOccupied[p.X]; !xExists {
@ -544,11 +544,26 @@ func (r *StandardRuleset) getUnoccupiedPoints(b *BoardState) []Point {
pointIsOccupied[p.X][p.Y] = true
}
for _, snake := range b.Snakes {
for _, p := range snake.Body {
for i, p := range snake.Body {
if _, xExists := pointIsOccupied[p.X]; !xExists {
pointIsOccupied[p.X] = map[int32]bool{}
}
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 {
// Start by getting unoccupied points
unoccupiedPoints := r.getUnoccupiedPoints(b)
unoccupiedPoints := r.getUnoccupiedPoints(b, true)
// Create a new array to hold points that are even
evenUnoccupiedPoints := []Point{}

View file

@ -44,17 +44,18 @@ func TestCreateInitialBoardState(t *testing.T) {
Err error
}{
{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},
{2, 2, []string{"one", "two"}, 2, nil},
{2, 2, []string{"one", "two"}, 2, nil},
{2, 2, []string{"one", "two"}, 0, nil},
{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")},
{BoardSizeSmall, BoardSizeSmall, []string{"one", "two"}, 3, nil},
}
r := StandardRuleset{}
for _, test := range tests {
for testNum, test := range tests {
state, err := r.CreateInitialBoardState(test.Width, test.Height, test.IDs)
require.Equal(t, test.Err, err)
if err != nil {
@ -68,7 +69,7 @@ func TestCreateInitialBoardState(t *testing.T) {
for i, id := range test.IDs {
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{}
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)
require.Equal(t, test.Err, err, "Snakes: %d", len(test.BoardState.Snakes))
if err == nil {
@ -1732,7 +1733,7 @@ func TestGetUnoccupiedPoints(t *testing.T) {
r := StandardRuleset{}
for _, test := range tests {
unoccupiedPoints := r.getUnoccupiedPoints(test.Board)
unoccupiedPoints := r.getUnoccupiedPoints(test.Board, true)
require.Equal(t, len(test.Expected), len(unoccupiedPoints))
for i, e := range test.Expected {
require.Equal(t, e, unoccupiedPoints[i])
@ -1834,12 +1835,12 @@ func TestMaybeSpawnFood(t *testing.T) {
ExpectedFood []Point
}{
// Use pre-tested seeds and results
{123, []Point{}, []Point{{2, 2}}},
{123, []Point{}, []Point{{2, 3}}},
{456, []Point{{4, 4}}, []Point{{4, 4}}},
{789, []Point{{4, 4}}, []Point{{4, 4}}},
{1024, []Point{}, []Point{{4, 1}}},
{511, []Point{{4, 4}}, []Point{{4, 4}, {2, 0}}},
{165, []Point{{4, 4}}, []Point{{4, 4}, {3, 1}}},
{1024, []Point{}, []Point{{1, 2}}},
{511, []Point{{4, 4}}, []Point{{4, 4}, {4, 1}}},
{165, []Point{{4, 4}}, []Point{{4, 4}, {4, 2}}},
}
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) {
tests := []struct {
Snakes []Snake