zogwarg

joined 1 year ago
[–] zogwarg@awful.systems 13 points 10 months ago* (last edited 10 months ago) (3 children)

No empire was ever born before 600 BCE (no no, the egyptians don't count), and no clear works of fiction, intended mostly to be dessiminated thorugh text ever existed before the 11th Century (You can dismiss anything prior as mere works of philosophy, poetry or mythology)

[–] zogwarg@awful.systems 3 points 11 months ago

Happy holidays!

[–] zogwarg@awful.systems 3 points 11 months ago

Only solved by receving heavy hints from other's solution, and it still took me forever. By far the hardest this year.

[–] zogwarg@awful.systems 3 points 11 months ago

[Language: jq]https://github.com/zogwarg/advent-of-code/blob/main/2023/jq/20-b.jq

Completed when waiting for the second leg of my Christmas holidays flight. (It was a long wait, can I blame jet-lag?).

Have a more compact implementation of LCM/GCD, something tells me it will come in handy In future editions. (I’ve also progressively been doing past years)

[–] zogwarg@awful.systems 3 points 11 months ago (1 children)

Replying in OP: Yeah, Lemmy punishes old threads/posts a bit too much for my taste ^^.

[–] zogwarg@awful.systems 2 points 11 months ago* (last edited 11 months ago) (2 children)

Day 18: Lavaduct Lagoon

[Language: jq]https://github.com/zogwarg/advent-of-code/blob/main/2023/jq/18-b.jq

Satisfyingly short (in lines, not in time writing) some of the longer part is hexadecimal parsing, that doesn't come natively in JQ, I started doing polygon math from part 1, and what took me the longest was properly handling the area contributed by the perimeter. (I toyed with trying very annoying things like computing the outmost vertex at each turn, which is complicated by the fact that you don't initially know which way the digger is turning, and needing previous and next point to disambiguate).

#!/usr/bin/env jq -n -R -f

reduce (
  # Produce stream of the vertices, for the position of the center
  foreach (
    # From hexadecimal representation
    # Get inputs as stream of directions = ["R", 5]
    inputs | scan("#(.+)\\)") | .[0] / ""
    | map(
        if tonumber? // false then tonumber
        else {"a":10,"b":11,"c":12,"d":13,"e":14,"f":15}[.] end
      )
    | [["R","D","L","U"][.[-1]], .[:-1]]
    | .[1] |= (
      # Convert base-16 array to numeric value.
      .[0] * pow(16;4) +
      .[1] * pow(16;3) +
      .[2] * pow(16;2) +
      .[3] * 16 +
      .[4]
    )
  ) as $dir ([0,0];
    if   $dir[0] == "R" then .[0] += $dir[1]
    elif $dir[0] == "D" then .[1] += $dir[1]
    elif $dir[0] == "L" then .[0] -= $dir[1]
    elif $dir[0] == "U" then .[1] -= $dir[1]
    end
  )
  # Add up total area enclosed by path of center
  # And up the are of the perimeter, perimeter * 1/2 + 1
) as [$x, $y] ( #
  {prev: [0,0], area: 0, perimeter_area: 1  };

  # Adds positve rectangles
  # Removes negative rectangles
  .area += ( $x - .prev[0] ) * $y |

  # Either Δx or Δy is 0, so this is safe
  .perimeter_area += (($x - .prev[0]) + ($y - .prev[1]) | abs) / 2 |

  # Keep current position for next vertex
  .prev = [$x, $y]
)

# Output total area
| ( .area | abs ) + .perimeter_area

Day 19: Aplenty

[Language: jq]https://github.com/zogwarg/advent-of-code/blob/main/2023/jq/19-b.jq

Satisfyingly very well suited to JQ once you are used to the stream, foreach(init; mod; extract) and recurse(exp) [where every output item of exp as a stream is fed back into recurse] operators. It's a different way of coding but has a certain elegance IMO. This was actually quick to implement, along with re-using the treating a range as a primitive approach of the seeds-to-soil day.

