Skip to content

Commit

Permalink
Introduce more stats to the guessed celebration
Browse files Browse the repository at this point in the history
  • Loading branch information
taiidani committed Sep 30, 2023
1 parent ed17407 commit 768b64a
Show file tree
Hide file tree
Showing 6 changed files with 189 additions and 115 deletions.
36 changes: 25 additions & 11 deletions app/guess.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ const (
ErrEmptyGuess = "Guess must not be empty"
)

type guessBag struct {
Session *sessions.SessionMode

Stats model.WordStats
}

var guessMutex = sync.Mutex{}

// GuessHandler is an API handler to process a user's guess.
Expand All @@ -39,18 +45,18 @@ func GuessHandler(c *gin.Context) {
return
}

word := strings.TrimSpace(c.Request.PostFormValue("word"))
guess := strings.TrimSpace(c.Request.PostFormValue("word"))

// Validate the guess
if len(word) == 0 {
if len(guess) == 0 {
errorResponse(c, http.StatusBadRequest, errors.New(ErrEmptyGuess))
return
} else if !wordStore.Validate(c, word) {
} else if !wordStore.Validate(c, guess) {
errorResponse(c, http.StatusBadRequest, errors.New(ErrInvalidWord))
return
}

err = guessHandlerReply(c, session, word)
word, err := guessHandlerReply(c, session, guess)
if err != nil {
errorResponse(c, http.StatusBadRequest, err)
return
Expand All @@ -61,15 +67,23 @@ func GuessHandler(c *gin.Context) {
return
}

c.HTML(http.StatusOK, "guesser.gohtml", session.Current())
data := fillGuessBag(session.Current(), wordStore, word)
c.HTML(http.StatusOK, "guesser.gohtml", data)
}

func fillGuessBag(s *sessions.SessionMode, w wordClient, word model.Word) guessBag {
bag := guessBag{}
bag.Session = s
bag.Stats = word.Stats()
return bag
}

var fnSetEndTime func() *time.Time = func() *time.Time {
now := time.Now()
return &now
}

func guessHandlerReply(ctx context.Context, session *sessions.Session, guess string) error {
func guessHandlerReply(ctx context.Context, session *sessions.Session, guess string) (model.Word, error) {
// Only one guess operation may happen simultaneously
// This allows us to get the Word then modify it with new data without overriding anyone
// else's contributions
Expand All @@ -80,23 +94,23 @@ func guessHandlerReply(ctx context.Context, session *sessions.Session, guess str
tm := session.DateUser()
word, err := wordStore.GetForDay(ctx, tm, session.Mode)
if err != nil {
return err
return word, err
}

current := session.Current()
switch strings.Compare(guess, word.Value) {
case -1:
for _, w := range current.Before {
if w == guess {
return fmt.Errorf("you have already guessed this word")
return word, fmt.Errorf("you have already guessed this word")
}
}
current.Before = append(current.Before, guess)
sort.Strings(current.Before)
case 1:
for _, w := range current.After {
if w == guess {
return fmt.Errorf("you have already guessed this word")
return word, fmt.Errorf("you have already guessed this word")
}
}
current.After = append(current.After, guess)
Expand All @@ -110,8 +124,8 @@ func guessHandlerReply(ctx context.Context, session *sessions.Session, guess str
Count: len(current.Before) + len(current.After) + 1,
})

return wordStore.SetWord(ctx, datastore.WordKey(session.Mode, tm), word)
return word, wordStore.SetWord(ctx, datastore.WordKey(session.Mode, tm), word)
}

return nil
return word, nil
}
113 changes: 72 additions & 41 deletions app/guess_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package app

import (
"errors"
"guess_my_word/internal/model"
"guess_my_word/internal/sessions"
"html/template"
"net/http"
Expand Down Expand Up @@ -46,10 +48,12 @@ func Test_GuessHandler(t *testing.T) {
},
}
},
want: render("guesser.gohtml", &sessions.SessionMode{
Start: time.Unix(1587930259, 0), // generates "belong"
Before: []string{},
After: []string{"power"},
want: render("guesser.gohtml", guessBag{
Session: &sessions.SessionMode{
Start: time.Unix(1587930259, 0), // generates "belong"
Before: []string{},
After: []string{"power"},
},
}),
},
{
Expand All @@ -65,10 +69,12 @@ func Test_GuessHandler(t *testing.T) {
},
}
},
want: render("guesser.gohtml", &sessions.SessionMode{
Start: time.Unix(1587930259, 0), // generates "belong"
Before: []string{"apple"},
After: []string{},
want: render("guesser.gohtml", guessBag{
Session: &sessions.SessionMode{
Start: time.Unix(1587930259, 0), // generates "belong"
Before: []string{"apple"},
After: []string{},
},
}),
},
{
Expand All @@ -84,12 +90,17 @@ func Test_GuessHandler(t *testing.T) {
},
}
},
want: render("guesser.gohtml", &sessions.SessionMode{
Start: time.Unix(1587930259, 0), // generates "belong"
End: &mockEndTime,
Before: []string{"apple"},
After: []string{},
Answer: "belong",
want: render("guesser.gohtml", guessBag{
Session: &sessions.SessionMode{
Start: time.Unix(1587930259, 0), // generates "belong"
End: &mockEndTime,
Before: []string{"apple"},
After: []string{},
Answer: "belong",
},
Stats: model.WordStats{
BestRun: 2,
},
}),
},
{
Expand All @@ -105,7 +116,7 @@ func Test_GuessHandler(t *testing.T) {
},
}
},
want: render("error.gohtml", ErrInvalidWord),
want: render("error.gohtml", errorBag{Message: errors.New(ErrInvalidWord)}),
},
{
name: "Empty word",
Expand All @@ -120,7 +131,7 @@ func Test_GuessHandler(t *testing.T) {
},
}
},
want: render("error.gohtml", ErrEmptyGuess),
want: render("error.gohtml", errorBag{Message: errors.New(ErrEmptyGuess)}),
},
{
name: "Correct Tomorrow",
Expand All @@ -135,12 +146,17 @@ func Test_GuessHandler(t *testing.T) {
},
}
},
want: render("guesser.gohtml", &sessions.SessionMode{
Start: time.Unix(1588030259, 0), // generates "roll"
End: &mockEndTime,
Before: []string{},
After: []string{},
Answer: "roll",
want: render("guesser.gohtml", guessBag{
Session: &sessions.SessionMode{
Start: time.Unix(1588030259, 0), // generates "roll"
End: &mockEndTime,
Before: []string{},
After: []string{},
Answer: "roll",
},
Stats: model.WordStats{
BestRun: 1,
},
}),
},
{
Expand All @@ -156,12 +172,17 @@ func Test_GuessHandler(t *testing.T) {
},
}
},
want: render("guesser.gohtml", &sessions.SessionMode{
Start: time.Unix(1587830259, 0), // generates "laundry"
End: &mockEndTime,
Before: []string{},
After: []string{},
Answer: "laundry",
want: render("guesser.gohtml", guessBag{
Session: &sessions.SessionMode{
Start: time.Unix(1587830259, 0), // generates "laundry"
End: &mockEndTime,
Before: []string{},
After: []string{},
Answer: "laundry",
},
Stats: model.WordStats{
BestRun: 1,
},
}),
},
{
Expand All @@ -177,12 +198,17 @@ func Test_GuessHandler(t *testing.T) {
},
}
},
want: render("guesser.gohtml", &sessions.SessionMode{
Start: time.Unix(1587930259, 0), // generates "teth"
End: &mockEndTime,
Before: []string{},
After: []string{},
Answer: "teth",
want: render("guesser.gohtml", guessBag{
Session: &sessions.SessionMode{
Start: time.Unix(1587930259, 0), // generates "teth"
End: &mockEndTime,
Before: []string{},
After: []string{},
Answer: "teth",
},
Stats: model.WordStats{
BestRun: 1,
},
}),
},
{
Expand All @@ -198,12 +224,17 @@ func Test_GuessHandler(t *testing.T) {
},
}
},
want: render("guesser.gohtml", &sessions.SessionMode{
Start: time.Unix(1587830259, 0), // generates "tayra"
End: &mockEndTime,
Before: []string{},
After: []string{},
Answer: "tayra",
want: render("guesser.gohtml", guessBag{
Session: &sessions.SessionMode{
Start: time.Unix(1587830259, 0), // generates "tayra"
End: &mockEndTime,
Before: []string{},
After: []string{},
Answer: "tayra",
},
Stats: model.WordStats{
BestRun: 1,
},
}),
},
{
Expand All @@ -219,7 +250,7 @@ func Test_GuessHandler(t *testing.T) {
},
}
},
want: render("error.gohtml", ErrEmptyGuess),
want: render("error.gohtml", errorBag{Message: errors.New(ErrEmptyGuess)}),
},
}
for _, tt := range tests {
Expand Down
13 changes: 12 additions & 1 deletion app/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import (

type indexBag struct {
baseBag
Mode string
Guesser guessBag
Mode string
}

func IndexHandler(c *gin.Context) {
Expand Down Expand Up @@ -44,5 +45,15 @@ func IndexHandler(c *gin.Context) {
return
}

// Generate the word for the day
tm := data.Session.DateUser()
word, err := wordStore.GetForDay(c, tm, data.Session.Mode)
if err != nil {
errorResponse(c, http.StatusBadRequest, err)
return
}

data.Guesser = fillGuessBag(data.Session.Current(), wordStore, word)

c.HTML(http.StatusOK, "index.gohtml", data)
}
52 changes: 10 additions & 42 deletions app/stats.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ const ErrRevealToday = "It's too early to reveal this word. Please try again lat

type statsBag struct {
baseBag
Yesterday replyData
YesterdayHard replyData
Today replyData
TodayHard replyData
Yesterday model.WordStats
YesterdayHard model.WordStats
Today model.WordStats
TodayHard model.WordStats
}

// StatsHandler is an HTML handler for pre-populating data to test with.
Expand Down Expand Up @@ -64,11 +64,11 @@ func StatsHandler(c *gin.Context) {
data := statsBag{}
data.Session = session
data.Page = "stats"
data.Yesterday = analyzeDay(wordYesterday)
data.YesterdayHard = analyzeDay(wordYesterdayHard)
data.Today = analyzeDay(wordToday)
data.Yesterday = wordYesterday.Stats()
data.YesterdayHard = wordYesterdayHard.Stats()
data.Today = wordToday.Stats()
data.Today.Word = ""
data.TodayHard = analyzeDay(wordTodayHard)
data.TodayHard = wordTodayHard.Stats()
data.TodayHard.Word = ""
c.HTML(http.StatusOK, "stats.gohtml", data)
}
Expand Down Expand Up @@ -103,7 +103,7 @@ func YesterdayHandler(c *gin.Context) {
return
}

data := analyzeDay(word)
data := word.Stats()
c.HTML(http.StatusOK, "stats.gohtml", data)
}

Expand All @@ -124,42 +124,10 @@ func TodayHandler(c *gin.Context) {
return
}

data := analyzeDay(word)
data := word.Stats()

// Wipe the word from the data, as it's today
data.Word = ""

c.HTML(http.StatusOK, "stats.gohtml", data)
}

type replyData struct {
Word string
Completions int
BestRun int
AvgRun int
}

func analyzeDay(word model.Word) replyData {
// If no one guessed that day
if len(word.Guesses) == 0 {
return replyData{Word: word.Value}
}

ret := replyData{
Word: word.Value,
Completions: len(word.Guesses),
BestRun: 999,
}

var guessCount = 0
for _, item := range word.Guesses {
guessCount += item.Count

if item.Count < ret.BestRun {
ret.BestRun = item.Count
}
}

ret.AvgRun = guessCount / len(word.Guesses)
return ret
}
Loading

0 comments on commit 768b64a

Please sign in to comment.