Stop spawning food in corners in competitive play. (#68)

This commit is contained in:
Brad Van Vugt 2022-04-07 07:59:21 -07:00 committed by GitHub
parent 762c94caf9
commit 573a93fa30
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 25 additions and 21 deletions

View file

@ -160,7 +160,7 @@ func PlaceFoodAutomatically(b *BoardState) error {
func PlaceFoodFixed(b *BoardState) error { func PlaceFoodFixed(b *BoardState) error {
centerCoord := Point{(b.Width - 1) / 2, (b.Height - 1) / 2} centerCoord := Point{(b.Width - 1) / 2, (b.Height - 1) / 2}
// Place 1 food within exactly 2 moves of each snake, but never towards the center // Place 1 food within exactly 2 moves of each snake, but never towards the center or in a corner
for i := 0; i < len(b.Snakes); i++ { for i := 0; i < len(b.Snakes); i++ {
snakeHead := b.Snakes[i].Body[0] snakeHead := b.Snakes[i].Body[0]
possibleFoodLocations := []Point{ possibleFoodLocations := []Point{
@ -170,9 +170,11 @@ func PlaceFoodFixed(b *BoardState) error {
{snakeHead.X + 1, snakeHead.Y + 1}, {snakeHead.X + 1, snakeHead.Y + 1},
} }
// Remove any positions already occupied by food or closer to center // Remove any invalid/unwanted positions
availableFoodLocations := []Point{} availableFoodLocations := []Point{}
for _, p := range possibleFoodLocations { for _, p := range possibleFoodLocations {
// Ignore points already occupied by food
isOccupiedAlready := false isOccupiedAlready := false
for _, food := range b.Food { for _, food := range b.Food {
if food.X == p.X && food.Y == p.Y { if food.X == p.X && food.Y == p.Y {
@ -184,21 +186,28 @@ func PlaceFoodFixed(b *BoardState) error {
continue continue
} }
// Food must be away from center on at least one axis // Food must be further than snake from center on at least one axis
isFarFromCenter := false isAwayFromCenter := false
if p.X < snakeHead.X && snakeHead.X < centerCoord.X { if p.X < snakeHead.X && snakeHead.X < centerCoord.X {
isFarFromCenter = true isAwayFromCenter = true
} else if centerCoord.X < snakeHead.X && snakeHead.X < p.X { } else if centerCoord.X < snakeHead.X && snakeHead.X < p.X {
isFarFromCenter = true isAwayFromCenter = true
} else if p.Y < snakeHead.Y && snakeHead.Y < centerCoord.Y { } else if p.Y < snakeHead.Y && snakeHead.Y < centerCoord.Y {
isFarFromCenter = true isAwayFromCenter = true
} else if centerCoord.Y < snakeHead.Y && snakeHead.Y < p.Y { } else if centerCoord.Y < snakeHead.Y && snakeHead.Y < p.Y {
isFarFromCenter = true isAwayFromCenter = true
} }
if isFarFromCenter { if !isAwayFromCenter {
continue
}
// Don't spawn food in corners
if (p.X == 0 || p.X == (b.Width-1)) && (p.Y == 0 || p.Y == (b.Height-1)) {
continue
}
availableFoodLocations = append(availableFoodLocations, p) availableFoodLocations = append(availableFoodLocations, p)
} }
}
if len(availableFoodLocations) <= 0 { if len(availableFoodLocations) <= 0 {
return ErrorNoRoomForFood return ErrorNoRoomForFood

View file

@ -461,8 +461,8 @@ func TestPlaceFoodFixedNoRoom_Corners(t *testing.T) {
Food: []Point{}, Food: []Point{},
} }
// There are only three possible spawn locations for each snake, // There are only two possible food spawn locations for each snake,
// so repeat calls to place food should fail after 3 successes // so repeat calls to place food should fail after 2 successes
err := PlaceFoodFixed(boardState) err := PlaceFoodFixed(boardState)
require.NoError(t, err) require.NoError(t, err)
boardState.Food = boardState.Food[:len(boardState.Food)-1] // Center food boardState.Food = boardState.Food[:len(boardState.Food)-1] // Center food
@ -473,20 +473,15 @@ func TestPlaceFoodFixedNoRoom_Corners(t *testing.T) {
boardState.Food = boardState.Food[:len(boardState.Food)-1] // Center food boardState.Food = boardState.Food[:len(boardState.Food)-1] // Center food
require.Equal(t, 8, len(boardState.Food)) require.Equal(t, 8, len(boardState.Food))
err = PlaceFoodFixed(boardState)
require.NoError(t, err)
boardState.Food = boardState.Food[:len(boardState.Food)-1] // Center food
require.Equal(t, 12, len(boardState.Food))
// And now there should be no more room. // And now there should be no more room.
err = PlaceFoodFixed(boardState) err = PlaceFoodFixed(boardState)
require.Error(t, err) require.Error(t, err)
expectedFood := []Point{ expectedFood := []Point{
{0, 0}, {0, 2}, {2, 0}, // Snake @ {1, 1} {0, 2}, {2, 0}, // Snake @ {1, 1}
{0, 4}, {0, 6}, {2, 6}, // Snake @ {1, 5} {0, 4}, {2, 6}, // Snake @ {1, 5}
{4, 0}, {6, 0}, {6, 2}, // Snake @ {5, 1} {4, 0}, {6, 2}, // Snake @ {5, 1}
{4, 6}, {6, 4}, {6, 6}, // Snake @ {5, 5} {4, 6}, {6, 4}, // Snake @ {5, 5}
} }
sortPoints(expectedFood) sortPoints(expectedFood)
sortPoints(boardState.Food) sortPoints(boardState.Food)