#!/usr/bin/perl use v5.35.0; # Parse input map my ($width, @edge, @obstacle, @obstacles, $start, $facing); my $pos = 0; while (<>) { $width ||= length; for (split //) { $edge[$pos] = /\s/; if (/#/) { $obstacle[$pos] = 1; push @obstacles, $pos; } if ((my $i = index('^>v<', $_)) >= 0) { $start = $pos; $facing = $i; } $pos++; } } my @step = (-$width, 1, $width, -1); # Walk the guard's route $pos = $start; my $dir = $facing; my %visited = (); for (;;) { $pos += $step[$dir]; # Advance last unless 0 <= $pos < @edge; # Escape last if $edge[$pos]; if ($obstacle[$pos]) { # Obstacle? $pos -= $step[$dir]; # Retreat $dir++, $dir &= 3 # Turn right } $visited{$pos} = 1; # Mark route } # Try placing a single obstacle on each step of that route # and count how many times that makes the guard get stuck my $loops = 0; my @hit = (0) x @edge; for my $proposed (keys %visited) { $obstacle[$proposed] = 1; my $pos = $start; my $dir = $facing; my $mark = 1 << $dir; for (;;) { $pos += $step[$dir]; # Advance last unless 0 <= $pos < @edge; # Escape last if $edge[$pos]; if ($obstacle[$pos]) { # If this obstacle has already been hit # from this direction, that's a loop if ($hit[$pos] & $mark) { ++$loops; last; } $hit[$pos] |= $mark; # Record hit $pos -= $step[$dir]; # Retreat $dir++, $dir &= 3; # Turn right $mark = 1 << $dir; } } # Clean up for next proposed obstacle $obstacle[$proposed] = 0; $hit[$_] = 0 for ($proposed, @obstacles); } say $loops;