diff --git a/go.sum b/go.sum index 8fdee58..68164f9 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/standard.go b/standard.go index 4ca5666..7773d78 100644 --- a/standard.go +++ b/standard.go @@ -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{} diff --git a/standard_test.go b/standard_test.go index f1aba4e..b4aa5d8 100644 --- a/standard_test.go +++ b/standard_test.go @@ -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