2020
This commit is contained in:
parent
6c0551059f
commit
8011c14aeb
13
2020/01/part1.pl
Normal file
13
2020/01/part1.pl
Normal 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
30
2020/01/part2.pl
Normal 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
22
2020/02/part1.pl
Normal 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
20
2020/02/part2.pl
Normal 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
24
2020/03/part1.pl
Normal 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
46
2020/03/part2.pl
Normal 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
11
2020/03/test
Normal file
@ -0,0 +1,11 @@
|
||||
..##.......
|
||||
#...#...#..
|
||||
.#....#..#.
|
||||
..#.#...#.#
|
||||
.#...##..#.
|
||||
..#.##.....
|
||||
.#.#.#....#
|
||||
.#........#
|
||||
#.##...#...
|
||||
#...##....#
|
||||
.#..#...#.#
|
25
2020/04/part1.pm
Normal file
25
2020/04/part1.pm
Normal 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
47
2020/04/part2.pm
Normal 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
13
2020/04/test
Normal 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
11
2020/04/tests.t
Normal 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
19
2020/04/tests2.t
Normal 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
34
2020/05/part1.pm
Normal 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
26
2020/05/part2.pm
Normal 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
21
2020/05/tests.t
Normal 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
21
2020/06/part1.pm
Normal 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
18
2020/06/part2.pm
Normal 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
11
2020/06/tests.t
Normal 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
42
2020/07/part1.pm
Normal 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
29
2020/07/part2.pl
Normal 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
45
2020/07/tests.t
Normal 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
43
2020/08/part1.pm
Normal 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
44
2020/08/part2.pm
Normal 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
25
2020/08/tests.t
Normal 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
37
2020/09/part1.pm
Normal 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
35
2020/09/part2.pm
Normal 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
38
2020/09/tests.t
Normal 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
20
2020/10/part1.pm
Normal 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
38
2020/10/part2.pm
Normal 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
31
2020/10/sample2
Normal 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
20
2020/10/tests.t
Normal 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
67
2020/11/part1.pm
Normal 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
71
2020/11/part2.pm
Normal 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
10
2020/11/sample
Normal file
@ -0,0 +1,10 @@
|
||||
#.##.##.##
|
||||
#######.##
|
||||
#.#.#..#..
|
||||
####.##.##
|
||||
#.##.##.##
|
||||
#.#####.##
|
||||
..#.#.....
|
||||
##########
|
||||
#.######.#
|
||||
#.#####.##
|
20
2020/11/tests.t
Normal file
20
2020/11/tests.t
Normal 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
66
2020/12/part1.pm
Normal 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
57
2020/12/part2.pm
Normal 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
24
2020/12/tests.t
Normal 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
22
2020/13/part1.pm
Normal 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
54
2020/13/part2.pm
Normal 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
18
2020/13/tests.t
Normal 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
31
2020/14/part1.pm
Normal 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
44
2020/14/part2.pm
Normal 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;
|