Advertisement
musifter

AoC 2024 day 20 (Perl)

Dec 20th, 2024 (edited)
67
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Perl 1.96 KB | Source Code | 0 0
  1. #!/usr/bin/perl
  2.  
  3. use strict;
  4. use warnings;
  5.  
  6. use feature         qw(say);
  7.  
  8. $| = 1;
  9.  
  10. # For better readability of from_end structure array:
  11. use constant POS  => 0;
  12. use constant TIME => 1;
  13.  
  14. # Read grid:
  15. my @Grid = map { [split(//)] } <>;
  16.  
  17. sub grid_at ($) { my $p = shift; return ($Grid[$p->[0]][$p->[1]]) }
  18. sub as_str ($)  { my $p = shift; return (join($;, @$p)) }
  19.  
  20. # Vector stuff:
  21. my @Dirs = ([0,1], [1,0], [0,-1], [-1,0]);
  22. sub vec_sum ($$) { my ($v,$w) = @_; return [$v->[0] + $w->[0], $v->[1] + $w->[1]] }
  23. sub dist ($$)    { my ($v,$w) = @_; return (abs($v->[0] - $w->[0]) + abs($v->[1] - $w->[1])) }
  24.  
  25. # Find start and end:
  26. my ($start, $end);
  27. foreach my $y (0 .. $#Grid) {
  28.     foreach my $x (0 .. $Grid[0]->$#*) {
  29.         $start = [$y,$x]  if ($Grid[$y][$x] eq 'S');
  30.         $end   = [$y,$x]  if ($Grid[$y][$x] eq 'E');
  31.     }
  32. }
  33.  
  34. # BFS walk to get distance to end of race:
  35. my %from_end;
  36. my @queue = ([$end, 0]);
  37.  
  38. QUEUE:
  39. while (my $state = shift @queue) {
  40.     my ($pos, $time) = @$state;
  41.  
  42.     next QUEUE if (exists $from_end{as_str($pos)});
  43.     $from_end{as_str($pos)} = [$pos, $time];
  44.  
  45.     foreach my $dir (@Dirs) {
  46.         my $move = &vec_sum( $pos, $dir );
  47.         push( @queue, [$move, $time + 1] )  if (grid_at($move) ne '#');
  48.     }
  49. }
  50.  
  51. my $part1 = 0;
  52. my $part2 = 0;
  53.  
  54. # Look for cheats between maze points, by taking distance between pairs to resrict range.
  55. my @pos = keys %from_end;
  56.  
  57. foreach (my $i = 1; $i < @pos; $i++) {
  58.     print ::stderr "[$i/$#pos] $part2\r"  if ($i % 100 == 0);
  59.     my $ipos  = $from_end{$pos[$i]}[POS];
  60.     my $itime = $from_end{$pos[$i]}[TIME];
  61.  
  62.     foreach (my $j = 0; $j < $i; $j++) {
  63.         my $dist = &dist( $ipos, $from_end{$pos[$j]}[POS] );
  64.         next if !(2 <= $dist <= 20);
  65.  
  66.         my $save = abs( $itime - $from_end{$pos[$j]}[TIME] ) - $dist;
  67.         if ($save >= 100) {
  68.             $part2++;
  69.             $part1++ if ($dist == 2);
  70.         }
  71.     }
  72. }
  73.  
  74. say "\nPart 1: $part1";
  75. say "Part 2: $part2";
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement