117 lines
3.7 KiB
Racket
117 lines
3.7 KiB
Racket
#lang racket
|
|
|
|
(require racket/string
|
|
math/number-theory)
|
|
|
|
(module+ test
|
|
(require rackunit))
|
|
|
|
(module+ main
|
|
(displayln "Day 22"))
|
|
|
|
(define (read-instructions filename)
|
|
(with-input-from-file filename
|
|
(lambda ()
|
|
(for/list ([line (in-lines)])
|
|
(cond
|
|
[(string-prefix? line "deal into new stack") 'deal]
|
|
[(string-prefix? line "cut") `(cut ,(string->number (last (string-split line " "))))]
|
|
[(string-prefix? line "deal with increment") `(increment ,(string->number (last (string-split line " "))))]
|
|
[else 'other])))))
|
|
|
|
(define (deal deck)
|
|
(reverse deck))
|
|
|
|
(define (cut deck n)
|
|
(define split-fn (if (> n 0) split-at split-at-right))
|
|
(define-values (a b) (split-fn deck (abs n)))
|
|
(append b a))
|
|
|
|
(define (increment deck n)
|
|
(for/list ([i (in-range (length deck))])
|
|
(list-ref deck
|
|
(remainder (* i (modular-inverse n (length deck))) (length deck)))))
|
|
|
|
(define (execute instruction deck)
|
|
(match instruction
|
|
['deal (deal deck)]
|
|
[(list 'cut n) (cut deck n)]
|
|
[(list 'increment n) (increment deck n)]))
|
|
|
|
(define (shuffle deck instructions)
|
|
(for/fold ([d deck]) ([i instructions])
|
|
(execute i d)))
|
|
|
|
(module+ test
|
|
(check-equal? (shuffle (range 10) (read-instructions "test1")) '(0 3 6 9 2 5 8 1 4 7))
|
|
(check-equal? (shuffle (range 10) (read-instructions "test2")) '(3 0 7 4 1 8 5 2 9 6))
|
|
(check-equal? (shuffle (range 10) (read-instructions "test3")) '(6 3 0 7 4 1 8 5 2 9))
|
|
(check-equal? (shuffle (range 10) (read-instructions "test4")) '(9 2 5 8 1 4 7 0 3 6)))
|
|
|
|
(module+ main
|
|
(displayln "Part 1:")
|
|
(define instructions (read-instructions "input"))
|
|
(define deck (range 10007))
|
|
(time (index-of (shuffle deck instructions) 2019)))
|
|
|
|
(define (deal-card deck-size card)
|
|
(- (sub1 deck-size) card))
|
|
|
|
(define (cut-card deck-size n card)
|
|
(modulo (- card n) deck-size))
|
|
|
|
(define (increment-card deck-size n card)
|
|
(modulo (* n card) deck-size))
|
|
|
|
(define (execute-card deck-size instruction card)
|
|
(match instruction
|
|
['deal (deal-card deck-size card)]
|
|
[(list 'cut n) (cut-card deck-size n card)]
|
|
[(list 'increment n) (increment-card deck-size n card)]))
|
|
|
|
(define (shuffle-card deck-size instructions card)
|
|
(for/fold ([c card]) ([i instructions])
|
|
(execute-card deck-size i c)))
|
|
|
|
(module+ main
|
|
(displayln "Part 1 (better):")
|
|
(time (shuffle-card 10007 instructions 2019)))
|
|
|
|
(define (deal-card-inv deck-size card)
|
|
(- (sub1 deck-size) card))
|
|
|
|
(define (cut-card-inv deck-size n card)
|
|
(modulo (+ card n) deck-size))
|
|
|
|
(define (increment-card-inv deck-size n card)
|
|
(remainder (* card (modular-inverse n deck-size)) deck-size))
|
|
|
|
(define (execute-card-inv deck-size instruction card)
|
|
(match instruction
|
|
['deal (deal-card-inv deck-size card)]
|
|
[(list 'cut n) (cut-card-inv deck-size n card)]
|
|
[(list 'increment n) (increment-card-inv deck-size n card)]))
|
|
|
|
(define (shuffle-card-inv deck-size reversed-instructions card)
|
|
(for/fold ([c card]) ([i reversed-instructions])
|
|
(execute-card-inv deck-size i c)))
|
|
|
|
(module+ main
|
|
(displayln "Part 2:")
|
|
(define deck-size 119315717514047)
|
|
(define iterations 101741582076661)
|
|
|
|
;; Operations are linear, we find the coefficients and apply them directly
|
|
(define x 2020)
|
|
(define y (shuffle-card-inv deck-size (reverse instructions) x))
|
|
(define z (shuffle-card-inv deck-size (reverse instructions) y))
|
|
|
|
(define a (modulo (* (- z y) (modular-inverse (- y x) deck-size)) deck-size))
|
|
(define b (modulo (- y (* a x)) deck-size))
|
|
|
|
(modulo (+ (* (modular-expt a iterations deck-size)
|
|
x)
|
|
(* (sub1 (modular-expt a iterations deck-size))
|
|
(modular-inverse (sub1 a) deck-size)
|
|
b))
|
|
deck-size))
|