Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/perl
- use v5.26;
- use warnings;
- use List::AllUtils qw(first firstidx);
- use Math::Vector::Real;
- # Set up unit vector basis and directions
- my ($vy,$vx) = Math::Vector::Real->canonical_base(2);
- my %Dirs = (S => $vy, E => $vx, N => -$vy, W => -$vx);
- my %Back = (S => 'N', E => 'W', N => 'S', W => 'E');
- # Hash of pipes to directions of travel with a list of adjacent RHS dirs
- my %Pipe = (
- '|' => {S => [ $vx], N => [-$vx]},
- '-' => {W => [ $vy], E => [-$vy]},
- 'L' => {N => [-$vx, $vy ], E => []},
- 'J' => {W => [ $vx, $vy ], N => []},
- 'F' => {E => [-$vx, -$vy ], S => []},
- '7' => {S => [ $vx, -$vy ], W => []},
- );
- # Hash from ends to type of piece
- my %Piece = ('NS' => '|', 'EW' => '-', 'EN' => 'L', 'NW' => 'J', 'ES' => 'F', 'SW' => '7');
- # Read in grid, adding sentinels to right and bottom
- my @Grid = map { chomp; [split(//), '.', '~'] } <>;
- push( @Grid, [(('.') x $Grid[0]->$#*), '~'] );
- push( @Grid, [('~') x $Grid[0]->@*] );
- # Helper to handle ugly access between Vector package in arrays
- sub grid_at ($) { my $p = shift; return ($Grid[$p->[0]][$p->[1]]) }
- my %path; # pipe path (don't need order, so hash)
- # Find starting location
- my $pos;
- foreach my $y (0 .. @Grid - 1) {
- my $x = firstidx { $_ eq 'S' } $Grid[$y]->@*;
- if ($x != -1) {
- $pos = V($y,$x);
- $path{$pos}++;
- last;
- }
- }
- # Figure out starting tile
- my @ends;
- for my $d (keys %Dirs) {
- my $move = $pos + $Dirs{$d};
- push( @ends, $d ) if (exists $Pipe{ grid_at($move) }{ $Back{$d} });
- }
- my $steps = 0;
- my $tile = $Piece{join('', sort @ends)}; # get starting tile from its ends
- my $come_from = $ends[0]; # take the first direction and go!
- my @rhs; # track squares on RHS of our path
- # Walk the path, keeping track of RHS and where we've been
- while ($tile ne 'S') {
- push( @rhs, map { $pos + $_ } $Pipe{$tile}{$come_from}->@* );
- my $move = first { $_ ne $come_from } keys $Pipe{$tile}->%*;
- $pos += $Dirs{$move};
- $path{$pos}++;
- $come_from = $Back{$move};
- $steps++;
- $tile = grid_at( $pos );
- }
- say "Part 1: ", $steps / 2;
- # Clear pipes not in cycle
- my $count = 0;
- foreach my $y (0 .. @Grid - 2) {
- foreach my $x (0 .. $Grid[0]->$#* - 1) {
- if (!exists $path{"{$y, $x}"}) {
- $Grid[$y][$x] = '.';
- $count++;
- }
- }
- }
- # Floodfill from the RHS of the path
- my $rhs_count = 0;
- foreach my $pt (@rhs) {
- next if (grid_at($pt) ne '.');
- $Grid[$pt->[0]][$pt->[1]] = '*';
- $rhs_count++;
- push( @rhs, map { $pt + $_ } values %Dirs );
- }
- # If RHS flowed into sentinels, then the inside is the LHS of path.
- say "Part 2: ", ($Grid[0][-2] eq '*') ? $count - $rhs_count : $rhs_count;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement