thorondir's recent activity

  1. Comment on Day 7: No Space Left On Device in ~comp.advent_of_code

    thorondir
    Link
    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))
    
    2 votes
  2. Comment on Day 6: Tuning Trouble in ~comp.advent_of_code

    thorondir
    Link Parent
    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! :)

    2 votes
  3. Comment on Day 6: Tuning Trouble in ~comp.advent_of_code

    thorondir
    Link Parent
    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)
    
    2 votes
  4. Comment on Day 4: Camp Cleanup in ~comp.advent_of_code

    thorondir
    Link Parent
    That's very helpful, thank you!

    That's very helpful, thank you!

    3 votes
  5. Comment on Day 5: Supply Stacks in ~comp.advent_of_code

    thorondir
    Link
    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)))
    
    2 votes
  6. Comment on Day 4: Camp Cleanup in ~comp.advent_of_code

    thorondir
    Link Parent
    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 and overlap? aren't necessary, but it was easier to think about, this way. :D

    3 votes
  7. Comment on Day 3: Rucksack Reorganization in ~comp.advent_of_code

    thorondir
    Link Parent
    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))
    
    2 votes
  8. Comment on Day 1: Calorie Counting in ~comp.advent_of_code

    thorondir
    Link Parent
    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!

    3 votes
  9. Comment on Day 1: Calorie Counting in ~comp.advent_of_code

    thorondir
    Link Parent
    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.

    2 votes
  10. Comment on Timasomo 2022: Final Update Thread in ~creative.timasomo

    thorondir
    Link
    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.

    3 votes
  11. Comment on Timasomo 2022: Week 1 Update Thread in ~creative.timasomo

    thorondir
    Link
    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.

    1 vote
  12. Comment on Timasomo 2022: Roll Call in ~creative.timasomo

  13. Comment on Timasomo 2022: Roll Call in ~creative.timasomo

    thorondir
    Link Parent
    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:

    I'm actually not sure where exactly I got them from, but probably something like Pimoroni?

    1 vote
  14. Comment on Timasomo 2022: Roll Call in ~creative.timasomo

    thorondir
    (edited )
    Link
    I'll build an automatic plant-watering system for indoor plants: a humidity sensor that checks soil humidity a microcontroller that decides whether what has been sensed is enough a pump, which the...

    I'll build an automatic plant-watering system for indoor plants:

    • a humidity sensor that checks soil humidity
    • a microcontroller that decides whether what has been sensed is enough
    • a pump, which the microcontroller can trigger, that waters the plant

    Ideally, it's all battery-powered [I've got some small LiPos lying around], but if I have to run some wires, so be it.

    If everything turns out great, I'll give myself bonus points for setting up a 433MHz sender on the thing, with a Rasberry Pi receiver, that centrally monitors N potential watering stations throughout the house.

    10 votes
  15. Comment on Announcing Tildes' Make Something Month (Timasomo) for 2022! in ~creative.timasomo

    thorondir
    Link
    The past two years I've watched people do amazing things, on here, but never had the time. This time around, I just got laid off, have two months of free time, which fits perfectly, so I'll do...

    The past two years I've watched people do amazing things, on here, but never had the time.

    This time around, I just got laid off, have two months of free time, which fits perfectly, so I'll do something I've been putting off for... way too long:

    I'll build an automatic plant-watering system for indoor plants:

    • a humidity sensor that checks soil humidity
    • a microcontroller that decides whether whether what has been sensed is enough
    • a pump, which the microcontroller can trigger, that waters the plant

    Ideally, it's all battery-powered [I've got some small LiPos lying around], but if I have to run some wires, so be it.

    If everything turns out great, I'll give myself bonus points for setting up a 433MHz sender on the thing, with a Rasberry Pi receiver, that centrally monitors N potential watering stations throughout the house.

    7 votes
  16. Comment on Where/how should I acquire a .com domain for three years in advance? in ~tech

    thorondir
    Link
    I think Namecheap allows for that. They also host Wordpress, if you're looking for that. I've been with them for a year, no complaints so far.

    I think Namecheap allows for that.
    They also host Wordpress, if you're looking for that.

    I've been with them for a year, no complaints so far.

    12 votes
  17. Comment on Self hosting email at home? in ~comp

    thorondir
    Link Parent
    That's pretty much exactly how it went for me, though I had the luxury of being a student with entirely too much free time, to figure stuff out. The initial setup is not all that complicated,...

    That's pretty much exactly how it went for me, though I had the luxury of being a student with entirely too much free time, to figure stuff out.

    The initial setup is not all that complicated, actually, as the Arch Wiki tells you pretty much exactly what to do ( for those interested: https://wiki.archlinux.org/index.php/Virtual_user_mail_system ).
    The difficult bit was in figuring out afterwards what I actually did, and whether what I did conformed to what I wanted to do.

    There are indeed a lot of moving parts, but after about a week of tinkering with it basically around the clock, back in 2015, I think, and some maintenance every now and again (and recently adding DMARC), it's been amazing.

    To be fair, I'm a nerd that likes that sort of thing, so YMMV.

    2 votes
  18. Comment on Everyone is beautiful and no one is horny in ~movies

    thorondir
    Link Parent
    As someone who doesn't know anything about buddhism, I'm genuinely curious: Why despise it?

    As someone who doesn't know anything about buddhism, I'm genuinely curious: Why despise it?

    5 votes
  19. Comment on What programming/technical projects have you been working on? in ~comp

    thorondir
    Link Parent
    That's very fair. If you do decide to publish it somewhere, do post it here on Tildes, I'd be curious. :)

    That's very fair.

    If you do decide to publish it somewhere, do post it here on Tildes, I'd be curious. :)

    3 votes
  20. Comment on What programming/technical projects have you been working on? in ~comp

    thorondir
    Link Parent
    Have you considered writing a blog post about it, or something along those line, to make the things you figured out public? Make it easier for the next person?

    Have you considered writing a blog post about it, or something along those line, to make the things you figured out public? Make it easier for the next person?

    2 votes