πŸŽ„βŒ¨οΈ Advent of Code 2018

Check-in [3893cb]
Overview
Comment:Add Day 9 solution finally!
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 3893cb5af06e0157e578a2237dace188ff13f64cf68188f78a62d98b86c31778
User & Date: joel on 2018-12-14 04:46:24
Other Links: manifest | tags
Context
2018-12-15
03:54
Add Day 10 input check-in: 0f1f90 user: joel tags: trunk
2018-12-14
04:46
Add Day 9 solution finally! check-in: 3893cb user: joel tags: trunk
2018-12-13
19:01
Add first and second tries at Day 9 solutions β€” too slow! check-in: 711f70 user: joel tags: trunk
Changes

Added day09.rkt version [997d00].



























































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#lang racket

(require rackunit sugar)

;; Parse the problem input
(define input
  (map string->number
       (rest (regexp-match #px"(\\d+) players; last marble is worth (\\d+) points"
                           (file->string "day09-input.txt")))))

(match-define (list input-num-players input-last-marble) input)

(struct zipper (head current tail) #:transparent)

;; Zipper movement, transparently wrapping around in circular fashion
(define (zipper-next z)
  (match-define (zipper h c t) z)
  (cond
    [(and (empty? t) (empty? h)) z]
    [(empty? t) (zipper (list c) (first (reverse h)) (rest (reverse h)))]
    [else (zipper (cons c h) (first t) (rest t))]))

(define (zipper-prev z)
  (match-define (zipper h c t) z)
  (cond
    [(and (empty? t) (empty? h)) z]
    [(empty? h) (zipper (rest (reverse t)) (first (reverse t)) (list c))]
    [else (zipper (rest h) (first h) (cons c t))]))

;; Change the value at the cursor location
(define (zipper-set z new-current)
  (match-define (zipper h _ t) z)
  (zipper h new-current t))

;; Applies pred to the current value
(define (zipper-edit z pred)
  (match-define (zipper h c t) z)
  (zipper h (pred c) t))

;; Remove the current value and move the cursor to the next
;; item in `tail`. Returns 2 values: the resulting zipper and the
;; removed value.
(define (zipper-remove z)
  (match-define (zipper h c t) z)
  (values
   (cond [(empty? t) (zipper '() (first (reverse h)) (rest (reverse h)))]
         [else (zipper h (first t) (rest t))])
   c))

;; Insert a value into the current position, pushing the
;; old current value to the left (onto the `head`)
(define (zipper-insert z val)
  (match-define (zipper h c t) z)
  (cond
    [(empty? h) (zipper (list c) val t)]
    [else (zipper (cons c h) val t)]))

;; Move the zipper a given number of times using dir
;; `dir` would normally be zipper-next or zipper-prev
;; but can also be zipper-remove or any function that
;; takes a zipper as its only argument
(define (move-along z dir #:times [times 1])
  (cond [(zero? times) z]
        [else (move-along (dir z) dir #:times (sub1 times))]))

(define (list->zipper lst)
  (zipper empty (first lst) (rest lst)))

(define (zipper->list z)
  (match-define (zipper h c t) z)
  (append (reverse h) (list c) t))

;; Now that we have zippers to play with, spell out the problem’s
;; functions with painful clarity

;; Insert a marble between the first and second marbles after the
;; current marble
(define (insert-marble z marble)
  (zipper-insert (zipper-next z) marble))

;; Remove the seventh marble and return `(values zipper removed-marble)`
(define (remove-7th-marble z)
  (zipper-remove (move-along z zipper-prev #:times 7)))

;; Returns a function that will add a single number to the 
(define (adder marble1 marble2)
  (curry + (* (+ marble1 marble2) (sgn marble2))))

;; Play the game with specified number of players and marbles.
;; Returns zippers for both the player scores and the resulting marble circle
(define (play-game num-players last-marble)
  (for/fold ([scores (list->zipper (make-list num-players 0))]
             [circle (list->zipper '(0))])
            ([current-marble (in-naturals 1)])
    #:final (equal? current-marble last-marble)
    (define-values (result-circle maybe-removed-marble)
      (cond [(zero? (modulo current-marble 23))
             (remove-7th-marble circle)]
            [else
             (values (insert-marble circle current-marble) 0)]))
    (define new-score (+ (zipper-current scores)
                         (* (+ current-marble maybe-removed-marble) (sgn maybe-removed-marble))))
    (values (zipper-next (zipper-set scores new-score)) result-circle)))

(define (day09-part1 [players input-num-players] [last-marble input-last-marble])
  (define-values (scores _) (play-game players last-marble))
  (apply max (zipper->list scores)))

(module+ test
  ;; Example inputs
  (check-equal? (day09-part1 10 1618) 8317)
  (check-equal? (day09-part1 13 7999) 146373)
  (check-equal? (day09-part1 17 1104) 2764)
  (check-equal? (day09-part1 21 6111) 54718)
  (check-equal? (day09-part1 30 5807) 37305)

  ;; Finishes in β‰ˆ40 msec (using `raco test`) or β‰ˆ120 msec (in DrRacket)
  (check-equal? (time (day09-part1)) 374287)) ; Correct answer for part 1

(define (day09-part2)
  (day09-part1 input-num-players (* 100 input-last-marble)))

(module+ test
  ;; Finishes in β‰ˆ7,500 msec (using `raco test`) or β‰ˆ18,400 msec (in DrRacket)
  (check-equal? (time (day09-part2)) 3083412635)) ; Correct answer for part 2