diff --git a/2023/.gitignore b/2023/.gitignore new file mode 100644 index 0000000..751553b --- /dev/null +++ b/2023/.gitignore @@ -0,0 +1 @@ +*.bak diff --git a/2023/06/Part1.pm b/2023/06/Part1.pm new file mode 100644 index 0000000..48fd67a --- /dev/null +++ b/2023/06/Part1.pm @@ -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; diff --git a/2023/06/Part2.pm b/2023/06/Part2.pm new file mode 100644 index 0000000..4147085 --- /dev/null +++ b/2023/06/Part2.pm @@ -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; diff --git a/2023/06/benchmark.pl b/2023/06/benchmark.pl new file mode 100644 index 0000000..9ad9e63 --- /dev/null +++ b/2023/06/benchmark.pl @@ -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; +} + diff --git a/2023/06/example b/2023/06/example new file mode 100644 index 0000000..28f5ae9 --- /dev/null +++ b/2023/06/example @@ -0,0 +1,2 @@ +Time: 7 15 30 +Distance: 9 40 200 diff --git a/2023/06/part1.t b/2023/06/part1.t new file mode 100644 index 0000000..5a77e5a --- /dev/null +++ b/2023/06/part1.t @@ -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; diff --git a/2023/06/part2.t b/2023/06/part2.t new file mode 100644 index 0000000..d5c7d4a --- /dev/null +++ b/2023/06/part2.t @@ -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; diff --git a/2023/06/solutions.yml b/2023/06/solutions.yml new file mode 100644 index 0000000..3ba62a0 --- /dev/null +++ b/2023/06/solutions.yml @@ -0,0 +1,2 @@ +1: 211904 +2: 43364472 diff --git a/2023/07/Part1.pm b/2023/07/Part1.pm new file mode 100644 index 0000000..31fd1b3 --- /dev/null +++ b/2023/07/Part1.pm @@ -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; diff --git a/2023/07/Part2.pm b/2023/07/Part2.pm new file mode 100644 index 0000000..99c8594 --- /dev/null +++ b/2023/07/Part2.pm @@ -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; diff --git a/2023/07/benchmark.pl b/2023/07/benchmark.pl new file mode 100644 index 0000000..6154f18 --- /dev/null +++ b/2023/07/benchmark.pl @@ -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; +} + diff --git a/2023/07/example b/2023/07/example new file mode 100644 index 0000000..e3500c3 --- /dev/null +++ b/2023/07/example @@ -0,0 +1,5 @@ +32T3K 765 +T55J5 684 +KK677 28 +KTJJT 220 +QQQJA 483 diff --git a/2023/07/part1.t b/2023/07/part1.t new file mode 100644 index 0000000..af95d38 --- /dev/null +++ b/2023/07/part1.t @@ -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; diff --git a/2023/07/part2.t b/2023/07/part2.t new file mode 100644 index 0000000..65499be --- /dev/null +++ b/2023/07/part2.t @@ -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; diff --git a/2023/07/solutions.yml b/2023/07/solutions.yml new file mode 100644 index 0000000..513f041 --- /dev/null +++ b/2023/07/solutions.yml @@ -0,0 +1,2 @@ +1: 250058342 +2: 250506580 diff --git a/2023/benchmark.json b/2023/benchmark.json index c3bb363..b264505 100644 --- a/2023/benchmark.json +++ b/2023/benchmark.json @@ -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} {"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"} +{"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"} diff --git a/2023/preset/day/templates/perl/benchmark.pl b/2023/preset/day/templates/perl/benchmark.pl index fcf3e70..36ab17c 100644 --- a/2023/preset/day/templates/perl/benchmark.pl +++ b/2023/preset/day/templates/perl/benchmark.pl @@ -4,16 +4,18 @@ 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 => 'TODO' }, - { part => 2, sub => \&Part2::solution_2, expected => 'TODO' }, + { part => 1, sub => \&Part1::solution_1, expected => $solutions->{1} }, + { part => 2, sub => \&Part2::solution_2, expected => $solutions->{2} }, ); my $input = path('./input')->slurp;