allow initial food placement for 8 snakes on 7x7 board (#87)
* allow initial food placement for 8 snakes on 7x7 * fix logic to be for small board * fix inverted logic * logic should be actually <= 4 snakes, not 7
This commit is contained in:
parent
5ecc285dcd
commit
f58df66e69
2 changed files with 81 additions and 48 deletions
101
board.go
101
board.go
|
|
@ -213,62 +213,67 @@ func PlaceFoodAutomatically(rand Rand, b *BoardState) error {
|
||||||
func PlaceFoodFixed(rand Rand, b *BoardState) error {
|
func PlaceFoodFixed(rand Rand, 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 or in a corner
|
isSmallBoard := b.Width*b.Height <= BoardSizeSmall*BoardSizeSmall
|
||||||
for i := 0; i < len(b.Snakes); i++ {
|
// Up to 4 snakes can be placed such that food is nearby on small boards.
|
||||||
snakeHead := b.Snakes[i].Body[0]
|
// Otherwise, we skip this and only try to place food in the center.
|
||||||
possibleFoodLocations := []Point{
|
if len(b.Snakes) <= 4 || !isSmallBoard {
|
||||||
{snakeHead.X - 1, snakeHead.Y - 1},
|
// Place 1 food within exactly 2 moves of each snake, but never towards the center or in a corner
|
||||||
{snakeHead.X - 1, snakeHead.Y + 1},
|
for i := 0; i < len(b.Snakes); i++ {
|
||||||
{snakeHead.X + 1, snakeHead.Y - 1},
|
snakeHead := b.Snakes[i].Body[0]
|
||||||
{snakeHead.X + 1, snakeHead.Y + 1},
|
possibleFoodLocations := []Point{
|
||||||
}
|
{snakeHead.X - 1, snakeHead.Y - 1},
|
||||||
|
{snakeHead.X - 1, snakeHead.Y + 1},
|
||||||
|
{snakeHead.X + 1, snakeHead.Y - 1},
|
||||||
|
{snakeHead.X + 1, snakeHead.Y + 1},
|
||||||
|
}
|
||||||
|
|
||||||
// Remove any invalid/unwanted positions
|
// Remove any invalid/unwanted positions
|
||||||
availableFoodLocations := []Point{}
|
availableFoodLocations := []Point{}
|
||||||
for _, p := range possibleFoodLocations {
|
for _, p := range possibleFoodLocations {
|
||||||
|
|
||||||
// Ignore points already occupied by food
|
// 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 {
|
||||||
isOccupiedAlready = true
|
isOccupiedAlready = true
|
||||||
break
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
if isOccupiedAlready {
|
||||||
if isOccupiedAlready {
|
continue
|
||||||
continue
|
}
|
||||||
|
|
||||||
|
// Food must be further than snake from center on at least one axis
|
||||||
|
isAwayFromCenter := false
|
||||||
|
if p.X < snakeHead.X && snakeHead.X < centerCoord.X {
|
||||||
|
isAwayFromCenter = true
|
||||||
|
} else if centerCoord.X < snakeHead.X && snakeHead.X < p.X {
|
||||||
|
isAwayFromCenter = true
|
||||||
|
} else if p.Y < snakeHead.Y && snakeHead.Y < centerCoord.Y {
|
||||||
|
isAwayFromCenter = true
|
||||||
|
} else if centerCoord.Y < snakeHead.Y && snakeHead.Y < p.Y {
|
||||||
|
isAwayFromCenter = true
|
||||||
|
}
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Food must be further than snake from center on at least one axis
|
if len(availableFoodLocations) <= 0 {
|
||||||
isAwayFromCenter := false
|
return ErrorNoRoomForFood
|
||||||
if p.X < snakeHead.X && snakeHead.X < centerCoord.X {
|
|
||||||
isAwayFromCenter = true
|
|
||||||
} else if centerCoord.X < snakeHead.X && snakeHead.X < p.X {
|
|
||||||
isAwayFromCenter = true
|
|
||||||
} else if p.Y < snakeHead.Y && snakeHead.Y < centerCoord.Y {
|
|
||||||
isAwayFromCenter = true
|
|
||||||
} else if centerCoord.Y < snakeHead.Y && snakeHead.Y < p.Y {
|
|
||||||
isAwayFromCenter = true
|
|
||||||
}
|
|
||||||
if !isAwayFromCenter {
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't spawn food in corners
|
// Select randomly from available locations
|
||||||
if (p.X == 0 || p.X == (b.Width-1)) && (p.Y == 0 || p.Y == (b.Height-1)) {
|
placedFood := availableFoodLocations[rand.Intn(len(availableFoodLocations))]
|
||||||
continue
|
b.Food = append(b.Food, placedFood)
|
||||||
}
|
|
||||||
|
|
||||||
availableFoodLocations = append(availableFoodLocations, p)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(availableFoodLocations) <= 0 {
|
|
||||||
return ErrorNoRoomForFood
|
|
||||||
}
|
|
||||||
|
|
||||||
// Select randomly from available locations
|
|
||||||
placedFood := availableFoodLocations[rand.Intn(len(availableFoodLocations))]
|
|
||||||
b.Food = append(b.Food, placedFood)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finally, always place 1 food in center of board for dramatic purposes
|
// Finally, always place 1 food in center of board for dramatic purposes
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,34 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestDev1235(t *testing.T) {
|
||||||
|
// Small boards should no longer error and only get 1 food when num snakes > 4
|
||||||
|
state, err := CreateDefaultBoardState(MaxRand, BoardSizeSmall, BoardSizeSmall, []string{
|
||||||
|
"1", "2", "3", "4", "5", "6", "7", "8",
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, state.Food, 1)
|
||||||
|
state, err = CreateDefaultBoardState(MaxRand, BoardSizeSmall, BoardSizeSmall, []string{
|
||||||
|
"1", "2", "3", "4", "5",
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, state.Food, 1)
|
||||||
|
|
||||||
|
// Small boards with <= 4 snakes should still get more than just center food
|
||||||
|
state, err = CreateDefaultBoardState(MaxRand, BoardSizeSmall, BoardSizeSmall, []string{
|
||||||
|
"1", "2", "3", "4",
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, state.Food, 5)
|
||||||
|
|
||||||
|
// Medium boards should still get 9 food
|
||||||
|
state, err = CreateDefaultBoardState(MaxRand, BoardSizeMedium, BoardSizeMedium, []string{
|
||||||
|
"1", "2", "3", "4", "5", "6", "7", "8",
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, state.Food, 9)
|
||||||
|
}
|
||||||
|
|
||||||
func sortPoints(p []Point) {
|
func sortPoints(p []Point) {
|
||||||
sort.Slice(p, func(i, j int) bool {
|
sort.Slice(p, func(i, j int) bool {
|
||||||
if p[i].X != p[j].X {
|
if p[i].X != p[j].X {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue