Restrict fixed food spawns to only locations further from center.
This commit is contained in:
parent
020303a8dd
commit
4df2c65432
2 changed files with 88 additions and 33 deletions
73
board.go
73
board.go
|
|
@ -1,6 +1,8 @@
|
||||||
package rules
|
package rules
|
||||||
|
|
||||||
import "math/rand"
|
import (
|
||||||
|
"math/rand"
|
||||||
|
)
|
||||||
|
|
||||||
type BoardState struct {
|
type BoardState struct {
|
||||||
Turn int32
|
Turn int32
|
||||||
|
|
@ -156,7 +158,9 @@ func PlaceFoodAutomatically(b *BoardState) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func PlaceFoodFixed(b *BoardState) error {
|
func PlaceFoodFixed(b *BoardState) error {
|
||||||
// Place 1 food within exactly 2 moves of each snake
|
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
|
||||||
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{
|
||||||
|
|
@ -165,8 +169,9 @@ func PlaceFoodFixed(b *BoardState) error {
|
||||||
{snakeHead.X + 1, snakeHead.Y - 1},
|
{snakeHead.X + 1, snakeHead.Y - 1},
|
||||||
{snakeHead.X + 1, snakeHead.Y + 1},
|
{snakeHead.X + 1, snakeHead.Y + 1},
|
||||||
}
|
}
|
||||||
availableFoodLocations := []Point{}
|
|
||||||
|
|
||||||
|
// Remove any positions already occupied by food or closer to center
|
||||||
|
availableFoodLocations := []Point{}
|
||||||
for _, p := range possibleFoodLocations {
|
for _, p := range possibleFoodLocations {
|
||||||
isOccupiedAlready := false
|
isOccupiedAlready := false
|
||||||
for _, food := range b.Food {
|
for _, food := range b.Food {
|
||||||
|
|
@ -175,8 +180,14 @@ func PlaceFoodFixed(b *BoardState) error {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if isOccupiedAlready {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// availableFoodLocations = append(availableFoodLocations, p)
|
||||||
|
|
||||||
if !isOccupiedAlready {
|
snakeHeadToCenter := getDistanceBetweenPoints(snakeHead, centerCoord)
|
||||||
|
foodToCenter := getDistanceBetweenPoints(p, centerCoord)
|
||||||
|
if snakeHeadToCenter <= foodToCenter {
|
||||||
availableFoodLocations = append(availableFoodLocations, p)
|
availableFoodLocations = append(availableFoodLocations, p)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -192,7 +203,6 @@ func 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}
|
|
||||||
unoccupiedPoints := getUnoccupiedPoints(b, true)
|
unoccupiedPoints := getUnoccupiedPoints(b, true)
|
||||||
for _, point := range unoccupiedPoints {
|
for _, point := range unoccupiedPoints {
|
||||||
if point == centerCoord {
|
if point == centerCoord {
|
||||||
|
|
@ -220,17 +230,26 @@ func PlaceFoodRandomly(b *BoardState, n int32) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func isKnownBoardSize(b *BoardState) bool {
|
func absInt32(n int32) int32 {
|
||||||
if b.Height == BoardSizeSmall && b.Width == BoardSizeSmall {
|
if n < 0 {
|
||||||
return true
|
return -n
|
||||||
}
|
}
|
||||||
if b.Height == BoardSizeMedium && b.Width == BoardSizeMedium {
|
return n
|
||||||
return true
|
}
|
||||||
|
|
||||||
|
func getEvenUnoccupiedPoints(b *BoardState) []Point {
|
||||||
|
// Start by getting unoccupied points
|
||||||
|
unoccupiedPoints := getUnoccupiedPoints(b, true)
|
||||||
|
|
||||||
|
// Create a new array to hold points that are even
|
||||||
|
evenUnoccupiedPoints := []Point{}
|
||||||
|
|
||||||
|
for _, point := range unoccupiedPoints {
|
||||||
|
if ((point.X + point.Y) % 2) == 0 {
|
||||||
|
evenUnoccupiedPoints = append(evenUnoccupiedPoints, point)
|
||||||
}
|
}
|
||||||
if b.Height == BoardSizeLarge && b.Width == BoardSizeLarge {
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
return false
|
return evenUnoccupiedPoints
|
||||||
}
|
}
|
||||||
|
|
||||||
func getUnoccupiedPoints(b *BoardState, includePossibleMoves bool) []Point {
|
func getUnoccupiedPoints(b *BoardState, includePossibleMoves bool) []Point {
|
||||||
|
|
@ -284,17 +303,19 @@ func getUnoccupiedPoints(b *BoardState, includePossibleMoves bool) []Point {
|
||||||
return unoccupiedPoints
|
return unoccupiedPoints
|
||||||
}
|
}
|
||||||
|
|
||||||
func getEvenUnoccupiedPoints(b *BoardState) []Point {
|
func getDistanceBetweenPoints(a, b Point) int32 {
|
||||||
// Start by getting unoccupied points
|
return absInt32(a.X-b.X) + absInt32(a.Y-b.Y)
|
||||||
unoccupiedPoints := getUnoccupiedPoints(b, true)
|
}
|
||||||
|
|
||||||
// Create a new array to hold points that are even
|
func isKnownBoardSize(b *BoardState) bool {
|
||||||
evenUnoccupiedPoints := []Point{}
|
if b.Height == BoardSizeSmall && b.Width == BoardSizeSmall {
|
||||||
|
return true
|
||||||
for _, point := range unoccupiedPoints {
|
}
|
||||||
if ((point.X + point.Y) % 2) == 0 {
|
if b.Height == BoardSizeMedium && b.Width == BoardSizeMedium {
|
||||||
evenUnoccupiedPoints = append(evenUnoccupiedPoints, point)
|
return true
|
||||||
}
|
}
|
||||||
}
|
if b.Height == BoardSizeLarge && b.Width == BoardSizeLarge {
|
||||||
return evenUnoccupiedPoints
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package rules
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
@ -390,6 +391,8 @@ func TestPlaceFoodFixed(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, len(test.BoardState.Snakes)+1, len(test.BoardState.Food))
|
require.Equal(t, len(test.BoardState.Snakes)+1, len(test.BoardState.Food))
|
||||||
|
|
||||||
|
midPoint := Point{(test.BoardState.Width - 1) / 2, (test.BoardState.Height - 1) / 2}
|
||||||
|
|
||||||
// Make sure every snake has food within 2 moves of it
|
// Make sure every snake has food within 2 moves of it
|
||||||
for _, snake := range test.BoardState.Snakes {
|
for _, snake := range test.BoardState.Snakes {
|
||||||
head := snake.Body[0]
|
head := snake.Body[0]
|
||||||
|
|
@ -403,6 +406,8 @@ func TestPlaceFoodFixed(t *testing.T) {
|
||||||
for _, food := range test.BoardState.Food {
|
for _, food := range test.BoardState.Food {
|
||||||
if food == bottomLeft || food == topLeft || food == bottomRight || food == topRight {
|
if food == bottomLeft || food == topLeft || food == bottomRight || food == topRight {
|
||||||
foundFoodInTwoMoves = true
|
foundFoodInTwoMoves = true
|
||||||
|
// Ensure it's not closer to the center than snake
|
||||||
|
require.True(t, getDistanceBetweenPoints(head, midPoint) <= getDistanceBetweenPoints(food, midPoint))
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -411,7 +416,6 @@ func TestPlaceFoodFixed(t *testing.T) {
|
||||||
|
|
||||||
// Make sure one food exists in center of board
|
// Make sure one food exists in center of board
|
||||||
foundFoodInCenter := false
|
foundFoodInCenter := false
|
||||||
midPoint := Point{(test.BoardState.Width - 1) / 2, (test.BoardState.Height - 1) / 2}
|
|
||||||
for _, food := range test.BoardState.Food {
|
for _, food := range test.BoardState.Food {
|
||||||
if food == midPoint {
|
if food == midPoint {
|
||||||
foundFoodInCenter = true
|
foundFoodInCenter = true
|
||||||
|
|
@ -442,6 +446,9 @@ func TestPlaceFoodFixedNoRoom(t *testing.T) {
|
||||||
},
|
},
|
||||||
Food: []Point{},
|
Food: []Point{},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// There are 3 possible spawn locations: {0, 0}, {0, 2}, {2, 0}
|
||||||
|
// So repeat calls to place food should fail after 3 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
|
||||||
|
|
@ -457,14 +464,41 @@ func TestPlaceFoodFixedNoRoom(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, 3, len(boardState.Food))
|
require.Equal(t, 3, len(boardState.Food))
|
||||||
|
|
||||||
err = PlaceFoodFixed(boardState)
|
|
||||||
require.NoError(t, err)
|
|
||||||
boardState.Food = boardState.Food[:len(boardState.Food)-1] // Center food
|
|
||||||
require.Equal(t, 4, 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{{0, 0}, {0, 2}, {2, 0}}
|
||||||
|
sort.Slice(boardState.Food, func(i, j int) bool {
|
||||||
|
if boardState.Food[i].X != boardState.Food[j].X {
|
||||||
|
return boardState.Food[i].X < boardState.Food[j].X
|
||||||
|
}
|
||||||
|
return boardState.Food[i].Y < boardState.Food[j].Y
|
||||||
|
})
|
||||||
|
require.Equal(t, expectedFood, boardState.Food)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetDistanceBetweenPoints(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
A Point
|
||||||
|
B Point
|
||||||
|
Expected int32
|
||||||
|
}{
|
||||||
|
{Point{0, 0}, Point{0, 0}, 0},
|
||||||
|
{Point{0, 0}, Point{1, 0}, 1},
|
||||||
|
{Point{0, 0}, Point{0, 1}, 1},
|
||||||
|
{Point{0, 0}, Point{1, 1}, 2},
|
||||||
|
{Point{0, 0}, Point{4, 4}, 8},
|
||||||
|
{Point{0, 0}, Point{4, 6}, 10},
|
||||||
|
{Point{8, 0}, Point{8, 0}, 0},
|
||||||
|
{Point{8, 0}, Point{8, 8}, 8},
|
||||||
|
{Point{8, 0}, Point{0, 8}, 16},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
require.Equal(t, getDistanceBetweenPoints(test.A, test.B), test.Expected)
|
||||||
|
require.Equal(t, getDistanceBetweenPoints(test.B, test.A), test.Expected)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIsKnownBoardSize(t *testing.T) {
|
func TestIsKnownBoardSize(t *testing.T) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue