]> www.fi.muni.cz Git - aoc.git/commitdiff
Merge branch 'aoc2021'
authorJan "Yenya" Kasprzak <kas@fi.muni.cz>
Mon, 3 Jan 2022 18:03:20 +0000 (19:03 +0100)
committerJan "Yenya" Kasprzak <kas@fi.muni.cz>
Mon, 3 Jan 2022 18:03:20 +0000 (19:03 +0100)
50 files changed:
2020/1.pl [new file with mode: 0755]
2020/10.pl [new file with mode: 0755]
2020/11.pl [new file with mode: 0755]
2020/12.pl [new file with mode: 0755]
2020/13.pl [new file with mode: 0755]
2020/14.pl [new file with mode: 0755]
2020/15.pl [new file with mode: 0755]
2020/16.pl [new file with mode: 0755]
2020/17.pl [new file with mode: 0755]
2020/18.pl [new file with mode: 0755]
2020/19.pl [new file with mode: 0755]
2020/2.pl [new file with mode: 0755]
2020/20.pl [new file with mode: 0755]
2020/21.pl [new file with mode: 0755]
2020/22.pl [new file with mode: 0755]
2020/23.pl [new file with mode: 0755]
2020/24.pl [new file with mode: 0755]
2020/25.pl [new file with mode: 0755]
2020/26.pl [new file with mode: 0755]
2020/27.pl [new file with mode: 0755]
2020/28.pl [new file with mode: 0755]
2020/29.pl [new file with mode: 0755]
2020/3.pl [new file with mode: 0755]
2020/30.pl [new file with mode: 0755]
2020/31.pl [new file with mode: 0755]
2020/32.pl [new file with mode: 0755]
2020/33.pl [new file with mode: 0755]
2020/34.pl [new file with mode: 0755]
2020/35.pl [new file with mode: 0755]
2020/36.pl [new file with mode: 0755]
2020/37.pl [new file with mode: 0755]
2020/38.pl [new file with mode: 0755]
2020/39.pl [new file with mode: 0755]
2020/4.pl [new file with mode: 0755]
2020/40.pl [new file with mode: 0755]
2020/41.pl [new file with mode: 0755]
2020/42.pl [new file with mode: 0755]
2020/43.pl [new file with mode: 0755]
2020/44.pl [new file with mode: 0755]
2020/45.pl [new file with mode: 0755]
2020/46.pl [new file with mode: 0755]
2020/47.pl [new file with mode: 0755]
2020/48.pl [new file with mode: 0755]
2020/49.pl [new file with mode: 0755]
2020/5.pl [new file with mode: 0755]
2020/6.pl [new file with mode: 0755]
2020/7.pl [new file with mode: 0755]
2020/8.pl [new file with mode: 0755]
2020/9-golf.pl [new file with mode: 0644]
2020/9.pl [new file with mode: 0755]

