#!/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)] [(? 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))