ETOOBUSY 🚀 minimal blogging for the impatient
AoC 2022/20 - Merry go round
TL;DR
On with Advent of Code puzzle 20 from 2022: going round and round and round…
Another day, another solution that was pretty inefficient, so I will not be discussing it here.
While coding the solution, I knew that there was something I was missing. Then, when I looked at the others’ solutions, it was pretty clear what I had hiding in my mind: a double-linked list.
So this is my solution, re-written with the idea of a double-linked list in mind:
#!/usr/bin/env raku
use v6;
class CircularLinkedList { ... }
sub MAIN ($filename = $?FILE.subst(/\.raku$/, '.sample')) {
my $inputs = get-inputs($filename);
put do-part($inputs, 1);
put do-part(@$inputs «*» 811589153, 10);
}
sub do-part ($inputs, $reps) {
my $cll = CircularLinkedList.create(|$inputs);
my @list = $cll.linear;
for ^$reps {
.say;
$cll.automove($_) for @list;
}
return $cll.result;
}
sub get-inputs ($filename) { [ $filename.IO.lines».Int ] }
class CircularLinkedList {
my class LinkedListItem {
has $.value is required;
has $.main is readonly = False;
has $.pred is rw = Nil;
has $.succ is rw = Nil;
}
has $.first is rw = Nil;
has $.count is rw = 0;
method create (::?CLASS:U $class: *@values) {
my $l = $class.new;
@values.map: {$l.push($^value)};
return $l;
}
method push ($l) { # as last element
if ! $.count {
$.first = LinkedListItem.new(value => $l, main => True);
$.first.pred = $.first.succ = $.first;
$.count = 1;
return $.first;
}
my $item = LinkedListItem.new(value => $l, main => False,
succ => $.first, pred => $.first.pred);
$.first.pred.succ = $item;
$.first.pred = $item;
++$.count;
return $item;
}
method linear {
my $p = $.first;
return [ (^$.count).map({ my $r = $p; $p = $p.succ; $r }) ]
}
method automove ($item) {
my $amount = $item.value % ($.count - 1) or return self;
my $p = $item;
$p = $p.succ for ^$amount;
# "detach" $item from current position
$item.pred.succ = $item.succ;
$item.succ.pred = $item.pred;
$p.succ.pred = $item;
$item.succ = $p.succ;
$item.pred = $p;
$p.succ = $item;
return self;
}
method result {
my $zero = $.first;
$zero = $zero.succ while $zero.value != 0;
my $offset = 0;
my $sum = 0;
for 1000, 2000, 3000 -> $shift {
my $mods = $shift mod $.count;
my $p = $zero;
$p = $p.succ for ^$mods;
$sum += $p.value;
}
return $sum;
}
method printout { self.linear».value.say }
}
There’s not much more to say, apart that… it’s perfectly OK to end up
with any circular shift of the examples. The final result is
calculated with respect to the position of element 0
(which is
unique), so there’s no need to implement things so that the same exact
sequence is obtained (the relative circular arrangement must be
preserved, of course).
Stay safe!