#lang br (require racket/file rackunit) (define (stack cards) (reverse cards)) (define (cut n cards) (define-values (head tail) (split-at cards (match n [(? positive?) n] [_ (- (length cards) (abs n))]))) (append tail head)) (define (increment n cards) (define len (length cards)) (define vec (make-vector len)) (for ([(card idx) (in-indexed cards)]) (vector-set! vec (modulo (* idx n) len) card)) (vector->list vec)) (define (run program [deck-size 10]) (for/fold ([cards (range deck-size)]) ([ln (in-list (string-split program "\n"))]) (match (string-split ln) [(list "cut" (app string->number num)) (cut num cards)] [(list _ _ "increment" (app string->number num)) (increment num cards)] [_ (stack cards)]))) (check-equal? (run "deal with increment 7 deal into new stack deal into new stack") '(0 3 6 9 2 5 8 1 4 7)) (check-equal? (run "cut 6 deal with increment 7 deal into new stack") '(3 0 7 4 1 8 5 2 9 6)) (check-equal? (run "deal into new stack cut -2 deal with increment 7 cut 8 cut -4 deal with increment 7 cut 3 deal with increment 9 deal with increment 3 cut -1") '(9 2 5 8 1 4 7 0 3 6)) ;; 1 (check-eq? (index-of (run (file->string "22.rktd") 10007) 2019) 4086) ;; 2 #| The idea is that the numbers are too large to shuffle the deck directly. Unlike the previous question, this one asks what card is at a certain position. So we want to go in reverse: just trace that position backwards through each operation and figure out where we are at the start. Also, we are likely going to discover a cycle in the transformations (that is, card 2020 returns to position 2020 after a while) and we can use this cycle length to reduce the number of times we have to execute the (un)shuffling. ("Likely" because otherwise this puzzle cannot be computed in reasonable time.) |# (define (unstack-pos cardlen pos) (- (sub1 cardlen) pos)) (check-eq? (unstack-pos 10 0) 9) (check-eq? (unstack-pos 10 9) 0) (define (uncut-pos cardlen pos n) (define cut-pos (match n [(? positive?) n] [_ (- cardlen (abs n))])) (cond [(<= (- cardlen cut-pos) pos) (- pos (- cardlen cut-pos))] [else (+ pos cut-pos)])) (check-eq? (uncut-pos 10 7 3) 0) (check-eq? (uncut-pos 10 0 3) 3) (check-eq? (uncut-pos 10 7 -4) 3) (check-eq? (uncut-pos 10 0 -4) 6) (define (unincrement-pos cardlen pos inc) 42)