diff --git a/cmd/server/games.go b/cmd/server/games.go index dbe4b38..378d871 100644 --- a/cmd/server/games.go +++ b/cmd/server/games.go @@ -11,6 +11,7 @@ import ( "slices" "strings" "time" + "unicode" "github.com/google/uuid" ) @@ -172,7 +173,10 @@ type ( ID string Started bool Completed time.Time - Players map[string]PlayerState + + Players map[string]PlayerState + + Trial Trial } PlayerState struct { @@ -210,6 +214,12 @@ type ( Points int } + Trial struct { + Prosecutor string + Killer string + Word string + } + EventType int EventPlayerJoin struct { @@ -249,6 +259,7 @@ type ( EventCodenameTrial struct { Type EventType Timestamp time.Time + Guilty bool } AllKillWords map[string]KillWords ) @@ -370,8 +381,6 @@ func (games Games) GameState(ctx context.Context, id string) (GameState, error) return result, err } - var stateBeforeAccusal *GameState - for _, event := range events { switch e := event.(type) { case EventPlayerJoin: @@ -406,19 +415,43 @@ func (games Games) GameState(ctx context.Context, id string) (GameState, error) result.Players[k] = player } case EventCodenameAccusal: - if stateBeforeAccusal == nil { - stateBeforeAccusal = &result - result = *stateBeforeAccusal + if actual := result.Players[e.Killer].KillWords.Codename; !actual.Consumed { + result.Trial.Prosecutor = e.Prosecutor + result.Trial.Killer = e.Killer + result.Trial.Word = e.Word - return GameState{}, fmt.Errorf("not impl: accusal: %+v", e) + guessed := e.Word + if !basicallyTheSame(actual, guessed) { + } else if err := games.CreateEventCodenameTrial(ctx, true); err != nil { + return err + } } case EventCodenameTrial: - if stateBeforeAccusal != nil { - result = *stateBeforeAccusal - stateBeforeAccusal = nil + if result.Trial == (Trial{}) { + } else if e.Guilty { + return GameState{}, fmt.Errorf("not impl: trial: guilty: %+v", e) + } else { + v := result.Players[result.Trial.Prosecutor] + v.Killwords.Codename.Consumed = true + v.Kills = append(v.Kills, Kill{ + Timestamp: e.Timestamp, + Victim: result.Trial.Killer, + KillWord: KillWord{ + Word: result.Trial.Word, + Points: -200, + }, + }) + result.Players[result.Trial.Prosecutor] = v - return GameState{}, fmt.Errorf("not impl: trial: %+v", e) + v = result.Players[result.Trial.Killer] + v.KillWords.Codename.KillWord.Word = TODO + + return fmt.Errorf("creating state CANNOT create events because it will eval every loop") + if err := games.CreateEventNotification(ctx, fmt.Sprintf(`%s accused the innocent %s of being %s. %s will get a new codename.`, prosecutorName, killerName, result.Trial.Word, killerName)); err != nil { + return err + } } + result.Trial = Trial{} case EventGameReset: return games.GameState(ctx, e.ID) default: @@ -429,6 +462,20 @@ func (games Games) GameState(ctx context.Context, id string) (GameState, error) return result, err } +func basicallyTheSame(a, b string) bool { + simplify := func(s string) string { + s = strings.TrimSpace(strings.ToLower(s)) + s2 := "" + for _, c := range s { + if unicode.IsLetter(c) { + s2 = fmt.Sprintf("%s%c", s2, c) + } + } + return s2 + } + return simplify(a) == simplify(b) +} + func (games Games) CreateGame(ctx context.Context, name string) (string, error) { var exists string if err := games.db.Query(ctx, @@ -490,10 +537,7 @@ func (games Games) CreateEventAssignmentRotation(ctx context.Context, id string, }, } - prevAllKillWords := make(AllKillWords) - for k, v := range state.Players { - prevAllKillWords[k] = v.KillWords - } + prevAllKillWords := games.AllKillWords() event.AllKillWords = prevAllKillWords.ShuffleAssignees(killer, victim, word) event.AllKillWords = event.AllKillWords.FillKillWords() @@ -501,6 +545,14 @@ func (games Games) CreateEventAssignmentRotation(ctx context.Context, id string, return games.createEvent(ctx, id, event) } +func (playerState PlayerState) AllKillWords() AllKillWords { + m := make(AllKillWords) + for k, v := range state.Players { + m[k] = v.KillWords + } + return m +} + func (games Games) CreateEventGameReset(ctx context.Context, gid string) error { state, err := games.GameState(ctx, gid) if err != nil {