Skip to content

Commit

Permalink
linting
Browse files Browse the repository at this point in the history
  • Loading branch information
Jan Steinke committed Feb 17, 2024
1 parent 66cc179 commit a2cd1df
Show file tree
Hide file tree
Showing 8 changed files with 111 additions and 81 deletions.
30 changes: 18 additions & 12 deletions game/game.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// Package game is the main game logic. It implements the simon says parts
// and the round/stage tracking.
package game

import (
Expand Down Expand Up @@ -28,11 +30,13 @@ const (
gameStateWon
)

func NewLogic() *logic {
return &logic{}
// New is the factory for the Game component.
func New() *Game {
return &Game{}
}

type logic struct {
// Game is the main Game component of the game. Create a new instance by
type Game struct {
difficulty storage.Difficulty
sequence []int64
clicks int
Expand All @@ -41,7 +45,7 @@ type logic struct {
storageMutex sync.Mutex
}

func (g *logic) simonSays(ctx app.Context, sequence []int64) {
func (g *Game) simonSays(ctx app.Context, sequence []int64) {
fmt.Println(sequence)
ctx.Async(func() {
<-time.After(200 * time.Millisecond)
Expand All @@ -54,22 +58,24 @@ func (g *logic) simonSays(ctx app.Context, sequence []int64) {
})
}

func (g *logic) HandleNewGame(ctx app.Context, a app.Action) {
// HandleNewGame is the handler to invoke to start a new game.
func (g *Game) HandleNewGame(ctx app.Context, a app.Action) {
d, ok := a.Value.(storage.Difficulty)
if !ok {
fmt.Println("wrong type")
return
}
g.difficulty = d
g.clicks = 0
g.sequence = []int64{NextNumber()}
g.sequence = []int64{nextNumber()}
g.stage = 1
g.state = gameStateSimonSays
ctx.NewActionWithValue(ui.EventStateChange, "Simon says...")
g.simonSays(ctx, g.sequence)
}

func (g *logic) HandleClick(ctx app.Context, a app.Action) {
// HandleClick is the handler to invoke on user input when it's the user's turn.
func (g *Game) HandleClick(ctx app.Context, a app.Action) {
if g.state != gameStatePlayerSays {
return
}
Expand All @@ -93,7 +99,7 @@ func (g *logic) HandleClick(ctx app.Context, a app.Action) {
}
}

func (g *logic) lostGame(ctx app.Context) {
func (g *Game) lostGame(ctx app.Context) {
g.storageMutex.Lock()
defer g.storageMutex.Unlock()
g.state = gameStateLost
Expand All @@ -114,7 +120,7 @@ func (g *logic) lostGame(ctx app.Context) {
}
}

func (g *logic) wonGame(ctx app.Context) {
func (g *Game) wonGame(ctx app.Context) {
g.storageMutex.Lock()
defer g.storageMutex.Unlock()
g.state = gameStateWon
Expand All @@ -133,18 +139,18 @@ func (g *logic) wonGame(ctx app.Context) {
}
}

func (g *logic) nextRound(ctx app.Context) {
func (g *Game) nextRound(ctx app.Context) {
g.clicks = 0
g.stage++
g.state = gameStateSimonSays
g.sequence = append(g.sequence, NextNumber())
g.sequence = append(g.sequence, nextNumber())
ctx.NewActionWithValue(ui.EventStateChange, "Simon says...")
ctx.After(1*time.Second, func(ctx app.Context) {
g.simonSays(ctx, g.sequence)
})
}

func NextNumber() int64 {
func nextNumber() int64 {
n, err := rand.Int(rand.Reader, big.NewInt(4))
if err != nil {
panic(err)
Expand Down
4 changes: 3 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// Package main is a game of simon-says. It provides a server and client application
// as well as a static-resource generator for the game.
package main

import (
Expand All @@ -15,7 +17,7 @@ func main() {
serve := flag.Bool("serve", false, "set to serve instead of generating resources")
flag.Parse()

l := game.NewLogic()
l := game.New()
// TODO: improve statistics
// TODO: for endless mode add histogram of how far you got.
// TODO: add tests
Expand Down
31 changes: 23 additions & 8 deletions storage/scores.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// Package storage is a library to make interfacing with the web storage simpler
// and provide a unified way to deal with it.
package storage

import (
Expand All @@ -9,20 +11,25 @@ const EventScoreUpdate = "scoreUpdate"

const localStorageScores = "scores"

// Scores is the representation of scores for ranking and statistics
type Scores struct {
Basic map[Difficulty]Score
Endless map[int]int
}

// Difficulty represents the games difficulty for the statistics.
type Difficulty string

// List of possible difficulties.
const (
Easy Difficulty = "easy"
Medium Difficulty = "medium"
Hard Difficulty = "hard"
Endless Difficulty = "endless"
)

// Score is the individual scores for the basic difficulties. Endless doesn't have Wins that
// is why it isn't tracked with Win/Loss Scores.
type Score struct {
Win int
Loss int
Expand All @@ -36,19 +43,22 @@ func newScores() Scores {
}, Endless: map[int]int{}}
}

// IncrementEasyLoss increments the losses for an easy game.
func IncrementEasyLoss(ctx app.Context) {
IncrementLoss(ctx, Easy)
incrementLoss(ctx, Easy)
}

// IncrementMediumLoss increments the losses for a medium game.
func IncrementMediumLoss(ctx app.Context) {
IncrementLoss(ctx, Medium)
incrementLoss(ctx, Medium)
}

// IncrementHardLoss increments the losses for a hard game.
func IncrementHardLoss(ctx app.Context) {
IncrementLoss(ctx, Hard)
incrementLoss(ctx, Hard)
}

func IncrementLoss(ctx app.Context, d Difficulty) {
func incrementLoss(ctx app.Context, d Difficulty) {
s := newScores()
ctx.LocalStorage().Get(localStorageScores, &s)
if d != Endless {
Expand All @@ -60,6 +70,7 @@ func IncrementLoss(ctx app.Context, d Difficulty) {
ctx.NewActionWithValue(EventScoreUpdate, s)
}

// UpdateEndless tracks the score for an endless game.
func UpdateEndless(ctx app.Context, stage int) {
s := newScores()
ctx.LocalStorage().Get(localStorageScores, &s)
Expand All @@ -68,19 +79,22 @@ func UpdateEndless(ctx app.Context, stage int) {
ctx.NewActionWithValue(EventScoreUpdate, s)
}

// IncrementEasyWin increments the win for an easy game.
func IncrementEasyWin(ctx app.Context) {
IncrementWin(ctx, Easy)
incrementWin(ctx, Easy)
}

// IncrementMediumWin incremens the win for a medium game.
func IncrementMediumWin(ctx app.Context) {
IncrementWin(ctx, Medium)
incrementWin(ctx, Medium)
}

// IncrementHardWin increments the win for a hard game.
func IncrementHardWin(ctx app.Context) {
IncrementWin(ctx, Hard)
incrementWin(ctx, Hard)
}

func IncrementWin(ctx app.Context, d Difficulty) {
func incrementWin(ctx app.Context, d Difficulty) {
s := newScores()
ctx.LocalStorage().Get(localStorageScores, &s)
f := s.Basic[d]
Expand All @@ -90,6 +104,7 @@ func IncrementWin(ctx app.Context, d Difficulty) {
ctx.NewActionWithValue(EventScoreUpdate, s)
}

// LoadScores returns the currently stored Scores.
func LoadScores(ctx app.Context) Scores {
s := newScores()
ctx.LocalStorage().Get(localStorageScores, &s)
Expand Down
11 changes: 7 additions & 4 deletions ui/button.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,24 @@ type button struct {
Active bool
}

func NewButton(id int64) *button {
func newButton(id int64) *button {
return &button{
id: id,
}
}

// OnMount implements the Mounter interface to run this on mounting the component.
func (b *button) OnMount(ctx app.Context) {
ctx.Handle(fmt.Sprintf(EventPlayButton, b.id), b.handleActivate)
ctx.Handle(fmt.Sprintf(EventPlayButton, b.id), b.handlePlayButton)
}

// Render implements the interface for go-app to render the component.
func (b *button) Render() app.UI {
id := fmt.Sprintf("button%d", b.id)
e := app.Button().
Class("simon-button", "game-button").
Body(app.Span().Text("")).
ID("button%d", b.id).
ID(id).
OnClick(b.handleClick)
if b.Active {
e.Class("active")
Expand All @@ -47,7 +50,7 @@ func (b *button) handleClick(ctx app.Context, _ app.Event) {
})
}

func (b *button) handleActivate(ctx app.Context, a app.Action) {
func (b *button) handlePlayButton(ctx app.Context, _ app.Action) {
ctx.Dispatch(func(_ app.Context) {
b.Active = true
})
Expand Down
6 changes: 2 additions & 4 deletions ui/menu.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,13 @@ type menu struct {
selectedDifficulty storage.Difficulty
}

func NewMenu() *menu {
return &menu{}
}

// OnMount implements the Mounter interface to run this on mounting the component.
func (g *menu) OnMount(ctx app.Context) {
d := storage.LoadDifficulty(ctx)
g.selectedDifficulty = d
}

// Render implements the interface for go-app to render the component.
func (g *menu) Render() app.UI {
modes := []app.UI{}
for _, mode := range []storage.Difficulty{storage.Easy, storage.Medium, storage.Hard, storage.Endless} {
Expand Down
12 changes: 6 additions & 6 deletions ui/menu_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func TestComponentLifcycle(t *testing.T) {
Path: app.TestPath(0, 0),
Expected: app.Button().
Class("simon-button", "new-game").
OnClick(func(ctx app.Context, e app.Event) {}),
OnClick(func(_ app.Context, _ app.Event) {}),
},
{
Path: app.TestPath(0, 0, 0),
Expand All @@ -45,7 +45,7 @@ func TestComponentLifcycle(t *testing.T) {
ID("difficultyeasy").
Value("easy").
Checked(true).
OnClick(func(ctx app.Context, e app.Event) {}),
OnClick(func(_ app.Context, _ app.Event) {}),
},
{
Path: app.TestPath(0, 1, 1),
Expand All @@ -59,7 +59,7 @@ func TestComponentLifcycle(t *testing.T) {
Name("difficulty-setting").
ID("difficultymedium").
Value("medium").
OnClick(func(ctx app.Context, e app.Event) {}),
OnClick(func(_ app.Context, _ app.Event) {}),
},
{
Path: app.TestPath(0, 1, 3),
Expand All @@ -73,7 +73,7 @@ func TestComponentLifcycle(t *testing.T) {
Name("difficulty-setting").
ID("difficultyhard").
Value("hard").
OnClick(func(ctx app.Context, e app.Event) {}),
OnClick(func(_ app.Context, _ app.Event) {}),
},
{
Path: app.TestPath(0, 1, 5),
Expand All @@ -87,7 +87,7 @@ func TestComponentLifcycle(t *testing.T) {
Name("difficulty-setting").
ID("difficultyendless").
Value("endless").
OnClick(func(ctx app.Context, e app.Event) {}),
OnClick(func(_ app.Context, _ app.Event) {}),
},
{
Path: app.TestPath(0, 1, 7),
Expand All @@ -100,7 +100,7 @@ func TestComponentLifcycle(t *testing.T) {

for _, tt := range tests {
t.Run(tt.Name, func(t *testing.T) {
compo := NewMenu()
compo := &menu{}

disp := app.NewClientTester(compo)
defer disp.Close()
Expand Down
38 changes: 20 additions & 18 deletions ui/scores.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,53 +17,55 @@ type scoreBoard struct {
endlessHighscore int
}

func (g *scoreBoard) OnMount(ctx app.Context) {
ctx.Handle(storage.EventScoreUpdate, g.HandleScoreUpdate)
// OnMount implements the Mounter interface to run this on mounting the component.
func (b *scoreBoard) OnMount(ctx app.Context) {
ctx.Handle(storage.EventScoreUpdate, b.HandleScoreUpdate)
s := storage.LoadScores(ctx)
g.storeScores(s)
b.storeScores(s)
}

func (g *scoreBoard) storeScores(s storage.Scores) {
g.easyWinRatio = float64(s.Basic[storage.Easy].Win) / float64(s.Basic[storage.Easy].Win+s.Basic[storage.Easy].Loss)
g.mediumWinRatio = float64(s.Basic[storage.Medium].Win) / float64(s.Basic[storage.Medium].Win+s.Basic[storage.Medium].Loss)
g.hardWinRatio = float64(s.Basic[storage.Hard].Win) / float64(s.Basic[storage.Hard].Win+s.Basic[storage.Hard].Loss)
func (b *scoreBoard) storeScores(s storage.Scores) {
b.easyWinRatio = float64(s.Basic[storage.Easy].Win) / float64(s.Basic[storage.Easy].Win+s.Basic[storage.Easy].Loss)
b.mediumWinRatio = float64(s.Basic[storage.Medium].Win) / float64(s.Basic[storage.Medium].Win+s.Basic[storage.Medium].Loss)
b.hardWinRatio = float64(s.Basic[storage.Hard].Win) / float64(s.Basic[storage.Hard].Win+s.Basic[storage.Hard].Loss)

max := 0
for score := range s.Endless {
if score > max {
max = score
}
}
g.endlessHighscore = max
b.endlessHighscore = max
}

func (s *scoreBoard) Render() app.UI {
// Render implements the interface for go-app to render the component.
func (b *scoreBoard) Render() app.UI {
easyText := "no game yet"
if !math.IsNaN(s.easyWinRatio) {
easyText = fmt.Sprintf("%.1f%%", s.easyWinRatio*100)
if !math.IsNaN(b.easyWinRatio) {
easyText = fmt.Sprintf("%.1f%%", b.easyWinRatio*100)
}
mediumText := "no game yet"
if !math.IsNaN(s.mediumWinRatio) {
mediumText = fmt.Sprintf("%.1f%%", s.mediumWinRatio*100)
if !math.IsNaN(b.mediumWinRatio) {
mediumText = fmt.Sprintf("%.1f%%", b.mediumWinRatio*100)
}
hardText := "no game yet"
if !math.IsNaN(s.hardWinRatio) {
hardText = fmt.Sprintf("%.1f%%", s.hardWinRatio*100)
if !math.IsNaN(b.hardWinRatio) {
hardText = fmt.Sprintf("%.1f%%", b.hardWinRatio*100)
}

return app.Table().Class("scores").Body(
app.Tr().Body(app.Td().Text("Easy"), app.Td().Text(easyText)),
app.Tr().Body(app.Td().Text("Medium"), app.Td().Text(mediumText)),
app.Tr().Body(app.Td().Text("Hard"), app.Td().Text(hardText)),
app.Tr().Body(app.Td().Text("Endless"), app.Td().Text(s.endlessHighscore)),
app.Tr().Body(app.Td().Text("Endless"), app.Td().Text(b.endlessHighscore)),
)
}

func (s *scoreBoard) HandleScoreUpdate(ctx app.Context, a app.Action) {
func (b *scoreBoard) HandleScoreUpdate(_ app.Context, a app.Action) {
scores, ok := a.Value.(storage.Scores)
if !ok {
fmt.Println("wrong type")
return
}
s.storeScores(scores)
b.storeScores(scores)
}
Loading

0 comments on commit a2cd1df

Please sign in to comment.