From 9fb6d13df2aa5d912dbf227d81125d9dc42d4ab2 Mon Sep 17 00:00:00 2001 From: Yanick Champoux Date: Tue, 10 Dec 2024 18:34:16 -0500 Subject: [PATCH] a few more days --- .gitignore | 2 ++ 2024/05/Part1.pm | 41 ++++++++++++++++++++++++ 2024/05/Part2.pm | 63 +++++++++++++++++++++++++++++++++++++ 2024/05/sample | 28 +++++++++++++++++ 2024/05/test.t | 25 +++++++++++++++ 2024/06/Part1.pm | 65 ++++++++++++++++++++++++++++++++++++++ 2024/06/Part2.pm | 74 ++++++++++++++++++++++++++++++++++++++++++++ 2024/06/benchmark.pl | 22 +++++++++++++ 2024/06/sample | 10 ++++++ 2024/06/test.t | 27 ++++++++++++++++ 2024/07/Part1.pm | 29 +++++++++++++++++ 2024/07/Part2.pm | 27 ++++++++++++++++ 2024/07/sample | 9 ++++++ 2024/07/test.t | 27 ++++++++++++++++ 2024/08/Grid.pm | 33 ++++++++++++++++++++ 2024/08/Part1.pm | 49 +++++++++++++++++++++++++++++ 2024/08/Part2.pm | 47 ++++++++++++++++++++++++++++ 2024/08/sample | 12 +++++++ 2024/08/test.t | 25 +++++++++++++++ 2024/09/Part1.pm | 39 +++++++++++++++++++++++ 2024/09/Part2.pm | 71 ++++++++++++++++++++++++++++++++++++++++++ 2024/09/sample | 1 + 2024/09/test.t | 28 +++++++++++++++++ 2024/10/Part1.pm | 58 ++++++++++++++++++++++++++++++++++ 2024/10/Part2.pm | 20 ++++++++++++ 2024/10/Solution.pm | 40 ++++++++++++++++++++++++ 2024/10/sample | 8 +++++ 2024/10/test.t | 22 +++++++++++++ 2024/cpanfile | 2 ++ 2024/deno.json | 8 +++++ perl-lib/AoC/Grid.pm | 56 +++++++++++++++++++++++++++++++++ 31 files changed, 968 insertions(+) create mode 100644 2024/05/Part1.pm create mode 100644 2024/05/Part2.pm create mode 100644 2024/05/sample create mode 100644 2024/05/test.t create mode 100644 2024/06/Part1.pm create mode 100644 2024/06/Part2.pm create mode 100755 2024/06/benchmark.pl create mode 100644 2024/06/sample create mode 100755 2024/06/test.t create mode 100644 2024/07/Part1.pm create mode 100644 2024/07/Part2.pm create mode 100644 2024/07/sample create mode 100644 2024/07/test.t create mode 100644 2024/08/Grid.pm create mode 100644 2024/08/Part1.pm create mode 100644 2024/08/Part2.pm create mode 100644 2024/08/sample create mode 100644 2024/08/test.t create mode 100644 2024/09/Part1.pm create mode 100644 2024/09/Part2.pm create mode 100644 2024/09/sample create mode 100644 2024/09/test.t create mode 100644 2024/10/Part1.pm create mode 100644 2024/10/Part2.pm create mode 100644 2024/10/Solution.pm create mode 100644 2024/10/sample create mode 100644 2024/10/test.t create mode 100644 2024/deno.json create mode 100644 perl-lib/AoC/Grid.pm diff --git a/.gitignore b/.gitignore index 31dc7d0..8fe6caa 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,5 @@ pnpm-lock.yaml *_LOCAL_* *_REMOTE_* *.orig +deno.lock +input diff --git a/2024/05/Part1.pm b/2024/05/Part1.pm new file mode 100644 index 0000000..cf3e8d0 --- /dev/null +++ b/2024/05/Part1.pm @@ -0,0 +1,41 @@ +package Part1; + +use 5.36.0; +use experimental 'declared_refs'; + +no warnings qw/ uninitialized /; + +use List::AllUtils qw/ sum all /; + +sub read_input ($file) { + my( $ordering, $manuals ) = split "\n\n", $file->slurp; + + $ordering = [ map { + [ split '\|' ] + } split "\n", $ordering ]; + + $manuals = [ split "\n", $manuals ]; + + return { ordering => $ordering, manuals => $manuals }; +} + +sub good_manual( $ordering, $manual ) { + return all { + my $m = $_; + my ($i,$j) = map { index $manual, $m->[$_] } 0..1; + return 1 unless all { $_ > -1 } $i, $j; + return $i <= $j; + } @$ordering; +} + +sub solve ($input) { + return sum + map { + my @m = split ','; + $m[@m/2]; + } + grep { good_manual($input->{ordering},$_) } + $input->{manuals}->@*; +} + +1; diff --git a/2024/05/Part2.pm b/2024/05/Part2.pm new file mode 100644 index 0000000..3f02c5b --- /dev/null +++ b/2024/05/Part2.pm @@ -0,0 +1,63 @@ +use lib '.'; + +use Part1; + +package Part2; + +use 5.36.0; +use experimental 'declared_refs'; + +no warnings qw/ uninitialized /; +use List::AllUtils qw/ sum /; + +sub read_input ($file) { + my ( $ordering, $manuals ) = split "\n\n", $file->slurp; + + $ordering = [ map { [ split '\|' ] } split "\n", $ordering ]; + + $manuals = [ split "\n", $manuals ]; + + return { ordering => $ordering, manuals => $manuals }; +} + +use List::AllUtils qw/ first_index all /; + +sub reshuffle_manual ( $ordering, $manual ) { + my @m = split ',', $manual; + + my @ordering = grep { + my @o = @$_; + my $i = first_index { $_ == $o[0] } @m; + my $j = first_index { $_ == $o[1] } @m; + all { $_ > -1 } $i, $j; + } @$ordering; + + my $redo = 1; + while ($redo) { + $redo = 0; + for my \@o(@ordering) { + my $i = first_index { $_ == $o[0] } @m; + my $j = first_index { $_ == $o[1] } @m; + + next if $i < $j; + + $redo = 1; + @m[ $i, $j ] = @m[ $j, $i ]; + } + } + + return join ',', @m; +} + +sub solve ($input) { + return sum + map { + my @m = split ','; + $m[ @m / 2 ]; + } + map { reshuffle_manual( $input->{ordering}, $_ ) } + grep { !Part1::good_manual( $input->{ordering}, $_ ) } + $input->{manuals}->@*; +} + +1; diff --git a/2024/05/sample b/2024/05/sample new file mode 100644 index 0000000..9d146d6 --- /dev/null +++ b/2024/05/sample @@ -0,0 +1,28 @@ +47|53 +97|13 +97|61 +97|47 +75|29 +61|13 +75|53 +29|13 +97|29 +53|29 +61|53 +97|53 +61|29 +47|13 +75|47 +97|75 +47|61 +75|61 +47|29 +75|13 +53|13 + +75,47,61,53,29 +97,61,53,29,13 +75,29,13 +75,97,47,61,53 +61,13,29 +97,13,75,29,47 diff --git a/2024/05/test.t b/2024/05/test.t new file mode 100644 index 0000000..d484703 --- /dev/null +++ b/2024/05/test.t @@ -0,0 +1,25 @@ +use lib qw~ . ~; + +use Part1; +use Part2; + +use Test2::V0; +use Path::Tiny; +use File::Serialize; + +my $solutions = deserialize_file './solutions.yml'; + +my $sample = -f 'sample' && Part1::read_input( path('sample') ); +my $input= -f 'input' && Part1::read_input( path('input') ); + +subtest 'part 1' => sub { + is Part1::solve($sample) => 143; + is Part1::solve($input) => $solutions->{part1}; +}; + +subtest 'part 2' => sub { + is Part2::solve($sample) => 123; + is Part2::solve($input) => $solutions->{part2}; +}; + +done_testing(); diff --git a/2024/06/Part1.pm b/2024/06/Part1.pm new file mode 100644 index 0000000..e04c178 --- /dev/null +++ b/2024/06/Part1.pm @@ -0,0 +1,65 @@ +package Part1; + +use Math::Vector::Real; +use 5.36.0; + +no warnings qw/ uninitialized /; + +sub read_input ($file) { + my @grid = map { [ split '' ] } $file->lines({chomp=>1}); + return \@grid; +} + +my @directions = map { V(@$_) } [-1,0], [ 0,1], [1,0], [0,-1]; + +sub find_guard($grid) { + for my $i (0..$grid->$#* ) { + for my $j ( 0..$grid->[0]->$#* ) { + return V($i,$j) if $grid->[$i][$j] eq '^'; + } + } + die "didn't find the guard!"; +} + +use List::AllUtils qw/ any /; + +sub inside_grid($grid,$guard) { + return 0 if any { $_ < 0 } @$guard; + return $grid->[$guard->[0]][$guard->[1]]; +} + +sub solve ($grid) { + use DDP; + my $guard = find_guard($grid); + my $current_direction = 0; + + use Clone qw/ clone /; + $grid = clone($grid); + + my %seen; + $seen{"$guard"}++; + + while() { + my $ahead = V(@$guard) + $directions[$current_direction]; + last unless inside_grid($grid,$ahead); + if( $grid->[$ahead->[0]][$ahead->[1]] eq '#' ) { + $current_direction++; + $current_direction %= @directions; + redo; + } + $guard = $ahead; + $seen{"$guard"}++; + $grid->[$ahead->[0]][$ahead->[1]] = 'X'; + } + use DDP; + # p %seen; + # p $guard; + # for my $l ( @$grid ) { + # say join '', @$l; + # } + + return scalar keys %seen; + +} + +1; diff --git a/2024/06/Part2.pm b/2024/06/Part2.pm new file mode 100644 index 0000000..bb3455f --- /dev/null +++ b/2024/06/Part2.pm @@ -0,0 +1,74 @@ + +use 5.36.0; +use lib '.'; + +use DDP; + +use Part1; + +package Part2; + +use Math::Vector::Real; +use List::AllUtils qw/ uniq /; + +my @directions = map { V(@$_) } [ -1, 0 ], [ 0, 1 ], [ 1, 0 ], [ 0, -1 ]; + +sub copy_grid ($grid) { + return [ map { [@$_] } @$grid ]; +} + +sub add_vectors($x,$y) { + return [ $x->[0]+$y->[0],$x->[1]+$y->[1]]; +} + +sub do_the_round ($grid) { + my $guard = Part1::find_guard($grid); + my $current_direction = 0; + + my %seen; + + my $loop = 0; + + while () { + my $ahead = $guard + $directions[$current_direction]; + last unless Part1::inside_grid( $grid, $ahead ); + if ( $grid->[ $ahead->[0] ][ $ahead->[1] ] eq '#' ) { + $current_direction++; + $current_direction %= @directions; + redo; + } + $guard = $ahead; + if ( $seen{"$guard"}{$current_direction} ) { + $loop = 1; + last; + } + $seen{"$guard"}{$current_direction}++; + } + + return { + loop => $loop, + seen => [ + keys %seen + ] + }; +} + +sub solve ($grid) { + my $guard = Part1::find_guard($grid); + my $current_direction = 0; + + my $r = do_the_round($grid); + + my $loops = 0; + + for my $loc ( grep { $grid->[ $_->[0] ][ $_->[1] ] ne '^' } + map { [/(\d+)/g] } $r->{seen}->@* ) { + my $copy = copy_grid($grid); + $copy->[ $loc->[0] ][ $loc->[1] ] = '#'; + $loops++ if do_the_round($copy)->{loop}; + } + + return $loops; +} + +1; diff --git a/2024/06/benchmark.pl b/2024/06/benchmark.pl new file mode 100755 index 0000000..5f12fab --- /dev/null +++ b/2024/06/benchmark.pl @@ -0,0 +1,22 @@ +#!/usr/bin/env perl + +use 5.36.0; + +use Benchmark qw/ timethis timeit /; + +use lib qw~ . ~; + +use Part1; +use Part2; + +use Path::Tiny; +use File::Serialize; + +my $sample = -f 'sample' && Part1::read_input( path('sample') ); +my $input= -f 'input' && Part1::read_input( path('input') ); + +my $res; +$res = timeit(0, sub { Part1::solve($input) }); +say "part 1: ", $res->cpu_a/$res->iters; +$res = timeit(0, sub { Part2::solve($input) }); +say "part 2: ", $res->cpu_a/$res->iters; diff --git a/2024/06/sample b/2024/06/sample new file mode 100644 index 0000000..a4eb402 --- /dev/null +++ b/2024/06/sample @@ -0,0 +1,10 @@ +....#..... +.........# +.......... +..#....... +.......#.. +.......... +.#..^..... +........#. +#......... +......#... diff --git a/2024/06/test.t b/2024/06/test.t new file mode 100755 index 0000000..c514679 --- /dev/null +++ b/2024/06/test.t @@ -0,0 +1,27 @@ +#!/usr/bin/env perl + +use lib qw~ . ~; + +use Part1; +use Part2; + +use Test2::V0; +use Path::Tiny; +use File::Serialize; + +my $solutions = deserialize_file './solutions.yml'; + +my $sample = -f 'sample' && Part1::read_input( path('sample') ); +my $input= -f 'input' && Part1::read_input( path('input') ); + +subtest 'part 1' => sub { + is Part1::solve($sample) => 41; + is Part1::solve($input) => $solutions->{part1}; +}; + +subtest 'part 2' => sub { + is Part2::solve($sample) => 6; + # is Part2::solve($input) => $solutions->{part2}; +}; + +done_testing(); diff --git a/2024/07/Part1.pm b/2024/07/Part1.pm new file mode 100644 index 0000000..43686c3 --- /dev/null +++ b/2024/07/Part1.pm @@ -0,0 +1,29 @@ +use 5.36.0; + +package Part1; + +use List::AllUtils qw/ sum /; + +no warnings qw/ uninitialized /; + +sub read_input ($file) { + return [ map { [ split /[: ]+/ ] } $file->lines( { chomp => 1 } ) ]; +} + +sub valid_equation ( $total, @numbers ) { + return $numbers[0] == $total if @numbers == 1; + + return 0 if $numbers[0] > $total; + + my $i = shift @numbers; + my $j = shift @numbers; + return 1 if valid_equation( $total, $i*$j, @numbers ); + return 1 if valid_equation( $total, $i+$j, @numbers ); + return 0; +} + +sub solve ($equations) { + return sum map { $_->[0] } grep { valid_equation(@$_) } @$equations; +} + +1; diff --git a/2024/07/Part2.pm b/2024/07/Part2.pm new file mode 100644 index 0000000..5f21670 --- /dev/null +++ b/2024/07/Part2.pm @@ -0,0 +1,27 @@ +use lib '.'; + +use Part1; + +package Part2; + +use List::AllUtils qw/ sum /; +use 5.36.0; + +sub valid_equation ( $total, @numbers ) { + return $numbers[0] == $total if @numbers == 1; + + return 0 if $numbers[0] > $total; + + my $i = shift @numbers; + my $j = shift @numbers; + return 1 if valid_equation( $total, "$i$j", @numbers ); + return 1 if valid_equation( $total, $i*$j, @numbers ); + return 1 if valid_equation( $total, $i+$j, @numbers ); + return 0; +} + +sub solve ($equations) { + return sum map { $_->[0] } grep { valid_equation(@$_) } @$equations; +} + +1; diff --git a/2024/07/sample b/2024/07/sample new file mode 100644 index 0000000..fc6e099 --- /dev/null +++ b/2024/07/sample @@ -0,0 +1,9 @@ +190: 10 19 +3267: 81 40 27 +83: 17 5 +156: 15 6 +7290: 6 8 6 15 +161011: 16 10 13 +192: 17 8 14 +21037: 9 7 18 13 +292: 11 6 16 20 diff --git a/2024/07/test.t b/2024/07/test.t new file mode 100644 index 0000000..e9efd40 --- /dev/null +++ b/2024/07/test.t @@ -0,0 +1,27 @@ +use lib qw~ . ~; + +use Part1; +use Part2; + +use Test2::V0; +use Path::Tiny; +use File::Serialize; + +my $solutions = deserialize_file './solutions.yml'; + +my $sample = -f 'sample' && Part1::read_input( path('sample') ); +my $input= -f 'input' && Part1::read_input( path('input') ); + +subtest 'part 1' => sub { + is Part1::solve($sample) => 3749; + + ok Part1::solve($input) > 14711898485202; + is Part1::solve($input) => $solutions->{part1}; +}; + +subtest 'part 2' => sub { + is Part2::solve($sample) => 11387; + is Part2::solve($input) => $solutions->{part2}; +}; + +done_testing(); diff --git a/2024/08/Grid.pm b/2024/08/Grid.pm new file mode 100644 index 0000000..0826dcb --- /dev/null +++ b/2024/08/Grid.pm @@ -0,0 +1,33 @@ +use 5.36.0; + +package Grid; + +use Moo; + +has grid => ( is => 'ro' ); + +has dimensions => is => 'lazy', + default => sub ($self) { + return [ 0 + $self->{grid}->@*, 0 + $self->{grid}[0]->@* ]; + }; + +sub get($self,$i,$j) { + return $self->grid->[$i][$j]; +} + +sub foreach($self,$sub) { + for my $l ( 0..$self->dimensions->[0]-1) { + + for my $c ( 0..$self->dimensions->[1]-1) { + $sub->($l,$c,$self->get($l,$c)); + } + } +} + +sub is_inside($self,$i,$j) { + return 0 if $i < 0 or $j < 0; + return 0 if $i >= $self->dimensions->[0]; + return 0 if $j >= $self->dimensions->[1]; + return 1; +} +1; diff --git a/2024/08/Part1.pm b/2024/08/Part1.pm new file mode 100644 index 0000000..3ee0178 --- /dev/null +++ b/2024/08/Part1.pm @@ -0,0 +1,49 @@ +use lib qw/ . /; + +package Part1; + +use 5.36.0; + +use List::AllUtils qw/ uniq /; +use Math::Vector::Real; +use Algorithm::Combinatorics qw(permutations variations); +use Grid; + +no warnings qw/ uninitialized /; + +sub read_input ($file) { + Grid->new( grid =>[ + map { [ split '' ] } + $file->lines({chomp=>1}) ]); +} + +sub find_antinodes($grid,@group) { + my %antinodes; + my $iter = variations(\@group,2); + while( my $p = $iter->next ) { + my( $x, $y) = @$p; + my $new = 2*$y-$x; + $antinodes{$new}++ if $grid->is_inside(@$new); + } + return keys %antinodes; +} + +sub solve ($grid) { + my %groups; + + $grid->foreach( sub($l,$r,$content) { + return if $content eq '.'; + my $g = ( $groups{$content} //= [] ); + push @$g, V($l,$r); + }); + + my %antinodes; + + for my $group ( values %groups ) { + $antinodes{$_}++ for find_antinodes($grid,@$group); + } + + return scalar keys %antinodes; +} + +1; diff --git a/2024/08/Part2.pm b/2024/08/Part2.pm new file mode 100644 index 0000000..43b1b4a --- /dev/null +++ b/2024/08/Part2.pm @@ -0,0 +1,47 @@ +use lib '.'; +use 5.36.0; + +use Part1; + +package Part2; + +use List::AllUtils qw/ uniq /; +use Math::Vector::Real; +use Algorithm::Combinatorics qw(permutations variations variations_with_repetition); +use Grid; + +sub find_antinodes($grid,@group) { + my %antinodes; + my $iter = variations(\@group,2); + while( my $p = $iter->next ) { + my( $x, $y) = @$p; + my $delta = $y - $x; + $y = V(@$y); + + while( $grid->is_inside(@$y) ) { + $antinodes{$y}++; + $y+=$delta; + } + } + return keys %antinodes; +} + +sub solve ($grid) { + my %groups; + + $grid->foreach( sub($l,$r,$content) { + return if $content eq '.'; + my $g = ( $groups{$content} //= [] ); + push @$g, V($l,$r); + }); + + my %antinodes; + + for my $group ( values %groups ) { + $antinodes{$_}++ for find_antinodes($grid,@$group); + } + + return scalar keys %antinodes; +} + +1; diff --git a/2024/08/sample b/2024/08/sample new file mode 100644 index 0000000..78a1e91 --- /dev/null +++ b/2024/08/sample @@ -0,0 +1,12 @@ +............ +........0... +.....0...... +.......0.... +....0....... +......A..... +............ +............ +........A... +.........A.. +............ +............ diff --git a/2024/08/test.t b/2024/08/test.t new file mode 100644 index 0000000..64ed1da --- /dev/null +++ b/2024/08/test.t @@ -0,0 +1,25 @@ +use lib qw~ . ~; + +use Part1; +use Part2; + +use Test2::V0; +use Path::Tiny; +use File::Serialize; + +my $solutions = deserialize_file './solutions.yml'; + +my $sample = -f 'sample' && Part1::read_input( path('sample') ); +my $input= -f 'input' && Part1::read_input( path('input') ); + +subtest 'part 1' => sub { + is Part1::solve($sample) => 14; + is Part1::solve($input) => $solutions->{part1}; +}; + +subtest 'part 2' => sub { + is Part2::solve($sample) => 34; + is Part2::solve($input) => $solutions->{part2}; +}; + +done_testing(); diff --git a/2024/09/Part1.pm b/2024/09/Part1.pm new file mode 100644 index 0000000..1548809 --- /dev/null +++ b/2024/09/Part1.pm @@ -0,0 +1,39 @@ +package Part1; + +use 5.36.0; + +no warnings qw/ uninitialized /; + +sub read_input ($file) { + my $i = 0; + my $data = 1; + my $x = [ + map { ( ( $data++ % 2 ) ? $i++ : -1 ) x $_ } + split '', $file->slurp + ]; + return $x; +} + +sub solve (@disk) { + use DDP; + + my $i = -1; + + while( ++$i < $#disk ) { + pop @disk while $disk[-1] == -1; + + next unless $disk[$i] == -1; + + $disk[$i] = pop @disk; + } + + my $checksum = 0; + + $i = 0; + $checksum += $i++ * $_ for @disk; + + return $checksum; + +} + +1; diff --git a/2024/09/Part2.pm b/2024/09/Part2.pm new file mode 100644 index 0000000..651346b --- /dev/null +++ b/2024/09/Part2.pm @@ -0,0 +1,71 @@ +use lib '.'; + +use Part1; + +package Part2; + +use 5.36.0; + +sub read_input ($file) { + my $i = 0; + my $data = 1; + my $x = [ + map { [ ( $data++ % 2 ) ? $i++ : -1, $_ ] } + split '', $file->slurp + ]; + return $x; +} + + +sub print_disk(@disk) { + for (@disk) { + my @a = ($_->[0] == -1 ? '.':$_->[0]) x $_->[1]; + print @a; + } + print "\n"; +} + +sub my_first_index($max,@disk) { + my $i = -1; + while(++$i < $max ) { + return $i if $_->[0] == -1 and $_->[1] >= $disk[$i][1]; + } + return -1; +} + +sub solve (@disk) { + use DDP; + + my $i = @disk; + + use List::MoreUtils qw/ first_index /; + + while( --$i >= 0 ) { + next if $i > $#disk; + + next if $disk[$i][0] == -1; + + my $swap = first_index { + $_->[0] == -1 and $_->[1] >= $disk[$i][1]; + } @disk; + + next if $swap == -1 or $swap >= $i; + + $disk[$swap][1] -= $disk[$i][1]; + splice @disk, $swap, 0, [ $disk[$i]->@* ]; + $disk[++$i][0] = -1; + } + + + my $checksum = 0; + + $i = 0; + while( my $p = shift @disk ) { + $checksum += $i++ * ( $_ == -1 ? 0 : $_ ) for ( $p->[0]) x $p->[1]; + } + + return $checksum; + +} + +1; diff --git a/2024/09/sample b/2024/09/sample new file mode 100644 index 0000000..f96c390 --- /dev/null +++ b/2024/09/sample @@ -0,0 +1 @@ +2333133121414131402 diff --git a/2024/09/test.t b/2024/09/test.t new file mode 100644 index 0000000..fde8e14 --- /dev/null +++ b/2024/09/test.t @@ -0,0 +1,28 @@ +use lib qw~ . ~; + +use Part1; +use Part2; + +use Test2::V0; +use Path::Tiny; +use File::Serialize; + +my $solutions = deserialize_file './solutions.yml'; + +my $sample = -f 'sample' && Part1::read_input( path('sample') ); +my $input= -f 'input' && Part1::read_input( path('input') ); + +subtest 'part 1' => sub { + skip_all; + is Part1::solve(@$sample) => 1928; + is Part1::solve(@$input) => $solutions->{part1}; +}; + +subtest 'part 2' => sub { +my $sample = -f 'sample' && Part2::read_input( path('sample') ); +my $input= -f 'input' && Part2::read_input( path('input') ); + is Part2::solve(@$sample) => 2858; + is Part2::solve(@$input) => $solutions->{part2}; +}; + +done_testing(); diff --git a/2024/10/Part1.pm b/2024/10/Part1.pm new file mode 100644 index 0000000..9fabdf9 --- /dev/null +++ b/2024/10/Part1.pm @@ -0,0 +1,58 @@ +use lib qw/ . /; +use lib qw! ../../perl-lib !; + +use 5.36.0; +use Solution; + +package Part1; + +use Moo; + +use AoC::Grid; +use List::AllUtils qw/ sum /; + +extends 'Solution'; + +has '+input', default => sub ($self) { + AoC::Grid->new( string => $self->file_slurp ); +}; + +sub trailhead_score ( $self, $x, $y ) { + + my $grid = $self->input; + my %endpoints; + + my @points = ( [ $x, $y, 0 ] ); + + while ( my $p = shift @points ) { + if ( $p->[2] == 9 ) { + $endpoints{ join ':', @$p }++; + next; + } + my $v = $p->[2] + 1; + push @points, + map { [ @$_, $v ] } + grep { $grid->get(@$_) == $v } + $grid->ortho_neighbors( $p->@[ 0, 1 ] ); + } + + return $self->trailhead_score_resolution(%endpoints); +} + +sub trailhead_score_resolution ( $self, %endpoints ) { + return scalar keys %endpoints; +} + +sub solve ($self) { + my @points; + + $self->input->foreach( + sub ( $x, $y, $v ) { + push @points, [ $x, $y ] if $v == 0; + } + ); + + return sum map { $self->trailhead_score(@$_) } @points; +} + +1; diff --git a/2024/10/Part2.pm b/2024/10/Part2.pm new file mode 100644 index 0000000..f899ecc --- /dev/null +++ b/2024/10/Part2.pm @@ -0,0 +1,20 @@ +use lib qw/ . /; +use lib qw! ../../perl-lib !; + +use 5.36.0; +use Solution; + +package Part2; + +use Moo; + +use AoC::Grid; +use List::AllUtils qw/ sum /; + +extends 'Part1'; + +sub trailhead_score_resolution($self,%endpoints) { + return sum values %endpoints; +} + +1; diff --git a/2024/10/Solution.pm b/2024/10/Solution.pm new file mode 100644 index 0000000..095ded1 --- /dev/null +++ b/2024/10/Solution.pm @@ -0,0 +1,40 @@ +use 5.36.0; + +package Solution; + +use Moo; + +use Path::Tiny; + +has file => ( is => 'ro' ); + +has path_file => ( + is => 'lazy', + default => sub ($self) { + path( $self->file ); + } +); + +sub day_part ($self) { + $self =~ /Part(\d)/g; + return $1; +} + +sub file_lines ($self) { + $self->path_file->lines( { chomp => 1 } ); +} + +sub file_slurp ($self) { + my $content = $self->path_file->slurp; + chomp $content; + return $content; +} + +has input => ( + is => 'lazy', + default => sub { ... } +); + +sub solve { ... } + +1; diff --git a/2024/10/sample b/2024/10/sample new file mode 100644 index 0000000..cada9b3 --- /dev/null +++ b/2024/10/sample @@ -0,0 +1,8 @@ +89010123 +78121874 +87430965 +96549874 +45678903 +32019012 +01329801 +10456732 diff --git a/2024/10/test.t b/2024/10/test.t new file mode 100644 index 0000000..dee3129 --- /dev/null +++ b/2024/10/test.t @@ -0,0 +1,22 @@ +use lib qw~ . ~; + +use Part1; +use Part2; + +use Test2::V0; +use Path::Tiny; +use File::Serialize; + +my $solutions = deserialize_file './solutions.yml'; + +subtest 'part 1' => sub { + is( Part1->new(file=>'sample')->solve() => 36 ); + is( Part1->new(file=>'input')->solve() => $solutions->{part1} ); +}; + +subtest 'part 2' => sub { + is( Part2->new(file=>'sample')->solve() => 81 ); + is( Part2->new(file=>'input')->solve() => $solutions->{part2}); +}; + +done_testing(); diff --git a/2024/cpanfile b/2024/cpanfile index b1653aa..dff50b1 100644 --- a/2024/cpanfile +++ b/2024/cpanfile @@ -1 +1,3 @@ requires 'Math::VectorXYZ'; +requires 'Math::VectorXYZ::XS'; +requires 'Algorithm::Combinatorics'; diff --git a/2024/deno.json b/2024/deno.json new file mode 100644 index 0000000..5b320c2 --- /dev/null +++ b/2024/deno.json @@ -0,0 +1,8 @@ +{ + "tasks": { + "dev": "deno run --watch main.ts" + }, + "imports": { + "@std/assert": "jsr:@std/assert@1" + } +} diff --git a/perl-lib/AoC/Grid.pm b/perl-lib/AoC/Grid.pm new file mode 100644 index 0000000..9f10ef5 --- /dev/null +++ b/perl-lib/AoC/Grid.pm @@ -0,0 +1,56 @@ +use 5.36.0; + +package AoC::Grid; + +use Math::Vector::Real; + +use Moo; + +has grid => ( is => 'lazy', default => sub($self) { + return [ + map { [ split '' ] } split "\n", $self->string + ] +}); + +has string => ( is => 'ro' ); + +has dimensions => is => 'lazy', + default => sub ($self) { + return [ 0 + $self->grid->@*, 0 + $self->grid->[0]->@* ]; + }; + +sub get($self,$i,$j) { + return unless $self->is_inside($i,$j); + return $self->grid->[$i][$j]; +} + +sub foreach($self,$sub) { + for my $l ( 0..$self->dimensions->[0]-1) { + + for my $c ( 0..$self->dimensions->[1]-1) { + $sub->($l,$c,$self->get($l,$c)); + } + } +} + +sub is_inside($self,$i,$j) { + return 0 if $i < 0 or $j < 0; + return 0 if $i >= $self->dimensions->[0]; + return 0 if $j >= $self->dimensions->[1]; + return 1; +} + +sub as_string($self) { + my $o = ''; + for my $l ( $self->grid->@* ) { + $o .= join( '', @$l)."\n"; + } + return $o; +} + +sub ortho_neighbors($self,$x,$y) { + return grep { + $self->is_inside(@$_) + } map { V($x,$y)+V(@$_) } [1,0],[0,1],[-1,0],[0,-1]; +} +1;