-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathranker.rkt
80 lines (62 loc) · 2.56 KB
/
ranker.rkt
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
#lang racket
; reads all lines of record.csv
(define record (file->lines "record.csv"))
; for each line in record, split on , and trim whitespace on the results
(define games
(map (lambda (x)
(map string-trim (string-split x ",")))
record))
; love some mutable state
(define ratings (make-hash))
(define (update-elo gm)
(define rating-white (hash-ref ratings (first gm) 800.0))
(define rating-black (hash-ref ratings (second gm) 800.0))
(define K 30)
; score for white and black
(define sw (cond
[(equal? (third gm) "1-0") 1]
[(equal? (third gm) "0-1") 0]
[(equal? (third gm) "1/2-1/2") .5]
[else (error "invalid match outcome")]))
(define sb (- 1 sw))
; used in calculating expected scores
(define qw (expt 10 (/ rating-white 400)))
(define qb (expt 10 (/ rating-black 400)))
; expected scores
(define ew (/ qw (+ qw qb)))
(define eb (- 1 ew))
; rating update is difference in actual score and expected score scaled by K
; love a good side effect
(hash-set! ratings (first gm) (+ rating-white (* K (- sw ew))))
(hash-set! ratings (second gm) (+ rating-black (* K (- sb eb)))))
(for-each update-elo games)
(define ranking (sort (hash->list ratings) #:key cdr >))
; pad string w spaces to target length
(define (pad str len)
(if (<= len (string-length str))
str
(pad (string-join (list str " ") "") len)))
; list of active players: people who played in the last 50 games
; this is kind of ugly?? used (let ()) before. maybe better?
(define active ((lambda ()
(define n_drop (- (length games) 50))
(define (extract-players gms)
(remove-duplicates
(foldr (lambda (x res) (append (take x 2) res)) '() gms)))
(extract-players (drop games n_drop)))))
; just to make a list '("1." "2." ...
(define (positions n_players)
(map (lambda (x) (string-join (list (~r x) ".") ""))
(range 1 (+ n_players 1))))
; filter inactive players out
(define ranked (filter (lambda (player) (member (car player) active)) ranking))
(displayln "A218B/HDL chess federation official ranking")
(displayln "-------------------------------------------")
(for-each (lambda (pos player)
(define longest_name (apply max (map string-length active)))
(define ppos (pad pos 3)) ; must change when we get 100 players
(define pplayer (pad (car player) longest_name))
(define rating (~r (exact-round (cdr player))))
(displayln (string-join (list ppos pplayer rating))))
(positions (length ranked))
ranked)