aoc2020/scripts/plot

103 lines
3.4 KiB
Racket
Executable File

#!/usr/bin/env racket
#lang racket
(require "aoc-lib.rkt" plot/no-gui)
(provide make-plot)
(define (make-plot plot-func)
(define data
(aoc-fetch-leaderboard (getenv "AOC_YEAR") (getenv "AOC_LEADERBOARD") (getenv "AOC_SESSION")))
(define (hash-ref* hash path [failure (lambda () (error "no such key" path))])
(define (->symbol el)
(match el
[(? string?) (string->symbol el)]
[(? number?) (string->symbol (number->string el))]
[(? symbol?) el]))
(define (do-failure) (if (procedure? failure) (failure) failure))
(match path
['() (do-failure)]
[(cons (app ->symbol fst) '())
(hash-ref hash fst failure)]
[(cons (app ->symbol fst) rst)
(if (hash-has-key? hash fst)
(hash-ref* (hash-ref hash fst) rst failure)
(do-failure))]))
(define members (hash-ref data 'members))
(define max-pts (hash-count members))
(define member-names (make-hash))
(struct tl-entry [mid day level] #:transparent)
(define timeline (make-hash))
(for ([(mid-in val) (in-hash members)])
(define mid (string->number (symbol->string mid-in)))
(define name (hash-ref val 'name))
(hash-set! member-names mid name)
(for* ([day (in-range 1 26)] [level (in-range 1 3)])
(match (hash-ref* val (list 'completion_day_level day level 'get_star_ts) #f)
[#f (void)]
[(app string->number (? number? time))
(hash-update! timeline time (lambda (v) (cons (tl-entry mid day level) v)) '())])))
(define timestamps (sort (hash-keys timeline) <))
(define point-values (make-hash))
(for* ([day (in-range 1 26)] [level (in-range 1 3)])
(hash-set! point-values (cons day level)
(if (= day 1)
0 ; day 1 is worth no points u___u
max-pts)))
(define point-tls (make-hash))
(for ([(mid _) (in-hash member-names)])
(hash-set! point-tls mid (vector)))
(define (get-last-pts data)
(if (vector-empty? data)
0
(vector-ref (vector-ref data (sub1 (vector-length data))) 1)))
(define (set-pts! mid time pts)
(hash-update! point-tls mid
(lambda (m-tl)
(vector-append m-tl (vector (vector time (get-last-pts m-tl))
(vector time pts))))))
(for ([time (in-list timestamps)])
(for ([solve (in-list (hash-ref timeline time))])
(match-define (tl-entry mid day level) solve)
(define key (cons day level))
(define m-tl (hash-ref point-tls mid))
(define old-pts (get-last-pts m-tl))
(define pts (hash-ref point-values key))
(hash-set! point-values key (max 0 (sub1 pts)))
(set-pts! mid time (+ old-pts pts))))
(define now (current-seconds))
(for ([(mid data) (in-hash point-tls)])
(define pts (get-last-pts data))
(set-pts! mid now pts))
(plot-x-ticks (date-ticks))
(define ordered-mids
(sort (hash-keys point-tls)
(lambda (a b)
(>= (get-last-pts (hash-ref point-tls a)) (get-last-pts (hash-ref point-tls b))))))
(plot-func
(for/list ([mid (in-list ordered-mids)])
(lines (hash-ref point-tls mid)
#:label (format "~a (~a pts)" (hash-ref member-names mid)
(get-last-pts (hash-ref point-tls mid)))
#:color mid
#:style mid
#:width 2))
#:x-label "date/time"
#:y-label "points"))
(module+ main
(require plot)
(plot-new-window? #t)
(make-plot plot))