Have been mostly using jq for fun.
Day 1
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.
Day 2
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.
Day 3
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.