Refactor RoyaleRuleset and move hazard damage into StandardRuleset (#50)

* move hazard damage into StandardRuleset

* OutOfBounds -> Hazards

* remove "out of bounds" in comment

* add cases for eating food to hazard damage test
This commit is contained in:
Rob O'Dwyer 2021-08-17 16:47:06 -07:00 committed by GitHub
parent dabbe7dfb5
commit e416384007
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 341 additions and 288 deletions

View file

@ -6,8 +6,9 @@ import (
)
type StandardRuleset struct {
FoodSpawnChance int32 // [0, 100]
MinimumFood int32
FoodSpawnChance int32 // [0, 100]
MinimumFood int32
HazardDamagePerTurn int32
}
func (r *StandardRuleset) Name() string { return "standard" }
@ -50,14 +51,14 @@ func (r *StandardRuleset) placeSnakesFixed(b *BoardState) error {
// Create start 8 points
mn, md, mx := int32(1), (b.Width-1)/2, b.Width-2
startPoints := []Point{
Point{mn, mn},
Point{mn, md},
Point{mn, mx},
Point{md, mn},
Point{md, mx},
Point{mx, mn},
Point{mx, md},
Point{mx, mx},
{mn, mn},
{mn, md},
{mn, mx},
{md, mn},
{md, mx},
{mx, mn},
{mx, md},
{mx, mx},
}
// Sanity check
@ -107,10 +108,10 @@ func (r *StandardRuleset) placeFoodFixed(b *BoardState) error {
for i := 0; i < len(b.Snakes); i++ {
snakeHead := b.Snakes[i].Body[0]
possibleFoodLocations := []Point{
Point{snakeHead.X - 1, snakeHead.Y - 1},
Point{snakeHead.X - 1, snakeHead.Y + 1},
Point{snakeHead.X + 1, snakeHead.Y - 1},
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},
{snakeHead.X + 1, snakeHead.Y + 1},
}
availableFoodLocations := []Point{}
@ -175,10 +176,11 @@ func (r *StandardRuleset) isKnownBoardSize(b *BoardState) bool {
func (r *StandardRuleset) CreateNextBoardState(prevState *BoardState, moves []SnakeMove) (*BoardState, error) {
// We specifically want to copy prevState, so as not to alter it directly.
nextState := &BoardState{
Height: prevState.Height,
Width: prevState.Width,
Food: append([]Point{}, prevState.Food...),
Snakes: make([]Snake, len(prevState.Snakes)),
Height: prevState.Height,
Width: prevState.Width,
Food: append([]Point{}, prevState.Food...),
Snakes: make([]Snake, len(prevState.Snakes)),
Hazards: append([]Point{}, prevState.Hazards...),
}
for i := 0; i < len(prevState.Snakes); i++ {
nextState.Snakes[i].ID = prevState.Snakes[i].ID
@ -202,6 +204,11 @@ func (r *StandardRuleset) CreateNextBoardState(prevState *BoardState, moves []Sn
return nil, err
}
err = r.maybeDamageHazards(nextState)
if err != nil {
return nil, err
}
// TODO: LOG?
// bvanvugt: We specifically want this to happen before elimination for two reasons:
// 1) We want snakes to be able to eat on their very last turn and still survive.
@ -307,6 +314,41 @@ func (r *StandardRuleset) reduceSnakeHealth(b *BoardState) error {
return nil
}
func (r *StandardRuleset) maybeDamageHazards(b *BoardState) error {
for i := 0; i < len(b.Snakes); i++ {
snake := &b.Snakes[i]
if snake.EliminatedCause != NotEliminated {
continue
}
head := snake.Body[0]
for _, p := range b.Hazards {
if head == p {
// If there's a food in this square, don't reduce health
foundFood := false
for _, food := range b.Food {
if p == food {
foundFood = true
}
}
if foundFood {
continue
}
// Snake is in a hazard, reduce health
snake.Health = snake.Health - r.HazardDamagePerTurn
if snake.Health < 0 {
snake.Health = 0
}
if r.snakeIsOutOfHealth(snake) {
snake.EliminatedCause = EliminatedByOutOfHealth
}
}
}
}
return nil
}
func (r *StandardRuleset) maybeEliminateSnakes(b *BoardState) error {
// First order snake indices by length.
// In multi-collision scenarios we want to always attribute elimination to the longest snake.