-
Notifications
You must be signed in to change notification settings - Fork 1
/
main.go
150 lines (119 loc) · 3.28 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package main
import (
"encoding/csv"
"flag"
"fmt"
"io"
"log"
"os"
"sort"
"strings"
elogo "github.com/kortemy/elo-go"
)
// The default starting score of players in our analyzer.
var defaultStartingScore = 1500
type currentGame struct {
winner string
loser string
}
type finalScore struct {
player string
eloScore int
}
func main() {
filepath := flag.String("path", "./mtgscores.csv", "path to analyze with tracker")
flag.Parse()
log.Printf("analyzing scores for %s", *filepath)
elo := elogo.NewElo()
elo.D = 800
elo.K = 40
scores := map[string]int{}
f, err := os.Open(*filepath)
if err != nil {
log.Fatalf("failed to open scores: %v", err)
}
reader := csv.NewReader(f)
for {
rec, err := reader.Read()
if err != nil {
if err == io.EOF {
break
}
log.Fatalf("error processing record: %v", err)
}
players := rec[2:]
log.Printf("comparing players: %s", players)
currentGame := []string{}
for _, player := range players {
player = strings.TrimSpace(player)
if player == "" {
// score the game once we've assembled it.
log.Printf("attempting to score game: %+v - %+v", scores, currentGame)
err := ScoreGame(elo, scores, currentGame)
if err != nil {
log.Fatalf("failed to score game: %v", err)
}
break
}
currentGame = append(currentGame, player)
}
}
// build final scores list
finalScores := []finalScore{}
for k, v := range scores {
fs := finalScore{
player: k,
eloScore: v,
}
finalScores = append(finalScores, fs)
}
// sort in descending order
sort.Slice(finalScores, func(i int, j int) bool {
return finalScores[i].eloScore > finalScores[j].eloScore
})
// print out final scores
for i, v := range finalScores {
fmt.Printf("%d --- %s --- %d\n", i, v.player, v.eloScore)
}
// log.Printf("player scores %v", scores)
}
// ScoreGame iterates over a game and updates scores in the playerMap accordingly
func ScoreGame(elo *elogo.Elo, scores map[string]int, game []string) error {
numPlayers := len(game)
if numPlayers < 2 {
return fmt.Errorf("invalid game")
}
for place := range game {
if place == numPlayers-1 {
// last place
break
}
// get player names
playerA := game[place]
playerB := game[place+1]
// get player scores from our score map
_, ok := scores[playerA]
if !ok {
scores[playerA] = defaultStartingScore
}
_, ok = scores[playerB]
if !ok {
scores[playerB] = defaultStartingScore
}
// get ranks after we've assured defaults
rankA := scores[playerA]
rankB := scores[playerB]
// check existence of player in map and set default score if they don't exist
// log.Printf("comparing ranks %s - %d to %s - %d", playerA, rankA, playerB, rankB)
// Results for A in the outcome of A defeats B
score := 1 // Use 1 in case A wins, 0 in case B wins, 0.5 in case of a draw
delta := elo.RatingDelta(rankA, rankB, float64(score)) // 20
// log.Printf("rating delta between %s and %s: %d", playerA, playerB, delta)
updatedRating := elo.Rating(rankA, rankB, float64(score)) // 1520
scores[playerA] = updatedRating
// log.Printf("updated %s score: %d", playerA, scores[playerA])
scores[playerB] = scores[playerB] - delta
// log.Printf("updated %s score: %d", playerB, scores[playerB])
}
return nil
}