Day 8: Haunted Wasteland
https://adventofcode.com/2023/day/8
Not so easy at least for part two.
spoiler
Do you remember high school math, like lowest common multiple, part 2 electric boogaloo.
https://adventofcode.com/2023/day/8
Not so easy at least for part two.
spoiler
Do you remember high school math, like lowest common multiple, part 2 electric boogaloo.
https://adventofcode.com/2023/day/6
Alternate spoiler name - for part 2
~~Do you remember highschool algebra?~~ Can you (or your compiler) remember highschool algebra fast enough to beat out a naïve implementation?
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.
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:
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:
[1,2,3] | [ .[] | if . == 2 then . * 10 + 1 , . * 10 + 2 else . end ]
-> [1, 21, 22, 3]
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 <
Back to a more straightfoward day, do they make them harder on the weekends?
Part 1
#!/usr/bin/env jq -n -R -f
[
inputs
# Split winning numbers | card
| split(" | ")
# Get numbers, remove game id
| .[] |= [ match("\\d+"; "g").string | tonumber ] | .[0] |= .[1:]
# Get score for each line
| .[1] - (.[1] - .[0]) | length | select(. > 0) | pow(2; . - 1)
]
# Output total score sum
| add
Very suited to JQ, extra trick learned using: [ match("\\d+"; "g").string | tonumber ]
as a parse all ints in line.
Part 2
#!/usr/bin/env jq -n -R -f
[
inputs
# Split winning numbers | card
| split(" | ")
# Get numbers, remove game id
| .[] |= [ match("\\d+"; "g").string | tonumber ] | .[0] |= .[1:]
# Set number of cards to 1, and further cards count
| .[1] - (.[1] - .[0]) | [ 1, length ]
]
| { cards: ., i: 0, l: length } | until (.i == .l;
# Get number for current card
.cards[.i][0] as $num
# Increase range of futher cards, by current number
| .cards[.i + range(.cards[.i][1]) + 1 ][0] += $num
| .i += 1
)
# Output total sum of cards
| [ .cards[][0] ] | add
Not too much of an edit compared to part one, being able to easily do operations on range of indices is convenient.
Have been mostly using jq for fun.
Part 1
#!/usr/bin/env jq -n -R -f
# Get and reduce every "pretty" line
reduce inputs as $line (
0;
# Add extracted number
. + ( $line / "" | [ .[] | tonumber? ] | [first * 10 , last] | add )
)
First part was easy, and very suited to jq
Part 2
#!/usr/bin/env jq -n -R -f
# Define string to num value map
{
"one": 1, "1": 1,
"two": 2, "2": 2,
"three": 3, "3": 3,
"four": 4, "4": 4,
"five": 5, "5": 5,
"six": 6, "6": 6,
"seven": 7, "7": 7,
"eight": 8, "8": 8,
"nine": 9, "9": 9
} as $to_num |
# Get and reduce every "pretty" line
reduce inputs as $line (
0;
. + (
$line |
# Try two capture two numbers
capture("(^.*?(?(one|two|three|four|five|six|seven|eight|nine|[1-9])).*(?(one|two|three|four|five|six|seven|eight|nine|[1-9])).*?$)?") |
# If no capture, get one number twice
if .f == "" then $line | capture("^.*?(?(one|two|three|four|five|six|seven|eight|nine|[1-9]))") | .l = .f else . end |
# Add extracted number
$to_num[.f] * 10 + $to_num[.l]
)
)
Second part was harder than expected, i had to resort to regex.
Part 1
#!/usr/bin/env jq -n -R -f
# For each game: Is 12 red cubes, 13 green cubes, and 14 blue cubes possible ?
# Line Format =
# Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green
[
# Splitting input game id and content
inputs / ": " |
# Saving id
(.[0] / " " | .[1] | tonumber ) as $id |
# Parsing game
.[1] / "; " | [
.[] / ", " | [ .[] / " " | {(.[1]): .[0] | tonumber} ] | add |
# Is given sample possible ?
.red <= 12 and .green <= 13 and .blue <= 14
] |
# If all samples possible, return id, else 0
if all then $id else 0 end
] |
# Return sum of all possible game ids
add
Not too much trickery in this example.
Part 2
#!/usr/bin/env jq -n -R -f
# Line Format =
# Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green
[
# Splitting input game id and content
inputs / ": " |
# Parsing game
.[1] / "; " |
[ .[] / ", " | [ .[] / " " | {(.[1]): .[0] | tonumber} ] | add ] |
# Getting minimum required mumber for each color,
# and computing the power
{
r: ([.[].red] | max),
g: ([.[].green] | max),
b: ([.[].blue] | max)
} | .r * .g * .b
] |
# Return sum of all powers
add
Satisifyingly straightfoward edit form part one.
Part 1
#!/usr/bin/env jq -n -R -f
# Getting input with padding, and padded width
[ "." + inputs + "." ] as $inputs | ( $inputs[0] | length ) as $w |
# Working with flattened string, convert all symbols to '#'
[
([range($w) | "."]|join("")), # Padding
$inputs[],
([range($w) | "."]|join("")) # Padding
] | join("") | gsub("[^0-9.]";"#") as $inputs |
reduce (
# Get all indices for symbols, in box pattern around symbols
$inputs | indices("#")[] |
. - $w -1 , . - $w , . - $w + 1 ,
. - 1 , empty , . + 1 ,
. + $w - 1 , . + $w , . + $w + 1
) as $i (
# Numbers containes bounding indices,
# of numbers bordering symbols
{numbers: []};
# Test if current index isn't included in any found number
def new_number($i): [ .numbers[] | .[0] <= $i and $i <= .[1] ] | any | not ;
# Make "number" as bounding indices, by extending left and right
def make_number($i):
{a: $i, b: ($i+1 )}
| until( $inputs[.a:.b] | test("^[^0-9]"); .a -= 1 )
| until( $inputs[.a:.b] | test("[^0-9]$"); .b += 1 )
| [ .a +1 , .b -1 ]
;
# Add numbers if bordering symbol and new
if ($inputs[$i:$i+1] | test("[0-9]")) and new_number($i) then .numbers += [ make_number($i) ] else . end
) |
# Output sum of all found numbers
[ .numbers[] | $inputs[.[0]:.[1]] | tonumber ] | add
Took More time than i expected, glad i had the idea early to search by the indices of the symbols and not the digits. Not super well suited to jq, unless I'm missing a better solution.
Part 2
#!/usr/bin/env jq -n -R -f
# Getting input with padding, and padded width
[ "." + inputs + "." ] as $inputs | ( $inputs[0] | length ) as $w |
# Working with flattened string, only keep gear '*' symbols
[
([range($w) | "."]|join("")), # Padding
$inputs[],
([range($w) | "."]|join("")) # Padding
] | join("") | gsub("[^0-9*]";".") as $inputs |
# Iterate over index positions of all gears
reduce ($inputs | indices("*")[]) as $i (
0;
# Re-use part-1 functions
def new_number($i):
[ .numbers[] | .[0] <= $i and $i <= .[1] ] | any | not
;
def make_number($i):
{a: $i, b: ($i+1 )}
| until( $inputs[.a:.b] | test("^[^0-9]"); .a -= 1 )
| until( $inputs[.a:.b] | test("[^0-9]$"); .b += 1 )
| [ .a +1 , .b -1 ]
;
# Reset and add numbers for each "box" ids
def add_numbers($box_idx):
reduce $box_idx[] as $i ({numbers:[]};
if ($inputs[$i:$i+1] | test("[0-9]")) and new_number($i) then
.numbers += [ make_number($i) ]
else
.
end
)
;
add_numbers([
$i - $w -1 , $i - $w , $i -$w + 1 ,
$i - 1 , empty , $i + 1 ,
$i + $w - 1, $i + $w , $i + $w + 1
]).numbers as $numbers |
if $numbers | length == 2 then
# Add product if exactly two bordering numbers
. += ( $numbers | map($inputs[.[0]:.[1]]|tonumber) | .[0] * .[1] )
else
.
end
)
Not too far of an edit from part one.
One (simpler) explanation is that proving an absence of something is almost impossible, and that attempting too hard would make them look a heck of a lot guilty.
There is a good reason why the burden of evidence is “innocent until proven guilty”, and yes this extends to the (in your eyes) untrustworthy.
Prove to me you never stole candy from a store as a child (or if you did, replace that accusation with any item of higher value until you hit something you did not steal)
One of the more disturbing things that happened at work when using MS Word, was the automatic addition of alt-text images. I didn't ask for that, I didn't click any "Please send my images to the cloud, possibly leaking sensitve material, so inference can be run there, to add potentially unhelpful descriptions"
Is document editing really a task that benefits from AI?
An example of unhelpfulness:
I'm torn between at almost praising meek half-assed attempt at accessibility, and shrieking to the heavens about this unweclome shoe-horned addition.
Either way it's a circus of incompetence.
Something something Poe's law, something something. Honestly some of the shit i've read should have been satire, but noooooo.
Absolutely this, shuf would easily come up in a normal google search (even in googles deteriorated relevancy).
For fun, "two" lines of bash + jq can easily achieve the result even without shuf
(yes I know this is pointlessly stupid)
cat /usr/share/dict/words | jq -R > words.json
cat /dev/urandom | od -A n -D | jq -r -n '
import "words" as $w;
($w | length) as $l |
label $out | foreach ( inputs * $l / 4294967295 | floor ) as $r (
{i:0,a:[]} ;
.i = (if .a[$r] then .i else .i + 1 end) | .a[$r] = true ;
if .i > 100 then break $out else $w[$r] end
)
'
Incidentally this is code that ChatGPT would be utterly incapable of producing, even as toy example but niche use of jq.
Cleaned up version of code used to solve part 2 in jq.
Spoiler code section