DEV 1666: Fix /end requests and clean up logging (#109)
* ensure /end request is always called, and refactor win/draw logic * clean up logging and error handling during initialization * automatically generate friendly snake names * title-case snake names * print out list of alive snake names instead of count * log snake names, IDs, and URLs at startup * print out state for turn zero
This commit is contained in:
parent
006f394355
commit
09aea9c49d
10 changed files with 317 additions and 151 deletions
|
|
@ -5,13 +5,12 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
|
|
@ -22,6 +21,7 @@ import (
|
|||
"github.com/google/uuid"
|
||||
"github.com/pkg/browser"
|
||||
"github.com/spf13/cobra"
|
||||
log "github.com/spf13/jwalterweatherman"
|
||||
)
|
||||
|
||||
// Used to store state for each SnakeState while running a local game
|
||||
|
|
@ -55,7 +55,6 @@ type GameState struct {
|
|||
UseColor bool
|
||||
Seed int64
|
||||
TurnDelay int
|
||||
DebugRequests bool
|
||||
Output string
|
||||
ViewInBrowser bool
|
||||
BoardURL string
|
||||
|
|
@ -98,7 +97,6 @@ func NewPlayCommand() *cobra.Command {
|
|||
playCmd.Flags().Int64VarP(&gameState.Seed, "seed", "r", time.Now().UTC().UnixNano(), "Random Seed")
|
||||
playCmd.Flags().IntVarP(&gameState.TurnDelay, "delay", "d", 0, "Turn Delay in Milliseconds")
|
||||
playCmd.Flags().IntVarP(&gameState.TurnDuration, "duration", "D", 0, "Minimum Turn Duration in Milliseconds")
|
||||
playCmd.Flags().BoolVar(&gameState.DebugRequests, "debug-requests", false, "Log body of all requests sent")
|
||||
playCmd.Flags().StringVarP(&gameState.Output, "output", "o", "", "File path to output game state to. Existing files will be overwritten")
|
||||
playCmd.Flags().BoolVar(&gameState.ViewInBrowser, "browser", false, "View the game in the browser using the Battlesnake game board")
|
||||
playCmd.Flags().StringVar(&gameState.BoardURL, "board-url", "https://board.battlesnake.com", "Base URL for the game board when using --browser")
|
||||
|
|
@ -129,7 +127,7 @@ func (gameState *GameState) initialize() {
|
|||
// Load game map
|
||||
gameMap, err := maps.GetMap(gameState.MapName)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to load game map %#v: %v", gameState.MapName, err)
|
||||
log.ERROR.Fatalf("Failed to load game map %#v: %v", gameState.MapName, err)
|
||||
}
|
||||
gameState.gameMap = gameMap
|
||||
|
||||
|
|
@ -173,10 +171,6 @@ func (gameState *GameState) Run() {
|
|||
isDraw: false,
|
||||
}
|
||||
|
||||
if gameState.ViewMap {
|
||||
gameState.printMap(boardState)
|
||||
}
|
||||
|
||||
boardGame := board.Game{
|
||||
ID: gameState.gameID,
|
||||
Status: "running",
|
||||
|
|
@ -194,23 +188,30 @@ func (gameState *GameState) Run() {
|
|||
if gameState.ViewInBrowser {
|
||||
serverURL, err := boardServer.Listen()
|
||||
if err != nil {
|
||||
log.Fatalf("Error starting HTTP server: %v", err)
|
||||
log.ERROR.Fatalf("Error starting HTTP server: %v", err)
|
||||
}
|
||||
log.Printf("Board server listening on %s", serverURL)
|
||||
defer boardServer.Shutdown()
|
||||
log.INFO.Printf("Board server listening on %s", serverURL)
|
||||
|
||||
boardURL := fmt.Sprintf(gameState.BoardURL+"?engine=%s&game=%s&autoplay=true", serverURL, gameState.gameID)
|
||||
|
||||
log.Printf("Opening board URL: %s", boardURL)
|
||||
log.INFO.Printf("Opening board URL: %s", boardURL)
|
||||
if err := browser.OpenURL(boardURL); err != nil {
|
||||
log.Printf("Failed to open browser: %v", err)
|
||||
log.ERROR.Printf("Failed to open browser: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if gameState.ViewInBrowser {
|
||||
// send turn zero to websocket server
|
||||
boardServer.SendEvent(gameState.buildFrameEvent(boardState))
|
||||
}
|
||||
|
||||
log.INFO.Printf("Ruleset: %v, Seed: %v", gameState.GameType, gameState.Seed)
|
||||
|
||||
if gameState.ViewMap {
|
||||
gameState.printMap(boardState)
|
||||
} else {
|
||||
gameState.printState(boardState)
|
||||
}
|
||||
|
||||
var endTime time.Time
|
||||
for v := false; !v; v, _ = gameState.ruleset.IsGameOver(boardState) {
|
||||
if gameState.TurnDuration > 0 {
|
||||
|
|
@ -238,7 +239,7 @@ func (gameState *GameState) Run() {
|
|||
if gameState.ViewMap {
|
||||
gameState.printMap(boardState)
|
||||
} else {
|
||||
log.Printf("[%v]: State: %v\n", boardState.Turn, boardState)
|
||||
gameState.printState(boardState)
|
||||
}
|
||||
|
||||
if gameState.TurnDelay > 0 {
|
||||
|
|
@ -254,36 +255,29 @@ func (gameState *GameState) Run() {
|
|||
}
|
||||
}
|
||||
|
||||
isDraw := true
|
||||
if gameState.GameType == "solo" {
|
||||
log.Printf("[DONE]: Game completed after %v turns.", boardState.Turn)
|
||||
if exportGame {
|
||||
// These checks for exportGame are present to avoid vacuuming up RAM when an export is not requred.
|
||||
for _, snakeState := range gameState.snakeStates {
|
||||
gameExporter.winner = snakeState
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var winner SnakeState
|
||||
for _, snake := range boardState.Snakes {
|
||||
snakeState := gameState.snakeStates[snake.ID]
|
||||
if snake.EliminatedCause == rules.NotEliminated {
|
||||
isDraw = false
|
||||
winner = snakeState
|
||||
}
|
||||
gameState.sendEndRequest(boardState, snakeState)
|
||||
gameExporter.isDraw = false
|
||||
|
||||
if len(gameState.snakeStates) > 1 {
|
||||
// A draw is possible if there is more than one snake in the game.
|
||||
gameExporter.isDraw = true
|
||||
}
|
||||
|
||||
for _, snake := range boardState.Snakes {
|
||||
snakeState := gameState.snakeStates[snake.ID]
|
||||
if snake.EliminatedCause == rules.NotEliminated {
|
||||
gameExporter.isDraw = false
|
||||
gameExporter.winner = snakeState
|
||||
}
|
||||
|
||||
if isDraw {
|
||||
log.Printf("[DONE]: Game completed after %v turns. It was a draw.", boardState.Turn)
|
||||
} else {
|
||||
log.Printf("[DONE]: Game completed after %v turns. %v is the winner.", boardState.Turn, winner.Name)
|
||||
}
|
||||
if exportGame {
|
||||
gameExporter.winner = winner
|
||||
gameExporter.isDraw = isDraw
|
||||
}
|
||||
gameState.sendEndRequest(boardState, snakeState)
|
||||
}
|
||||
|
||||
if gameExporter.isDraw {
|
||||
log.INFO.Printf("Game completed after %v turns. It was a draw.", boardState.Turn)
|
||||
} else if gameExporter.winner.Name != "" {
|
||||
log.INFO.Printf("Game completed after %v turns. %v was the winner.", boardState.Turn, gameExporter.winner.Name)
|
||||
} else {
|
||||
log.INFO.Printf("Game completed after %v turns.", boardState.Turn)
|
||||
}
|
||||
|
||||
if gameState.ViewInBrowser {
|
||||
|
|
@ -296,14 +290,9 @@ func (gameState *GameState) Run() {
|
|||
if exportGame {
|
||||
err := gameExporter.FlushToFile(gameState.Output, "JSONL")
|
||||
if err != nil {
|
||||
log.Printf("[WARN]: Unable to export game. Reason: %v\n", err.Error())
|
||||
os.Exit(1)
|
||||
log.ERROR.Fatalf("Unable to export game. Reason: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if gameState.ViewInBrowser {
|
||||
boardServer.Shutdown()
|
||||
}
|
||||
}
|
||||
|
||||
func (gameState *GameState) initializeBoardFromArgs() *rules.BoardState {
|
||||
|
|
@ -313,11 +302,11 @@ func (gameState *GameState) initializeBoardFromArgs() *rules.BoardState {
|
|||
}
|
||||
boardState, err := maps.SetupBoard(gameState.gameMap.ID(), gameState.ruleset.Settings(), gameState.Width, gameState.Height, snakeIds)
|
||||
if err != nil {
|
||||
log.Fatalf("Error Initializing Board State: %v", err)
|
||||
log.ERROR.Fatalf("Error Initializing Board State: %v", err)
|
||||
}
|
||||
boardState, err = gameState.ruleset.ModifyInitialBoardState(boardState)
|
||||
if err != nil {
|
||||
log.Fatalf("Error Initializing Board State: %v", err)
|
||||
log.ERROR.Fatalf("Error Initializing Board State: %v", err)
|
||||
}
|
||||
|
||||
for _, snakeState := range gameState.snakeStates {
|
||||
|
|
@ -325,12 +314,10 @@ func (gameState *GameState) initializeBoardFromArgs() *rules.BoardState {
|
|||
requestBody := serialiseSnakeRequest(snakeRequest)
|
||||
u, _ := url.ParseRequestURI(snakeState.URL)
|
||||
u.Path = path.Join(u.Path, "start")
|
||||
if gameState.DebugRequests {
|
||||
log.Printf("POST %s: %v", u, string(requestBody))
|
||||
}
|
||||
log.DEBUG.Printf("POST %s: %v", u, string(requestBody))
|
||||
_, err = gameState.httpClient.Post(u.String(), "application/json", bytes.NewBuffer(requestBody))
|
||||
if err != nil {
|
||||
log.Printf("[WARN]: Request to %v failed", u.String())
|
||||
log.WARN.Printf("Request to %v failed", u.String())
|
||||
}
|
||||
}
|
||||
return boardState
|
||||
|
|
@ -376,12 +363,12 @@ func (gameState *GameState) createNextBoardState(boardState *rules.BoardState) *
|
|||
}
|
||||
boardState, err := gameState.ruleset.CreateNextBoardState(boardState, moves)
|
||||
if err != nil {
|
||||
log.Fatalf("Error producing next board state: %v", err)
|
||||
log.ERROR.Fatalf("Error producing next board state: %v", err)
|
||||
}
|
||||
|
||||
boardState, err = maps.UpdateBoard(gameState.gameMap.ID(), boardState, gameState.ruleset.Settings())
|
||||
if err != nil {
|
||||
log.Fatalf("Error updating board with game map: %v", err)
|
||||
log.ERROR.Fatalf("Error updating board with game map: %v", err)
|
||||
}
|
||||
|
||||
boardState.Turn += 1
|
||||
|
|
@ -394,54 +381,52 @@ func (gameState *GameState) getMoveForSnake(boardState *rules.BoardState, snakeS
|
|||
requestBody := serialiseSnakeRequest(snakeRequest)
|
||||
u, _ := url.ParseRequestURI(snakeState.URL)
|
||||
u.Path = path.Join(u.Path, "move")
|
||||
if gameState.DebugRequests {
|
||||
log.Printf("POST %s: %v", u, string(requestBody))
|
||||
}
|
||||
log.DEBUG.Printf("POST %s: %v", u, string(requestBody))
|
||||
res, err := gameState.httpClient.Post(u.String(), "application/json", bytes.NewBuffer(requestBody))
|
||||
|
||||
// Use snake's last move as the default in case of an error
|
||||
snakeMove := rules.SnakeMove{ID: snakeState.ID, Move: snakeState.LastMove}
|
||||
|
||||
if err != nil {
|
||||
log.Printf(
|
||||
"[WARN]: Request to %v failed\n"+
|
||||
"\tError: %s\n", u.String(), err)
|
||||
log.WARN.Printf(
|
||||
"Request to %v failed\n"+
|
||||
"\tError: %s", u.String(), err)
|
||||
return snakeMove
|
||||
}
|
||||
if res.Body == nil {
|
||||
log.Printf(
|
||||
"[WARN]: Failed to parse response from %v\n"+
|
||||
"\tError: body is empty\n", u.String())
|
||||
log.WARN.Printf(
|
||||
"Failed to parse response from %v\n"+
|
||||
"\tError: body is empty", u.String())
|
||||
return snakeMove
|
||||
}
|
||||
defer res.Body.Close()
|
||||
body, readErr := ioutil.ReadAll(res.Body)
|
||||
if readErr != nil {
|
||||
log.Printf(
|
||||
"[WARN]: Failed to read response body from %v\n"+
|
||||
"\tError: %v\n", u.String(), readErr)
|
||||
log.WARN.Printf(
|
||||
"Failed to read response body from %v\n"+
|
||||
"\tError: %v", u.String(), readErr)
|
||||
return snakeMove
|
||||
}
|
||||
if res.StatusCode != http.StatusOK {
|
||||
log.Printf(
|
||||
"[WARN]: Got non-ok status code from %v\n"+
|
||||
log.WARN.Printf(
|
||||
"Got non-ok status code from %v\n"+
|
||||
"\tStatusCode: %d (expected %d)\n"+
|
||||
"\tBody: %q\n", u.String(), res.StatusCode, http.StatusOK, body)
|
||||
"\tBody: %q", u.String(), res.StatusCode, http.StatusOK, body)
|
||||
return snakeMove
|
||||
}
|
||||
playerResponse := client.MoveResponse{}
|
||||
jsonErr := json.Unmarshal(body, &playerResponse)
|
||||
if jsonErr != nil {
|
||||
log.Printf(
|
||||
"[WARN]: Failed to decode JSON from %v\n"+
|
||||
log.WARN.Printf(
|
||||
"Failed to decode JSON from %v\n"+
|
||||
"\tError: %v\n"+
|
||||
"\tBody: %q\n"+
|
||||
"\tSee https://docs.battlesnake.com/references/api#post-move", u.String(), jsonErr, body)
|
||||
return snakeMove
|
||||
}
|
||||
if playerResponse.Move != "up" && playerResponse.Move != "down" && playerResponse.Move != "left" && playerResponse.Move != "right" {
|
||||
log.Printf(
|
||||
"[WARN]: Failed to parse JSON data from %v\n"+
|
||||
log.WARN.Printf(
|
||||
"Failed to parse JSON data from %v\n"+
|
||||
"\tError: invalid move %q, valid moves are \"up\", \"down\", \"left\" or \"right\"\n"+
|
||||
"\tBody: %q\n"+
|
||||
"\tSee https://docs.battlesnake.com/references/api#post-move", u.String(), playerResponse.Move, body)
|
||||
|
|
@ -457,12 +442,10 @@ func (gameState *GameState) sendEndRequest(boardState *rules.BoardState, snakeSt
|
|||
requestBody := serialiseSnakeRequest(snakeRequest)
|
||||
u, _ := url.ParseRequestURI(snakeState.URL)
|
||||
u.Path = path.Join(u.Path, "end")
|
||||
if gameState.DebugRequests {
|
||||
log.Printf("POST %s: %v", u, string(requestBody))
|
||||
}
|
||||
log.DEBUG.Printf("POST %s: %v", u, string(requestBody))
|
||||
_, err := gameState.httpClient.Post(u.String(), "application/json", bytes.NewBuffer(requestBody))
|
||||
if err != nil {
|
||||
log.Printf("[WARN]: Request to %v failed", u.String())
|
||||
log.WARN.Printf("Request to %v failed", u.String())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -507,9 +490,6 @@ func (gameState *GameState) buildSnakesFromOptions() map[string]SnakeState {
|
|||
} else {
|
||||
numSnakes = numURLs
|
||||
}
|
||||
if numNames != numURLs {
|
||||
log.Println("[WARN]: Number of Names and URLs do not match: defaults will be applied to missing values")
|
||||
}
|
||||
for i := int(0); i < numSnakes; i++ {
|
||||
var snakeName string
|
||||
var snakeURL string
|
||||
|
|
@ -519,21 +499,18 @@ func (gameState *GameState) buildSnakesFromOptions() map[string]SnakeState {
|
|||
if i < numNames {
|
||||
snakeName = gameState.Names[i]
|
||||
} else {
|
||||
log.Printf("[WARN]: Name for URL %v is missing: a default name will be applied\n", gameState.URLs[i])
|
||||
snakeName = id
|
||||
log.DEBUG.Printf("Name for URL %v is missing: a name will be generated automatically", gameState.URLs[i])
|
||||
snakeName = GenerateSnakeName()
|
||||
}
|
||||
|
||||
if i < numURLs {
|
||||
u, err := url.ParseRequestURI(gameState.URLs[i])
|
||||
if err != nil {
|
||||
log.Printf("[WARN]: URL %v is not valid: a default will be applied\n", gameState.URLs[i])
|
||||
snakeURL = "https://example.com"
|
||||
} else {
|
||||
snakeURL = u.String()
|
||||
log.ERROR.Fatalf("URL %v is not valid: %v", gameState.URLs[i], err)
|
||||
}
|
||||
snakeURL = u.String()
|
||||
} else {
|
||||
log.Printf("[WARN]: URL for Name %v is missing: a default URL will be applied\n", gameState.Names[i])
|
||||
snakeURL = "https://example.com"
|
||||
log.ERROR.Fatalf("URL for name %v is missing", gameState.Names[i])
|
||||
}
|
||||
|
||||
snakeState := SnakeState{
|
||||
|
|
@ -542,44 +519,60 @@ func (gameState *GameState) buildSnakesFromOptions() map[string]SnakeState {
|
|||
var snakeErr error
|
||||
res, err := gameState.httpClient.Get(snakeURL)
|
||||
if err != nil {
|
||||
log.Printf("[WARN]: Request to %v failed: %v", snakeURL, err)
|
||||
snakeErr = err
|
||||
} else {
|
||||
snakeState.StatusCode = res.StatusCode
|
||||
|
||||
if res.Body != nil {
|
||||
defer res.Body.Close()
|
||||
body, readErr := ioutil.ReadAll(res.Body)
|
||||
if readErr != nil {
|
||||
log.Fatal(readErr)
|
||||
}
|
||||
|
||||
pingResponse := client.SnakeMetadataResponse{}
|
||||
jsonErr := json.Unmarshal(body, &pingResponse)
|
||||
if jsonErr != nil {
|
||||
snakeErr = jsonErr
|
||||
log.Printf("Error reading response from %v: %v", snakeURL, jsonErr)
|
||||
} else {
|
||||
snakeState.Head = pingResponse.Head
|
||||
snakeState.Tail = pingResponse.Tail
|
||||
snakeState.Color = pingResponse.Color
|
||||
snakeState.Author = pingResponse.Author
|
||||
snakeState.Version = pingResponse.Version
|
||||
}
|
||||
}
|
||||
log.ERROR.Fatalf("Snake metadata request to %v failed: %v", snakeURL, err)
|
||||
}
|
||||
|
||||
snakeState.StatusCode = res.StatusCode
|
||||
|
||||
if res.Body == nil {
|
||||
log.ERROR.Fatalf("Empty response body from snake metadata URL: %v", snakeURL)
|
||||
}
|
||||
|
||||
defer res.Body.Close()
|
||||
body, readErr := ioutil.ReadAll(res.Body)
|
||||
if readErr != nil {
|
||||
log.ERROR.Fatalf("Error reading from snake metadata URL %v: %v", snakeURL, readErr)
|
||||
}
|
||||
|
||||
pingResponse := client.SnakeMetadataResponse{}
|
||||
jsonErr := json.Unmarshal(body, &pingResponse)
|
||||
if jsonErr != nil {
|
||||
log.ERROR.Fatalf("Failed to parse response from %v: %v", snakeURL, jsonErr)
|
||||
}
|
||||
|
||||
snakeState.Head = pingResponse.Head
|
||||
snakeState.Tail = pingResponse.Tail
|
||||
snakeState.Color = pingResponse.Color
|
||||
snakeState.Author = pingResponse.Author
|
||||
snakeState.Version = pingResponse.Version
|
||||
|
||||
if snakeErr != nil {
|
||||
snakeState.Error = snakeErr
|
||||
}
|
||||
|
||||
snakes[snakeState.ID] = snakeState
|
||||
|
||||
log.INFO.Printf("Snake ID: %v URL: %v, Name: \"%v\"", snakeState.ID, snakeURL, snakeState.Name)
|
||||
}
|
||||
return snakes
|
||||
}
|
||||
|
||||
func (gameState *GameState) printState(boardState *rules.BoardState) {
|
||||
var aliveSnakeNames []string
|
||||
for _, snake := range boardState.Snakes {
|
||||
if snake.EliminatedCause == rules.NotEliminated {
|
||||
aliveSnakeNames = append(aliveSnakeNames, gameState.snakeStates[snake.ID].Name)
|
||||
}
|
||||
}
|
||||
log.INFO.Printf(
|
||||
"Turn: %d, Snakes Alive: [%v], Food: %d, Hazards: %d",
|
||||
boardState.Turn, strings.Join(aliveSnakeNames, ", "), len(boardState.Food), len(boardState.Hazards),
|
||||
)
|
||||
}
|
||||
|
||||
func (gameState *GameState) printMap(boardState *rules.BoardState) {
|
||||
var o bytes.Buffer
|
||||
o.WriteString(fmt.Sprintf("Ruleset: %s, Seed: %d, Turn: %v\n", gameState.GameType, gameState.Seed, boardState.Turn))
|
||||
o.WriteString(fmt.Sprintf("Turn: %d\n", boardState.Turn))
|
||||
board := make([][]string, boardState.Width)
|
||||
for i := range board {
|
||||
board[i] = make([]string, boardState.Height)
|
||||
|
|
@ -618,21 +611,28 @@ func (gameState *GameState) printMap(boardState *rules.BoardState) {
|
|||
o.WriteString(fmt.Sprintf("Food ⚕: %v\n", boardState.Food))
|
||||
}
|
||||
for _, s := range boardState.Snakes {
|
||||
red, green, blue := parseSnakeColor(gameState.snakeStates[s.ID].Color)
|
||||
state := gameState.snakeStates[s.ID]
|
||||
|
||||
red, green, blue := parseSnakeColor(state.Color)
|
||||
for _, b := range s.Body {
|
||||
if b.X >= 0 && b.X < boardState.Width && b.Y >= 0 && b.Y < boardState.Height {
|
||||
if gameState.UseColor {
|
||||
board[b.X][b.Y] = fmt.Sprintf(TERM_FG_RGB+"■", red, green, blue)
|
||||
} else {
|
||||
board[b.X][b.Y] = string(gameState.snakeStates[s.ID].Character)
|
||||
board[b.X][b.Y] = string(state.Character)
|
||||
}
|
||||
}
|
||||
}
|
||||
if gameState.UseColor {
|
||||
o.WriteString(fmt.Sprintf("%v "+TERM_FG_RGB+TERM_BG_WHITE+"■■■"+TERM_RESET+": %v\n", gameState.snakeStates[s.ID].Name, red, green, blue, s))
|
||||
o.WriteString(fmt.Sprintf("%v "+TERM_FG_RGB+TERM_BG_WHITE+"■■■"+TERM_RESET+": ", state.Name, red, green, blue))
|
||||
} else {
|
||||
o.WriteString(fmt.Sprintf("%v %c: %v\n", gameState.snakeStates[s.ID].Name, gameState.snakeStates[s.ID].Character, s))
|
||||
o.WriteString(fmt.Sprintf("%v %c: ", state.Name, state.Character))
|
||||
}
|
||||
o.WriteString(fmt.Sprintf("Health: %d", s.Health))
|
||||
if s.EliminatedCause != rules.NotEliminated {
|
||||
o.WriteString(fmt.Sprintf(", Eliminated: %v, Turn: %d", s.EliminatedCause, s.EliminatedOnTurn))
|
||||
}
|
||||
o.WriteString("\n")
|
||||
}
|
||||
for y := boardState.Height - 1; y >= 0; y-- {
|
||||
if gameState.UseColor {
|
||||
|
|
@ -646,7 +646,7 @@ func (gameState *GameState) printMap(boardState *rules.BoardState) {
|
|||
}
|
||||
o.WriteString("\n")
|
||||
}
|
||||
log.Print(o.String())
|
||||
fmt.Println(o.String())
|
||||
}
|
||||
|
||||
func (gameState *GameState) buildFrameEvent(boardState *rules.BoardState) board.GameEvent {
|
||||
|
|
@ -705,7 +705,7 @@ func (gameState *GameState) buildFrameEvent(boardState *rules.BoardState) board.
|
|||
func serialiseSnakeRequest(snakeRequest client.SnakeRequest) []byte {
|
||||
requestJSON, err := json.Marshal(snakeRequest)
|
||||
if err != nil {
|
||||
log.Fatalf("Error marshalling JSON from State: %v", err)
|
||||
log.ERROR.Fatalf("Error marshalling JSON from State: %v", err)
|
||||
}
|
||||
return requestJSON
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue