forked from BattlesnakeOfficial/rules
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathteam.go
128 lines (108 loc) · 3.09 KB
/
team.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
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 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 snake.ID != snake.EliminatedBy && 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
}
func (r *TeamRuleset) IsGameOver(b *BoardState) (bool, error) {
snakesRemaining := []*Snake{}
for i := 0; i < len(b.Snakes); i++ {
if b.Snakes[i].EliminatedCause == NotEliminated {
snakesRemaining = append(snakesRemaining, &b.Snakes[i])
}
}
for i := 0; i < len(snakesRemaining); i++ {
if !r.areSnakesOnSameTeam(snakesRemaining[i], snakesRemaining[0]) {
// There are multiple teams remaining
return false, nil
}
}
// no snakes or single team remaining
return true, nil
}