aboutsummaryrefslogtreecommitdiff
path: root/racket/aoc2020
diff options
context:
space:
mode:
authorH.J <thechairman@thechairman.info>2024-10-09 11:36:55 -0400
committerH.J <thechairman@thechairman.info>2024-10-09 11:36:55 -0400
commit8777ff071f7bb37631baa7b6717ad29961e50911 (patch)
tree6d59c4ed58e454b960339c3d1151f0a879e8d7cb /racket/aoc2020
parent6156a9ef7be4012063a042aafb4e9b0d7eadde8e (diff)
downloadgleam_aoc-8777ff071f7bb37631baa7b6717ad29961e50911.tar.gz
gleam_aoc-8777ff071f7bb37631baa7b6717ad29961e50911.zip
sorting by language
Diffstat (limited to 'racket/aoc2020')
-rw-r--r--racket/aoc2020/day-01/day-01.rkt20
-rw-r--r--racket/aoc2020/day-02/day-02.rkt33
-rw-r--r--racket/aoc2020/day-03/day-03.rkt16
-rw-r--r--racket/aoc2020/day-04/day-04.rkt64
-rw-r--r--racket/aoc2020/day-05/day-05.rkt35
-rw-r--r--racket/aoc2020/day-06/day-06.rkt22
-rw-r--r--racket/aoc2020/day-07/day-07.rkt46
-rw-r--r--racket/aoc2020/day-08/day-08.ipynb220
-rw-r--r--racket/aoc2020/day-09/day-09.ipynb166
-rw-r--r--racket/aoc2020/day-10/day-10.rkt37
-rw-r--r--racket/aoc2020/day-11/day-11.rkt60
-rw-r--r--racket/aoc2020/day-12/day-12.rkt56
-rw-r--r--racket/aoc2020/day-13/day-13.rkt32
-rw-r--r--racket/aoc2020/day-14/day-14.rkt11
-rw-r--r--racket/aoc2020/day-15/day-15.rkt22
-rw-r--r--racket/aoc2020/day-16/day-16.rkt52
16 files changed, 892 insertions, 0 deletions
diff --git a/racket/aoc2020/day-01/day-01.rkt b/racket/aoc2020/day-01/day-01.rkt
new file mode 100644
index 0000000..e31c45c
--- /dev/null
+++ b/racket/aoc2020/day-01/day-01.rkt
@@ -0,0 +1,20 @@
+#lang racket
+(require "../../jj-aoc.rkt"
+ threading)
+
+(define entries (~>> (open-day 01 2020) (port->list read) list->set))
+
+;; part 1
+(define (look-for-complement xs)
+ (define x (set-first xs))
+ (cond
+ [(set-member? xs (- 2020 x)) (* x (- 2020 x))]
+ [else (look-for-complement (set-rest xs))]))
+
+(time (look-for-complement entries))
+
+;; part 2
+(time (for*/first ([x (in-set entries)]
+ [y (in-set (set-subtract entries (set x)))]
+ #:when (set-member? (set-subtract entries (set x y)) (- 2020 x y)))
+ (* x y (- 2020 x y))))
diff --git a/racket/aoc2020/day-02/day-02.rkt b/racket/aoc2020/day-02/day-02.rkt
new file mode 100644
index 0000000..9e22a1a
--- /dev/null
+++ b/racket/aoc2020/day-02/day-02.rkt
@@ -0,0 +1,33 @@
+#lang racket
+(require "../../jj-aoc.rkt"
+ threading)
+
+(struct policy (least most char pwd) #:transparent)
+
+(define (make-policy least-str most-str char-str pwd)
+ (policy (string->number least-str) (string->number most-str) (string-ref char-str 0) pwd))
+
+(define policies
+ (for/list ([l (in-lines (open-day 02 2020))])
+ (~>> l (regexp-match #px"(\\d+)-(\\d+) (\\w): (.*)") rest (apply make-policy))))
+
+;; part 1
+(define (valid-policy? p)
+ (~>> p
+ policy-pwd
+ string->list
+ (count (curry char=? (policy-char p)))
+ ((λ (n) (and (>= n (policy-least p)) (<= n (policy-most p)))))))
+
+(for/sum ([p (in-list policies)] #:when (valid-policy? p)) 1)
+
+;; part 2
+(define (valid-revised-policy? p)
+ (~>> p
+ policy-pwd
+ string->list
+ ((λ (lst) (list (list-ref lst (sub1 (policy-most p))) (list-ref lst (sub1 (policy-least p))))))
+ (count (curry char=? (policy-char p)))
+ (= 1)))
+
+(for/sum ([p (in-list policies)] #:when (valid-revised-policy? p)) 1)
diff --git a/racket/aoc2020/day-03/day-03.rkt b/racket/aoc2020/day-03/day-03.rkt
new file mode 100644
index 0000000..ee9edcf
--- /dev/null
+++ b/racket/aoc2020/day-03/day-03.rkt
@@ -0,0 +1,16 @@
+#lang racket
+(require "../../jj-aoc.rkt"
+ threading)
+
+(define (check-for-trees run rise)
+ (for*/sum ([(row i) (in-indexed (port->lines (open-day 3 2020)))]
+ #:when (= 0 (modulo i rise))
+ [possible-tree (in-value (sequence-ref (in-cycle row) (* (/ run rise) i)))]
+ #:when (and (char=? possible-tree #\#)))
+ 1))
+
+;; part 1
+(check-for-trees 3 1)
+
+;; part 2
+(~>> '((1 1) (3 1) (5 1) (7 1) (1 2)) (map (curry apply check-for-trees)) (apply *))
diff --git a/racket/aoc2020/day-04/day-04.rkt b/racket/aoc2020/day-04/day-04.rkt
new file mode 100644
index 0000000..54d50f8
--- /dev/null
+++ b/racket/aoc2020/day-04/day-04.rkt
@@ -0,0 +1,64 @@
+#lang racket
+(require "../../jj-aoc.rkt"
+ threading)
+
+(define passports
+ (~> (open-day 4 2020) (port->string) (string-split "\n\n") (map (λ~> (string-replace "\n" " ")) _)))
+
+;; part 1
+(define required-fields (list "byr:" "iyr:" "eyr:" "hgt:" "hcl:" "ecl:" "pid:"))
+
+(define (valid-passport? p)
+ (andmap (λ (s) (string-contains? p s)) required-fields))
+
+(count valid-passport? passports)
+
+;; part 2
+(define passport-fields
+ (for/list ([p (in-list passports)] #:when (valid-passport? p))
+ (~> p string-split (map (curryr string-split ":") _) flatten (apply hash _))))
+
+(define (between x low high)
+ (and (x . >= . low) (x . <= . high)))
+
+(define (valid-byr? p)
+ (define year (string->number (hash-ref p "byr")))
+ (between year 1920 2002))
+
+(define (valid-iyr? p)
+ (define year (string->number (hash-ref p "iyr")))
+ (between year 2010 2020))
+
+(define (valid-eyr? p)
+ (define year (string->number (hash-ref p "eyr")))
+ (between year 2020 2030))
+
+(define (valid-hgt? p)
+ (define height (hash-ref p "hgt"))
+ (cond
+ [(string-suffix? height "cm")
+ (let ([h (string->number (string-trim height "cm"))]) (between h 150 193))]
+ [(string-suffix? height "in")
+ (let ([h (string->number (string-trim height "in"))]) (between h 59 76))]
+ [else #false]))
+
+(define (valid-hcl? p)
+ (define color (hash-ref p "hcl"))
+ (regexp-match #px"^#[0-9a-f]{6}$" color))
+
+(define (valid-ecl? p)
+ (member (hash-ref p "ecl") (list "amb" "blu" "brn" "gry" "grn" "hzl" "oth")))
+
+(define (valid-pid? p)
+ (regexp-match #px"^\\d{9}$" (hash-ref p "pid")))
+
+(define (valid-stricter-passport? p)
+ (and (valid-byr? p)
+ (valid-iyr? p)
+ (valid-eyr? p)
+ (valid-hgt? p)
+ (valid-hcl? p)
+ (valid-ecl? p)
+ (valid-pid? p)))
+
+(count valid-stricter-passport? passport-fields)
diff --git a/racket/aoc2020/day-05/day-05.rkt b/racket/aoc2020/day-05/day-05.rkt
new file mode 100644
index 0000000..bd89ede
--- /dev/null
+++ b/racket/aoc2020/day-05/day-05.rkt
@@ -0,0 +1,35 @@
+#lang racket
+(require "../../jj-aoc.rkt"
+ threading)
+
+(define tickets
+ (for/list ([l (in-lines (open-day 5 2020))])
+ (~>> l (regexp-match #px"(.{7})(.{3})") rest)))
+
+(define (find-place str min-p max-p l r)
+ (if (string=? "" str)
+ min-p
+ (let ([p-range (/ (add1 (- max-p min-p)) 2)] [c (substring str 0 1)])
+ (cond
+ [(string=? c l) (find-place (substring str 1) min-p (- max-p p-range) l r)]
+ [(string=? c r) (find-place (substring str 1) (+ min-p p-range) max-p l r)]))))
+
+(define (find-row str)
+ (find-place str 0 127 "F" "B"))
+(define (find-col str)
+ (find-place str 0 7 "L" "R"))
+
+(define (ticket-id t)
+ (let ([row (find-row (first t))] [col (find-col (second t))]) (+ col (* 8 row))))
+
+;; part 1
+(define occupied-seats
+ (~>> (for/list ([t (in-list tickets)])
+ (ticket-id t))))
+
+(apply max occupied-seats)
+
+;; part 2
+(set-first (set-subtract
+ (list->set (inclusive-range (apply min occupied-seats) (apply max occupied-seats)))
+ (list->set occupied-seats)))
diff --git a/racket/aoc2020/day-06/day-06.rkt b/racket/aoc2020/day-06/day-06.rkt
new file mode 100644
index 0000000..b0e2af9
--- /dev/null
+++ b/racket/aoc2020/day-06/day-06.rkt
@@ -0,0 +1,22 @@
+#lang racket
+(require "../../jj-aoc.rkt"
+ threading)
+
+(define responses (~> (open-day 6 2020) (port->string) (string-split "\n\n")))
+
+;; part 1
+(define (response-count-total rs)
+ (for/sum ([r (in-list rs)]) (~> r (string-replace _ "\n" "") string->list list->set set-count)))
+
+(response-count-total responses)
+
+;; part 2
+(define (response-consensus-total rs)
+ (for/sum ([r (in-list rs)])
+ (~> r
+ (string-split _ "\n")
+ (map (λ~> string->list list->set) _)
+ (apply set-intersect _)
+ set-count)))
+
+(response-consensus-total responses) \ No newline at end of file
diff --git a/racket/aoc2020/day-07/day-07.rkt b/racket/aoc2020/day-07/day-07.rkt
new file mode 100644
index 0000000..f2a1ffe
--- /dev/null
+++ b/racket/aoc2020/day-07/day-07.rkt
@@ -0,0 +1,46 @@
+#lang racket
+(require advent-of-code
+ threading
+ rebellion/collection/entry
+ rebellion/collection/multidict)
+
+(define raw-rules (~> (open-aoc-input (find-session) 2020 7) (port->string) (string-split "\n")))
+
+(define (split-rule r)
+ (match-define (list head tail) (string-split (string-trim r #px" bags?.") " bags contain "))
+ (~>> tail
+ (regexp-split #px"( bags?,\\s)" _)
+ (map (λ~> (regexp-match* #px"(\\d+) (\\w+ \\w+)" _ #:match-select rest)))
+ append*
+ (map (λ~> (match _
+ [(list n c) (list (string->symbol c) (string->number n))]
+ ['() '()])))
+ (cons (string->symbol head) _)))
+
+(define rules-multidict
+ (for*/multidict ([ln (in-list raw-rules)] #:do [(match-define (list* from tos) (split-rule ln))]
+ [to (in-list tos)])
+ (entry from to)))
+
+;; part 1
+(define (bags-that-eventually-contain target)
+ (for/fold ([holders (set)]) ([rule (in-multidict-entries rules-multidict)])
+ (match rule
+ [(entry outside (list (== target) _))
+ (set-union (set-add holders outside) (bags-that-eventually-contain outside))]
+ [_ holders])))
+
+(define part-1 (set-count (bags-that-eventually-contain '|shiny gold|)))
+(~a "Part 1: " part-1)
+;; (aoc-submit (find-session) 2020 7 1 part-1)
+
+;; part 2
+(define (bags-that-are-contained-by target)
+ (for/sum ([holding (in-multidict-entries rules-multidict)])
+ (match holding
+ [(entry (== target) (list held n)) (+ n (* n (bags-that-are-contained-by held)))]
+ [_ 0])))
+
+(define part-2 (bags-that-are-contained-by '|shiny gold|))
+(~a "Part 2: " part-2)
+;; (aoc-submit (find-session) 2020 7 1 part-2)
diff --git a/racket/aoc2020/day-08/day-08.ipynb b/racket/aoc2020/day-08/day-08.ipynb
new file mode 100644
index 0000000..1cb060b
--- /dev/null
+++ b/racket/aoc2020/day-08/day-08.ipynb
@@ -0,0 +1,220 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Advent of Code 2020\n",
+ "#### Day 8: Handheld Halting\n",
+ "\n",
+ "A series of instructions consisting of jumps, accumulator increments and no-ops has an infinite loop, but changing one no-op to a jump or vice versa will allow it to run to completion.\n",
+ "\n",
+ "1. What's the value of the accumulator immediately before the instructions begin to loop?\n",
+ "2. After fixing the wrong instruction, what's the value of the accumulator at the end of execution?\n",
+ "\n",
+ "No surprises in the preamble, just the usual AOC utility functions and the threading macros."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "#lang iracket/lang #:require racket\n",
+ "(require advent-of-code\n",
+ " threading)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The instructions are in a text file that looks like\n",
+ "```\n",
+ "nop +0\n",
+ "acc +1\n",
+ "jmp +4\n",
+ "acc +3\n",
+ "jmp -3\n",
+ "acc -99\n",
+ "acc +1\n",
+ "jmp -4\n",
+ "acc +6\n",
+ "```\n",
+ "Since we need to keep track of which instructions to jump to, I've turned it into a hash table so each instruction is indexed starting at 0."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "<code>'((0 acc . 49) (1 jmp . 274) (2 acc . 49) (3 acc . 49) (4 jmp . 476) (5 jmp . 409) (6 jmp . 269) (7 jmp . 1) (8 acc . -11) (9 acc . 5))</code>"
+ ],
+ "text/plain": [
+ "'((0 acc . 49) (1 jmp . 274) (2 acc . 49) (3 acc . 49) (4 jmp . 476) (5 jmp . 409) (6 jmp . 269) (7 jmp . 1) (8 acc . -11) (9 acc . 5))"
+ ]
+ },
+ "execution_count": 2,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(define raw-instructions (~> (fetch-aoc-input (find-session) 2020 8) (string-split \"\\n\")))\n",
+ "\n",
+ "(define instruction-set\n",
+ " (for/hash ([instruction (in-list raw-instructions)] [i (in-naturals)])\n",
+ " (match-define (list op val) (string-split instruction))\n",
+ " (values i (cons (string->symbol op) (string->number val)))))\n",
+ "\n",
+ "(for/list ([i (in-range 10)])\n",
+ " (cons i (hash-ref instruction-set i)))\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### Part 1\n",
+ "\n",
+ "Now I write a little interpreter using structural pattern matching and recursion to execute the code.\n",
+ "* If the program tried to execute an instruction on the line immediately after the last instruction, the program terminates normally. (This won't happen in part 1, but it's important for part 2.)\n",
+ "* We track the line numbers that have been visited in each step. If a number comes up a second time, that means we're about to start looping, so we break execution here.\n",
+ "* `acc n` instructions increment the accumulator by `n` and go to the next line.\n",
+ "* `jmp n` instructions skip up or down `n` instructions.\n",
+ "* `nop n` instructions don't do anything besides go to the next line."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "<code>'(looping . 1949)</code>"
+ ],
+ "text/plain": [
+ "'(looping . 1949)"
+ ]
+ },
+ "execution_count": 3,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(define (execute code [acc 0] [line 0] [visited (set)])\n",
+ " (match (hash-ref code line 'terminated)\n",
+ " ['terminated (cons 'terminated acc)]\n",
+ " [_\n",
+ " #:when (set-member? visited line)\n",
+ " (cons 'looping acc)]\n",
+ " [(cons 'acc n) (execute code (+ acc n) (add1 line) (set-add visited line))]\n",
+ " [(cons 'jmp n) (execute code acc (+ n line) (set-add visited line))]\n",
+ " [(cons 'nop _) (execute code acc (add1 line) (set-add visited line))]))\n",
+ "\n",
+ "(execute instruction-set)\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### Part 2\n",
+ "\n",
+ "So far so good. Now we're told that flipping exactly one `jmp` to a `nop` or vice versa will fix the code, so let's write a utility function to perform that flip, identify the potential candidates for the fix and get an idea of what our workload will be for this problem."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "<code>305</code>"
+ ],
+ "text/plain": [
+ "305"
+ ]
+ },
+ "execution_count": 4,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(define (flip-op op)\n",
+ " (match op\n",
+ " [(cons 'jmp n) (cons 'nop n)]\n",
+ " [(cons 'nop n) (cons 'jmp n)]))\n",
+ "\n",
+ "(define instruction-count (hash-count instruction-set))\n",
+ "(define flippable-bits\n",
+ " (filter (λ (i) (member (car (hash-ref instruction-set i)) '(jmp nop))) (range instruction-count)))\n",
+ "\n",
+ "(length flippable-bits)\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "There's only 305 cases to check, so just starting from the top and trying each possible swap in sequence should work fine, rather than trying to come up with some fancier backtracking algorithm. `for/or` stops at the first non-falsy result, so we just have to wait for the first result that matches the `(cons 'terminated _)` pattern."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "<code>'(terminated . 2092)</code>"
+ ],
+ "text/plain": [
+ "'(terminated . 2092)"
+ ]
+ },
+ "execution_count": 5,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(for/or ([i (in-list flippable-bits)])\n",
+ " (define flipped-instruction-set (hash-update instruction-set i flip-op))\n",
+ " (match (execute flipped-instruction-set)\n",
+ " [(cons 'looping _) #f]\n",
+ " [(and success (cons 'terminated _)) success]))\n"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Racket (trusted)",
+ "language": "racket",
+ "name": "racket-trusted"
+ },
+ "language_info": {
+ "codemirror_mode": "scheme",
+ "file_extension": ".rkt",
+ "mimetype": "text/x-racket",
+ "name": "racket",
+ "pygments_lexer": "racket",
+ "version": "8.7"
+ },
+ "orig_nbformat": 4
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/racket/aoc2020/day-09/day-09.ipynb b/racket/aoc2020/day-09/day-09.ipynb
new file mode 100644
index 0000000..e6f712b
--- /dev/null
+++ b/racket/aoc2020/day-09/day-09.ipynb
@@ -0,0 +1,166 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Advent of Code 2020\n",
+ "#### Day 9: Encoding Error\n",
+ "\n",
+ "In a list of integers, each number after the 25th should be the sum of two of the previous 25 numbers.\n",
+ "\n",
+ "1. What's the first number in the list that does not have this property?\n",
+ "2. The \"encryption weakness\" is the sum of the extrema in a contiguous range of numbers that sums up to the invalid number in part 1. Find the encryption weakness.\n",
+ "\n",
+ "I'm using structural pattern matching for this solution, so no extra packages are required beyond the usual."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {
+ "vscode": {
+ "languageId": "racket"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "#lang iracket/lang #:require racket\n",
+ "\n",
+ "(require advent-of-code\n",
+ " threading)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The data is just a list of integers, so it's straightforward to process."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {
+ "vscode": {
+ "languageId": "racket"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "(define preamble\n",
+ " (~> (fetch-aoc-input (find-session) 2020 9) (string-split \"\\n\") (map string->number _)))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "##### Part 1\n",
+ "\n",
+ "First, we bind the first 25 values to `head` and the 26th to `x`.\n",
+ "\n",
+ "In the `match` syntax, `list-no-order` binds `a` and `b` to the first pair of numbers from anywhere in the first 25 values that satisfies the `#:when` guard. The exact pair doesn't matter; all we need to know is if it's valid and we can move on to the next test.\n",
+ "\n",
+ "If nothing satisfies the first clause, we've found our invalid number. We're guaranteed to have an invalid number in the set, so we don't need to guard against `match-define-values` failing when there's fewer than 26 values to work with."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {
+ "vscode": {
+ "languageId": "racket"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "<code>1038347917</code>"
+ ],
+ "text/plain": [
+ "1038347917"
+ ]
+ },
+ "execution_count": 10,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(define (find-invalid-number xs)\n",
+ " (match-define-values (head (list x _ ...)) (split-at xs 25))\n",
+ " (match head\n",
+ " [(list-no-order a b _ ...)\n",
+ " #:when (= x (+ a b))\n",
+ " (find-invalid-number (rest xs))]\n",
+ " [_ x]))\n",
+ "\n",
+ "(define target-sum (find-invalid-number preamble))\n",
+ "target-sum"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "##### Part 2\n",
+ "\n",
+ "We can find the range with another match statement, this time looking for a sub-list that's at least two elements long and that satisfies the guard. Everything after this is just arithmetic."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "metadata": {
+ "vscode": {
+ "languageId": "racket"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "<code>137394018</code>"
+ ],
+ "text/plain": [
+ "137394018"
+ ]
+ },
+ "execution_count": 11,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(define (find-contiguous-range xs)\n",
+ " (match xs\n",
+ " [(list _ ... x ..2 _ ...)\n",
+ " #:when (= (apply + x) target-sum)\n",
+ " x]))\n",
+ "\n",
+ "(define target-range (find-contiguous-range preamble))\n",
+ "(+ (apply max target-range) (apply min target-range))"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Racket (trusted)",
+ "language": "racket",
+ "name": "racket-trusted"
+ },
+ "language_info": {
+ "codemirror_mode": "scheme",
+ "file_extension": ".rkt",
+ "mimetype": "text/x-racket",
+ "name": "Racket",
+ "pygments_lexer": "racket",
+ "version": "8.7"
+ },
+ "orig_nbformat": 4
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/racket/aoc2020/day-10/day-10.rkt b/racket/aoc2020/day-10/day-10.rkt
new file mode 100644
index 0000000..77d9bb7
--- /dev/null
+++ b/racket/aoc2020/day-10/day-10.rkt
@@ -0,0 +1,37 @@
+#lang racket
+
+(require advent-of-code
+ threading
+ algorithms
+ memoize)
+
+;; part 1
+(define adapters
+ (~> (fetch-aoc-input (find-session) 2020 10)
+ (string-split "\n")
+ (map string->number _)
+ (sort <)
+ ((λ (xs) (flatten (list 0 xs (+ 3 (last xs))))))))
+
+(~>> adapters
+ (sliding _ 2)
+ (map (match-lambda
+ [(list b a) (- a b)]))
+ (group-by identity)
+ (map length)
+ (apply *))
+
+;; part 2
+(define subpaths
+ (for*/hash ([adapter (in-list adapters)])
+ (define predecessor-candidates (inclusive-range (+ 1 adapter) (+ 3 adapter)))
+ (values adapter (filter (λ (p) (member p adapters)) predecessor-candidates))))
+
+(define/memo (find-paths from to)
+ (define paths (hash-ref subpaths from 'failed))
+ (match paths
+ ['failed 0]
+ [(list-no-order (== to) _ ...) 1]
+ [ts (for/sum ([t (in-list ts)]) (find-paths t to))]))
+
+(find-paths (first adapters) (last adapters))
diff --git a/racket/aoc2020/day-11/day-11.rkt b/racket/aoc2020/day-11/day-11.rkt
new file mode 100644
index 0000000..e2fe052
--- /dev/null
+++ b/racket/aoc2020/day-11/day-11.rkt
@@ -0,0 +1,60 @@
+#lang racket
+
+(require advent-of-code)
+
+(define raw-grid (fetch-aoc-input (find-session) 2020 11))
+
+(define/match (parse _)
+ [(#\L) 'empty]
+ [(#\#) 'occupied]
+ [(#\.) 'floor])
+
+(define seat-grid
+ (for*/hash ([(row r) (in-indexed (in-list (string-split raw-grid)))]
+ [(col c) (in-indexed (in-string row))])
+ (values (cons r c) (parse col))))
+
+(define (next-seat-state seat state h rule [max-occupy 4])
+ (define neighbor-states (rule seat h))
+ (match* (state (count (curry eq? 'occupied) neighbor-states))
+ [('empty 0) 'occupied]
+ [('occupied n)
+ #:when (>= n max-occupy)
+ 'empty]
+ [(_ _) state]))
+
+(define (stabilize h [i 1] #:rule rule #:max-occupy [max-occupy 4])
+ (define h*
+ (for/hash ([(seat state) (in-hash h)])
+ (cond
+ [(eq? state 'floor) (values seat state)]
+ [else (values seat (next-seat-state seat state h rule max-occupy))])))
+ (if (equal? h h*)
+ (count (curry equal? 'occupied) (hash-values h))
+ (stabilize h* (add1 i) #:rule rule #:max-occupy max-occupy)))
+
+;; part 1
+(define (find-nearest-neighbors p h)
+ (match-define (cons r c) p)
+ (for*/list ([r* (in-inclusive-range (sub1 r) (add1 r))]
+ [c* (in-inclusive-range (sub1 c) (add1 c))]
+ [p* (in-value (cons r* c*))]
+ #:unless (equal? p p*))
+ (hash-ref h p* 'out-of-bounds)))
+
+(stabilize seat-grid #:rule find-nearest-neighbors)
+
+;; part 2
+(define (find-visible-neighbors p h)
+ (match-define (cons r c) p)
+ (define directions
+ (for*/list ([dr '(-1 0 1)] [dc '(-1 0 1)] #:unless (= 0 dr dc))
+ (cons dr dc)))
+ (for/list ([dir (in-list directions)] #:do [(match-define (cons dr dc) dir)])
+ (for*/first ([i (in-naturals 1)]
+ #:do [(define p* (cons (+ r (* i dr)) (+ c (* i dc))))
+ (define state (hash-ref h p* 'out-of-bounds))]
+ #:unless (equal? state 'floor))
+ state)))
+
+(stabilize seat-grid #:rule find-visible-neighbors #:max-occupy 5)
diff --git a/racket/aoc2020/day-12/day-12.rkt b/racket/aoc2020/day-12/day-12.rkt
new file mode 100644
index 0000000..e4bbd32
--- /dev/null
+++ b/racket/aoc2020/day-12/day-12.rkt
@@ -0,0 +1,56 @@
+#lang rackjure
+
+(require advent-of-code
+ threading
+ (only-in relation ->symbol ->number))
+
+(struct instruction (direction distance) #:transparent)
+
+(define (parse-instruction str)
+ (match str
+ [(regexp #px"(\\w)(\\d+)" (list _ dir dist)) (instruction (->symbol dir) (->number dist))]))
+
+(define instructions
+ (~>> (fetch-aoc-input (find-session) 2020 12) string-split (map parse-instruction)))
+
+;; part 1
+(struct boat (x y nav) #:transparent)
+
+(define (angle->direction n)
+ (case n
+ [(0) 'E]
+ [(90) 'N]
+ [(180) 'W]
+ [(270) 'S]))
+
+(define (move-via-direct-command inst b)
+ (match-define (boat x y facing) b)
+ (match inst
+ [(instruction 'N n) (boat x (+ y n) facing)]
+ [(instruction 'S n) (boat x (- y n) facing)]
+ [(instruction 'E n) (boat (+ x n) y facing)]
+ [(instruction 'W n) (boat (- x n) y facing)]
+ [(instruction 'L n) (boat x y (modulo (+ facing n) 360))]
+ [(instruction 'R n) (boat x y (modulo (- facing n) 360))]
+ [(instruction 'F n) (move-via-direct-command (instruction (angle->direction facing) n) b)]))
+
+(define (find-boat-destination using start instructions)
+ (match-define (boat x y _) (foldl using start instructions))
+ (+ (abs x) (abs y)))
+
+(find-boat-destination move-via-direct-command (boat 0 0 0) instructions)
+
+;; part 2
+(define (move-via-waypoint inst b)
+ (match-define (boat x y (cons wp-x wp-y)) b)
+ (match inst
+ [(instruction 'N n) (boat x y (cons wp-x (+ wp-y n)))]
+ [(instruction 'S n) (boat x y (cons wp-x (- wp-y n)))]
+ [(instruction 'E n) (boat x y (cons (+ wp-x n) wp-y))]
+ [(instruction 'W n) (boat x y (cons (- wp-x n) wp-y))]
+ [(instruction _ 180) (boat x y (cons (- wp-x) (- wp-y)))]
+ [(or (instruction 'L 90) (instruction 'R 270)) (boat x y (cons (- wp-y) wp-x))]
+ [(or (instruction 'R 90) (instruction 'L 270)) (boat x y (cons wp-y (- wp-x)))]
+ [(instruction 'F n) (boat (+ x (* n wp-x)) (+ y (* n wp-y)) (cons wp-x wp-y))]))
+
+(find-boat-destination move-via-waypoint (boat 0 0 '(10 . 1)) instructions)
diff --git a/racket/aoc2020/day-13/day-13.rkt b/racket/aoc2020/day-13/day-13.rkt
new file mode 100644
index 0000000..b53f045
--- /dev/null
+++ b/racket/aoc2020/day-13/day-13.rkt
@@ -0,0 +1,32 @@
+#lang racket
+
+(require advent-of-code
+ (only-in relation ->number)
+ threading)
+
+(define (process-ids str)
+ (~> str (string-split ",") (filter-map (λ (s) (string->number s 10 'number-or-false)) _)))
+
+(match-define (regexp #px"(\\d+)\n(.+)" (list _ (app ->number timestamp) raw-bus-ids))
+ (fetch-aoc-input (find-session) 2020 13))
+
+(define bus-ids (process-ids raw-bus-ids))
+
+;; part 1
+(for/first ([minute (in-naturals timestamp)]
+ #:do [(define departing-bus
+ (for/first ([b bus-ids] #:when (= 0 (remainder minute b)))
+ b))]
+ #:when departing-bus)
+ (* departing-bus (- minute timestamp)))
+
+;; part 2
+(for/fold ([step 1] [current-timestamp 1] #:result current-timestamp)
+ ([b* (in-list (string-split (string-trim raw-bus-ids) ","))]
+ [offset (in-naturals)]
+ #:unless (equal? b* "x")
+ #:do [(define bus (->number b*))])
+ (values
+ (* step bus)
+ (for/first ([n (in-range current-timestamp +inf.0 step)] #:when (= 0 (remainder (+ n offset) bus)))
+ n)))
diff --git a/racket/aoc2020/day-14/day-14.rkt b/racket/aoc2020/day-14/day-14.rkt
new file mode 100644
index 0000000..9ac339c
--- /dev/null
+++ b/racket/aoc2020/day-14/day-14.rkt
@@ -0,0 +1,11 @@
+#lang racket
+
+(require advent-of-code
+ threading
+ fancy-app
+ relation
+ rebellion/binary/bitstring)
+
+(define instructions (string-split (fetch-aoc-input (find-session) 2020 14) "\n"))
+
+(~> (number->string 11 2) ->list (map (->number _) _))
diff --git a/racket/aoc2020/day-15/day-15.rkt b/racket/aoc2020/day-15/day-15.rkt
new file mode 100644
index 0000000..4dd9e88
--- /dev/null
+++ b/racket/aoc2020/day-15/day-15.rkt
@@ -0,0 +1,22 @@
+#lang rackjure
+
+(define first-numbers '(2 20 0 4 1 17))
+
+(define number-hash
+ (for/hash ([(xs i) (in-indexed (drop-right first-numbers 1))])
+ (values xs (add1 i))))
+
+(define starting-round (~> number-hash hash-values (apply max _) (+ 2)))
+
+(define (find-spoken-number-at end)
+ (for/fold ([ns number-hash] [previous-number (last first-numbers)] #:result previous-number)
+ ([rnd (inclusive-range starting-round end)])
+ (define next-spoken-number
+ (match (ns previous-number)
+ [#f 0]
+ [n (- (sub1 rnd) n)]))
+ (values (ns previous-number (sub1 rnd)) next-spoken-number)))
+
+(find-spoken-number-at 2020)
+
+(find-spoken-number-at 30000000) \ No newline at end of file
diff --git a/racket/aoc2020/day-16/day-16.rkt b/racket/aoc2020/day-16/day-16.rkt
new file mode 100644
index 0000000..9a38eda
--- /dev/null
+++ b/racket/aoc2020/day-16/day-16.rkt
@@ -0,0 +1,52 @@
+#lang racket
+
+(require racket/struct
+ advent-of-code
+ fancy-app
+ relation
+ threading
+ rebellion/base/range)
+
+(struct field-rule (name range1 range2) #:transparent)
+
+(define (make-lines strs)
+ (string-split strs "\n"))
+(define (seperate-fields strs)
+ (~>> (string-split strs ",") (map ->number)))
+
+(define (process-rules str)
+ (match str
+ [(regexp
+ #px"(.+): (\\d+)-(\\d+) or (\\d+)-(\\d+)"
+ (list _ name (app ->number min1) (app ->number max1) (app ->number min2) (app ->number max2)))
+ (field-rule name (closed-range min1 max1) (closed-range min2 max2))]))
+
+(match-define (list (app (λ~>> make-lines (map process-rules)) ticket-rules)
+ (app (λ~>> make-lines second seperate-fields) your-ticket)
+ (app (λ~>> make-lines rest (map seperate-fields)) other-tickets))
+ (~> (fetch-aoc-input (find-session) 2020 16 #:cache #true) (string-split "\n\n")))
+
+;; part 1
+(define (fails-all-checks? field rules)
+ (define rule-list (~>> rules (map (λ~> struct->list rest)) flatten))
+ (for/and ([rule (in-list rule-list)])
+ (not (range-contains? rule field))))
+
+(define (ticket-scanning-error-rate tickets rules)
+ (for*/sum
+ ([ticket (in-list tickets)] (field (in-list ticket)) #:when (fails-all-checks? field rules))
+ field))
+
+(ticket-scanning-error-rate other-tickets ticket-rules)
+
+;; part 2
+(define valid-tickets (filter (ormap (fails-all-checks? _ ticket-rules) _) other-tickets))
+
+(define fields (apply map list valid-tickets))
+
+(for/list ([field (in-list fields)])
+ (for*/list (
+ [rule (in-list ticket-rules)]
+ #:unless (not (or (range-contains? (field-rule-range1 rule) value)
+ (range-contains? (field-rule-range2 rule) value))))
+ (field-rule-name rule)))