class Cart {
has $.state is rw;
has $.y is rw;
has $.x is rw;
has $.id;
has $!turn = 0;
method move-on (@map) {
given $.state {
when '>' { $.x++ }
when '<' { $.x-- }
when 'v' { $.y++ }
when '^' { $.y-- }
}
$.state = do given $.state, @map[$.y; $.x] {
when '>', '/' { '^' }
when '>', '\\' { 'v' }
when '>', '-' { $.state }
when '>', '+' { ('^', $.state, 'v')[$!turn++ % 3] }
when '<', '/' { 'v' }
when '<', '\\' { '^' }
when '<', '-' { $.state }
when '<', '+' { ('v', $.state, '^')[$!turn++ % 3] }
when 'v', '/' { '<' }
when 'v', '\\' { '>' }
when 'v', '|' { $.state }
when 'v', '+' { ('>', $.state, '<')[$!turn++ % 3] }
when '^', '/' { '>' }
when '^', '\\' { '<' }
when '^', '|' { $.state }
when '^', '+' { ('<', $.state, '>')[$!turn++ % 3] }
}
}
method yx { $.y, $.x }
}
my @map = $*IN.lines.map({ .comb.Array });
my %carts;
my $max-y = @map.elems;
my $max-x = max @map.map(*.elems);
for ^$max-x X ^$max-y -> ($x, $y) {
my $track = @map[$y; $x];
given $track {
when '>' | '<' { @map[$y; $x] = '-' }
when 'v' | '^' { @map[$y; $x] = '|' }
}
if $track ne @map[$y; $x] {
my $id = %carts.elems;
%carts{$id} = Cart.new(state => $track, :$x, :$y, :$id)
}
}
repeat {
for %carts.values.sort(*.yx) -> $cart {
next if %carts{$cart.id}:!exists;
$cart.move-on(@map);
my %positions = %carts.values.classify(*.yx.Str);
my @crashes = %positions.values.grep(*.elems > 1);
for @crashes -> @crashed-carts {
for @crashed-carts -> $crashed-cart {
say "crashed: {$crashed-cart.perl}";
%carts{$crashed-cart.id}:delete;
}
}
}
} while %carts.elems > 1;
say "survivor: {%carts{*}».perl}";