Advertisement
musifter

AoC 2021 day 19 (Perl)

Dec 19th, 2021 (edited)
1,581
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Perl 6.65 KB | None | 0 0
  1. #!/usr/bin/perl
  2.  
  3. use strict;
  4. use warnings;
  5.  
  6. use List::AllUtils  qw(sum max true pairwise all firstidx onlyval);
  7.  
  8. $; = ',';
  9. $/ = '';
  10.  
  11. # Table of rotations (maybe write code to build this instead?)
  12. my @Rot = ( [[ 1, 0, 0], [ 0, 1, 0], [ 0, 0, 1]],
  13.             [[ 1, 0, 0], [ 0, 0, 1], [ 0,-1, 0]],
  14.             [[ 1, 0, 0], [ 0,-1, 0], [ 0, 0,-1]],
  15.             [[ 1, 0, 0], [ 0, 0,-1], [ 0, 1, 0]],
  16.  
  17.             [[-1, 0, 0], [ 0, 0, 1], [ 0, 1, 0]],
  18.             [[-1, 0, 0], [ 0, 1, 0], [ 0, 0,-1]],
  19.             [[-1, 0, 0], [ 0, 0,-1], [ 0,-1, 0]],
  20.             [[-1, 0, 0], [ 0,-1, 0], [ 0, 0, 1]],
  21.  
  22.             [[ 0, 1, 0], [ 0, 0, 1], [ 1, 0, 0]],
  23.             [[ 0, 1, 0], [ 1, 0, 0], [ 0, 0,-1]],
  24.             [[ 0, 1, 0], [ 0, 0,-1], [-1, 0, 0]],
  25.             [[ 0, 1, 0], [-1, 0, 0], [ 0, 0, 1]],
  26.  
  27.             [[ 0,-1, 0], [ 1, 0, 0], [ 0, 0, 1]],
  28.             [[ 0,-1, 0], [ 0, 0, 1], [-1, 0, 0]],
  29.             [[ 0,-1, 0], [-1, 0, 0], [ 0, 0,-1]],
  30.             [[ 0,-1, 0], [ 0, 0,-1], [ 1, 0, 0]],
  31.  
  32.             [[ 0, 0, 1], [ 1, 0, 0], [ 0, 1, 0]],
  33.             [[ 0, 0, 1], [ 0, 1, 0], [-1, 0, 0]],
  34.             [[ 0, 0, 1], [-1, 0, 0], [ 0,-1, 0]],
  35.             [[ 0, 0, 1], [ 0,-1, 0], [ 1, 0, 0]],
  36.  
  37.             [[ 0, 0,-1], [ 0, 1, 0], [ 1, 0, 0]],
  38.             [[ 0, 0,-1], [ 1, 0, 0], [ 0,-1, 0]],
  39.             [[ 0, 0,-1], [ 0,-1, 0], [-1, 0, 0]],
  40.             [[ 0, 0,-1], [-1, 0, 0], [ 0, 1, 0]] );
  41.  
  42. #
  43. # Vector and matrix arithmetic functions
  44. #
  45. sub vecmatrix_mult (\@\@) {
  46.     my ($vec, $mat) = @_;
  47.     return ([map { sum pairwise { $a * $b } @$vec, @$_ } @$mat]);
  48. }
  49.  
  50. sub vec_add (\@\@) {
  51.     my ($v, $w) = @_;
  52.     return ([pairwise { $a + $b } @$v, @$w]);
  53. }
  54.  
  55. sub vec_subtract (\@\@) {
  56.     my ($v, $w) = @_;
  57.     return ([pairwise { $a - $b } @$v, @$w]);
  58. }
  59.  
  60. sub vec_equal (\@\@) {
  61.     my ($v, $w) = @_;
  62.     return (@$v == sum pairwise { $a == $b } @$v, @$w);
  63. }
  64.  
  65. sub grid_distance (\@\@) {
  66.     my ($p, $q) = @_;
  67.     return (sum pairwise { abs( $a - $b ) } @$p, @$q);
  68. }
  69.  
  70. sub euclid_distance (\@\@) {
  71.     my ($p, $q) = @_;
  72.     return (sum pairwise { ($a - $b) ** 2 } @$p, @$q);
  73. }
  74.  
  75.  
  76. #
  77. #  Mainline: Read in data
  78. #
  79. my @Scan;           # Array of vectors, scanners -> beacon vectors
  80.  
  81. while (<>) {
  82.     my @lines = split( /\n/, $_ );
  83.  
  84.     my $head = shift( @lines );
  85.     my ($i) = ($head =~ m#scanner (\d+)#);
  86.  
  87.     $Scan[$i]->@* = map { [split /,/] } @lines;
  88. }
  89.  
  90. #  Build table to use square-of-Euclidean-distances as a hash to detect overlaps
  91. #
  92. #  Note: Test and my input have unique distances for each pair in a scanner block,
  93. #        so we're going to assume that and just use a pair instead of a list of pairs.
  94. #
  95. my @Dist_hash;      # Array of hash of array pair, scanner -> distance -> indices of beacons
  96.  
  97. foreach my $s (0 .. $#Scan) {
  98.     foreach my $i (0 .. $Scan[$s]->$#* - 1) {
  99.         foreach my $j ($i + 1 .. $Scan[$s]->$#*) {
  100.             next if ($i == $j);
  101.  
  102.             my $d = &euclid_distance( $Scan[$s][$i], $Scan[$s][$j] );
  103.  
  104.             $Dist_hash[$s]{$d} = [$i,$j];
  105.         }
  106.     }
  107. }
  108.  
  109. #  Build Graph
  110. #
  111. #  Overlaps have at least triangle(11) equal distances.  One pair of scanners in my data
  112. #  has 67 equal distances (each scanner has a match not part of the complete K12 graph).
  113. #  This is the only non-triangular number of counts in my input.  This suggests that it
  114. #  might be a monkey wrench intentionally thrown in by the input generator.  We'll deal
  115. #  this that when we actually build mappings and can see what's not in K12.
  116. #
  117. my @Graph;          # Array of lists, scanner -> list of overlapping scanners (symmetric)
  118.  
  119. foreach my $s (0 .. $#Scan - 1) {
  120.     foreach my $t ($s + 1 .. $#Scan) {
  121.         my $count = true {exists $Dist_hash[$t]{$_}} keys %{$Dist_hash[$s]};
  122.  
  123.         if ($count >= 66) {    # triangle(11)
  124.             push( @{$Graph[$s]}, $t );
  125.             push( @{$Graph[$t]}, $s );
  126.         }
  127.     }
  128. }
  129.  
  130. #
  131. # Function to shift the frame of the beacons of scanner #t into the frame of scanner #s.
  132. # Returns position of scanner (ie origin of t in the frame of s)
  133. #
  134. sub frame_shift {
  135.     my ($s,$t) = @_;
  136.  
  137.     # Build map of scan-s beacon indices to scan-t beacon indices
  138.     #
  139.     # Start by building a sparse array mapping (scan-s indices, scan-t indices) to
  140.     # the count of the number of times those indices match on a distance. The valid
  141.     # mapping between the indices scan-s to scan-t is therefore the ones which equal 11.
  142.     my %map_table;
  143.     foreach my $sd (keys %{$Dist_hash[$s]}) {
  144.         foreach my $i (@{$Dist_hash[$s]{$sd}}) {
  145.             foreach my $j (@{$Dist_hash[$t]{$sd}}) {
  146.                 $map_table{$i,$j}++;
  147.             }
  148.         }
  149.     }
  150.  
  151.     # Condense sparse map
  152.     my %map = map {($map_table{$_} == 11) ? (split($;, $_)) : ()} keys %map_table;
  153.  
  154.     #
  155.     # Find rotation that maps points from t onto s
  156.     #
  157.     my @sidx = keys %map;
  158.  
  159.     # parallel arrays of beacons that are in both
  160.     my @spt = map { [ @{$Scan[$s][$_]} ]       } @sidx;
  161.     my @tpt = map { [ @{$Scan[$t][$map{$_}]} ] } @sidx;
  162.  
  163.     # find rotation by making pt 1 relative to pt 0, then trying them
  164.     my $srel = &vec_subtract( $spt[1], $spt[0] );
  165.     my $trel = &vec_subtract( $tpt[1], $tpt[0] );
  166.  
  167.     my $r = firstidx { &vec_equal( &vecmatrix_mult($trel, $_), $srel ) } @Rot;
  168.  
  169.     # transform is: get relative to t[0] by subtraction, mult to rotate, then add s[0] to shift
  170.     my $trans = sub { &vec_add( &vecmatrix_mult( &vec_subtract(shift, $tpt[0]), $Rot[$r] ), $spt[0]) };
  171.  
  172.     # Do transposition
  173.     foreach my $i (0 .. $Scan[$t]->$#*) {
  174.         # just copy coords from s that are already in t
  175.         my $si = onlyval { $map{$_} == $i } keys %map;
  176.  
  177.         $Scan[$t][$i] = (defined $si) ? $Scan[$s][$si] : &$trans( $Scan[$t][$i] );
  178.     }
  179.  
  180.     return (&$trans([0,0,0]));
  181. }
  182.  
  183. #
  184. # Finally, progressively merge scanners into the frame of scanner #0
  185. #
  186.  
  187. # Scanner positions in the frame of scanner #0
  188. my @Scan_pos = ([0,0,0]);
  189.  
  190. # Job queue to process merges, start by queuing up everything from scanner #0
  191. my @queue  = map { [0,$_] } @{$Graph[0]};
  192. my @merged = (1, (0) x ($#Scan - 1));
  193.  
  194. while (my $job = shift @queue) {
  195.     next if ($merged[$job->[1]]++);
  196.  
  197.     $Scan_pos[$job->[1]] = &frame_shift( @$job );
  198.  
  199.     # Push merging of neighbours of newly merged scanner into then queue
  200.     push( @queue, map { [$job->[1],$_] } @{$Graph[$job->[1]]} );
  201. }
  202.  
  203. # Build set of unique beacon coords
  204. my %Beacons = map {my $scan = $_; map { join($;, @$_) => 1 } @$scan} @Scan;
  205.  
  206. print "Part 1: ", scalar keys %Beacons, "\n";
  207.  
  208. # Find max distance between two scanners
  209. my $max_dist = max map {my $s = $_; map { &grid_distance($s, $_) } @Scan_pos} @Scan_pos;
  210.  
  211. print "Part 2: $max_dist\n";
  212.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement