Skip to content
Merged
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
10 changes: 6 additions & 4 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,18 @@ jobs:
- name: Setting up go
uses: actions/setup-go@v2
with:
go-version: '1.22'
go-version: '1.24'
- name: Caching go modules
uses: actions/cache@v4
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Installing golangci-lint
run: wget -O - -q https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh| sh -s v1.58.1
- name: lint
uses: golangci/golangci-lint-action@v8
with:
version: v2.1
- name: Build and Unit Test
run: ./hack/build.sh

14 changes: 14 additions & 0 deletions .golangci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
version: "2"
linters:
default: none
enable:
- gosec
- misspell
- gocritic
- whitespace
- goprintffuncname
settings:
gosec:
excludes:
- G304

4 changes: 3 additions & 1 deletion TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@

* Verify batting order

* Record defensive positions
~~* Record defensive positions~~

* Count hard hit balls (foul or in-play)

* Implement "final" and "err" specials. Remove final score.

* Upload game logs. Add per-game RE24.

* Record DP, FLEX and pinch runners somehow
4 changes: 4 additions & 0 deletions cmd/alt.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ func altCommand() *cobra.Command {
alt.Name = fmt.Sprintf("%s game %s %s at %s Alt Plays", g.Date, g.Number, g.Visitor.Name, g.Home.Name)
alt.RemoveColumn("Game")
fmt.Println(alt)
pp := gs.GetPerPlayerAltData()
if pp.RowCount() > 0 {
fmt.Println(pp)
}
}
return nil
},
Expand Down
4 changes: 1 addition & 3 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
module github.com/slshen/paperscore

go 1.22.0

toolchain go1.22.3
go 1.24.0

require (
github.com/alecthomas/participle/v2 v2.1.1
Expand Down
7 changes: 0 additions & 7 deletions hack/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,3 @@ set -euo pipefail

set -x
go test -cover ./...

linter=golangci-lint
if [ -x ./bin/golangci-lint ]; then
linter=./bin/golangci-lint
fi
$linter run -E stylecheck -E gosec -E goimports -E misspell -E gocritic \
-E whitespace -E goprintffuncname
48 changes: 40 additions & 8 deletions pkg/boxscore/box.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ func NewBoxScore(g *game.Game, re stats.RunExpectancy) (*BoxScore, error) {
boxscore := &BoxScore{
Game: g,
Stats: gs,
HomeLineup: &Lineup{gs.GetStats(g.Home)},
VisitorLineup: &Lineup{gs.GetStats(g.Visitor)},
HomeLineup: newLineup(gs.GetStats(g.Home)),
VisitorLineup: newLineup(gs.GetStats(g.Visitor)),
}
if err := boxscore.run(); err != nil {
return nil, err
Expand Down Expand Up @@ -94,11 +94,9 @@ func (box *BoxScore) InningScoreTable() *dataframe.Data {
tab := &dataframe.Data{
Columns: []*dataframe.Column{
{
Name: fmt.Sprintf("%s #%s", box.Game.Date, box.Game.Number),
Format: "%-20s",
Values: []string{
firstWord(box.Game.Visitor.Name, 20),
firstWord(box.Game.Home.Name, 20),
box.Game.Visitor.ShortName,
box.Game.Home.ShortName,
},
},
},
Expand Down Expand Up @@ -139,13 +137,47 @@ func (box *BoxScore) InningScoreTable() *dataframe.Data {
func (box *BoxScore) AltPlays() *dataframe.Data {
dat := box.Stats.GetAltData()
dat = dat.Select(
dataframe.Col("In"),
dataframe.DeriveStrings("Inn", func(idx *dataframe.Index, i int) string {
inn := idx.GetInt(i, "I")
half := idx.GetString(i, "H")
o := idx.GetInt(i, "O")
return fmt.Sprintf("%c%d.%d", half[0], inn, o)
}).WithFormat("%4s"),
dataframe.Rename("Reality", "Play").WithFormat("%-30s"),
dataframe.Col("RCost"), dataframe.Col("Comment"))
dataframe.Col("RCost"),
dataframe.Col("Comment"),
dataframe.DeriveStrings("Players", func(idx *dataframe.Index, i int) string {
credit := idx.GetString(i, "Credit")
if credit == "" {
return ""
}
s := &strings.Builder{}
for p := range strings.FieldsSeq(credit) {
player := box.Game.GetPlayer(game.PlayerID(p))
if s.Len() > 0 {
s.WriteString(", ")
}
s.WriteString(player.GetShortName())
}
return s.String()
}),
)
dat.Name = "ALT"
return dat
}

func (box *BoxScore) AltPlaysPerPlayer() *dataframe.Data {
dat := box.Stats.GetPerPlayerAltData()
dat.Name = "ALT CREDIT"
idx := dat.GetIndex()
idx.GetColumn("Player").Format = "%-20s"
dat.RApply(func(row int) {
player := box.Game.GetPlayer(game.PlayerID(idx.GetString(row, "Player")))
idx.GetColumn("Player").GetStrings()[row] = player.GetShortName()
})
return dat
}

func (box *BoxScore) ScoringPlays() (string, error) {
gen := playbyplay.Generator{
Game: box.Game,
Expand Down
3 changes: 2 additions & 1 deletion pkg/boxscore/box.tmpl
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
{{.Game.Visitor.Name}} at {{.Game.Home.Name}} {{.Game.Date}} game {{.Game.Number}}

{{.InningScoreTable}}
{{paste .VisitorLineup.BattingTable.String .HomeLineup.BattingTable.String 1 44}}
{{paste .VisitorLineup.PlayerTable.String .HomeLineup.PlayerTable.String 1 44}}
{{paste (execute "batting.tmpl" .VisitorLineup) (execute "batting.tmpl" .HomeLineup) 1 44}}
{{- paste .VisitorLineup.PitchingTable.String .HomeLineup.PitchingTable.String 1 -44}}
{{paste (execute "pitching.tmpl" .VisitorLineup) (execute "pitching.tmpl" .HomeLineup) 1 44}}
{{.AltPlays}}
{{.AltPlaysPerPlayer}}
{{if (not (or .IncludePlays .IncludeScoringPlays))}}
{{- range .Comments}}{{.Half}} {{ordinal .Inning}}, {{.Outs}} Outs - {{.Text}}
{{end}}{{end}}
Expand Down
64 changes: 54 additions & 10 deletions pkg/boxscore/lineup.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"strings"

"github.com/slshen/paperscore/pkg/dataframe"
"github.com/slshen/paperscore/pkg/game"
"github.com/slshen/paperscore/pkg/stats"
"github.com/slshen/paperscore/pkg/text"
)
Expand All @@ -13,29 +14,57 @@ type Lineup struct {
*stats.TeamStats
}

func (lineup *Lineup) BattingTable() *dataframe.Data {
dat := lineup.GetBattingData().Select(
func newLineup(ts *stats.TeamStats) *Lineup {
return &Lineup{
TeamStats: ts,
}
}

func (lineup *Lineup) haveDefensivePositions() bool {
for _, positions := range lineup.PositionsByPlayer {
for _, p := range positions {
if p != 1 {
return true
}
}
}
return false
}

func (lineup *Lineup) PlayerTable() *dataframe.Data {
batting := lineup.GetBattingData()
selection := []dataframe.Selection{}
// if we have any defensive lineup data available other than pitchers, include "F" column
if lineup.haveDefensivePositions() {
selection = append(selection,
dataframe.DeriveStrings("F", func(idx *dataframe.Index, i int) string {
positions := lineup.PositionsByPlayer[game.PlayerID(idx.GetString(i, "PlayerID"))]
s := &strings.Builder{}
for _, pos := range positions {
if s.Len() > 0 {
s.WriteRune(' ')
}
s.WriteString(game.FielderNames[pos-1])
}
return s.String()
}).WithFormat("%-5s"))
}
selection = append(selection,
dataframe.Rename("Name", "#").WithFormat("%-14s"),
dataframe.Col("AB"),
dataframe.Rename("Hits", "H"),
dataframe.Col("LOPH"),
dataframe.Rename("StrikeOuts", "K"),
dataframe.Rename("Walks", "BB"),
)
dat := batting.Select(selection...)
idx := dat.GetIndex()
names := idx.GetColumn("#")
dat.RApply(func(row int) {
// Shorten "Babe Ruth" to "B Ruth"
name := names.GetString(row)
if strings.ContainsRune(name, ' ') {
parts := strings.Split(name, " ")
for i, part := range parts[0 : len(parts)-1] {
if len(part) > 2 {
// unless it's a very short name
parts[i] = part[0:1]
}
}
names.GetStrings()[row] = strings.Join(parts, " ")
names.GetStrings()[row] = text.NameShorten(name)
}
})
idx.GetColumn("AB").Summary = dataframe.Sum
Expand Down Expand Up @@ -68,6 +97,21 @@ func (lineup *Lineup) ErrorsList() string {
fmt.Fprintf(s, " E%d:%d", f.Position, f.Errors)
}
}
if len(lineup.ErrorsByPlayer) > 0 {
fmt.Fprintln(s)
s.WriteString("Errors - ")
comma := false
for playerID, n := range lineup.ErrorsByPlayer {
if comma {
s.WriteString(", ")
}
comma = true
s.WriteString(text.NameShorten(lineup.Team.Players[playerID].NameOrNumber()))
if n > 1 {
fmt.Fprintf(s, "(%d)", n)
}
}
}
return s.String()
}

Expand Down
23 changes: 0 additions & 23 deletions pkg/boxscore/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,6 @@ import (
"github.com/slshen/paperscore/pkg/text"
)

func firstWord(s string, w int) string {
out := &strings.Builder{}
for s != "" {
space := strings.IndexRune(s, ' ')
if space > 0 {
if out.Len()+space < w {
if out.Len() > 0 {
out.WriteRune(' ')
}
out.WriteString(s[0:space])
s = s[space+1:]
continue
}
}
if out.Len()+len(s) < w {
out.WriteString(s)
}
break
}

return out.String()
}

func paste(c1, c2 string, sepWidth, leftLen int) string {
switch {
case leftLen > 0:
Expand Down
5 changes: 0 additions & 5 deletions pkg/boxscore/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,3 @@ world`, "xxxxxx", 4, 0)
world
`, s)
}

func TestFirstWord(t *testing.T) {
assert := assert.New(t)
assert.Equal("Athletics Mercado", firstWord("Athletics Mercado Walling", 20))
}
8 changes: 4 additions & 4 deletions pkg/dataframe/data.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ func (dat *Data) RowCount() int {
func (dat *Data) RSort(less func(r1 int, r2 int) bool) *Data {
rc := dat.RowCount()
rowNumbers := make([]int, rc)
for i := 0; i < rc; i++ {
for i := range rc {
rowNumbers[i] = i
}
sort.Slice(rowNumbers, func(i, j int) bool {
Expand All @@ -224,19 +224,19 @@ func (dat *Data) RSort(less func(r1 int, r2 int) bool) *Data {
switch scol.GetType() {
case Int:
values := make([]int, scol.Len())
for row := 0; row < scol.Len(); row++ {
for row := range scol.Len() {
values[row] = scol.GetInt(rowNumbers[row])
}
rcol.Values = values
case Float:
values := make([]float64, scol.Len())
for row := 0; row < scol.Len(); row++ {
for row := range scol.Len() {
values[row] = scol.GetFloat(rowNumbers[row])
}
rcol.Values = values
case String:
values := make([]string, scol.Len())
for row := 0; row < scol.Len(); row++ {
for row := range scol.Len() {
values[row] = scol.GetString(rowNumbers[row])
}
rcol.Values = values
Expand Down
4 changes: 2 additions & 2 deletions pkg/dataframe/pkg/datapackage.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ func (dp *DataPackage) Write(dir string) error {
}

func (dp *DataPackage) writeJSON(path string, val interface{}) error {
if err := os.MkdirAll(filepath.Dir(path), 0777); err != nil {
if err := os.MkdirAll(filepath.Dir(path), 0750); err != nil {
return err
}
f, err := os.Create(path)
Expand All @@ -129,7 +129,7 @@ func (dp *DataPackage) writeJSON(path string, val interface{}) error {

func (dp *DataPackage) writeContent(dir string, r Resource) error {
path := filepath.Join(dir, r.GetPath())
if err := os.MkdirAll(filepath.Dir(path), 0777); err != nil {
if err := os.MkdirAll(filepath.Dir(path), 0750); err != nil {
return err
}
f, err := os.Create(filepath.Join(dir, r.GetPath()))
Expand Down
1 change: 1 addition & 0 deletions pkg/dataframe/select.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ func (dat *Data) Add(sels ...Selection) {
}
}

// Adds an existing column to the new frame
func Col(name string) Selection {
return func(i *Index) *Column {
return i.GetColumn(name)
Expand Down
5 changes: 5 additions & 0 deletions pkg/game/fieldingerror.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ type FieldingError struct {
Modifiers
}

var FielderNames = []string{
"P", "C", "1B", "2B", "3B", "SS",
"LF", "CF", "RF",
}

var NoError = FieldingError{}

func parseFieldingError(play gamefile.Play, s string) (FieldingError, error) {
Expand Down
Loading