#!/usr/bin/env jq -n -sR -f

inputs / "\n\n"

# Parse rules
| .[0] / "\n"
| .[] |= (
  scan("(.+){(.+)}")
  | .[1] |= (. / ",")
  | .[1][] |= capture("^((?<reg>.)(?<op>[^\\d]+)(?<num>\\d+):)?(?<to>[a-zA-Z]+)$")
  | ( .[1][].num | strings ) |= tonumber
  | {key: .[0], value: (.[1]) }
) | from_entries as $rules |

# Split part ranges into new ranges
def split_parts($part; $rule_seq):
  # For each rule in the sequence
  foreach $rule_seq[] as $r (
    # INIT = full range
    {f:$part};

    # OPERATE =
    # Adjust parts being sent forward to next rule
    if $r.reg == null then
      .out = [ .f , $r.to ]
    elif $r.op == "<" and .f[$r.reg][0] < $r.num then
      ([ .f[$r.reg][1], $r.num - 1] | min ) as $split |
      .out = [(.f | .[$r.reg][1] |= $split ), $r.to ] |
      .f[$r.reg][0] |= ($split + 1)
    elif $r.op == ">" and .f[$r.reg][1] > $r.num then
      ([ .f[$r.reg][0], $r.num + 1] | max ) as $split |
      .out = [(.f | .[$r.reg][0] |= $split), $r.to ]  |
      .f[$r.reg][1] |= ($split - 1)
    end;

    # EXTRACT = parts sent to other nodes
    # for recursion call
    .out | select(all(.[0][]; .[0] < .[1]))
  )
;

[ # Start with full range of possible sings in input = "in"
  [ {x:[1,4000],m:[1,4000],a:[1,4000],s:[1,4000]} , "in" ] |

  # Recusively split musical parts, into new ranges objects
  recurse(
    if .[1] == "R" or .[1] == "A" then
      # Stop recursion if "Rejected" or "Accepted"
      empty
    else
      # Recursively split
      split_parts(.[0];$rules[.[1]])
    end
    # Keep only part ranges in "Accepted" state
  ) | select(.[1] == "A") | .[0]

  # Total number if parts in each object is the product of the ranges
  | ( 1 + .x[1] - .x[0] ) *
    ( 1 + .m[1] - .m[0] ) *
    ( 1 + .a[1] - .a[0] ) *
    ( 1 + .s[1] - .s[0] )
  # Sum total number of possibly accepted musical parts
] | add

EDIT: Less-thans and greater-thans replaced by fullwidth version, because lemmy is a hungry little goblin.

[–] zogwarg@awful.systems 3 points 11 months ago (1 children)

18The beauty is you don't need to keep track of the corners at all: ultimately the area contributed by the perimeter is ( 1/2 * perimeter ) + 1. The short justification is that is if was just ( 1/2 * perimeter ), for every inside corners you overcount by 1/4 and for every outside corner you undercount. And there is exactly 4 more outside corners that inside ones, always. You can justify that by having an arrow follow the eddges, utlmately the arrow must make 1 full turn, each outside corner adds 1/4 turn. each inside corner removes 1/4 turn.

[–] zogwarg@awful.systems 2 points 11 months ago (1 children)

Day 17: Clumsy Crucible

Intimidating at first, so I went shopping for christmas presents first, which engaged my brain juices.

[Language: jq]https://github.com/zogwarg/advent-of-code/blob/main/2023/jq/17-b.jq

In hindsight this is is searching for the shortest path in a graph, making sure that each square is actually two nodes, for when approached vertically or horizontally. My shcool days are distant now, and i wonder how far from optimal my implementation is ^^.

Part two was only a small edit from part one for me, and the code actually ran faster! from ~45s -> ~20s.

[–] zogwarg@awful.systems 2 points 11 months ago (1 children)

16 a,bNeat!

