Skip to content

Commit ef9c766

Browse files
authored
Revert SnailMode to not use PreUpdate (#121)
In the fall me and Jonathan worked on an update to the Engine specifically with SnailMode in mind, and refactored SnailMode to use this new `PreUpdate` method This allowed us to not have to use off-board hazards as state! However we want to use Snail Mode for our first Community Tournament! And the web engine doesn't support PreUpdate yet, so we are reverting Snail Mode to its old Pre-PreUpdate version :lol:
1 parent 932f541 commit ef9c766

File tree

1 file changed

+79
-35
lines changed

1 file changed

+79
-35
lines changed

maps/snail_mode.go

Lines changed: 79 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,20 @@ import (
44
"github.com/BattlesnakeOfficial/rules"
55
)
66

7-
type SnailModeMap struct {
8-
lastTailPositions map[rules.Point]int // local state is preserved during the turn
9-
}
7+
type SnailModeMap struct{}
108

119
// init registers this map in the global registry.
1210
func init() {
13-
globalRegistry.RegisterMap("snail_mode", &SnailModeMap{lastTailPositions: nil})
11+
globalRegistry.RegisterMap("snail_mode", SnailModeMap{})
1412
}
1513

1614
// ID returns a unique identifier for this map.
17-
func (m *SnailModeMap) ID() string {
15+
func (m SnailModeMap) ID() string {
1816
return "snail_mode"
1917
}
2018

2119
// Meta returns the non-functional metadata about this map.
22-
func (m *SnailModeMap) Meta() Metadata {
20+
func (m SnailModeMap) Meta() Metadata {
2321
return Metadata{
2422
Name: "Snail Mode",
2523
Description: "Snakes leave behind a trail of hazards",
@@ -33,7 +31,7 @@ func (m *SnailModeMap) Meta() Metadata {
3331
}
3432

3533
// SetupBoard here is pretty 'standard' and doesn't do any special setup for this game mode
36-
func (m *SnailModeMap) SetupBoard(initialBoardState *rules.BoardState, settings rules.Settings, editor Editor) error {
34+
func (m SnailModeMap) SetupBoard(initialBoardState *rules.BoardState, settings rules.Settings, editor Editor) error {
3735
rand := settings.GetRand(0)
3836

3937
if len(initialBoardState.Snakes) > int(m.Meta().MaxPlayers) {
@@ -59,6 +57,23 @@ func (m *SnailModeMap) SetupBoard(initialBoardState *rules.BoardState, settings
5957
return nil
6058
}
6159

60+
// storeTailLocation returns an offboard point that corresponds to the given point.
61+
// This is useful for storing state that can be accessed next turn.
62+
func storeTailLocation(point rules.Point, height int) rules.Point {
63+
return rules.Point{X: point.X, Y: point.Y + height}
64+
}
65+
66+
// getPrevTailLocation returns the onboard point that corresponds to an offboard point.
67+
// This is useful for restoring state that was stored last turn.
68+
func getPrevTailLocation(point rules.Point, height int) rules.Point {
69+
return rules.Point{X: point.X, Y: point.Y - height}
70+
}
71+
72+
// outOfBounds determines if the given point is out of bounds for the current board size
73+
func outOfBounds(p rules.Point, w, h int) bool {
74+
return p.X < 0 || p.Y < 0 || p.X >= w || p.Y >= h
75+
}
76+
6277
// doubleTail determine if the snake has a double stacked tail currently
6378
func doubleTail(snake *rules.Snake) bool {
6479
almostTail := snake.Body[len(snake.Body)-2]
@@ -71,27 +86,15 @@ func isEliminated(s *rules.Snake) bool {
7186
return s.EliminatedCause != rules.NotEliminated
7287
}
7388

74-
// PreUpdateBoard stores the tail position of each snake in memory, to be
75-
// able to place hazards there after the snakes move.
76-
func (m *SnailModeMap) PreUpdateBoard(lastBoardState *rules.BoardState, settings rules.Settings, editor Editor) error {
77-
m.lastTailPositions = make(map[rules.Point]int)
78-
for _, snake := range lastBoardState.Snakes {
79-
if isEliminated(&snake) {
80-
continue
81-
}
82-
// Double tail means that the tail will stay on the same square for more
83-
// than one turn, so we don't want to spawn hazards
84-
if doubleTail(&snake) {
85-
continue
86-
}
87-
m.lastTailPositions[snake.Body[len(snake.Body)-1]] = len(snake.Body)
88-
}
89+
func (m SnailModeMap) PreUpdateBoard(lastBoardState *rules.BoardState, settings rules.Settings, editor Editor) error {
8990
return nil
9091
}
9192

9293
// PostUpdateBoard does the work of placing the hazards along the 'snail tail' of snakes
93-
// This also handles removing one hazards from the current stacks so the hazards tails fade as the snake moves away.
94-
func (m *SnailModeMap) PostUpdateBoard(lastBoardState *rules.BoardState, settings rules.Settings, editor Editor) error {
94+
// This is responsible for saving the current tail location off the board
95+
// and restoring the previous tail position. This also handles removing one hazards from
96+
// the current stacks so the hazards tails fade as the snake moves away.
97+
func (m SnailModeMap) PostUpdateBoard(lastBoardState *rules.BoardState, settings rules.Settings, editor Editor) error {
9598
err := StandardMap{}.PostUpdateBoard(lastBoardState, settings, editor)
9699
if err != nil {
97100
return err
@@ -101,38 +104,79 @@ func (m *SnailModeMap) PostUpdateBoard(lastBoardState *rules.BoardState, setting
101104
// need to be cleared first.
102105
editor.ClearHazards()
103106

107+
// This is a list of all the hazards we want to add for the previous tails
108+
// These were stored off board in the previous turn as a way to save state
109+
// When we add the locations to this list we have already converted the off-board
110+
// points to on-board points
111+
tailLocations := make([]rules.Point, 0, len(lastBoardState.Snakes))
112+
104113
// Count the number of hazards for a given position
114+
// Add non-double tail locations to a slice
105115
hazardCounts := map[rules.Point]int{}
106116
for _, hazard := range lastBoardState.Hazards {
107-
hazardCounts[hazard]++
117+
118+
// discard out of bound
119+
if outOfBounds(hazard, lastBoardState.Width, lastBoardState.Height) {
120+
onBoardTail := getPrevTailLocation(hazard, lastBoardState.Height)
121+
tailLocations = append(tailLocations, onBoardTail)
122+
} else {
123+
hazardCounts[hazard]++
124+
}
108125
}
109126

110127
// Add back existing hazards, but with a stack of 1 less than before.
111128
// This has the effect of making the snail-trail disappear over time.
112129
for hazard, count := range hazardCounts {
130+
113131
for i := 0; i < count-1; i++ {
114132
editor.AddHazard(hazard)
115133
}
116134
}
117135

118-
// Place a new stack of hazards where each snake's tail used to be
119-
NewHazardLoop:
120-
for location, count := range m.lastTailPositions {
136+
// Store a stack of hazards for the tail of each snake. This is stored out
137+
// of bounds and then applied on the next turn. The stack count is equal
138+
// the lenght of the snake.
139+
for _, snake := range lastBoardState.Snakes {
140+
if isEliminated(&snake) {
141+
continue
142+
}
143+
144+
// Double tail means that the tail will stay on the same square for more
145+
// than one turn, so we don't want to spawn hazards
146+
if doubleTail(&snake) {
147+
continue
148+
}
149+
150+
tail := snake.Body[len(snake.Body)-1]
151+
offBoardTail := storeTailLocation(tail, lastBoardState.Height)
152+
for i := 0; i < len(snake.Body); i++ {
153+
editor.AddHazard(offBoardTail)
154+
}
155+
}
156+
157+
// Read offboard tails and move them to the board. The offboard tails are
158+
// stacked based on the length of the snake
159+
for _, p := range tailLocations {
160+
161+
// Skip position if a snakes head occupies it.
162+
// Otherwise hazard shows up in the viewer on top of a snake head, but
163+
// does not damage the snake, which is visually confusing.
164+
isHead := false
121165
for _, snake := range lastBoardState.Snakes {
122166
if isEliminated(&snake) {
123167
continue
124168
}
125169
head := snake.Body[0]
126-
if location.X == head.X && location.Y == head.Y {
127-
// Skip position if a snakes head occupies it.
128-
// Otherwise hazard shows up in the viewer on top of a snake head, but
129-
// does not damage the snake, which is visually confusing.
130-
continue NewHazardLoop
170+
if p.X == head.X && p.Y == head.Y {
171+
isHead = true
172+
break
131173
}
132174
}
133-
for i := 0; i < count; i++ {
134-
editor.AddHazard(location)
175+
if isHead {
176+
continue
135177
}
178+
179+
editor.AddHazard(p)
136180
}
137181

138182
return nil

0 commit comments

Comments
 (0)