This commit is contained in:
Yanick Champoux 2021-12-05 11:51:21 -05:00
parent 6c0551059f
commit 8011c14aeb
56 changed files with 1659 additions and 0 deletions

13
2020/01/part1.pl Normal file
View File

@ -0,0 +1,13 @@
use 5.20.0;
use warnings;
use Path::Tiny;
my @expenses = path('input')->lines;
while(my $this = shift @expenses) {
my $that = 2020 - $this;
next unless grep { $_ == $that } @expenses;
print join " ", $this, $that, $this*$that;
}

30
2020/01/part2.pl Normal file
View File

@ -0,0 +1,30 @@
use 5.20.0;
use warnings;
use Path::Tiny;
use List::AllUtils qw/ product /;
use experimental qw/ signatures /;
my @expenses = path('input')->lines;
$, = " ";
print product find_group( \@expenses, 2020, 3, [] )->@*;
sub find_group($list,$goal,$n,$so_far) {
if( $n == 1 ) {
if( grep { $_ == $goal } @$list ) {
return [ @$so_far, $goal ];
};
return;
}
my @list = grep { $_ < $goal } @$list;
while ( my $next = shift @list ) {
my $result = find_group( \@list, $goal - $next, $n-1, [ @$so_far, $next ] ) or next;
return $result;
}
}

22
2020/02/part1.pl Normal file
View File

@ -0,0 +1,22 @@
use 5.20.0;
use warnings;
use Path::Tiny;
use experimental qw/
signatures
postderef
/;
my @lines = path(shift)->lines;
print scalar grep { is_valid($_) } @lines;
sub is_valid($line) {
$line =~ /(?<min>\d+)-(?<max>\d+) (?<letter>\w): (?<password>\w+)/;
my $min = $+{min};
my $max = $+{max};
my @matches = $+{password} =~ /$+{letter}/g;
return ( @matches >= $min and @matches <= $max );
}

20
2020/02/part2.pl Normal file
View File

@ -0,0 +1,20 @@
use 5.20.0;
use warnings;
use Path::Tiny;
use experimental qw/
signatures
postderef
/;
my @lines = path(shift)->lines;
print scalar grep { is_valid($_) } @lines;
sub is_valid($line) {
$line =~ /(?<min>\d+)-(?<max>\d+) (?<letter>\w): (?<password>\w+)/;
return 1 == grep { $_ eq $+{letter} }
(split '', $+{password})[ map { $_ -1 } @+{'min','max'} ];
}

24
2020/03/part1.pl Normal file
View File

@ -0,0 +1,24 @@
use 5.20.0;
use warnings;
use experimental qw/
signatures
postderef
/;
use Path::Tiny;
my @forest = path(shift)->lines({chomp => 1 });
my $trees = 0;
my $i = 0;
for my $line ( @forest ) {
$trees++ if '#' eq substr $line, $i, 1;
$i+=3;
$i %= length $line;
}
print $trees;

46
2020/03/part2.pl Normal file
View File

@ -0,0 +1,46 @@
use 5.20.0;
use warnings;
use experimental qw/
signatures
postderef
/;
use Path::Tiny;
my @forest = path(shift)->lines({chomp => 1 });
sub go_down($forest,$right,$down=1) {
my $trees = 0;
my @forest = @$forest;
my $line = shift @forest;
my $i = 0;
use DDP;
while( $line) {
$trees++ if '#' eq substr $line, $i, 1;
$i+=$right;
$i %= length $line;
$line = shift @forest for 1..$down;
}
return $trees
}
use List::AllUtils qw/ product /;
$, = " ";
print product map { go_down([@forest],@$_) } (
[1,1],
[3,1],
[5,1],
[7,1],
[1,2],
)

11
2020/03/test Normal file
View File

@ -0,0 +1,11 @@
..##.......
#...#...#..
.#....#..#.
..#.#...#.#
.#...##..#.
..#.##.....
.#.#.#....#
.#........#
#.##...#...
#...##....#
.#..#...#.#

25
2020/04/part1.pm Normal file
View File

@ -0,0 +1,25 @@
use 5.20.0;
use warnings;
use Path::Tiny;
use List::AllUtils qw/all/;
use parent qw/ Exporter/;
use experimental qw/
signatures
postderef
/;
our @EXPORT = qw/ valid_passports /;
sub valid_passports($file) {
my $data = path($file)->slurp;
my @passports = map { +{ split /:| |\n/ } } split "\n\n", $data;
return scalar grep {
my $p = $_;
all { $p->{$_} } qw/ byr iyr eyr hgt hcl ecl pid /
} @passports;
}

47
2020/04/part2.pm Normal file
View File

