--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.30;
+use strict;
+
+use List::Util qw(sum);
+say sum <>;
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.30;
+use strict;
+
+my %seen;
+my $freq = 0;
+chomp(my @changes = <>);
+while (1) {
+ for my $x (@changes) {
+ $freq += $x;
+ if ($seen{$freq}++) {
+ say $freq;
+ exit 0;
+ }
+ }
+}
+
+
+
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.30;
+use strict;
+
+my ($two, $three);
+while (<>) {
+ chomp;
+ my %h;
+ $h{$_}++ for split //;
+ $two++ if grep { $_ == 2 } values %h;
+ $three++ if grep { $_ == 3 } values %h;
+}
+
+say $two * $three;
+
+
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.30;
+use strict;
+
+my %seen;
+while (<>) {
+ chomp;
+ for my $i (0 .. length($_) - 1) {
+ my $x = $_;
+ substr($x, $i, 1) = '_';
+ $seen{$x}++;
+ }
+}
+
+for my $k (keys %seen) {
+ if ($seen{$k} == 2) {
+ $k =~ s/_//;
+ say $k;
+ }
+}
+
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.30;
+use strict;
+
+my %map;
+while (<>) {
+ chomp;
+ my ($id, $x, $y, $w, $h) = /(\d+)/g;
+ for my $dx (0 .. $w-1) {
+ for my $dy (0 .. $h-1) {
+ $map{$x+$dx,$y+$dy}++;
+ }
+ }
+}
+
+my $sum;
+for my $k (keys %map) {
+ $sum++ if $map{$k} > 1;
+}
+say $sum;
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.30;
+use strict;
+
+my %map;
+my %no;
+while (<>) {
+ chomp;
+ my ($id, $x, $y, $w, $h) = /(\d+)/g;
+ my $o;
+ for my $dx (0 .. $w-1) {
+ for my $dy (0 .. $h-1) {
+ if (defined $map{$x+$dx,$y+$dy}) {
+ delete $no{$map{$x+$dx,$y+$dy}};
+ $o++;
+ }
+ $map{$x+$dx,$y+$dy} = $id;
+ }
+ }
+ $no{$id} = 1 if !$o;
+}
+
+say keys %no;
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.30;
+use strict;
+
+my ($g, $sleep_m);
+my (%sleep_g, %sleep_g_m);
+for (sort <>) {
+ if (/#(\d+) begins/) {
+ $g = $1;
+ } elsif (/:(\d+)\] falls/) {
+ $sleep_m = $1;
+ } elsif (/:(\d+)\] wakes/) {
+ $sleep_g{$g} += $1 - $sleep_m;
+ for my $min ($sleep_m .. $1 - 1) {
+ $sleep_g_m{$g}{$min}++;
+ }
+ }
+}
+
+my $max_g;
+my $max_g_sleep;
+for my $g (keys %sleep_g) {
+ if (!$max_g_sleep || $max_g_sleep < $sleep_g{$g}) {
+ $max_g = $g;
+ $max_g_sleep = $sleep_g{$g};
+ }
+}
+my $max_min;
+my $max_min_sleep;
+for my $min (keys %{ $sleep_g_m{$max_g} }) {
+ my $n = $sleep_g_m{$max_g}{$min};
+ if (!defined $max_min_sleep || $max_min_sleep < $n) {
+ $max_min = $min;
+ $max_min_sleep = $n;
+ }
+}
+
+say $max_g * $max_min;
+
+
+
+
+
+
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.30;
+use strict;
+
+my ($g, $sleep_m);
+my (%sleep_g, %sleep_g_m);
+for (sort <>) {
+ if (/#(\d+) begins/) {
+ $g = $1;
+ } elsif (/:(\d+)\] falls/) {
+ $sleep_m = $1;
+ } elsif (/:(\d+)\] wakes/) {
+ $sleep_g{$g} += $1 - $sleep_m;
+ for my $min ($sleep_m .. $1 - 1) {
+ $sleep_g_m{$g}{$min}++;
+ }
+ }
+}
+
+my $max_g;
+my $max_min;
+my $max_min_sleep;
+for my $g (keys %sleep_g) {
+ for my $min (keys %{ $sleep_g_m{$g} }) {
+ my $n = $sleep_g_m{$g}{$min};
+ if (!defined $max_min_sleep || $max_min_sleep < $n) {
+ $max_g = $g;
+ $max_min = $min;
+ $max_min_sleep = $n;
+ }
+ }
+}
+
+say $max_g * $max_min;
+
+
+
+
+
+
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.30;
+use strict;
+
+chomp (my $data = <>);
+
+my $re = join('|', map { ("\l$_\u$_", "\u$_\l$_") } ('a' .. 'z'));
+
+1 while $data =~ s/$re//;
+
+say length $data;
+
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.30;
+use strict;
+
+chomp (my $data = <>);
+
+my $re = join('|', map { ("\l$_\u$_", "\u$_\l$_") } ('a' .. 'z'));
+
+my $min;
+for my $c ('a' .. 'z') {
+ my $d1 = $data;
+ $d1 =~ s/$_//g for $c, "\u$c";
+ 1 while $d1 =~ s/$re//;
+ $min = length $d1 if !$min || $min > length $d1;
+}
+
+say $min;
+
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.30;
+use strict;
+
+my @pts = map { [ /(\d+)/g ] } <>;
+
+my ($minx, $miny, $maxx, $maxy);
+for my $pt (@pts) {
+ $minx = $pt->[0] if !defined $minx || $minx > $pt->[0];
+ $maxx = $pt->[0] if !defined $maxx || $maxx < $pt->[0];
+ $miny = $pt->[1] if !defined $miny || $miny > $pt->[1];
+ $maxy = $pt->[1] if !defined $maxy || $maxy < $pt->[1];
+}
+
+my (%count, %inf);
+for my $x ($minx .. $maxx) {
+for my $y ($miny .. $maxy) {
+ my ($minpt, $mindist);
+ for my $i (0 .. $#pts) {
+ my $pt = $pts[$i];
+ my $dist = abs($x-$pt->[0]) + abs($y-$pt->[1]);
+ if (!defined $mindist || $mindist > $dist) {
+ $minpt = $i;
+ $mindist = $dist;
+ } elsif (defined $mindist && $mindist == $dist) {
+ $minpt = undef;
+ }
+ }
+ next if !defined $minpt;
+ say "$x,$y closest to $minpt at $mindist";
+ if ($y == $miny || $y == $maxy || $x == $minx || $x == $maxx) {
+ $inf{$minpt}++;
+ say "$minpt is infinite";
+ } else {
+ $count{$minpt}++;
+ }
+} }
+
+my $maxarea;
+for my $i (0 .. $#pts) {
+ next if $inf{$i};
+ $maxarea = $count{$i} if !$maxarea || $maxarea < $count{$i};
+}
+
+say $maxarea;
+
+
+
+
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.30;
+use strict;
+
+my @pts = map { [ /(\d+)/g ] } <>;
+
+my ($minx, $miny, $maxx, $maxy);
+for my $pt (@pts) {
+ $minx = $pt->[0] if !defined $minx || $minx > $pt->[0];
+ $maxx = $pt->[0] if !defined $maxx || $maxx < $pt->[0];
+ $miny = $pt->[1] if !defined $miny || $miny > $pt->[1];
+ $maxy = $pt->[1] if !defined $maxy || $maxy < $pt->[1];
+}
+
+my $count;
+for my $x ($minx .. $maxx) {
+for my $y ($miny .. $maxy) {
+ my $dist;
+ for my $i (0 .. $#pts) {
+ my $pt = $pts[$i];
+ $dist += abs($x-$pt->[0]) + abs($y-$pt->[1]);
+ }
+ $count++ if $dist < 10000;
+} }
+
+say $count;
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.30;
+use strict;
+
+my %prereqs;
+my %steps;
+while (<>) {
+ my ($src, $dst) = /(\w) must be.* (\w) can/;
+ $prereqs{$dst}->{$src} = 1;
+ $steps{$src} = 1;
+ $steps{$dst} = 1;
+}
+
+my $res = '';
+while (%steps) {
+ my @avail = sort grep { keys %{ $prereqs{$_} } == 0 } keys %steps;
+ my $finished = $avail[0];
+ $res .= $finished;
+ delete $steps{$finished};
+ for my $p (keys %prereqs) {
+ delete $prereqs{$p}->{$finished};
+ }
+}
+
+say $res;
+
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.30;
+use strict;
+
+my %prereqs;
+my %steps;
+while (<>) {
+ my ($src, $dst) = /(\w) must be.* (\w) can/;
+ $prereqs{$dst}->{$src} = 1;
+ $steps{$src} = ord($src)-ord('A')+61;
+ $steps{$dst} = ord($dst)-ord('A')+61;
+}
+
+my $time = 0;
+while (%steps) {
+ $time++;
+ my @avail = sort grep { keys %{ $prereqs{$_} } == 0 } keys %steps;
+ for my $step (splice @avail, 0, 5) {
+ next if --$steps{$step} > 0;
+ delete $steps{$step};
+ for my $p (keys %prereqs) {
+ delete $prereqs{$p}->{$step};
+ }
+ }
+}
+
+say $time;
+
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.30;
+use strict;
+
+my @tree = split /\s+/, <>;
+
+use List::Util qw(sum);
+
+my $metasum = 0;
+sub walk {
+ my $subnodes = shift @tree;
+ my $metadata = shift @tree;
+ say "walk $subnodes $metadata";
+ while ($subnodes--) {
+ walk();
+ }
+ while ($metadata--) {
+ $metasum += shift @tree;
+ }
+}
+walk();
+say $metasum;
+
+
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.30;
+use strict;
+
+my @tree = split /\s+/, <>;
+
+use List::Util qw(sum);
+
+sub walk {
+ my $subnodes = shift @tree;
+ my $metadata = shift @tree;
+ my $sum = 0;
+ say "walk $subnodes $metadata";
+ my $sn1 = $subnodes;
+ my @subvals;
+ while ($sn1--) {
+ push @subvals, walk();
+ }
+ if ($subnodes) {
+ while ($metadata--) {
+ my $id = shift @tree;
+ $id--;
+ $sum += $subvals[$id]
+ if $id < @subvals;
+ }
+ } else {
+ while ($metadata--) {
+ $sum += shift @tree;
+ }
+ }
+ say "returning $sum";
+ $sum;
+}
+say walk();
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.30;
+use strict;
+
+my ($players, $last) = @ARGV;
+
+my @l = (0);
+my $cur = 0;
+
+my %score;
+for (1 .. $last) {
+ my $player = $_ % $players;
+ if ($_ % 23 == 0) {
+ $score{$player} += $_;
+ $cur -= 7;
+ $cur += @l if $cur < 0;
+ my ($val) = splice @l, $cur, 1;
+ $score{$player} += $val;
+ } else {
+ my $dst = @l > 1 ? ($cur + 2) % @l : $cur + 1;
+ $dst = @l if $dst == 0;
+ splice @l, $dst, 0, $_;
+ $cur = $dst;
+ }
+ say $_ if $_ % 100000 == 0;
+ # say join(' ', $cur, @l);
+}
+
+use List::Util qw(max);
+say max values %score;
+
+
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.30;
+use strict;
+
+my ($players, $last) = @ARGV;
+
+my %prev = ( 0 => 0 );
+my %next = ( 0 => 0 );
+my $cur = 0;
+
+my %score;
+for (1 .. $last) {
+ my $player = $_ % $players;
+ if ($_ % 23 == 0) {
+ $score{$player} += $_;
+ $cur = $prev{$cur} for 1 .. 7;
+ $score{$player} += $cur;
+ $next{$prev{$cur}} = $next{$cur};
+ $prev{$next{$cur}} = $prev{$cur};
+ $cur = $next{$cur};
+ } else {
+ $cur = $next{$cur};
+ my $n = $next{$cur};
+ $next{$cur} = $_;
+ $prev{$_} = $cur;
+ $prev{$n} = $_;
+ $next{$_} = $n;
+ $cur = $_;
+ }
+ say $_ if $_ % 100000 == 0;
+ # say join(' ', $cur, @l);
+}
+
+use List::Util qw(max);
+say max values %score;
+
+
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.30;
+use strict;
+
+my @pts;
+my @vel;
+while (<>) {
+ my ($x, $y, $dx, $dy) = /(-?\d+)/g;
+ push @pts, [ $x, $y ];
+ push @vel, [ $dx, $dy ];
+}
+
+$; = ',';
+my $printed;
+
+while (1) {
+ my %map;
+ my ($minx, $miny, $maxx, $maxy);
+ for my $i (0 .. $#pts) {
+ my $pt = $pts[$i];
+ my $ve = $vel[$i];
+ $pt->[$_] += $ve->[$_] for 0 .. 1;
+ $maxx = $pt->[0] if !defined $maxx || $maxx < $pt->[0];
+ $maxy = $pt->[1] if !defined $maxy || $maxy < $pt->[1];
+ $minx = $pt->[0] if !defined $minx || $minx > $pt->[0];
+ $miny = $pt->[1] if !defined $miny || $miny > $pt->[1];
+ $map{$pt->[0],$pt->[1]}++;
+ }
+ my $dim = $maxx-$minx + $maxy-$miny;
+ if ($dim < 200) {
+ for my $y ($miny .. $maxy) {
+ for my $x ($minx .. $maxx) {
+ print $map{$x,$y} ? '#' : '.';
+ }
+ print "\n";
+ }
+ $printed = 1;
+ } elsif ($printed) {
+ last;
+ }
+ say "dim=$dim\n";
+}
+
+
+
+
+
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.30;
+use strict;
+
+my @pts;
+my @vel;
+while (<>) {
+ my ($x, $y, $dx, $dy) = /(-?\d+)/g;
+ push @pts, [ $x, $y ];
+ push @vel, [ $dx, $dy ];
+}
+
+$; = ',';
+my $printed;
+my $count;
+
+while (1) {
+ my %map;
+ my ($minx, $miny, $maxx, $maxy);
+ for my $i (0 .. $#pts) {
+ my $pt = $pts[$i];
+ my $ve = $vel[$i];
+ $pt->[$_] += $ve->[$_] for 0 .. 1;
+ $maxx = $pt->[0] if !defined $maxx || $maxx < $pt->[0];
+ $maxy = $pt->[1] if !defined $maxy || $maxy < $pt->[1];
+ $minx = $pt->[0] if !defined $minx || $minx > $pt->[0];
+ $miny = $pt->[1] if !defined $miny || $miny > $pt->[1];
+ $map{$pt->[0],$pt->[1]}++;
+ }
+ $count++;
+ my $dim = $maxx-$minx + $maxy-$miny;
+ if ($dim < 150) {
+ say "count=$count :";
+ for my $y ($miny .. $maxy) {
+ for my $x ($minx .. $maxx) {
+ print $map{$x,$y} ? '#' : '.';
+ }
+ print "\n";
+ }
+ $printed = 1;
+ } elsif ($printed) {
+ last;
+ }
+ say "dim=$dim\n";
+}
+
+
+
+
+
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.30;
+use strict;
+
+my $serial = shift @ARGV;
+
+my %cache;
+sub power_at {
+ my ($x, $y) = @_;
+ return $cache{$x,$y} if defined $cache{$x,$y};
+
+ my $rack_id = $x + 10;
+ my $power = ($rack_id * $y + $serial) * $rack_id;
+ $power = "000" . $power;
+ $power =~ s/.*(.)..\z/$1/;
+ $power -= 5;
+ return $cache{$x,$y} = $power;
+}
+
+my ($maxx, $maxy, $maxpwr);
+for my $x (1 .. 300) {
+ for my $y (1 .. 300) {
+ my $sum;
+ for my $dx (0 .. 2) {
+ next if $x + $dx > 300;
+ for my $dy (0 .. 2) {
+ next if $y + $dy > 300;
+ $sum += power_at($x+$dx, $y+$dy);
+ }
+ }
+ if (!defined $maxpwr || $sum > $maxpwr) {
+ $maxpwr = $sum;
+ $maxx = $x;
+ $maxy = $y;
+ }
+ }
+}
+
+say power_at(33,45);
+say "$maxx,$maxy = $maxpwr";
+
+
+
+
+
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.30;
+use strict;
+
+my $serial = shift @ARGV;
+
+my %cache;
+sub power_at {
+ my ($x, $y) = @_;
+ return $cache{$x,$y} if defined $cache{$x,$y};
+
+ my $rack_id = $x + 10;
+ my $power = ($rack_id * $y + $serial) * $rack_id;
+ $power = "000" . $power;
+ $power =~ s/.*(.)..\z/$1/;
+ $power -= 5;
+ return $cache{$x,$y} = $power;
+}
+
+my %sq_cache;
+my ($maxx, $maxy, $maxsize, $maxpwr);
+sub square_at {
+ my ($x, $y, $sizex, $sizey) = @_;
+
+ return $sq_cache{$x,$y,$sizex,$sizey}
+ if defined $sq_cache{$x,$y,$sizex,$sizey};
+
+ my $sum;
+ if ($sizex == 1 && $sizey == 1) {
+ $sum = power_at($x, $y);
+ } elsif ($sizex == 1) {
+ $sum = square_at($x, $y, $sizex, $sizey-1)
+ + power_at($x, $y+$sizey-1);
+ } elsif ($sizey == 1) {
+ $sum = square_at($x, $y, $sizex-1, $sizey)
+ + power_at($x+$sizex-1, $y);
+ } else {
+ $sum = square_at($x, $y, $sizex-1, $sizey-1)
+ + square_at($x+$sizex-1, $y, 1, $sizey-1)
+ + square_at($x, $y+$sizey-1, $sizex-1, 1)
+ + power_at($x+$sizex-1, $y+$sizey-1);
+ }
+ if ($sizex == $sizey && (!defined $maxpwr || $sum > $maxpwr)) {
+ $maxpwr = $sum;
+ $maxx = $x;
+ $maxy = $y;
+ $maxsize = $sizex;
+ }
+ $sq_cache{$x,$y,$sizex,$sizey} = $sum;
+}
+
+for my $size (1 .. 300) {
+ for my $x (1 .. 301-$size) {
+ for my $y (1 .. 301-$size) {
+ my $sz = square_at($x, $y, $size, $size);
+ # say "$x,$y,$size = $sz";
+ }
+ }
+ say "size $size, $maxx,$maxy,$maxsize = $maxpwr";
+}
+
+say "$maxx,$maxy,$maxsize = $maxpwr";
+
+
+
+
+
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.30;
+use strict;
+
+chomp (my $state = <>);
+$state =~ s/.*: //;
+
+scalar <>;
+
+my %rules;
+while (<>) {
+ chomp;
+ my ($src, $dst) = split / => /;
+ $rules{$src} = $dst;
+}
+
+my $off = 0;
+for (1 .. 20) {
+ if ($state !~ /^\.\.\.\./) {
+ $state = '....' . $state;
+ $off -= 4;
+ say "adding 4";
+ }
+ $state = $state . '....' if $state !~ /\.\.\.\.$/;
+
+ my $nstate = $state;
+ $nstate =~ y/#/./;
+
+ for my $p (2 .. length($state)-3) {
+ my $src = substr($state, $p-2, 5);
+ # die "no rule for $src" if !exists $rules{$src};
+ substr($nstate, $p, 1) = $rules{$src} if defined $rules{$src};
+ }
+ $state = $nstate;
+ say $state;
+}
+
+my $sum;
+while ($state =~ /#/g) {
+ say "at ", pos($state) + $off - 1;
+ $sum += pos($state) + $off - 1;
+}
+say $sum;
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.30;
+use strict;
+
+chomp (my $state = <>);
+$state =~ s/.*: //;
+
+scalar <>;
+
+my %rules;
+while (<>) {
+ chomp;
+ my ($src, $dst) = split / => /;
+ $rules{$src} = $dst;
+}
+
+my $off = 0;
+my %seen;
+my $gen = 0;
+while (1) {
+ $gen++;
+ if ($state !~ /^\.\.\.\./) {
+ $state = '....' . $state;
+ $off -= 4;
+ }
+ $state = $state . '....' if $state !~ /\.\.\.\.$/;
+ while ($state =~ /^\.\.\.\.\./) {
+ $state =~ s/^.//;
+ $off++;
+ }
+ while ($state =~ /\.\.\.\.\.$/) {
+ $state =~ s/.$//;
+ }
+
+ my $nstate = $state;
+ $nstate =~ y/#/./;
+
+ for my $p (2 .. length($state)-3) {
+ my $src = substr($state, $p-2, 5);
+ # die "no rule for $src" if !exists $rules{$src};
+ substr($nstate, $p, 1) = $rules{$src} if defined $rules{$src};
+ }
+ $state = $nstate;
+ last if ($seen{$state});
+ $seen{$state} = [ $gen, $off ];
+ say $state;
+}
+
+say "state $gen off $off";
+say $state;
+say "is equal to ", $seen{$state}[0], ' ', $seen{$state}[1];
+
+$off += (50000000000 - $gen) * ($off - $seen{$state}[1]);
+my $sum;
+while ($state =~ /#/g) {
+ $sum += pos($state) + $off - 1;
+}
+say $sum;
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.30;
+use strict;
+
+$; = ',';
+my @map = map { chomp; [ split // ] } <>;
+my $xmax = $#{ $map[0] };
+my $ymax = $#map;
+
+my @carts;
+for my $y (0 .. $ymax) {
+ for my $x (0 .. $xmax) {
+ my $p = $map[$y][$x];
+ if ($p eq '^') {
+ $map[$y][$x] = '|';
+ push @carts, [ $x, $y, 0, 0 ];
+ }
+ if ($p eq '>') {
+ $map[$y][$x] = '-';
+ push @carts, [ $x, $y, 1, 0 ];
+ }
+ if ($p eq 'v') {
+ $map[$y][$x] = '|';
+ push @carts, [ $x, $y, 2, 0 ];
+ }
+ if ($p eq '<') {
+ $map[$y][$x] = '-';
+ push @carts, [ $x, $y, 3, 0 ];
+ }
+ }
+}
+
+my %cartpos;
+for my $c (@carts) {
+ my ($x, $y) = @$c;
+ $cartpos{$x,$y} = 1;
+}
+while (1) {
+ for my $c (@carts) {
+ my ($x, $y, $dir, $state) = @$c;
+ my $m = $map[$y][$x];
+ if ($m eq '/') {
+ $dir =~ y/0123/1032/;
+ } elsif ($m eq '\\') {
+ $dir =~ y/0123/3210/;
+ } elsif ($m eq '+') {
+ if ($state == 0) {
+ $dir =~ y/0123/3012/;
+ } elsif ($state == 2) {
+ $dir =~ y/0123/1230/;
+ }
+ $state = 0 if ++$state > 2;
+ }
+ delete $cartpos{$x,$y};
+ $y-- if $dir == 0;
+ $x++ if $dir == 1;
+ $y++ if $dir == 2;
+ $x-- if $dir == 3;
+ if ($cartpos{$x,$y}++) {
+ say "$x,$y";
+ exit 0;
+ }
+ $c->[0] = $x;
+ $c->[1] = $y;
+ $c->[2] = $dir;
+ $c->[3] = $state;
+ }
+ @carts = sort { $a->[1] == $b->[1]
+ ? $a->[0] <=> $b->[0]
+ : $a->[1] <=> $b->[1] } @carts;
+}
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.30;
+use strict;
+
+$; = ',';
+my @map = map { chomp; [ split // ] } <>;
+my $xmax = $#{ $map[0] };
+my $ymax = $#map;
+
+my @carts;
+for my $y (0 .. $ymax) {
+ for my $x (0 .. $xmax) {
+ my $p = $map[$y][$x];
+ if ($p eq '^') {
+ $map[$y][$x] = '|';
+ push @carts, [ $x, $y, 0, 0 ];
+ }
+ if ($p eq '>') {
+ $map[$y][$x] = '-';
+ push @carts, [ $x, $y, 1, 0 ];
+ }
+ if ($p eq 'v') {
+ $map[$y][$x] = '|';
+ push @carts, [ $x, $y, 2, 0 ];
+ }
+ if ($p eq '<') {
+ $map[$y][$x] = '-';
+ push @carts, [ $x, $y, 3, 0 ];
+ }
+ }
+}
+
+my %cartpos;
+for my $i (0 .. $#carts) {
+ my $c = $carts[$i];
+ my ($x, $y) = @$c;
+ $cartpos{$x,$y} = $i;
+}
+while (1) {
+ for my $i (0 .. $#carts) {
+ my $c = $carts[$i];
+ next if $c->[4];
+ my ($x, $y, $dir, $state) = @$c;
+ my $m = $map[$y][$x];
+ if ($m eq '/') {
+ $dir =~ y/0123/1032/;
+ } elsif ($m eq '\\') {
+ $dir =~ y/0123/3210/;
+ } elsif ($m eq '+') {
+ if ($state == 0) {
+ $dir =~ y/0123/3012/;
+ } elsif ($state == 2) {
+ $dir =~ y/0123/1230/;
+ }
+ $state = 0 if ++$state > 2;
+ }
+ delete $cartpos{$x,$y};
+ $y-- if $dir == 0;
+ $x++ if $dir == 1;
+ $y++ if $dir == 2;
+ $x-- if $dir == 3;
+ if (defined $cartpos{$x,$y}) {
+ $c->[4] = 1;
+ $carts[$cartpos{$x,$y}]->[4] = 1;
+ delete $cartpos{$x,$y};
+ } else {
+ $cartpos{$x,$y} = $i;
+ }
+ $c->[0] = $x;
+ $c->[1] = $y;
+ $c->[2] = $dir;
+ $c->[3] = $state;
+ }
+ @carts = sort { $a->[1] == $b->[1]
+ ? $a->[0] <=> $b->[0]
+ : $a->[1] <=> $b->[1] } grep { !defined $_->[4] } @carts;
+ if (@carts == 1) {
+ say $carts[0][0], ',', $carts[0][1];
+ last;
+ }
+}
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.30;
+use strict;
+
+my $iters = shift @ARGV;
+$iters += 10;
+
+my @r = qw(3 7);
+my @p = qw(0 1);
+
+while (@r < $iters) {
+ my $n = $r[$p[0]] + $r[$p[1]];
+ push @r, split //, $n;
+ for (0 .. 1) {
+ $p[$_] += 1 + $r[$p[$_]];
+ $p[$_] -= @r while $p[$_] >= @r;
+ }
+ # say join ' ', @p, @r;
+}
+
+say join('', @r[-10 .. -1])
+
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.20;
+use strict;
+
+my $input = shift @ARGV;
+
+my $r = '37';
+my @p = qw(0 1);
+
+my $l = length $input;
+my $lr = length $r;
+while (1) {
+ my @n = (substr($r, $p[0], 1), substr($r, $p[1], 1));
+ $r .= $n[0]+$n[1];
+ $lr = length $r;
+ for (0 .. 1) {
+ $p[$_] += 1 + $n[$_];
+ $p[$_] -= $lr while $p[$_] >= $lr;
+ }
+ next if $lr < $l;
+ last if substr($r, -$l) eq $input;
+ last if substr($r, -$l-1, -1) eq $input;
+}
+
+$r =~ s/$input.*//;
+say length $r;
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.36;
+use strict;
+use experimental 'multidimensional';
+
+my @map = map { chomp; [ split // ] } <>;
+my $xmax = $#{ $map[0] };
+my $ymax = $#map;
+
+my @units;
+for my $y (0 .. $ymax) {
+ for my $x (0 .. $xmax) {
+ if ($map[$y][$x] eq 'G') {
+ push @units, [$x, $y, 'G', 200];
+ } elsif ($map[$y][$x] eq 'E') {
+ push @units, [$x, $y, 'E', 200];
+ }
+ }
+}
+
+sub enemy_of($unit) { $unit->[2] eq 'G' ? 'E' : 'G' }
+
+my @neighbors = ([0, -1], [-1, 0], [1, 0], [0, 1]);
+
+sub reading_sort {
+ return sort { $a->[1] == $b->[1] ? $a->[0] <=> $b->[0] : $a->[1] <=> $b->[1] } @_;
+}
+
+sub print_map {
+ for my $y (0 .. $ymax) {
+ for my $x (0 .. $xmax) {
+ print $map[$y][$x];
+ }
+ print "\n";
+ }
+ for my $unit (grep { $_->[3] } reading_sort(@units)) {
+ say "$unit->[2] at $unit->[0],$unit->[1] ($unit->[3])"
+ }
+}
+
+sub neigh_squares($unit, $type) {
+ my @rv;
+ for my $n (@neighbors) {
+ my ($dx, $dy) = @$n;
+ $dx += $unit->[0];
+ $dy += $unit->[1];
+ if ($map[$dy][$dx] eq $type) {
+ push @rv, [ $dx, $dy ];
+ }
+ }
+ return @rv;
+}
+
+sub enemy_within_range($unit) {
+ return neigh_squares($unit, enemy_of($unit));
+}
+
+sub loc2unit($loc) {
+ for my $unit (@units) {
+ return $unit
+ if $unit->[0] == $loc->[0] && $unit->[1] == $loc->[1];
+ }
+ die "Can't locate unit at ", join(',', @$loc);
+}
+
+sub attack($unit, $enemies) {
+ say "Attacking unit: ", join(',', @$unit);
+ my $min_hp;
+ my @min_hp;
+ for my $enemy (@$enemies) {
+ say "Enemy: ", join(',', @$enemy);
+ my $enemy_unit = loc2unit($enemy);
+ if (!defined $min_hp || $min_hp > $enemy_unit->[3]) {
+ $min_hp = $enemy_unit->[3];
+ @min_hp = ();
+ }
+ if ($min_hp == $enemy_unit->[3]) {
+ push @min_hp, $enemy_unit;
+ }
+ }
+
+ @min_hp = reading_sort(@min_hp);
+ my $enemy = shift @min_hp;
+ say "attacking";
+ $enemy->[3] -= 3;
+ if ($enemy->[3] <= 0) {
+ $enemy->[3] = 0;
+ $map[$enemy->[1]][$enemy->[0]] = '.';
+ }
+ $enemy->[3] = 0 if $enemy->[3] < 0;
+ say "attacked ", join(',', @$enemy);
+}
+
+sub dump_path($path) {
+ join(' ', map { "[$_->[0],$_->[1]]" } @$path);
+}
+
+sub move($unit) {
+ my %target_sq;
+ for my $enemy (grep { $_->[2] ne $unit->[2] } @units) {
+ for my $sq (neigh_squares($enemy, '.')) {
+ if (!$target_sq{$sq->[0],$sq->[1]}++) {
+ # say "target square $sq->[0],$sq->[1]";
+ }
+ }
+ }
+
+ my @q = [ [ $unit->[0], $unit->[1] ] ];
+ my %visited;
+ my $found_at_dist;
+ my @reachable;
+ while (@q) {
+ my $path = shift @q;
+ # say "walking path ", dump_path($path);
+ last if $found_at_dist && @$path > $found_at_dist;
+ for my $sq (neigh_squares($path->[-1], '.')) {
+ next if $visited{$sq->[0],$sq->[1]}++;
+
+ if ($target_sq{$sq->[0],$sq->[1]}) {
+ $found_at_dist //= @$path;
+ my $path1 = [ @$path, [ @$sq ] ];
+ # say "Found square $sq->[0],$sq->[1] through ",
+ # dump_path($path1);
+ push @reachable, [ @$sq, @{ $path1->[1] } ];
+ } else {
+ push @q, [ @$path, [ @$sq ] ];
+ }
+ }
+ }
+
+ if (!@reachable) {
+ # say "no reachable squares";
+ return;
+ }
+
+ @reachable = reading_sort(@reachable);
+ my $target = $reachable[0];
+ # say "moving to $target->[0],$target->[1] via $target->[2],$target->[3]";
+ $map[$unit->[1]][$unit->[0]] = '.';
+ $map[$unit->[1] = $target->[3]][$unit->[0] = $target->[2]] = $unit->[2];
+}
+
+sub unit_round($unit) {
+ return if !$unit->[3]; # are we alive?
+ # say "\nunit_round ", join(',', @$unit);
+
+ my @enemies = enemy_within_range($unit);
+ if (!@enemies) {
+ move($unit);
+ @enemies = enemy_within_range($unit);
+ }
+ if (@enemies) {
+ attack($unit, \@enemies);
+ }
+}
+
+my $round = 0;
+my %total_hp;
+ROUND:
+while (1) {
+ say "After $round rounds:";
+ print_map;
+ $round++;
+ @units = reading_sort(grep { $_->[3] } @units);
+
+ for my $unit (@units) {
+ next if !$unit->[3];
+ %total_hp = ();
+ for my $u (@units) {
+ $total_hp{$u->[2]} += $u->[3]
+ if $u->[3];
+ }
+ if (keys %total_hp < 2) {
+ $round--;
+ last ROUND;
+ }
+ unit_round($unit);
+ }
+
+}
+
+say "After $round rounds:";
+print_map;
+
+say "ended after round $round with hp ", join('=>', %total_hp);
+say $round * (%total_hp)[1];
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.36;
+use strict;
+use experimental 'multidimensional';
+
+my @input = <>;
+
+my (@map, $xmax, $ymax, @units);
+
+sub init_map {
+ @map = map { chomp; [ split // ] } @input;
+ $xmax = $#{ $map[0] };
+ $ymax = $#map;
+
+ @units = ();
+ for my $y (0 .. $ymax) {
+ for my $x (0 .. $xmax) {
+ if ($map[$y][$x] eq 'G') {
+ push @units, [$x, $y, 'G', 200];
+ } elsif ($map[$y][$x] eq 'E') {
+ push @units, [$x, $y, 'E', 200];
+ }
+ }
+ }
+}
+
+init_map;
+
+sub enemy_of($unit) { $unit->[2] eq 'G' ? 'E' : 'G' }
+
+my @neighbors = ([0, -1], [-1, 0], [1, 0], [0, 1]);
+
+sub reading_sort {
+ return sort { $a->[1] == $b->[1] ? $a->[0] <=> $b->[0] : $a->[1] <=> $b->[1] } @_;
+}
+
+sub print_map {
+ for my $y (0 .. $ymax) {
+ for my $x (0 .. $xmax) {
+ print $map[$y][$x];
+ }
+ print "\n";
+ }
+ for my $unit (grep { $_->[3] } reading_sort(@units)) {
+ say "$unit->[2] at $unit->[0],$unit->[1] ($unit->[3])"
+ }
+}
+
+sub neigh_squares($unit, $type) {
+ my @rv;
+ for my $n (@neighbors) {
+ my ($dx, $dy) = @$n;
+ $dx += $unit->[0];
+ $dy += $unit->[1];
+ if ($map[$dy][$dx] eq $type) {
+ push @rv, [ $dx, $dy ];
+ }
+ }
+ return @rv;
+}
+
+sub enemy_within_range($unit) {
+ return neigh_squares($unit, enemy_of($unit));
+}
+
+sub loc2unit($loc) {
+ for my $unit (@units) {
+ return $unit
+ if $unit->[0] == $loc->[0] && $unit->[1] == $loc->[1];
+ }
+ die "Can't locate unit at ", join(',', @$loc);
+}
+
+sub attack($unit, $enemies, $power) {
+ say "Attacking unit: ", join(',', @$unit);
+ my $min_hp;
+ my @min_hp;
+ for my $enemy (@$enemies) {
+ say "Enemy: ", join(',', @$enemy);
+ my $enemy_unit = loc2unit($enemy);
+ if (!defined $min_hp || $min_hp > $enemy_unit->[3]) {
+ $min_hp = $enemy_unit->[3];
+ @min_hp = ();
+ }
+ if ($min_hp == $enemy_unit->[3]) {
+ push @min_hp, $enemy_unit;
+ }
+ }
+
+ @min_hp = reading_sort(@min_hp);
+ my $enemy = shift @min_hp;
+ say "attacking";
+ $enemy->[3] -= $power;
+ if ($enemy->[3] <= 0) {
+ die if $enemy->[2] eq 'E';
+ $enemy->[3] = 0;
+ $map[$enemy->[1]][$enemy->[0]] = '.';
+ }
+ $enemy->[3] = 0 if $enemy->[3] < 0;
+ say "attacked ", join(',', @$enemy);
+}
+
+sub dump_path($path) {
+ join(' ', map { "[$_->[0],$_->[1]]" } @$path);
+}
+
+sub move($unit) {
+ my %target_sq;
+ for my $enemy (grep { $_->[2] ne $unit->[2] } @units) {
+ for my $sq (neigh_squares($enemy, '.')) {
+ if (!$target_sq{$sq->[0],$sq->[1]}++) {
+ # say "target square $sq->[0],$sq->[1]";
+ }
+ }
+ }
+
+ my @q = [ [ $unit->[0], $unit->[1] ] ];
+ my %visited;
+ my $found_at_dist;
+ my @reachable;
+ while (@q) {
+ my $path = shift @q;
+ # say "walking path ", dump_path($path);
+ last if $found_at_dist && @$path > $found_at_dist;
+ for my $sq (neigh_squares($path->[-1], '.')) {
+ next if $visited{$sq->[0],$sq->[1]}++;
+
+ if ($target_sq{$sq->[0],$sq->[1]}) {
+ $found_at_dist //= @$path;
+ my $path1 = [ @$path, [ @$sq ] ];
+ # say "Found square $sq->[0],$sq->[1] through ",
+ # dump_path($path1);
+ push @reachable, [ @$sq, @{ $path1->[1] } ];
+ } else {
+ push @q, [ @$path, [ @$sq ] ];
+ }
+ }
+ }
+
+ if (!@reachable) {
+ # say "no reachable squares";
+ return;
+ }
+
+ @reachable = reading_sort(@reachable);
+ my $target = $reachable[0];
+ # say "moving to $target->[0],$target->[1] via $target->[2],$target->[3]";
+ $map[$unit->[1]][$unit->[0]] = '.';
+ $map[$unit->[1] = $target->[3]][$unit->[0] = $target->[2]] = $unit->[2];
+}
+
+sub unit_round($unit, $attack) {
+ return if !$unit->[3]; # are we alive?
+ # say "\nunit_round ", join(',', @$unit);
+
+ my @enemies = enemy_within_range($unit);
+ if (!@enemies) {
+ move($unit);
+ @enemies = enemy_within_range($unit);
+ }
+ if (@enemies) {
+ attack($unit, \@enemies, $attack);
+ }
+}
+
+sub battle($elf_hp) {
+ my $round = 0;
+ init_map;
+ say "elf_hp: $elf_hp =====================================";
+ my %total_hp;
+ ROUND:
+ while (1) {
+ say "After $round rounds:";
+ print_map;
+ $round++;
+ @units = reading_sort(grep { $_->[3] } @units);
+
+ for my $unit (@units) {
+ next if !$unit->[3];
+ %total_hp = ();
+ for my $u (@units) {
+ $total_hp{$u->[2]} += $u->[3]
+ if $u->[3];
+ }
+ if (keys %total_hp < 2) {
+ $round--;
+ last ROUND;
+ }
+ eval { unit_round($unit, $unit->[2] eq 'E' ? $elf_hp : 3); };
+ return 0 if $@;
+ }
+
+ }
+ say "After $round rounds with hp $elf_hp:";
+ print_map;
+
+ say "elf_hp $elf_hp ended after round $round with hp ", join('=>', %total_hp);
+ say $round * (%total_hp)[1];
+ return 1;
+}
+
+my $elf_hp = 4;
+$elf_hp++ while !battle($elf_hp);
+
+# There is a bug out there - on the real puzzle input it returned
+# remaining hp lower by three, which means there had to be one less Goblin
+# attack somewhere.
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.30;
+use strict;
+
+local $/ = "\n\n";
+
+my $total = 0;
+while(<>) {
+ chomp;
+ last if !length;
+
+ my @nums = /(\d+)/g;
+ my @before = @nums[0..3];
+ my ($op, $a, $b, $c) = @nums[4..7];
+ my @after = @nums[8..11];
+ my $match = 0;
+
+ # addr
+ $match++ if ($before[$a] + $before[$b] == $after[$c]);
+ # addi
+ $match++ if ($before[$a] + $b == $after[$c]);
+ # mulr
+ $match++ if ($before[$a] * $before[$b] == $after[$c]);
+ # muli
+ $match++ if ($before[$a] * $b == $after[$c]);
+ # bandr
+ $match++ if (($before[$a] & $before[$b]) == $after[$c]);
+ # bandi
+ $match++ if (($before[$a] & $b) == $after[$c]);
+ # borr
+ $match++ if (($before[$a] | $before[$b]) == $after[$c]);
+ # bori
+ $match++ if (($before[$a] | $b) == $after[$c]);
+ # setr
+ $match++ if ($before[$a] == $after[$c]);
+ # seti
+ $match++ if ($a == $after[$c]);
+ # gtir
+ $match++ if (($a > $before[$b] ? 1 : 0) == $after[$c]);
+ # gtri
+ $match++ if (($before[$a] > $b ? 1 : 0) == $after[$c]);
+ # gtrr
+ $match++ if (($before[$a] > $before[$b] ? 1 : 0) == $after[$c]);
+ # eqir
+ $match++ if (($a == $before[$b] ? 1 : 0) == $after[$c]);
+ # eqri
+ $match++ if (($before[$a] == $b ? 1 : 0) == $after[$c]);
+ # eqrr
+ $match++ if (($before[$a] == $before[$b] ? 1 : 0) == $after[$c]);
+
+ $total++ if $match >= 3;
+}
+
+say $total;
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.30;
+use strict;
+
+local $/ = "\n\n";
+
+my @is_op = (65535) x 16;
+
+my $total = 0;
+while(<>) {
+ chomp;
+ last if !length;
+
+ my @nums = /(\d+)/g;
+ my @before = @nums[0..3];
+ my ($op, $a, $b, $c) = @nums[4..7];
+ my @after = @nums[8..11];
+ my $match = 0;
+
+ print join(' ', @nums), "\n";
+ # addr
+ $is_op[$op] &= ~(1 << 0) unless ($before[$a] + $before[$b] == $after[$c]);
+ # addi
+ $is_op[$op] &= ~(1 << 1) unless ($before[$a] + $b == $after[$c]);
+ # mulr
+ $is_op[$op] &= ~(1 << 2) unless ($before[$a] * $before[$b] == $after[$c]);
+ # muli
+ $is_op[$op] &= ~(1 << 3) unless ($before[$a] * $b == $after[$c]);
+ # bandr
+ $is_op[$op] &= ~(1 << 4) unless (($before[$a] & $before[$b]) == $after[$c]);
+ # bandi
+ $is_op[$op] &= ~(1 << 5) unless (($before[$a] & $b) == $after[$c]);
+ # borr
+ $is_op[$op] &= ~(1 << 6) unless (($before[$a] | $before[$b]) == $after[$c]);
+ # bori
+ $is_op[$op] &= ~(1 << 7) unless (($before[$a] | $b) == $after[$c]);
+ # setr
+ $is_op[$op] &= ~(1 << 8) unless ($before[$a] == $after[$c]);
+ # seti
+ $is_op[$op] &= ~(1 << 9) unless ($a == $after[$c]);
+ # gtir
+ $is_op[$op] &= ~(1 << 10) unless (($a > $before[$b] ? 1 : 0) == $after[$c]);
+ # gtri
+ $is_op[$op] &= ~(1 << 11) unless (($before[$a] > $b ? 1 : 0) == $after[$c]);
+ # gtrr
+ $is_op[$op] &= ~(1 << 12) unless (($before[$a] > $before[$b] ? 1 : 0) == $after[$c]);
+ # eqir
+ $is_op[$op] &= ~(1 << 13) unless (($a == $before[$b] ? 1 : 0) == $after[$c]);
+ # eqri
+ $is_op[$op] &= ~(1 << 14) unless (($before[$a] == $b ? 1 : 0) == $after[$c]);
+ # eqrr
+ $is_op[$op] &= ~(1 << 15) unless (($before[$a] == $before[$b] ? 1 : 0) == $after[$c]);
+ print join(' ', map { sprintf("%04x", $_) } @is_op), "\n";
+}
+
+my %is_2n = map { (1 << $_) => 1 } 0 .. 15;
+
+my %done_2n;
+my $modified = 1;
+while ($modified) {
+ say join(" ", map { sprintf("%04x", $_) } @is_op);
+ $modified = 0;
+ for my $try (0 .. 15) {
+ next if $done_2n{$try};
+ next if !$is_2n{$is_op[$try]};
+ print "Unique: $try: $is_op[$try]\n";
+ $done_2n{$try} = 1;
+ $modified = 1;
+ for my $other (0 .. 15) {
+ next if $done_2n{$other};
+ $is_op[$other] &= ~$is_op[$try];
+ }
+ }
+}
+
+my @op_trans;
+for my $op (0 .. 15) {
+ for my $l (0 .. 15) {
+ next if (1 << $l) != $is_op[$op];
+ print "$op => $l\n";
+ $op_trans[$op] = $l;
+ }
+}
+
+local $/ = "\n";
+my @regs = (0) x 4;
+while (<>) {
+ my ($op, $a, $b, $c) = /(\d+)/g;
+ say "$op $a $b $c ", join(' ', @regs);
+
+ # addr
+ $regs[$c] = $regs[$a] + $regs[$b] if $op_trans[$op] == 0;
+ # addi
+ $regs[$c] = $regs[$a] + $b if $op_trans[$op] == 1;
+ # mulr
+ $regs[$c] = $regs[$a] * $regs[$b] if $op_trans[$op] == 2;
+ # muli
+ $regs[$c] = $regs[$a] * $b if $op_trans[$op] == 3;
+ # bandr
+ $regs[$c] = ($regs[$a] & $regs[$b]) if $op_trans[$op] == 4;
+ # bandi
+ $regs[$c] = ($regs[$a] & $b) if $op_trans[$op] == 5;
+ # borr
+ $regs[$c] = ($regs[$a] | $regs[$b]) if $op_trans[$op] == 6;
+ # bori
+ $regs[$c] = ($regs[$a] | $b) if $op_trans[$op] == 7;
+ # setr
+ $regs[$c] = $regs[$a] if $op_trans[$op] == 8;
+ # seti
+ $regs[$c] = $a if $op_trans[$op] == 9;
+ # gtir
+ $regs[$c] = ($a > $regs[$b] ? 1 : 0) if $op_trans[$op] == 10;
+ # gtri
+ $regs[$c] = ($regs[$a] > $b ? 1 : 0) if $op_trans[$op] == 11;
+ # gtrr
+ $regs[$c] = ($regs[$a] > $regs[$b] ? 1 : 0) if $op_trans[$op] == 12;
+ # eqir
+ $regs[$c] = ($a == $regs[$b] ? 1 : 0) if $op_trans[$op] == 13;
+ # eqri
+ $regs[$c] = ($regs[$a] == $b ? 1 : 0) if $op_trans[$op] == 14;
+ # eqrr
+ $regs[$c] = ($regs[$a] == $regs[$b] ? 1 : 0) if $op_trans[$op] == 15;
+}
+
+say $regs[0];
+
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.30;
+use strict;
+
+my %map;
+
+my ($ymin, $ymax, $xmin, $xmax);
+
+while (<>) {
+ chomp;
+ my ($c1n, $c1, $c2min, $c2max) = /\A(.)=(\d+), .=(\d+)\.\.(\d+)/;
+
+ for my $c2 ($c2min .. $c2max) {
+ if ($c1n eq 'x') {
+ $map{$c1}{$c2} = '#';
+ $ymin = $c2 if !defined $ymin || $ymin > $c2;
+ $ymax = $c2 if !defined $ymax || $ymax < $c2;
+ $xmin = $c1 if !defined $xmin || $xmin > $c1;
+ $xmax = $c1 if !defined $xmax || $xmax < $c1;
+ } else {
+ $map{$c2}{$c1} = '#';
+ $ymin = $c1 if !defined $ymin || $ymin > $c1;
+ $ymax = $c1 if !defined $ymax || $ymax < $c1;
+ $xmin = $c2 if !defined $xmin || $xmin > $c2;
+ $xmax = $c2 if !defined $xmax || $xmax < $c2;
+ }
+ }
+}
+$xmin--;
+$xmax++;
+
+say "y=$ymin .. $ymax";
+
+my $iter = 0;
+sub dump_map {
+ return if $iter++ % 1000;
+ my $total = 0;
+ for my $y (0 .. $ymax) {
+ for my $x ($xmin .. $xmax) {
+ print $map{$x}{$y} // ' ';
+ $total++ if defined $map{$x}{$y}
+ && $map{$x}{$y} =~ /[~0]/;
+ }
+ print "\n";
+ }
+ say "total=$total";
+ return $total;
+}
+
+dump_map();
+
+my $total = 0;
+
+sub explore {
+ my ($x, $y) = @_;
+ return 0 if $y > $ymax;
+ $map{$x}{$y} //= 0;
+ my $was_change;
+ while (1) {
+ $was_change = 0;
+ say "explore $x, $y";
+ dump_map();
+ my $xp = $x;
+ while ($map{$xp}{$y+1}) {
+ last if $map{$xp+1}{$y};
+ $xp++;
+ $map{$xp}{$y} //= 0;
+ }
+ my $xm = $x;
+ while ($map{$xm}{$y+1}) {
+ last if $map{$xm-1}{$y};
+ $xm--;
+ $map{$xm}{$y} //= 0;
+ }
+
+ say "$xm .. $xp";
+ if (!$map{$xp}{$y+1} && $y < $ymax) {
+ $map{$xp}{$y} //= 0;
+ if (!defined $map{$xp}{$y+1}) {
+ say "calling to $xp, $y+1";
+ $was_change += explore($xp, $y+1);
+ }
+ }
+
+ if (!$map{$xm}{$y+1} && $y < $ymax) {
+ $map{$xm}{$y} //= 0;
+ if (!defined $map{$xm}{$y+1}) {
+ say "calling to $xm, $y+1";
+ $was_change += explore($xm, $y+1);
+ }
+ }
+ say "was_change = $was_change";
+
+ next if $was_change;
+
+ if ($map{$xp+1}{$y} && $map{$xm-1}{$y}) {
+ for my $x0 ($xm .. $xp) {
+ $map{$x0}{$y} = '~';
+ }
+ return 1;
+ }
+ last;
+ }
+ say "$x,$y returning $was_change";
+ return $was_change;
+}
+
+explore(500, $ymin);
+
+$iter = 0;
+dump_map();
+
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.30;
+use strict;
+
+my %map;
+
+my ($ymin, $ymax, $xmin, $xmax);
+
+while (<>) {
+ chomp;
+ my ($c1n, $c1, $c2min, $c2max) = /\A(.)=(\d+), .=(\d+)\.\.(\d+)/;
+
+ for my $c2 ($c2min .. $c2max) {
+ if ($c1n eq 'x') {
+ $map{$c1}{$c2} = '#';
+ $ymin = $c2 if !defined $ymin || $ymin > $c2;
+ $ymax = $c2 if !defined $ymax || $ymax < $c2;
+ $xmin = $c1 if !defined $xmin || $xmin > $c1;
+ $xmax = $c1 if !defined $xmax || $xmax < $c1;
+ } else {
+ $map{$c2}{$c1} = '#';
+ $ymin = $c1 if !defined $ymin || $ymin > $c1;
+ $ymax = $c1 if !defined $ymax || $ymax < $c1;
+ $xmin = $c2 if !defined $xmin || $xmin > $c2;
+ $xmax = $c2 if !defined $xmax || $xmax < $c2;
+ }
+ }
+}
+$xmin--;
+$xmax++;
+
+say "y=$ymin .. $ymax";
+
+my $iter = 0;
+sub dump_map {
+ return if $iter++ % 1000;
+ my $total = 0;
+ for my $y (0 .. $ymax) {
+ for my $x ($xmin .. $xmax) {
+ print $map{$x}{$y} // ' ';
+ $total++ if defined $map{$x}{$y}
+ && $map{$x}{$y} =~ /[~]/;
+ }
+ print "\n";
+ }
+ say "total=$total";
+ return $total;
+}
+
+dump_map();
+
+my $total = 0;
+
+sub explore {
+ my ($x, $y) = @_;
+ return 0 if $y > $ymax;
+ $map{$x}{$y} //= 0;
+ my $was_change;
+ while (1) {
+ $was_change = 0;
+ say "explore $x, $y";
+ dump_map();
+ my $xp = $x;
+ while ($map{$xp}{$y+1}) {
+ last if $map{$xp+1}{$y};
+ $xp++;
+ $map{$xp}{$y} //= 0;
+ }
+ my $xm = $x;
+ while ($map{$xm}{$y+1}) {
+ last if $map{$xm-1}{$y};
+ $xm--;
+ $map{$xm}{$y} //= 0;
+ }
+
+ say "$xm .. $xp";
+ if (!$map{$xp}{$y+1} && $y < $ymax) {
+ $map{$xp}{$y} //= 0;
+ if (!defined $map{$xp}{$y+1}) {
+ say "calling to $xp, $y+1";
+ $was_change += explore($xp, $y+1);
+ }
+ }
+
+ if (!$map{$xm}{$y+1} && $y < $ymax) {
+ $map{$xm}{$y} //= 0;
+ if (!defined $map{$xm}{$y+1}) {
+ say "calling to $xm, $y+1";
+ $was_change += explore($xm, $y+1);
+ }
+ }
+ say "was_change = $was_change";
+
+ next if $was_change;
+
+ if ($map{$xp+1}{$y} && $map{$xm-1}{$y}) {
+ for my $x0 ($xm .. $xp) {
+ $map{$x0}{$y} = '~';
+ }
+ return 1;
+ }
+ last;
+ }
+ say "$x,$y returning $was_change";
+ return $was_change;
+}
+
+explore(500, $ymin);
+
+$iter = 0;
+dump_map();
+
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.36;
+use strict;
+use experimental 'multidimensional';
+
+my @map = map { chomp; [ split // ] } <>;
+
+my $rows = @map;
+my $cols = @{ $map[0] };
+
+my %total;
+for (1..10) {
+ %total = ();
+ my @nmap;
+ for my $y (0 .. $rows-1) {
+ my @row;
+ for my $x (0 .. $cols-1) {
+ my %count = map { $_ => 0 } ('#', '|', '.');
+ for my $neigh ([-1,-1], [0, -1], [1, -1],
+ [-1, 0], [1, 0],
+ [-1, 1], [0, 1], [1, 1]) {
+ my $nx = $x + $neigh->[0];
+ my $ny = $y + $neigh->[1];
+ next if $nx < 0 || $nx >= $cols
+ || $ny < 0 || $ny >= $cols;
+ $count{$map[$ny][$nx]}++;
+ }
+ my $self = $map[$y][$x];
+ if ($self eq '.' && $count{'|'} >= 3) {
+ $self = '|';
+ } elsif ($self eq '|' && $count{'#'} >= 3) {
+ $self = '#';
+ } elsif ($self eq '#' &&
+ ($count{'#'} == 0 || $count{'|'} == 0)) {
+ $self = '.';
+ }
+ push @row, $self;
+ $total{$self}++;
+ }
+ push @nmap, \@row;
+ }
+ @map = @nmap;
+ say map { join('', @$_, "\n") } @map;
+}
+
+say $total{"#"} * $total{'|'};
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.36;
+use strict;
+use experimental 'multidimensional';
+
+my @map = map { chomp; [ split // ] } <>;
+
+my $rows = @map;
+my $cols = @{ $map[0] };
+my $periods = 1000000000;
+
+my $gen = 0;
+my %seen;
+while ($gen++ < $periods) {
+ my %total;
+ my @nmap;
+ say "gen $gen";
+ for my $y (0 .. $rows-1) {
+ my @row;
+ for my $x (0 .. $cols-1) {
+ my %count = map { $_ => 0 } ('#', '|', '.');
+ for my $neigh ([-1,-1], [0, -1], [1, -1],
+ [-1, 0], [1, 0],
+ [-1, 1], [0, 1], [1, 1]) {
+ my $nx = $x + $neigh->[0];
+ my $ny = $y + $neigh->[1];
+ next if $nx < 0 || $nx >= $cols
+ || $ny < 0 || $ny >= $cols;
+ $count{$map[$ny][$nx]}++;
+ }
+ my $self = $map[$y][$x];
+ if ($self eq '.' && $count{'|'} >= 3) {
+ $self = '|';
+ } elsif ($self eq '|' && $count{'#'} >= 3) {
+ $self = '#';
+ } elsif ($self eq '#' &&
+ ($count{'#'} == 0 || $count{'|'} == 0)) {
+ $self = '.';
+ }
+ push @row, $self;
+ $total{$self}++;
+ }
+ push @nmap, \@row;
+ }
+ @map = @nmap;
+ my $state = join('', map { join('', @$_, "\n") } @map);
+
+ say $total{"#"} * $total{'|'};
+ if (defined $seen{$state} && $gen < 500) {
+ say "at $gen: the same state seen at $seen{$state}";
+ my $period = $gen - $seen{$state};
+ say "period $period";
+ $gen += $period * int(($periods-1-$gen)/$period);
+ say " => $gen";
+ }
+ $seen{$state} = $gen;
+}
+
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.30;
+use strict;
+
+my @code;
+my @regs = ((0) x 6);
+my $ip_reg;
+
+while(<>) {
+ if (/\A#ip (\d+)/) {
+ $ip_reg = $1;
+ next;
+ }
+ chomp;
+ push @code, [ split /\s+/ ];
+}
+
+my $ip = 0;
+while ($ip < @code) {
+ my ($op, $a, $b, $c) = @{ $code[$ip] };
+
+ $regs[$ip_reg] = $ip;
+
+ if ($op eq 'addr') {
+ $regs[$c] = $regs[$a] + $regs[$b];
+ } elsif ($op eq 'addi') {
+ $regs[$c] = $regs[$a] + $b;
+ } elsif ($op eq 'mulr') {
+ $regs[$c] = $regs[$a] * $regs[$b];
+ } elsif ($op eq 'muli') {
+ $regs[$c] = $regs[$a] * $b;
+ } elsif ($op eq 'andr') {
+ $regs[$c] = $regs[$a] & $regs[$b];
+ } elsif ($op eq 'andi') {
+ $regs[$c] = $regs[$a] & $b;
+ } elsif ($op eq 'orr') {
+ $regs[$c] = $regs[$a] | $regs[$b];
+ } elsif ($op eq 'ori') {
+ $regs[$c] = $regs[$a] | $b;
+ } elsif ($op eq 'setr') {
+ $regs[$c] = $regs[$a];
+ } elsif ($op eq 'seti') {
+ $regs[$c] = $a;
+ } elsif ($op eq 'gtrr') {
+ $regs[$c] = $regs[$a] > $regs[$b] ? 1 : 0;
+ } elsif ($op eq 'eqrr') {
+ $regs[$c] = $regs[$a] == $regs[$b] ? 1 : 0;
+ }
+ $ip = $regs[$ip_reg] + 1;
+}
+
+say $regs[0];
+
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.30;
+use strict;
+
+my @code;
+my @regs = (1, (0) x 5);
+my $ip_reg;
+
+while(<STDIN>) {
+ if (/\A#ip (\d+)/) {
+ $ip_reg = $1;
+ next;
+ }
+ chomp;
+ push @code, [ split /\s+/ ];
+}
+
+my $ip = 0;
+while ($ip < @code) {
+ my ($op, $a, $b, $c) = @{ $code[$ip] };
+
+ $regs[$ip_reg] = $ip;
+
+ if ($op eq 'addr') {
+ $regs[$c] = $regs[$a] + $regs[$b];
+ } elsif ($op eq 'addi') {
+ $regs[$c] = $regs[$a] + $b;
+ } elsif ($op eq 'mulr') {
+ $regs[$c] = $regs[$a] * $regs[$b];
+ } elsif ($op eq 'muli') {
+ $regs[$c] = $regs[$a] * $b;
+ } elsif ($op eq 'banr') {
+ $regs[$c] = $regs[$a] & $regs[$b];
+ } elsif ($op eq 'bani') {
+ $regs[$c] = $regs[$a] & $b;
+ } elsif ($op eq 'borr') {
+ $regs[$c] = $regs[$a] | $regs[$b];
+ } elsif ($op eq 'bori') {
+ $regs[$c] = $regs[$a] | $b;
+ } elsif ($op eq 'setr') {
+ $regs[$c] = $regs[$a];
+ } elsif ($op eq 'seti') {
+ $regs[$c] = $a;
+ } elsif ($op eq 'gtrr') {
+ $regs[$c] = $regs[$a] > $regs[$b] ? 1 : 0;
+ } elsif ($op eq 'eqrr') {
+ $regs[$c] = $regs[$a] == $regs[$b] ? 1 : 0;
+ } elsif ($op eq 'nop') {
+
+ } else {
+ die "Unknown op $ op at $ip";
+ }
+ say join(' ', $ip, "\t", @{ $code[$ip] }, "\t", @regs);
+ # $regs[3] = $ARGV[0] if $ip == 35;
+ $ip = $regs[$ip_reg] + 1;
+}
+
+say $regs[0];
+
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.36;
+use strict;
+use List::Util qw(max);
+use feature 'multidimensional';
+
+my (%hdoor, %vdoor);
+my ($minx, $miny, $maxx, $maxy) = (0, 0, 0, 0);
+my $was_change;
+my $maxdist;
+chomp (my $re = <>);
+
+$re =~ s/\A\^//;
+$re =~ s/\$\z//;
+
+sub walk($positions, $re) {
+ my @npos;
+ my %seen;
+ for my $p (@$positions) {
+ next if $seen{$p->[0],$p->[1]}++;
+ push @npos, [ @$p ];
+ }
+
+ say "len=", length($re), " npos=", scalar @npos;
+
+ my @rv;
+
+ while (length $re) {
+ my ($first, $rest) = $re =~ /\A(.)(.*)\z/;
+
+ if ($first =~ /[NSWE]/) {
+ for my $p (@npos) {
+ my ($x, $y) = @$p;
+ if ($first eq 'N') { $hdoor{$x,$y} = 1; $y--; }
+ elsif ($first eq 'S') { $y++; $hdoor{$x,$y} = 1; }
+ elsif ($first eq 'W') { $vdoor{$x,$y} = 1; $x--; }
+ elsif ($first eq 'E') { $x++; $vdoor{$x,$y} = 1; }
+ $p->[0] = $x;
+ $p->[1] = $y;
+ $minx = $x if $minx > $x;
+ $maxx = $x if $maxx < $x;
+ $miny = $y if $miny > $y;
+ $maxy = $y if $maxy < $y;
+ }
+ } elsif ($first eq '|') {
+ push @rv, @npos;
+ @npos = map { [ @$_ ] } @$positions;
+ } elsif ($first eq ')') {
+ push @rv, @npos;
+ return (\@rv, $rest);
+ } elsif ($first eq '(') {
+ my ($n, $re1) = walk(\@npos, $rest);
+ @npos = @$n;
+ $rest = $re1;
+ }
+ $re = $rest;
+ }
+ return (\@npos, '');
+}
+
+sub print_map {
+ for my $y ($miny .. $maxy+1) {
+ for my $x ($minx .. $maxx) {
+ print $hdoor{$x,$y} ? '#-' : '##';
+ }
+ print "#\n";
+ if ($y <= $maxy) {
+ for my $x ($minx .. $maxx) {
+ print $vdoor{$x,$y} ? '|.' : '#.';
+ }
+ print "#\n";
+ }
+ }
+}
+
+walk([ [0, 0] ], $re);
+print_map();
+
+my @q = ([ 0, 0, 0 ]);
+my %seen;
+$seen{0,0} = 1;
+my $max_dist = 0;
+
+while (@q) {
+ my $h = shift @q;
+ my ($x, $y, $dist) = @$h;
+ if (!$max_dist || $dist > $max_dist) {
+ $max_dist = $dist;
+ }
+ if ($hdoor{$x,$y} && !$seen{$x,$y-1}++) {
+ push @q, [$x, $y-1, $dist+1];
+ }
+ if ($hdoor{$x,$y+1} && !$seen{$x,$y+1}++) {
+ push @q, [$x, $y+1, $dist+1];
+ }
+ if ($vdoor{$x,$y} && !$seen{$x-1,$y}++) {
+ push @q, [$x-1, $y, $dist+1];
+ }
+ if ($vdoor{$x+1,$y} && !$seen{$x+1,$y}++) {
+ push @q, [$x+1, $y, $dist+1];
+ }
+}
+
+say $max_dist;
+
+
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.36;
+use strict;
+use List::Util qw(max);
+use feature 'multidimensional';
+
+my (%hdoor, %vdoor);
+my ($minx, $miny, $maxx, $maxy) = (0, 0, 0, 0);
+my $was_change;
+my $maxdist;
+chomp (my $re = <>);
+
+$re =~ s/\A\^//;
+$re =~ s/\$\z//;
+
+sub walk($positions, $re) {
+ my @npos;
+ my %seen;
+ for my $p (@$positions) {
+ next if $seen{$p->[0],$p->[1]}++;
+ push @npos, [ @$p ];
+ }
+
+ say "len=", length($re), " npos=", scalar @npos;
+
+ my @rv;
+
+ while (length $re) {
+ my ($first, $rest) = $re =~ /\A(.)(.*)\z/;
+
+ if ($first =~ /[NSWE]/) {
+ for my $p (@npos) {
+ my ($x, $y) = @$p;
+ if ($first eq 'N') { $hdoor{$x,$y} = 1; $y--; }
+ elsif ($first eq 'S') { $y++; $hdoor{$x,$y} = 1; }
+ elsif ($first eq 'W') { $vdoor{$x,$y} = 1; $x--; }
+ elsif ($first eq 'E') { $x++; $vdoor{$x,$y} = 1; }
+ $p->[0] = $x;
+ $p->[1] = $y;
+ $minx = $x if $minx > $x;
+ $maxx = $x if $maxx < $x;
+ $miny = $y if $miny > $y;
+ $maxy = $y if $maxy < $y;
+ }
+ } elsif ($first eq '|') {
+ push @rv, @npos;
+ @npos = map { [ @$_ ] } @$positions;
+ } elsif ($first eq ')') {
+ push @rv, @npos;
+ return (\@rv, $rest);
+ } elsif ($first eq '(') {
+ my ($n, $re1) = walk(\@npos, $rest);
+ @npos = @$n;
+ $rest = $re1;
+ }
+ $re = $rest;
+ }
+ return (\@npos, '');
+}
+
+sub print_map {
+ for my $y ($miny .. $maxy+1) {
+ for my $x ($minx .. $maxx) {
+ print $hdoor{$x,$y} ? '#-' : '##';
+ }
+ print "#\n";
+ if ($y <= $maxy) {
+ for my $x ($minx .. $maxx) {
+ print $vdoor{$x,$y} ? '|.' : '#.';
+ }
+ print "#\n";
+ }
+ }
+}
+
+walk([ [0, 0] ], $re);
+print_map();
+
+my @q = ([ 0, 0, 0 ]);
+my %seen;
+$seen{0,0} = 1;
+my $max_dist = 0;
+my $rooms = 0;
+
+while (@q) {
+ my $h = shift @q;
+ my ($x, $y, $dist) = @$h;
+ $rooms++ if $dist >= 1000;
+
+ if (!$max_dist || $dist > $max_dist) {
+ $max_dist = $dist;
+ }
+ if ($hdoor{$x,$y} && !$seen{$x,$y-1}++) {
+ push @q, [$x, $y-1, $dist+1];
+ }
+ if ($hdoor{$x,$y+1} && !$seen{$x,$y+1}++) {
+ push @q, [$x, $y+1, $dist+1];
+ }
+ if ($vdoor{$x,$y} && !$seen{$x-1,$y}++) {
+ push @q, [$x-1, $y, $dist+1];
+ }
+ if ($vdoor{$x+1,$y} && !$seen{$x+1,$y}++) {
+ push @q, [$x+1, $y, $dist+1];
+ }
+}
+
+say $max_dist;
+
+say $rooms;
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.36;
+use strict;
+
+my @code;
+my @regs = ((0) x 6);
+my $ip_reg;
+
+while(<STDIN>) {
+ if (/\A#ip (\d+)/) {
+ $ip_reg = $1;
+ next;
+ }
+ chomp;
+ push @code, [ split /\s+/ ];
+}
+
+my $nmachines = shift @ARGV;
+my @machines;
+# push @machines, [ ($_, ((0) x 6)) ] for 0 .. $nmachines;
+push @machines, [ (10961197, ((0) x 6)) ];
+
+my %seen;
+
+sub one_step($regs) {
+ my $ip = $regs->[6];
+ my ($op, $a, $b, $c) = @{ $code[$ip] };
+
+ $regs->[$ip_reg] = $ip;
+
+ if ($op eq 'addr') {
+ $regs->[$c] = $regs->[$a] + $regs->[$b];
+ } elsif ($op eq 'addi') {
+ $regs->[$c] = $regs->[$a] + $b;
+ } elsif ($op eq 'mulr') {
+ $regs->[$c] = $regs->[$a] * $regs->[$b];
+ } elsif ($op eq 'muli') {
+ $regs->[$c] = $regs->[$a] * $b;
+ } elsif ($op eq 'banr') {
+ $regs->[$c] = $regs->[$a] & $regs->[$b];
+ } elsif ($op eq 'bani') {
+ $regs->[$c] = $regs->[$a] & $b;
+ } elsif ($op eq 'borr') {
+ $regs->[$c] = $regs->[$a] | $regs->[$b];
+ } elsif ($op eq 'bori') {
+ $regs->[$c] = $regs->[$a] | $b;
+ } elsif ($op eq 'setr') {
+ $regs->[$c] = $regs->[$a];
+ } elsif ($op eq 'seti') {
+ $regs->[$c] = $a;
+ } elsif ($op eq 'gtrr') {
+ $regs->[$c] = $regs->[$a] > $regs->[$b] ? 1 : 0;
+ } elsif ($op eq 'gtir') {
+ $regs->[$c] = $a > $regs->[$b] ? 1 : 0;
+ } elsif ($op eq 'eqrr') {
+ $regs->[$c] = $regs->[$a] == $regs->[$b] ? 1 : 0;
+ } elsif ($op eq 'eqri') {
+ $regs->[$c] = $regs->[$a] == $b ? 1 : 0;
+ } elsif ($op eq 'nop') {
+
+ } else {
+ die "Unknown op $ op at $ip";
+ }
+ say join(' ', @{ $code[$ip] }, "\t", @$regs);
+ # $regs->[3] = $ARGV[0] if $ip == 35;
+ $regs->[6] = $regs->[$ip_reg] + 1;
+
+# if ($seen{join(',', @$regs)}++) {
+# say "seen";
+# $regs->[6] = -1;
+# return 0;
+# }
+ if ($regs->[6] >= @code) {
+ say "halts";
+ return 1;
+ } else {
+ return undef;
+ }
+}
+
+my $step = 0;
+while (1) {
+ say "step ", ++$step;
+ for my $m (0 .. $#machines) {
+ next if $machines[$m]->[6] == -1;
+ # say "machine $m";
+ if (one_step($machines[$m])) {
+ say "Machine $m halts after ste $step";
+ exit 0;
+ }
+ }
+}
+
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.36;
+use strict;
+
+my @code;
+my @regs = ((0) x 6);
+my $ip_reg;
+
+while(<STDIN>) {
+ if (/\A#ip (\d+)/) {
+ $ip_reg = $1;
+ next;
+ }
+ chomp;
+ push @code, [ split /\s+/ ];
+}
+
+my $nmachines = shift @ARGV;
+my @machines;
+# push @machines, [ ($_, ((0) x 6)) ] for 0 .. $nmachines;
+push @machines, [ (10961198, ((0) x 6)) ];
+
+my %seen;
+my $prev_halting;
+
+sub one_step($regs) {
+ my $ip = $regs->[6];
+ my ($op, $a, $b, $c) = @{ $code[$ip] };
+
+ $regs->[$ip_reg] = $ip;
+
+ if ($op eq 'addr') {
+ $regs->[$c] = $regs->[$a] + $regs->[$b];
+ } elsif ($op eq 'addi') {
+ $regs->[$c] = $regs->[$a] + $b;
+ } elsif ($op eq 'mulr') {
+ $regs->[$c] = $regs->[$a] * $regs->[$b];
+ } elsif ($op eq 'muli') {
+ $regs->[$c] = $regs->[$a] * $b;
+ } elsif ($op eq 'banr') {
+ $regs->[$c] = $regs->[$a] & $regs->[$b];
+ } elsif ($op eq 'bani') {
+ $regs->[$c] = $regs->[$a] & $b;
+ } elsif ($op eq 'borr') {
+ $regs->[$c] = $regs->[$a] | $regs->[$b];
+ } elsif ($op eq 'bori') {
+ $regs->[$c] = $regs->[$a] | $b;
+ } elsif ($op eq 'setr') {
+ $regs->[$c] = $regs->[$a];
+ } elsif ($op eq 'seti') {
+ $regs->[$c] = $a;
+ } elsif ($op eq 'gtrr') {
+ $regs->[$c] = $regs->[$a] > $regs->[$b] ? 1 : 0;
+ } elsif ($op eq 'gtir') {
+ $regs->[$c] = $a > $regs->[$b] ? 1 : 0;
+ } elsif ($op eq 'eqrr') {
+ $regs->[$c] = $regs->[$a] == $regs->[$b] ? 1 : 0;
+ } elsif ($op eq 'eqri') {
+ $regs->[$c] = $regs->[$a] == $b ? 1 : 0;
+ } elsif ($op eq 'nop') {
+
+ } else {
+ die "Unknown op $ op at $ip";
+ }
+ # say join(' ', @{ $code[$ip] }, "\t", @$regs);
+ # $regs->[3] = $ARGV[0] if $ip == 35;
+ $regs->[6] = $regs->[$ip_reg] + 1;
+
+ if ($regs->[6] == 28) {
+ if ($seen{$regs->[4]}++) {
+ say "$regs->[4] already seen";
+ return 1;
+ }
+ say "halts at $regs->[4]\t", sprintf("%08x", $regs->[4]);
+ $prev_halting = $regs->[4];
+ }
+
+# if ($seen{join(',', @$regs)}++) {
+# say "seen";
+# $regs->[6] = -1;
+# return 0;
+# }
+ if ($regs->[6] >= @code) {
+ say "halts";
+ return 1;
+ } else {
+ return undef;
+ }
+}
+
+my $step = 0;
+while (1) {
+ ++$step;
+ # say "step ", ++$step;
+ for my $m (0 .. $#machines) {
+ next if $machines[$m]->[6] == -1;
+ # say "machine $m";
+ if (one_step($machines[$m])) {
+ say "Machine $m halts after step $step prev $prev_halting";
+ exit 0;
+ }
+ }
+}
+
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.36;
+use strict;
+use feature 'multidimensional';
+
+my $depth = 11739;
+my ($tx, $ty) = (11,718);
+
+my %gi;
+sub gi2el($gi) { ($gi + $depth) % 20183 }
+my %el;
+
+my $sum = 0;
+for my $n (1 .. $tx + $ty) {
+ my $y = 0;
+ my $x = $n;
+ for my $i (0 .. $n) {
+ if (($x == 0 && $y == 0) || ($x == $tx && $y == $ty)) {
+ $gi{$x,$y} = 0;
+ } elsif ($y == 0) {
+ $gi{$x,$y} = 16807*$x;
+ } elsif ($x == 0) {
+ $gi{$x,$y} = 48271*$y;
+ } else {
+ $gi{$x,$y} = $el{$x-1,$y} * $el{$x,$y-1};
+ }
+ $el{$x,$y} = gi2el($gi{$x,$y});
+ $sum += $el{$x,$y} % 3
+ if $x <= $tx && $y <= $ty;
+ $y++;
+ $x--;
+ }
+}
+
+say $sum;
+
+
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.36;
+use strict;
+use feature 'multidimensional';
+
+my $depth = 11739;
+my ($tx, $ty) = (11,718);
+
+my %gi;
+sub gi2el($gi) { ($gi + $depth) % 20183 }
+my %el;
+my %type;
+
+sub can_use($x, $y, $gear) {
+ return 1 if
+ $type{$x,$y} == 0 && $gear
+ || $type{$x,$y} == 1 && ($gear == 0 || $gear == 2)
+ || $type{$x,$y} == 2 && ($gear == 0 || $gear == 1);
+ return 0;
+}
+
+my $sum = 0;
+for my $n (0 .. 6*($tx + $ty)) {
+ my $y = 0;
+ my $x = $n;
+ for my $i (0 .. $n) {
+ if (($x == 0 && $y == 0) || ($x == $tx && $y == $ty)) {
+ $gi{$x,$y} = 0;
+ } elsif ($y == 0) {
+ $gi{$x,$y} = 16807*$x;
+ } elsif ($x == 0) {
+ $gi{$x,$y} = 48271*$y;
+ } else {
+ $gi{$x,$y} = $el{$x-1,$y} * $el{$x,$y-1};
+ }
+ $el{$x,$y} = gi2el($gi{$x,$y});
+ $type{$x,$y} = $el{$x,$y} % 3;
+ $y++;
+ $x--;
+ }
+}
+
+my @q = ([0, 0, 0, 1]);
+
+my %map;
+$map{0,0,1} = 0;
+
+say "walking the graph";
+my $now = 0;
+while (@q) {
+ my @current_minute;
+ while (@q && $q[0]->[0] <= $now) {
+ push @current_minute, shift @q;
+ }
+ say scalar(@current_minute), " states in $now";
+
+ for my $state (@current_minute) {
+ my ($time, $x, $y, $tool) = @$state;
+ say "state $x,$y,$tool";
+ next if !defined $type{$x,$y};
+ $map{$x,$y,$tool} = $now
+ if !defined $map{$x,$y,$tool}
+ || $map{$x,$y,$tool} > $now;
+
+ if ($x == $tx && $y == $ty) {
+ say "Target reached at $now";
+ exit 0;
+ }
+ $map{$x,$y,$tool} = $now if !defined $map{$x,$y,$tool}
+ || $map{$x,$y,$tool} > $now;
+
+ if ($tool != 0 && can_use($x, $y, 0)
+ && (!defined $map{$x,$y,0}
+ || ($map{$x,$y,0} > $now+7))) {
+ push @q, [ $now+7, $x, $y, 0];
+ $map{$x,$y,$0} = $now+7;
+ }
+
+ if ($tool != 1 && can_use($x, $y, 1)
+ && (!defined $map{$x,$y,1}
+ || ($map{$x,$y,1} > $now+7))) {
+ push @q, [ $now+7, $x, $y, 1];
+ $map{$x,$y,1} = $now+7;
+ }
+
+ if ($tool != 2 && can_use($x, $y, 2)
+ && (!defined $map{$x,$y,2}
+ || ($map{$x,$y,2} > $now+7))) {
+ push @q, [ $now+7, $x, $y, 2];
+ $map{$x,$y,2} = $now+7;
+ }
+
+ for my $v ([-1, 0], [0, -1], [1, 0], [0, 1]) {
+ my ($dx, $dy) = @$v;
+ next if $x+$dx < 0 || $y+$dy < 0;
+ next if !can_use($x+$dx, $y+$dy, $tool);
+ next if defined $map{$x+$dx,$y+$dy,$tool}
+ && $map{$x+$dx,$y+$dy,$tool} <= $now+1;
+ unshift @q, [ $now+1, $x+$dx, $y+$dy, $tool ];
+ $map{$x+$dx,$y+$dy,$tool} = $now+1;
+ }
+ }
+ $now++;
+}
+
+say join(',', $map{$tx,$ty,0}, $map{$tx,$ty,1}, $map{$tx,$ty,2});
+
+
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.34;
+use strict;
+
+my @bots;
+my $highest;
+while (<>) {
+ my ($x, $y, $z, $r) = /(-?\d+)/g;
+ push @bots, [$x, $y, $z, $r];
+ if (!$highest || $highest->[3] < $r) {
+ $highest = [$x, $y, $z, $r];
+ }
+}
+
+my ($x, $y, $z, $r) = @$highest;
+
+my $in_range;
+for my $bot (@bots) {
+ $in_range++ if
+ abs($bot->[0] - $x)
+ + abs($bot->[1] - $y)
+ + abs($bot->[2] - $z) <= $r;
+}
+
+say $in_range;
+
+
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.36;
+use strict;
+
+my @bots = sort { $b->[3] <=> $a->[3] } map { [ /(-?\d+)/g ] } <>;
+# my @bots = map { [ /(-?\d+)/g ] } <>;
+
+sub overlaps($bot1, $bot2) {
+ abs($bot1->[0] - $bot2->[0])
+ + abs($bot1->[1] - $bot2->[1])
+ + abs($bot1->[2] - $bot2->[2]) <= $bot1->[3] + $bot2->[3];
+}
+
+my @best;
+
+sub walk($subset) {
+ our $loop;
+ our $min_sub = @$subset if !defined $min_sub || @$subset < $min_sub;
+ if (++$loop % 10000 == 0) {
+ say "walk: best ", scalar @best,
+ ' min ', $min_sub;
+ $min_sub = undef;
+ die;
+ }
+ my $to_check = @bots - $subset->[-1];
+ return if @$subset + $to_check < @best;
+ CANDIDATE:
+ for my $i ($subset->[-1] + 1 .. $#bots) {
+ for my $j (@$subset) {
+ next CANDIDATE if !overlaps($bots[$i], $bots[$j]);
+ }
+ walk([ @$subset, $i ]);
+ }
+
+ if (@best < @$subset) {
+ @best = @$subset;
+ }
+}
+
+# eval { say "walk($_)"; walk([$_]) } for 0 .. $#bots;
+eval { walk([0]); };
+
+@best = sort { $bots[$a]->[3] <=> $bots[$b]->[3] } @best;
+say "Best subset has ", scalar @best, " members";
+say "Smallest is ", join(',', @{ $bots[$best[0]] });
+
+sub try_overlaps {
+ for my $j (1 .. $#best) {
+ last return undef
+ if !overlaps($bots[$best[0]], $bots[$best[$j]]);
+ }
+ return 1;
+}
+
+my $shrink;
+DIM:
+while (1) {
+ say "Trying diameter $bots[$best[0]]->[3]";
+ if (try_overlaps) {
+ last DIM if $bots[$best[0]]->[3] == 0;
+ $shrink = int($bots[$best[0]]->[3]/4);
+ $shrink = 1 if $shrink < 1;
+ $bots[$best[0]]->[3] -= $shrink;
+ next;
+ }
+ for my $dir ([-1, 0, 0], [1, 0, 0], [0, -1, 0], [0, 1, 0], [0, 0, -1], [0, 0, 1]) {
+ say "trying ", join(',', @$dir);
+ for (0 .. 2) {
+ $bots[$best[0]]->[$_] += $shrink*$dir->[$_];
+ }
+ next DIM if try_overlaps;
+ say "failed";
+ for (0 .. 2) {
+ $bots[$best[0]]->[$_] -= $shrink*$dir->[$_];
+ }
+ }
+ die "nothing matched. strange";
+}
+say "end: ", join(',', @{ $bots[$best[0]] });
+say $bots[$best[0]]->[0]
+ + $bots[$best[0]]->[1]
+ + $bots[$best[0]]->[2];
+
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.36;
+use strict;
+
+my @armies_txt;
+{ local $/ = "\n\n"; chomp (@armies_txt = <>); }
+
+my @units = map { parse_army($_) } @armies_txt;
+use Data::Dumper;
+# print Dumper \@units;
+
+sub parse_army($text) {
+ my ($name, @rest) = split /\n/, $text;
+ $name =~ s/:\z//;
+ my $i = 0;
+ return ( map { parse_group($name, ++$i, $_) }
+ grep { length $_ } @rest );
+}
+
+sub parse_group($army, $id, $text) {
+ my %rv = ( army => $army, name => "$army group $id", text => $text );
+ ($rv{units}) = $text =~ /\A(\d+) unit/;
+ ($rv{hp}) = $text =~ / (\d+) hit point/;
+ ($rv{hp}) = $text =~ / (\d+) hit point/;
+ ($rv{damage}, $rv{damage_type})
+ = $text =~ /that does (\d+) (\w+) damage /;
+ ($rv{init}) = $text =~ /at initiative (\d+)\z/;
+ $rv{weak} = {
+ map { defined $_ ? ($_ => 1) : () }
+ $text =~ /weak to (\w+)(?:, (\w+))*/
+ };
+ $rv{immune} = {
+ map { defined $_ ? ($_ => 1) : () }
+ $text =~ /immune to (\w+)(?:, (\w+))*/
+ };
+ return \%rv;
+}
+
+sub e_pwr($unit) { return $unit->{units} * $unit->{damage} };
+
+sub damage_to($attacker, $defender) {
+ return 0 if $defender->{immune}->{$attacker->{damage_type}};
+ my $power = e_pwr($attacker);
+ $power *= 2 if $defender->{weak}->{$attacker->{damage_type}};
+ return $power;
+}
+
+sub selection {
+ say "";
+ my @rv;
+ my %attacked;
+ for my $unit (sort {
+ $b->{units}*$b->{damage} <=> $a->{units}*$a->{damage}
+ || $b->{init} <=> $a->{init}
+ } @units) {
+ next if !$unit->{units};
+ for my $def (sort {
+ damage_to($unit, $b) <=> damage_to($unit, $a)
+ || e_pwr($b) <=> e_pwr($a)
+ || $b->{init} <=> $a->{init}
+ }
+ grep {
+ $_->{army} ne $unit->{army}
+ && $_->{units}
+ && !$attacked{$_->{name}}
+ } @units) {
+ my $d = damage_to($unit, $def);
+ next if !$d;
+ say $unit->{name}, ' => ', $def->{name}, ': ', $d;
+ $attacked{$def->{name}} = $unit->{name};
+ push @rv, [ $unit, $def ];
+ last;
+ }
+ }
+ return \@rv;
+}
+
+sub attack($items) {
+ say "";
+ for my $item (sort { $b->[0]->{init} <=> $a->[0]->{init} } @$items) {
+ my ($att, $def) = @$item;
+ next if !$att->{units};
+ my $killed = int(damage_to($att, $def) / $def->{hp});
+ $killed = $def->{units} if $killed > $def->{units};
+
+ say "$att->{name} attacks $def->{name} killing $killed units";
+ $def->{units} -= $killed;
+ $def->{units} = 0 if $def->{units} < 0;
+ }
+}
+
+my $round = 0;
+while (1) {
+ say "round ", $round++;
+ my %alive;
+ my $sum;
+ for my $unit (@units) {
+ next if !$unit->{units};
+ say "$unit->{name} contains $unit->{units} units";
+ $sum += $unit->{units};
+ $alive{$unit->{army}}++;
+ }
+ if (keys(%alive) < 2) {
+ say "sum=$sum";
+ last;
+ }
+ attack(selection());
+}
+
+__END__
+Immune System:
+889 units each with 3275 hit points (weak to bludgeoning, radiation; immune to cold) with an attack that does 36 bludgeoning damage at initiative 12
+94 units each with 1336 hit points (weak to radiation, cold) with an attack that does 127 bludgeoning damage at initiative 7
+1990 units each with 5438 hit points with an attack that does 25 slashing damage at initiative 20
+
+
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.36;
+use strict;
+
+my @armies_txt;
+{ local $/ = "\n\n"; chomp (@armies_txt = <>); }
+
+my @units = map { parse_army($_) } @armies_txt;
+use Data::Dumper;
+print Dumper \@units;
+
+sub parse_army($text) {
+ my ($name, @rest) = split /\n/, $text;
+ $name =~ s/:\z//;
+ my $i = 0;
+ return ( map { parse_group($name, ++$i, $_) }
+ grep { length $_ } @rest );
+}
+
+sub parse_group($army, $id, $text) {
+ my %rv = ( army => $army, name => "$army group $id", text => $text );
+ ($rv{units}) = $text =~ /\A(\d+) unit/;
+ ($rv{hp}) = $text =~ / (\d+) hit point/;
+ ($rv{damage}, $rv{damage_type})
+ = $text =~ /that does (\d+) (\w+) damage /;
+ ($rv{init}) = $text =~ /at initiative (\d+)\z/;
+ my ($l) = $text =~ /weak to ([^;\)]+)[\);]/;
+
+ $rv{weak} = { map { $_ => 1 } split(/, /, $l) }
+ if length $l;
+ ($l) = $text =~ /immune to ([^;\)]+)[\);]/;
+ $rv{immune} = { map { $_ => 1 } split(/, /, $l) }
+ if length $l;
+ $rv{orig_units} = $rv{units};
+ $rv{orig_damage} = $rv{damage};
+ return \%rv;
+}
+
+sub e_pwr($unit) { return $unit->{units} * $unit->{damage} };
+
+sub damage_to($attacker, $defender) {
+ return 0 if $defender->{immune}->{$attacker->{damage_type}};
+ my $power = e_pwr($attacker);
+ $power *= 2 if $defender->{weak}->{$attacker->{damage_type}};
+ return $power;
+}
+
+sub selection {
+ say "";
+ my @rv;
+ my %attacked;
+ for my $unit (sort {
+ $b->{units}*$b->{damage} <=> $a->{units}*$a->{damage}
+ || $b->{init} <=> $a->{init}
+ } @units) {
+ next if !$unit->{units};
+ for my $def (sort {
+ damage_to($unit, $b) <=> damage_to($unit, $a)
+ || e_pwr($b) <=> e_pwr($a)
+ || $b->{init} <=> $a->{init}
+ }
+ grep {
+ $_->{army} ne $unit->{army}
+ && $_->{units}
+ && !$attacked{$_->{name}}
+ } @units) {
+ my $d = damage_to($unit, $def);
+ next if !$d;
+ say $unit->{name}, ' => ', $def->{name}, ': ', $d;
+ $attacked{$def->{name}} = $unit->{name};
+ push @rv, [ $unit, $def ];
+ last;
+ }
+ }
+ return \@rv;
+}
+
+sub attack($items) {
+ say "";
+ my $total_killed = 0;
+ for my $item (sort { $b->[0]->{init} <=> $a->[0]->{init} } @$items) {
+ my ($att, $def) = @$item;
+ next if !$att->{units};
+ my $killed = int(damage_to($att, $def) / $def->{hp});
+ $killed = $def->{units} if $killed > $def->{units};
+ $total_killed += $killed;
+
+ say "$att->{name} attacks $def->{name} killing $killed units";
+ $def->{units} -= $killed;
+ $def->{units} = 0 if $def->{units} < 0;
+ }
+ return $total_killed;
+}
+
+sub battle($boost) {
+ my $round = 0;
+
+ for my $unit (@units) {
+ $unit->{units} = $unit->{orig_units};
+ $unit->{damage} = $unit->{orig_damage};
+ $unit->{damage} += $boost
+ if $unit->{army} eq 'Immune System';
+ }
+
+ say " ################## boost $boost ##################### ";
+ while (1) {
+ say "round ", $round++;
+ my %alive;
+ my $sum;
+ for my $unit (@units) {
+ next if !$unit->{units};
+ say "$unit->{name} contains $unit->{units} units";
+ $sum += $unit->{units};
+ $alive{$unit->{army}}++;
+ }
+ if (keys(%alive) < 2) {
+ say "sum=$sum";
+ return $alive{"Immune System"} ? 1 : 0;
+ }
+ return 0 if !attack(selection());
+ }
+}
+
+my $boost = 0;
+1 while !battle($boost++);
+
+__END__
+Immune System:
+889 units each with 3275 hit points (weak to bludgeoning, radiation; immune to cold) with an attack that does 36 bludgeoning damage at initiative 12
+94 units each with 1336 hit points (weak to radiation, cold) with an attack that does 127 bludgeoning damage at initiative 7
+1990 units each with 5438 hit points with an attack that does 25 slashing damage at initiative 20
+
+Infection group 4 attacks Immune System group 9 killing 0 units
+6523
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.36;
+use strict;
+use List::Util qw(sum);
+use experimental 'multidimensional';
+
+my @points = map { chomp; [ split /,/ ] } <>;
+
+sub dist($p1, $p2) { my $sum; $sum+=abs($p1->[$_]-$p2->[$_]) for 0 .. 3; $sum };
+
+my $n_const;
+my %const_of;
+for my $x (0 .. $#points) {
+ for my $y ($x .. $#points) {
+ next if dist($points[$x], $points[$y]) > 3;
+ say "dist($x,$y) = ", dist($points[$x], $points[$y]);
+ if (!defined $const_of{$x} && !defined $const_of{$y}) {
+ $const_of{$x} = $const_of{$y} = ++$n_const;
+ } elsif (!defined $const_of{$x}) {
+ $const_of{$x} = $const_of{$y};
+ } elsif (!defined $const_of{$y}) {
+ $const_of{$y} = $const_of{$x};
+ } elsif ($const_of{$x} != $const_of{$y}) {
+ # both different, join them
+ say "adding $const_of{$y} to $const_of{$x}";
+ my $to_del = $const_of{$y};
+ for my $p (0 .. $#points) {
+ $const_of{$p} = $const_of{$x}
+ if defined $const_of{$p}
+ && $const_of{$p} == $to_del;
+ }
+ }
+ say " $x in $const_of{$x}, $y in $const_of{$y}";
+ }
+}
+
+say "$_ in $const_of{$_}" for sort keys %const_of;
+my %const_used = map { $_ => 1 } values %const_of;
+say join(',', keys %const_used);
+say scalar keys %const_used;
+
--- /dev/null
+#!/bin/bash
+
+DAY=`date +%d|sed 's/ //g'`
+test -n "$1" && DAY="$1"
+FILE="$((2*DAY - 1))in.txt"
+COOKIE=`cat cookie`
+
+START="6:00:02"
+MAXWAIT=300
+STARTSEC=`date -d "$START" "+%s"`
+NOW=`date "+%s"`
+WAITSEC=`expr $STARTSEC - $NOW`
+
+if [ $WAITSEC -gt 0 -a $WAITSEC -lt $MAXWAIT ]
+then
+ echo "Waiting for $WAITSEC seconds till $START for getting $FILE ..."
+ sleep $WAITSEC
+fi
+
+URL="https://adventofcode.com/2018/day/$DAY/input"
+echo
+echo "Downloading $URL to $FILE"
+curl -s -b "$COOKIE" "$URL" --output "$FILE"
+echo ========================================================================
+cat "$FILE"
+echo ========================================================================
+echo "lines words chars"
+wc "$FILE"
+echo