kari's recent activity

  1. Comment on US President Donald Trump announces 25% tariffs against Canada, Mexico starting Tuesday; 10% against China in ~society

    kari
    Link Parent
    I live in Texas and gas is already up 40 cents since the election. I need to leave this state bad…

    I live in Texas and gas is already up 40 cents since the election. I need to leave this state bad…

    9 votes
  2. Comment on AI is creating a generation of illiterate programmers in ~tech

    kari
    Link Parent
    I do not. I almost exclusively write C code for the dataplane of enterprise IPS appliances. I suppose it could actually be useful when working on our Python test suites, but I haven't written...

    I do not. I almost exclusively write C code for the dataplane of enterprise IPS appliances. I suppose it could actually be useful when working on our Python test suites, but I haven't written anything for those in a while.

    5 votes
  3. Comment on AI is creating a generation of illiterate programmers in ~tech

    kari
    Link Parent
    My work let us use Copilot so I used it for a month or two last year and it honestly didn't do anything that snippets + an LSP server can't do already, which is what makes this so weird to me. I...

    It's a tool no different from a better IDE or syntax highlighting or autocomplete

    My work let us use Copilot so I used it for a month or two last year and it honestly didn't do anything that snippets + an LSP server can't do already, which is what makes this so weird to me. I mean, yeah, it can autogenerate boilerplate but we already have tools for that. Anything more complex than that didn't really work and, even if it did, I'd have to double check the entire thing anyways because it's AI-generated.

    9 votes
  4. Comment on Deadlock breaks 100,000 concurrent players with new peak in ~games

    kari
    Link Parent
    100% feel that. When the new characters came out, it was nice for a few days while the concurrent players was up to 20k or so but it's dropped down again and a lot of my games are pretty rough....

    Lately I've been struggling with finding suitable matches in my skill level because of the lack of players in recent weeks.

    100% feel that. When the new characters came out, it was nice for a few days while the concurrent players was up to 20k or so but it's dropped down again and a lot of my games are pretty rough. Like I said, I've never played a MOBA before, but it's brutal when I'm winning lane but one or two other lanes are feeding so suddenly I get ganked and can't do anything really the rest of the game... and I'm only in like Ritualist(?).

    I think if player counts come back up it has the beginnings of a very very good game.

    2 votes
  5. Comment on Deadlock breaks 100,000 concurrent players with new peak in ~games

    kari
    Link Parent
    I disagree, at least personally. I've never played a MOBA before Deadlock, and don't really want to, but I'm still enjoying playing Deadlock even right now when it's down to ~10k-15k concurrent...

    This game has very little appeal for someone looking for another multiplayer shooter

    I disagree, at least personally. I've never played a MOBA before Deadlock, and don't really want to, but I'm still enjoying playing Deadlock even right now when it's down to ~10k-15k concurrent players. I agree that it's not an Overwatch or TF2 killer (it seems like maybe Marvel Rivals could be that), but I really like Deadlock for the shooter mechanics even if it just happens to be a MOBA. There are some things like the movement in Deadlock that I think are super great and I don't think we've really had a (popular) game with movement this good since TF2 (and maybe Titanfall 2, too).

    1 vote
  6. Comment on How Donald Trump plans to seize the power of the purse from US Congress in ~society

    kari
    (edited )
    Link Parent
    I’m from Texas; we already had like half of that, so I’m sure it’ll get a lot worse. We had abstinence-only sex education, were told rape inside marriage isn’t real, and I literally had a teacher...

    I’m from Texas; we already had like half of that, so I’m sure it’ll get a lot worse. We had abstinence-only sex education, were told rape inside marriage isn’t real, and I literally had a teacher get fired for refusing to teach creationism.

    E: this was 2013-2017

    14 votes
  7. Comment on LCD Soundsystem - All My Friends (2007) in ~music

    kari
    Link
    In honor of me just scoring tickets to see them in April

    In honor of me just scoring tickets to see them in April

    2 votes
  8. Comment on How Donald Trump plans to seize the power of the purse from US Congress in ~society

    kari
    Link
    This is literally one of the things you learn about in high school government when they teach separation of powers. :/

    This is literally one of the things you learn about in high school government when they teach separation of powers. :/

    25 votes
  9. Comment on Austin rents have fallen for nearly two years in ~finance

    kari
    Link
    I live in Austin and just signed a lease to move to a newer, bigger, nicer apartment for about ~$100 less than I pay right now. It seems like prices were a bit lower in December than now, at least...

    I live in Austin and just signed a lease to move to a newer, bigger, nicer apartment for about ~$100 less than I pay right now. It seems like prices were a bit lower in December than now, at least from my personal experience, but stuff is still cheaper than it has been since 2021.

    9 votes
  10. Comment on What are you reading these days? in ~books

    kari
    Link
    I've been reading the Lightbringer series by Brent Weeks and enjoying it a lot. The first book took me a while to really get into it, but I'm definitely glad I stuck with it and I'm enjoying the...

    I've been reading the Lightbringer series by Brent Weeks and enjoying it a lot. The first book took me a while to really get into it, but I'm definitely glad I stuck with it and I'm enjoying the rest a lot. I'm currently about 40% through The Blood Mirror, book 4.

    1 vote
  11. Comment on I hate 2FA in ~tech

    kari
    Link
    I just put most 2FA secrets into my Bitwarden vault. It's definitely less secure and kinda defeats the point because someone can just get into my Bitwarden account and then they don't even need...

    I just put most 2FA secrets into my Bitwarden vault. It's definitely less secure and kinda defeats the point because someone can just get into my Bitwarden account and then they don't even need anything else, but it makes it a lot easier for things I don't particularly care about. It also still adds some security if my password(s) get leaked, since there's still some 2FA.

    To be honest, the ones I hate the most are email because I don't have an email client open 24/7 on my laptop or desktop. At least for SMS codes they're easy for me to check, but those aren't particularly secure.

    5 votes
  12. Comment on Proton CEO tweets support for Donald Trump's Department of Justice pick and the US Republican Party in ~society

    kari
    Link Parent
    Yeah, I like them a lot and they’re really cheap for what you get.

    Yeah, I like them a lot and they’re really cheap for what you get.

    1 vote
  13. Comment on Day 11: Plutonian Pebbles in ~comp.advent_of_code

    kari
    Link
    My part 2 works pretty well, but I don't think it's particularly idiomatic Racket, so anyone who knows more can feel free to let me know how the blink-efficient procedure can be improved :) Racket...

    My part 2 works pretty well, but I don't think it's particularly idiomatic Racket, so anyone who knows more can feel free to let me know how the blink-efficient procedure can be improved :)

    Racket
    #! /usr/bin/env racket
    #lang racket
    
    (module+ test
      (require rackunit))
    
    (define (input->stones in-port)
      (let ([stone-strs (string-split (read-line in-port) " ")])
        (map string->number stone-strs)))
    
    ;; Helper procedure to apply the rules for a single stone value
    (define (apply-rules stone)
      (cond
        ;; If the stone is engraved with the number 0, it is replaced by a stone engraved with the number 1.
        [(zero? stone) (list 1)]
        ;; If the stone is engraved with a number that has an even number of digits, it is replaced by two stones.
        ;; The left half of the digits are engraved on the new left stone, and the right half of the digits are
        ;; engraved on the new right stone. (The new numbers don't keep extra leading zeroes: 1000 would become
        ;; stones 10 and 0.)
        [(even? (string-length (number->string stone)))
         (let* ([stone-str (number->string stone)]
                [middle (quotient (string-length stone-str) 2)]
                [left-str (substring stone-str 0 middle)]
                [right-str (substring stone-str middle)]
                [left (string->number left-str)]
                [right (string->number right-str)])
           (list left right))]
        ;; If none of the other rules apply, the stone is replaced by a new stone; the old stone's number
        ;; multiplied by 2024 is engraved on the new stone.
        [else (list (* stone 2024))]))
    
    ;; I'm sure there's a trick to this...
    (define (blink-orig stones [count 1])
      (if (zero? count)
          stones
          (blink-orig (for/fold ([new-stones '()])
                                ([stone (in-list stones)])
                        (append (apply-rules stone) new-stones))
                      (sub1 count))))
    
    ;; Ah...
    ;; This version of the procedure loses the ordering but is much faster for lots of iterations.
    (define (blink-efficient stones [count 1])
      ;; Iteratively create a hash table and put the stones in it.
      ;; Each key is a value on a stone and each value is the number of
      ;; that value.
      ;; I don't know how to do this not iteratively.
      (define ht (make-hash))
      (for ([stone (in-list stones)])
        (hash-update! ht stone add1 0))
      ;; We also create a hash table to dynamically store our results, this way we don't have to redo calculations!
      (define calculated (make-hash))
      ;; Use a helper since we want don't want to re-create ht every time.
      (let helper ([stones ht] [count count])
        (define new-stones (make-hash))
        (cond
          [(zero? count) stones]
          ;; Also don't really know how to do this without side effects.
          [else
           ;; First, iterate through stones and update ht.
           (for ([stone-pair (in-list (hash->list stones))])
             (let* ([stone (car stone-pair)]
                    [count (cdr stone-pair)]
                    [blinked-stones (hash-ref calculated stone (lambda () (apply-rules stone)))])
               (hash-set! calculated stone blinked-stones)
               (for ([stone (in-list blinked-stones)])
                 (hash-update! new-stones stone (lambda (n) (+ n count)) 0))))
           ;; Then recurse
           (helper new-stones (sub1 count))])))
    
    (module+ test
      ;; Part 1
      (check-equal? (length (blink-orig (input->stones (open-input-string "0 1 10 99 999")) 1)) 7 "Test blink 1:")
      (let* ([input (open-input-string "125 17")]
             [stones (input->stones input)])
        (check-equal? (length (blink-orig stones 1)) 3 "Test blink 1: 2")
        (check-equal? (length (blink-orig stones 2)) 4 "Test blink 1: 3")
        (check-equal? (length (blink-orig stones 3)) 5 "Test blink 1: 4"))
      ;; Part 2
      (check-equal? (for/sum ([x (in-list (hash-values (blink-efficient (input->stones (open-input-string "0 1 10 99 999")) 1)))])
                      x) 7 "Test blink 2: 1")
      (let* ([input (open-input-string "125 17")]
             [stones (input->stones input)])
        (check-equal? (for/sum ([x (in-list (hash-values (blink-efficient stones 1)))])
                        x) 3 "Test blink 2: 2")
        (check-equal? (for/sum ([x (in-list (hash-values (blink-efficient stones 2)))])
                        x) 4 "Test blink 2: 3")
        (check-equal? (for/sum ([x (in-list (hash-values (blink-efficient stones 3)))])
                        x) 5 "Test blink 2: 4")))
    
    (define (day11/run-part01 stones)
      (length (blink-orig stones 25)))
    (define (day11/run-part02 stones [count 75])
      (for/sum ([x (in-list (hash-values (blink-efficient stones count)))])
        x))
    
    (module+ test
      (let* ([in-port (open-input-string "125 17")]
             [stones (input->stones in-port)])
        (check-equal? (day11/run-part01 stones) 55312 "Test part 1 (blink-orig)")
        (check-equal? (day11/run-part02 stones 25) 55312 "Test part 2 (blink-efficient)")))
    
    ;; Run against my input and print the results
    (module+ main
      (let* ([in-port (open-input-file "inputs/day11.in")]
             [stones (input->stones in-port)]
             [part1 (day11/run-part01 stones)]
             [part2 (day11/run-part02 stones)])
        (printf "Part 1: ~a, Part 2: ~a\n" part1 part2)))
    
    1 vote
  14. Comment on Day 14: Restroom Redoubt in ~comp.advent_of_code

    kari
    Link
    Part 1 was easy (and I finally learned about structs in Racket!) Part 2 I just stole the idea from lily's solution in here. Racket #! /usr/bin/env racket #lang racket (require "common.rkt")...

    Part 1 was easy (and I finally learned about structs in Racket!)
    Part 2 I just stole the idea from lily's solution in here.

    Racket
    #! /usr/bin/env racket
    #lang racket
    
    (require "common.rkt")
    
    (module+ test
      (require rackunit))
    
    (struct robot (x y Δx Δy))
    
    (define (parse-robots in-port)
      (for/list ([line (in-lines in-port)])
        (let* ([split (string-split line " ")]
               [position (string-split (substring (first split) 2) ",")]
               [x (string->number (first position))]
               [y (string->number (second position))]
               [velocity (string-split (substring (second split) 2) ",")]
               [Δx (string->number (first velocity))]
               [Δy (string->number (second velocity))])
          (robot x y Δx Δy))))
    
    (define (top-left? q)
      (equal? 'tl q))
    (define (top-right? q)
      (equal? 'tr q))
    (define (bottom-left? q)
      (equal? 'bl q))
    (define (bottom-right? q)
      (equal? 'br q))
    
    (define (step robots width height [k 1])
      (for/list ([r (in-list robots)])
        (let* ([new-x (modulo (+ (robot-x r) (* k (robot-Δx r))) width)]
               [new-y (modulo (+ (robot-y r) (* k (robot-Δy r))) height)])
          (struct-copy robot r [x new-x] [y new-y]))))
    
    (define (safety-factor robots width height)
      (let* ([mid-x (quotient width 2)]
             [mid-y (quotient height 2)]
             [quadrants (for/fold ([quadrants '()])
                                  ([r (in-list robots)])
                          (let* ([x (robot-x r)]
                                 [y (robot-y r)])
                            (cond
                              ;; top left
                              [(and (< x mid-x)
                                    (< y mid-y))
                               (cons 'tl quadrants)]
                              ;; top right
                              [(and (> x mid-x)
                                    (< y mid-y))
                               (cons 'tr quadrants)]
                              ;; bottom left
                              [(and (< x mid-x)
                                    (> y mid-y))
                               (cons 'bl quadrants)]
                              ;; bottom right
                              [(and (> x mid-x)
                                    (> y mid-y))
                               (cons 'br quadrants)]
                              [else (cons 'middle quadrants)])))]
             [tl-count (length (filter top-left? quadrants))]
             [tr-count (length (filter top-right? quadrants))]
             [bl-count (length (filter bottom-right? quadrants))]
             [br-count (length (filter bottom-left? quadrants))])
        (* tl-count tr-count bl-count br-count)))
    
    ;; Returns the number of unique positions the robots are in
    (define (unique-positions robots)
      (set-count (for/set ([r (in-list robots)])
                   (list (robot-x r) (robot-y r)))))
    
    (define (day14/run-part01 robots width height)
      (safety-factor (step robots
                           width
                           height
                           100)
                     width
                     height))
    
    ;; I stole the idea of "no robots on the same space" from here
    ;; https://tildes.net/~comp.advent_of_code/1kos/day_14_restroom_redoubt#comment-ecv5
    (define (day14/run-part02 robots width height)
      (let helper ((robots robots) (steps 0))
        (let ([posn-count (unique-positions robots)])
          (cond
            [(= posn-count (length robots)) steps]
            [else (helper (step robots width height) (add1 steps))]))))
    
    (module+ test
      (check-equal? (day14/run-part01 (parse-robots (open-input-file "examples/day14.in")) 11 7) 12 "Test part 1"))
    
    ;; Run against my input and print the results
    (module+ main
      (let* ([in-port (open-input-file "inputs/day14.in")]
             [robots (parse-robots in-port)]
             [part1 (day14/run-part01 robots 101 103)]
             [part2 (day14/run-part02 robots 101 103)])
        (printf "Part 1: ~a, Part 2: ~a\n" part1 part2)))
    
  15. Comment on Day 10: Hoof It in ~comp.advent_of_code

    kari
    Link
    Honestly, I kind of wish I hadn't used the graph library for Racket lol. I went back after I did part 2 and my day10/run-part01-better procedure drops the run time for part 1 from ~17 seconds to 1...

    Honestly, I kind of wish I hadn't used the graph library for Racket lol. I went back after I did part 2 and my day10/run-part01-better procedure drops the run time for part 1 from ~17 seconds to 1 second lol. I guess that's partly just because my "better" procedure doesn't find the shortest path, just a path.

    Anyways, I really enjoyed that one. I did UIL Computer Science competitions in high school that had lots of graph problems like this and they were by far my favorite. If I were doing this back then I probably could've finished it in Java (without any graph library, just by coordinates) in like 5 minutes lol.

    Racket
    #! /usr/bin/env racket
    #lang racket
    
    (require graph "common.rkt")
    
    (module+ test
      (require rackunit))
    
    (define (map->graph in-map)
      ;; Check if (i1, j1) -> (i2, j2) should be an edge
      ;; Can only be an edge if (map-ref in-map i1 j1) + 1 = (map-ref in-map i2 j2)
      (define (edge? i1 j1 i2 j2)
        (cond
          [(or (out-of-bounds? in-map i1 j1)
               (out-of-bounds? in-map i2 j2)) #f]
          [else (eq? 1 (- (map-ref in-map i2 j2)
                          (map-ref in-map i1 j1)))]))
      ;; Iterate through the entire map and create any edges
      (directed-graph (for*/fold ([edges '()])
                                 ([i (length in-map)]
                                  [j (length (list-ref in-map i))]
                                  [dest (in-list (list (list (sub1 i) j)
                                                       (list (add1 i) j)
                                                       (list i (sub1 j))
                                                       (list i (add1 j))))])
                        (cond
                          [(edge? i j (first dest) (second dest))
                           (let* ([start (list i j)]
                                  [new-edge (list start dest)])
                             (cons new-edge edges))]
                          [else edges]))))
    
    ;; Get the coordinates of all 0s on the map
    (define (trailhead-coords in-map)
      (for*/fold ([trailhead-coords '()])
                 ([i (length in-map)]
                  [j (length (list-ref in-map i))])
        (cond
          [(zero? (map-ref in-map i j)) (cons (list i j) trailhead-coords)]
          [else trailhead-coords])))
    ;; Get the coordinates of all 9s on the map
    (define (peak-coords in-map)
      (for*/fold ([trailhead-coords '()])
                 ([i (length in-map)]
                  [j (length (list-ref in-map i))])
        (cond
          [(= 9 (map-ref in-map i j)) (cons (list i j) trailhead-coords)]
          [else trailhead-coords])))
    
    ;; Calculate if a path exists for a single trailhead->peak pair
    (define (has-path? g trailhead peak)
      (let helper ((v trailhead) (visited '()))
        (cond
          ;; found a path
          [(equal? v peak) 1]
          [else (for/or ([neighbor (in-list (get-neighbors g v))])
                  (cond
                    ;; Already visited this vertex
                    [(member neighbor visited) 0]
                    [else (helper neighbor (cons v visited))]))])))
    
    ;; Calculate the rating for a single trailhead->peak pair.
    ;; This is essentially (has-path?) but it doesn't return
    ;; as soon as it finds a path.
    (define (rating g trailhead peak)
      (let helper ((v trailhead) (visited '()))
        (cond
          ;; found a path
          [(equal? v peak) 1]
          [else (for/sum ([neighbor (in-list (get-neighbors g v))])
                  (cond
                    ;; Already visited this vertex
                    [(member neighbor visited) 0]
                    [else (helper neighbor (cons v visited))]))])))
    
    
    (define (day10/run-part01 in-map)
      (let ([map-graph (map->graph in-map)]
            [trailhead-coords (trailhead-coords in-map)]
            [peak-coords (peak-coords in-map)])
        ;; For each trailhead->peak pair, see if there's a path.
        ;; If there is, add 1 to our sum of paths.
        (for*/sum ([trailhead-coord (in-list trailhead-coords)]
                   [peak-coord (in-list peak-coords)])
          (cond
            [(fewest-vertices-path map-graph trailhead-coord peak-coord) 1]
            [else 0]))))
    
    (define (day10/run-part01-better in-map)
      (let ([map-graph (map->graph in-map)]
            [trailhead-coords (trailhead-coords in-map)]
            [peak-coords (peak-coords in-map)])
        ;; For each trailhead->peak pair, see if there's a path.
        ;; If there is, add 1 to our sum of paths.
        (for*/sum ([trailhead-coord (in-list trailhead-coords)]
                   [peak-coord (in-list peak-coords)])
          (if (has-path? map-graph trailhead-coord peak-coord)
              1
              0))))
    
    (define (day10/run-part02 in-map)
      (let ([map-graph (map->graph in-map)]
            [trailhead-coords (trailhead-coords in-map)]
            [peak-coords (peak-coords in-map)])
        ;; For each trailhead->peak pair, see if there's a path.
        ;; If there is, add 1 to our sum of paths.
        (for*/sum ([trailhead-coord (in-list trailhead-coords)]
                   [peak-coord (in-list peak-coords)])
          (rating map-graph trailhead-coord peak-coord))))
    
    (module+ test
      (check-equal? (day10/run-part01 (input->list-numbers (open-input-file "examples/day10_1.in")))   2 "Test part 1: 1")
      (check-equal? (day10/run-part01 (input->list-numbers (open-input-file "examples/day10_2.in")))   4 "Test part 1: 2")
      (check-equal? (day10/run-part01 (input->list-numbers (open-input-file "examples/day10_3.in")))   3 "Test part 1: 3")
      (check-equal? (day10/run-part01 (input->list-numbers (open-input-file "examples/day10_4.in")))  36 "Test part 1: 4")
      (check-equal? (day10/run-part02 (input->list-numbers (open-input-file "examples/day10_5.in")))   3 "Test part 2: 1")
      (check-equal? (day10/run-part02 (input->list-numbers (open-input-file "examples/day10_6.in")))  13 "Test part 2: 2")
      (check-equal? (day10/run-part02 (input->list-numbers (open-input-file "examples/day10_7.in"))) 227 "Test part 2: 3")
      (check-equal? (day10/run-part02 (input->list-numbers (open-input-file "examples/day10_4.in")))  81 "Test part 2: 4"))
    
    ;; Run against my input and print the results
    (module+ main
      (let* ([in-port (open-input-file "inputs/day10.in")]
             [in-map (input->list-numbers in-port)]
             [part1 (day10/run-part01-better in-map)]
             [part2 (day10/run-part02 in-map)])
        (printf "Part 1: ~a, Part 2: ~a\n" part1 part2)))
    
    1 vote
  16. Comment on Day 9: Disk Fragmenter in ~comp.advent_of_code

    kari
    Link
    I've been busy so I'm catching up right now. My part 1 took like 20 minutes and then part 2, which I thought I'd made more efficient, actually took 63 lol. Advent of Code does not bring out the...

    I've been busy so I'm catching up right now.

    My part 1 took like 20 minutes and then part 2, which I thought I'd made more efficient, actually took 63 lol. Advent of Code does not bring out the best in my coding abilities

    Racket
    #! /usr/bin/env racket
    #lang racket
    
    (require "common.rkt")
    
    (module+ test
      (require rackunit))
    
    ;; I could probably do all of this without disk-map->mem
    ;; and it'd be more efficient... but I'm lazy
    
    (define (disk-map->mem disk-map)
      ;; prepend k ids to mem
      ;; TODO: use make-list instead of this
      (define (prepend-proc id k mem)
        (cond
          [(< k 0) (error "invalid k")]
          [(zero? k) mem]
          [else (prepend-proc id (sub1 k) (cons id mem))]))
      ;; recursive helper procedure that does the work
      (define (helper disk-map type id mem)
        (cond
          ;; reached the end of the map; reverse our mem list and return it
          [(empty? disk-map) (reverse mem)]
          ;; free space: add #\.s to mem but don't increment id
          [(eq? type 'free-space)
           (helper (cdr disk-map)
                   'file
                   id
                   (prepend-proc #\. (char->number (car disk-map)) mem))]
          ;; file: add the ID to mem but don't increment id
          [(eq? type 'file)
           (helper (cdr disk-map)
                   'free-space
                   (add1 id)
                   (prepend-proc id (char->number (car disk-map)) mem))]))
      (helper disk-map 'file 0 '()))
    
    (define (mem-compact-blocks mem)
      ;; recursive helper procedure that does the work
      (define (helper mem compacted-mem)
        (cond
          ;; reached the end of mem; return out compacted-mem
          [(empty? mem) compacted-mem]
          ;; found a free space and (last mem) is at a file-id; let's compact
          [(and (eq? (car mem) #\.)
                (not (eq? (last mem) #\.)))
           ;; want to drop the first and last of mem
           (cons #\. (helper (take (cdr mem) (- (length mem) 2))
                             (cons (last mem) compacted-mem)))]
          ;; found a free space but (last mem) is not at a file id;
          ;; recurse but get rid of the last value of mem
          [(eq? (car mem) #\.)
           (cons #\. (helper (take mem (sub1 (length mem)))
                             compacted-mem))]
          ;; this space already has a file block in it; just add it to the new compacted-mem then continue
          [else (helper (cdr mem)
                        (cons (car mem) compacted-mem))]))
      (reverse (helper mem '())))
    
    (define (mem-checksum mem)
      (define (helper mem idx)
        (cond
          ;; done
          [(empty? mem) 0]
          ;; found an empty block
          [(eq? (car mem) #\.)
           (helper (cdr mem) (add1 idx))]
          ;; found a file block
          [else (+ (* idx (car mem))
                   (helper (cdr mem) (add1 idx)))]))
      (helper mem 0))
    
    (define (disk-map->enc disk-map)
      (let helper ((disk-map disk-map) (enc '()) (type 'file) (id 0))
        (cond
          [(empty? disk-map) (reverse enc)]
          [(eq? type 'file)
           (helper (cdr disk-map)
                   (cons (list id (char->number (car disk-map)))
                         enc)
                   'free-space
                   (add1 id))]
          [(eq? type 'free-space)
           (helper (cdr disk-map)
                   (cons (list #f (char->number (car disk-map)))
                         enc)
                   'file
                   id)]
          [else (error 'enc-fail)])))
    
    (define (mem-compact-files enc)
      (define (val-to-insert free-space req-size)
        (cond
          [(< req-size free-space) (list (list #f (- free-space req-size)))]
          [(= req-size free-space) '()]
          [else (error 'invalid-size)]))
      (define (clean-enc enc [i 0])
        (let ([j (add1 i)])
          (cond
            [(>= j (length enc)) enc]
            [(and (not (car (list-ref enc i)))
                  (not (car (list-ref enc j))))
             (clean-enc (append (take enc i)
                                (list (list #f (+ (cadr (list-ref enc i))
                                                  (cadr (list-ref enc j)))))
                                (list-tail enc (add1 j))) i)]
            [else (clean-enc enc (add1 i))])))
      (let helper ((enc enc) (i 0) (j (sub1 (length enc))) (checked '()))
        (cond
          [(negative? j) enc]
          [(or (>= i j)
               (member (car (list-ref enc j)) checked))
           (helper enc 0 (sub1 j) (cons (car (list-ref enc j)) checked)) ]
          [(not (car (list-ref enc j))) (helper enc i (sub1 j) (cons (car (list-ref enc j)) checked))]
          [(number? (car (list-ref enc i))) (helper enc (add1 i) j checked)]
          [(and (not (car (list-ref enc i)))
                (<= (cadr (list-ref enc j)) (cadr (list-ref enc i))))
           (let* ([new-free-space (val-to-insert (cadr (list-ref enc i)) (cadr (list-ref enc j)))]
                  [new-enc (append (take enc i)
                                   (list (list-ref enc j))
                                   new-free-space
                                   (list-tail (take enc j) (add1 i))
                                   (list (list #f (cadr (list-ref enc j))))
                                   (list-tail enc (add1 j)))]
                  [new-j (cond
                           [(empty? new-free-space) (sub1 j)]
                           [else j])]
                  [checked (cons (car (list-ref enc j)) checked)]
                  [cleaned-enc (clean-enc new-enc)])
             (helper cleaned-enc 0 new-j checked))]
          [else (helper enc (add1 i) j '())])))
    
    (define (enc->mem enc)
      (let helper ((enc enc) (mem '()))
        (cond
          [(empty? enc) (reverse mem)]
          [else (let* ([type (caar enc)]
                       [count (cadar enc)]
                       [char (cond
                               [(not type) #\.]
                               [else type])])
                  (helper (cdr enc) (append (make-list count char)
                                            mem)))])))
    
    (define (day09/run-part01 mem)
      (mem-checksum (mem-compact-blocks mem)))
    (define (day09/run-part02 enc)
      (mem-checksum (enc->mem (mem-compact-files enc))))
    
    (module+ test
      (let* ([in-port (open-input-string "2333133121414131402")]
             [disk-map (string->list (read-line in-port))]
             [mem (disk-map->mem disk-map)]
             [enc (disk-map->enc disk-map)])
        (check-equal? (day09/run-part01 mem) 1928 "Test part 1")
        (check-equal? (day09/run-part02 enc) 2858 "Test part 2")))
    
    (module+ main
      (let* ([in-port (open-input-file "inputs/day09.in")]
             [disk-map (string->list (read-line in-port))]
             [mem (disk-map->mem disk-map)]
             [enc (disk-map->enc disk-map)]
             ; [part1 (day09/run-part01 mem)]
             [part2 (day09/run-part02 enc)])
        (printf "Part 1: ~a, Part 2: ~a\n" 'part1 part2)))
    
    1 vote
  17. Comment on Day 8: Resonant Collinearity in ~comp.advent_of_code

    kari
    Link
    My code for today is kinda blegh but honestly I just wanted to get it done. It wasn't too bad when I'd only done part 1, but I just didn't feel like doing much refactoring during part 2. I am...

    My code for today is kinda blegh but honestly I just wanted to get it done. It wasn't too bad when I'd only done part 1, but I just didn't feel like doing much refactoring during part 2. I am enjoying Racket, though.

    Racket
    #! /usr/bin/env racket
    #lang racket
    
    (require "common.rkt")
    
    (module+ test
      (require rackunit))
    
    ;; return the antinodes for pairs p1 and p2
    ;; TODO: This could be cleaned up a lot so there's less repeated code
    (define (antinodes p1 p2 [in-map '()] [repeat #f])
      ;; helper that can tell whether to add or subtract dx from x1 based on x1/x2
      (define (calc-new x1 x2 dx)
        (cond
          [(> x1 x2) (+ x1 dx)]
          [else (- x1 dx)]))
      ;; helper for part2, this could be removed if I cleaned things up
      (define (calc-dir x1 x2 dx)
        (cond
          [(> x1 x2) dx]
          [else (* dx -1)]))
      ;; helper for part2
      ;; returns a list of antinodes until the boundary of the map
      (define (calc-repeats i j di dj [lst '()])
        (cond
          [(out-of-bounds? in-map i j) lst]
          [else (calc-repeats (+ i di) (+ j dj) di dj (cons (list i j) lst))]))
      ;; get individual coordinates
      (let* ([i1 (first p1)]
             [j1 (second p1)]
             [i2 (first p2)]
             [j2 (second p2)]
             [di (abs (- i1 i2))]
             [dj (abs (- j1 j2))])
        (append
         ;; these are the regular antinodes
         (list (list (calc-new i1 i2 di) (calc-new j1 j2 dj))
               (list (calc-new i2 i1 di) (calc-new j2 j1 dj)))
         ;; if repeat, then we calculate the repeating ones, too
         (cond
           [repeat (append
                    (calc-repeats i1 j1 (calc-dir i1 i2 di) (calc-dir j1 j2 dj))
                    (calc-repeats i2 j2 (calc-dir i2 i1 di) (calc-dir j2 j1 dj)))]
           [else '()]))))
    
    (module+ test
      (check-equal? (antinodes (list 3 4) (list 5 5))
                    (list (list 1 3) (list 7 6))
                    "Test antinodes: basic")
      (check-equal? (antinodes (list 0 4) (list 0 5))
                    (list (list 0 3) (list 0 6))
                    "Test antinodes: same i"))
    
    (define (antenna-coords in-map)
      (for*/fold ([antenna-coords '()])
                 ([i (length in-map)]
                  [j (length (list-ref in-map i))])
        (let ([freq (map-ref in-map i j)])
          (cond
            [(not (equal? freq #\.)) (cons (list freq (list i j)) antenna-coords)]
            [else antenna-coords]))))
    
    (define (day08/run-part01 in-map)
      (let* ([coords (antenna-coords in-map)]
             [freqs (list->set (map first coords))])
        ;; these isn't very efficient but oh well
        (set-count
         (list->set
          (for/fold ([antinds '()])
                    ([freq (in-set freqs)])
            ;; get all the coordinates for antennas on freq
            (append (let ([freq-coords (map second (filter (lambda (p) (eq? (first p) freq)) coords))])
                      ;; calculate all antinodes, then filter out out-of-bounds ones
                      (filter (lambda (p) (not (out-of-bounds? in-map (first p) (second p))))
                              (for*/fold ([antinds '()])
                                         ([m (sub1 (length freq-coords))]
                                          [n (in-range (add1 m) (length freq-coords))])
                                (append (antinodes (list-ref freq-coords m) (list-ref freq-coords n)) antinds))))
                    antinds))))))
    (define (day08/run-part02 in-map)
      (let* ([coords (antenna-coords in-map)]
             [freqs (list->set (map first coords))])
        ;; these isn't very efficient but oh well
        (let ([x
               (list->set
                (for/fold ([antinds '()])
                          ([freq (in-set freqs)])
                  ;; get all the coordinates for antennas on freq
                  (append (let ([freq-coords (map second (filter (lambda (p) (eq? (first p) freq)) coords))])
                            ;; calculate all antinodes, then filter out out-of-bounds ones
                            (filter (lambda (p) (not (out-of-bounds? in-map (first p) (second p))))
                                    (for*/fold ([antinds '()])
                                               ([m (sub1 (length freq-coords))]
                                                [n (in-range (add1 m) (length freq-coords))])
                                      (append (antinodes (list-ref freq-coords m) (list-ref freq-coords n) in-map #t) antinds))))
                          antinds)))])
          (set-count x))))
    
    ;; Test examples from the problem
    (module+ test
      (check-equal? (day08/run-part01 (input->list (open-input-file "examples/day08_1.in")))  4 "Test part 1: simple")
      (check-equal? (day08/run-part01 (input->list (open-input-file "examples/day08_2.in"))) 14 "Test part 1: less simple")
      (check-equal? (day08/run-part02 (input->list (open-input-file "examples/day08_3.in")))  9 "Test part 2: simple")
      (check-equal? (day08/run-part02 (input->list (open-input-file "examples/day08_2.in"))) 34 "Test part 2: less simple")
      (check-equal? (day08/run-part02 (input->list (open-input-file "examples/day08_1.in")))  8 "Test part 2: ???"))
    
    ;; Run against my input and print the results
    (module+ main
      (let* ([in-port (open-input-file "inputs/day08.in")]
             [in-map (input->list in-port)]
             [part1 (day08/run-part01 in-map)]
             [part2 (day08/run-part02 in-map)])
        (printf "Part 1: ~a, Part 2: ~a\n" part1 part2)))
    
    2 votes
  18. Comment on Day 7: Bridge Repair in ~comp.advent_of_code