In my case it was a lot more of headbanging, the traverse function i wrote for part a was way to slow, since JQ isn't happy with loop-heavy assignments (if not buried within C-implemented builtins). Part a completed in ~2seconds, which was never going to do (in hindsight it would have taken me less time to simply let it run slowly), I had to optimize it so that the beams don't step one square at a time, but shoot straight to any obstacle.

It took me waaaay too long to troubleshoot it into something that actually worked. I'm sure there's a compact implementation out there, but my part b ended up looking very meaty (and still took ~30s to run): https://github.com/zogwarg/advent-of-code/blob/main/2023/jq/16-b.jq

[–] zogwarg@awful.systems 9 points 11 months ago* (last edited 11 months ago) (1 children)

Vigorous mask-dropping very early on in the post:

The term "eugenics" has absorbed so much baggage over the last century that it somehow refers both to swiping right on Tinder when you see an attractive person and to the holocaust.

Not all dating is done with reproduction in mind. What are members of the opposite, or indeed same gender: baby synthesis apparatus? Unless you go out of your way in selecting blue eyed, blond haired people, restricting the definition of beautiful to these people, and restricting the teleology of tinder to the begetting progeny, how is it even remotely eugenics?

EDIT: Uncharacteristically for LW the post, was very short short, "very early" is actually about midway in a proposal of little substance, also choosing attractive partners doesn't guarantee ensure children anyway (unless using very specific definitions of beauty).

[–] zogwarg@awful.systems 4 points 11 months ago

How about not fiddling with indices?

JQ Notfiddlingwithindexificationhttps://github.com/zogwarg/advent-of-code/blob/main/2023/jq/14-a.jq

#!/usr/bin/env jq -n -R -f

# Dish to grid
[ inputs / "" ]

# Tilt UP
| transpose                       # Transpose, for easier RE use
| map(                            #
  ("#" + add) | [                 # For each column,   replace '^' with '#'
    scan("#[O.]*") | [            # From '#' get empty spaces and 'O' rocks
      "#", scan("O"), scan("\\.") # Let gravity do it's work.
    ]                             #
  ] | add[1:]                     # Add groups back together
 )                                #
| transpose                       # Transpose back

# For each row, count  'O'  rocks
| map(add | [scan("O")] | length)

# Add total load on "N" beam
| [0] + reverse | to_entries
| map( .key * .value ) | add

Similarly tired with index fiddling, I was pretty happy with my approach, which led to satisfying transpose cancelling in part 2. Not the fastest code out there, but it works. Day 14 was actually my favorite one so far ^^.

[–] zogwarg@awful.systems 2 points 11 months ago* (last edited 11 months ago) (1 children)

a,bI took a very similar approach to parts a and b, with the difference that i was too lazy to do titling in each direction, and wanted to abuse regex so Instead i always titled up and rotated, which given my method of tilting up and rotating had some satisfying cancelling of transpose operations: https://github.com/zogwarg/advent-of-code/blob/main/2023/jq/14-b.jq

# Relevant portion
# oneCycle expects an array, of array of chars (3x3 eg: [[".","#","."],[".",".","."],["O",".","#"]])
def oneCycle:
  # Tilt UP          = T . MAP(scan) . T
  # Rotate           = T . MAP(reverse)
  # Titl UP . Rotate = T . MAP(scan) . Map(reverse) | T . T = Identity
  def tilt_up_rotate:
      transpose          # Gets subgroups    # Within each group,
                         # starring with "#" # In order 1 "#", x "O", y "." 
    | map( ("#" + add) | [ scan("#[^#]*")    | ["#", scan("O"), scan("\\.")] ] | add[1:])
    | map(reverse)
  ;
  # Tilt   North,            West,           South,            East
  tilt_up_rotate | tilt_up_rotate | tilt_up_rotate | tilt_up_rotate
;

JQ does allow some nice sortcuts sometimes, again transpose is nice to have.

view more: ‹ prev next ›