parent
8153585f57
commit
44b6b94666
5 changed files with 420 additions and 15 deletions
|
|
@ -34,5 +34,5 @@ type SnakeMove struct {
|
||||||
|
|
||||||
type Ruleset interface {
|
type Ruleset interface {
|
||||||
CreateInitialBoardState(width int32, height int32, snakeIDs []string) (*BoardState, error)
|
CreateInitialBoardState(width int32, height int32, snakeIDs []string) (*BoardState, error)
|
||||||
ResolveMoves(prevState *BoardState, moves []SnakeMove) (*BoardState, error)
|
CreateNextBoardState(prevState *BoardState, moves []SnakeMove) (*BoardState, error)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
25
standard.go
25
standard.go
|
|
@ -131,7 +131,7 @@ func (r *StandardRuleset) isKnownBoardSize(b *BoardState) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *StandardRuleset) ResolveMoves(prevState *BoardState, moves []SnakeMove) (*BoardState, error) {
|
func (r *StandardRuleset) CreateNextBoardState(prevState *BoardState, moves []SnakeMove) (*BoardState, error) {
|
||||||
// We specifically want to copy prevState, so as not to alter it directly.
|
// We specifically want to copy prevState, so as not to alter it directly.
|
||||||
nextState := &BoardState{
|
nextState := &BoardState{
|
||||||
Height: prevState.Height,
|
Height: prevState.Height,
|
||||||
|
|
@ -160,7 +160,7 @@ func (r *StandardRuleset) ResolveMoves(prevState *BoardState, moves []SnakeMove)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: LOG?
|
// TODO: LOG?
|
||||||
err = r.eliminateSnakes(nextState)
|
err = r.maybeEliminateSnakes(nextState)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -172,7 +172,7 @@ func (r *StandardRuleset) ResolveMoves(prevState *BoardState, moves []SnakeMove)
|
||||||
// of equal length actually show length + 1
|
// of equal length actually show length + 1
|
||||||
|
|
||||||
// TODO: LOG?
|
// TODO: LOG?
|
||||||
err = r.feedSnakes(nextState)
|
err = r.maybeFeedSnakes(nextState)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -256,7 +256,7 @@ func (r *StandardRuleset) reduceSnakeHealth(b *BoardState) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *StandardRuleset) eliminateSnakes(b *BoardState) error {
|
func (r *StandardRuleset) maybeEliminateSnakes(b *BoardState) error {
|
||||||
// First order snake indices by length.
|
// First order snake indices by length.
|
||||||
// In multi-collision scenarios we want to always attribute elimination to the longest snake.
|
// In multi-collision scenarios we want to always attribute elimination to the longest snake.
|
||||||
snakeIndicesByLength := make([]int, len(b.Snakes))
|
snakeIndicesByLength := make([]int, len(b.Snakes))
|
||||||
|
|
@ -351,7 +351,7 @@ func (r *StandardRuleset) snakeHasLostHeadToHead(s *Snake, other *Snake) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *StandardRuleset) feedSnakes(b *BoardState) error {
|
func (r *StandardRuleset) maybeFeedSnakes(b *BoardState) error {
|
||||||
newFood := []Point{}
|
newFood := []Point{}
|
||||||
for _, food := range b.Food {
|
for _, food := range b.Food {
|
||||||
foodHasBeenEaten := false
|
foodHasBeenEaten := false
|
||||||
|
|
@ -364,10 +364,8 @@ func (r *StandardRuleset) feedSnakes(b *BoardState) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if snake.Body[0].X == food.X && snake.Body[0].Y == food.Y {
|
if snake.Body[0].X == food.X && snake.Body[0].Y == food.Y {
|
||||||
|
r.feedSnake(snake)
|
||||||
foodHasBeenEaten = true
|
foodHasBeenEaten = true
|
||||||
// Update snake
|
|
||||||
snake.Body = append(snake.Body, snake.Body[len(snake.Body)-1])
|
|
||||||
snake.Health = SnakeMaxHealth
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Persist food to next BoardState if not eaten
|
// Persist food to next BoardState if not eaten
|
||||||
|
|
@ -380,6 +378,17 @@ func (r *StandardRuleset) feedSnakes(b *BoardState) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *StandardRuleset) feedSnake(snake *Snake) {
|
||||||
|
r.growSnake(snake)
|
||||||
|
snake.Health = SnakeMaxHealth
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *StandardRuleset) growSnake(snake *Snake) {
|
||||||
|
if len(snake.Body) > 0 {
|
||||||
|
snake.Body = append(snake.Body, snake.Body[len(snake.Body)-1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (r *StandardRuleset) maybeSpawnFood(b *BoardState) error {
|
func (r *StandardRuleset) maybeSpawnFood(b *BoardState) error {
|
||||||
if len(b.Food) == 0 || rand.Float32() <= FoodSpawnChance {
|
if len(b.Food) == 0 || rand.Float32() <= FoodSpawnChance {
|
||||||
return r.spawnFood(b, 1)
|
return r.spawnFood(b, 1)
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ func TestSanity(t *testing.T) {
|
||||||
require.Len(t, state.Food, 0)
|
require.Len(t, state.Food, 0)
|
||||||
require.Len(t, state.Snakes, 0)
|
require.Len(t, state.Snakes, 0)
|
||||||
|
|
||||||
next, err := r.ResolveMoves(
|
next, err := r.CreateNextBoardState(
|
||||||
&BoardState{},
|
&BoardState{},
|
||||||
[]SnakeMove{},
|
[]SnakeMove{},
|
||||||
)
|
)
|
||||||
|
|
@ -244,7 +244,7 @@ func TestPlaceFood(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestResolveMoves(t *testing.T) {
|
func TestCreateNextBoardState(t *testing.T) {
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -761,7 +761,7 @@ func TestSnakeHasLostHeadToHead(t *testing.T) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEliminateSnakes(t *testing.T) {
|
func TestMaybeEliminateSnakes(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
Name string
|
Name string
|
||||||
Snakes []Snake
|
Snakes []Snake
|
||||||
|
|
@ -938,7 +938,7 @@ func TestEliminateSnakes(t *testing.T) {
|
||||||
Height: 10,
|
Height: 10,
|
||||||
Snakes: test.Snakes,
|
Snakes: test.Snakes,
|
||||||
}
|
}
|
||||||
err := r.eliminateSnakes(b)
|
err := r.maybeEliminateSnakes(b)
|
||||||
require.Equal(t, test.Err, err)
|
require.Equal(t, test.Err, err)
|
||||||
for i, snake := range b.Snakes {
|
for i, snake := range b.Snakes {
|
||||||
require.Equal(t, test.ExpectedEliminatedCauses[i], snake.EliminatedCause)
|
require.Equal(t, test.ExpectedEliminatedCauses[i], snake.EliminatedCause)
|
||||||
|
|
@ -948,7 +948,7 @@ func TestEliminateSnakes(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFeedSnakes(t *testing.T) {
|
func TestMaybeFeedSnakes(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
Name string
|
Name string
|
||||||
Snakes []Snake
|
Snakes []Snake
|
||||||
|
|
@ -1021,7 +1021,7 @@ func TestFeedSnakes(t *testing.T) {
|
||||||
Snakes: test.Snakes,
|
Snakes: test.Snakes,
|
||||||
Food: test.Food,
|
Food: test.Food,
|
||||||
}
|
}
|
||||||
err := r.feedSnakes(b)
|
err := r.maybeFeedSnakes(b)
|
||||||
require.NoError(t, err, test.Name)
|
require.NoError(t, err, test.Name)
|
||||||
require.Equal(t, len(test.ExpectedSnakes), len(b.Snakes), test.Name)
|
require.Equal(t, len(test.ExpectedSnakes), len(b.Snakes), test.Name)
|
||||||
for i := 0; i < len(b.Snakes); i++ {
|
for i := 0; i < len(b.Snakes); i++ {
|
||||||
|
|
|
||||||
110
team.go
Normal file
110
team.go
Normal file
|
|
@ -0,0 +1,110 @@
|
||||||
|
package rules
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TeamRuleset struct {
|
||||||
|
StandardRuleset
|
||||||
|
|
||||||
|
TeamMap map[string]string
|
||||||
|
|
||||||
|
// These are intentionally designed so that they default to a standard game.
|
||||||
|
AllowBodyCollisions bool
|
||||||
|
SharedElimination bool
|
||||||
|
SharedHealth bool
|
||||||
|
SharedLength bool
|
||||||
|
}
|
||||||
|
|
||||||
|
const EliminatedByTeam = "team-eliminated"
|
||||||
|
|
||||||
|
func (r *TeamRuleset) CreateNextBoardState(prevState *BoardState, moves []SnakeMove) (*BoardState, error) {
|
||||||
|
nextBoardState, err := r.StandardRuleset.CreateNextBoardState(prevState, moves)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: LOG?
|
||||||
|
err = r.resurrectTeamBodyCollisions(nextBoardState)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: LOG?
|
||||||
|
err = r.shareTeamAttributes(nextBoardState)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nextBoardState, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *TeamRuleset) areSnakesOnSameTeam(snake *Snake, other *Snake) bool {
|
||||||
|
return r.areSnakeIDsOnSameTeam(snake.ID, other.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *TeamRuleset) areSnakeIDsOnSameTeam(snakeID string, otherID string) bool {
|
||||||
|
return snakeID != otherID && r.TeamMap[snakeID] == r.TeamMap[otherID]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *TeamRuleset) resurrectTeamBodyCollisions(b *BoardState) error {
|
||||||
|
if !r.AllowBodyCollisions {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(b.Snakes); i++ {
|
||||||
|
snake := &b.Snakes[i]
|
||||||
|
if snake.EliminatedCause == EliminatedByCollision {
|
||||||
|
if snake.EliminatedBy == "" {
|
||||||
|
return errors.New("snake eliminated by collision and eliminatedby is not set")
|
||||||
|
}
|
||||||
|
if r.areSnakeIDsOnSameTeam(snake.ID, snake.EliminatedBy) {
|
||||||
|
snake.EliminatedCause = NotEliminated
|
||||||
|
snake.EliminatedBy = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *TeamRuleset) shareTeamAttributes(b *BoardState) error {
|
||||||
|
if !(r.SharedElimination || r.SharedLength || r.SharedHealth) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(b.Snakes); i++ {
|
||||||
|
snake := &b.Snakes[i]
|
||||||
|
if snake.EliminatedCause != NotEliminated {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for j := 0; j < len(b.Snakes); j++ {
|
||||||
|
other := &b.Snakes[j]
|
||||||
|
if r.areSnakesOnSameTeam(snake, other) {
|
||||||
|
if r.SharedHealth {
|
||||||
|
if snake.Health < other.Health {
|
||||||
|
snake.Health = other.Health
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if r.SharedLength {
|
||||||
|
if len(snake.Body) == 0 || len(other.Body) == 0 {
|
||||||
|
return errors.New("found snake of zero length")
|
||||||
|
}
|
||||||
|
for len(snake.Body) < len(other.Body) {
|
||||||
|
r.growSnake(snake)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if r.SharedElimination {
|
||||||
|
if snake.EliminatedCause == NotEliminated && other.EliminatedCause != NotEliminated {
|
||||||
|
snake.EliminatedCause = EliminatedByTeam
|
||||||
|
// We intentionally do not set snake.EliminatedBy because there might be multiple culprits.
|
||||||
|
snake.EliminatedBy = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
286
team_test.go
Normal file
286
team_test.go
Normal file
|
|
@ -0,0 +1,286 @@
|
||||||
|
package rules
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCreateNextBoardStateSanity(t *testing.T) {
|
||||||
|
boardState := &BoardState{}
|
||||||
|
r := TeamRuleset{}
|
||||||
|
_, err := r.CreateNextBoardState(boardState, []SnakeMove{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestResurrectTeamBodyCollisionsSanity(t *testing.T) {
|
||||||
|
boardState := &BoardState{}
|
||||||
|
r := TeamRuleset{}
|
||||||
|
err := r.resurrectTeamBodyCollisions(boardState)
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSharedAttributesSanity(t *testing.T) {
|
||||||
|
boardState := &BoardState{}
|
||||||
|
r := TeamRuleset{}
|
||||||
|
err := r.shareTeamAttributes(boardState)
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAllowBodyCollisions(t *testing.T) {
|
||||||
|
testSnakes := []struct {
|
||||||
|
SnakeID string
|
||||||
|
TeamID string
|
||||||
|
EliminatedCause string
|
||||||
|
EliminatedBy string
|
||||||
|
ExpectedCause string
|
||||||
|
ExpectedBy string
|
||||||
|
}{
|
||||||
|
// Team Red
|
||||||
|
{"R1", "red", NotEliminated, "", NotEliminated, ""},
|
||||||
|
{"R2", "red", EliminatedByCollision, "R1", NotEliminated, ""},
|
||||||
|
// Team Blue
|
||||||
|
{"B1", "blue", EliminatedByCollision, "R1", EliminatedByCollision, "R1"},
|
||||||
|
{"B2", "blue", EliminatedBySelfCollision, "B1", EliminatedBySelfCollision, "B1"},
|
||||||
|
{"B4", "blue", EliminatedByOutOfBounds, "", EliminatedByOutOfBounds, ""},
|
||||||
|
{"B3", "blue", NotEliminated, "", NotEliminated, ""},
|
||||||
|
// More Team Red
|
||||||
|
{"R3", "red", NotEliminated, "", NotEliminated, ""},
|
||||||
|
{"R4", "red", EliminatedByCollision, "R4", EliminatedByCollision, "R4"}, // this is an error case but worth testing
|
||||||
|
{"R5", "red", EliminatedByCollision, "R4", NotEliminated, ""},
|
||||||
|
// // Team Green
|
||||||
|
{"G1", "green", EliminatedByStarvation, "x", EliminatedByStarvation, "x"},
|
||||||
|
// // Team Yellow
|
||||||
|
{"Y1", "yellow", EliminatedByCollision, "B4", EliminatedByCollision, "B4"},
|
||||||
|
}
|
||||||
|
|
||||||
|
boardState := &BoardState{}
|
||||||
|
teamMap := make(map[string]string)
|
||||||
|
for _, testSnake := range testSnakes {
|
||||||
|
boardState.Snakes = append(boardState.Snakes, Snake{
|
||||||
|
ID: testSnake.SnakeID,
|
||||||
|
EliminatedCause: testSnake.EliminatedCause,
|
||||||
|
EliminatedBy: testSnake.EliminatedBy,
|
||||||
|
})
|
||||||
|
teamMap[testSnake.SnakeID] = testSnake.TeamID
|
||||||
|
}
|
||||||
|
require.Equal(t, len(teamMap), len(boardState.Snakes), "team map is wrong size, error in test setup")
|
||||||
|
|
||||||
|
r := TeamRuleset{TeamMap: teamMap, AllowBodyCollisions: true}
|
||||||
|
err := r.resurrectTeamBodyCollisions(boardState)
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, len(boardState.Snakes), len(testSnakes))
|
||||||
|
for i := 0; i < len(boardState.Snakes); i++ {
|
||||||
|
require.Equal(
|
||||||
|
t,
|
||||||
|
testSnakes[i].ExpectedCause,
|
||||||
|
boardState.Snakes[i].EliminatedCause,
|
||||||
|
"snake %s failed shared eliminated cause",
|
||||||
|
testSnakes[i].SnakeID,
|
||||||
|
)
|
||||||
|
require.Equal(
|
||||||
|
t,
|
||||||
|
testSnakes[i].ExpectedBy,
|
||||||
|
boardState.Snakes[i].EliminatedBy,
|
||||||
|
"snake %s failed shared eliminated by",
|
||||||
|
testSnakes[i].SnakeID,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAllowBodyCollisionsEliminatedByNotSet(t *testing.T) {
|
||||||
|
boardState := &BoardState{
|
||||||
|
Snakes: []Snake{
|
||||||
|
Snake{ID: "1", EliminatedCause: EliminatedByCollision},
|
||||||
|
Snake{ID: "2"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
r := TeamRuleset{
|
||||||
|
AllowBodyCollisions: true,
|
||||||
|
TeamMap: map[string]string{
|
||||||
|
"1": "red",
|
||||||
|
"2": "red",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
err := r.resurrectTeamBodyCollisions(boardState)
|
||||||
|
require.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestShareTeamHealth(t *testing.T) {
|
||||||
|
testSnakes := []struct {
|
||||||
|
SnakeID string
|
||||||
|
TeamID string
|
||||||
|
Health int32
|
||||||
|
ExpectedHealth int32
|
||||||
|
}{
|
||||||
|
// Team Red
|
||||||
|
{"R1", "red", 11, 88},
|
||||||
|
{"R2", "red", 22, 88},
|
||||||
|
// Team Blue
|
||||||
|
{"B1", "blue", 33, 333},
|
||||||
|
{"B2", "blue", 333, 333},
|
||||||
|
{"B3", "blue", 3, 333},
|
||||||
|
// More Team Red
|
||||||
|
{"R3", "red", 77, 88},
|
||||||
|
{"R4", "red", 88, 88},
|
||||||
|
// Team Green
|
||||||
|
{"G1", "green", 100, 100},
|
||||||
|
// Team Yellow
|
||||||
|
{"Y1", "yellow", 1, 1},
|
||||||
|
}
|
||||||
|
|
||||||
|
boardState := &BoardState{}
|
||||||
|
teamMap := make(map[string]string)
|
||||||
|
for _, testSnake := range testSnakes {
|
||||||
|
boardState.Snakes = append(boardState.Snakes, Snake{
|
||||||
|
ID: testSnake.SnakeID,
|
||||||
|
Health: testSnake.Health,
|
||||||
|
})
|
||||||
|
teamMap[testSnake.SnakeID] = testSnake.TeamID
|
||||||
|
}
|
||||||
|
require.Equal(t, len(teamMap), len(boardState.Snakes), "team map is wrong size, error in test setup")
|
||||||
|
|
||||||
|
r := TeamRuleset{SharedHealth: true, TeamMap: teamMap}
|
||||||
|
err := r.shareTeamAttributes(boardState)
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, len(boardState.Snakes), len(testSnakes))
|
||||||
|
for i := 0; i < len(boardState.Snakes); i++ {
|
||||||
|
require.Equal(
|
||||||
|
t,
|
||||||
|
testSnakes[i].ExpectedHealth,
|
||||||
|
boardState.Snakes[i].Health,
|
||||||
|
"snake %s failed shared health",
|
||||||
|
testSnakes[i].SnakeID,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSharedLength(t *testing.T) {
|
||||||
|
testSnakes := []struct {
|
||||||
|
SnakeID string
|
||||||
|
TeamID string
|
||||||
|
Body []Point
|
||||||
|
ExpectedBody []Point
|
||||||
|
}{
|
||||||
|
// Team Red
|
||||||
|
{"R1", "red", []Point{{1, 1}}, []Point{{1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}}},
|
||||||
|
{"R2", "red", []Point{{2, 2}, {2, 2}}, []Point{{2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}}},
|
||||||
|
// Team Blue
|
||||||
|
{"B1", "blue", []Point{{1, 1}, {1, 2}}, []Point{{1, 1}, {1, 2}}},
|
||||||
|
{"B2", "blue", []Point{{2, 1}}, []Point{{2, 1}, {2, 1}}},
|
||||||
|
{"B3", "blue", []Point{{3, 3}}, []Point{{3, 3}, {3, 3}}},
|
||||||
|
// More Team Red
|
||||||
|
{"R3", "red", []Point{{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}}, []Point{{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}}},
|
||||||
|
{"R4", "red", []Point{{4, 4}}, []Point{{4, 4}, {4, 4}, {4, 4}, {4, 4}, {4, 4}}},
|
||||||
|
// Team Green
|
||||||
|
{"G1", "green", []Point{{1, 1}}, []Point{{1, 1}}},
|
||||||
|
// Team Yellow
|
||||||
|
{"Y1", "yellow", []Point{{1, 3}, {1, 4}, {1, 5}, {1, 6}}, []Point{{1, 3}, {1, 4}, {1, 5}, {1, 6}}},
|
||||||
|
}
|
||||||
|
|
||||||
|
boardState := &BoardState{}
|
||||||
|
teamMap := make(map[string]string)
|
||||||
|
for _, testSnake := range testSnakes {
|
||||||
|
boardState.Snakes = append(boardState.Snakes, Snake{
|
||||||
|
ID: testSnake.SnakeID,
|
||||||
|
Body: testSnake.Body,
|
||||||
|
})
|
||||||
|
teamMap[testSnake.SnakeID] = testSnake.TeamID
|
||||||
|
}
|
||||||
|
require.Equal(t, len(teamMap), len(boardState.Snakes), "team map is wrong size, error in test setup")
|
||||||
|
|
||||||
|
r := TeamRuleset{SharedLength: true, TeamMap: teamMap}
|
||||||
|
err := r.shareTeamAttributes(boardState)
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, len(boardState.Snakes), len(testSnakes))
|
||||||
|
for i := 0; i < len(boardState.Snakes); i++ {
|
||||||
|
require.Equal(
|
||||||
|
t,
|
||||||
|
testSnakes[i].ExpectedBody,
|
||||||
|
boardState.Snakes[i].Body,
|
||||||
|
"snake %s failed shared length",
|
||||||
|
testSnakes[i].SnakeID,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSharedElimination(t *testing.T) {
|
||||||
|
testSnakes := []struct {
|
||||||
|
SnakeID string
|
||||||
|
TeamID string
|
||||||
|
EliminatedCause string
|
||||||
|
EliminatedBy string
|
||||||
|
ExpectedCause string
|
||||||
|
ExpectedBy string
|
||||||
|
}{
|
||||||
|
// Team Red
|
||||||
|
{"R1", "red", NotEliminated, "", EliminatedByTeam, ""},
|
||||||
|
{"R2", "red", EliminatedByHeadToHeadCollision, "y", EliminatedByHeadToHeadCollision, "y"},
|
||||||
|
// Team Blue
|
||||||
|
{"B1", "blue", EliminatedByOutOfBounds, "z", EliminatedByOutOfBounds, "z"},
|
||||||
|
{"B2", "blue", NotEliminated, "", EliminatedByTeam, ""},
|
||||||
|
{"B3", "blue", NotEliminated, "", EliminatedByTeam, ""},
|
||||||
|
// More Team Red
|
||||||
|
{"R3", "red", NotEliminated, "", EliminatedByTeam, ""},
|
||||||
|
{"R4", "red", EliminatedByCollision, "B1", EliminatedByCollision, "B1"},
|
||||||
|
// Team Green
|
||||||
|
{"G1", "green", EliminatedByStarvation, "x", EliminatedByStarvation, "x"},
|
||||||
|
// Team Yellow
|
||||||
|
{"Y1", "yellow", NotEliminated, "", NotEliminated, ""},
|
||||||
|
}
|
||||||
|
|
||||||
|
boardState := &BoardState{}
|
||||||
|
teamMap := make(map[string]string)
|
||||||
|
for _, testSnake := range testSnakes {
|
||||||
|
boardState.Snakes = append(boardState.Snakes, Snake{
|
||||||
|
ID: testSnake.SnakeID,
|
||||||
|
EliminatedCause: testSnake.EliminatedCause,
|
||||||
|
EliminatedBy: testSnake.EliminatedBy,
|
||||||
|
})
|
||||||
|
teamMap[testSnake.SnakeID] = testSnake.TeamID
|
||||||
|
}
|
||||||
|
require.Equal(t, len(teamMap), len(boardState.Snakes), "team map is wrong size, error in test setup")
|
||||||
|
|
||||||
|
r := TeamRuleset{SharedElimination: true, TeamMap: teamMap}
|
||||||
|
err := r.shareTeamAttributes(boardState)
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, len(boardState.Snakes), len(testSnakes))
|
||||||
|
for i := 0; i < len(boardState.Snakes); i++ {
|
||||||
|
require.Equal(
|
||||||
|
t,
|
||||||
|
testSnakes[i].ExpectedCause,
|
||||||
|
boardState.Snakes[i].EliminatedCause,
|
||||||
|
"snake %s failed shared eliminated cause",
|
||||||
|
testSnakes[i].SnakeID,
|
||||||
|
)
|
||||||
|
require.Equal(
|
||||||
|
t,
|
||||||
|
testSnakes[i].ExpectedBy,
|
||||||
|
boardState.Snakes[i].EliminatedBy,
|
||||||
|
"snake %s failed shared eliminated by",
|
||||||
|
testSnakes[i].SnakeID,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSharedAttributesErrorLengthZero(t *testing.T) {
|
||||||
|
boardState := &BoardState{
|
||||||
|
Snakes: []Snake{
|
||||||
|
Snake{ID: "1"},
|
||||||
|
Snake{ID: "2"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
r := TeamRuleset{
|
||||||
|
SharedLength: true,
|
||||||
|
TeamMap: map[string]string{
|
||||||
|
"1": "red",
|
||||||
|
"2": "red",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
err := r.shareTeamAttributes(boardState)
|
||||||
|
require.Error(t, err)
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue