Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "rules.battlesnake.local",
"image": "mcr.microsoft.com/devcontainers/go:1.21",
"postCreateCommand": "go mod download",
"image": "mcr.microsoft.com/devcontainers/go:1.23",
"postCreateCommand": "sh -c \"go mod download && git config --global --add safe.directory /workspaces/rules\"",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@xtagon What's going on here?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some Go package expects expects the VCS to be available during build and was throwing an error because I was running in the dev container. This allows git to work in the dev container in the workspace directory.

I think there's a way to handle that with a Go flag to not use VCSbuild if you prefer

"forwardPorts": [],
"customizations": {
"vscode": {
Expand All @@ -14,4 +14,4 @@
}
}
}
}
}
4 changes: 4 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
* text=auto eol=lf
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TIL this file is a thing 💡

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, have to do this a lot when switching between Windows and Linux


# Keep Windows-only scripts with CRLF if they ever appear
*.bat text eol=crlf
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
branches: [main]

env:
GO_VERSION: '1.20'
GO_VERSION: '1.23'

jobs:

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ on:
workflow_call: # Allow other workflows to call this one

env:
GO_VERSION: '1.20'
GO_VERSION: '1.23'

jobs:

Expand Down
2 changes: 2 additions & 0 deletions .golangci.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
version: 2

issues:
exclude-rules:
- linters:
Expand Down
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
GOPATH := $(shell go env GOPATH)

GOLANGCI_LINT_PATH := ${GOPATH}/bin/golangci-lint
GOLANGCI_LINT_VERSION := 1.55.2
GOLANGCI_LINT_VERSION := 2.2.1


${GOLANGCI_LINT_PATH}:
Expand All @@ -12,7 +12,7 @@ install-cli:
.PHONY: install-cli

test-format:
test -z $$(gofmt -l .) || (gofmt -l . && exit 1)
@files=$$(gofmt -l .); if [ -n "$$files" ]; then echo "$$files"; exit 1; fi
.PHONY: test-format

test-lint: ${GOLANGCI_LINT_PATH}
Expand Down
4 changes: 2 additions & 2 deletions board_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,8 +299,8 @@ func TestPlaceSnakesDefault(t *testing.T) {
}

// All snakes are expected to be placed on an even square - this is true even of fixed positions for known board sizes
var snakePlacedOnEvenSquare bool = ((test.BoardState.Snakes[i].Body[0].X + test.BoardState.Snakes[i].Body[0].Y) % 2) == 0
require.Equal(t, true, snakePlacedOnEvenSquare)
snakePlacedOnEvenSquare := ((test.BoardState.Snakes[i].Body[0].X + test.BoardState.Snakes[i].Body[0].Y) % 2) == 0
require.True(t, snakePlacedOnEvenSquare)
}
}
})
Expand Down
62 changes: 36 additions & 26 deletions cli/commands/play.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"math/rand"
"net/http"
"net/url"
"os"
Expand Down Expand Up @@ -139,7 +137,7 @@ func (gameState *GameState) Initialize() error {
// Load game map
gameMap, err := maps.GetMap(gameState.MapName)
if err != nil {
return fmt.Errorf("Failed to load game map %#v: %v", gameState.MapName, err)
return fmt.Errorf("failed to load game map %#v: %v", gameState.MapName, err)
}
gameState.gameMap = gameMap

Expand All @@ -165,7 +163,7 @@ func (gameState *GameState) Initialize() error {
if gameState.OutputPath != "" {
f, err := os.OpenFile(gameState.OutputPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
if err != nil {
return fmt.Errorf("Failed to open output file: %w", err)
return fmt.Errorf("failed to open output file: %w", err)
}
gameState.outputFile = f
}
Expand All @@ -181,14 +179,14 @@ func (gameState *GameState) Run() error {
// Setup local state for snakes
gameState.snakeStates, err = gameState.buildSnakesFromOptions()
if err != nil {
return fmt.Errorf("Error getting snake metadata: %w", err)
return fmt.Errorf("error getting snake metadata: %w", err)
}

rand.Seed(gameState.Seed)
rules.SetGlobalSeed(gameState.Seed)

gameOver, boardState, err := gameState.initializeBoardFromArgs()
if err != nil {
return fmt.Errorf("Error initializing board: %w", err)
return fmt.Errorf("error initializing board: %w", err)
}

gameExporter := GameExporter{
Expand All @@ -199,7 +197,11 @@ func (gameState *GameState) Run() error {
}
exportGame := gameState.outputFile != nil
if exportGame {
defer gameState.outputFile.Close()
defer func() {
if err := gameState.outputFile.Close(); err != nil {
log.WARN.Printf("error closing output file: %v", err)
}
}()
}

boardGame := board.Game{
Expand All @@ -219,7 +221,7 @@ func (gameState *GameState) Run() error {
if gameState.ViewInBrowser {
serverURL, err := boardServer.Listen()
if err != nil {
return fmt.Errorf("Error starting HTTP server: %w", err)
return fmt.Errorf("error starting HTTP server: %w", err)
}
defer boardServer.Shutdown()
log.INFO.Printf("Board server listening on %s", serverURL)
Expand Down Expand Up @@ -267,7 +269,7 @@ func (gameState *GameState) Run() error {

gameOver, boardState, err = gameState.createNextBoardState(boardState)
if err != nil {
return fmt.Errorf("Error processing game: %w", err)
return fmt.Errorf("error processing game: %w", err)
}

if gameOver {
Expand Down Expand Up @@ -337,7 +339,7 @@ func (gameState *GameState) Run() error {
if exportGame {
lines, err := gameExporter.FlushToFile(gameState.outputFile)
if err != nil {
return fmt.Errorf("Unable to export game: %w", err)
return fmt.Errorf("unable to export game: %w", err)
}
log.INFO.Printf("Wrote %d lines to output file: %s", lines, gameState.OutputPath)
}
Expand All @@ -352,11 +354,11 @@ func (gameState *GameState) initializeBoardFromArgs() (bool, *rules.BoardState,
}
boardState, err := maps.SetupBoard(gameState.gameMap.ID(), gameState.ruleset.Settings(), gameState.Width, gameState.Height, snakeIds)
if err != nil {
return false, nil, fmt.Errorf("Error initializing BoardState with map: %w", err)
return false, nil, fmt.Errorf("error initializing BoardState with map: %w", err)
}
gameOver, boardState, err := gameState.ruleset.Execute(boardState, nil)
if err != nil {
return false, nil, fmt.Errorf("Error initializing BoardState with ruleset: %w", err)
return false, nil, fmt.Errorf("error initializing BoardState with ruleset: %w", err)
}

for _, snakeState := range gameState.snakeStates {
Expand All @@ -377,7 +379,7 @@ func (gameState *GameState) createNextBoardState(boardState *rules.BoardState) (
// apply PreUpdateBoard before making requests to snakes
boardState, err := maps.PreUpdateBoard(gameState.gameMap, boardState, gameState.ruleset.Settings())
if err != nil {
return false, boardState, fmt.Errorf("Error pre-updating board with game map: %w", err)
return false, boardState, fmt.Errorf("error pre-updating board with game map: %w", err)
}

// get moves from snakes
Expand Down Expand Up @@ -420,13 +422,13 @@ func (gameState *GameState) createNextBoardState(boardState *rules.BoardState) (

gameOver, boardState, err := gameState.ruleset.Execute(boardState, moves)
if err != nil {
return false, boardState, fmt.Errorf("Error updating board state from ruleset: %w", err)
return false, boardState, fmt.Errorf("error updating board state from ruleset: %w", err)
}

// apply PostUpdateBoard after ruleset operates on snake moves
boardState, err = maps.PostUpdateBoard(gameState.gameMap, boardState, gameState.ruleset.Settings())
if err != nil {
return false, boardState, fmt.Errorf("Error post-updating board with game map: %w", err)
return false, boardState, fmt.Errorf("error post-updating board with game map: %w", err)
}

boardState.Turn += 1
Expand Down Expand Up @@ -470,8 +472,12 @@ func (gameState *GameState) getSnakeUpdate(boardState *rules.BoardState, snakeSt
"\tError: body is empty", u.String())
return snakeState
}
defer res.Body.Close()
body, readErr := ioutil.ReadAll(res.Body)
defer func() {
if err := res.Body.Close(); err != nil {
log.WARN.Printf("error closing response body from %v: %v", u.String(), err)
}
}()
body, readErr := io.ReadAll(res.Body)
if readErr != nil {
log.WARN.Printf(
"Failed to read response body from %v\n"+
Expand Down Expand Up @@ -586,11 +592,11 @@ func (gameState *GameState) buildSnakesFromOptions() (map[string]SnakeState, err
if i < numURLs {
u, err := url.ParseRequestURI(gameState.URLs[i])
if err != nil {
return nil, fmt.Errorf("URL %v is not valid: %w", gameState.URLs[i], err)
return nil, fmt.Errorf("invalid URL: %v: %w", gameState.URLs[i], err)
}
snakeURL = u.String()
} else {
return nil, fmt.Errorf("URL for name %v is missing", gameState.Names[i])
return nil, fmt.Errorf("missing URL for name: %v", gameState.Names[i])
}

snakeState := SnakeState{
Expand All @@ -599,25 +605,29 @@ func (gameState *GameState) buildSnakesFromOptions() (map[string]SnakeState, err
var snakeErr error
res, _, err := gameState.httpClient.Get(snakeURL)
if err != nil {
return nil, fmt.Errorf("Snake metadata request to %v failed: %w", snakeURL, err)
return nil, fmt.Errorf("snake metadata request to %v failed: %w", snakeURL, err)
}

snakeState.StatusCode = res.StatusCode

if res.Body == nil {
return nil, fmt.Errorf("Empty response body from snake metadata URL: %v", snakeURL)
return nil, fmt.Errorf("empty response body from snake metadata URL: %v", snakeURL)
}

defer res.Body.Close()
body, readErr := ioutil.ReadAll(res.Body)
defer func() {
if err := res.Body.Close(); err != nil {
log.WARN.Printf("error closing snake metadata response from %v: %v", snakeURL, err)
}
}()
body, readErr := io.ReadAll(res.Body)
if readErr != nil {
return nil, fmt.Errorf("Error reading from snake metadata URL %v: %w", snakeURL, readErr)
return nil, fmt.Errorf("error reading from snake metadata URL %v: %w", snakeURL, readErr)
}

pingResponse := client.SnakeMetadataResponse{}
jsonErr := json.Unmarshal(body, &pingResponse)
if jsonErr != nil {
return nil, fmt.Errorf("Failed to parse response from %v: %w", snakeURL, jsonErr)
return nil, fmt.Errorf("failed to parse response from %v: %w", snakeURL, jsonErr)
}

snakeState.Head = pingResponse.Head
Expand Down
3 changes: 1 addition & 2 deletions cli/commands/play_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"errors"
"fmt"
"io"
"io/ioutil"
"net/http"
"strings"
"testing"
Expand Down Expand Up @@ -629,7 +628,7 @@ func (client stubHTTPClient) request(url string) (*http.Response, time.Duration,
if client.err != nil {
return nil, client.latency, client.err
}
body := ioutil.NopCloser(bytes.NewBufferString(client.body(url)))
body := io.NopCloser(bytes.NewBufferString(client.body(url)))

response := &http.Response{
Header: make(http.Header),
Expand Down
8 changes: 4 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/BattlesnakeOfficial/rules

go 1.21
go 1.23.0

require (
github.com/google/uuid v1.5.0
Expand Down Expand Up @@ -32,9 +32,9 @@ require (
github.com/subosito/gotenv v1.6.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a // indirect
golang.org/x/net v0.23.0 // indirect
golang.org/x/sys v0.18.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/net v0.38.0 // indirect
golang.org/x/sys v0.31.0 // indirect
golang.org/x/text v0.23.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
12 changes: 6 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,13 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA=
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08=
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
Expand Down
3 changes: 2 additions & 1 deletion maps/hazard_pits.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ func (m HazardPitsMap) SetupBoard(initialBoardState *rules.BoardState, settings

tempBoardState := rules.NewBoardState(initialBoardState.Width, initialBoardState.Height)
tempBoardState.Snakes = make([]rules.Snake, len(snakeIDs))
tempEditor := NewBoardStateEditor(tempBoardState)

for i := 0; i < len(snakeIDs); i++ {
tempBoardState.Snakes[i] = rules.Snake{
Expand All @@ -79,7 +80,7 @@ func (m HazardPitsMap) SetupBoard(initialBoardState *rules.BoardState, settings
}
}

err := rules.PlaceFoodFixed(rand, tempBoardState)
err := PlaceFoodFixed(rand, tempBoardState, tempEditor)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@xtagon any idea why it's suggesting this change?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made that change to make the lint pass, it said the old way was deprecated

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this break the map behavior? I didn't test this map specifically

if err != nil {
return err
}
Expand Down
17 changes: 7 additions & 10 deletions maps/hazard_pits_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,21 +49,18 @@ func TestHazardPitsMap(t *testing.T) {
state.Turn = i
err = m.PostUpdateBoard(state, settings, editor)
require.NoError(t, err)
if i == 1 {
switch i {
case 1:
require.Len(t, state.Hazards, 21)
} else if i == 2 {
case 2:
require.Len(t, state.Hazards, 42)
} else if i == 3 {
case 3:
require.Len(t, state.Hazards, 63)
} else if i == 4 {
case 4, 5, 6:
require.Len(t, state.Hazards, 84)
} else if i == 5 {
require.Len(t, state.Hazards, 84)
} else if i == 6 {
require.Len(t, state.Hazards, 84)
} else if i == 7 {
case 7:
require.Len(t, state.Hazards, 0)
} else if i == 8 {
case 8:
require.Len(t, state.Hazards, 21)
}
}
Expand Down
Loading