Advent of Code '23 - Day 5
This was a tough one… save for the datamanament that took a lot of time to figure out, I kind of got to the first answer pretty quickly… Then I refactored to suit part 2, which worked for the example, but would probably have taken days to brute force his way through the data. In the end I solved it by going the other way around; find the first location that has a seed assigned to it. This still took pretty long, but it was less than 30 minutes so good enough.
Input
Example
seeds: 79 14 55 13 seed-to-soil map: 50 98 2 52 50 48 soil-to-fertilizer map: 0 15 37 37 52 2 39 0 15 fertilizer-to-water map: 49 53 8 0 11 42 42 0 7 57 7 4 water-to-light map: 88 18 7 18 25 70 light-to-temperature map: 45 77 23 81 45 19 68 64 13 temperature-to-humidity map: 0 69 1 1 0 69 humidity-to-location map: 60 56 37 56 93 4
Part 1
(defun aoc23/parse-seeds (string) (let* ((seeds (cadr (string-split string ":" nil " "))) (seeds (string-split seeds " "))) (mapcar 'string-to-number seeds))) (defun aoc23/parse-ranges (string) (let* ((input (mapcar 'string-to-number (string-split string " "))) (src-b (car input)) (src-a (cadr input)) (length (caddr input))) `(((,src-a . ,(+ src-a (1- length))) (,src-b . ,(+ src-b (1- length))))) )) (defun aoc23/parse-type (string) (car (string-split string " "))) (defun aoc23/build-almanak (string) (let ((lines (string-split string "\n")) (state 'init) (almanac '()) (type nil) (data nil)) (dolist (line lines) (unless (string= line "") (cond ((string-search "seeds:" line) (setq seeds (aoc23/parse-seeds line))) ((string-search "map:" line) (when type (setq almanac (append almanac `((,type . ,data))))) (setq data '()) (setq type (aoc23/parse-type line))) (t (message "adding to %s" type) (setq data (append data (aoc23/parse-ranges line))))))) (when (and data type) (setq almanac (append almanac `((,type . ,data))))) `((:seeds . ,seeds) ,almanac))) (defun aoc23/parse-step (step src alamanak) (let ((ranges (cdr (assoc step (cadr almanak)))) (result nil)) (dolist (r ranges) (let* ((s-range (car r)) (d-range (cadr r)) (diff (- (car d-range) (car s-range)))) (when (and r (>= src (car s-range)) (<= src (cdr s-range))) (message "%S between %d and %d (diff %d)" src (car s-range) (cdr s-range) diff) (setq result (+ diff src)) ))) (message "found %s: %d > %d" step src (or result src)) (or result src))) (let* ((almanak (aoc23/build-almanak input)) (seeds (alist-get :seeds almanak)) (steps '("seed-to-soil" "soil-to-fertilizer" "fertilizer-to-water" "water-to-light" "light-to-temperature" "temperature-to-humidity" "humidity-to-location")) (ranges (cadr almanak)) (src nil) (locations '()) (lowest nil)) (dolist (seed seeds) (setq src seed) (message "parsing seed: %d" seed) (dolist (step steps) (setq src (aoc23/parse-step step src almanak))) (setq locations (append locations `((,seed . ,src)))) (when (or (eq nil lowest) (< src lowest)) (setq lowest src))) lowest)
Part 2
This part isn't the best solution, But I kinda don't think its worth it at this moment to try any harder to fix it… it gave the right answer, so it's good enough.
(defun aoc23/parse-seeds (string) (let* ((seeds (cadr (string-split string ":" nil " "))) (seeds (string-split seeds " ")) (seeds (mapcar 'string-to-number seeds)) (result '())) (while (car seeds) (let ((seed (car seeds)) (length (cadr seeds))) (setq result (append result `((,seed . ,length))))) (setq seeds (cddr seeds))) result)) (defun aoc23/parse-ranges (string) (let* ((input (mapcar 'string-to-number (string-split string " "))) (src-a (car input)) (src-b (cadr input)) (length (caddr input))) `(((,src-a . ,(+ src-a (1- length))) (,src-b . ,(+ src-b (1- length))))) )) (defun aoc23/parse-type (string) (car (string-split string " "))) (defun aoc23/sort (a b) (> (caar b) (caar a))) (defun aoc23/build-almanak (string) (let ((lines (string-split string "\n")) (state 'init) (almanac '()) (type nil) (data nil)) (dolist (line lines) (unless (string= line "") (cond ((string-search "seeds:" line) (setq seeds (aoc23/parse-seeds line))) ((string-search "map:" line) (when type (sort data 'aoc23/sort) (setq almanac (append almanac `((,type . ,data))))) (setq data '()) (setq type (aoc23/parse-type line))) (t (message "adding to %s" type) (setq data (append data (aoc23/parse-ranges line))))))) (when (and data type) (sort data 'aoc23/sort) (message "%S" data) (setq almanac (append almanac `((,type . ,data))))) `((:seeds . ,seeds) ,almanac))) (defun aoc23/parse-step (src almanak) (let ((ranges (cdr almanak)) (result nil)) (catch 'result (dolist (r ranges) (let* ((s-range (car r)) (d-range (cadr r)) (diff (- (car d-range) (car s-range)))) (when (and (>= src (car s-range)) (<= src (cdr s-range))) (setq result (+ diff src)) (throw 'result "found") )))) (or result src))) (defun aoc23/is-seedp (seed seeds) (let ((found nil)) (catch 'found (dolist (line seeds) (when (and (>= seed (car line)) (<= seed (+ (car line) (1- (cdr line))))) (setq found t) (throw 'found "found")))) found)) (let* ((almanak (aoc23/build-almanak input)) (seeds (alist-get :seeds almanak)) (steps `(,(assoc "humidity-to-location" (cadr almanak)) ,(assoc "temperature-to-humidity" (cadr almanak)) ,(assoc "light-to-temperature" (cadr almanak)) ,(assoc "water-to-light" (cadr almanak)) ,(assoc "fertilizer-to-water" (cadr almanak)) ,(assoc "soil-to-fertilizer" (cadr almanak)) ,(assoc "seed-to-soil" (cadr almanak)) )) (ranges (cadr almanak)) (seed 0) (locations '()) (lowest nil)) (catch 'found ; (dolist (seedr seeds) ; (dotimes (i (cdr seedr)) (while t (let* ((src seed)) (dolist (step steps) (set 'src (aoc23/parse-step src step))) ; (setq locations (append locations `((,seed . ,src)))) ; (message "processed seed %d: %d" seed src) (when (and src (aoc23/is-seedp src seeds)) (setq lowest seed) (message "found something at %d: %S" seed src) (throw 'found "found")) (setq seed (1+ seed))))) lowest)