a few more days

This commit is contained in:
Yanick Champoux 2024-12-10 18:34:16 -05:00
parent 0ebf67a5cc
commit 9fb6d13df2
31 changed files with 968 additions and 0 deletions

2
.gitignore vendored
View File

@ -7,3 +7,5 @@ pnpm-lock.yaml
*_LOCAL_*
*_REMOTE_*
*.orig
deno.lock
input

41
2024/05/Part1.pm Normal file
View File

@ -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;

63
2024/05/Part2.pm Normal file
View File

@ -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;

28
2024/05/sample Normal file
View File

@ -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

25
2024/05/test.t Normal file
View File

@ -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();

65
2024/06/Part1.pm Normal file
View File

@ -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;

74
2024/06/Part2.pm Normal file
View File

@ -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;

22
2024/06/benchmark.pl Executable file
View File

@ -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;

10
2024/06/sample Normal file
View File

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

27
2024/06/test.t Executable file
View File

@ -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();

29
2024/07/Part1.pm Normal file
View File

@ -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;

27
2024/07/Part2.pm Normal file
View File

@ -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;

9
2024/07/sample Normal file
View File

@ -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

27
2024/07/test.t Normal file
View File

@ -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();

33
2024/08/Grid.pm Normal file
View File

@ -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;

49
2024/08/Part1.pm Normal file
View File

@ -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;

47
2024/08/Part2.pm Normal file
View File

@ -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;

12
2024/08/sample Normal file
View File

@ -0,0 +1,12 @@
............
........0...
.....0......
.......0....
....0.......
......A.....
............
............
........A...
.........A..
............
............

25
2024/08/test.t Normal file
View File

@ -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();

39
2024/09/Part1.pm Normal file
View File

@ -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;

71
2024/09/Part2.pm Normal file
View File

@ -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;

1
2024/09/sample Normal file
View File

@ -0,0 +1 @@
2333133121414131402

28
2024/09/test.t Normal file
View File

@ -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();

58
2024/10/Part1.pm Normal file
View File

@ -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;

20
2024/10/Part2.pm Normal file
View File

@ -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;

40
2024/10/Solution.pm Normal file
View File

@ -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;

8
2024/10/sample Normal file
View File

@ -0,0 +1,8 @@
89010123
78121874
87430965
96549874
45678903
32019012
01329801
10456732

22
2024/10/test.t Normal file
View File

@ -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();

View File

@ -1 +1,3 @@
requires 'Math::VectorXYZ';
requires 'Math::VectorXYZ::XS';
requires 'Algorithm::Combinatorics';

8
2024/deno.json Normal file
View File

@ -0,0 +1,8 @@
{
"tasks": {
"dev": "deno run --watch main.ts"
},
"imports": {
"@std/assert": "jsr:@std/assert@1"
}
}

56
perl-lib/AoC/Grid.pm Normal file
View File

@ -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;