@ -0,0 +1,47 @@
use 5.20.0;
no warnings;
use Path::Tiny;
use List::AllUtils qw/all/;
use parent qw/ Exporter/;
use experimental qw/
signatures
postderef
/;
our @EXPORT = qw/ valid_passports_2 validate_passport /;
my %validator = (
byr => sub($x) { $x >= 1920 and $x <= 2002 },
iyr => sub($x) { $x >= 2010 and $x <= 2020 },
eyr => sub($x) { $x >= 2020 and $x <= 2030 },
hgt => sub($x) { $x =~ /(in|cm)$/ and (
$& eq 'in' ? ( $x >= 59 and $x <= 76) :( $x >= 150 and $x <= 193)
)},
hcl => sub($x) {
!!($x =~ /^#[0-9a-f]{6}$/)
},
ecl => sub($x) { $x =~ /^(amb|blu|brn|gry|grn|hzl|oth)$/ },
pid => sub($x) { $x =~ /^\d{9}$/ },
);
use List::AllUtils qw/ pairmap /;
sub validate_passport(%passport) {
pairmap { $a => $validator{$a}->($b) } %passport;
}
sub valid_passports_2($file) {
my $data = path($file)->slurp;
my @passports = map { +{ split /:| |\n/ } } split "\n\n", $data;
return scalar grep {
my $p = $_;
all { $validator{$_}->($p->{$_}) } qw/ byr iyr eyr hgt hcl ecl pid /
} @passports;
}

13
2020/04/test Normal file
View File

@ -0,0 +1,13 @@
ecl:gry pid:860033327 eyr:2020 hcl:#fffffd
byr:1937 iyr:2017 cid:147 hgt:183cm
iyr:2013 ecl:amb cid:350 eyr:2023 pid:028048884
hcl:#cfa07d byr:1929
hcl:#ae17e1 iyr:2013
eyr:2024
ecl:brn pid:760753108 byr:1931
hgt:179cm
hcl:#cfa07d eyr:2025 pid:166559648
iyr:2011 ecl:brn hgt:59in

11
2020/04/tests.t Normal file
View File

@ -0,0 +1,11 @@
use Test::More;
BEGIN { push @INC, '.'; }
use part1;
is valid_passports('test') => 2;
is valid_passports('input') => 210;
done_testing();

19
2020/04/tests2.t Normal file
View File

@ -0,0 +1,19 @@
use Test2::V0;
BEGIN { push @INC, '.'; }
use List::AllUtils qw/ pairmap /;
use part2;
is { pairmap { $a => !!$b } validate_passport(
pid => '087499704', hgt => '74in',
ecl => 'grn', iyr => 2012, eyr => 2030, byr => 1980,
hcl => '#623a2f')
} => {
map {
$_ => 1 } qw/ pid hgt ecl iyr eyr byr hcl /
};
is valid_passports_2('input') => 131;
done_testing();

34
2020/05/part1.pm Normal file
View File

@ -0,0 +1,34 @@
package part1;
use 5.20.0;
use warnings;
use experimental qw/
signatures
postderef
switch
/;
use parent qw/ Exporter::Tiny /;
our @EXPORT = qw/ resolve /;
sub resolve($code) {
my @row = ( 0, 127 );
my @column = ( 0, 7 );
for ( split '', $code ) {
when ('F') { $row[1] = $row[0] + int( ( $row[1] - $row[0] ) / 2 ) }
when ('B') { $row[0] = $row[0] + 1 + int( ( $row[1] - $row[0] ) / 2 ) }
when ('L') {
$column[1] = $column[0] + int( ( $column[1] - $column[0] ) / 2 )
}
when ('R') {
$column[0] = $column[0] + 1 +
int( ( $column[1] - $column[0] ) / 2 )
}
}
return 8 * $row[0] + $column[0];
}

26
2020/05/part2.pm Normal file
View File

@ -0,0 +1,26 @@
package part2;
use 5.20.0;
use warnings;
use experimental qw/
signatures
postderef
switch
/;
use part1;
sub solution(@codes) {
my @positions = sort { $a <=> $b } map { resolve($_) } @codes;
my $i = $positions[0];
for (@positions) {
return $_ - 1 if $_ != $i++;
}
}
1;

21
2020/05/tests.t Normal file
View File

@ -0,0 +1,21 @@
use Test2::V0;
BEGIN { push @INC, '.' }
use Path::Tiny;
use List::AllUtils qw/ max /;
use part1;
use part2;
subtest "part1" => sub {
is resolve('BFFFBBFRRR') => 567;
is max( map { resolve($_) } path('input')->lines ) => 813;
};
subtest part2 => sub {
is part2::solution( path('input')->lines ) => 612;
};
done_testing();

21
2020/06/part1.pm Normal file
View File

@ -0,0 +1,21 @@
package part1;
use 5.20.0;
use warnings;
use Path::Tiny;
use List::AllUtils qw/ sum /;
use experimental qw/
signatures
postderef
/;
sub solution($input) {
return sum map {
my %x = map { $_ => $_ } /(\w)/g;
scalar keys %x;
} split "\n\n", $input;
}
1;

18
2020/06/part2.pm Normal file
View File

@ -0,0 +1,18 @@
package part2;
use 5.20.0;
use warnings;
use List::AllUtils qw/ sum reduce /;
use Set::Object qw/ set /;
use experimental qw/ signatures postderef /;
sub solution($input) {
return sum map {
my $answers = reduce { $a * $b } map { set( split '' ) } split "\n";
$answers->size;
} split "\n\n", $input;
}
1;

11
2020/06/tests.t Normal file
View File

@ -0,0 +1,11 @@
use Test2::V0;
BEGIN { push @INC, '.'; }
use part1;
use part2;
use Path::Tiny;
is part1::solution( path('input')->slurp ) => 6633;
is part2::solution( path('input')->slurp ) => 3202;
done_testing();

42
2020/07/part1.pm Normal file
View File

@ -0,0 +1,42 @@
package part1;
use 5.20.0;
use warnings;
use experimental qw/
signatures
postderef
/;
sub parse_line($line) {
$line =~ s/(\w+ \w+) bags//;
my $bag = $1;
my %content = ();
while ( $line =~ /(\d+) (\w+ \w+) bag/g ) {
$content{$2} = $1;
}
return $bag => \%content;
}
sub contains( $name, $target, $bags ) {
return 1 if $bags->{$name}{$target};
for ( keys $bags->{$name}->%* ) {
return 1 if contains( $_, $target, $bags );
}
return 0;
}
sub solution(@lines) {
my %bags = map { parse_line($_) } @lines;
return scalar grep { contains($_, 'shiny gold', \%bags) } keys %bags;
}
1;

29
2020/07/part2.pl Normal file
View File

@ -0,0 +1,29 @@
package part2;
use 5.20.0;
use warnings;
use experimental qw/
signatures
postderef
/;
require './part1.pm';
sub contains($name, $bags) {
my $contained = 0;
while( my( $name, $qty ) = each $bags->{$name}->%* ) {
$contained += $qty * ( 1+ contains($name, $bags));
}
return $contained;
}
sub solution(@lines) {
my %bags = map { part1::parse_line($_) } @lines;
return contains('shiny gold', \%bags );
}

45
2020/07/tests.t Normal file
View File

@ -0,0 +1,45 @@
use Test2::V0;
use Path::Tiny;
require './part1.pm';
require './part2.pl';
my $sample = <<'END';
light red bags contain 1 bright white bag, 2 muted yellow bags.
dark orange bags contain 3 bright white bags, 4 muted yellow bags.
bright white bags contain 1 shiny gold bag.
muted yellow bags contain 2 shiny gold bags, 9 faded blue bags.
shiny gold bags contain 1 dark olive bag, 2 vibrant plum bags.
dark olive bags contain 3 faded blue bags, 4 dotted black bags.
vibrant plum bags contain 5 faded blue bags, 6 dotted black bags.
faded blue bags contain no other bags.
dotted black bags contain no other bags.
END
subtest part1 => sub {
is part1::solution(split "\n", $sample) => 4;
is part1::solution(path('input')->lines) => 278;
};
subtest part2 => sub {
is part2::solution( sample2() ) => 126, 'example';
is part2::solution(path('input')->lines) => 45157, 'solution';
};
done_testing();
sub sample2 {
return split "\n", <<'END';
shiny gold bags contain 2 dark red bags.
dark red bags contain 2 dark orange bags.
dark orange bags contain 2 dark yellow bags.
dark yellow bags contain 2 dark green bags.
dark green bags contain 2 dark blue bags.
dark blue bags contain 2 dark violet bags.
dark violet bags contain no other bags.
END
}

43
2020/08/part1.pm Normal file
View File

@ -0,0 +1,43 @@
package part1;
use 5.20.0;
use warnings;
use experimental qw/
signatures
postderef
switch
/;
sub parse_code($lines) {
return map { [ split ] }
split "\n", $lines
}
sub run_once($index,$accum,$code) {
my( $op, $val ) = $code->[$index]->@*;
given( $op ) {
when( 'nop' ) { $index++ }
when( 'acc' ) { $index++; $accum += $val; }
when( 'jmp' ) { $index += $val; }
}
return ( $index, $accum );
}
sub solution($lines) {
my @code = parse_code($lines);
my $index = 0;
my $accum = 0;
my %seen;
until( $seen{$index}++ ) {
($index,$accum) = run_once($index,$accum,\@code);
}
return $accum;
}
1;

44
2020/08/part2.pm Normal file
View File

@ -0,0 +1,44 @@
package part2;
use 5.20.0;
use warnings;
require './part1.pm';
use experimental qw/
signatures
postderef
switch
/;
sub run_instance(@code) {
my $index = 0;
my $accum = 0;
my %seen;
until( $seen{$index}++ ) {
($index,$accum) = part1::run_once($index,$accum,\@code);
return $accum if $index >= @code;
}
return undef;
}
sub solution($lines) {
my @code = part1::parse_code($lines);
for(0..$#code) {
next if $code[$_][0] eq 'acc';
my @new_code = @code;
$new_code[$_] = [ $code[$_]->@* ];
$new_code[$_][0] = $new_code[$_][0] eq 'nop' ? 'jmp' : 'nop';
my $result = run_instance(@new_code);
return $result if $result;
}
}
1;

25
2020/08/tests.t Normal file
View File

@ -0,0 +1,25 @@
use Test2::V0;
use Path::Tiny;
require './part1.pm';
require './part2.pm';
subtest part1 => sub {
is part1::solution(<<END) => 5, 'sample';
nop +0
acc +1
jmp +4
acc +3
jmp -3
acc -99
acc +1
jmp -4
acc +6
END
is part1::solution( path('input')->slurp ) => 1262, 'part1';
};
is part2::solution( path('input')->slurp ) => 1643, 'part2';
done_testing();

37
2020/09/part1.pm Normal file
View File

@ -0,0 +1,37 @@
package part1;
use 5.20.0;
use warnings;
use experimental qw/
signatures
postderef
/;
sub solution($n, $entries) {
my @numbers = split "\n", $entries;
my %seen;
my @previous;
# prime the pump
while( @previous < $n ) {
my $n = shift @numbers;
$seen{$n + $_}++ for @previous;
push @previous, $n;
}
while(my $next = shift @numbers) {
return $next unless $seen{$next};
my $goner = shift @previous;
$seen{$goner + $_}-- for @previous;
$seen{$next + $_}++ for @previous;
push @previous, $next;
}
}
1;

35
2020/09/part2.pm Normal file
View File

@ -0,0 +1,35 @@
package part2;
use 5.20.0;
use warnings;
use experimental qw/
signatures
postderef
/;
use List::AllUtils qw/ minmax sum /;
sub solution($target, $entries) {
my @numbers = split "\n", $entries;
my $sum = 0;
my @seq;
while( @numbers ) {
while( $sum < $target ) {
push @seq, shift @numbers;
$sum += $seq[-1];
}
while( $sum > $target ) {
$sum -= shift @seq;
}
if( $sum == $target ) {
return sum minmax @seq;
}
}
}
1;

38
2020/09/tests.t Normal file
View File

@ -0,0 +1,38 @@
use Test2::V0;
use Path::Tiny;
require './part1.pm';
require './part2.pm';
subtest part1 => sub {
is part1::solution( 5, <<'END') => 127;
35
20
15
25
47
40
62
55
65
95
102
117
150
182
127
219
299
277
309
576
END
is part1::solution( 25, path('input')->slurp ) => 26796446;
};
subtest part2 => sub {
is part2::solution( 26796446, path('input')->slurp ) => 3353494;
};
done_testing();

20
2020/10/part1.pm Normal file
View File

@ -0,0 +1,20 @@
package part1;
use 5.20.0;
use warnings;
use List::AllUtils qw/ part /;
use experimental qw/ signatures postderef /;
sub solution(@lines) {
@lines = sort {$a<=>$b}@lines;
unshift @lines, 0;
push @lines, $lines[-1] + 3;
my @diffs = part { $_ } map { $lines[$_] - $lines[$_-1] } 1..$#lines;
return $diffs[1]->@* * $diffs[3]->@*;
}
1;

38
2020/10/part2.pm Normal file
View File

@ -0,0 +1,38 @@
package part2;
use 5.20.0;
use warnings;
use List::AllUtils qw/ /;
require './part1.pm';
use experimental qw/ signatures postderef /;
use Memoize;
memoize('ways');
sub ways($first, @rest) {
return 1 unless @rest;
my $ways = 0;
while( my $next = shift @rest ) {
last if $next - $first > 3;
$ways += ways($next, @rest);
}
return $ways;
}
sub solution(@lines) {
@lines = sort {$a<=>$b}@lines;
unshift @lines, 0;
push @lines, $lines[-1] + 3;
return ways(@lines);
}
1;

31
2020/10/sample2 Normal file
View File

@ -0,0 +1,31 @@
28
33
18
42
31
14
46
20
48
47
24
23
49
45
19
38
39
11
1
32
25
35
8
17
7
9
4
2
34
10
3

20
2020/10/tests.t Normal file
View File

@ -0,0 +1,20 @@
use Test2::V0;
use Path::Tiny;
require './part1.pm';
require './part2.pm';
my @sample2 = path('sample2')->lines;
my @input = path('input')->lines;
subtest part1 => sub {
is part1::solution(@sample2) => 220, 'sample2';
is part1::solution( @input) => 1700;
};
subtest part2 => sub {
is part2::solution(@input ) => 12401793332096;
};
done_testing();

67
2020/11/part1.pm Normal file
View File

@ -0,0 +1,67 @@
package P1;
use 5.20.0;
use warnings;
use List::AllUtils qw/ /;
use Moose;
use experimental qw/ signatures postderef /;
sub neighbours ( $self, $i, $j, $grid ) {
my $n = 0;
for my $x ( -1, 0, 1 ) {
for my $y ( -1, 0, 1 ) {
next unless $x or $y;
my ( $c, $d ) = ( $i + $x, $j + $y );
next if $c < 0 or $d < 0;
no warnings;
$n += $grid->[$c][$d] eq '#';
}
}
return $n;
}
sub iterate ( $self, $room ) {
my @grid = map { [ split '' ] } split "\n", $room;
my @next = map { [@$_] } @grid;
for my $i ( 0 .. $#grid ) {
my $row = $grid[$i];
for my $j ( 0 .. $row->$#* ) {
if ( $row->[$j] eq 'L' ) {
if ( not $self->neighbours( $i, $j, \@grid ) ) {
$next[$i][$j] = '#';
}
}
elsif ( $row->[$j] eq '#' ) {
if ( $self->neighbours( $i, $j, \@grid ) >= 4 ) {
$next[$i][$j] = 'L';
}
}
}
}
return join "\n", map { join '', @$_ } @next;
}
sub solution ( $self, $room ) {
my $previous;
no warnings;
while ( $room ne $previous ) {
( $previous, $room ) = ( $room, $self->iterate($room) );
}
my @m = $room =~ /(#)/g;
return scalar @m;
}
1;

71
2020/11/part2.pm Normal file
View File

@ -0,0 +1,71 @@
package P2;
use 5.20.0;
use warnings;
use List::AllUtils qw/ /;
use Math::Vector::Real;
require './part1.pm';
use Moose;
extends 'P1';
use experimental qw/ signatures postderef /;
sub neighbours ( $self, $i, $j, $grid ) {
my $n = 0;
for my $x ( -1, 0, 1 ) {
for my $y ( -1, 0, 1 ) {
next unless $x or $y;
my $delta = V( $x, $y );
my $c = V( $i, $j );
while () {
$c += $delta;
last if grep { $_ < 0 } @$c;
my $x = $grid->[ $c->[0] ][ $c->[1] ];
last if !$x or $x eq 'L';
next if $x eq '.';
$n++;
last;
}
}
}
return $n;
}
sub iterate ( $self, $room ) {
my @grid = map { [ split '' ] } split "\n", $room;
my @next = map { [@$_] } @grid;
for my $i ( 0 .. $#grid ) {
my $row = $grid[$i];
for my $j ( 0 .. $row->$#* ) {
if ( $row->[$j] eq 'L' ) {
if ( not $self->neighbours( $i, $j, \@grid ) ) {
$next[$i][$j] = '#';
}
}
elsif ( $row->[$j] eq '#' ) {
if ( $self->neighbours( $i, $j, \@grid ) >= 5 ) {
$next[$i][$j] = 'L';
}
}
}
}
return join "\n", map { join '', @$_ } @next;
}
1;

10
2020/11/sample Normal file
View File

@ -0,0 +1,10 @@
#.##.##.##
#######.##
#.#.#..#..
####.##.##
#.##.##.##
#.#####.##
..#.#.....
##########
#.######.#
#.#####.##

20
2020/11/tests.t Normal file
View File

@ -0,0 +1,20 @@
use Test2::V0;
use Path::Tiny;
require './part1.pm';
require './part2.pm';
my $sample = path('sample')->slurp;
my $input = path('input')->slurp;
subtest part1 => sub {
is P1->new->solution( $sample ) => 37;
is P1->new->solution( $input ) => 2427;
};
subtest part2 => sub {
is P2->new->solution( $sample ) => 26;
is P2->new->solution( $input ) => 2199;
};
done_testing();

66
2020/12/part1.pm Normal file
View File

@ -0,0 +1,66 @@
package P1;
use 5.20.0;
use warnings;
use List::AllUtils qw/ /;
use Math::Vector::Real;
use Moose;
use MooseX::MungeHas 'is_rw';
use experimental qw/ signatures postderef switch /;
has coords => sub {
return V(0,0);
};
has facing => sub {
1;
};
my @delta = (
V(0,1),
V(1,0),
V(0,-1),
V(-1,0),
);
sub delta {
return \@delta;
}
sub turn($self,$n) {
my $d = ($self->facing + $n/90)%4;
$self->facing($d);
}
sub move($self,$m) {
$m =~ /(.)(\d+)/;
my ( $x, $c ) = ($1,$2);
my $coords = $self->coords;
given($x) {
$coords += $c * $delta[$self->facing] when 'F';
$coords += $c * $delta[0] when 'N';
$coords += $c * $delta[1] when 'E';
$coords += $c * $delta[2] when 'S';
$coords += $c * $delta[3] when 'W';
$self->turn($c) when 'R';
$self->turn(-$c) when 'L';
}
$self->coords($coords);
}
sub solution($class,@instructions) {
my $self = $class->new;
$self->move($_) for @instructions;
$self->coords->manhattan_dist(V(0,0));
}
1;

57
2020/12/part2.pm Normal file
View File

@ -0,0 +1,57 @@
package P2;
use 5.20.0;
use warnings;
use List::AllUtils qw/ /;
use Math::Vector::Real;
require './part1.pm';
use Moose;
use MooseX::MungeHas 'is_rw';
extends 'P1';
use experimental qw/ signatures postderef /;
has +facing => sub { V(10,1) };
sub turn($self,$n) {
my $f = $self->facing;
my $d = ($n/90)%4;
given( $d ) {
return V($f->[1], -$f->[0]) when 1;
return V(-$f->[0], -$f->[1]) when 2;
return V(-$f->[1], $f->[0]) when 3;
}
return $f;
}
sub move($self,$m) {
$m =~ /(.)(\d+)/;
my ( $x, $c ) = ($1,$2);
my $coords = $self->coords;
my $facing = $self->facing;
given($x) {
$coords += $c * $self->facing when 'F';
$facing += $c * $self->delta->[0] when 'N';
$facing += $c * $self->delta->[1] when 'E';
$facing += $c * $self->delta->[2] when 'S';
$facing += $c * $self->delta->[3] when 'W';
$facing = $self->turn($c) when 'R';
$facing = $self->turn(-$c) when 'L';
}
$self->facing($facing);
$self->coords($coords);
warn $self->facing;
warn $self->coords;
}
1;

24
2020/12/tests.t Normal file
View File

@ -0,0 +1,24 @@
use Test2::V0;
use Path::Tiny;
require './part1.pm';
require './part2.pm';
my @input = path('input')->lines;
0 and subtest part1 => sub {
is P1->solution(@input) => 1221;
};
subtest part2 => sub {
is P2->solution(qw/
F10
N3
F7
R90
F11 /) => 286, 'example';
is P2->solution(@input ) => 59435;
};
done_testing();

22
2020/13/part1.pm Normal file
View File

@ -0,0 +1,22 @@
package part1;
use 5.20.0;
use warnings;
use List::AllUtils qw/ min /;
use experimental qw/ signatures postderef /;
sub solution($timestamp,@entries) {
@entries = grep { $_ ne 'x' } @entries;
my %sched = map {
(int($timestamp / $_)+1 )* $_ => $_
} @entries;
my $k = min keys %sched;
$sched{ $k } * ( $k - $timestamp ) ;
}
1;

54
2020/13/part2.pm Normal file
View File

@ -0,0 +1,54 @@
package part2;
use 5.20.0;
use warnings;
use List::AllUtils qw/ min max /;
use POSIX qw/ ceil /;
require './part1.pm';
use experimental qw/ signatures postderef /;
sub find($t, $next=undef, @buses) {
return 0 unless $next;
if( $next eq 'x' ) {
return find($t+1, @buses );
}
my $n = $t / $next;
my $nint = int $n;
if( $nint == $n ) {
return find( $t+1, @buses );
}
return (1+$nint)*$next;
}
sub solution(@buses) {
@buses = map { $_ eq 'x'?1:$_
} @buses;
my $t = 1;
T: while() {
my $i = 0;
my @min = map {
my $s = $t + $i++;
$_ * ceil($s/$_);
} @buses;
warn join " ", @min;
for(0..$#min-1) {
if( $min[$_] != $min[$_+1]-1 ) {
$t = max map { $min[$_] - $_ } 0..$#min;
next T;
}
}
return $t;
}
}
1;

18
2020/13/tests.t Normal file
View File

@ -0,0 +1,18 @@
use Test2::V0;
use Path::Tiny;
require './part1.pm';
require './part2.pm';
my @input = path('input')->lines;
subtest part1 => sub {
is part1::solution( $input[0], split ',', $input[1] ) => 171;
};
subtest part2 => sub {
is part2::solution( 17, 'x', 13, 19 ) => 3417;
# is part2::solution( split ',', $input[1] ) => 171;
};
done_testing();

31
2020/14/part1.pm Normal file
View File

@ -0,0 +1,31 @@
package part1;
use 5.20.0;
use warnings;
use List::AllUtils qw/ zip pairmap reduce sum/;
use experimental qw/ signatures postderef /;
sub solution(@lines) {
my @mask;
my @mem;
while( my $line = shift @lines ) {
if( $line =~ /mask = (.*)/ ) {
@mask = split '', $1;
next;
}
$line =~ /mem\[(\d+)\] = (\d+)/ or die;
my @num = split '', sprintf "%036b", $2;
$mem[$1] = reduce { 2*$a + $b } pairmap { $a eq 'X' ? $b : $a } zip @mask, @num;
}
no warnings;
return sum @mem;
}
1;

44
2020/14/part2.pm Normal file
View File

@ -0,0 +1,44 @@
package part2;
use 5.20.0;
use warnings;
use List::AllUtils qw/ zip pairmap sum reduce/;
require './part1.pm';
use experimental qw/ signatures postderef /;
sub solution(@lines) {
my @mask;
my %mem;
while( my $line = shift @lines ) {
warn $line;
if( $line =~ /mask = (.*)/ ) {
@mask = split '', $1;
next;
}
$line =~ /mem\[(\d+)\] = (\d+)/ or die;
my $value = $2;
my @num = split '', sprintf "%036b", $1;
my $l = join '', pairmap { $a || $b } zip @mask, @num;
my $num = $l =~ s/X/%d/g;
for( 0..(2**$num)-1 ) {
my $address = reduce {
2*$a + $b
} split '', sprintf $l, split '', sprintf "%0${num}b", $_;
$mem{$address} = $value;
}
}
no warnings;
return sum values %mem;
}
1;

4
2020/14/sample1 Normal file
View File

@ -0,0 +1,4 @@
mask = XXXXXXXXXXXXXXXXXXXXXXXXXXXXX1XXXX0X
mem[8] = 11
mem[7] = 101
mem[8] = 0

4
2020/14/sample2 Normal file
View File

@ -0,0 +1,4 @@
mask = 000000000000000000000000000000X1001X
mem[42] = 100
mask = 00000000000000000000000000000000X0XX
mem[26] = 1

22
2020/14/tests.t Normal file
View File

@ -0,0 +1,22 @@
use Test2::V0;
use Path::Tiny;
require './part1.pm';
require './part2.pm';
my @sample1 = path('sample1')->lines;
my @input = path('input')->lines;
subtest part1 => sub {
skip_all;
is part1::solution(@sample1) => 165;
is part1::solution(@input) => 14954914379452;
};
subtest part2 => sub {
my @sample2 = path('sample2')->lines;
is part2::solution(@sample2) => 208;
is part2::solution(@input) => 3415488160714;
};
done_testing();

41
2020/15/part1.pm Normal file
View File

@ -0,0 +1,41 @@
package part1;
use 5.20.0;
use warnings;
use List::AllUtils qw/ /;
use experimental qw/ signatures postderef /;
sub solution(@seeds) {
my $last = shift @seeds;
my $turn = 1;
my %said;
for(@seeds) {
$said{$last} ||= [];
push $said{$last}->@*, $turn++;
$last = $_;
}
until( $turn == 2020 ) {
if( !$said{$last} ) {
$said{$last} = [$turn];
$last = 0
} else {
push $said{$last}->@*, $turn;
if( $said{$last}->@* > 2 ) {
shift $said{$last}->@*;
}
$last = $said{$last}[-1] - $said{$last}[-2];
}
$turn++;
}
return $last;
}
1;

44
2020/15/part2.pm Normal file
View File

@ -0,0 +1,44 @@
package part2;
use 5.20.0;
use warnings;
use List::AllUtils qw/ /;
require './part1.pm';
use experimental qw/ signatures postderef /;
sub solution(@seeds) {
my $last = shift @seeds;
my $turn = 1;
my %said;
for(@seeds) {
$said{$last} ||= [];
push $said{$last}->@*, $turn++;
$last = $_;
}
until( $turn == 30000000 ) {
if( !$said{$last} ) {
$said{$last} = [$turn];
$last = 0
} else {
push $said{$last}->@*, $turn;
if( $said{$last}->@* > 2 ) {
shift $said{$last}->@*;
}
$last = $said{$last}[-1] - $said{$last}[-2];
}
$turn++;
}
return $last;
}
1;

16
2020/15/tests.t Normal file
View File

@ -0,0 +1,16 @@
use Test2::V0;
use Path::Tiny;
require './part1.pm';
require './part2.pm';
subtest part1 => sub {
is part1::solution(0,3,6) => 436;
is part1::solution(0,13,1,16,6,17) => 234;
};
subtest part2 => sub {
is part2::solution(0,13,1,16,6,17) => 8984;
};
done_testing();

27
2020/16/part1.pm Normal file
View File

@ -0,0 +1,27 @@
package part1;
use 5.20.0;
use warnings;
use List::AllUtils qw/ sum /;
use Range::Merge qw/ merge /;
use experimental qw/ signatures postderef /;
sub numbers_out_of_range($input) {
my $ranges = merge([ map { [ split '-' ] } $input =~ /(\d+-\d+)/g]);
$input =~ /nearby tickets:(.*)/s;
my @tickets = $1 =~ /(\d+)/g;
return grep {
not( $_ >= $ranges->[0][0] and $_ <= $ranges->[0][1])
} @tickets;
}
sub solution($input) {
return sum numbers_out_of_range($input);
}
1;

87
2020/16/part2.pm Normal file
View File

@ -0,0 +1,87 @@
package part2;
use 5.20.0;
use warnings;
use List::AllUtils qw/ pairmap zip product /;
use Range::Merge qw/ merge /;
use Set::Object qw/ set /;
require './part1.pm';
use experimental qw/ signatures postderef /;
sub inRanges($n, $ranges) {
for (@$ranges) {
return 1 if $_->[0] <= $n and $_->[1] >= $n;
}
return 0;
}
sub possibilities($n,$fields) {
return set(
grep { inRanges($n, $fields->{$_})} keys %$fields
)
}
sub solution($input) {
my ( $fields, $myticket, $tickets ) = split "\n\n", $input;
my %fields = map {
my ( $name, $ranges) = split ':';
$name => merge([ map { [ split '-'] } $ranges =~ /(\d+-\d+)/g ])
} split "\n", $fields;
my @global;
for my $ticket ( split "\n", $tickets ) {
next if $ticket =~ /nearby/;
my @fields = split ',', $ticket;
@fields = map { possibilities($_,\%fields) } @fields;
next if grep { not $_->size } @fields;
if(!@global) {
@global = @fields;
}
else {
@global = pairmap { $a*$b } zip @global, @fields;
}
}
use DDP; say join " ", @$_ for @global;
my %indexes;
LOOP: while() {
for( 0..$#global ) {
if( $global[$_]->size == 1 ) {
my $e = $global[$_];
$indexes{$global[$_][0]} = $_;
@global = map { $_ - $e } @global;
redo LOOP;
}
}
last LOOP;
}
p %indexes;
my @myt = $myticket =~ /(\d+)/g;
return product
@myt[
map { $indexes{$_} }
grep { /departure/ } keys %indexes ];
}
1;

11
2020/16/sample Normal file
View File

@ -0,0 +1,11 @@
class: 0-1 or 4-19
row: 0-5 or 8-19
seat: 0-13 or 16-19
your ticket:
11,12,13
nearby tickets:
3,9,18
15,1,5
5,14,9

17
2020/16/tests.t Normal file
View File

@ -0,0 +1,17 @@
use Test2::V0;
use Path::Tiny;
require './part1.pm';
require './part2.pm';
my $input = path('input')->slurp;
subtest part1 => sub {
is part1::solution($input) => 20048;
};
subtest part2 => sub {
is part2::solution($input) => 4810284647569;
};
done_testing();

13
2020/template/part1.pm Normal file
View File

@ -0,0 +1,13 @@
package part1;
use 5.20.0;
use warnings;
use List::AllUtils qw/ /;
use experimental qw/ signatures postderef /;
sub solution() {
}
1;

15
2020/template/part2.pm Normal file
View File

@ -0,0 +1,15 @@
package part2;
use 5.20.0;
use warnings;
use List::AllUtils qw/ /;
require './part1.pm';
use experimental qw/ signatures postderef /;
sub solution() {
}
1;

15
2020/template/tests.t Normal file
View File

@ -0,0 +1,15 @@
use Test2::V0;
use Path::Tiny;
require './part1.pm';
require './part2.pm';
subtest part1 => sub {
is part1::solution( ) => 'TODO';
};
subtest part2 => sub {
is part2::solution( ) => 'TODO';
};
done_testing();