Advertisement
musifter

AoC 2023 day 22 (Perl)

Dec 22nd, 2023 (edited)
1,316
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Perl 2.76 KB | Source Code | 0 0
  1. #!/usr/bin/perl
  2.  
  3. use strict;
  4. use warnings;
  5.  
  6. use feature         qw(say);
  7. use List::AllUtils  qw(all max min uniqint);
  8.  
  9. $| = 1;
  10.  
  11. # Read in pieces:
  12. my @Piece;
  13. while (<>) {
  14.     my @ends = map {[map {int} m#(\d+)#g]} m#(.*)~(.*)#;
  15.  
  16.     my @delta = map {$ends[1][$_] - $ends[0][$_]} (0 .. 2);
  17.     my @step  = map {$ends[1][$_] <=> $ends[0][$_]} (0 .. 2);
  18.  
  19.     my $height = min( $ends[0][2], $ends[1][2] );
  20.     my $top = abs( $ends[1][2] - $ends[0][2] ) + 1;
  21.     my $len = max( @delta ) + 1;
  22.  
  23.     my @block;          # mask of piece cubes
  24.     my @bottom;         # (x,y) coordinates of cubes on the bottom layer
  25.  
  26.     my @pos = $ends[0]->@*;
  27.     $pos[2] -= $height;
  28.  
  29.     for (my $i = 0; $i < $len; $i++) {
  30.         push( @block, [@pos] );
  31.         push( @bottom, [$pos[0],$pos[1]] ) if ($pos[2] == 0);
  32.         @pos = map { $pos[$_] += $step[$_] } (0 .. 2);
  33.     }
  34.  
  35.     push( @Piece, {height => $height, block => \@block, bottom => \@bottom, top => $top} );
  36. }
  37.  
  38. # Sort pieces by height and drop to form tower and build support graph.
  39. @Piece = sort { $a->{height} <=> $b->{height} } @Piece;
  40.  
  41. my %Tower;
  42. my %Tops;
  43. my @Support;
  44. for (my $i = 0; $i < @Piece; $i++) {
  45.     my %piece = $Piece[$i]->%*;
  46.  
  47.     # Drop piece until it intersects the top of the tower:
  48.     my @intersect;
  49.     my $h = $piece{height};
  50.  
  51.     DROP:
  52.     while ($h > 0) {
  53.         @intersect = grep { $h <= ($Tops{join($;, @$_)} // 0) } $piece{bottom}->@*;
  54.         last DROP if (@intersect);
  55.         $h--;
  56.     }
  57.  
  58.     # Get supporing pieces and link in graph:
  59.     my @supp = uniqint map { $Tower{$_->[0],$_->[1],$h} } @intersect;
  60.  
  61.     push( $Support[$i]{below}->@*, @supp );
  62.     push( $Support[$_]{above}->@*, $i ) foreach (@supp);
  63.  
  64.     # Update (x,y) tops of the tower:
  65.     foreach my $xy ($piece{bottom}->@*) {
  66.         $Tops{join($;,@$xy)} = $h + $piece{top};
  67.     }
  68.  
  69.     # Add piece to Tower (coord => piece id).
  70.     foreach my $cube ($piece{block}->@*) {
  71.         $Tower{ $cube->[0], $cube->[1], $cube->[2] + $h + 1 } = $i;
  72.     }
  73. }
  74.  
  75. # Use support graph to get answers for part 1 and 2:
  76. my $part1 = 0;
  77. my $part2 = 0;
  78. for (my $i = 0; $i < @Piece; $i++) {
  79.     print ::stderr "Piece: $i / $#Piece\r";
  80.     if (!defined $Support[$i]{above} or (all {$Support[$_]{below}->@* > 1} $Support[$i]{above}->@*)) {
  81.         $part1++;
  82.         next;
  83.     }
  84.  
  85.     # Brute forcing part 2 with a queue:
  86.     my %falls = ($i => 1);
  87.     my @queue = $Support[$i]{above}->@*;
  88.  
  89.     while (my $p = shift @queue) {
  90.         if (all { $falls{$_} } $Support[$p]{below}->@*) {
  91.             $falls{$p} = 1;
  92.             push( @queue, $Support[$p]{above}->@* ) if (defined $Support[$p]{above});
  93.         }
  94.     }
  95.  
  96.     $part2 += %falls - 1;       # -1 for the block we removed
  97. }
  98.  
  99. say "\nPart 1: $part1";
  100. say "Part 2: $part2";
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement