thorondir's recent activity
-
Comment on Day 2: Cube Conundrum in ~comp.advent_of_code
-
Comment on Day 1: Trebuchet?! in ~comp.advent_of_code
thorondir I'm trying out Guile Scheme, this year. Didn't get to it yesterday, sadly, but such is life. I wrote a "utils.scm" library, to do things I'll have to do over and over again: utils.scm For now,...I'm trying out Guile Scheme, this year.
Didn't get to it yesterday, sadly, but such is life.I wrote a "utils.scm" library, to do things I'll have to do over and over again:
utils.scm
For now, it's just a function that reads in the input-file line by line, hands back a list of lines.
(use-modules (ice-9 rdelim)) (define (slurp filename) (call-with-input-file filename (lambda (p) (let loop ([line (read-line p)] [result '()]) (if (eof-object? line) result (loop (read-line p) (cons line result)))))))
Part 1
Nothing particularly fancy, just regex-ing it.
(use-modules (ice-9 regex) (ice-9 format)) (load "utils.scm") (define beginning-digit-re "^[^1-9]*([1-9])") (define end-digit-re "([1-9])[^1-9]*$") (define (process-line line) (let ([begin-match (string-match beginning-digit-re line)] [end-match (string-match end-digit-re line)]) (string-join (list (match:substring begin-match 1) (match:substring end-match 1)) ""))) (define (proc-task-01a filename) (define puzzle-input (slurp filename)) (apply + (map string->number (map process-line puzzle-input)))) (if (= (proc-task-01a "tiny_input_01a") 142) (format #t "Task 01, part 1: ~a\n" (proc-task-01a "input_01")) (error "tiny-task-01a didn't pass sanity checks"))
Second part was, as everyone already mentioned, a bit trickier.
Still regex-ing it, though.Part 2
(define (front-splicer str matcher) (let [(m (string-match matcher str))] (if (regexp-match? m) m (front-splicer (string-drop str 1) matcher)))) (define (back-splicer str matcher) (let [(m (string-match matcher str))] (if (regexp-match? m) m (back-splicer (string-drop-right str 1) matcher)))) (define (digit-getter m) (cond [(or (string=? "1" m) (string=? "one" m)) "1"] [(or (string=? "2" m) (string=? "two" m)) "2"] [(or (string=? "3" m) (string=? "three" m)) "3"] [(or (string=? "4" m) (string=? "four" m)) "4"] [(or (string=? "5" m) (string=? "five" m)) "5"] [(or (string=? "6" m) (string=? "six" m)) "6"] [(or (string=? "7" m) (string=? "seven" m)) "7"] [(or (string=? "8" m) (string=? "eight" m)) "8"] [(or (string=? "9" m) (string=? "nine" m)) "9"] [else (error (format #f "we matched something else? ~a" m))]) ) (define (grab-first line) (define matcher "^(1|2|3|4|5|6|7|8|9|one|two|three|four|five|six|seven|eight|nine)") (let [(m (match:substring (front-splicer line matcher) 1))] (digit-getter m))) (define (grab-last line) (define matcher "(1|2|3|4|5|6|7|8|9|one|two|three|four|five|six|seven|eight|nine)$") (let [(m (match:substring (back-splicer line matcher) 1))] (digit-getter m))) (define (proc-line line) (let [(front (grab-first line)) (back (grab-last line))] (string->number (format #f "~a~a" front back)))) (define (proc-task-01b filename) (define puzzle-input (slurp filename)) (apply + (map proc-line puzzle-input))) (if (= (proc-task-01b "tiny_input_01b") 281) (format #t "Task 01, part 2: ~a\n" (proc-task-01b "input_01")) (error "tiny-task-01b didn't pass sanity checks"))
The solutions don't feel particularly... scheme-y, but then again, I don't know what that would feel like, exactly. xD
-
Comment on What is your most essential pessimistic belief? Conversely, what is your most essential optimistic belief? in ~talk
thorondir The series is simply that good, yes. xD Had I known of it, I'd also have gone for one of the box sets. :| But such is life.The series is simply that good, yes. xD
Had I known of it, I'd also have gone for one of the box sets. :|
But such is life. -
Comment on What is your most essential pessimistic belief? Conversely, what is your most essential optimistic belief? in ~talk
thorondir I'm not surprised at, but definitely delighted by, this particular tidbit: I'll check it out, thanks!I'm not surprised at, but definitely delighted by, this particular tidbit:
$1,405,859 pledged of $25,000 goal
I'll check it out, thanks!
-
Comment on What is your most essential pessimistic belief? Conversely, what is your most essential optimistic belief? in ~talk
thorondir Man, The Prisoner's Dilemma does pop up everywhere, doesn't it. xDMan, The Prisoner's Dilemma does pop up everywhere, doesn't it. xD
-
Comment on What is your most essential pessimistic belief? Conversely, what is your most essential optimistic belief? in ~talk
thorondir I most warmly recommend The Expanse book-series by James S.A. Corey. It's hard sci-fi, but that's not the most interesting thing to me. Rather, what keeps me so hooked is the realistic portrayal...I most warmly recommend The Expanse book-series by James S.A. Corey.
It's hard sci-fi, but that's not the most interesting thing to me. Rather, what keeps me so hooked is the realistic portrayal of people at the point where we have started colonizing the solar system.
It shares both your optimism and your pessimism, then adds shenanigans to it. :D
-
Comment on What is your most essential pessimistic belief? Conversely, what is your most essential optimistic belief? in ~talk
thorondir Optimistically, I would love for there to be more things where you first had to prove a basic understanding to be allowed to use/have it. Children, the internet, guns, etc. Pessimistically...Optimistically, I would love for there to be more things where you first had to prove a basic understanding to be allowed to use/have it. Children, the internet, guns, etc.
Pessimistically speaking, though, gating voting behind one feels like another instance of "agree with the state or we won't let you pass the test".
-
Comment on Day 7: No Space Left On Device in ~comp
thorondir Today felt... hacky, again. Lots of mutation of state going on, to set everything up for the calculations at the end. After solving, I realized how to better reuse the solver from part 1 for part...Today felt... hacky, again. Lots of mutation of state going on, to set everything up for the calculations at the end.
After solving, I realized how to better reuse the solver from part 1 for part 2, so I rewrote the bits I had.
Input-"sanitizer"
I figured I'd just build a tree of inodes while parsing.
(struct inode (type name size parent children) #:mutable #:transparent) (define (add-child! current-dir new-inode) (mappend! (inode-children current-dir) (mlist new-inode))) (define (find-dir tree dirname) (for/first ([child (inode-children tree)] #:when (and (inode? child) (eq? (inode-type child) 'dir) (string=? (inode-name child) dirname))) child)) (define (sanitize-input input) (define lines (string-split input "\n")) (define tree (inode 'dir "/" 0 #f (mlist empty))) ; the (mlist empty) makes it so I need guards in all the for loops ; but if I take it away, it breaks, so... ¯\\\_(ツ)\_/¯ (define current-dir tree) (for ([line lines]) (define parts (string-split line)) (cond [(string=? (first parts) "$") (cond [(string=? (second parts) "cd") (match (third parts) ["/" (set! current-dir tree)] [".." (set! current-dir (inode-parent current-dir))] [else (define target-dir (find-dir current-dir (third parts))) (when (not (inode? target-dir)) (error 'cd (~a "could not find" (third parts)))) (set! current-dir target-dir)])] [(string=? (second parts) "ls") 'ignore] [else 'unknown-command])] [(string=? (first parts) "dir") (define new-dir (inode 'dir (second parts) 0 current-dir (mlist empty))) (add-child! current-dir new-dir)] [else (define size (string->number (first parts))) (define filename (second parts)) (define new-file (inode 'file filename size current-dir empty)) (add-child! current-dir new-file)])) tree )
With that done, I calculated all the sizes, recursively:
size-calculations
(define (size-calculator! tree) (define size (for/fold ([total-size 0]) ([child (inode-children tree)] #:when (inode? child)) (match (inode-type child) ['file (+ total-size (inode-size child))] ['dir (define sub-size (size-calculator! child)) (set-inode-size! child sub-size) (+ total-size sub-size)]))) (set-inode-size! tree size) size)
The "solver" can then go through the tree and find all the directories that match some criterion:
Solver
(define (solver tree compare-func target-size) (define subdirs (for/fold ([d empty]) ([child (inode-children tree)] #:when (and (inode? child) (eq? (inode-type child) 'dir))) (append d (solver child compare-func target-size)))) (define dir (list (inode-name tree) (inode-size tree))) (if (compare-func (inode-size tree) target-size) (append (list dir) subdirs) subdirs))
At this point, parts one and two are just a few lines of code:
Both Parts
(define (part-1 input) (define total-size (size-calculator! input)) (define out (solver input <= 100000)) (apply + (map second out))) (define (part-2 input) (define total-size (size-calculator! input)) (define target-size (- REQUIRED (- MAX total-size))) (define candidates (sort (map second (solver input >= target-size)) <)) (first candidates))
-
Comment on Day 6: Tuning Trouble in ~comp
thorondir Oh, right. O(n) in a place I didn't expect. Thanks for the pointers! :)Oh, right. O(n) in a place I didn't expect.
Thanks for the pointers! :)
-
Comment on Day 6: Tuning Trouble in ~comp
thorondir Reading your solutions [and the docs, to figure out what you did xD] is always super helpful. :D I went about it a bit differently, by manually keeping track of the index. Which also meant part...Reading your solutions [and the docs, to figure out what you did xD] is always super helpful. :D
I went about it a bit differently, by manually keeping track of the index. Which also meant part two was a change of adding a single character. xD
Both Parts
(define (unique? lst) (= (length lst) (set-count (list->set lst)))) (define (process-slice slice idx slice-length) (cond [(< (length slice) slice-length) (displayln "slice too small") (values empty -1)] [(>= (length slice) slice-length) (define new-slice (take slice slice-length)) (cond [(unique? new-slice) (values empty (+ idx slice-length))] [else (process-slice (rest slice) (add1 idx) slice-length)])] [else (values 'never-supposed-to-get-here -1)])) (define (part-1 input) (define-values (m i) (process-slice input 0 4)) i) (define (part-2 input) (define-values (m i) (process-slice input 0 14)) i)
-
Comment on Day 4: Camp Cleanup in ~comp
-
Comment on Day 5: Supply Stacks in ~comp
thorondir Still doesn't feel idiomatic, because I use a bunch of mutation, but it's getting easier to write down what I want to do, so that's a win. Now I just have to not do the unidiomatic thing. xD...Still doesn't feel idiomatic, because I use a bunch of mutation, but it's getting easier to write down what I want to do, so that's a win. Now I just have to not do the unidiomatic thing. xD
Racket:
Part 1
(struct move (amount src dst) #:transparent) (define (parse-stacks stack-lines) (define stacks# (length (string-split (last stack-lines)))) (define stacks (make-vector stacks# '())) (for ([stack-line (rest (reverse stack-lines))]) (define matches (regexp-match* #rx"(...) ?" stack-line)) (for ([m matches] [i (in-range stacks#)]) (when (not (char=? (string-ref m 1) #\ )) ; only add something if the slot is not empty (vector-set! stacks i (cons (string-ref m 1) (vector-ref stacks i)))))) stacks) (define (parse-moves move-lines) (for/list ([line move-lines]) (define matches (regexp-match #px"move (\\d+) from (\\d+) to (\\d+)" line)) (move (string->number (second matches)) ; amount (sub1 (string->number (third matches))) ; src (sub1 (string->number (fourth matches)))))) ; dst ;; function to make input usable (define (sanitize-input input) (define split (string-split input "\n\n")) (define stack-def-raw (string-split (first split) "\n")) (define proc-def-raw (string-split (second split) "\n")) (define stacks (parse-stacks stack-def-raw)) (define moves (parse-moves proc-def-raw)) (values stacks moves)) (define (proc-moves! stacks moves) (for ([m moves]) (for ([i (in-range (move-amount m))]) (move-container! stacks (move-src m) (move-dst m))))) (define (move-container! stack src dst) (define container (first (vector-ref stack src))) (vector-set! stack src (rest (vector-ref stack src))) (vector-set! stack dst (cons container (vector-ref stack dst)))) ;; take the small input from the text (define raw-small-input (file->string "small-input_05")) (define small-answer-1 "CMZ") ;; workhorse, part 1 (define (part-1 raw-input) (define-values (stacks moves) (sanitize-input raw-input)) (proc-moves! stacks moves) (list->string (vector->list (vector-map first stacks)))) ;; test that "workhorse, part 1" does as it should (check-equal? (part-1 raw-small-input) small-answer-1) (displayln (~a "part 1: " (part-1 raw-aoc-input)))
And Part 2:
Part 2
(define (move-multiple-containers! stacks move) (define containers (take (vector-ref stacks (move-src move)) (move-amount move))) (vector-set! stacks (move-src move) (drop (vector-ref stacks (move-src move)) (move-amount move))) (vector-set! stacks (move-dst move) (append containers (vector-ref stacks (move-dst move))))) (define (proc-moves-2! stacks moves) (for ([m moves]) (move-multiple-containers! stacks m))) ;; part 2 (define small-answer-2 "MCD") (define (part-2 raw-input) (define-values (stacks moves) (sanitize-input raw-input)) (proc-moves-2! stacks moves) (list->string (vector->list (vector-map first stacks)))) (check-equal? (part-2 raw-small-input) small-answer-2) (displayln (~a "part 2: " (part-2 raw-aoc-input)))
-
Comment on Day 4: Camp Cleanup in ~comp
thorondir This, I feel, went much better than day 03. Part 1 ;; function to make input usable (define (sanitize-input input) (for/list ([l (string-split input "\n")]) (parse-values l))) ;; take the small...This, I feel, went much better than day 03.
Part 1
;; function to make input usable (define (sanitize-input input) (for/list ([l (string-split input "\n")]) (parse-values l))) ;; take the small input from the text (define raw-small-input (file->string "small-input_04")) (define small-answer-1 2) (define small-input (sanitize-input raw-small-input)) (define (process-line line) (define l (first line)) (define r (second line)) (if (or (and (<= (first l) (first r)) (>= (second l) (second r))) (and (>= (first l) (first r)) (<= (second l) (second r)))) #t #f)) ;; workhorse, part 1 (define (part-1 input) (for/fold ([acc 0] #:result acc) ([l input]) (if (process-line l) (add1 acc) acc)))
And I'm quite proud of the overlap function. xD
Part 2
(define (overlap? line) (define l (first line)) (define r (second line)) (if (or (< (second l) (first r)) (> (first l) (second r))) #f #t)) ;; part 2 (define small-answer-2 4) (define (part-2 input) (for/fold ([acc 0] #:result acc) ([l input]) (if (overlap? l) (add1 acc) acc)))
I know the destructuring in
process-line
andoverlap?
aren't necessary, but it was easier to think about, this way. :D -
Comment on Day 3: Rucksack Reorganization in ~comp
thorondir Ah, sets make a lot of sense, didn't think of them. I went about it in a rather... procedural way, which felt dirty, but worked. xD Part 1 ;; sanitize-input gets called before part-1 (define...Ah, sets make a lot of sense, didn't think of them.
I went about it in a rather... procedural way, which felt dirty, but worked. xD
Part 1
;; sanitize-input gets called before part-1 (define (sanitize-input raw-input) (for/list ([l (string-split raw-input "\n")]) (define strlen (string-length l)) (list ; return halves independently (string->list (substring l 0 (/ strlen 2))) (string->list (substring l (/ strlen 2)))))) (define (char-mapper c) (if (char-lower-case? c) (- (char->integer c) (sub1 (char->integer #\a))) (+ 26 (- (char->integer c) (sub1 (char->integer #\A)) )))) (define (calculate-priority priorities) (for/fold ([acc 0]) ([c priorities]) (+ acc (char-mapper c)))) (define (part-1 input) (define duplicates (for/list ([backpack input]) (define h (make-hash)) (for ([c (first backpack)]) (hash-set! h c 1)) (for/last ([c (second backpack)] #:when (hash-has-key? h c)) c))) (calculate-priority duplicates))
And part 2 also makes use of hash-maps a lot.
Part 2
; each backpack gets a hash where the characters are mapped to 1 (define (map-backpack backpack) (define h (make-hash)) (map (lambda (c) (hash-set! h c 1)) (append (first backpack) (second backpack))) h) ; calculate the union of the hashes by combining the values with + ; which means that the key that has a value of 3 is the badge we're looking for (define (compare-backpack-maps backpack-maps) (apply hash-union! backpack-maps #:combine +) (define combined (first backpack-maps)) (for/last ([k (in-hash-keys combined)] #:when (= (hash-ref combined k) 3)) k)) ; split the backpacks into groups of three ; calculate the badge for the current group ; recursively calculate the badge for the next group (define (process-backpacks backpacks) (cond [(< (length backpacks) 3) empty] [else (define-values (current-group other-groups) (split-at backpacks 3)) (cons (compare-backpack-maps (for/list ([b current-group]) (map-backpack b))) (process-backpacks other-groups))])) ;; part 2 (define small-answer-2 70) (define (part-2 input) (define badges (process-backpacks input)) (calculate-priority badges))
-
Comment on Day 1: Calorie Counting in ~comp
thorondir Oh, nice! That does make it much more readable. xD Thank you so much!Oh, nice! That does make it much more readable. xD
Thank you so much!
-
Comment on Day 1: Calorie Counting in ~comp
thorondir That's... much more elegant than what I did. From how the story went, I was sure there was going to be something more difficult for part two, so I set it up in a broader way, which then turned out...That's... much more elegant than what I did.
From how the story went, I was sure there was going to be something more difficult for part two, so I set it up in a broader way, which then turned out to be unnecessary...
Input parsing
;; function to make input usable (define (sanitize-input input) (define elf-strings (string-split input "\n\n")) (for/list ([e elf-strings]) (define inventory (map string->number (string-split e "\n"))) (elf (apply + inventory) inventory) )) ; returns a list of elves, each with their own inventory and the sum of calories as its own thing, for easy access
Given that, parts one was actually a simple one-liner.
Part 1
(define (part-1 input) (elf-calories (argmax elf-calories input)))
Part two was a bit more complicated, because I have to tell it how to sort the elves, but it's not rocket science.
Part 2
(define (part-2 input) (apply + (map elf-calories (take (sort input (lambda (a b) (> (elf-calories a) (elf-calories b)))) 3))))
For starting with racket not all that long ago, I'm quite happy about how it turned out.
-
Comment on Timasomo 2022: Final Update Thread in ~creative
thorondir I've also not gotten very far, turns out the difference between "things that are Arduino-compatible" and an RP2040 is... huge, when it comes to usability. I've come to appreciate what Arduino has...I've also not gotten very far, turns out the difference between "things that are Arduino-compatible" and an RP2040 is... huge, when it comes to usability. I've come to appreciate what Arduino has done for entering a field that seems so incredibly opaque (to someone who's done sysadmin and "above the kernel" programming work).
I didn't manage to get my RP2040 to do what I thought it should do. Readouts of pins make no sense and don't correlate with what happens on those pins electrically, and I'm very confused.
-
Comment on Timasomo 2022: Week 1 Update Thread in ~creative
thorondir Got a bit stuck with trying to figure out why the capacitative sensor doesn't work correctly, but the gold-plated one does, so... :shrug: I'll go with that. I did manage to track a complete drying...Got a bit stuck with trying to figure out why the capacitative sensor doesn't work correctly, but the gold-plated one does, so... :shrug: I'll go with that.
I did manage to track a complete drying cycle in one of the pots, turns out "dry" does not equal "dry", so I'll probably have to customize thresholds for when to water per plant. Which will take a while, so I don't think I'll manage all of that in the next two weeks.
-
Comment on Timasomo 2022: Roll Call in ~creative
-
Comment on Timasomo 2022: Roll Call in ~creative
thorondir Off-the-shelf, I'm using one of the following, depending on what works better: https://learn.sparkfun.com/tutorials/soil-moisture-sensor-hookup-guide...Off-the-shelf, I'm using one of the following, depending on what works better:
- https://learn.sparkfun.com/tutorials/soil-moisture-sensor-hookup-guide
- https://blog.zerokol.com/2021/01/hw-390-capacitive-soil-moisture-sensor.html
I'm actually not sure where exactly I got them from, but probably something like Pimoroni?
Guile Scheme again, and again my solution feels... Complicated.
My
utils.scm
got an addition:utils.scm
It makes sure everything in the `lst` maps to `true` when `proc` is applied to itPart 1
For part 2 I ran through every game again to calculate the number of cubes.
Part 2