--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.16;
+
+my @map = map { chomp; [ split // ] } <>;
+my $maxx = @{ $map[0] };
+my $maxy = @map;
+
+$; = ',';
+
+my %portals;
+
+sub add_portal {
+ my ($name, $x, $y) = @_;
+ say "add_portal: $name, $x, $y";
+ if (defined $portals{$name}) {
+ my ($x1, $y1) = @{ $portals{$name} };
+ $map[$y][$x] = [$x1, $y1];
+ $map[$y1][$x1] = [$x, $y];
+ say "portal $name: $x,$y <==> $x1,$y1";
+ } else {
+ $portals{$name} = [$x, $y];
+ }
+}
+
+sub find_portals {
+ my @d = @_;
+ for my $x (2 .. $maxx-3) {
+ for my $y (2 .. $maxy-3) {
+ next if $map[$y][$x] ne '.';
+ my $l1 = $map[$y+$d[1]][$x+$d[0]];
+ my $l2 = $map[$y+$d[3]][$x+$d[2]];
+ next if "$l1$l2" !~ /^[A-Z][A-Z]$/;
+ add_portal("$l1$l2", $x, $y);
+ }
+ }
+}
+
+find_portals(0, -2, 0, -1);
+find_portals(0, 1, 0, 2);
+find_portals(-2, 0, -1, 0);
+find_portals(1, 0, 2, 0);
+
+
+my %seen;
+my @q = [ @{ $portals{'AA'} }, 0 ];
+my ($ex, $ey) = @{ $portals{'ZZ'} };
+say "walking towards $ex, $ey";
+
+while (@q) {
+ my ($x, $y, $steps) = @{ shift @q };
+ next if $seen{$x,$y}++;
+ say "at $x,$y $steps";
+
+ if ($x == $ex && $y == $ey) {
+ say "Found after $steps steps";
+ last;
+ }
+
+ $steps++;
+ if (ref $map[$y][$x]) {
+ my ($nx, $ny) = @{ $map[$y][$x] };
+ push @q, [ $nx, $ny, $steps ];
+ }
+ for my ($dx, $dy) (0, 1, 1, 0, -1, 0, 0, -1) {
+ my $nx = $x + $dx;
+ my $ny = $y + $dy;
+ next if $map[$ny][$nx] ne '.' && !ref $map[$ny][$nx];
+ push @q, [$nx, $ny, $steps];
+ }
+}
+
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.16;
+
+my @map = map { chomp; [ split // ] } <>;
+my $maxx = @{ $map[0] };
+my $maxy = @map;
+
+$; = ',';
+
+my %portals;
+
+sub add_portal {
+ my ($name, $x, $y) = @_;
+ say "add_portal: $name, $x, $y";
+ if (defined $portals{$name}) {
+ my ($x1, $y1) = @{ $portals{$name} };
+ $map[$y][$x] = [$x1, $y1];
+ $map[$y1][$x1] = [$x, $y];
+ say "portal $name: $x,$y <==> $x1,$y1";
+ } else {
+ $portals{$name} = [$x, $y];
+ }
+}
+
+sub find_portals {
+ my @d = @_;
+ for my $x (2 .. $maxx-3) {
+ for my $y (2 .. $maxy-3) {
+ next if $map[$y][$x] ne '.';
+ my $l1 = $map[$y+$d[1]][$x+$d[0]];
+ my $l2 = $map[$y+$d[3]][$x+$d[2]];
+ next if "$l1$l2" !~ /^[A-Z][A-Z]$/;
+ add_portal("$l1$l2", $x, $y);
+ }
+ }
+}
+
+find_portals(0, -2, 0, -1);
+find_portals(0, 1, 0, 2);
+find_portals(-2, 0, -1, 0);
+find_portals(1, 0, 2, 0);
+
+
+my %seen;
+my @q = [ @{ $portals{'AA'} }, 0, 0 ];
+my ($ex, $ey) = @{ $portals{'ZZ'} };
+say "walking towards $ex, $ey, 0";
+
+while (@q) {
+ my ($x, $y, $z, $steps) = @{ shift @q };
+ next if $seen{$x,$y,$z}++;
+ say "at $x,$y,$z $steps";
+
+ if ($x == $ex && $y == $ey && $z == 0) {
+ say "Found after $steps steps";
+ last;
+ }
+
+ $steps++;
+ if (ref $map[$y][$x]) {
+ my ($nx, $ny) = @{ $map[$y][$x] };
+ if ($x == 2 || $y == 2 || $x == $maxx - 3 || $y == $maxy - 3) {
+ if ($z > 0) {
+ push @q, [ $nx, $ny, $z-1, $steps ];
+ }
+ } else {
+ push @q, [ $nx, $ny, $z+1, $steps ];
+ }
+ }
+ for my ($dx, $dy) (0, 1, 1, 0, -1, 0, 0, -1) {
+ my $nx = $x + $dx;
+ my $ny = $y + $dy;
+ next if $map[$ny][$nx] ne '.' && !ref $map[$ny][$nx];
+ push @q, [$nx, $ny, $z, $steps];
+ }
+}
+
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.16;
+
+package IntComp;
+use bigint;
+
+sub new {
+ my ($class, $mem, $inputs) = @_;
+ my $self = {
+ mem => [ @$mem ],
+ pc => 0,
+ want_input => undef,
+ base => 0,
+ };
+ $self->{inputs} = [ @$inputs ]
+ if defined $inputs;
+ bless $self, $class;
+}
+
+sub clone {
+ my ($class, $other) = @_;
+ my $self = {
+ mem => [ @{ $other->{mem} } ],
+ pc => $other->{pc},
+ want_input => undef,
+ base => $other->{base},
+ inputs => [ ],
+ };
+ bless $self, $class;
+}
+
+sub m2val {
+ my ($self, $off, $mode) = @_;
+ if ($mode == 0) {
+ return $self->{mem}->[ $self->{mem}->[$self->{pc} + $off] ] // 0;
+ } elsif ($mode == 1) {
+ return $self->{mem}->[$self->{pc} + $off] // 0;
+ } elsif ($mode == 2) {
+ return $self->{mem}->[ $self->{mem}->[$self->{pc} + $off ] + $self->{base} ] // 0;
+ }
+}
+
+sub m2pos {
+ my ($self, $off, $mode) = @_;
+ if ($mode == 0) {
+ return $self->{mem}->[$self->{pc} + $off];
+ } elsif ($mode == 2) {
+ return $self->{mem}->[$self->{pc} + $off] + $self->{base};
+ }
+}
+
+sub input {
+ my ($self, @input) = @_;
+ push @{ $self->{inputs} }, @input;
+}
+
+sub run {
+ my ($self, @input) = @_;
+ my $mem = $self->{mem};
+
+ push @{ $self->{inputs} }, @input;
+ if (defined $self->{want_input}) {
+ $mem->[$self->{want_input}] = shift @{ $self->{inputs} };
+ $self->{want_input} = undef;
+ }
+
+ while (1) {
+ my $opcode = $mem->[$self->{pc}];
+ # say "pc=", $self->{pc}, " opcode=$opcode";
+ # say "mem=", join(',', map { $_ // '_' } @{ $self->{mem} });
+ my $op = int($opcode % 100);
+ my $m1 = int($opcode / 100) % 10;
+ my $m2 = int($opcode / 1000) % 10;
+ my $m3 = int($opcode / 10000) % 10;
+ if ($op == 1) {
+ $mem->[ m2pos($self, 3, $m3) ]
+ = m2val($self, 1, $m1)
+ + m2val($self, 2, $m2);
+ $self->{pc} += 4;
+ } elsif ($op == 2) {
+ $mem->[ m2pos($self, 3, $m3) ]
+ = m2val($self, 1, $m1)
+ * m2val($self, 2, $m2);
+ $self->{pc} += 4;
+ } elsif ($op == 3) {
+ if (@{ $self->{inputs} }) {
+ $mem->[ m2pos($self, 1, $m1) ]
+ = shift @{ $self->{inputs} };
+ $self->{pc} += 2;
+ } else {
+ $self->{want_input} = m2pos($self, 1, $m1);
+ $self->{pc} += 2;
+ return undef;
+ }
+ } elsif ($op == 4) {
+ my $val = m2val($self, 1, $m1);
+ $self->{pc} += 2;
+ return $val;
+ } elsif ($op == 5) {
+ if (m2val($self, 1, $m1)) {
+ $self->{pc} = m2val($self, 2, $m2);
+ } else {
+ $self->{pc} += 3;
+ }
+ } elsif ($op == 6) {
+ if (!m2val($self, 1, $m1)) {
+ $self->{pc} = m2val($self, 2, $m2);
+ } else {
+ $self->{pc} += 3;
+ }
+ } elsif ($op == 7) {
+ $mem->[ m2pos($self, 3, $m3) ] =
+ m2val($self, 1, $m1)
+ < m2val($self, 2, $m2) ? 1 : 0;
+ $self->{pc} += 4;
+ } elsif ($op == 8) {
+ $mem->[ m2pos($self, 3, $m3) ] =
+ m2val($self, 1, $m1)
+ == m2val($self, 2, $m2) ? 1 : 0;
+ $self->{pc} += 4;
+ } elsif ($op == 9) {
+ $self->{base} += m2val($self, 1, $m1);
+ $self->{pc} += 2;
+ } elsif ($op == 99) {
+ return undef;
+ }
+ }
+}
+
+package main;
+
+chomp (my @mem = split /,/, <>);
+
+$; = ',';
+
+my $input = <<EOF;
+NOT A J
+NOT B T
+OR T J
+NOT C T
+OR T J
+AND D J
+WALK
+EOF
+
+my $comp = IntComp->new(\@mem, [ map { ord } split //, $input ]);
+
+while (my $rv = $comp->run()) {
+ if ($rv > 126) {
+ say $rv;
+ } else {
+ print chr($rv);
+ }
+}
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.16;
+
+package IntComp;
+use bigint;
+
+sub new {
+ my ($class, $mem, $inputs) = @_;
+ my $self = {
+ mem => [ @$mem ],
+ pc => 0,
+ want_input => undef,
+ base => 0,
+ };
+ $self->{inputs} = [ @$inputs ]
+ if defined $inputs;
+ bless $self, $class;
+}
+
+sub clone {
+ my ($class, $other) = @_;
+ my $self = {
+ mem => [ @{ $other->{mem} } ],
+ pc => $other->{pc},
+ want_input => undef,
+ base => $other->{base},
+ inputs => [ ],
+ };
+ bless $self, $class;
+}
+
+sub m2val {
+ my ($self, $off, $mode) = @_;
+ if ($mode == 0) {
+ return $self->{mem}->[ $self->{mem}->[$self->{pc} + $off] ] // 0;
+ } elsif ($mode == 1) {
+ return $self->{mem}->[$self->{pc} + $off] // 0;
+ } elsif ($mode == 2) {
+ return $self->{mem}->[ $self->{mem}->[$self->{pc} + $off ] + $self->{base} ] // 0;
+ }
+}
+
+sub m2pos {
+ my ($self, $off, $mode) = @_;
+ if ($mode == 0) {
+ return $self->{mem}->[$self->{pc} + $off];
+ } elsif ($mode == 2) {
+ return $self->{mem}->[$self->{pc} + $off] + $self->{base};
+ }
+}
+
+sub input {
+ my ($self, @input) = @_;
+ push @{ $self->{inputs} }, @input;
+}
+
+sub run {
+ my ($self, @input) = @_;
+ my $mem = $self->{mem};
+
+ push @{ $self->{inputs} }, @input;
+ if (defined $self->{want_input}) {
+ $mem->[$self->{want_input}] = shift @{ $self->{inputs} };
+ $self->{want_input} = undef;
+ }
+
+ while (1) {
+ my $opcode = $mem->[$self->{pc}];
+ # say "pc=", $self->{pc}, " opcode=$opcode";
+ # say "mem=", join(',', map { $_ // '_' } @{ $self->{mem} });
+ my $op = int($opcode % 100);
+ my $m1 = int($opcode / 100) % 10;
+ my $m2 = int($opcode / 1000) % 10;
+ my $m3 = int($opcode / 10000) % 10;
+ if ($op == 1) {
+ $mem->[ m2pos($self, 3, $m3) ]
+ = m2val($self, 1, $m1)
+ + m2val($self, 2, $m2);
+ $self->{pc} += 4;
+ } elsif ($op == 2) {
+ $mem->[ m2pos($self, 3, $m3) ]
+ = m2val($self, 1, $m1)
+ * m2val($self, 2, $m2);
+ $self->{pc} += 4;
+ } elsif ($op == 3) {
+ if (@{ $self->{inputs} }) {
+ $mem->[ m2pos($self, 1, $m1) ]
+ = shift @{ $self->{inputs} };
+ $self->{pc} += 2;
+ } else {
+ $self->{want_input} = m2pos($self, 1, $m1);
+ $self->{pc} += 2;
+ return undef;
+ }
+ } elsif ($op == 4) {
+ my $val = m2val($self, 1, $m1);
+ $self->{pc} += 2;
+ return $val;
+ } elsif ($op == 5) {
+ if (m2val($self, 1, $m1)) {
+ $self->{pc} = m2val($self, 2, $m2);
+ } else {
+ $self->{pc} += 3;
+ }
+ } elsif ($op == 6) {
+ if (!m2val($self, 1, $m1)) {
+ $self->{pc} = m2val($self, 2, $m2);
+ } else {
+ $self->{pc} += 3;
+ }
+ } elsif ($op == 7) {
+ $mem->[ m2pos($self, 3, $m3) ] =
+ m2val($self, 1, $m1)
+ < m2val($self, 2, $m2) ? 1 : 0;
+ $self->{pc} += 4;
+ } elsif ($op == 8) {
+ $mem->[ m2pos($self, 3, $m3) ] =
+ m2val($self, 1, $m1)
+ == m2val($self, 2, $m2) ? 1 : 0;
+ $self->{pc} += 4;
+ } elsif ($op == 9) {
+ $self->{base} += m2val($self, 1, $m1);
+ $self->{pc} += 2;
+ } elsif ($op == 99) {
+ return undef;
+ }
+ }
+}
+
+package main;
+
+chomp (my @mem = split /,/, <>);
+
+$; = ',';
+
+my $input = <<EOF;
+OR I J
+OR F J
+AND E J
+OR H J
+AND D J
+OR A T
+AND B T
+AND C T
+NOT T T
+AND T J
+RUN
+EOF
+
+my $comp = IntComp->new(\@mem, [ map { ord } split //, $input ]);
+
+while (my $rv = $comp->run()) {
+ if ($rv > 126) {
+ say $rv;
+ } else {
+ print chr($rv);
+ }
+}
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.16;
+
+my @deck = (0 .. 10006);
+# my @deck = (0 .. 9);
+
+while (<>) {
+ if (/new stack/) {
+ @deck = reverse @deck;
+ } elsif (/increment (\d+)/) {
+ my @nd;
+ my $p = 0;
+ for (@deck) {
+ $nd[$p] = $_;
+ $p += $1;
+ $p %= @deck;
+ }
+ @deck = @nd;
+ } elsif (/cut (\d+)/) {
+ push @deck, splice @deck, 0, $1;
+ } elsif (/cut (-\d+)/) {
+ unshift @deck, splice @deck, $1;
+ }
+ say join(' ', @deck);
+}
+
+my $p = 0;
+for (@deck) {
+ say "pos $p" if $_ == 2019;
+ $p++;
+}
--- /dev/null
+#!/usr/bin/perl -w
+use v5.38;
+use bigint;
+# use Math::BigInt lib => 'GMP';
+# Math::BigInt->precision(64);
+# say Math::BigInt->precision();
+
+my ($pos, $ncards, $iters) = # @ARGV;
+ # (2020, 119315717514047, 0);
+ (2020, 119315717514047, 101741582076661);
+ # (10, 11, 4);
+ # (28, 29, 1);
+ # (7975, 10007, 0);
+ # (42, 2147483647, 0);
+ # (42, 8589935681, 0);
+
+$ncards = Math::BigInt->new($ncards);
+
+chomp(my @cmds = <STDIN>);
+
+sub simplify {
+ my @cmds = @_;
+
+ RETRY:
+ for my $i (0 .. $#cmds-1) {
+ my $r = $cmds[$i].";".$cmds[$i+1];
+ if ($r =~ /new stack.*new stack/) {
+ splice @cmds, $i, 2;
+ goto RETRY;
+ }
+ if ($r =~ /cut (-?\d+);cut (-?\d+)/) {
+ my $n1 = +(0+$1);
+ my $n2 = +(0+$2);
+ splice @cmds, $i, 2,
+ 'cut ' . (($ncards+$n1+$n2) % $ncards);
+ goto RETRY;
+ }
+ if ($r =~ /increment (\d+);.*increment (\d+)\z/) {
+ my $n1 = +(0+$1);
+ my $n2 = +(0+$2);
+ splice @cmds, $i, 2,
+ 'deal with increment ' . (($n1*$n2) % $ncards);
+ goto RETRY;
+ }
+ # Try to move "cut" down and possibly merge it
+ if ($r =~ /cut (-?\d+);.*new stack/) {
+ my $n1 = +(0+$1);
+ splice @cmds, $i, 2,
+ 'deal into new stack',
+ 'cut ' . (-$n1);
+ goto RETRY;
+ }
+ if ($r =~ /cut (-?\d+);.*increment (\d+)\z/) {
+ my $n1 = +(0+$1);
+ my $n2 = +(0+$2);
+ splice @cmds, $i, 2,
+ 'deal with increment ' . ($n2),
+ 'cut ' . (($n1*$n2) % $ncards);
+ goto RETRY;
+ }
+ if ($r =~ /new stack;.*increment (\d+)\z/) {
+ my $n1 = +(0+$1);
+ splice @cmds, $i, 2,
+ 'deal with increment ' . ($ncards-$n1),
+ 'cut ' . $n1;
+ goto RETRY;
+ }
+ }
+ # say "=====\nReturning:\n", join("\n", @cmds);
+ return @cmds;
+}
+
+my $loops = $ncards-1;
+my $n = 0;
+my @repeated_cmds;
+while ((1 << $n) <= $loops) {
+ @cmds = simplify(@cmds);
+ $repeated_cmds[$n] = [ @cmds ];
+ $n++;
+ @cmds = (@cmds, @cmds);
+}
+
+sub apply {
+ my ($cmds, $pos) = @_;
+
+ # say "apply $pos:";
+ for (@$cmds) {
+ if (/new stack/) {
+ $pos = $ncards-1-$pos;
+ } elsif (/increment (\d+)/) {
+ $pos *= $1;
+ $pos %= $ncards;
+ } elsif (/cut (\d+)/) {
+ $pos -= $1;
+ $pos %= $ncards;
+ } elsif (/cut (-\d+)/) {
+ $pos -= $1;
+ $pos %= $ncards;
+ }
+ }
+ return $pos;
+}
+
+my $l = $ncards - 1 - $iters;
+my @r;
+while ($n--) {
+ my $two = 1 << $n;
+ # say "1 << $n == $two, rem $l";
+ if ($l >= $two) {
+ push @r, @{ $repeated_cmds[$n] };
+ $l -= $two;
+ }
+}
+@r = simplify(@r);
+say "Rules\n", join("\n", @r);
+say apply(\@r, $pos);
+
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.16;
+
+package IntComp;
+use bigint;
+
+sub new {
+ my ($class, $mem, $inputs) = @_;
+ my $self = {
+ mem => [ @$mem ],
+ pc => 0,
+ want_input => undef,
+ base => 0,
+ };
+ $self->{inputs} = [ @$inputs ]
+ if defined $inputs;
+ bless $self, $class;
+}
+
+sub clone {
+ my ($class, $other) = @_;
+ my $self = {
+ mem => [ @{ $other->{mem} } ],
+ pc => $other->{pc},
+ want_input => undef,
+ base => $other->{base},
+ inputs => [ ],
+ };
+ bless $self, $class;
+}
+
+sub m2val {
+ my ($self, $off, $mode) = @_;
+ if ($mode == 0) {
+ return $self->{mem}->[ $self->{mem}->[$self->{pc} + $off] ] // 0;
+ } elsif ($mode == 1) {
+ return $self->{mem}->[$self->{pc} + $off] // 0;
+ } elsif ($mode == 2) {
+ return $self->{mem}->[ $self->{mem}->[$self->{pc} + $off ] + $self->{base} ] // 0;
+ }
+}
+
+sub m2pos {
+ my ($self, $off, $mode) = @_;
+ if ($mode == 0) {
+ return $self->{mem}->[$self->{pc} + $off];
+ } elsif ($mode == 2) {
+ return $self->{mem}->[$self->{pc} + $off] + $self->{base};
+ }
+}
+
+sub input {
+ my ($self, @input) = @_;
+ push @{ $self->{inputs} }, @input;
+}
+
+sub run {
+ my ($self, @input) = @_;
+ my $mem = $self->{mem};
+
+ push @{ $self->{inputs} }, @input;
+ if (defined $self->{want_input}) {
+ $mem->[$self->{want_input}] = shift @{ $self->{inputs} };
+ $self->{want_input} = undef;
+ }
+
+ while (1) {
+ my $opcode = $mem->[$self->{pc}];
+ # say "pc=", $self->{pc}, " opcode=$opcode";
+ # say "mem=", join(',', map { $_ // '_' } @{ $self->{mem} });
+ my $op = int($opcode % 100);
+ my $m1 = int($opcode / 100) % 10;
+ my $m2 = int($opcode / 1000) % 10;
+ my $m3 = int($opcode / 10000) % 10;
+ if ($op == 1) {
+ $mem->[ m2pos($self, 3, $m3) ]
+ = m2val($self, 1, $m1)
+ + m2val($self, 2, $m2);
+ $self->{pc} += 4;
+ } elsif ($op == 2) {
+ $mem->[ m2pos($self, 3, $m3) ]
+ = m2val($self, 1, $m1)
+ * m2val($self, 2, $m2);
+ $self->{pc} += 4;
+ } elsif ($op == 3) {
+ if (@{ $self->{inputs} }) {
+ $mem->[ m2pos($self, 1, $m1) ]
+ = shift @{ $self->{inputs} };
+ $self->{pc} += 2;
+ } else {
+ $self->{want_input} = m2pos($self, 1, $m1);
+ $self->{pc} += 2;
+ return undef;
+ }
+ } elsif ($op == 4) {
+ my $val = m2val($self, 1, $m1);
+ $self->{pc} += 2;
+ return $val;
+ } elsif ($op == 5) {
+ if (m2val($self, 1, $m1)) {
+ $self->{pc} = m2val($self, 2, $m2);
+ } else {
+ $self->{pc} += 3;
+ }
+ } elsif ($op == 6) {
+ if (!m2val($self, 1, $m1)) {
+ $self->{pc} = m2val($self, 2, $m2);
+ } else {
+ $self->{pc} += 3;
+ }
+ } elsif ($op == 7) {
+ $mem->[ m2pos($self, 3, $m3) ] =
+ m2val($self, 1, $m1)
+ < m2val($self, 2, $m2) ? 1 : 0;
+ $self->{pc} += 4;
+ } elsif ($op == 8) {
+ $mem->[ m2pos($self, 3, $m3) ] =
+ m2val($self, 1, $m1)
+ == m2val($self, 2, $m2) ? 1 : 0;
+ $self->{pc} += 4;
+ } elsif ($op == 9) {
+ $self->{base} += m2val($self, 1, $m1);
+ $self->{pc} += 2;
+ } elsif ($op == 99) {
+ return undef;
+ }
+ }
+}
+
+package main;
+
+chomp (my @mem = split /,/, <>);
+
+$; = ',';
+
+my @comps;
+for (0 .. 49) {
+ push @comps, IntComp->new(\@mem, [ $_ ]);
+}
+
+my @queues;
+push @queues, [] for 1 .. 50;
+
+while(1) {
+for my $i (0 .. 49) {
+ my $c = $comps[$i];
+ my $dst = $c->run;
+ if (!defined $dst) {
+ say "$i wants input";
+ my $q = $queues[$i];
+ if (defined $q->[0]) {
+ my $in = shift @$q;
+ $c->input(@$in);
+ say "$i received @$in";
+ } else {
+ say "$i queue empty";
+ $c->input(-1);
+ }
+ } else {
+ my ($x, $y) = ($c->run, $c->run);
+
+ say "$i sends $dst $x $y";
+ my $q = $queues[$dst];
+ push @$q, [$x, $y];
+ if ($dst == 255) {
+ exit 0;
+ }
+ }
+}
+}
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.16;
+
+package IntComp;
+use bigint;
+
+sub new {
+ my ($class, $mem, $inputs) = @_;
+ my $self = {
+ mem => [ @$mem ],
+ pc => 0,
+ want_input => undef,
+ base => 0,
+ };
+ $self->{inputs} = [ @$inputs ]
+ if defined $inputs;
+ bless $self, $class;
+}
+
+sub clone {
+ my ($class, $other) = @_;
+ my $self = {
+ mem => [ @{ $other->{mem} } ],
+ pc => $other->{pc},
+ want_input => undef,
+ base => $other->{base},
+ inputs => [ ],
+ };
+ bless $self, $class;
+}
+
+sub m2val {
+ my ($self, $off, $mode) = @_;
+ if ($mode == 0) {
+ return $self->{mem}->[ $self->{mem}->[$self->{pc} + $off] ] // 0;
+ } elsif ($mode == 1) {
+ return $self->{mem}->[$self->{pc} + $off] // 0;
+ } elsif ($mode == 2) {
+ return $self->{mem}->[ $self->{mem}->[$self->{pc} + $off ] + $self->{base} ] // 0;
+ }
+}
+
+sub m2pos {
+ my ($self, $off, $mode) = @_;
+ if ($mode == 0) {
+ return $self->{mem}->[$self->{pc} + $off];
+ } elsif ($mode == 2) {
+ return $self->{mem}->[$self->{pc} + $off] + $self->{base};
+ }
+}
+
+sub input {
+ my ($self, @input) = @_;
+ push @{ $self->{inputs} }, @input;
+}
+
+sub run {
+ my ($self, @input) = @_;
+ my $mem = $self->{mem};
+
+ push @{ $self->{inputs} }, @input;
+ if (defined $self->{want_input}) {
+ $mem->[$self->{want_input}] = shift @{ $self->{inputs} };
+ $self->{want_input} = undef;
+ }
+
+ while (1) {
+ my $opcode = $mem->[$self->{pc}];
+ # say "pc=", $self->{pc}, " opcode=$opcode";
+ # say "mem=", join(',', map { $_ // '_' } @{ $self->{mem} });
+ my $op = int($opcode % 100);
+ my $m1 = int($opcode / 100) % 10;
+ my $m2 = int($opcode / 1000) % 10;
+ my $m3 = int($opcode / 10000) % 10;
+ if ($op == 1) {
+ $mem->[ m2pos($self, 3, $m3) ]
+ = m2val($self, 1, $m1)
+ + m2val($self, 2, $m2);
+ $self->{pc} += 4;
+ } elsif ($op == 2) {
+ $mem->[ m2pos($self, 3, $m3) ]
+ = m2val($self, 1, $m1)
+ * m2val($self, 2, $m2);
+ $self->{pc} += 4;
+ } elsif ($op == 3) {
+ if (@{ $self->{inputs} }) {
+ $mem->[ m2pos($self, 1, $m1) ]
+ = shift @{ $self->{inputs} };
+ $self->{pc} += 2;
+ } else {
+ $self->{want_input} = m2pos($self, 1, $m1);
+ $self->{pc} += 2;
+ return undef;
+ }
+ } elsif ($op == 4) {
+ my $val = m2val($self, 1, $m1);
+ $self->{pc} += 2;
+ return $val;
+ } elsif ($op == 5) {
+ if (m2val($self, 1, $m1)) {
+ $self->{pc} = m2val($self, 2, $m2);
+ } else {
+ $self->{pc} += 3;
+ }
+ } elsif ($op == 6) {
+ if (!m2val($self, 1, $m1)) {
+ $self->{pc} = m2val($self, 2, $m2);
+ } else {
+ $self->{pc} += 3;
+ }
+ } elsif ($op == 7) {
+ $mem->[ m2pos($self, 3, $m3) ] =
+ m2val($self, 1, $m1)
+ < m2val($self, 2, $m2) ? 1 : 0;
+ $self->{pc} += 4;
+ } elsif ($op == 8) {
+ $mem->[ m2pos($self, 3, $m3) ] =
+ m2val($self, 1, $m1)
+ == m2val($self, 2, $m2) ? 1 : 0;
+ $self->{pc} += 4;
+ } elsif ($op == 9) {
+ $self->{base} += m2val($self, 1, $m1);
+ $self->{pc} += 2;
+ } elsif ($op == 99) {
+ return undef;
+ }
+ }
+}
+
+package main;
+
+chomp (my @mem = split /,/, <>);
+
+$; = ',';
+
+my @comps;
+for (0 .. 49) {
+ push @comps, IntComp->new(\@mem, [ $_ ]);
+}
+
+my @queues;
+push @queues, [] for 1 .. 50;
+
+my @nat;
+my $prev_y;
+
+while(1) {
+ my $idle = 1;
+ for my $i (0 .. 49) {
+ my $c = $comps[$i];
+ my $dst = $c->run;
+ if (!defined $dst) {
+ say "$i wants input";
+ my $q = $queues[$i];
+ if (defined $q->[0]) {
+ my $in = shift @$q;
+ $c->input(@$in);
+ say "$i received @$in";
+ $idle = 0;
+ } else {
+ say "$i queue empty";
+ $c->input(-1);
+ }
+ } else {
+ my ($x, $y) = ($c->run, $c->run);
+
+ say "$i sends $dst $x $y";
+ $idle = 0;
+ if ($dst == 255) {
+ @nat = ($x, $y);
+ } else {
+ my $q = $queues[$dst];
+ push @$q, [$x, $y];
+ }
+ }
+ }
+ if ($idle) {
+ say "all idle, sending ", join(' ', @nat), " to 0";
+ $comps[0]->input(@nat);
+ if (defined $prev_y && $nat[1] == $prev_y) {
+ say "$prev_y sent second time";
+ exit 0;
+ }
+ $prev_y = $nat[1];
+ }
+}
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.16;
+
+my @map = map { chomp; [ split // ] } <>;
+
+my $min;
+my %seen;
+while (1) {
+ say "After ", ++$min, ":";
+ my @nm;
+ for my $y (0 .. 4) {
+ for my $x (0 .. 4) {
+ my $sum = 0;
+ for my ($dx, $dy) (1, 0, 0, 1, -1, 0, 0, -1) {
+ my $nx = $x + $dx;
+ my $ny = $y + $dy;
+ next if $nx < 0 || $nx > 4 || $ny < 0 || $ny > 4;
+ $sum++ if $map[$ny][$nx] eq '#';
+ }
+ # say "at $x $y neigbors $sum";
+ if ($map[$y][$x] eq '#') {
+ $nm[$y][$x] = $sum == 1 ? '#' : '.';
+ } else {
+ $nm[$y][$x] = $sum == 1 || $sum == 2 ? '#' : '.';
+ }
+ print $nm[$y][$x];
+ }
+ print "\n";
+ }
+ @map = @nm;
+ my $key = join('', map { join('', @$_) } @map);
+ if ($seen{$key}) {
+ say "this one was last seen at min $seen{$key}";
+ my $b = reverse $key;
+ $b =~ tr/.#/01/;
+ say oct "0b$b";
+ last;
+ }
+ $seen{$key} = $min;
+ print "\n";
+}
+
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.16;
+
+my @map = [ map { chomp; [ split // ] } <> ];
+
+my $min;
+my $levels = 0;
+while (1) {
+ say "After ", ++$min, ":";
+ my $count = 0;
+ my @nm;
+ $levels += 2;
+ for my $l (0 .. $levels) {
+ say "Depth ", $l - $min;
+ for my $y (0 .. 4) {
+ for my $x (0 .. 4) {
+ my $sum = 0;
+ my @pts;
+ for my ($dx, $dy) (1, 0, 0, 1, -1, 0, 0, -1) {
+ my $nx = $x + $dx;
+ my $ny = $y + $dy;
+ if ($nx < 0) {
+ push @pts, [$l-2, 2, 1]; # L Y X
+ } elsif ($ny < 0) {
+ push @pts, [$l-2, 1, 2];
+ } elsif ($nx > 4) {
+ push @pts, [$l-2, 2, 3];
+ } elsif ($ny > 4) {
+ push @pts, [$l-2, 3, 2];
+ } elsif ($nx == 2 && $ny == 2) {
+ if ($x == 1) {
+ push @pts, [$l, $_, 0] for 0 .. 4;
+ } elsif ($y == 1) {
+ push @pts, [$l, 0, $_] for 0 .. 4;
+ } elsif ($x == 3) {
+ push @pts, [$l, $_, 4] for 0 .. 4;
+ } elsif ($y == 3) {
+ push @pts, [$l, 4, $_] for 0 .. 4;
+ }
+ } else {
+ push @pts, [$l-1, $ny, $nx];
+ }
+ }
+ for my $pt (@pts) {
+ my ($nl, $ny, $nx) = @$pt;
+ next if $nl < 0 || $nl > $levels-2;
+ $sum++ if $map[$nl][$ny][$nx] eq '#';
+ }
+ if ($x == 2 & $y == 2) {
+ $nm[$l][$y][$x] = '?';
+ } elsif ($l > 0 && $l <= $levels-1 && $map[$l-1][$y][$x] eq '#') {
+ $nm[$l][$y][$x] = $sum == 1 ? '#' : '.';
+ } else {
+ $nm[$l][$y][$x] = $sum == 1 || $sum == 2 ? '#' : '.';
+ }
+ print $nm[$l][$y][$x];
+ $count++ if $nm[$l][$y][$x] eq '#';
+ }
+ print "\n";
+ }
+ print "\n";
+ }
+ @map = @nm;
+ print "==== $count ====\n";
+ last if $min >= 200;
+}
+
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.16;
+
+package IntComp;
+use bigint;
+
+sub new {
+ my ($class, $mem, $inputs) = @_;
+ my $self = {
+ mem => [ @$mem ],
+ pc => 0,
+ want_input => undef,
+ base => 0,
+ };
+ $self->{inputs} = [ @$inputs ]
+ if defined $inputs;
+ bless $self, $class;
+}
+
+sub clone {
+ my ($class, $other) = @_;
+ my $self = {
+ mem => [ @{ $other->{mem} } ],
+ pc => $other->{pc},
+ want_input => undef,
+ base => $other->{base},
+ inputs => [ ],
+ };
+ bless $self, $class;
+}
+
+sub m2val {
+ my ($self, $off, $mode) = @_;
+ if ($mode == 0) {
+ return $self->{mem}->[ $self->{mem}->[$self->{pc} + $off] ] // 0;
+ } elsif ($mode == 1) {
+ return $self->{mem}->[$self->{pc} + $off] // 0;
+ } elsif ($mode == 2) {
+ return $self->{mem}->[ $self->{mem}->[$self->{pc} + $off ] + $self->{base} ] // 0;
+ }
+}
+
+sub m2pos {
+ my ($self, $off, $mode) = @_;
+ if ($mode == 0) {
+ return $self->{mem}->[$self->{pc} + $off];
+ } elsif ($mode == 2) {
+ return $self->{mem}->[$self->{pc} + $off] + $self->{base};
+ }
+}
+
+sub input {
+ my ($self, @input) = @_;
+ push @{ $self->{inputs} }, @input;
+}
+
+sub run {
+ my ($self, @input) = @_;
+ my $mem = $self->{mem};
+
+ push @{ $self->{inputs} }, @input;
+ if (defined $self->{want_input}) {
+ $mem->[$self->{want_input}] = shift @{ $self->{inputs} };
+ $self->{want_input} = undef;
+ }
+
+ while (1) {
+ my $opcode = $mem->[$self->{pc}];
+ # say "pc=", $self->{pc}, " opcode=$opcode";
+ # say "mem=", join(',', map { $_ // '_' } @{ $self->{mem} });
+ my $op = int($opcode % 100);
+ my $m1 = int($opcode / 100) % 10;
+ my $m2 = int($opcode / 1000) % 10;
+ my $m3 = int($opcode / 10000) % 10;
+ if ($op == 1) {
+ $mem->[ m2pos($self, 3, $m3) ]
+ = m2val($self, 1, $m1)
+ + m2val($self, 2, $m2);
+ $self->{pc} += 4;
+ } elsif ($op == 2) {
+ $mem->[ m2pos($self, 3, $m3) ]
+ = m2val($self, 1, $m1)
+ * m2val($self, 2, $m2);
+ $self->{pc} += 4;
+ } elsif ($op == 3) {
+ if (@{ $self->{inputs} }) {
+ $mem->[ m2pos($self, 1, $m1) ]
+ = shift @{ $self->{inputs} };
+ $self->{pc} += 2;
+ } else {
+ $self->{want_input} = m2pos($self, 1, $m1);
+ $self->{pc} += 2;
+ return undef;
+ }
+ } elsif ($op == 4) {
+ my $val = m2val($self, 1, $m1);
+ $self->{pc} += 2;
+ return $val;
+ } elsif ($op == 5) {
+ if (m2val($self, 1, $m1)) {
+ $self->{pc} = m2val($self, 2, $m2);
+ } else {
+ $self->{pc} += 3;
+ }
+ } elsif ($op == 6) {
+ if (!m2val($self, 1, $m1)) {
+ $self->{pc} = m2val($self, 2, $m2);
+ } else {
+ $self->{pc} += 3;
+ }
+ } elsif ($op == 7) {
+ $mem->[ m2pos($self, 3, $m3) ] =
+ m2val($self, 1, $m1)
+ < m2val($self, 2, $m2) ? 1 : 0;
+ $self->{pc} += 4;
+ } elsif ($op == 8) {
+ $mem->[ m2pos($self, 3, $m3) ] =
+ m2val($self, 1, $m1)
+ == m2val($self, 2, $m2) ? 1 : 0;
+ $self->{pc} += 4;
+ } elsif ($op == 9) {
+ $self->{base} += m2val($self, 1, $m1);
+ $self->{pc} += 2;
+ } elsif ($op == 99) {
+ return undef;
+ }
+ }
+}
+
+sub ascii {
+ my ($self, $text) = @_;
+ $text .= "\n" if $text !~ /\n\z/;
+ my @bytes = map { ord } split //, $text;
+ $self->input(@bytes);
+ return $self;
+}
+
+package main;
+
+open my $fh, '<', $ARGV[0] or die;
+my @mem;
+{ local $/; chomp(@mem = split /,/, <>) };
+
+$; = ',';
+
+my $comp = IntComp->new(\@mem);
+
+while (my $rv = $comp->run) {
+ print chr($rv);
+}
+
+while (<STDIN>) {
+ $comp->ascii($_);
+
+ while (my $rv = $comp->run) {
+ print chr($rv);
+ }
+}
--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.16;
+
+