mirror of
https://github.com/jcreek/CosmicClash.git
synced 2026-09-13 03:22:04 +00:00
feat: add authoritative rating outcome scoring
This commit is contained in:
@@ -30,6 +30,33 @@ type Opponent struct {
|
||||
Score float64
|
||||
}
|
||||
|
||||
type MatchOutcome struct {
|
||||
Team0Score int
|
||||
Team1Score int
|
||||
Overtime bool
|
||||
Abandoners map[string]bool
|
||||
}
|
||||
|
||||
func ScoreForPlayer(outcome MatchOutcome, playerID string, team int) (float64, error) {
|
||||
if playerID == "" || (team != 0 && team != 1) || outcome.Team0Score < 0 || outcome.Team1Score < 0 {
|
||||
return 0, fmt.Errorf("invalid match outcome")
|
||||
}
|
||||
if outcome.Abandoners[playerID] {
|
||||
return 0, nil
|
||||
}
|
||||
if outcome.Team0Score == outcome.Team1Score {
|
||||
return 0.5, nil
|
||||
}
|
||||
winner := 0
|
||||
if outcome.Team1Score > outcome.Team0Score {
|
||||
winner = 1
|
||||
}
|
||||
if team == winner {
|
||||
return 1, nil
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
type RankedProfile struct {
|
||||
Rating
|
||||
RankedGames int
|
||||
|
||||
@@ -6,6 +6,27 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestScoreForPlayerHandlesDrawOvertimeAndAbandon(t *testing.T) {
|
||||
draw := MatchOutcome{Team0Score: 2, Team1Score: 2}
|
||||
if score, err := ScoreForPlayer(draw, "player-1", 0); err != nil || score != 0.5 {
|
||||
t.Fatalf("draw score = %v, %v", score, err)
|
||||
}
|
||||
overtime := MatchOutcome{Team0Score: 2, Team1Score: 3, Overtime: true}
|
||||
if score, err := ScoreForPlayer(overtime, "player-1", 0); err != nil || score != 0 {
|
||||
t.Fatalf("overtime loser score = %v, %v", score, err)
|
||||
}
|
||||
if score, err := ScoreForPlayer(overtime, "player-2", 1); err != nil || score != 1 {
|
||||
t.Fatalf("overtime winner score = %v, %v", score, err)
|
||||
}
|
||||
abandon := MatchOutcome{Team0Score: 0, Team1Score: 10, Abandoners: map[string]bool{"player-1": true}}
|
||||
if score, err := ScoreForPlayer(abandon, "player-1", 0); err != nil || score != 0 {
|
||||
t.Fatalf("abandoner score = %v, %v", score, err)
|
||||
}
|
||||
if _, err := ScoreForPlayer(draw, "", 0); err == nil {
|
||||
t.Fatal("empty player accepted")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateRatingMatchesCanonicalGlicko2Example(t *testing.T) {
|
||||
current := Rating{Value: 1500, RD: 200, Volatility: 0.06}
|
||||
opponents := []Opponent{
|
||||
|
||||
Reference in New Issue
Block a user