18
The 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
Day 17: Clumsy Crucible
Intimidating at first, so I went shopping for christmas presents first, which engaged my brain juices.
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.
16 a,b
Neat!
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
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).
How about not fiddling with indices?
JQ Notfiddlingwithindexification
https://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 ^^.
a,b
I 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.
A nice workaround to jq single threadedness, since this is maq reduce and safe to parallelize. 17m10s -> 20s !!!
Spoiler link to commit.
https://github.com/zogwarg/advent-of-code/commit/fef153411fe0bfe0e7d5f2d07da80bcaa18c952c
Not really spoilery details: Revolves around spawing mutiple jq instances and filtering the inputs bassed on a modulo of number of instances:
# Option to run in parallel using xargs
# Eg: ( seq 0 9 | \
# xargs -P 10 -n 1 ./2023/jq/12-b.jq input.txt --argjson s 10 --argjson i \
# ) | jq -s add
# Execution time 17m10s -> 20s
if $ARGS.named.s and $ARGS.named.i then #
[inputs] | to_entries[] | select(.key % $ARGS.named.s == $ARGS.named.i) | .value / " "
else
inputs / " "
end
I use JQ at work, and never really needed this, i guess this trick is nice to have under the belt just in case.
Day 12: Hot springs
https://adventofcode.com/2023/day/12
- Leaderboard completion time: 22:57
- Personal completion time: ahahahahahahaha (at least i had fun)
Where a curse the fact I decided to use JQ and not a "real" programming language.
spoiler
Had to resort to memoization, but sadly JQ isn't mega well suited to that. I had to refactor my part 1 function, to make including the "state" at every function call possible. I wish it were as easy as a @cache
decorator, but i guess this way i had fun (for an arbitrary definition of "fun")
Further cleaned up version: https://github.com/zogwarg/advent-of-code/blob/main/2023/jq/12-b.jq
Also lost a fair amount of time not not noticing that the sequence should be joined with "?"
not with ""
. (that'll teach me to always run on the example before the full input, when execution time is super long).
Execution time: 17m10s (without memoization a single row was taking multiple minutes, and there's 1000 rows ^^...)
EDIT: see massive improvement by running in parallel in reply.
discussion
In retrospect that would have been far better for runtime, my dist function ended up being a tad expensive.
I substituted the rows/columns, with multiplication by the expansion rate if they were all numbers. And then for each galaxy pair do a running sum by going “down” the “right” and adding the distance for each row and column crossed.
https://github.com/zogwarg/advent-of-code/blob/main/2023/jq/11-b.jq
transpose
is nice to have in that approach.
Ah! Thanks for making my notice the GCM -> GCD typo. I'm not gunning for the leaderboards myself, it's pretty hopeless ^^. Yes i am assuming based off of experience and utility tools.
I myself have tools to automatically get the inputs, and submit outputs, but that's more because it pleases me than to actually be fast: https://github.com/zogwarg/advent-of-code/blob/main/functions.sh
(Also completely pointlessly have a functions to extract the session cookie from chrome storage from the CLI, despite being long-lived, and therefore much simpler to simply copy-paste from debugger window)
spoiler
Part 2 only, but Part 1 is very similar.
#!/usr/bin/env jq -n -R -f
[
# For each line, get numbers eg: [ [1,2,3] ]
inputs / " " | map(tonumber) | [ . ] |
# Until latest row is all zeroes
until (.[-1] | [ .[] == 0] | all;
. += [
# Add next row, where for element(i) = prev(i+1) - prev(i)
[ .[-1][1:] , .[-1][0:-1] ] | transpose | map(.[0] - .[1])
]
)
# Get extrapolated previous element for first row
| [ .[][0] ] | reverse | reduce .[] as $i (0; $i - . )
]
# Output sum of extapolations for all lines
| add
I'm pretty sure you could make this one line and unreadable ^^.
Day 18: Lavaduct Lagoon
[Language: jq]
https://github.com/zogwarg/advent-of-code/blob/main/2023/jq/18-b.jqSatisfyingly 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).
Day 19: Aplenty
[Language: jq]
https://github.com/zogwarg/advent-of-code/blob/main/2023/jq/19-b.jqSatisfyingly very well suited to JQ once you are used to the
stream
,foreach(init; mod; extract)
andrecurse(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.EDIT: Less-thans and greater-thans replaced by fullwidth version, because lemmy is a hungry little goblin.