Skip to main content

Elias Watson

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))))