--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.16;
+
+$; = ',';
+my %scans;
+my $scanner;
+my $first_scanner;
+while (<>) {
+ chomp;
+ if (/--- (scanner \d+) ---/) {
+ $scanner = $1;
+ $first_scanner //= $1;
+ } elsif (/^$/) {
+ next;
+ } else {
+ $scans{$scanner}->{$_} = 1;
+ }
+}
+
+my @perms = (
+ [ 0, 1, 2],
+ [ 0, 2, 1],
+ [ 1, 0, 2],
+ [ 1, 2, 0],
+ [ 2, 0, 1],
+ [ 2, 1, 0],
+);
+
+my @rotations;
+my %r_seen;
+for (0 .. 7) {
+ for my $p (@perms) {
+ my @r = @$p;
+ $r[3] = $_ & 0x01 ? -1 : 1;
+ $r[4] = $_ & 0x02 ? -1 : 1;
+ $r[5] = $_ & 0x04 ? -1 : 1;
+ push @rotations, \@r;
+ }
+}
+
+use Data::Dumper;
+say "rotations: ", scalar @rotations;
+
+sub transform {
+ my ($coords, $trans) = @_;
+ my @res;
+ for (0 .. $#$coords) {
+ $res[$_] = $coords->[$trans->[$_]];
+ $res[$_] *= -1 if $trans->[$_+@$coords] < 0;
+ }
+ return @res;
+}
+
+sub is_aligned {
+ my ($s1, $s2) = @_;
+ my $coords1 = $scans{$s1};
+ my $coords2 = $scans{$s2};
+
+ say "comparing $s1 and $s2";
+ for my $from_t (keys %$coords1) {
+ my @from = split /,/, $from_t;
+ for my $rot (@rotations) {
+ my @from_rotated = transform(\@from, $rot);
+ # say "rotated ", join(',', @from), ' to ', join(',', @from_rotated);
+ for my $to_t (keys %$coords2) {
+ my @off = split /,/, $to_t;
+ $off[$_] -= $from_rotated[$_] for 0..$#off;
+ # say "trying offset ", join(',', @off);
+ my $count = 0;
+ for my $c1_t (keys %$coords1) {
+ my @c1 = split /,/, $c1_t;
+ @c1 = transform(\@c1, $rot);
+ $c1[$_] += $off[$_] for 0..$#off;
+ # say "searching for ", join(',',@c1);
+ $count++ if $coords2->{join(',', @c1)};
+ }
+ if ($count >= 12) {
+ say "found $count matches ", join(',', @$rot);
+ return (\@off, $rot);
+ }
+ }
+ }
+ }
+}
+
+my %aligned_scanners = ($first_scanner => [ [ 0, 0, 0], [ 0, 1, 2, 1, 1, 1 ] ]);
+my %beacons = ( %{ $scans{$first_scanner} } );
+my %tested;
+while (scalar keys %aligned_scanners < scalar keys %scans) {
+ say scalar keys %beacons, ' beacons found:';
+ for my $s1 (grep { $aligned_scanners{$_} } keys %scans) {
+ for my $s2 (grep { !$aligned_scanners{$_} } keys %scans) {
+ next if $tested{$s1,$s2} || $tested{$s2,$s1};
+ my ($off, $rot) = is_aligned($s2, $s1);
+ $tested{$s1,$s2} = $tested{$s2,$s1} = 1;
+ next if !defined $rot;
+ my %nscans;
+ for my $c2_t (keys %{ $scans{$s2}}) {
+ my @c2 = split /,/, $c2_t;
+ @c2 = transform(\@c2, $rot);
+ $c2[$_] += $off->[$_] for 0 .. $#c2;
+ my $key = join(',', @c2);
+ $beacons{$key}++;
+ $nscans{$key}++;
+ }
+ $scans{$s2} = \%nscans;
+ $aligned_scanners{$s2} = 1;
+ say "$s1 and $s2 aligned: ", join(',', @$off, @$rot), " beacons: ", scalar keys %beacons;
+ }
+ }
+}
+
+say '=========';
+for my $b (sort keys %beacons) {
+ say $b;
+}
+say "total beacons: ", scalar keys %beacons;
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.16;
+
+$; = ',';
+
+sub transform {
+ my ($coords, $trans) = @_;
+ my @res;
+ for (0 .. $#$coords) {
+ $res[$_] = $coords->[$trans->[$_]];
+ $res[$_] *= -1 if $trans->[$_+@$coords] < 0;
+ }
+ return @res;
+}
+
+my %aligned_scanners = ('scanner 0' => [ 0, 0, 0 ]);
+
+# parsing the debugging output of the first part ...
+while (<>) {
+ my ($s1, $s2, $rest) = /(.*) and (.*) aligned: (.*) beacons:/;
+ next if !$rest;
+
+ my @r = split /,/, $rest;
+ my @off = @r[0..2];
+ my @rot = @r[3..8];
+
+ # @off = transform(\@off, \@rot);
+ say "$s1 to $s2";
+ # $off[$_] += $aligned_scanners{$s1}->[$_] for 0..2;
+ say "$s2 at ", join(',', @off);
+ $aligned_scanners{$s2} = [ @off ];
+}
+
+my $max_dist = 0;
+for my $s1 (keys %aligned_scanners) {
+for my $s2 (keys %aligned_scanners) {
+ my $dist;
+ $dist += abs($aligned_scanners{$s1}->[$_] - $aligned_scanners{$s2}->[$_]) for 0 .. 2;
+ $max_dist = $dist if $dist > $max_dist;
+ say "$s1 to $s2 = $dist";
+} }
+
+say $max_dist;
+