From f31bdff4b87b11c11fde59167de86fc756e0e7db Mon Sep 17 00:00:00 2001 From: John Oram Date: Fri, 2 Jul 2021 20:00:19 -0700 Subject: [PATCH] add ruleset data to the calls made by the cli (name and version) (#32) * add ruleset data to the cli (name and version) * remove double ruleset tracking with royale mode --- cli/commands/play.go | 59 ++++++++++++++++++++------------------- cli/commands/play_test.go | 22 +++++++++++++++ royale.go | 4 +++ royale_test.go | 5 ++++ ruleset.go | 2 ++ solo.go | 2 ++ solo_test.go | 5 ++++ squad.go | 3 ++ squad_test.go | 5 ++++ standard.go | 3 ++ standard_test.go | 5 ++++ 11 files changed, 86 insertions(+), 29 deletions(-) create mode 100644 cli/commands/play_test.go diff --git a/cli/commands/play.go b/cli/commands/play.go index 4d07e7b..9f68292 100644 --- a/cli/commands/play.go +++ b/cli/commands/play.go @@ -52,9 +52,15 @@ type BoardResponse struct { Snakes []SnakeResponse `json:"snakes"` } +type GameResponseRuleset struct { + Name string `json:"name"` + Version string `json:"version"` +} + type GameResponse struct { - Id string `json:"id"` - Timeout int32 `json:"timeout"` + Id string `json:"id"` + Timeout int32 `json:"timeout"` + Ruleset GameResponseRuleset `json:"ruleset"` } type ResponsePayload struct { @@ -125,9 +131,8 @@ var run = func(cmd *cobra.Command, args []string) { snakes := buildSnakesFromOptions() var ruleset rules.Ruleset - var royale rules.RoyaleRuleset var outOfBounds []rules.Point - ruleset, _ = getRuleset(Seed, Turn, snakes) + ruleset = getRuleset(Seed, Turn, snakes) state := initializeBoardFromArgs(ruleset, snakes) for _, snake := range snakes { Battlesnakes[snake.ID] = snake @@ -135,8 +140,8 @@ var run = func(cmd *cobra.Command, args []string) { for v := false; !v; v, _ = ruleset.IsGameOver(state) { Turn++ - ruleset, royale = getRuleset(Seed, Turn, snakes) - state, outOfBounds = createNextBoardState(ruleset, royale, state, outOfBounds, snakes) + ruleset = getRuleset(Seed, Turn, snakes) + state = createNextBoardState(ruleset, state, outOfBounds, snakes) if ViewMap { printMap(state, outOfBounds, Turn) } else { @@ -153,7 +158,7 @@ var run = func(cmd *cobra.Command, args []string) { if snake.EliminatedCause == rules.NotEliminated { isDraw = false winner = Battlesnakes[snake.ID].Name - sendEndRequest(state, Battlesnakes[snake.ID]) + sendEndRequest(ruleset, state, Battlesnakes[snake.ID]) } } @@ -165,7 +170,7 @@ var run = func(cmd *cobra.Command, args []string) { } } -func getRuleset(seed int64, gameTurn int32, snakes []Battlesnake) (rules.Ruleset, rules.RoyaleRuleset) { +func getRuleset(seed int64, gameTurn int32, snakes []Battlesnake) rules.Ruleset { var ruleset rules.Ruleset var royale rules.RoyaleRuleset @@ -208,7 +213,7 @@ func getRuleset(seed int64, gameTurn int32, snakes []Battlesnake) (rules.Ruleset default: ruleset = &standard } - return ruleset, royale + return ruleset } func initializeBoardFromArgs(ruleset rules.Ruleset, snakes []Battlesnake) *rules.BoardState { @@ -229,7 +234,7 @@ func initializeBoardFromArgs(ruleset rules.Ruleset, snakes []Battlesnake) *rules panic(err) } for _, snake := range snakes { - requestBody := getIndividualBoardStateForSnake(state, snake, nil) + requestBody := getIndividualBoardStateForSnake(state, snake, nil, ruleset) u, _ := url.ParseRequestURI(snake.URL) u.Path = path.Join(u.Path, "start") _, err = HttpClient.Post(u.String(), "application/json", bytes.NewBuffer(requestBody)) @@ -240,16 +245,16 @@ func initializeBoardFromArgs(ruleset rules.Ruleset, snakes []Battlesnake) *rules return state } -func createNextBoardState(ruleset rules.Ruleset, royale rules.RoyaleRuleset, state *rules.BoardState, outOfBounds []rules.Point, snakes []Battlesnake) (*rules.BoardState, []rules.Point) { +func createNextBoardState(ruleset rules.Ruleset, state *rules.BoardState, outOfBounds []rules.Point, snakes []Battlesnake) *rules.BoardState { var moves []rules.SnakeMove if Sequential { for _, snake := range snakes { - moves = append(moves, getMoveForSnake(state, snake, outOfBounds)) + moves = append(moves, getMoveForSnake(ruleset, state, snake, outOfBounds)) } } else { c := make(chan rules.SnakeMove, len(snakes)) for _, snake := range snakes { - go getConcurrentMoveForSnake(state, snake, outOfBounds, c) + go getConcurrentMoveForSnake(ruleset, state, snake, outOfBounds, c) } for range snakes { moves = append(moves, <-c) @@ -260,27 +265,20 @@ func createNextBoardState(ruleset rules.Ruleset, royale rules.RoyaleRuleset, sta snake.LastMove = move.Move Battlesnakes[move.ID] = snake } - if GameType == "royale" { - _, err := royale.CreateNextBoardState(state, moves) - if err != nil { - log.Panic("[PANIC]: Error Producing Next Royale Board State") - panic(err) - } - } state, err := ruleset.CreateNextBoardState(state, moves) if err != nil { log.Panic("[PANIC]: Error Producing Next Board State") panic(err) } - return state, royale.OutOfBounds + return state } -func getConcurrentMoveForSnake(state *rules.BoardState, snake Battlesnake, outOfBounds []rules.Point, c chan rules.SnakeMove) { - c <- getMoveForSnake(state, snake, outOfBounds) +func getConcurrentMoveForSnake(ruleset rules.Ruleset, state *rules.BoardState, snake Battlesnake, outOfBounds []rules.Point, c chan rules.SnakeMove) { + c <- getMoveForSnake(ruleset, state, snake, outOfBounds) } -func getMoveForSnake(state *rules.BoardState, snake Battlesnake, outOfBounds []rules.Point) rules.SnakeMove { - requestBody := getIndividualBoardStateForSnake(state, snake, outOfBounds) +func getMoveForSnake(ruleset rules.Ruleset, state *rules.BoardState, snake Battlesnake, outOfBounds []rules.Point) rules.SnakeMove { + requestBody := getIndividualBoardStateForSnake(state, snake, outOfBounds, ruleset) u, _ := url.ParseRequestURI(snake.URL) u.Path = path.Join(u.Path, "move") res, err := HttpClient.Post(u.String(), "application/json", bytes.NewBuffer(requestBody)) @@ -306,8 +304,8 @@ func getMoveForSnake(state *rules.BoardState, snake Battlesnake, outOfBounds []r return rules.SnakeMove{ID: snake.ID, Move: move} } -func sendEndRequest(state *rules.BoardState, snake Battlesnake) { - requestBody := getIndividualBoardStateForSnake(state, snake, nil) +func sendEndRequest(ruleset rules.Ruleset, state *rules.BoardState, snake Battlesnake) { + requestBody := getIndividualBoardStateForSnake(state, snake, nil, ruleset) u, _ := url.ParseRequestURI(snake.URL) u.Path = path.Join(u.Path, "end") _, err := HttpClient.Post(u.String(), "application/json", bytes.NewBuffer(requestBody)) @@ -316,7 +314,7 @@ func sendEndRequest(state *rules.BoardState, snake Battlesnake) { } } -func getIndividualBoardStateForSnake(state *rules.BoardState, snake Battlesnake, outOfBounds []rules.Point) []byte { +func getIndividualBoardStateForSnake(state *rules.BoardState, snake Battlesnake, outOfBounds []rules.Point, ruleset rules.Ruleset) []byte { var youSnake rules.Snake for _, snk := range state.Snakes { if snake.ID == snk.ID { @@ -325,7 +323,10 @@ func getIndividualBoardStateForSnake(state *rules.BoardState, snake Battlesnake, } } response := ResponsePayload{ - Game: GameResponse{Id: GameId, Timeout: Timeout}, + Game: GameResponse{Id: GameId, Timeout: Timeout, Ruleset: GameResponseRuleset{ + Name: ruleset.Name(), + Version: ruleset.Version(), + }}, Turn: Turn, Board: BoardResponse{ Height: state.Height, diff --git a/cli/commands/play_test.go b/cli/commands/play_test.go new file mode 100644 index 0000000..d0ca048 --- /dev/null +++ b/cli/commands/play_test.go @@ -0,0 +1,22 @@ +package commands + +import ( + "github.com/BattlesnakeOfficial/rules" + "github.com/stretchr/testify/require" + "testing" +) + +func TestGetIndividualBoardStateForSnake(t *testing.T) { + s1 := rules.Snake{ID: "one", Body: []rules.Point{{X: 3, Y: 3}}} + s2 := rules.Snake{ID: "two", Body: []rules.Point{{X: 4, Y: 3}}} + state := &rules.BoardState{ + Height: 11, + Width: 11, + Snakes: []rules.Snake{s1, s2}, + } + bs := Battlesnake{Name: "one", URL: "", ID: "one"} + requestBody := getIndividualBoardStateForSnake(state, bs, nil, &rules.StandardRuleset{}) + + expected := "{\"game\":{\"id\":\"\",\"timeout\":500,\"ruleset\":{\"name\":\"standard\",\"version\":\"1.0.0\"}},\"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)) +} diff --git a/royale.go b/royale.go index e61bf7a..34fedb5 100644 --- a/royale.go +++ b/royale.go @@ -55,6 +55,10 @@ func (r *RoyaleRuleset) CreateNextBoardState(prevState *BoardState, moves []Snak return nextBoardState, nil } +func (r *RoyaleRuleset) Name() string { return "royale" } + +func (r *RoyaleRuleset) Version() string { return "1.0.0" } + func (r *RoyaleRuleset) populateOutOfBounds(b *BoardState, turn int32) error { r.OutOfBounds = []Point{} diff --git a/royale_test.go b/royale_test.go index 0b971c7..5e955bf 100644 --- a/royale_test.go +++ b/royale_test.go @@ -23,6 +23,11 @@ func TestRoyaleDefaultSanity(t *testing.T) { require.NoError(t, err) } +func TestRoyaleName(t *testing.T) { + r := RoyaleRuleset{} + require.Equal(t, "royale", r.Name()) +} + func TestRoyaleOutOfBounds(t *testing.T) { seed := int64(25543234525) tests := []struct { diff --git a/ruleset.go b/ruleset.go index e97601b..89cbb9f 100644 --- a/ruleset.go +++ b/ruleset.go @@ -62,4 +62,6 @@ type Ruleset interface { CreateInitialBoardState(width int32, height int32, snakeIDs []string) (*BoardState, error) CreateNextBoardState(prevState *BoardState, moves []SnakeMove) (*BoardState, error) IsGameOver(state *BoardState) (bool, error) + Name() string + Version() string } diff --git a/solo.go b/solo.go index fab5e37..15a05ca 100644 --- a/solo.go +++ b/solo.go @@ -12,3 +12,5 @@ func (r *SoloRuleset) IsGameOver(b *BoardState) (bool, error) { } return true, nil } +func (r *SoloRuleset) Name() string { return "solo" } +func (r *SoloRuleset) Version() string { return "1.0.0" } diff --git a/solo_test.go b/solo_test.go index cbb3ef9..289e821 100644 --- a/solo_test.go +++ b/solo_test.go @@ -10,6 +10,11 @@ func TestSoloRulesetInterface(t *testing.T) { var _ Ruleset = (*SoloRuleset)(nil) } +func TestSoloName(t *testing.T) { + r := SoloRuleset{} + require.Equal(t, "solo", r.Name()) +} + func TestSoloCreateNextBoardStateSanity(t *testing.T) { boardState := &BoardState{} r := SoloRuleset{} diff --git a/squad.go b/squad.go index 6468b7c..5e8290d 100644 --- a/squad.go +++ b/squad.go @@ -18,6 +18,9 @@ type SquadRuleset struct { const EliminatedBySquad = "squad-eliminated" +func (r *SquadRuleset) Name() string { return "squad" } +func (r *SquadRuleset) Version() string { return "1.0.0" } + func (r *SquadRuleset) CreateNextBoardState(prevState *BoardState, moves []SnakeMove) (*BoardState, error) { nextBoardState, err := r.StandardRuleset.CreateNextBoardState(prevState, moves) if err != nil { diff --git a/squad_test.go b/squad_test.go index a6c7a31..865e36d 100644 --- a/squad_test.go +++ b/squad_test.go @@ -10,6 +10,11 @@ func TestSquadRulesetInterface(t *testing.T) { var _ Ruleset = (*SquadRuleset)(nil) } +func TestSquadName(t *testing.T) { + r := SquadRuleset{} + require.Equal(t, "squad", r.Name()) +} + func TestSquadCreateNextBoardStateSanity(t *testing.T) { boardState := &BoardState{} r := SquadRuleset{} diff --git a/standard.go b/standard.go index 14d1c43..d5ffe80 100644 --- a/standard.go +++ b/standard.go @@ -10,6 +10,9 @@ type StandardRuleset struct { MinimumFood int32 } +func (r *StandardRuleset) Name() string { return "standard" } +func (r *StandardRuleset) Version() string { return "1.0.0" } + func (r *StandardRuleset) CreateInitialBoardState(width int32, height int32, snakeIDs []string) (*BoardState, error) { initialBoardState := &BoardState{ Height: height, diff --git a/standard_test.go b/standard_test.go index 1044a61..c938d92 100644 --- a/standard_test.go +++ b/standard_test.go @@ -34,6 +34,11 @@ func TestSanity(t *testing.T) { require.Len(t, state.Snakes, 0) } +func TestStandardName(t *testing.T) { + r := StandardRuleset{} + require.Equal(t, "standard", r.Name()) +} + func TestCreateInitialBoardState(t *testing.T) { tests := []struct { Height int32