-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathswiss.js
202 lines (169 loc) · 5.24 KB
/
swiss.js
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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
// players = []
// // ["white_name", "black_name", "1-0"] draw is "1/2-1/2", ongoing is "*"
// games = []
// games_by_round = []
// const Swiss = {
const get_standings = (players, games) => {
const table = get_table(players, games)
return table.map(([player, record]) => { return [player, score(record)] })
}
const get_record = (games, player) => {
// console.log(games, player)
let record = []
games.map((game) => {
if (game[0] == player) {
if (game[2] == "1-0") {
record.push(["white", game[1], 1])
} else if (game[2] == "0-1") {
record.push(["white", game[1], 0])
} else if (game[2] == "1/2-1/2") {
record.push(["white", game[1], 0.5])
}
} else if (game[1] == player) {
if (game[2] == "1-0") {
record.push(["black", game[0], 0])
} else if (game[2] == "0-1") {
record.push(["black", game[0], 1])
} else if (game[2] == "1/2-1/2") {
record.push(["black", game[0], 0.5])
}
}
})
return record
}
// returns a list of players' records, sorted by highest score
const get_table = (players, games) => {
// console.log(players, games)
let records = players.map(function(player){
return [player, get_record(games, player)]
})
// console.log(records)
return records.sort((a,b) => {return score(b[1]) - score(a[1])})
}
const color_count = (record, color) => {
return record.reduce((total, game) => {return total + (game[0] === color)}, 0)
}
const color_diff = (record) => {
return color_count(record, "white") - color_count(record, "black")
}
const games_against = (player, record) => {
return record.reduce((total, game) => {return total + (game[1] === player)}, 0)
}
const colsum = (matrix, col) => {
return matrix.reduce((sum,row) => {return sum + row[col]}, 0)
}
const score = (record) => {
return colsum(record, 2)
}
const potential_opponents = (player, record, players_to_pair, max_games) => {
// get all players with the highest score who have not yet played more than max_games
// against the given player
max_games = max_games || 0
let potential_opps = {}
// console.log("finding matches")
for (let i=0; i<players_to_pair.length; i++) {
let [other_player, other_record] = players_to_pair[i]
// console.log("checking ", other_player)
if (games_against(player, other_record) > max_games) {
// console.log("already played--continuing")
continue
}
if (Object.keys(potential_opps).length != 0) {
if ( score(other_record) != score(some_value_of(potential_opps)[1]) ){
break
}
}
potential_opps[i] = [other_player, other_record]
}
if (Object.keys(potential_opps).length != 0){
return potential_opps
}
return potential_opponents(player, record, players_to_pair, max_games+1)
}
const next_round = (players, games) => {
games = games || []
// order players by score
const players_to_pair = get_table(players, games)
let pairings = []
while (players_to_pair.length > 1) {
const [player, record] = players_to_pair.shift()
// console.log("pairing ", player)
const opps = potential_opponents(player, record, players_to_pair)
// console.log("potential_opps:\n", JSON.stringify(opps))
// of potential opponents, find the one with the most complementary white-black record
const wb_diff = color_diff(record)
let best_diff = 9999999
let best_opp = null
for (let i in opps) {
const diff = Math.abs(color_diff(opps[i][1]) - wb_diff)
// console.log(opps[i][0], " has diff ", diff)
if (diff < best_diff) {
best_diff = diff
best_opp = i
}
}
const chosen_player = players_to_pair.splice(best_opp, 1)[0]
// console.log("selecting ", chosen_player[0], " with diff ", best_diff)
// add them to the pairings, with the player who has played fewer blacks as black
if (wb_diff > color_diff(chosen_player[1])) {
pairings.push([chosen_player[0], player, "*"])
} else {
pairings.push([player, chosen_player[0], "*"])
}
}
if (players_to_pair.length) {
pairings.push([players_to_pair[0][0], null, "*"])
}
return pairings
}
const some_value_of = (obj) => {
return obj[Object.keys(obj)[0]]
}
const assert = (condition, message) => {
if (!condition) {
throw message || "Assertion failed"
}
}
const print_table = (table) => {
for (let [player, record] of table) {
console.log(player, JSON.stringify(record))
}
}
const print_scores = (table) => {
for (let [player, record] of table) {
console.log(player, score(record), color_diff(record))
}
}
const test = () => {
let test_players = ["a", "b", "c", "d", "e"]
let games = []
for (let i=1; i<16; i++){
for (let game of games) {
game[2] = "1-0"
}
games = games.concat(next_round(test_players, games))
console.log("\n\n==round ", i, "==\npairings: ", games, "\n")
console.log("table: ")
print_scores(get_table(test_players, games))
}
}
module.exports = {
get_standings : get_standings,
get_record : get_record,
get_table : get_table,
color_count : color_count,
color_diff : color_diff,
games_against : games_against,
colsum : colsum,
score : score,
potential_opponents : potential_opponents,
next_round : next_round,
some_value_of : some_value_of,
assert : assert,
print_table : print_table,
print_scores : print_scores,
test : test
}
// }
// if (typeof exports !== 'undefined') exports.Swiss = Swiss;
// if (typeof define !== 'undefined') define( function () { return Swiss; });