Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/perl
- use strict;
- use warnings;
- use feature qw(say);
- $| = 1;
- # For better readability of from_end structure array:
- use constant POS => 0;
- use constant TIME => 1;
- # Read grid:
- my @Grid = map { [split(//)] } <>;
- sub grid_at ($) { my $p = shift; return ($Grid[$p->[0]][$p->[1]]) }
- sub as_str ($) { my $p = shift; return (join($;, @$p)) }
- # Vector stuff:
- my @Dirs = ([0,1], [1,0], [0,-1], [-1,0]);
- sub vec_sum ($$) { my ($v,$w) = @_; return [$v->[0] + $w->[0], $v->[1] + $w->[1]] }
- sub dist ($$) { my ($v,$w) = @_; return (abs($v->[0] - $w->[0]) + abs($v->[1] - $w->[1])) }
- # Find start and end:
- my ($start, $end);
- foreach my $y (0 .. $#Grid) {
- foreach my $x (0 .. $Grid[0]->$#*) {
- $start = [$y,$x] if ($Grid[$y][$x] eq 'S');
- $end = [$y,$x] if ($Grid[$y][$x] eq 'E');
- }
- }
- # BFS walk to get distance to end of race:
- my %from_end;
- my @queue = ([$end, 0]);
- QUEUE:
- while (my $state = shift @queue) {
- my ($pos, $time) = @$state;
- next QUEUE if (exists $from_end{as_str($pos)});
- $from_end{as_str($pos)} = [$pos, $time];
- foreach my $dir (@Dirs) {
- my $move = &vec_sum( $pos, $dir );
- push( @queue, [$move, $time + 1] ) if (grid_at($move) ne '#');
- }
- }
- my $part1 = 0;
- my $part2 = 0;
- # Look for cheats between maze points, by taking distance between pairs to resrict range.
- my @pos = keys %from_end;
- foreach (my $i = 1; $i < @pos; $i++) {
- print ::stderr "[$i/$#pos] $part2\r" if ($i % 100 == 0);
- my $ipos = $from_end{$pos[$i]}[POS];
- my $itime = $from_end{$pos[$i]}[TIME];
- foreach (my $j = 0; $j < $i; $j++) {
- my $dist = &dist( $ipos, $from_end{$pos[$j]}[POS] );
- next if !(2 <= $dist <= 20);
- my $save = abs( $itime - $from_end{$pos[$j]}[TIME] ) - $dist;
- if ($save >= 100) {
- $part2++;
- $part1++ if ($dist == 2);
- }
- }
- }
- say "\nPart 1: $part1";
- say "Part 2: $part2";
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement