current difficulties
- Day 21 - Keypad Conundrum: 01h01m23s
- Day 17 - Chronospatial Computer: 44m39s
- Day 15 - Warehouse Woes: 30m00s
- Day 12 - Garden Groups: 17m42s
- Day 20 - Race Condition: 15m58s
- Day 14 - Restroom Redoubt: 15m48s
- Day 09 - Disk Fragmenter: 14m05s
- Day 16 - Reindeer Maze: 13m47s
- Day 22 - Monkey Market: 12m15s
- Day 13 - Claw Contraption: 11m04s
- Day 06 - Guard Gallivant: 08m53s
- Day 08 - Resonant Collinearity: 07m12s
- Day 11 - Plutonian Pebbles: 06m24s
- Day 18 - RAM Run: 05m55s
- Day 04 - Ceres Search: 05m41s
- Day 23 - LAN Party: 05m07s
- Day 02 - Red Nosed Reports: 04m42s
- Day 10 - Hoof It: 04m14s
- Day 07 - Bridge Repair: 03m47s
- Day 05 - Print Queue: 03m43s
- Day 03 - Mull It Over: 03m22s
- Day 19 - Linen Layout: 03m16s
- Day 01 - Historian Hysteria: 02m31s
23!
Spoilerific
Got lucky on the max clique in part 2, my solution only works if there are at least 2 nodes in the clique, that only have the clique members as common neighbours.
Ended up reading wikipedia to lift one the Bron-Kerbosch methods:
#!/usr/bin/env jq -n -rR -f reduce ( inputs / "-" # Build connections dictionary # ) as [$a,$b] ({}; .[$a] += [$b] | .[$b] += [$a]) | . as $conn | # Allow Loose max clique check # if $ARGS.named.loose == true then # Only works if there is at least one pair in the max clique # # That only have the clique members in common. # [ # For pairs of connected nodes # ( $conn | keys[] ) as $a | $conn[$a][] as $b | select($a < $b) | # Get the list of nodes in common # [$a,$b] + ($conn[$a] - ($conn[$a]-$conn[$b])) | unique ] # From largest size find the first where all the nodes in common # # are interconnected -> all(connections ⋂ shared == shared) # | sort_by(-length) | first ( .[] | select( . as $cb | [ $cb[] as $c | ( [$c] + $conn[$c] | sort ) | ( . - ( . - $cb) ) | length ] | unique | length == 1 ) ) else # Do strict max clique check # # Example of loose failure: # 0-1 0-2 0-3 0-4 0-5 1-2 1-3 1-4 1-5 # 2-3 2-4 2-5 3-4 3-5 4-5 a-0 a-1 a-2 # a-3 b-2 b-3 b-4 b-5 c-0 c-1 c-4 c-5 def bron_kerbosch1($R; $P; $X; $cliques): if ($P|length) == 0 and ($X|length) == 0 then if ($R|length) > 2 then {cliques: ($cliques + [$R|sort])} end else reduce $P[] as $v ({$R,$P,$X,$cliques}; .cliques = bron_kerbosch1( .R - [$v] + [$v] ; # R ∪ {v} .P - (.P - $conn[$v]); # P ∩ neighbours(v) .X - (.X - $conn[$v]); # X ∩ neighbours(v) .cliques ) .cliques | .P = (.P - [$v]) | # P ∖ {v} .X = (.X - [$v] + [$v]) # X ∪ {v} ) end ; bron_kerbosch1([];$conn|keys;[];[]).cliques | max_by(length) end | join(",") # Output password
day 23
this is one of those days when it’s all about the right term to google right
23-2
Leaving something to run for 20-30 minutes expecting nothing and actually getting a valid and correct result: new positive feeling unlocked.
Now to find out how I was ideally supposed to solve it.
24! - Crossed Wires - Leaderboard time 01h01m13s (and a close personal time of 01h09m51s)
Spoilers
I liked this one! It was faster the solve part 2 semi-manually before doing it “programmaticly”, which feels fun.
Way too many lines follow (but gives the option to finding swaps “manually”):
#!/usr/bin/env jq -n -crR -f ( # If solving manually input need --arg swaps # Expected format --arg swaps 'n01-n02,n03-n04' # Trigger start with --arg swaps '0-0' if $ARGS.named.swaps then $ARGS.named.swaps | split(",") | map(split("-") | {(.[0]):.[1]}, {(.[1]):.[0]}) | add else {} end ) as $swaps | [ inputs | select(test("->")) / " " | del(.[3]) ] as $gates | [ # Defining Target Adder Circuit # def pad: "0\(.)"[-2:]; ( [ "x00", "AND", "y00", "c00" ], [ "x00", "XOR", "y00", "z00" ], ( (range(1;45)|pad) as $i | [ "x\($i)", "AND", "y\($i)", "c\($i)" ], [ "x\($i)", "XOR", "y\($i)", "a\($i)" ] ) ), ( ["a01", "AND", "c00", "e01"], ["a01", "XOR", "c00", "z01"], ( (range(2;45) | [. , . -1 | pad]) as [$i,$j] | ["a\($i)", "AND", "s\($j)", "e\($i)"], ["a\($i)", "XOR", "s\($j)", "z\($i)"] ) ), ( ( (range(1;44)|pad) as $i | ["c\($i)", "OR", "e\($i)", "s\($i)"] ), ["c44", "OR", "e44", "z45"] ) ] as $target_circuit | ( # Re-order xi XOR yi wires so that xi comes first # $gates | map(if .[0][0:1] == "y" then [.[2],.[1],.[0],.[3]] end) ) as $gates | # Find swaps, mode=0 is automatic, mode>0 is manual # def find_swaps($gates; $swaps; $mode): $gates as $old | # Swap output wires # ( $gates | map(.[3] |= ($swaps[.] // .)) ) as $gates | # First level: 'x0i AND y0i -> c0i' and 'x0i XOR y0i -> a0i' # # Get candidate wire dict F, with reverse dict R # ( [ $gates[] | select(.[0][0:1] == "x" ) | select(.[0:2] != ["x00", "XOR"] ) | if .[1] == "AND" then { "\(.[3])": "c\(.[0][1:])" } elif .[1] == "XOR" then { "\(.[3])": "a\(.[0][1:])" } else "Unexpected firt level op" | halt_error end ] | add ) as $F | ($F | with_entries({key:.value,value:.key})) as $R | # Replace input and output wires with candidates # ( [ $gates[] | map($F[.] // .) | if .[2] | test("c\\d") then [ .[2],.[1],.[0],.[3] ] end | if .[2] | test("a\\d") then [ .[2],.[1],.[0],.[3] ] end ] # Makes sure that when possible a0i comes 1st, then c0i # ) as $gates | # Second level: use info rich 'c0i OR e0i -> s0i' gates # # Get candidate wire dict S, with reverse dict T # ( [ $gates[] | select((.[0] | test("c\\d")) and .[1] == "OR" ) | {"\(.[2])": "e\(.[0][1:])"}, {"\(.[3])": "s\(.[0][1:])"} ] | add | with_entries(select(.key[0:1] != "z")) ) as $S | ($S | with_entries({key:.value,value:.key})) as $T | ( # Replace input and output wires with candidates # [ $gates[] | map($S[.] // .) ] | sort_by(.[0][0:1]!="x",.) ) as $gates | # Ensure "canonical" order # [ # Diff - our input gates only $gates - $target_circuit | .[] | [ . , map($R[.] // $T[.] // .) ] ] as $g | [ # Diff + target circuit only $target_circuit - $gates | .[] | [ . , map($R[.] // $T[.] // .) ] ] as $c | if $mode > 0 then # Manual mode print current difference # debug("gates", $g[], "target_circuit", $c[]) | if $gates == $target_circuit then $swaps | keys | join(",") # Output successful swaps # else "Difference remaining with target circuit!" | halt_error end else # Automatic mode, recursion end # if $gates == $target_circuit then $swaps | keys | join(",") # Output successful swaps # else [ first( # First case when only output wire is different first( [$g,$c|map(last)] | combinations | select(first[0:3] == last[0:3]) | map(last) | select(all(.[]; test("e\\d")|not)) | select(.[0] != .[1]) | { (.[0]): .[1], (.[1]): .[0] } ), # "Only" case where candidate a0i and c0i are in an # incorrect input location. # Might be more than one for other inputs. first( [ $g[] | select( ((.[0][0] | test("a\\d")) and .[0][1] == "OR") or ((.[0][0] | test("c\\d")) and .[0][1] == "XOR") ) | map(first) ] | if length != 2 then "More a0i-c0i swaps required" | halt_error end | map(last) | select(.[0] != .[1]) | { (.[0]): .[1], (.[1]): .[0] } ) ) ] as [$pair] | if $pair | not then "Unexpected pair match failure!" | halt_error else find_swaps($old; $pair+$swaps; 0) end end end ; find_swaps($gates;$swaps;$swaps|length)
It’s a wrap!
One of the easier years imho. Better than last year in any case.
I get the feeling that this is Eric’s way of saying goodbye, and that this might be the last year, but I might be wrong.
Puzzles by difficulty (leaderboard completion times)
- Day 21 - Keypad Conundrum: 01h01m23s
- Day 24 - Crossed Wires: 01h01m13s
- Day 17 - Chronospatial Computer: 44m39s
- Day 15 - Warehouse Woes: 30m00s
- Day 12 - Garden Groups: 17m42s
- Day 20 - Race Condition: 15m58s
- Day 14 - Restroom Redoubt: 15m48s
- Day 09 - Disk Fragmenter: 14m05s
- Day 16 - Reindeer Maze: 13m47s
- Day 22 - Monkey Market: 12m15s
- Day 13 - Claw Contraption: 11m04s
- Day 06 - Guard Gallivant: 08m53s
- Day 08 - Resonant Collinearity: 07m12s
- Day 11 - Plutonian Pebbles: 06m24s
- Day 18 - RAM Run: 05m55s
- Day 04 - Ceres Search: 05m41s
- Day 23 - LAN Party: 05m07s
- Day 25 - Code Chronicle: 04m43s
- Day 02 - Red Nosed Reports: 04m42s
- Day 10 - Hoof It: 04m14s
- Day 07 - Bridge Repair: 03m47s
- Day 05 - Print Queue: 03m43s
- Day 03 - Mull It Over: 03m22s
- Day 19 - Linen Layout: 03m16s
- Day 01 - Historian Hysteria: 02m31s