Skip to content

Commit 82e1999

Browse files
DEV-1761: New rules API (#118)
* DEV-1761: Clean up Ruleset interface (#115) * remove legacy ruleset types and simplify ruleset interface * remove unnecessary settings argument from Ruleset interface * decouple rules.Settings from client API and store settings as strings * DEV 1761: Add new BoardState and Point fields (#117) * add Point.TTL, Point.Value, GameState and PointState to BoardState * allow maps to access BoardState.GameState,PointState * add PreUpdateBoard and refactor snail_mode with it * fix bug where an extra turn was printed to the console * fix formatting * fix lint errors Co-authored-by: JonathanArns <jonathan.arns@googlemail.com>
1 parent 639362e commit 82e1999

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+1349
-1610
lines changed

board.go

Lines changed: 88 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,36 @@ package rules
22

33
import "fmt"
44

5+
// BoardState represents the internal state of a game board.
6+
// NOTE: use NewBoardState to construct these to ensure fields are initialized
7+
// correctly and that tests are resilient to changes to this type.
58
type BoardState struct {
69
Turn int
710
Height int
811
Width int
912
Food []Point
1013
Snakes []Snake
1114
Hazards []Point
15+
16+
// Generic game-level state for maps and rules stages to persist data between turns.
17+
GameState map[string]string
18+
19+
// Numeric state keyed to specific points, also persisted between turns.
20+
PointState map[Point]int
1221
}
1322

1423
type Point struct {
15-
X int
16-
Y int
24+
X int `json:"X"`
25+
Y int `json:"Y"`
26+
TTL int `json:"TTL,omitempty"`
27+
Value int `json:"Value,omitempty"`
1728
}
1829

1930
// Makes it easier to copy sample points out of Go logs and test failures.
2031
func (p Point) GoString() string {
32+
if p.TTL != 0 || p.Value != 0 {
33+
return fmt.Sprintf("{X:%d, Y:%d, TTL:%d, Value:%d}", p.X, p.Y, p.TTL, p.Value)
34+
}
2135
return fmt.Sprintf("{X:%d, Y:%d}", p.X, p.Y)
2236
}
2337

@@ -33,24 +47,34 @@ type Snake struct {
3347
// NewBoardState returns an empty but fully initialized BoardState
3448
func NewBoardState(width, height int) *BoardState {
3549
return &BoardState{
36-
Turn: 0,
37-
Height: height,
38-
Width: width,
39-
Food: []Point{},
40-
Snakes: []Snake{},
41-
Hazards: []Point{},
50+
Turn: 0,
51+
Height: height,
52+
Width: width,
53+
Food: []Point{},
54+
Snakes: []Snake{},
55+
Hazards: []Point{},
56+
GameState: map[string]string{},
57+
PointState: map[Point]int{},
4258
}
4359
}
4460

4561
// Clone returns a deep copy of prevState that can be safely modified without affecting the original
4662
func (prevState *BoardState) Clone() *BoardState {
4763
nextState := &BoardState{
48-
Turn: prevState.Turn,
49-
Height: prevState.Height,
50-
Width: prevState.Width,
51-
Food: append([]Point{}, prevState.Food...),
52-
Snakes: make([]Snake, len(prevState.Snakes)),
53-
Hazards: append([]Point{}, prevState.Hazards...),
64+
Turn: prevState.Turn,
65+
Height: prevState.Height,
66+
Width: prevState.Width,
67+
Food: append([]Point{}, prevState.Food...),
68+
Snakes: make([]Snake, len(prevState.Snakes)),
69+
Hazards: append([]Point{}, prevState.Hazards...),
70+
GameState: make(map[string]string, len(prevState.GameState)),
71+
PointState: make(map[Point]int, len(prevState.PointState)),
72+
}
73+
for key, value := range prevState.GameState {
74+
nextState.GameState[key] = value
75+
}
76+
for key, value := range prevState.PointState {
77+
nextState.PointState[key] = value
5478
}
5579
for i := 0; i < len(prevState.Snakes); i++ {
5680
nextState.Snakes[i].ID = prevState.Snakes[i].ID
@@ -63,6 +87,42 @@ func (prevState *BoardState) Clone() *BoardState {
6387
return nextState
6488
}
6589

90+
// Builder method to set Turn and return the modified BoardState.
91+
func (state *BoardState) WithTurn(turn int) *BoardState {
92+
state.Turn = turn
93+
return state
94+
}
95+
96+
// Builder method to set Food and return the modified BoardState.
97+
func (state *BoardState) WithFood(food []Point) *BoardState {
98+
state.Food = food
99+
return state
100+
}
101+
102+
// Builder method to set Hazards and return the modified BoardState.
103+
func (state *BoardState) WithHazards(hazards []Point) *BoardState {
104+
state.Hazards = hazards
105+
return state
106+
}
107+
108+
// Builder method to set Snakes and return the modified BoardState.
109+
func (state *BoardState) WithSnakes(snakes []Snake) *BoardState {
110+
state.Snakes = snakes
111+
return state
112+
}
113+
114+
// Builder method to set State and return the modified BoardState.
115+
func (state *BoardState) WithGameState(gameState map[string]string) *BoardState {
116+
state.GameState = gameState
117+
return state
118+
}
119+
120+
// Builder method to set PointState and return the modified BoardState.
121+
func (state *BoardState) WithPointState(pointState map[Point]int) *BoardState {
122+
state.PointState = pointState
123+
return state
124+
}
125+
66126
// CreateDefaultBoardState is a convenience function for fully initializing a
67127
// "default" board state with snakes and food.
68128
// In a real game, the engine may generate the board without calling this
@@ -120,16 +180,16 @@ func PlaceSnakesFixed(rand Rand, b *BoardState, snakeIDs []string) error {
120180
// Create start 8 points
121181
mn, md, mx := 1, (b.Width-1)/2, b.Width-2
122182
cornerPoints := []Point{
123-
{mn, mn},
124-
{mn, mx},
125-
{mx, mn},
126-
{mx, mx},
183+
{X: mn, Y: mn},
184+
{X: mn, Y: mx},
185+
{X: mx, Y: mn},
186+
{X: mx, Y: mx},
127187
}
128188
cardinalPoints := []Point{
129-
{mn, md},
130-
{md, mn},
131-
{md, mx},
132-
{mx, md},
189+
{X: mn, Y: md},
190+
{X: md, Y: mn},
191+
{X: md, Y: mx},
192+
{X: mx, Y: md},
133193
}
134194

135195
// Sanity check
@@ -325,7 +385,7 @@ func PlaceFoodAutomatically(rand Rand, b *BoardState) error {
325385

326386
// Deprecated: will be replaced by maps.PlaceFoodFixed
327387
func PlaceFoodFixed(rand Rand, b *BoardState) error {
328-
centerCoord := Point{(b.Width - 1) / 2, (b.Height - 1) / 2}
388+
centerCoord := Point{X: (b.Width - 1) / 2, Y: (b.Height - 1) / 2}
329389

330390
isSmallBoard := b.Width*b.Height < BoardSizeMedium*BoardSizeMedium
331391
// Up to 4 snakes can be placed such that food is nearby on small boards.
@@ -335,10 +395,10 @@ func PlaceFoodFixed(rand Rand, b *BoardState) error {
335395
for i := 0; i < len(b.Snakes); i++ {
336396
snakeHead := b.Snakes[i].Body[0]
337397
possibleFoodLocations := []Point{
338-
{snakeHead.X - 1, snakeHead.Y - 1},
339-
{snakeHead.X - 1, snakeHead.Y + 1},
340-
{snakeHead.X + 1, snakeHead.Y - 1},
341-
{snakeHead.X + 1, snakeHead.Y + 1},
398+
{X: snakeHead.X - 1, Y: snakeHead.Y - 1},
399+
{X: snakeHead.X - 1, Y: snakeHead.Y + 1},
400+
{X: snakeHead.X + 1, Y: snakeHead.Y - 1},
401+
{X: snakeHead.X + 1, Y: snakeHead.Y + 1},
342402
}
343403

344404
// Remove any invalid/unwanted positions
@@ -448,7 +508,7 @@ func GetEvenUnoccupiedPoints(b *BoardState) []Point {
448508

449509
// removeCenterCoord filters out the board's center point from a list of points.
450510
func removeCenterCoord(b *BoardState, points []Point) []Point {
451-
centerCoord := Point{(b.Width - 1) / 2, (b.Height - 1) / 2}
511+
centerCoord := Point{X: (b.Width - 1) / 2, Y: (b.Height - 1) / 2}
452512
var noCenterPoints []Point
453513
for _, p := range points {
454514
if p != centerCoord {

0 commit comments

Comments
 (0)