Merge branch '2023-07'
This commit is contained in:
commit
18eb1ef0dc
1
2023/.gitignore
vendored
Normal file
1
2023/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
*.bak
|
30
2023/06/Part1.pm
Normal file
30
2023/06/Part1.pm
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
use 5.38.0;
|
||||||
|
|
||||||
|
package Part1;
|
||||||
|
|
||||||
|
use List::AllUtils qw/ product /;
|
||||||
|
use POSIX qw/ floor ceil /;
|
||||||
|
|
||||||
|
sub my_zip($a,$b) {
|
||||||
|
return map { [ $a->[$_], $b->[$_] ] } 0..$a->@*-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub parse_file($input) {
|
||||||
|
my_zip( map { [/(\d+)/g ] } split "\n", $input );
|
||||||
|
}
|
||||||
|
|
||||||
|
sub race_solution($time,$distance) {
|
||||||
|
my $min = ( $time - sqrt( $time**2 - 4 * $distance) ) / 2;
|
||||||
|
my $max = ( $time + sqrt( $time**2 - 4 * $distance) ) / 2;
|
||||||
|
|
||||||
|
$min = int($min)+1;
|
||||||
|
$max = ($max == int $max)?$max-1:int $max;
|
||||||
|
|
||||||
|
return $max-$min+1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub solution_1 ($input) {
|
||||||
|
product map { race_solution(@$_)} parse_file($input);
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
17
2023/06/Part2.pm
Normal file
17
2023/06/Part2.pm
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
use 5.38.0;
|
||||||
|
|
||||||
|
package Part2;
|
||||||
|
|
||||||
|
use Part1;
|
||||||
|
|
||||||
|
use List::AllUtils qw/ product /;
|
||||||
|
|
||||||
|
sub parse_file($input) {
|
||||||
|
map { join '', /(\d+)/g } split "\n", $input;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub solution_2 ($input) {
|
||||||
|
Part1::race_solution(parse_file($input));
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
45
2023/06/benchmark.pl
Normal file
45
2023/06/benchmark.pl
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
use 5.38.0;
|
||||||
|
|
||||||
|
use Benchmark ':hireswallclock';
|
||||||
|
use Path::Tiny;
|
||||||
|
use JSON qw/ to_json /;
|
||||||
|
use DateTime;
|
||||||
|
use File::Serialize;
|
||||||
|
|
||||||
|
use Part1;
|
||||||
|
use Part2;
|
||||||
|
|
||||||
|
my $day = path('.')->absolute->basename =~ s/^0//r;
|
||||||
|
my $year = path('.')->absolute->parent->basename;
|
||||||
|
|
||||||
|
my $solutions = deserialize_file('solutions.yml');
|
||||||
|
|
||||||
|
my @parts = (
|
||||||
|
{ part => 1, sub => \&Part1::solution_1, expected => $solutions->{1} },
|
||||||
|
{ part => 2, sub => \&Part2::solution_2, expected => $solutions->{2} },
|
||||||
|
);
|
||||||
|
|
||||||
|
my $input = path('./input')->slurp;
|
||||||
|
|
||||||
|
for my $part (@parts) {
|
||||||
|
my $res = Benchmark::countit(
|
||||||
|
10,
|
||||||
|
sub {
|
||||||
|
$part->{sub}->($input) == $part->{expected} or die;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
my $result = {
|
||||||
|
day => $day,
|
||||||
|
year => $year,
|
||||||
|
|
||||||
|
#variant => '',
|
||||||
|
language => 'perl',
|
||||||
|
part => $part->{part},
|
||||||
|
time => $res->cpu_a / $res->iters,
|
||||||
|
persec => $res->iters/$res->cpu_a,
|
||||||
|
timestamp => DateTime->now->iso8601,
|
||||||
|
};
|
||||||
|
say to_json $result;
|
||||||
|
}
|
||||||
|
|
2
2023/06/example
Normal file
2
2023/06/example
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
Time: 7 15 30
|
||||||
|
Distance: 9 40 200
|
22
2023/06/part1.t
Normal file
22
2023/06/part1.t
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
use 5.38.0;
|
||||||
|
|
||||||
|
use Test2::V0;
|
||||||
|
|
||||||
|
use Path::Tiny;
|
||||||
|
use File::Serialize;
|
||||||
|
|
||||||
|
use Part1;
|
||||||
|
|
||||||
|
my $input = path('input')->slurp;
|
||||||
|
my $example = path('example')->slurp;
|
||||||
|
my $solutions = deserialize_file('solutions.yml');
|
||||||
|
|
||||||
|
is [ map { Part1::race_solution(@$_)} Part1::parse_file($example)]
|
||||||
|
=> [4,8,9];
|
||||||
|
|
||||||
|
is Part1::solution_1($example) => 288;
|
||||||
|
|
||||||
|
cmp_ok Part1::solution_1($input), '<' ,230202;
|
||||||
|
is Part1::solution_1($input) => $solutions->{1};
|
||||||
|
|
||||||
|
done_testing;
|
17
2023/06/part2.t
Normal file
17
2023/06/part2.t
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
use 5.38.0;
|
||||||
|
|
||||||
|
use Test2::V0;
|
||||||
|
|
||||||
|
use Path::Tiny;
|
||||||
|
use File::Serialize;
|
||||||
|
|
||||||
|
use Part2;
|
||||||
|
|
||||||
|
my $input = path('input')->slurp;
|
||||||
|
my $example = path('example')->slurp;
|
||||||
|
my $solutions = deserialize_file('solutions.yml');
|
||||||
|
|
||||||
|
is Part2::solution_2($example) => 71503;
|
||||||
|
is Part2::solution_2($input) => $solutions->{2};
|
||||||
|
|
||||||
|
done_testing;
|
2
2023/06/solutions.yml
Normal file
2
2023/06/solutions.yml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
1: 211904
|
||||||
|
2: 43364472
|
60
2023/07/Part1.pm
Normal file
60
2023/07/Part1.pm
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
use 5.38.0;
|
||||||
|
|
||||||
|
package Part1;
|
||||||
|
|
||||||
|
use List::AllUtils qw/ pairmap sum /;
|
||||||
|
use List::UtilsBy qw/ sort_by /;
|
||||||
|
|
||||||
|
sub rank_hand (@hand) {
|
||||||
|
@hand = split '', shift @hand if @hand == 1;
|
||||||
|
|
||||||
|
my %group;
|
||||||
|
$group{$_}++ for @hand;
|
||||||
|
|
||||||
|
return 7 if 1 == keys %group;
|
||||||
|
|
||||||
|
return 6 if grep { $_ == 4 } values %group;
|
||||||
|
|
||||||
|
if( grep { $_ == 3 } values %group ) {
|
||||||
|
return 5 if keys %group == 2;
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 3 if keys %group == 3;
|
||||||
|
|
||||||
|
return 2 if keys %group == 4;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub handify ($str) {
|
||||||
|
state %map = (
|
||||||
|
'T' => 10,
|
||||||
|
'J' => 11,
|
||||||
|
'Q' => 12,
|
||||||
|
'K' => 13,
|
||||||
|
A => 14
|
||||||
|
);
|
||||||
|
|
||||||
|
return [
|
||||||
|
map { chr( ord('a') + $_ - 2 ) } map { $map{$_} // $_ } split '', $str
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
sub parse_input ($input) {
|
||||||
|
return pairmap {
|
||||||
|
[ handify($a), $b ]
|
||||||
|
}
|
||||||
|
map { split " " } split "\n", $input;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub score (@hand) {
|
||||||
|
join '', rank_hand(@hand), @hand;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub solution_1 ($input) {
|
||||||
|
my $rank = 0;
|
||||||
|
return sum map { ++$rank * $_->[1] } sort_by { score( $_->[0]->@* ) } parse_input($input);
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
70
2023/07/Part2.pm
Normal file
70
2023/07/Part2.pm
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
use 5.38.0;
|
||||||
|
|
||||||
|
package Part2;
|
||||||
|
|
||||||
|
use Part1;
|
||||||
|
|
||||||
|
use List::AllUtils qw/ pairmap sum max /;
|
||||||
|
use List::UtilsBy qw/ sort_by /;
|
||||||
|
|
||||||
|
sub rank_hand (@hand) {
|
||||||
|
@hand = split '', shift @hand if @hand == 1;
|
||||||
|
|
||||||
|
my %group;
|
||||||
|
$group{$_}++ for @hand;
|
||||||
|
|
||||||
|
my $jokers = delete $group{'`'} // 0;
|
||||||
|
|
||||||
|
return 7 if 1 >= keys %group;
|
||||||
|
|
||||||
|
return 6 if grep { $_ + $jokers >= 4 } values %group;
|
||||||
|
|
||||||
|
my $max = max values %group;
|
||||||
|
|
||||||
|
if ( $max + $jokers >= 3 ) {
|
||||||
|
return 5 if keys %group <= 2;
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 3 if keys %group == 3;
|
||||||
|
|
||||||
|
return 2 if keys %group == 4;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub handify ($str) {
|
||||||
|
state %map = (
|
||||||
|
'T' => 10,
|
||||||
|
'J' => 1,
|
||||||
|
'Q' => 12,
|
||||||
|
'K' => 13,
|
||||||
|
A => 14
|
||||||
|
);
|
||||||
|
|
||||||
|
my @cards =
|
||||||
|
map { chr( ord('a') + $_ - 2 ) } map { $map{$_} // $_ } split '', $str;
|
||||||
|
|
||||||
|
return \@cards;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub parse_input ($input) {
|
||||||
|
my @lines = split "\n", $input;
|
||||||
|
my @hand_score = pairmap {
|
||||||
|
[ handify($a), $b ]
|
||||||
|
}
|
||||||
|
map { split " " } @lines;
|
||||||
|
return @hand_score;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub score (@hand) {
|
||||||
|
join '', rank_hand(@hand), @hand;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub solution_2 ($input) {
|
||||||
|
my @hand_score = sort_by { score( $_->[0]->@* ) } parse_input($input);
|
||||||
|
my $rank = 0;
|
||||||
|
return sum map { ++$rank * $_->[1] } @hand_score;
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
44
2023/07/benchmark.pl
Normal file
44
2023/07/benchmark.pl
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
use 5.38.0;
|
||||||
|
|
||||||
|
use Benchmark ':hireswallclock';
|
||||||
|
use Path::Tiny;
|
||||||
|
use JSON qw/ to_json /;
|
||||||
|
use DateTime;
|
||||||
|
use File::Serialize;
|
||||||
|
|
||||||
|
use Part1;
|
||||||
|
use Part2;
|
||||||
|
|
||||||
|
my $day = path('.')->absolute->basename =~ s/^0//r;
|
||||||
|
my $year = path('.')->absolute->parent->basename;
|
||||||
|
my $solutions = deserialize_file('solutions.yml');
|
||||||
|
|
||||||
|
my @parts = (
|
||||||
|
{ part => 1, sub => \&Part1::solution_1, expected => $solutions->{1} },
|
||||||
|
{ part => 2, sub => \&Part2::solution_2, expected => $solutions->{2} },
|
||||||
|
);
|
||||||
|
|
||||||
|
my $input = path('./input')->slurp;
|
||||||
|
|
||||||
|
for my $part (@parts) {
|
||||||
|
my $res = Benchmark::countit(
|
||||||
|
10,
|
||||||
|
sub {
|
||||||
|
$part->{sub}->($input) == $part->{expected} or die;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
my $result = {
|
||||||
|
day => $day,
|
||||||
|
year => $year,
|
||||||
|
|
||||||
|
#variant => '',
|
||||||
|
language => 'perl',
|
||||||
|
part => $part->{part},
|
||||||
|
time => $res->cpu_a / $res->iters,
|
||||||
|
persec => $res->iters / $res->cpu_a,
|
||||||
|
timestamp => DateTime->now->iso8601,
|
||||||
|
};
|
||||||
|
say to_json $result;
|
||||||
|
}
|
||||||
|
|
5
2023/07/example
Normal file
5
2023/07/example
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
32T3K 765
|
||||||
|
T55J5 684
|
||||||
|
KK677 28
|
||||||
|
KTJJT 220
|
||||||
|
QQQJA 483
|
25
2023/07/part1.t
Normal file
25
2023/07/part1.t
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
use 5.38.0;
|
||||||
|
|
||||||
|
use Test2::V0;
|
||||||
|
|
||||||
|
use Path::Tiny;
|
||||||
|
|
||||||
|
use Part1;
|
||||||
|
|
||||||
|
my $input = path('input')->slurp;
|
||||||
|
my $example = path('example')->slurp;
|
||||||
|
|
||||||
|
is Part1::rank_hand('AAAAA') => 7;
|
||||||
|
is Part1::rank_hand('AA8AA') => 6;
|
||||||
|
is Part1::rank_hand('23332') => 5;
|
||||||
|
is Part1::rank_hand('TTT98') => 4;
|
||||||
|
is Part1::rank_hand('23432') => 3;
|
||||||
|
is Part1::rank_hand('A23A4') => 2;
|
||||||
|
is Part1::rank_hand('23456') => 1;
|
||||||
|
|
||||||
|
is [ Part1::parse_input('2345A 123') ] => [ [ [qw/ a b c d m /], 123 ] ];
|
||||||
|
|
||||||
|
is Part1::solution_1($example) => 6440;
|
||||||
|
is Part1::solution_1($input) => 'TODO';
|
||||||
|
|
||||||
|
done_testing;
|
17
2023/07/part2.t
Normal file
17
2023/07/part2.t
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
use 5.38.0;
|
||||||
|
|
||||||
|
use Test2::V0;
|
||||||
|
|
||||||
|
use Path::Tiny;
|
||||||
|
|
||||||
|
use Part2;
|
||||||
|
|
||||||
|
my $input = path('input')->slurp;
|
||||||
|
my $example = path('example')->slurp;
|
||||||
|
|
||||||
|
is Part2::solution_2($example) => 5905;
|
||||||
|
|
||||||
|
cmp_ok Part2::solution_2($input), '<' => 250843163;
|
||||||
|
is Part2::solution_2($input) => 250506580;
|
||||||
|
|
||||||
|
done_testing;
|
2
2023/07/solutions.yml
Normal file
2
2023/07/solutions.yml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
1: 250058342
|
||||||
|
2: 250506580
|
@ -12,3 +12,7 @@
|
|||||||
{"day":4,"year":2023,"language":"javascript","part":"2","timestamp":"2023-12-04T15:22:09.052Z","time":0.002291685911610818,"persec":436.3599719025647}
|
{"day":4,"year":2023,"language":"javascript","part":"2","timestamp":"2023-12-04T15:22:09.052Z","time":0.002291685911610818,"persec":436.3599719025647}
|
||||||
{"timestamp":"2023-12-05T23:24:26","persec":1139.15547024952,"year":"2023","day":"5","part":1,"language":"perl","time":0.000877843302443134}
|
{"timestamp":"2023-12-05T23:24:26","persec":1139.15547024952,"year":"2023","day":"5","part":1,"language":"perl","time":0.000877843302443134}
|
||||||
{"timestamp":"2023-12-05T23:24:38","persec":79.2380952380952,"year":"2023","day":"5","part":2,"time":0.0126201923076923,"language":"perl"}
|
{"timestamp":"2023-12-05T23:24:38","persec":79.2380952380952,"year":"2023","day":"5","part":2,"time":0.0126201923076923,"language":"perl"}
|
||||||
|
{"year":"2023","part":1,"day":"6","language":"perl","time":1.43016642439049e-05,"timestamp":"2023-12-06T15:43:00","persec":69921.9323671498}
|
||||||
|
{"year":"2023","part":2,"language":"perl","day":"6","time":7.07875059716474e-06,"timestamp":"2023-12-06T15:43:13","persec":141267.867298578}
|
||||||
|
{"language":"perl","persec":85.2380952380952,"year":"2023","timestamp":"2023-12-07T16:08:30","time":0.011731843575419,"part":1,"day":"7"}
|
||||||
|
{"day":"7","part":2,"persec":85.2380952380952,"year":"2023","timestamp":"2023-12-07T16:08:43","time":0.011731843575419,"language":"perl"}
|
||||||
|
@ -4,16 +4,18 @@ use Benchmark ':hireswallclock';
|
|||||||
use Path::Tiny;
|
use Path::Tiny;
|
||||||
use JSON qw/ to_json /;
|
use JSON qw/ to_json /;
|
||||||
use DateTime;
|
use DateTime;
|
||||||
|
use File::Serialize;
|
||||||
|
|
||||||
use Part1;
|
use Part1;
|
||||||
use Part2;
|
use Part2;
|
||||||
|
|
||||||
my $day = path('.')->absolute->basename =~ s/^0//r;
|
my $day = path('.')->absolute->basename =~ s/^0//r;
|
||||||
my $year = path('.')->absolute->parent->basename;
|
my $year = path('.')->absolute->parent->basename;
|
||||||
|
my $solutions = deserialize_file('solutions.yml');
|
||||||
|
|
||||||
my @parts = (
|
my @parts = (
|
||||||
{ part => 1, sub => \&Part1::solution_1, expected => 'TODO' },
|
{ part => 1, sub => \&Part1::solution_1, expected => $solutions->{1} },
|
||||||
{ part => 2, sub => \&Part2::solution_2, expected => 'TODO' },
|
{ part => 2, sub => \&Part2::solution_2, expected => $solutions->{2} },
|
||||||
);
|
);
|
||||||
|
|
||||||
my $input = path('./input')->slurp;
|
my $input = path('./input')->slurp;
|
||||||
|
Loading…
Reference in New Issue
Block a user