diff --git a/2020/1.pl b/2020/1.pl
new file mode 100755 (executable)
index 0000000..2052140
--- /dev/null
+++ b/2020/1.pl
@@ -0,0 +1,20 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+my @nums = sort { $a <=> $b } <>;
+chomp @nums;
+
+while (@nums > 1) {
+       print "Nums: ", join(' ', @nums), "\n";
+       my $cmp = $nums[0] + $nums[-1] <=> 2020;
+       if ($cmp < 0) {
+               shift @nums;
+       } elsif ($cmp == 0) {
+               print "$nums[0]*$nums[-1]=", $nums[0]*$nums[-1], "\n";
+               last;
+       } else {
+               pop @nums;
+       }
+}
+
diff --git a/2020/10.pl b/2020/10.pl
new file mode 100755 (executable)
index 0000000..2f7ade1
--- /dev/null
@@ -0,0 +1,16 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+my @seats;
+while (<>) {
+       chomp;
+       y/FBRL/0110/;
+       my $seat =  eval "0b$_";
+       $seats[$seat] = 1;
+}
+
+for (1 .. $#seats-1) {
+       print $_, "\n" if $seats[$_-1] && $seats[$_+1]
+               && !$seats[$_];
+}
diff --git a/2020/11.pl b/2020/11.pl
new file mode 100755 (executable)
index 0000000..256c0ec
--- /dev/null
@@ -0,0 +1,16 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+local $/ = "\n\n";
+
+my $sum = 0;
+
+while (<>) {
+       s/\n//g;
+       my %q = map { $_ => 1 } split //;
+       $sum += keys %q;
+}
+
+print "$sum\n";
+
diff --git a/2020/12.pl b/2020/12.pl
new file mode 100755 (executable)
index 0000000..c8b97b9
--- /dev/null
@@ -0,0 +1,20 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+local $/ = "\n\n";
+
+my $sum = 0;
+
+while (<>) {
+       my $nper = 0;
+       my %q;
+       for (split /\n/) {
+               $nper++;
+               $q{$_}++ for split //;
+       }
+       $sum += grep { $q{$_} == $nper } keys %q;
+}
+
+print "$sum\n";
+
diff --git a/2020/13.pl b/2020/13.pl
new file mode 100755 (executable)
index 0000000..93739e0
--- /dev/null
@@ -0,0 +1,32 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+my %graph;
+my $count = 0;
+
+while (<>) {
+       my ($bag, $rest) = /\A(.*) bags? contain (.*)./;
+       if ($rest eq 'no other bags') {
+               # $graph{$bag} = []; # ale neprojevi se
+       } else {
+               for my $contain (split /, /, $rest) {
+                       my ($count, $color) = ($contain =~ /\A(\d+) (.*) bag/);
+                       print "\t$count\t$color.\n";
+                       push @{ $graph{$color} }, $bag;
+               }
+       }
+}
+
+my %seen = ('shiny gold' => 1);
+my @todo = 'shiny gold';
+
+while (my $color = shift @todo) {
+       for my $next (@{ $graph{$color} }) {
+               next if $seen{ $next };
+               unshift @todo, $next;
+               $seen{ $next } = 1;
+       }
+}
+
+print "Seen ", keys(%seen)-1, " nodes\n";
diff --git a/2020/14.pl b/2020/14.pl
new file mode 100755 (executable)
index 0000000..ab8f8b4
--- /dev/null
@@ -0,0 +1,38 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+my %graph;
+my $count = 0;
+
+while (<>) {
+       my ($bag, $rest) = /\A(.*) bags? contain (.*)./;
+       if ($rest eq 'no other bags') {
+               $graph{$bag} = []; # ale neprojevi se
+       } else {
+               for my $contain (split /, /, $rest) {
+                       my ($count, $color) = ($contain =~ /\A(\d+) (.*) bag/);
+                       push @{ $graph{$bag} }, [ $color => $count ];
+               }
+       }
+}
+
+my %seen = ('shiny gold' => 1);
+my %total;
+
+sub walk {
+       my ($color) = @_;
+       
+       return $total{$color}
+               if defined $total{$color};
+
+       $total{$color} = 1;
+       for my $next (@{ $graph{$color} }) {
+               my ($ncol, $ncount) = @$next;
+               $total{$color} += $ncount*walk($ncol);
+       }
+
+       return $total{$color};
+}
+
+print "Result is ", walk('shiny gold') - 1, "\n";
diff --git a/2020/15.pl b/2020/15.pl
new file mode 100755 (executable)
index 0000000..f82c817
--- /dev/null
@@ -0,0 +1,23 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+my @code = map { [ split /\s+/ ] } <>;
+
+my $acc = 0;
+my $pc = 0;
+my %seen;
+
+while (!$seen{$pc}) {
+       $seen{$pc} = 1;
+       my ($ins, $arg) = @{ $code[$pc] };
+       print "pc=$pc, acc=$acc, $ins $arg\n";
+       if ($ins eq 'nop') {
+               $pc++;
+       } elsif ($ins eq 'acc') {
+               $acc += $arg; $pc++;
+       } elsif ($ins eq 'jmp') {
+               $pc += $arg;
+       }
+}
+print "loop at $pc, acc=$acc\n";
diff --git a/2020/16.pl b/2020/16.pl
new file mode 100755 (executable)
index 0000000..899e942
--- /dev/null
@@ -0,0 +1,40 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+my @code = map { [ split /\s+/ ] } <>;
+
+for my $i (0 .. $#code) {
+       if ($code[$i]->[0] eq 'nop') {
+               local $code[$i]->[0] = 'jmp';
+               interp(\@code);
+       } elsif ($code[$i]->[0] eq 'jmp') {
+               local $code[$i]->[0] = 'nop';
+               interp(\@code);
+       }
+}
+               
+sub interp {
+       my ($code) = @_;
+       my $acc = 0;
+       my $pc = 0;
+       my %seen;
+
+       while (!$seen{$pc} && $pc != @$code) {
+               $seen{$pc} = 1;
+               my ($ins, $arg) = @{ $code->[$pc] };
+               # print "pc=$pc, acc=$acc, $ins $arg\n";
+               if ($ins eq 'nop') {
+                       $pc++;
+               } elsif ($ins eq 'acc') {
+                       $acc += $arg; $pc++;
+               } elsif ($ins eq 'jmp') {
+                       $pc += $arg;
+               }
+       }
+       if ($pc == @$code) {
+               print "terminating with acc=$acc.\n";
+               exit 0;
+       }
+       # print "\n";
+}
diff --git a/2020/17.pl b/2020/17.pl
new file mode 100755 (executable)
index 0000000..c972894
--- /dev/null
@@ -0,0 +1,25 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+my @prev;
+my $len = 25;
+
+while (my $num = <>) {
+       chomp $num;
+       if (@prev >= $len) {
+               shift @prev if @prev > $len;
+               for my $i (0 .. $len-2) {
+                       for my $j ($i+1 .. $len-1) {
+                               if ($prev[$i]+$prev[$j] == $num) {
+                                       goto FOUND;
+                               }
+                       }
+               }
+               print "$num is not a sum of ", join(', ', @prev), "\n";
+               exit 0;
+       FOUND:
+       }
+       push @prev, $num;
+}
+
diff --git a/2020/18.pl b/2020/18.pl
new file mode 100755 (executable)
index 0000000..3801b4e
--- /dev/null
@@ -0,0 +1,35 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+my @prev;
+
+my $target = 375054920;
+
+my $sum = 0;
+while (my $num = <>) {
+       chomp $num;
+
+       $sum += $num;
+       push @prev, $num;
+       
+       print "Adding $num, sum=$sum\n";
+
+       while ($sum > $target) {
+               my $n1 = shift @prev;
+               $sum -= $n1;
+               print "Removing $n1, sum=$sum\n";
+       }
+
+       if ($sum == $target) {
+               print "found $sum = ", join('+', @prev), "\n";
+               my ($min, $max);
+               for my $n1 (@prev) {
+                       $min = $n1 if !defined $min || $min > $n1;
+                       $max = $n1 if !defined $max || $max < $n1;
+               }
+               print "$min+$max=", $min+$max, "\n";
+               last;
+       }
+}
+
diff --git a/2020/19.pl b/2020/19.pl
new file mode 100755 (executable)
index 0000000..c9f7f39
--- /dev/null
@@ -0,0 +1,16 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+local $/;
+my @ads = sort { $a <=> $b } map { chomp; $_ } split /\n/, <>;
+
+my $prev = 0;
+my %diffs;
+for my $n (@ads) {
+       $diffs{ $n - $prev }++;
+       $prev = $n;
+}
+$diffs{3}++;
+
+print $diffs{1} * $diffs{3}, "\n";
diff --git a/2020/2.pl b/2020/2.pl
new file mode 100755 (executable)
index 0000000..d950fae
--- /dev/null
+++ b/2020/2.pl
@@ -0,0 +1,20 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+my @nums = sort { $a <=> $b } <>;
+chomp @nums;
+
+my %is_listed = map { $_ => 1 } @nums;
+
+for my $i (0 .. $#nums-1) {
+       for my $j ($i .. $#nums) {
+               my $rest = 2020-$nums[$i]-$nums[$j];
+               if ($rest > 0 && $is_listed{$rest} && $rest != $nums[$i]
+                       && $rest != $nums[$j]) {
+                       print "$nums[$i]*$nums[$j]*$rest=", $nums[$i]*$nums[$j]*$rest, "\n";
+                       last;
+               }
+       }
+}
+
diff --git a/2020/20.pl b/2020/20.pl
new file mode 100755 (executable)
index 0000000..09210e2
--- /dev/null
@@ -0,0 +1,26 @@
+#!/usr/bin/perl -w
+
+use strict;
+use Data::Dumper;
+
+local $/;
+my @ads = sort { $a <=> $b } map { chomp; $_ } split /\n/, <>;
+
+my $prev = 0;
+my $ones = 0;
+my $total = 1;
+my @ones2count = qw(1 1 2 4 7);
+push @ads, $ads[-1]+3;
+for my $n (@ads) {
+       if ($n - $prev == 1) {
+               $ones++;
+       } else {
+               print "$ones ones + 3, total=$total*$ones2count[$ones]\n";
+               $total *= $ones2count[$ones];
+               $ones = 0;
+       }
+       $prev = $n;
+}
+
+print "total=$total\n";
+
diff --git a/2020/21.pl b/2020/21.pl
new file mode 100755 (executable)
index 0000000..4a3d0ba
--- /dev/null
@@ -0,0 +1,55 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+my @seats = map { chomp; [ split // ] } (<>);
+
+my $cols = @{ $seats[0] };
+my $rows = @seats;
+
+print "$cols x $rows\n";
+
+while (1) {
+       my $was_change = 0;
+       my $occup = 0;
+       my @newseats;
+       for my $row (0 .. $rows-1) {
+               my @newrow;
+               for my $col (0 .. $cols-1) {
+                       my $neigh = '';
+                       for my $add ([-1, -1], [-1, 0], [-1, 1],
+                               [0, -1], [0, 1],
+                               [1, -1], [1, 0], [1, 1]) {
+                               my $row1 = $row + $add->[0];
+                               my $col1 = $col + $add->[1];
+                               next if $row1 >= $rows || $row1 < 0
+                                       || $col1 >= $cols || $col1 < 0;
+                               $neigh .= $seats[$row1]->[$col1];
+                       }
+                       my $neigh_empty =()= $neigh =~ /L/g;
+                       my $neigh_occup =()= $neigh =~ /#/g;
+
+                       if ($seats[$row]->[$col] eq 'L' && !$neigh_occup) {
+                               push @newrow, '#';
+                               $was_change = 1;
+                               $occup++;
+                       } elsif ($seats[$row]->[$col] eq '#' && $neigh_occup >= 4) {
+                               push @newrow, 'L';
+                               $was_change = 1;
+                       } else {
+                               push @newrow, $seats[$row]->[$col];
+                               $occup++ if $seats[$row]->[$col] eq '#';
+                       }
+               }
+               push @newseats, \@newrow;
+       }
+       @seats = @newseats;
+       #for my $row (@seats) {
+       #       print @$row, "\n";
+       #}
+       print "$occup occupied seats\n";
+       # print "\n";
+       last if !$was_change;
+}
+
+
diff --git a/2020/22.pl b/2020/22.pl
new file mode 100755 (executable)
index 0000000..eb4a0ff
--- /dev/null
@@ -0,0 +1,74 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+my @seats = map { chomp; [ split // ] } (<>);
+
+my $cols = @{ $seats[0] };
+my $rows = @seats;
+
+print "$cols x $rows\n";
+
+my @neighs;
+for my $row (0 .. $rows-1) {
+       my @neigh_row;
+       for my $col (0 .. $cols-1) {
+               my @neigh_seats;
+               for my $add ([-1, -1], [-1, 0], [-1, 1],
+                       [0, -1], [0, 1],
+                       [1, -1], [1, 0], [1, 1]) {
+                       my ($row1, $col1) = ($row, $col);
+                       while (1) {
+                               $row1 += $add->[0];
+                               $col1 += $add->[1];
+                               last if $row1 >= $rows || $row1 < 0
+                                       || $col1 >= $cols || $col1 < 0;
+                               if ($seats[$row1]->[$col1] ne '.') {
+                                       push @neigh_seats, [$row1, $col1];
+                                       last;
+                               }
+                       }
+               }
+               push @neigh_row, \@neigh_seats;
+       }
+       push @neighs, \@neigh_row;
+}
+
+while (1) {
+       my $was_change = 0;
+       my $occup = 0;
+       my @newseats;
+       for my $row (0 .. $rows-1) {
+               my @newrow;
+               for my $col (0 .. $cols-1) {
+                       my $neigh = '';
+                       for my $nl (@{ $neighs[$row]->[$col] }) {
+                               $neigh .= $seats[$nl->[0]]->[$nl->[1]];
+                       }
+                       my $neigh_empty =()= $neigh =~ /L/g;
+                       my $neigh_occup =()= $neigh =~ /#/g;
+
+                       if ($seats[$row]->[$col] eq 'L' && !$neigh_occup) {
+                               push @newrow, '#';
+                               $was_change = 1;
+                               $occup++;
+                       } elsif ($seats[$row]->[$col] eq '#' && $neigh_occup >= 5) {
+                               push @newrow, 'L';
+                               $was_change = 1;
+                       } else {
+                               push @newrow, $seats[$row]->[$col];
+                               $occup++ if $seats[$row]->[$col] eq '#';
+                       }
+               }
+               push @newseats, \@newrow;
+       }
+       @seats = @newseats;
+       # for my $row (@seats) {
+       #       print @$row, "\n";
+       # }
+       print "$occup occupied seats\n";
+       # print "\n";
+       last if !$was_change;
+}
+
+
diff --git a/2020/23.pl b/2020/23.pl
new file mode 100755 (executable)
index 0000000..0ffeb01
--- /dev/null
@@ -0,0 +1,32 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+my ($x, $y, $dir) = (0, 0, 'E');
+
+while (<>) {
+       my ($cmd, $num) = /\A(.)(\d+)/;
+       print "cmd=$cmd, num=$num: ";
+       if ($cmd eq 'F') {
+               $cmd = $dir;
+       }
+       if ($cmd eq 'R') {
+               $num += 90;
+               $dir =~ y/NESW/ESWN/ while $num -= 90;
+       } elsif ($cmd eq 'L') {
+               $num += 90;
+               $dir =~ y/NESW/WNES/ while $num -= 90;
+       } elsif ($cmd eq 'N') {
+               $y += $num;
+       } elsif ($cmd eq 'S') {
+               $y -= $num;
+       } elsif ($cmd eq 'E') {
+               $x += $num;
+       } elsif ($cmd eq 'W') {
+               $x -= $num;
+       }
+       print " at ($x, $y, $dir)\n";
+}
+
+print abs($x) + abs($y), "\n";
+               
diff --git a/2020/24.pl b/2020/24.pl
new file mode 100755 (executable)
index 0000000..29b1fdf
--- /dev/null
@@ -0,0 +1,38 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+my ($wx, $wy, $x, $y) = (10, 1);
+
+while (<>) {
+       my ($cmd, $num) = /\A(.)(\d+)/;
+       print "cmd=$cmd, num=$num: ";
+       if ($cmd eq 'F') {
+               $x += $num*$wx;
+               $y += $num*$wy;
+       } elsif ($cmd eq 'R') {
+               while (($num -= 90) >= 0) {
+                       my $tmp = $wx;
+                       $wx = $wy;
+                       $wy = -$tmp;
+               }
+       } elsif ($cmd eq 'L') {
+               while (($num -= 90) >= 0) {
+                       my $tmp = $wy;
+                       $wy = $wx;
+                       $wx = -$tmp;
+               }
+       } elsif ($cmd eq 'N') {
+               $wy += $num;
+       } elsif ($cmd eq 'S') {
+               $wy -= $num;
+       } elsif ($cmd eq 'E') {
+               $wx += $num;
+       } elsif ($cmd eq 'W') {
+               $wx -= $num;
+       }
+       print " at ($x, $y, $wx, $wy)\n";
+}
+
+print abs($x) + abs($y), "\n";
+               
diff --git a/2020/25.pl b/2020/25.pl
new file mode 100755 (executable)
index 0000000..60b72ca
--- /dev/null
@@ -0,0 +1,19 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+my $timestamp = <>;
+my @buses = grep { /\d+/ } split /,/, <>;
+
+my ($min_del, $min_bus);
+for my $bus (@buses) {
+       my $delay = $bus*(1+int(($timestamp-1)/$bus))-$timestamp;
+       print "Bus $bus delay $delay\n";
+       if (!defined $min_del || $min_del > $delay) {
+               $min_bus = $bus;
+               $min_del = $delay;
+       }
+}
+
+print "$min_bus * $min_del = ", $min_bus * $min_del, "\n";
+
diff --git a/2020/26.pl b/2020/26.pl
new file mode 100755 (executable)
index 0000000..90d07ae
--- /dev/null
@@ -0,0 +1,34 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+my $timestamp = <>;
+my @buses = split /,/, <>;
+
+my $mins = 1;
+my %bus_time;
+my $first = shift @buses;
+for my $bus (@buses) {
+       if ($bus =~ /\d/) {
+               $bus_time{$bus} = $mins % $bus;
+               print "Bus $bus at t+$mins ($bus_time{$bus})\n";
+       }
+       $mins++;
+}
+
+@buses = grep /\d/, @buses;
+
+my $t = $first;
+my $add = $first;
+
+for my $bus (keys %bus_time) {
+       print "bus $bus at $bus_time{$bus}\n";
+       while (1) {
+               print "t=$t, add=$add\n";
+               last if ($t + $bus_time{$bus}) % $bus == 0;
+               $t += $add;
+       }
+       $add *= $bus;
+}
+print "t=$t\n";
+
diff --git a/2020/27.pl b/2020/27.pl
new file mode 100755 (executable)
index 0000000..a17a382
--- /dev/null
@@ -0,0 +1,26 @@
+#!/usr/bin/perl -w
+
+use strict;
+no warnings 'portable';
+
+my @mem;
+
+my ($andm, $orm);
+while (<>) {
+       chomp;
+       if (/mask = (\S+)/) {
+               ($orm, $andm) = ($1, $1);
+               $andm =~ s/X/1/g; $andm = oct "0b$andm";
+               $orm  =~ s/X/0/g; $orm  = oct "0b$orm";
+               print "andm=$andm\n orm=$orm\n";
+               next;
+       }
+       if (/mem\[(\d+)\] = (\d+)/) {
+               $mem[$1] = ($2 | $orm) & $andm;
+               print "mem[$1] = $2 => $mem[$1]\n";
+       }
+}
+
+my $sum;
+for (@mem) { $sum += $_ if defined $_ };
+print "Sum=$sum\n";
diff --git a/2020/28.pl b/2020/28.pl
new file mode 100755 (executable)
index 0000000..edf339e
--- /dev/null
@@ -0,0 +1,39 @@
+#!/usr/bin/perl -w
+
+use strict;
+no warnings 'portable';
+
+my %mem;
+
+my ($floatm, $orm);
+while (<>) {
+       chomp;
+       if (/mask = (\S+)/) {
+               ($orm, $floatm) = ($1, $1);
+               $floatm =~ s/1/0/g;
+               $orm  =~ s/X/0/g; $orm  = oct "0b$orm";
+               next;
+       }
+       if (/mem\[(\d+)\] = (\d+)/) {
+               my $data = $2;
+               for my $addr (addr2list($1|$orm, $floatm)) {
+                       $mem{$addr} = $data;
+                       # print "mem[$addr=", pack("b*", $addr),"] = $2\n";
+               }
+       }
+}
+
+my $sum;
+for (keys %mem) { $sum += $mem{$_} };
+print "Sum=$sum\n";
+
+sub addr2list {
+       my ($addr, $mask) = @_;
+       if ($mask =~ s/X/0/) {
+               my $off = length $';
+               return (addr2list($addr | (1 << $off), $mask),
+                       addr2list($addr & ~(1 << $off), $mask));
+       } else {
+               return ($addr);
+       }
+}
diff --git a/2020/29.pl b/2020/29.pl
new file mode 100755 (executable)
index 0000000..e99bf39
--- /dev/null
@@ -0,0 +1,28 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+my @start = map { chomp; $_ } split /,/, <>;
+
+my $turn = 0;
+my %nums;
+my $num;
+
+while ($turn < 2020) {
+       $turn++;
+       if (@start) {
+               $num = shift @start;
+       }
+       my $next;
+       if (defined $nums{$num}) {
+               $next = $turn - $nums{$num};
+       } else {
+               $next = 0;
+       }
+       $nums{$num} = $turn;
+       print "turn $turn, num=$num\n";
+       $num = $next;
+}
+
+
+
diff --git a/2020/3.pl b/2020/3.pl
new file mode 100755 (executable)
index 0000000..2865016
--- /dev/null
+++ b/2020/3.pl
@@ -0,0 +1,22 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+my $count = 0;
+
+while (<>) {
+       my ($min, $max, $letter, $pass) = /\A(\d+)-(\d+)\s+(\S):\s+(\S+)\s*\z/;
+       if (!defined $pass) {
+               print "Divny radek $_\n";
+               next;
+       }
+
+       
+       my $chars =()= ($pass =~ /$letter/g);
+
+       # print "$min-$max $letter: [$pass] - $chars\n";
+       $count++ if $chars >= $min && $chars <= $max;
+}
+
+print $count, "\n";
+
diff --git a/2020/30.pl b/2020/30.pl
new file mode 100755 (executable)
index 0000000..838171d
--- /dev/null
@@ -0,0 +1,29 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+my @start = map { chomp; $_ } split /,/, <>;
+
+my $turn = 0;
+my %nums;
+my $num;
+
+while ($turn < 30000000) {
+       $turn++;
+       if (@start) {
+               $num = shift @start;
+       }
+       my $next;
+       if (defined $nums{$num}) {
+               $next = $turn - $nums{$num};
+       } else {
+               $next = 0;
+       }
+       $nums{$num} = $turn;
+       print "turn $turn, num=$num\n"
+               if $turn % 1_000_000 == 0;
+       $num = $next;
+}
+
+
+
diff --git a/2020/31.pl b/2020/31.pl
new file mode 100755 (executable)
index 0000000..ad2fc0e
--- /dev/null
@@ -0,0 +1,36 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+local $/ = "\n\n";
+my @ranges;
+for (split /\n/, <>) {
+       my (@cls) = /\A(.*): (\d+)-(\d+) or (\d+)-(\d+)/;
+       print "$2:$3,$4:$5.\n";
+       push @ranges, [$2, $3], [$4, $5];
+}
+
+$_ = <>;
+my @your = /(\d+)/g;
+print "Your:", join("|", @your), "\n";
+
+my $sum = 0;
+# my @nearby;
+for (split /\n/, <>) {
+       next if !/\d/;
+       my @n = /(\d+)/g;
+       # print "nearby:", join("|", @n), "\n";
+       # push @nearby, \@n;
+       NUM:
+       for my $num (@n) {
+               for my $r (@ranges) {
+                       if ($num >= $r->[0] && $num <= $r->[1]) {
+                               next NUM;
+                       }
+               }
+               print "$num is invalid\n";
+               $sum += $num;
+       }
+}
+
+print "sum=$sum\n";
diff --git a/2020/32.pl b/2020/32.pl
new file mode 100755 (executable)
index 0000000..f033a41
--- /dev/null
@@ -0,0 +1,71 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+local $/ = "\n\n";
+my @classes;
+for (split /\n/, <>) {
+       my (@cls) = /\A(.*): (\d+)-(\d+) or (\d+)-(\d+)/;
+       print "$2:$3,$4:$5.\n";
+       push @classes, \@cls;
+}
+
+$_ = <>;
+my @your = /(\d+)/g;
+print "Your:", join("|", @your), "\n";
+
+my @nearby;
+TICKET:
+for (split /\n/, <>) {
+       next if !/\d/;
+       my @n = /(\d+)/g;
+       NUM:
+       for my $num (@n) {
+               for my $r (@classes) {
+                       if (($num >= $r->[1] && $num <= $r->[2])
+                               || $num >= $r->[3] && $num <= $r->[4]) {
+                               next NUM;
+                       }
+               }
+               next TICKET;
+       }
+       # print "nearby:", join("|", @n), "\n";
+       push @nearby, \@n;
+}
+
+my %valid_cols;
+for my $cls (@classes) {
+       my ($name, $f1, $t1, $f2, $t2) = @$cls;
+       # print "Class $name:\n";
+       COL:
+       for my $col (0 .. $#your) {
+               # print "col $col\n";
+               for my $ticket (@nearby) {
+                       if (($ticket->[$col] < $f1 || $ticket->[$col] > $t1)
+                               && $ticket->[$col] < $f2 || $ticket->[$col] > $t2) {
+                               next COL;
+                       }
+               }
+               $valid_cols{$name}->{$col} = 1;
+               print "$name can be $col\n";
+       }
+}
+
+my $mul = 1;
+LOOP:
+while (keys %valid_cols) {
+       for my $class (keys %valid_cols) {
+               if (keys %{ $valid_cols{$class} } == 1) {
+                       my ($col) = keys %{ $valid_cols{$class} };
+                       print "$class is $col\n";
+                       delete $valid_cols{$class};
+                       for my $cl1 (keys %valid_cols) {
+                               delete $valid_cols{$cl1}->{$col};
+                       }
+                       $mul *= $your[$col] if $class =~ /\Adeparture/;
+                       next LOOP;
+               }
+       }
+}
+
+print "Total: $mul\n";
diff --git a/2020/33.pl b/2020/33.pl
new file mode 100755 (executable)
index 0000000..c8085ae
--- /dev/null
@@ -0,0 +1,75 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+my $is_active;
+
+my ($min_x, $min_y, $min_z) = (-1, -1, -1);
+my ($max_x, $max_y, $max_z) = (0, 0, 1);
+
+my $y = 0;
+while (<>) {
+       chomp;
+       my $x = 0;
+       for (split //) {
+               $is_active->{"$x,$y,0"} = 1
+                       if $_ eq '#';
+               print "Active $x,$y,0\n";
+               $x++;
+       }
+       $max_x = $x;
+       $y++;
+}
+$max_y = $y;
+
+my $iter = 0;
+while ($iter < 6) {
+       my $new_active;
+       my ($new_min_x, $new_min_y, $new_min_z) = ($max_x, $max_y, $max_z);
+       my ($new_max_x, $new_max_y, $new_max_z) = ($min_x, $min_y, $min_z);
+       print "iter $iter x=$min_x:$max_x, y=$min_y:$max_y, z=$min_z:$max_z\n";
+       for my $z ($min_z .. $max_z) {
+       for my $y ($min_y .. $max_y) {
+       for my $x ($min_x .. $max_x) {
+               # print "Trying $x,$y,$z\n";
+               my $count = 0;
+               for my $neigh (
+                       [-1, -1, -1], [-1, -1, 0], [-1, -1, 1],
+                       [-1, 0, -1 ], [-1, 0, 0],  [-1, 0, 1],
+                       [-1, 1, -1 ], [-1, 1, 0],  [-1, 1, 1],
+                       [0,  -1, -1], [0,  -1, 0], [0,  -1, 1],
+                       [0,  0, -1 ],              [0,  0, 1],
+                       [0,  1, -1 ], [0,  1, 0],  [0,  1, 1],
+                       [1,  -1, -1], [1,  -1, 0], [1,  -1, 1],
+                       [1,  0, -1 ], [1,  0, 0],  [1,  0, 1],
+                       [1,  1, -1 ], [1,  1, 0],  [1,  1, 1],) {
+                       my ($nx, $ny, $nz) = @$neigh;
+                       $nx += $x; $ny += $y; $nz += $z;
+                       $count++ if $is_active->{"$nx,$ny,$nz"};
+               }
+               # print "$count neighbours\n";
+               if (($is_active->{"$x,$y,$z"} && ($count == 2 || $count == 3))
+                       || (!$is_active->{"$x,$y,$z"} && $count == 3)) {
+                       $new_active->{"$x,$y,$z"} = 1;
+                       # print "new active: $x, $y, $z\n";
+                       $new_min_x = $x if $x < $new_min_x;
+                       $new_min_y = $y if $y < $new_min_y;
+                       $new_min_z = $z if $z < $new_min_z;
+                       $new_max_x = $x if $x > $new_max_x;
+                       $new_max_y = $y if $y > $new_max_y;
+                       $new_max_z = $z if $z > $new_max_z;
+               }
+       } } }
+       $min_x = $new_min_x-1;
+       $min_y = $new_min_y-1;
+       $min_z = $new_min_z-1;
+       $max_x = $new_max_x+1;
+       $max_y = $new_max_y+1;
+       $max_z = $new_max_z+1;
+
+       $is_active = $new_active;
+       $iter++;
+}
+
+print "Acive: ", scalar keys %$is_active, "\n";
+
diff --git a/2020/34.pl b/2020/34.pl
new file mode 100755 (executable)
index 0000000..501265c
--- /dev/null
@@ -0,0 +1,79 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+my $is_active;
+
+my ($min_x, $min_y, $min_z, $min_q) = (-1, -1, -1, -1);
+my ($max_x, $max_y, $max_z, $max_q) = (0, 0, 1, 1);
+
+my $y = 0;
+while (<>) {
+       chomp;
+       my $x = 0;
+       for (split //) {
+               $is_active->{"$x,$y,0,0"} = 1
+                       if $_ eq '#';
+               print "Active $x,$y,0,0\n";
+               $x++;
+       }
+       $max_x = $x;
+       $y++;
+}
+$max_y = $y;
+
+my $iter = 0;
+while ($iter < 6) {
+       my $new_active;
+       my ($new_min_x, $new_min_y, $new_min_z, $new_min_q)
+               = ($max_x, $max_y, $max_z, $max_q);
+       my ($new_max_x, $new_max_y, $new_max_z, $new_max_q)
+               = ($min_x, $min_y, $min_z, $min_q);
+       print "iter $iter x=$min_x:$max_x, y=$min_y:$max_y, z=$min_z:$max_z, q=$min_q:$max_q\n";
+       for my $q ($min_q .. $max_q) {
+       for my $z ($min_z .. $max_z) {
+       for my $y ($min_y .. $max_y) {
+       for my $x ($min_x .. $max_x) {
+               # print "Trying $x,$y,$z,$q: ";
+               my $count = 0;
+               for my $nx (-1 .. 1) {
+               for my $ny (-1 .. 1) {
+               for my $nz (-1 .. 1) {
+               for my $nq (-1 .. 1) {
+                       next if $nx == 0 && $ny == 0 && $nz == 0 && $nq == 0;
+                       my $mx = $nx + $x;
+                       my $my = $ny + $y;
+                       my $mz = $nz + $z;
+                       my $mq = $nq + $q;
+                       $count++ if $is_active->{"$mx,$my,$mz,$mq"};
+               } } } }
+               # print "$count neighbours\n";
+               if (($is_active->{"$x,$y,$z,$q"} && ($count == 2 || $count == 3))
+                       || (!$is_active->{"$x,$y,$z,$q"} && $count == 3)) {
+                       $new_active->{"$x,$y,$z,$q"} = 1;
+                       # print "new active: $x, $y, $z, $q\n";
+                       $new_min_x = $x if $x < $new_min_x;
+                       $new_min_y = $y if $y < $new_min_y;
+                       $new_min_z = $z if $z < $new_min_z;
+                       $new_min_q = $q if $q < $new_min_q;
+                       $new_max_x = $x if $x > $new_max_x;
+                       $new_max_y = $y if $y > $new_max_y;
+                       $new_max_z = $z if $z > $new_max_z;
+                       $new_max_q = $q if $q > $new_max_q;
+               }
+       } } } }
+       $min_x = $new_min_x-1;
+       $min_y = $new_min_y-1;
+       $min_z = $new_min_z-1;
+       $min_q = $new_min_q-1;
+       $max_x = $new_max_x+1;
+       $max_y = $new_max_y+1;
+       $max_z = $new_max_z+1;
+       $max_q = $new_max_q+1;
+
+       $is_active = $new_active;
+       $iter++;
+}
+
+print "Acive: ", scalar keys %$is_active, "\n";
+
diff --git a/2020/35.pl b/2020/35.pl
new file mode 100755 (executable)
index 0000000..5582de3
--- /dev/null
@@ -0,0 +1,13 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+my $sum = 0;
+while (<>) {
+       chomp;
+       1 while
+               s/\((\d+)\)/$1/ ||
+               s/(\d+)\s*([\+\*])\s*(\d+)/"$1$2$3"/ee;
+       $sum += $_;
+}
+print "sum=$sum\n";
diff --git a/2020/36.pl b/2020/36.pl
new file mode 100755 (executable)
index 0000000..4e25489
--- /dev/null
@@ -0,0 +1,11 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+$_ = '(('.join(')+(', <>).'))';
+s/\s//g;
+1 while
+       s/\((\d+)\)/$1/ ||
+       s/(\([^\(\)]*?)(\d+)\+(\d+)([^\(\)]*\))/$1.($2+$3).$4/e ||
+       s/(\([^\(\)]*?)(\d+)\*(\d+)([^\(\)]*\))/$1.($2*$3).$4/e;
+print "sum=$_\n";
diff --git a/2020/37.pl b/2020/37.pl
new file mode 100755 (executable)
index 0000000..97b5487
--- /dev/null
@@ -0,0 +1,49 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+my %rules;
+my %subrules;
+my %final;
+
+while (<>) {
+       chomp;
+       last if /^$/;
+       my ($id, $rest) = /\A(\d+): (.*)\z/;
+       if ($rest =~ /"(.)"/) {
+               $final{$id} = $1;
+               next;
+       }
+       my @alts;
+       my @subalts;
+       for my $seq (split /\|/, $rest) {
+               push @alts, [ $seq =~ /(\d+)/g ];
+               push @subalts, ($seq =~ /(\d+)/g);
+       }
+       $rules{$id} = \@alts;
+       $subrules{$id} = \@subalts;
+}
+
+while (keys %rules) {
+       RULE:
+       for my $id (keys %rules) {
+               for my $subr (@{ $subrules{$id} }) {
+                       next RULE if !defined $final{$subr};
+               }
+               $final{$id} = '(?:(?:' . join(')|(?:', map {
+                       join('', map { $final{$_} } @$_)
+                       } @{ $rules{$id} }) .'))';
+               print "\$final{$id} = $final{$id}\n";
+               delete $rules{$id};
+       }
+}
+
+my $re = '\A'.$final{0}.'\z';
+
+my $count = 0;
+while (<>) {
+       chomp;
+       $count++ if ($_ =~ /$re/);
+}
+
+print "$count matched\n";
diff --git a/2020/38.pl b/2020/38.pl
new file mode 100755 (executable)
index 0000000..7707c8e
--- /dev/null
@@ -0,0 +1,74 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+my %rules;
+my %subrules;
+my %final;
+
+while (<>) {
+       chomp;
+       last if /^$/;
+       my ($id, $rest) = /\A(\d+): (.*)\z/;
+       if ($rest =~ /"(.)"/) {
+               $final{$id} = $1;
+               next;
+       }
+       my @alts;
+       my @subalts;
+       for my $seq (split /\|/, $rest) {
+               push @alts, [ $seq =~ /(\d+)/g ];
+               push @subalts, ($seq =~ /(\d+)/g);
+       }
+       $rules{$id} = \@alts;
+       $subrules{$id} = \@subalts;
+}
+
+while (keys %rules) {
+       RULE:
+       for my $id (keys %rules) {
+               for my $subr (@{ $subrules{$id} }) {
+                       next RULE if !defined $final{$subr};
+               }
+               if ($id == 8) {
+                       $final{$id} = $final{42} . '+';
+                       delete $rules{$id};
+                       next RULE;
+               }
+
+               if ($id == 11) {
+                       $final{$id} = '(?:' . join('|',
+                               $final{42} . $final{31},
+                               $final{42} x 2 . $final{31} x 2,
+                               $final{42} x 3 . $final{31} x 3,
+                               $final{42} x 4 . $final{31} x 4,
+                               $final{42} x 5 . $final{31} x 5,
+                               $final{42} x 6 . $final{31} x 6,
+                               $final{42} x 7 . $final{31} x 7,
+                               $final{42} x 8 . $final{31} x 8,
+                               $final{42} x 9 . $final{31} x 9,
+                               $final{42} x 10 . $final{31} x 10,
+                               $final{42} x 11 . $final{31} x 11,
+                               $final{42} x 12 . $final{31} x 12,
+                               ) . ')';
+                       delete $rules{$id};
+                       next RULE;
+               }
+               
+               $final{$id} = '(?:(?:' . join(')|(?:', map {
+                       join('', map { $final{$_} } @$_)
+                       } @{ $rules{$id} }) .'))';
+               print "\$final{$id} = $final{$id}\n";
+               delete $rules{$id};
+       }
+}
+
+my $re = '\A'.$final{0}.'\z';
+
+my $count = 0;
+while (<>) {
+       chomp;
+       $count++ if ($_ =~ /$re/);
+}
+
+print "$count matched\n";
diff --git a/2020/39.pl b/2020/39.pl
new file mode 100755 (executable)
index 0000000..40f98ab
--- /dev/null
@@ -0,0 +1,57 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+sub rows2str {
+       my (@rows) = @_;
+       return join("\n", @rows) . "\n";
+}
+
+sub rotate {
+       my ($str) = (@_);
+       my $dim =()= $str =~ /(\n)/g;
+       my $newstr = '';
+       for my $y (0 .. $dim-1) {
+               for my $x (0 .. $dim-1) {
+                       $newstr .= substr($str, $y + ($dim+1)*($dim - $x - 1), 1);
+               }
+               $newstr .= "\n";
+       }
+       return $newstr;
+}
+
+sub flip {
+       my ($str) = @_;
+       return rows2str(map { join('', reverse split //) } split /\n/, $str);
+}
+
+sub rotate_or_flip {
+       my ($str, $count) = @_;
+       return $count == 4 ? flip($str) : rotate($str);
+}
+
+sub top_side {
+       $_[0] =~ /\A(.*?)\n/xms;
+       return $1;
+}
+
+my %top2tile;
+
+local $/ = "\n\n";
+
+while (<>) {
+       my ($id, $data) = /\ATile\s+(\d+):\n(.*?\n)\n?\z/xms;
+       for (1 .. 8) {
+               push @{ $top2tile{top_side($data)} }, $id;
+               $data = rotate_or_flip($data, $_);
+       }
+}
+
+my %single_ids;
+for my $row (keys %top2tile) {
+       next if @{ $top2tile{$row} } != 1;
+       $single_ids{ $top2tile{$row}->[0] }++;
+}
+
+my @corners = grep { $single_ids{$_} == 4 } keys %single_ids;
+print join('*', @corners), '=', eval join('*', @corners), "\n";
diff --git a/2020/4.pl b/2020/4.pl
new file mode 100755 (executable)
index 0000000..d16915a
--- /dev/null
+++ b/2020/4.pl
@@ -0,0 +1,22 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+my $count = 0;
+
+while (<>) {
+       my ($min, $max, $letter, $pass) = /\A(\d+)-(\d+)\s+(\S):\s+(\S+)\s*\z/;
+       if (!defined $pass) {
+               print "Divny radek $_\n";
+               next;
+       }
+
+       no warnings 'substr';
+       # no warnings 'uninitialized';
+       $count++ if !!(substr($pass, $min-1, 1) eq $letter)
+               + !!(substr($pass, $max-1, 1) eq $letter)
+               == 1;
+}
+
+print $count, "\n";
+
diff --git a/2020/40.pl b/2020/40.pl
new file mode 100755 (executable)
index 0000000..074e561
--- /dev/null
@@ -0,0 +1,124 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+sub rows2str {
+       my (@rows) = @_;
+       return join("\n", @rows) . "\n";
+}
+
+sub rotate {
+       my ($str) = (@_);
+       my $dim =()= $str =~ /(\n)/g;
+       my $newstr = '';
+       for my $y (0 .. $dim-1) {
+               for my $x (0 .. $dim-1) {
+                       $newstr .= substr($str, $y + ($dim+1)*($dim - $x - 1), 1);
+               }
+               $newstr .= "\n";
+       }
+       return $newstr;
+}
+
+sub flip {
+       my ($str) = @_;
+       return rows2str(map { join('', reverse split //) } split /\n/, $str);
+}
+
+sub rotate_or_flip {
+       my ($str, $count) = @_;
+       return $count == 4 ? flip($str) : rotate($str);
+}
+
+sub trim {
+       my ($str) = @_;
+       my @rows = split /\n/, $str;
+       shift @rows;
+       pop @rows;
+       return map { s/\A.//; s/.\z//; $_ } @rows;
+}
+
+sub top_side {
+       $_[0] =~ /\A(.*?)\n/xms;
+       return $1;
+}
+
+sub bottom_side {
+       $_[0] =~ /\A.*\n(.*?)\n\z/xms;
+       return $1;
+}
+
+sub left_side {
+       return join('', map { substr($_, 0, 1) } split /\n/, $_[0]);
+}
+
+sub right_side {
+       return join('', map { substr($_, -1, 1) } split /\n/, $_[0]);
+}
+
+my (%top2tile, %left2tile);
+
+local $/ = "\n\n";
+
+while (<>) {
+       my ($id, $data) = /\ATile\s+(\d+):\n(.*?\n)\n?\z/xms;
+       for (1 .. 8) {
+               push @{ $top2tile{top_side($data)} }, [ $id, $data ];
+               push @{ $left2tile{left_side($data)} }, [ $id, $data ];
+               $data = rotate_or_flip($data, $_);
+       }
+}
+
+# computed in 39.pl
+my $dim = 12;
+my $top_row = '###.#.#...';
+
+# my $dim = 3;
+# my $top_row = '#...##.#..';
+
+my $top_id = -1;
+my @map_rows;
+my $data;
+
+for my $y (0 .. $dim-1) {
+       my ($id)  = grep { $_->[0] != $top_id } @{ $top2tile{$top_row} };
+       ($top_id, $data) = @$id;
+       $top_row = bottom_side($data);
+
+       push @map_rows, trim($data);
+
+       my $right_id = $top_id;
+       my $right_row = right_side($data);
+
+       for my $x (1 .. $dim-1) {
+               my ($id) = grep { $_->[0] != $right_id } @{ $left2tile{$right_row} };
+               ($right_id, $data) = @$id;
+               $right_row = right_side($data);
+
+               my $i = @map_rows - 8;
+               for (trim($data)) {
+                       $map_rows[$i++] .= $_;
+               }
+       }
+}
+
+my $map = rows2str(@map_rows);
+my $hashes =()= $map =~ /#/g;
+
+my $monster = '.(?='
+       . join('[\.#\n]' x ($dim * 8 - 20 + 1),
+               map { my $a=$_; $a=~ s/\./[\.#]/g; $a }
+                '..................#.',
+               '#....##....##....###',
+               '.#..#..#..#..#..#...'
+       ) . ')';
+
+for (1 .. 8) {
+       if (my $count =()= $map =~ /($monster)/g) {
+               print "$count matches\n";
+               print "$hashes-$count*15=", $hashes-$count*15, "\n";
+               exit 0;
+       }
+       $map = rotate_or_flip($map, $_);
+}
+
diff --git a/2020/41.pl b/2020/41.pl
new file mode 100755 (executable)
index 0000000..846f78e
--- /dev/null
@@ -0,0 +1,46 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+my %allergens;
+my %ingredients;
+my %in_count;
+
+while (<>) {
+       chomp;
+       my ($ingr, $al) = /\A([^\(]+) \(contains (.*)\)\z/;
+       my %ingr = map { $_ => 1 } split /\s+/, $ingr;
+       my @al = split /, /, $al;
+       for my $al (@al) {
+               if (defined $allergens{$al}) {
+                       for my $in1 (keys %{ $allergens{$al} }) {
+                               if (! $ingr{$in1}) {
+                                       delete $allergens{$al}->{$in1};
+                               }
+                       }
+               } else {
+                       $allergens{$al} = { %ingr };
+               }
+       }
+       for my $in (keys %ingr) {
+               $ingredients{$in} = {};
+               $in_count{$in}++;
+       }
+}
+
+for my $al (keys %allergens) {
+       for my $in (keys %{ $allergens{$al} }) {
+               print "$in can be $al.\n";
+               $ingredients{$in}->{$al} = 1;
+       }
+}
+
+my $sum = 0;
+for my $in (keys %ingredients) {
+       if (keys %{ $ingredients{$in} } == 0) {
+               # print "$in can't contain allergen\n";
+               $sum += $in_count{$in};
+       }
+}
+
+print "Sum=$sum\n";
diff --git a/2020/42.pl b/2020/42.pl
new file mode 100755 (executable)
index 0000000..05bf2ac
--- /dev/null
@@ -0,0 +1,42 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+my %allergens;
+my %is_al;
+
+while (<>) {
+       chomp;
+       my ($ingr, $al) = /\A([^\(]+) \(contains (.*)\)\z/;
+       my %ingr = map { $_ => 1 } split /\s+/, $ingr;
+       my @al = split /, /, $al;
+       for my $al (@al) {
+               if (defined $allergens{$al}) {
+                       for my $in1 (keys %{ $allergens{$al} }) {
+                               if (! $ingr{$in1}) {
+                                       delete $allergens{$al}->{$in1};
+                               }
+                       }
+               } else {
+                       $allergens{$al} = { %ingr };
+               }
+       }
+}
+
+AGAIN:
+for my $al (keys %allergens) {
+       if (keys %{ $allergens{$al} } == 1) {
+               my $in1;
+               for my $in (keys %{ $allergens{$al} }) {
+                       $is_al{$in} = $al;
+                       $in1 = $in;
+               }
+               delete $allergens{$al};
+               for my $al1 (keys %allergens) {
+                       delete $allergens{$al1}->{$in1};
+               }
+               goto AGAIN;
+       }
+}
+
+print join(',', sort { $is_al{$a} cmp $is_al{$b} } keys %is_al), "\n";
diff --git a/2020/43.pl b/2020/43.pl
new file mode 100755 (executable)
index 0000000..3f6ebe6
--- /dev/null
@@ -0,0 +1,28 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+local $/ = "\n\n";
+
+my @pl1 = <> =~ /^(\d+)$/gxms;
+my @pl2 = <> =~ /^(\d+)$/gxms;
+print "pl2=@pl2.\n";
+
+while (@pl1 && @pl2) {
+       my $p1 = shift @pl1;
+       my $p2 = shift @pl2;
+       print "$p1 vs $p2\n";
+       if ($p1 > $p2) {
+               push @pl1, $p1, $p2;
+       } else {
+               push @pl2, $p2, $p1;
+       }
+}
+
+@pl1 = @pl2 if @pl2;
+my $sum = 0;
+while (@pl1) {
+       $sum += +$pl1[0] * @pl1;
+       shift @pl1;
+}
+print "sum=$sum\n";
diff --git a/2020/44.pl b/2020/44.pl
new file mode 100755 (executable)
index 0000000..05d941f
--- /dev/null
@@ -0,0 +1,59 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+local $/ = "\n\n";
+
+my @pl1 = <> =~ /^(\d+)$/gxms;
+my @pl2 = <> =~ /^(\d+)$/gxms;
+
+print "sum=", play(\@pl1, \@pl2), "\n";
+
+sub play {
+       my ($d1, $d2) = @_;
+       my @pl1 = @$d1;
+       my @pl2 = @$d2;
+
+       my %seen;
+
+       while (@pl1 && @pl2) {
+               my $conf = join('|', @pl1, "#", @pl2);
+               if ($seen{$conf}++) {
+                       return score(@pl1);
+               }
+               my $p1 = shift @pl1;
+               my $p2 = shift @pl2;
+
+               if ($p1 <= @pl1 && $p2 <= @pl2) {
+                       my @sub1 = @pl1[0 .. $p1-1];
+                       my @sub2 = @pl2[0 .. $p2-1];
+                       if (play(\@sub1, \@sub2) > 0) {
+                               push @pl1, $p1, $p2;
+                       } else {
+                               push @pl2, $p2, $p1;
+                       }
+               } else {
+                       if ($p1 > $p2) {
+                               push @pl1, $p1, $p2;
+                       } else {
+                               push @pl2, $p2, $p1;
+                       }
+               }
+       }
+       if (@pl1) {
+               return score(@pl1);
+       } else {
+               return -score(@pl2);
+       }
+}
+
+sub score {
+       my @pl1 = @_;
+       my $sum = 0;
+       while (@pl1) {
+               $sum += +$pl1[0] * @pl1;
+               shift @pl1;
+       }
+       return $sum;
+}
+
diff --git a/2020/45.pl b/2020/45.pl
new file mode 100755 (executable)
index 0000000..b1b5516
--- /dev/null
@@ -0,0 +1,20 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+my $rounds;
+($rounds, $_) = @ARGV;
+
+while ($rounds--) {
+       my ($cur) = /\A(.)/;
+       do {
+               $cur--;
+               $cur = 9 if $cur == 0;
+       } while (/\A.{1,3}$cur/);
+       
+       s/\A(.)(...)(.*)$cur(.*)\z/$3$cur$2$4$1/;
+       print $_, "\n";
+}
+
+s/\A(.*)1(.*)\z/Result is $2$1\n/;
+print;
diff --git a/2020/46.pl b/2020/46.pl
new file mode 100755 (executable)
index 0000000..b9744b8
--- /dev/null
@@ -0,0 +1,75 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+my $rounds;
+($rounds, $_) = @ARGV;
+
+my @cups_nums = split //;
+my @cups_pos;
+my $i;
+for (@cups_nums) {
+       $cups_pos[$_] = $i++;
+}
+
+my $max = 1_000_000;
+
+my ($prev, @cups, $one);
+for (@cups_nums, 10 .. $max) {
+       my $cup = {
+               num => $_,
+               prev_num => $prev,
+               next => undef,
+       };
+       push @cups, $cup;
+       $one = $cup if $cup->{num} == 1;
+       $prev = $cup;
+}
+
+$prev = $cups[-1];
+for my $cup (@cups) {
+       $cup->{prev_num} = $cups[$cups_pos[$cup->{num}-1]]
+               if $cup->{num} > 1 && $cup->{num} <= 10;
+       $cup->{prev_num} = $cups[-1]
+               if $cup->{num} == 1;
+       $prev->{next} = $cup;
+       $prev = $cup;
+}
+
+# for my $cup (@cups) {
+#      print $cup->{num}, ": >", $cup->{next}->{num}, " <", $cup->{prev_num}->{num}, "\n";
+# }
+
+my $cur = $cups[0];
+
+while ($rounds--) {
+       my @pickup;
+       my $cup = $cur->{next};
+       my $dest = $cur->{prev_num};
+       my %num_seen;
+       # print "pickup: ";
+       for (1..3) {
+               push @pickup, $cup;
+               $num_seen{ $cup->{num} } = 1;
+               # print $cup->{num}, ' ';
+               $cup = $cup->{next};
+       }
+       $dest = $dest->{prev_num} while $num_seen{$dest->{num}};
+       # print " dest: ", $dest->{num}, "\n";
+       $cur->{next} = $cup;
+       my $end = $dest->{next};
+       $dest->{next} = $pickup[0];
+       $pickup[-1]->{next} = $end;
+
+       $cur = $cur->{next};
+       # $end = $cur;
+       # do {
+       #       print $end->{num}, ',';
+       #       $end = $end->{next};
+       # } while ($end->{num} != $cur->{num});
+       # print "\n";
+       print "round $rounds\n" if $rounds % 100_000 == 0;
+}
+
+print "one -> ", $one->{next}->{num}, '*', $one->{next}->{next}->{num},
+       "=", $one->{next}->{num} * $one->{next}->{next}->{num}, "\n";
diff --git a/2020/47.pl b/2020/47.pl
new file mode 100755 (executable)
index 0000000..fca227f
--- /dev/null
@@ -0,0 +1,29 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+my %tiles;
+
+while (<>) {
+       chomp;
+       my ($x, $y) = (0, 0);
+       while (length) {
+               s/\A(se|ne|sw|nw|e|w)//;
+               if ($1 eq 'ne') {
+                       $y++;
+               } elsif ($1 eq 'e') {
+                       $x++;
+               } elsif ($1 eq 'se') {
+                       $x++; $y--;
+               } elsif ($1 eq 'sw') {
+                       $y--;
+               } elsif ($1 eq 'w') {
+                       $x--;
+               } elsif ($1 eq 'nw') {
+                       $x--; $y++;
+               }
+       }
+       $tiles{"$x|$y"}++;
+}
+
+print "Tiles=" . (grep { $tiles{$_} % 2 == 1 } keys %tiles)."\n";
diff --git a/2020/48.pl b/2020/48.pl
new file mode 100755 (executable)
index 0000000..f76f993
--- /dev/null
@@ -0,0 +1,60 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+my %tiles;
+
+my ($min_x, $max_x, $min_y, $max_y);
+
+while (<>) {
+       chomp;
+       my ($x, $y) = (0, 0);
+       while (length) {
+               s/\A(se|ne|sw|nw|e|w)//;
+               if ($1 eq 'ne') {
+                       $y++;
+               } elsif ($1 eq 'e') {
+                       $x++;
+               } elsif ($1 eq 'se') {
+                       $x++; $y--;
+               } elsif ($1 eq 'sw') {
+                       $y--;
+               } elsif ($1 eq 'w') {
+                       $x--;
+               } elsif ($1 eq 'nw') {
+                       $x--; $y++;
+               }
+       }
+       $tiles{"$x|$y"} ^= 1;
+       $min_x = $x if !defined $min_x || $min_x > $x;
+       $max_x = $x if !defined $max_x || $max_x < $x;
+       $min_y = $y if !defined $min_y || $min_y > $y;
+       $max_y = $y if !defined $max_y || $max_y < $y;
+}
+
+
+for (1 .. 100) {
+       $min_x--; $min_y--; $max_x++, $max_y++;
+       my %newtiles;
+       for my $x ($min_x .. $max_x) {
+       for my $y ($min_y .. $max_y) {
+               my $count = 0;
+               $count++ if $tiles{($x+1).'|'.($y)}; # E
+               $count++ if $tiles{($x).'|'.($y+1)}; # NE
+               $count++ if $tiles{($x+1).'|'.($y-1)}; # SE
+               $count++ if $tiles{($x).'|'.($y-1)}; # SW
+               $count++ if $tiles{($x-1).'|'.($y)}; # W
+               $count++ if $tiles{($x-1).'|'.($y+1)}; # NW
+               if ($tiles{"$x|$y"} && ($count == 0 || $count > 2)) {
+                       $newtiles{"$x|$y"} = 0;
+               } elsif (!$tiles{"$x|$y"} && $count == 2) {
+                       $newtiles{"$x|$y"} = 1;
+               } else {
+                       $newtiles{"$x|$y"} = $tiles{"$x|$y"};
+               }
+       }
+       }
+       %tiles = %newtiles;
+       print "Tiles=" . (grep { $tiles{$_} } keys %tiles)."\n";
+}
+
diff --git a/2020/49.pl b/2020/49.pl
new file mode 100755 (executable)
index 0000000..eeeead3
--- /dev/null
@@ -0,0 +1,35 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+my ($val1, $val2) = (1, 1);
+my $iter = 0;
+my $subj1 = 7;
+my $subj2 = 7;
+my ($pub1, $pub2) = @ARGV;
+
+while (++$iter) {
+       $val1 *= $subj1;
+       $val2 *= $subj2;
+       $val1 %= 20201227;
+       $val2 %= 20201227;
+       if ($val1 == $pub1) {
+               print "$iter loops for subj1\n";
+               $val1 = $pub2;
+               for (2 .. $iter) {
+                       $val1 *= $pub2;
+                       $val1 %= 20201227;
+               }
+               print "key=$val1\n";
+       }
+       if ($val2 == $pub2) {
+               print "$iter loops for subj2\n";
+               $val2 = $pub1;
+               for (2 .. $iter) {
+                       $val2 *= $pub1;
+                       $val2 %= 20201227;
+               }
+               print "key=$val2\n";
+       }
+}
+
diff --git a/2020/5.pl b/2020/5.pl
new file mode 100755 (executable)
index 0000000..2a7b22d
--- /dev/null
+++ b/2020/5.pl
@@ -0,0 +1,24 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+my $field = do { local $/; <> };
+my $rows = $field =~ s/\s//g;
+my $flen = length $field;
+my $cols = $flen/$rows;
+
+print "Field has $flen bytes, in $rows rows and $cols cols\n";
+
+my $step = $cols + 2;
+
+my $col = 0;
+my $sum = 0;
+for my $row (0 .. $rows-1) {
+       $sum++ if substr($field, $row*$cols + $col, 1) eq '#';
+       $col += 3;
+       $col %= $cols;
+}
+print "sum=$sum\n";
+
+       
+
diff --git a/2020/6.pl b/2020/6.pl
new file mode 100755 (executable)
index 0000000..6749d06
--- /dev/null
+++ b/2020/6.pl
@@ -0,0 +1,38 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+my $field = do { local $/; <> };
+my $rows = $field =~ s/\s//g;
+my $flen = length $field;
+my $cols = $flen/$rows;
+
+print "Field has $flen bytes, in $rows rows and $cols cols\n";
+
+sub slope {
+       my ($colstep, $rowstep) = @_;
+
+       $colstep += $cols;
+
+       my $col = 0;
+       my $sum = 0;
+       my $row = 0;
+       while ($row < $rows) {
+               print "row $row\n";
+               $sum++ if substr($field, $row*$cols + $col, 1) eq '#';
+               $col += $colstep;
+               $col %= $cols;
+               $row += $rowstep;
+       }
+       print "$colstep x $rowstep, sum=$sum\n";
+       return $sum;
+}
+
+print "Total: ",
+       slope(1, 1)
+       * slope(3, 1)
+       * slope(5, 1)
+       * slope(7, 1)
+       * slope(1, 2),
+       "\n";
+
diff --git a/2020/7.pl b/2020/7.pl
new file mode 100755 (executable)
index 0000000..d6147c3
--- /dev/null
+++ b/2020/7.pl
@@ -0,0 +1,16 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+$/ = "\n\n";
+
+my $valid = 0;
+while (<>) {
+       $valid++ if (
+               grep { /^(?:byr|iyr|eyr|hgt|hcl|ecl|pid):/ }
+               split /\s+/
+       ) == 7;
+}
+
+print "Valid: $valid\n";
+
diff --git a/2020/8.pl b/2020/8.pl
new file mode 100755 (executable)
index 0000000..c8e2a20
--- /dev/null
+++ b/2020/8.pl
@@ -0,0 +1,38 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+$/ = "\n\n";
+
+my $valid = 0;
+while (<>) {
+       my $correct = 0;
+       for my $field (split /\s+/) {
+               print "$field";
+               $correct++
+                       if $field =~ /\Abyr:(\d{4})\z/
+                       && $1 >= 1920 && $1 <= 2002;
+               $correct++
+                       if $field =~ /\Aiyr:(\d{4})\z/
+                       && $1 >= 2010 && $1 <= 2020;
+               $correct++
+                       if $field =~ /\Aeyr:(\d{4})\z/
+                       && $1 >= 2020 && $1 <= 2030;
+               $correct++
+                       if $field =~ /\Ahgt:(\d+)(cm|in)\z/
+                       && ($2 eq 'cm' && $1 >= 150 && $1 <= 193
+                       || $2 eq 'in' && $1 >= 59 && $1 <= 76);
+               $correct++
+                       if $field =~ /\Ahcl:#[a-f0-9]{6}\z/;
+               $correct++
+                       if $field =~ /\Aecl:(?:amb|blu|brn|gry|grn|hzl|oth)\z/;
+               $correct++
+                       if $field =~ /\Apid:(?:\d{9})\z/;
+               print "\t$correct\n";
+       }
+       $valid++ if $correct >= 7;
+       print "\n";
+}
+
+print "Valid: $valid\n";
+
diff --git a/2020/9-golf.pl b/2020/9-golf.pl
new file mode 100644 (file)
index 0000000..d941993
--- /dev/null
@@ -0,0 +1 @@
+perl -lnE 'y/FBRL/0110/;$x=$_ if$x<$_}{say oct"0b$x"'
diff --git a/2020/9.pl b/2020/9.pl
new file mode 100755 (executable)
index 0000000..dcdc4ff
--- /dev/null
+++ b/2020/9.pl
@@ -0,0 +1,13 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+my $max = 0;
+while (<>) {
+       chomp;
+       y/FBRL/0110/;
+       my $seat =  eval "0b$_";
+       $max = $seat if $max < $seat;
+}
+
+print $max, "\n";