Advent of Code 2022
Table of Contents
This year I decided to solve Advent of Code in Racket. I’ve seen lots of people gush over how much they love Lisp, so I want to see what it’s all about. The functional paradigm isn’t completely foreign to me as I’ve written a few web apps in Elm and I’ve messed around with Haskell, but I’ve never used a Lisp for anything complex.
##
Day 1 - Calorie Counting
#
Part 1
; Sum integers from stdin until a blank line (or EOF) is encountered
(define (sum-input-integers-until-break line)
(cond
[(non-empty-string? line)
(+ (string->number line)
(sum-input-integers-until-break (read-line)))]
[else 0]))
; Get a list of calories from stdin, stopping when EOF is encountered
(define (get-calorie-list)
(let ([line (read-line)])
(cond
[(eof-object? line) '()]
[else (cons (sum-input-integers-until-break line)
(get-calorie-list))])))
; Get the maximum calories
(apply max (get-calorie-list))
#
Part 2
Part 2 is almost identical. The only difference is that we sum the top three calorie counts instead of just taking the maximum.
; Sum integers from stdin until a blank line (or EOF) is encountered
(define (sum-input-integers-until-break line)
(cond
[(non-empty-string? line)
(+ (string->number line)
(sum-input-integers-until-break (read-line)))]
[else 0]))
; Get a list of calories from stdin, stopping when EOF is encountered
(define (get-calorie-list)
(let ([line (read-line)])
(cond
[(eof-object? line) '()]
[else (cons (sum-input-integers-until-break line)
(get-calorie-list))])))
; Sum a list of numbers
(define (sum nums) (apply + nums))
; Get the top N largest numbers
(define (largest-nums nums n) (take (sort nums >) n))
; Sum the top three largest calorie counts
(sum (largest-nums (get-calorie-list) 3))
##
Day 2 - Rock Paper Scissors
#
Part 1
(require racket/string)
;; PARSING
(define (parse-choice c)
(cond
[(or (equal? c "A") (equal? c "X")) 0] ; Rock -> 0
[(or (equal? c "B") (equal? c "Y")) 1] ; Paper -> 1
[(or (equal? c "C") (equal? c "Z")) 2] ; Scissors -> 2
[else -1]))
; Example: "B X" -> '(1 0)
(define (parse-strategy-line line)
(map parse-choice (string-split line)))
;; IO
(define (read-all-lines)
(let ([line (read-line)])
(if (string? line)
(cons line (read-all-lines))
'())))
(define (read-strategies)
(map parse-strategy-line (read-all-lines)))
;; SCORING
; The winning choice can be calculated by just adding 1 and modulo'ing by 3
; 0 (Rock) is beat by 1 (Paper)
; 1 (Paper) is beat by 2 (Scissors)
; 2 (Scissors) is beat by 0 (Rock)
(define (winning-choice n) (modulo (+ n 1) 3))
; Since rock gets 1 point, paper gets 2, and scissors gets 3, we can just
; add one to our numeric representation of the choices
(define (score-choice n) (+ n 1))
(define (score-outcome other you)
(cond
((= you (winning-choice other)) 6) ; 6 points for winning
((= you other) 3) ; 3 points for drawing
(else 0))) ; No points for losing
(define (score-round other you)
(+ (score-choice you)
(score-outcome other you)))
(define (score-rounds rounds)
(map (lambda (r) (apply score-round r)) rounds))
;; MAIN
(define (sum nums) (apply + nums))
(sum (score-rounds (read-strategies)))
#
Part 2
(require racket/string)
;; LOGIC
; The winning choice can be calculated by just adding 1 and modulo'ing by 3
; 0 (Rock) is beat by 1 (Paper)
; 1 (Paper) is beat by 2 (Scissors)
; 2 (Scissors) is beat by 0 (Rock)
(define (winning-choice n)
(modulo (+ n 1) 3))
; Likewise, the losing choice can be calculated by adding 2 and modulo'ing by 3
; 0 (Rock) beats 2 (Scissors)
; 1 (Paper) beats 0 (Rock)
; 2 (Scissors) beats 1 (Paper)
(define (losing-choice n)
(modulo (+ n 2) 3))
;; PARSING
(define (parse-choice c)
(cond
[(equal? c "A") 0] ; Rock -> 0
[(equal? c "B") 1] ; Paper -> 1
[(equal? c "C") 2] ; Scissors -> 2
[else -1]))
(define (parse-outcome other outcome)
(list other
(cond
[(equal? outcome "X") (losing-choice other)] ; Lose
[(equal? outcome "Y") other] ; Draw
[(equal? outcome "Z") (winning-choice other)] ; Win
[else -1])))
(define (parse-strategy-line line)
(let ([fields (string-split line)])
(parse-outcome
(parse-choice (list-ref fields 0))
(list-ref fields 1))))
;; IO
(define (read-all-lines)
(let ([line (read-line)])
(if (string? line)
(cons line (read-all-lines))
'())))
(define (read-strategies)
(map parse-strategy-line (read-all-lines)))
; Since rock gets 1 point, paper gets 2, and scissors gets 3, we can just
; add one to our numeric representation of the choices
(define (score-choice n) (+ n 1))
(define (score-outcome other you)
(cond
((= you (winning-choice other)) 6) ; 6 points for winning
((= you other) 3) ; 3 points for drawing
(else 0))) ; No points for losing
(define (score-round other you)
(+ (score-choice you)
(score-outcome other you)))
(define (score-rounds rounds)
(map (lambda (r) (apply score-round r)) rounds))
;; MAIN
(define (sum nums) (apply + nums))
(sum (score-rounds (read-strategies)))
##
Day 3 - Get Organized
#
Part 1
(require racket/set)
;; IO
(define (get-input-lines)
(let ([line (read-line)])
(if (string? line)
(cons line (get-input-lines))
'())))
;; DUPLICATE FINDING
(define (split-string-in-half s)
(let ([half-index (/ (string-length s) 2)])
(list
(substring s 0 half-index)
(substring s half-index))))
(define (chars-in-both-strings a b)
(set-intersect
(string->list a)
(string->list b)))
(define (find-duplicate-in-rucksack s)
(apply chars-in-both-strings (split-string-in-half s)))
;; PRIORITY
(define (item-priority c)
(let ([i (char->integer c)])
(if (< i (char->integer #\a))
(+ (- i (char->integer #\A)) 27)
(+ (- i (char->integer #\a)) 1))))
(define (rucksack-priority s)
(item-priority (car (find-duplicate-in-rucksack s))))
;; MAIN
(apply + (map rucksack-priority (get-input-lines)))
#
Part 2
(require racket/set)
;; IO
(define (get-input-lines)
(let ([line (read-line)])
(if (string? line)
(cons line (get-input-lines))
'())))
;; GROUPING
(define (into-groups elves)
(if (not (empty? elves))
(cons (take elves 3) (into-groups (drop elves 3)))
'()))
(define (duplicate-char-in-strings strs)
(car (apply set-intersect (map string->list strs))))
;; PRIORITY
(define (item-priority c)
(let ([i (char->integer c)])
(if (< i (char->integer #\a))
(+ (- i (char->integer #\A)) 27)
(+ (- i (char->integer #\a)) 1))))
(define (group-priority g)
(item-priority (duplicate-char-in-strings g)))
;; MAIN
(apply + (map group-priority (into-groups (get-input-lines))))