Rules: no spoilers.

The other rules are made up as we go along.

Share code by link to a forge, home page, pastebin (Eric Wastl has one here) or code section in a comment.

    • @zogwarg
      link
      English
      4
      edit-2
      7 months ago

      I liked the slight trickiness of part 2, that the naive implementation would never complete in time.

      As always doing a JQ implementation:

      Part 1
      #!/usr/bin/env jq -n -R -f
      
      # Get seeds
      input | [ match("\\d+"; "g").string | tonumber ] as $seeds |
      
      # Collect maps
      reduce inputs as $line ({};
        if $line == "" then
          .
        elif $line | test(":") then
          .k = ( $line / " " | .[0] )
        else
          .[.k] += [[ $line | match("\\d+"; "g").string | tonumber ]]
        end
      )
      
      # For each map, apply transformation to all seeds.
      # seed -> ... -> location
      | reduce ( to_entries[] | select(.key != "k") .value) as $map ({s:$seeds};
        .s[] |= (
          # Only attempt transform if element is in one of the ranges
          [ . as $e | $map[] | select(. as  [$d,$s,$l] | $e >= $s and $e$s + $l) ] as $range |
          if ($range | length ) > 0 then
            $range[0] as [$d,$s] |
            . - $s + $d
          else
            .
          end
        )
      )
      
      # Get lowest location
      | .s | min
      

      Some comments:

      • A nice use of input first to get the seeds, then inputs to get remaining lines.
      Part 2
      #!/usr/bin/env jq -n -R -f
      
      # Utility function
      def group_of($n):
        ( length / $n ) as $l |
        . as $arr |
        range($l) | $arr[.*$n:.*$n+$n]
      ;
      
      # Get all seed ranges
      input | [ match("\\d+"; "g").string | tonumber ] | [group_of(2)] as $seeds |
      
      # Collect maps
      reduce inputs as $line ({};
        if $line == "" then
          .
        elif $line | test(":") then
          .k = ( $line / " " | .[0] )
        else
          .[.k] += [[ $line | match("\\d+"; "g").string | tonumber ]]
        end
      )
      
      # For each map, apply transformation to all seeds ranges.
      # Producing new seed ranges if applicable
      # seed -> ... -> location
      | reduce (to_entries[] | select(.key != "k") .value) as $map ({s:$seeds};
        .s |= [
          # Only attempt transform if seed range and map range instersect
          .[] | [.[0], add, .[1] ] as [$ea, $eb, $el] | [
            $map[] | select(.[1:] | [.[0], add ] as [$sa,$sb] |
              ( $ea >= $sa and $ea$sb ) or
              ( $eb >= $sa and $eb$sb ) or
              ( $sa >= $ea and $sa$eb )
            )
          ] as $range |
          if $range | length > 0 then
            $range[0] as [$d,$s,$l] |
            # ( only end ) inside map range
            if $ea$s and $eb$s + $l then
              [$ea, $s - $ea], [$d, $eb - $s ]
            # ( both start, end ) outside map range
            elif $ea$s then
              [$ea, $s - $ea], [$d, $l], [ $s + $l, $eb ]
            # ( only start ) inside map range
            elif $eb$s + $l then
              [$ea + $d - $s, $l - $ea + $s ], [$s + $l, $eb - $s - $l]
            # ( both start, end ) inside map range
            else
              [$ea + $d - $s , $el]
            end
          else
            .
          end
        ]
      )
      
      # Get lowest location
      | [.s[][0]] | min
      

      Some comments:

      • Since iterating across all seeds naively would take forever, iterating over seed ranges instead.
      • It’s nice that JQ can neatly produce extra elements: [1,2,3] | [ .[] | if . == 2 then . * 10 + 1 , . * 10 + 2 else . end ] -> [1, 21, 22, 3]
      • There is probably a more compact way of expressing all the conditions and produced outputs.

      Replaced less-than (and greater-than for symmetry) symbols with full-width version, since lemmy apparently doesn’t handle them well within a code block: replacing less than with <

      • @geriksonOP
        link
        English
        37 months ago

        Part 2 is a classic AoC move, where suddenly the problem space becomes much larger.

      • @swlabr
        link
        English
        27 months ago

        JQ looks like magic. So short! So clean! What’s the catch?

        • @zogwarg
          link
          English
          37 months ago

          The main catch is it would often be faster to use a “real” programming langage ^^, both in writing the code, and in execution time for some loop heavy examples: equivalent code that completes say in 1 second in python, completing in 1 minute in jq. Also missing a way to call native libraries, to do stuff like say “md5” (relevant) in past years advents-of-code.

          That being said i like the general “pipe”, map-reduce feel of the language. Like bash one-liners It can make for very terse implementations. I like to add comments, and indentation to make it readable though.

          • @swlabr
            link
            English
            17 months ago

            Thanks for the insight!