From 31faba642ccb088b62422926ba6327801de2c899 Mon Sep 17 00:00:00 2001 From: Rob O'Dwyer Date: Tue, 7 Sep 2021 14:58:10 -0700 Subject: [PATCH] Add missing ruleset params to request body and --debug-requests option (#55) * fix sending of new params * add royale and squad settings to requests * add --debug-requests option * update test for request body and add helper Co-authored-by: Penelope Phippen --- cli/commands/play.go | 49 +++++++++- cli/commands/play_test.go | 4 +- cli/commands/testdata/snake_request_body.json | 90 +++++++++++++++++++ ruleset.go | 11 +-- test_utils.go | 35 ++++++++ 5 files changed, 179 insertions(+), 10 deletions(-) create mode 100644 cli/commands/testdata/snake_request_body.json create mode 100644 test_utils.go diff --git a/cli/commands/play.go b/cli/commands/play.go index 2eb6ef5..622edad 100644 --- a/cli/commands/play.go +++ b/cli/commands/play.go @@ -54,9 +54,29 @@ type BoardResponse struct { Snakes []SnakeResponse `json:"snakes"` } +type GameResponseRulesetSettings struct { + HazardDamagePerTurn int32 `json:"hazardDamagePerTurn"` + FoodSpawnChance int32 `json:"foodSpawnChance"` + MinimumFood int32 `json:"minimumFood"` + RoyaleSettings RoyaleSettings `json:"royale"` + SquadSettings SquadSettings `json:"squad"` +} + +type RoyaleSettings struct { + ShrinkEveryNTurns int32 `json:"shrinkEveryNTurns"` +} + +type SquadSettings struct { + AllowBodyCollisions bool `json:"allowBodyCollisions"` + SharedElimination bool `json:"sharedElimination"` + SharedHealth bool `json:"sharedHealth"` + SharedLength bool `json:"sharedLength"` +} + type GameResponseRuleset struct { - Name string `json:"name"` - Version string `json:"version"` + Name string `json:"name"` + Version string `json:"version"` + Settings GameResponseRulesetSettings `json:"settings"` } type GameResponse struct { @@ -101,6 +121,7 @@ var GameType string var ViewMap bool var Seed int64 var TurnDelay int32 +var DebugRequests bool var FoodSpawnChance int32 var MinimumFood int32 @@ -128,6 +149,7 @@ func init() { playCmd.Flags().BoolVarP(&ViewMap, "viewmap", "v", false, "View the Map Each Turn") playCmd.Flags().Int64VarP(&Seed, "seed", "r", time.Now().UTC().UnixNano(), "Random Seed") playCmd.Flags().Int32VarP(&TurnDelay, "delay", "d", 0, "Turn Delay in Milliseconds") + playCmd.Flags().BoolVar(&DebugRequests, "debug-requests", false, "Log body of all requests sent") playCmd.Flags().Int32Var(&FoodSpawnChance, "foodSpawnChance", 15, "Percentage chance of spawning a new food every round") playCmd.Flags().Int32Var(&MinimumFood, "minimumFood", 1, "Minimum food to keep on the board every turn") @@ -263,6 +285,9 @@ func initializeBoardFromArgs(ruleset rules.Ruleset, snakes []Battlesnake) *rules requestBody := getIndividualBoardStateForSnake(state, snake, ruleset) u, _ := url.ParseRequestURI(snake.URL) u.Path = path.Join(u.Path, "start") + if DebugRequests { + log.Printf("POST %s: %v", u, string(requestBody)) + } _, err = HttpClient.Post(u.String(), "application/json", bytes.NewBuffer(requestBody)) if err != nil { log.Printf("[WARN]: Request to %v failed", u.String()) @@ -326,6 +351,9 @@ func getMoveForSnake(ruleset rules.Ruleset, state *rules.BoardState, snake Battl requestBody := getIndividualBoardStateForSnake(state, snake, ruleset) u, _ := url.ParseRequestURI(snake.URL) u.Path = path.Join(u.Path, "move") + if DebugRequests { + log.Printf("POST %s: %v", u, string(requestBody)) + } res, err := HttpClient.Post(u.String(), "application/json", bytes.NewBuffer(requestBody)) move := snake.LastMove if err != nil { @@ -353,6 +381,9 @@ func sendEndRequest(ruleset rules.Ruleset, state *rules.BoardState, snake Battle requestBody := getIndividualBoardStateForSnake(state, snake, ruleset) u, _ := url.ParseRequestURI(snake.URL) u.Path = path.Join(u.Path, "end") + if DebugRequests { + log.Printf("POST %s: %v", u, string(requestBody)) + } _, err := HttpClient.Post(u.String(), "application/json", bytes.NewBuffer(requestBody)) if err != nil { log.Printf("[WARN]: Request to %v failed", u.String()) @@ -371,6 +402,20 @@ func getIndividualBoardStateForSnake(state *rules.BoardState, snake Battlesnake, Game: GameResponse{Id: GameId, Timeout: Timeout, Ruleset: GameResponseRuleset{ Name: ruleset.Name(), Version: "cli", // TODO: Use GitHub Release Version + Settings: GameResponseRulesetSettings{ + HazardDamagePerTurn: HazardDamagePerTurn, + FoodSpawnChance: FoodSpawnChance, + MinimumFood: MinimumFood, + RoyaleSettings: RoyaleSettings{ + ShrinkEveryNTurns: ShrinkEveryNTurns, + }, + SquadSettings: SquadSettings{ + AllowBodyCollisions: true, + SharedElimination: true, + SharedHealth: true, + SharedLength: true, + }, + }, }}, Turn: Turn, Board: BoardResponse{ diff --git a/cli/commands/play_test.go b/cli/commands/play_test.go index 26dfdc4..b50f8db 100644 --- a/cli/commands/play_test.go +++ b/cli/commands/play_test.go @@ -4,7 +4,6 @@ import ( "testing" "github.com/BattlesnakeOfficial/rules" - "github.com/stretchr/testify/require" ) func TestGetIndividualBoardStateForSnake(t *testing.T) { @@ -18,6 +17,5 @@ func TestGetIndividualBoardStateForSnake(t *testing.T) { snake := Battlesnake{Name: "one", URL: "", ID: "one"} requestBody := getIndividualBoardStateForSnake(state, snake, &rules.StandardRuleset{}) - expected := "{\"game\":{\"id\":\"\",\"timeout\":500,\"ruleset\":{\"name\":\"standard\",\"version\":\"cli\"}},\"turn\":0,\"board\":{\"height\":11,\"width\":11,\"food\":[],\"hazards\":[],\"snakes\":[{\"id\":\"one\",\"name\":\"\",\"health\":0,\"body\":[{\"x\":3,\"y\":3}],\"latency\":\"0\",\"head\":{\"x\":3,\"y\":3},\"length\":1,\"shout\":\"\",\"squad\":\"\"},{\"id\":\"two\",\"name\":\"\",\"health\":0,\"body\":[{\"x\":4,\"y\":3}],\"latency\":\"0\",\"head\":{\"x\":4,\"y\":3},\"length\":1,\"shout\":\"\",\"squad\":\"\"}]},\"you\":{\"id\":\"one\",\"name\":\"\",\"health\":0,\"body\":[{\"x\":3,\"y\":3}],\"latency\":\"0\",\"head\":{\"x\":3,\"y\":3},\"length\":1,\"shout\":\"\",\"squad\":\"\"}}" - require.Equal(t, expected, string(requestBody)) + rules.RequireJSONMatchesFixture(t, "testdata/snake_request_body.json", string(requestBody)) } diff --git a/cli/commands/testdata/snake_request_body.json b/cli/commands/testdata/snake_request_body.json new file mode 100644 index 0000000..34c8a52 --- /dev/null +++ b/cli/commands/testdata/snake_request_body.json @@ -0,0 +1,90 @@ +{ + "game": { + "id": "", + "timeout": 500, + "ruleset": { + "name": "standard", + "version": "cli", + "settings": { + "hazardDamagePerTurn": 14, + "foodSpawnChance": 15, + "minimumFood": 1, + "royale": { + "shrinkEveryNTurns": 25 + }, + "squad": { + "allowBodyCollisions": true, + "sharedElimination": true, + "sharedHealth": true, + "sharedLength": true + } + } + } + }, + "turn": 0, + "board": { + "height": 11, + "width": 11, + "food": [], + "hazards": [], + "snakes": [ + { + "id": "one", + "name": "", + "health": 0, + "body": [ + { + "x": 3, + "y": 3 + } + ], + "latency": "0", + "head": { + "x": 3, + "y": 3 + }, + "length": 1, + "shout": "", + "squad": "" + }, + { + "id": "two", + "name": "", + "health": 0, + "body": [ + { + "x": 4, + "y": 3 + } + ], + "latency": "0", + "head": { + "x": 4, + "y": 3 + }, + "length": 1, + "shout": "", + "squad": "" + } + ] + }, + "you": { + "id": "one", + "name": "", + "health": 0, + "body": [ + { + "x": 3, + "y": 3 + } + ], + "latency": "0", + "head": { + "x": 3, + "y": 3 + }, + "length": 1, + "shout": "", + "squad": "" + } +} \ No newline at end of file diff --git a/ruleset.go b/ruleset.go index 0486abd..63c3216 100644 --- a/ruleset.go +++ b/ruleset.go @@ -39,11 +39,12 @@ type Point struct { } type Snake struct { - ID string - Body []Point - Health int32 - EliminatedCause string - EliminatedBy string + ID string + Body []Point + Health int32 + EliminatedCause string + EliminatedOnTurn int32 + EliminatedBy string } type SnakeMove struct { diff --git a/test_utils.go b/test_utils.go new file mode 100644 index 0000000..7913efc --- /dev/null +++ b/test_utils.go @@ -0,0 +1,35 @@ +package rules + +import ( + "bytes" + "encoding/json" + "flag" + "io/ioutil" + "testing" + + "github.com/stretchr/testify/require" +) + +var updateFixtures = flag.Bool("update-fixtures", false, "Regenerate fixtures in testdata based on current test output") + +// RequireJSONMatchesFixture asserts that the JSON text in actual matches the +// JSON read from filename, without taking into account whitespace and +// ordering. Files can be specified relative to the calling test (e.g. +// testdata/example.json). To regenerate the expected test data automatically +// after making a code change, pass the `-update-fixtures` flag to `go test`. +func RequireJSONMatchesFixture(t *testing.T, filename string, actual string) { + t.Helper() + + if *updateFixtures { + var indented bytes.Buffer + err := json.Indent(&indented, []byte(actual), "", " ") + require.NoError(t, err, "Failed to indent JSON") + err = ioutil.WriteFile(filename, indented.Bytes(), 0644) + require.NoError(t, err, "Failed to update fixture", filename) + } + + expectedData, err := ioutil.ReadFile(filename) + require.NoError(t, err, "Failed to read fixture", filename) + + require.JSONEq(t, string(expectedData), actual) +}