2 Day 2: Gift Shop
2.1 Part 1
Today, we have to review intervals. Each interval is defined by a start-end pair and we must sum all "invalid" numbers in that range.
Now, an "invalid" number is defined as: made only of some sequence of digits repeated twice.
This sounds simple enough: split the number in two (as a string), compare each part, done!
Function "sum-invalid-ids" is just a helper to go over ranges from the input and add invalid values. It’s nothing really exciting.
(define (is-valid? n) (let* ([number (~a n)] [mid (quotient (string-length number) 2)] [head (substring number 0 mid)] [tail (substring number mid)]) (not (string=? head tail)))) (sum-invalid-ids is-valid-1? input)
2.2 Part 2
The definition of an "invalid" number changes a bit to include sequence of digits repeated twice or more. This means that we must consider splitting the string in chunks of 1, 2, 3… until half of the number of digits (which is basically splitting in 2, like we did previously).
"split-string" is another utility function that splits a string into chuncks of the specified size. It could be useful some other times, maybe I should add that to a utility file…
(define (is-valid-2? n) (let* ([number (~a n)] [max-chunk-size (/ (string-length number) 2)]) (for/and ([i (in-inclusive-range 1 max-chunk-size)]) (let ([chunks (split-string number i)]) (or (empty? chunks) (not (apply string=? chunks))))))) (sum-invalid-ids is-valid-2? input)
It works sufficiently well for the input, but I think it could be optimized a bit. If a number is not a repetition of a group of 2 digits, it cannot be a repetition of a group of 4 digits, 8 digits… any multiple of 2 actually. I could have a "sieve" reducing the number of test to perform until we reach half the size of the input number. Pretty much like a primality test.
But since it could complexify the solution and there are only 24 hours in a days, and many other fun things to do, I didn’t bother. Feel free to try it out though!
2.3 Complete implementation
#lang racket (define input (if (file-exists? "inputs/day2.txt") (string-trim (file->string "inputs/day2.txt")) (begin (println "Warning: 'inputs/day2.txt' is not found, using 'samples/day2.txt' instead.") (string-trim (file->string "samples/day2.txt"))))) (define (string->ranges s) (map (lambda (x) (map string->number (take (string-split x "-") 2))) (string-split s ","))) (define (split-string s chunk-size) (let ([len (string-length s)]) (cond [(not(= 0 (remainder len chunk-size))) '()] [else (for/list ([i (in-range 0 len chunk-size)]) (substring s i (min (+ i chunk-size) len)))]))) (define (sum-invalid-ids validator input) ; Input = list of (start end) ranges (for/sum ([start-end (in-list (string->ranges input))]) (let ([start (first start-end)] [end (last start-end)]) (for/sum ([n (in-inclusive-range start end)]) (if (validator n) 0 n))))) ; ----------------------------------------------------------------------------- ; PART 1 (define (is-valid-1? n) (let* ([number (~a n)] [mid (quotient (string-length number) 2)] [head (substring number 0 mid)] [tail (substring number mid)]) (not (string=? head tail)))) (define solution1 (sum-invalid-ids is-valid-1? input)) (printf "Part 1: ~a~n" solution1) ; ----------------------------------------------------------------------------- ; PART 2 (define (is-valid-2? n) (let* ([number (~a n)] [max-chunk-size (/ (string-length number) 2)]) (for/and ([i (in-inclusive-range 1 max-chunk-size)]) (let ([chunks (split-string number i)]) (or (empty? chunks) (not (apply string=? chunks))))))) (define solution2 (sum-invalid-ids is-valid-2? input)) (printf "Part 2: ~a~n" solution2)