You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

219 lines
5.0 KiB
Go

package valueobjects
import (
"fmt"
"time"
"github.com/knowfoolery/backend/shared/errors"
"github.com/knowfoolery/backend/shared/types"
)
type LeaderboardEntry struct {
playerID types.UserID
playerName string
score int32
rank int32
achievedAt time.Time
sessionID types.GameSessionID
gamesPlayed int32
avgScore float32
lastActivity time.Time
metadata map[string]interface{}
}
func NewLeaderboardEntry(
playerID types.UserID,
playerName string,
score int32,
sessionID types.GameSessionID,
) (*LeaderboardEntry, error) {
if playerID == "" {
return nil, errors.ErrInvalidPlayerID
}
if playerName == "" {
return nil, errors.ErrInvalidPlayerName
}
if score < 0 {
return nil, errors.ErrInvalidScore
}
if sessionID == "" {
return nil, errors.ErrInvalidSessionID
}
now := time.Now()
return &LeaderboardEntry{
playerID: playerID,
playerName: playerName,
score: score,
rank: 0,
achievedAt: now,
sessionID: sessionID,
gamesPlayed: 1,
avgScore: float32(score),
lastActivity: now,
metadata: make(map[string]interface{}),
}, nil
}
func (le *LeaderboardEntry) PlayerID() types.UserID {
return le.playerID
}
func (le *LeaderboardEntry) PlayerName() string {
return le.playerName
}
func (le *LeaderboardEntry) Score() int32 {
return le.score
}
func (le *LeaderboardEntry) Rank() int32 {
return le.rank
}
func (le *LeaderboardEntry) AchievedAt() time.Time {
return le.achievedAt
}
func (le *LeaderboardEntry) SessionID() types.GameSessionID {
return le.sessionID
}
func (le *LeaderboardEntry) GamesPlayed() int32 {
return le.gamesPlayed
}
func (le *LeaderboardEntry) AvgScore() float32 {
return le.avgScore
}
func (le *LeaderboardEntry) LastActivity() time.Time {
return le.lastActivity
}
func (le *LeaderboardEntry) Metadata() map[string]interface{} {
result := make(map[string]interface{})
for k, v := range le.metadata {
result[k] = v
}
return result
}
func (le *LeaderboardEntry) UpdateScore(newScore int32, sessionID types.GameSessionID) error {
if newScore < 0 {
return errors.ErrInvalidScore
}
if sessionID == "" {
return errors.ErrInvalidSessionID
}
if newScore > le.score {
le.score = newScore
le.sessionID = sessionID
le.achievedAt = time.Now()
}
le.gamesPlayed++
le.avgScore = (le.avgScore*float32(le.gamesPlayed-1) + float32(newScore)) / float32(le.gamesPlayed)
le.lastActivity = time.Now()
return nil
}
func (le *LeaderboardEntry) SetRank(rank int32) error {
if rank <= 0 {
return errors.ErrInvalidRank
}
le.rank = rank
return nil
}
func (le *LeaderboardEntry) UpdatePlayerName(name string) error {
if name == "" {
return errors.ErrInvalidPlayerName
}
le.playerName = name
return nil
}
func (le *LeaderboardEntry) SetMetadata(key string, value interface{}) error {
if key == "" {
return errors.ErrInvalidMetadata
}
le.metadata[key] = value
return nil
}
func (le *LeaderboardEntry) RemoveMetadata(key string) {
delete(le.metadata, key)
}
func (le *LeaderboardEntry) IsEqual(other *LeaderboardEntry) bool {
if other == nil {
return false
}
return le.playerID == other.playerID &&
le.score == other.score &&
le.sessionID == other.sessionID
}
func (le *LeaderboardEntry) String() string {
return fmt.Sprintf("LeaderboardEntry{PlayerID: %s, Name: %s, Score: %d, Rank: %d}",
le.playerID, le.playerName, le.score, le.rank)
}
type LeaderboardEntrySnapshot struct {
PlayerID types.UserID `json:"player_id"`
PlayerName string `json:"player_name"`
Score int32 `json:"score"`
Rank int32 `json:"rank"`
AchievedAt time.Time `json:"achieved_at"`
SessionID types.GameSessionID `json:"session_id"`
GamesPlayed int32 `json:"games_played"`
AvgScore float32 `json:"avg_score"`
LastActivity time.Time `json:"last_activity"`
Metadata map[string]interface{} `json:"metadata"`
}
func (le *LeaderboardEntry) ToSnapshot() LeaderboardEntrySnapshot {
return LeaderboardEntrySnapshot{
PlayerID: le.playerID,
PlayerName: le.playerName,
Score: le.score,
Rank: le.rank,
AchievedAt: le.achievedAt,
SessionID: le.sessionID,
GamesPlayed: le.gamesPlayed,
AvgScore: le.avgScore,
LastActivity: le.lastActivity,
Metadata: le.Metadata(),
}
}
func (le *LeaderboardEntry) FromSnapshot(snapshot LeaderboardEntrySnapshot) error {
if snapshot.PlayerID == "" {
return errors.ErrInvalidPlayerID
}
if snapshot.PlayerName == "" {
return errors.ErrInvalidPlayerName
}
if snapshot.Score < 0 {
return errors.ErrInvalidScore
}
le.playerID = snapshot.PlayerID
le.playerName = snapshot.PlayerName
le.score = snapshot.Score
le.rank = snapshot.Rank
le.achievedAt = snapshot.AchievedAt
le.sessionID = snapshot.SessionID
le.gamesPlayed = snapshot.GamesPlayed
le.avgScore = snapshot.AvgScore
le.lastActivity = snapshot.LastActivity
le.metadata = make(map[string]interface{})
for k, v := range snapshot.Metadata {
le.metadata[k] = v
}
return nil
}