DEV-1479 ensure snake elimination turn is set (#93)
* ensure snake elimination turn is set - centralise elimination update logic to a single place to ensure consistency * doc the method * testing
This commit is contained in:
parent
663c377cc4
commit
e1289af5fb
6 changed files with 143 additions and 73 deletions
11
board.go
11
board.go
|
|
@ -57,6 +57,7 @@ func (prevState *BoardState) Clone() *BoardState {
|
|||
nextState.Snakes[i].Health = prevState.Snakes[i].Health
|
||||
nextState.Snakes[i].Body = append([]Point{}, prevState.Snakes[i].Body...)
|
||||
nextState.Snakes[i].EliminatedCause = prevState.Snakes[i].EliminatedCause
|
||||
nextState.Snakes[i].EliminatedOnTurn = prevState.Snakes[i].EliminatedOnTurn
|
||||
nextState.Snakes[i].EliminatedBy = prevState.Snakes[i].EliminatedBy
|
||||
}
|
||||
return nextState
|
||||
|
|
@ -551,3 +552,13 @@ func getDistanceBetweenPoints(a, b Point) int {
|
|||
func isSquareBoard(b *BoardState) bool {
|
||||
return b.Width == b.Height
|
||||
}
|
||||
|
||||
// EliminateSnake updates a snake's state to reflect that it was eliminated.
|
||||
// - "cause" identifies what type of event caused the snake to be eliminated
|
||||
// - "by" identifies which snake (if any, use empty string "" if none) eliminated the snake.
|
||||
// - "turn" is the turn number that this snake was eliminated on.
|
||||
func EliminateSnake(s *Snake, cause, by string, turn int) {
|
||||
s.EliminatedCause = cause
|
||||
s.EliminatedBy = by
|
||||
s.EliminatedOnTurn = turn
|
||||
}
|
||||
|
|
|
|||
|
|
@ -881,3 +881,11 @@ func TestPlaceFoodRandomly(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
require.Equal(t, len(b.Food), 0)
|
||||
}
|
||||
|
||||
func TestEliminateSnake(t *testing.T) {
|
||||
s := &Snake{}
|
||||
EliminateSnake(s, "test-cause", "", 2)
|
||||
require.Equal(t, "test-cause", s.EliminatedCause)
|
||||
require.Equal(t, "", s.EliminatedBy)
|
||||
require.Equal(t, 2, s.EliminatedOnTurn)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ func TestConstrictorRulesetInterface(t *testing.T) {
|
|||
var constrictorMoveAndCollideMAD = gameTestCase{
|
||||
"Constrictor Case Move and Collide",
|
||||
&BoardState{
|
||||
Turn: 41,
|
||||
Width: 10,
|
||||
Height: 10,
|
||||
Snakes: []Snake{
|
||||
|
|
@ -42,18 +43,20 @@ var constrictorMoveAndCollideMAD = gameTestCase{
|
|||
Height: 10,
|
||||
Snakes: []Snake{
|
||||
{
|
||||
ID: "one",
|
||||
Body: []Point{{1, 2}, {1, 1}, {1, 1}},
|
||||
Health: 100,
|
||||
EliminatedCause: EliminatedByCollision,
|
||||
EliminatedBy: "two",
|
||||
ID: "one",
|
||||
Body: []Point{{1, 2}, {1, 1}, {1, 1}},
|
||||
Health: 100,
|
||||
EliminatedCause: EliminatedByCollision,
|
||||
EliminatedBy: "two",
|
||||
EliminatedOnTurn: 42,
|
||||
},
|
||||
{
|
||||
ID: "two",
|
||||
Body: []Point{{1, 1}, {1, 2}, {1, 2}},
|
||||
Health: 100,
|
||||
EliminatedCause: EliminatedByCollision,
|
||||
EliminatedBy: "one",
|
||||
ID: "two",
|
||||
Body: []Point{{1, 1}, {1, 2}, {1, 2}},
|
||||
Health: 100,
|
||||
EliminatedCause: EliminatedByCollision,
|
||||
EliminatedBy: "one",
|
||||
EliminatedOnTurn: 42,
|
||||
},
|
||||
},
|
||||
Food: []Point{},
|
||||
|
|
|
|||
31
solo_test.go
31
solo_test.go
|
|
@ -116,3 +116,34 @@ func TestSoloCreateNextBoardState(t *testing.T) {
|
|||
gc.requireValidNextState(t, NewRulesetBuilder().PipelineRuleset(GameTypeSolo, NewPipeline(soloRulesetStages...)))
|
||||
}
|
||||
}
|
||||
|
||||
// Test a snake running right into the wall is properly eliminated
|
||||
func TestSoloEliminationOutOfBounds(t *testing.T) {
|
||||
r := SoloRuleset{}
|
||||
|
||||
// Using MaxRand is important because it ensures that the snakes are consistently placed in a way this test will work.
|
||||
// Actually random placement could result in the assumptions made by this test being incorrect.
|
||||
initialState, err := CreateDefaultBoardState(MaxRand, 2, 2, []string{"one"})
|
||||
require.NoError(t, err)
|
||||
|
||||
_, next, err := r.Execute(
|
||||
initialState,
|
||||
r.Settings(),
|
||||
[]SnakeMove{{ID: "one", Move: "right"}},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, initialState)
|
||||
|
||||
ended, next, err := r.Execute(
|
||||
next,
|
||||
r.Settings(),
|
||||
[]SnakeMove{{ID: "one", Move: "right"}},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, initialState)
|
||||
|
||||
require.True(t, ended)
|
||||
require.Equal(t, EliminatedByOutOfBounds, next.Snakes[0].EliminatedCause)
|
||||
require.Equal(t, "", next.Snakes[0].EliminatedBy)
|
||||
require.Equal(t, 1, next.Snakes[0].EliminatedOnTurn)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@ func DamageHazardsStandard(b *BoardState, settings Settings, moves []SnakeMove)
|
|||
snake.Health = SnakeMaxHealth
|
||||
}
|
||||
if snakeIsOutOfHealth(snake) {
|
||||
snake.EliminatedCause = EliminatedByOutOfHealth
|
||||
EliminateSnake(snake, EliminatedByOutOfHealth, "", b.Turn+1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -221,12 +221,12 @@ func EliminateSnakesStandard(b *BoardState, settings Settings, moves []SnakeMove
|
|||
}
|
||||
|
||||
if snakeIsOutOfHealth(snake) {
|
||||
snake.EliminatedCause = EliminatedByOutOfHealth
|
||||
EliminateSnake(snake, EliminatedByOutOfHealth, "", b.Turn+1)
|
||||
continue
|
||||
}
|
||||
|
||||
if snakeIsOutOfBounds(snake, b.Width, b.Height) {
|
||||
snake.EliminatedCause = EliminatedByOutOfBounds
|
||||
EliminateSnake(snake, EliminatedByOutOfBounds, "", b.Turn+1)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
|
@ -306,8 +306,7 @@ func EliminateSnakesStandard(b *BoardState, settings Settings, moves []SnakeMove
|
|||
for i := 0; i < len(b.Snakes); i++ {
|
||||
snake := &b.Snakes[i]
|
||||
if snake.ID == elimination.ID {
|
||||
snake.EliminatedCause = elimination.Cause
|
||||
snake.EliminatedBy = elimination.By
|
||||
EliminateSnake(snake, elimination.Cause, elimination.By, b.Turn+1)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
|
|
|||
134
standard_test.go
134
standard_test.go
|
|
@ -164,6 +164,7 @@ var standardCaseMoveEatAndGrow = gameTestCase{
|
|||
var standardMoveAndCollideMAD = gameTestCase{
|
||||
"Standard Case Move and Collide",
|
||||
&BoardState{
|
||||
Turn: 0,
|
||||
Width: 10,
|
||||
Height: 10,
|
||||
Snakes: []Snake{
|
||||
|
|
@ -191,18 +192,20 @@ var standardMoveAndCollideMAD = gameTestCase{
|
|||
Height: 10,
|
||||
Snakes: []Snake{
|
||||
{
|
||||
ID: "one",
|
||||
Body: []Point{{1, 2}, {1, 1}},
|
||||
Health: 98,
|
||||
EliminatedCause: EliminatedByCollision,
|
||||
EliminatedBy: "two",
|
||||
ID: "one",
|
||||
Body: []Point{{1, 2}, {1, 1}},
|
||||
Health: 98,
|
||||
EliminatedCause: EliminatedByCollision,
|
||||
EliminatedBy: "two",
|
||||
EliminatedOnTurn: 1,
|
||||
},
|
||||
{
|
||||
ID: "two",
|
||||
Body: []Point{{1, 1}, {1, 2}},
|
||||
Health: 98,
|
||||
EliminatedCause: EliminatedByCollision,
|
||||
EliminatedBy: "one",
|
||||
ID: "two",
|
||||
Body: []Point{{1, 1}, {1, 2}},
|
||||
Health: 98,
|
||||
EliminatedCause: EliminatedByCollision,
|
||||
EliminatedBy: "one",
|
||||
EliminatedOnTurn: 1,
|
||||
},
|
||||
},
|
||||
Food: []Point{},
|
||||
|
|
@ -275,10 +278,11 @@ func TestEatingOnLastMove(t *testing.T) {
|
|||
Health: 100,
|
||||
},
|
||||
{
|
||||
ID: "two",
|
||||
Body: []Point{{3, 1}, {3, 2}, {3, 3}},
|
||||
Health: 0,
|
||||
EliminatedCause: EliminatedByOutOfHealth,
|
||||
ID: "two",
|
||||
Body: []Point{{3, 1}, {3, 2}, {3, 3}},
|
||||
Health: 0,
|
||||
EliminatedCause: EliminatedByOutOfHealth,
|
||||
EliminatedOnTurn: 1,
|
||||
},
|
||||
},
|
||||
Food: []Point{{9, 9}},
|
||||
|
|
@ -315,6 +319,7 @@ func TestHeadToHeadOnFood(t *testing.T) {
|
|||
}{
|
||||
{
|
||||
&BoardState{
|
||||
Turn: 41,
|
||||
Width: 10,
|
||||
Height: 10,
|
||||
Snakes: []Snake{
|
||||
|
|
@ -341,18 +346,20 @@ func TestHeadToHeadOnFood(t *testing.T) {
|
|||
Height: 10,
|
||||
Snakes: []Snake{
|
||||
{
|
||||
ID: "one",
|
||||
Body: []Point{{0, 3}, {0, 2}, {0, 1}, {0, 1}},
|
||||
Health: 100,
|
||||
EliminatedCause: EliminatedByHeadToHeadCollision,
|
||||
EliminatedBy: "two",
|
||||
ID: "one",
|
||||
Body: []Point{{0, 3}, {0, 2}, {0, 1}, {0, 1}},
|
||||
Health: 100,
|
||||
EliminatedCause: EliminatedByHeadToHeadCollision,
|
||||
EliminatedBy: "two",
|
||||
EliminatedOnTurn: 42,
|
||||
},
|
||||
{
|
||||
ID: "two",
|
||||
Body: []Point{{0, 3}, {0, 4}, {0, 5}, {0, 5}},
|
||||
Health: 100,
|
||||
EliminatedCause: EliminatedByHeadToHeadCollision,
|
||||
EliminatedBy: "one",
|
||||
ID: "two",
|
||||
Body: []Point{{0, 3}, {0, 4}, {0, 5}, {0, 5}},
|
||||
Health: 100,
|
||||
EliminatedCause: EliminatedByHeadToHeadCollision,
|
||||
EliminatedBy: "one",
|
||||
EliminatedOnTurn: 42,
|
||||
},
|
||||
},
|
||||
Food: []Point{{9, 9}},
|
||||
|
|
@ -360,6 +367,7 @@ func TestHeadToHeadOnFood(t *testing.T) {
|
|||
},
|
||||
{
|
||||
&BoardState{
|
||||
Turn: 41,
|
||||
Width: 10,
|
||||
Height: 10,
|
||||
Snakes: []Snake{
|
||||
|
|
@ -386,11 +394,12 @@ func TestHeadToHeadOnFood(t *testing.T) {
|
|||
Height: 10,
|
||||
Snakes: []Snake{
|
||||
{
|
||||
ID: "one",
|
||||
Body: []Point{{0, 3}, {0, 2}, {0, 1}, {0, 1}},
|
||||
Health: 100,
|
||||
EliminatedCause: EliminatedByHeadToHeadCollision,
|
||||
EliminatedBy: "two",
|
||||
ID: "one",
|
||||
Body: []Point{{0, 3}, {0, 2}, {0, 1}, {0, 1}},
|
||||
Health: 100,
|
||||
EliminatedCause: EliminatedByHeadToHeadCollision,
|
||||
EliminatedBy: "two",
|
||||
EliminatedOnTurn: 42,
|
||||
},
|
||||
{
|
||||
ID: "two",
|
||||
|
|
@ -1272,66 +1281,75 @@ func TestMaybeEliminateSnakesPriority(t *testing.T) {
|
|||
|
||||
func TestMaybeDamageHazards(t *testing.T) {
|
||||
tests := []struct {
|
||||
Snakes []Snake
|
||||
Hazards []Point
|
||||
Food []Point
|
||||
ExpectedEliminatedCauses []string
|
||||
ExpectedEliminatedByIDs []string
|
||||
Snakes []Snake
|
||||
Hazards []Point
|
||||
Food []Point
|
||||
ExpectedEliminatedCauses []string
|
||||
ExpectedEliminatedByIDs []string
|
||||
ExpectedEliminatedOnTurns []int
|
||||
}{
|
||||
{},
|
||||
{
|
||||
Snakes: []Snake{{Body: []Point{{0, 0}}}},
|
||||
Hazards: []Point{},
|
||||
ExpectedEliminatedCauses: []string{NotEliminated},
|
||||
ExpectedEliminatedByIDs: []string{""},
|
||||
Snakes: []Snake{{Body: []Point{{0, 0}}}},
|
||||
Hazards: []Point{},
|
||||
ExpectedEliminatedCauses: []string{NotEliminated},
|
||||
ExpectedEliminatedByIDs: []string{""},
|
||||
ExpectedEliminatedOnTurns: []int{0},
|
||||
},
|
||||
{
|
||||
Snakes: []Snake{{Body: []Point{{0, 0}}}},
|
||||
Hazards: []Point{{0, 0}},
|
||||
ExpectedEliminatedCauses: []string{EliminatedByOutOfHealth},
|
||||
ExpectedEliminatedByIDs: []string{""},
|
||||
Snakes: []Snake{{Body: []Point{{0, 0}}}},
|
||||
Hazards: []Point{{0, 0}},
|
||||
ExpectedEliminatedCauses: []string{EliminatedByOutOfHealth},
|
||||
ExpectedEliminatedByIDs: []string{""},
|
||||
ExpectedEliminatedOnTurns: []int{42},
|
||||
},
|
||||
{
|
||||
Snakes: []Snake{{Body: []Point{{0, 0}}}},
|
||||
Hazards: []Point{{0, 0}},
|
||||
Food: []Point{{0, 0}},
|
||||
ExpectedEliminatedCauses: []string{NotEliminated},
|
||||
ExpectedEliminatedByIDs: []string{""},
|
||||
Snakes: []Snake{{Body: []Point{{0, 0}}}},
|
||||
Hazards: []Point{{0, 0}},
|
||||
Food: []Point{{0, 0}},
|
||||
ExpectedEliminatedCauses: []string{NotEliminated},
|
||||
ExpectedEliminatedByIDs: []string{""},
|
||||
ExpectedEliminatedOnTurns: []int{0},
|
||||
},
|
||||
{
|
||||
Snakes: []Snake{{Body: []Point{{0, 0}, {1, 0}, {2, 0}}}},
|
||||
Hazards: []Point{{1, 0}, {2, 0}},
|
||||
ExpectedEliminatedCauses: []string{NotEliminated},
|
||||
ExpectedEliminatedByIDs: []string{""},
|
||||
Snakes: []Snake{{Body: []Point{{0, 0}, {1, 0}, {2, 0}}}},
|
||||
Hazards: []Point{{1, 0}, {2, 0}},
|
||||
ExpectedEliminatedCauses: []string{NotEliminated},
|
||||
ExpectedEliminatedByIDs: []string{""},
|
||||
ExpectedEliminatedOnTurns: []int{0},
|
||||
},
|
||||
{
|
||||
Snakes: []Snake{
|
||||
{Body: []Point{{0, 0}, {1, 0}, {2, 0}}},
|
||||
{Body: []Point{{3, 3}, {3, 4}, {3, 5}, {3, 6}}},
|
||||
},
|
||||
Hazards: []Point{{1, 0}, {2, 0}, {3, 4}, {3, 5}, {3, 6}},
|
||||
ExpectedEliminatedCauses: []string{NotEliminated, NotEliminated},
|
||||
ExpectedEliminatedByIDs: []string{"", ""},
|
||||
Hazards: []Point{{1, 0}, {2, 0}, {3, 4}, {3, 5}, {3, 6}},
|
||||
ExpectedEliminatedCauses: []string{NotEliminated, NotEliminated},
|
||||
ExpectedEliminatedByIDs: []string{"", ""},
|
||||
ExpectedEliminatedOnTurns: []int{0, 0},
|
||||
},
|
||||
{
|
||||
Snakes: []Snake{
|
||||
{Body: []Point{{0, 0}, {1, 0}, {2, 0}}},
|
||||
{Body: []Point{{3, 3}, {3, 4}, {3, 5}, {3, 6}}},
|
||||
},
|
||||
Hazards: []Point{{3, 3}},
|
||||
ExpectedEliminatedCauses: []string{NotEliminated, EliminatedByOutOfHealth},
|
||||
ExpectedEliminatedByIDs: []string{"", ""},
|
||||
Hazards: []Point{{3, 3}},
|
||||
ExpectedEliminatedCauses: []string{NotEliminated, EliminatedByOutOfHealth},
|
||||
ExpectedEliminatedByIDs: []string{"", ""},
|
||||
ExpectedEliminatedOnTurns: []int{0, 42},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
b := &BoardState{Snakes: test.Snakes, Hazards: test.Hazards, Food: test.Food}
|
||||
b := &BoardState{Turn: 41, Snakes: test.Snakes, Hazards: test.Hazards, Food: test.Food}
|
||||
r := StandardRuleset{HazardDamagePerTurn: 100}
|
||||
_, err := DamageHazardsStandard(b, r.Settings(), mockSnakeMoves())
|
||||
require.NoError(t, err)
|
||||
|
||||
for i, snake := range b.Snakes {
|
||||
require.Equal(t, test.ExpectedEliminatedCauses[i], snake.EliminatedCause)
|
||||
require.Equal(t, test.ExpectedEliminatedByIDs[i], snake.EliminatedBy)
|
||||
require.Equal(t, test.ExpectedEliminatedOnTurns[i], snake.EliminatedOnTurn)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue