a few more days
This commit is contained in:
parent
0ebf67a5cc
commit
9fb6d13df2
2
.gitignore
vendored
2
.gitignore
vendored
@ -7,3 +7,5 @@ pnpm-lock.yaml
|
|||||||
*_LOCAL_*
|
*_LOCAL_*
|
||||||
*_REMOTE_*
|
*_REMOTE_*
|
||||||
*.orig
|
*.orig
|
||||||
|
deno.lock
|
||||||
|
input
|
||||||
|
41
2024/05/Part1.pm
Normal file
41
2024/05/Part1.pm
Normal 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
63
2024/05/Part2.pm
Normal 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
28
2024/05/sample
Normal 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
25
2024/05/test.t
Normal 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
65
2024/06/Part1.pm
Normal 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
74
2024/06/Part2.pm
Normal 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
22
2024/06/benchmark.pl
Executable 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
10
2024/06/sample
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
....#.....
|
||||||
|
.........#
|
||||||
|
..........
|
||||||
|
..#.......
|
||||||
|
.......#..
|
||||||
|
..........
|
||||||
|
.#..^.....
|
||||||
|
........#.
|
||||||
|
#.........
|
||||||
|
......#...
|
27
2024/06/test.t
Executable file
27
2024/06/test.t
Executable 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
29
2024/07/Part1.pm
Normal 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
27
2024/07/Part2.pm
Normal 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
9
2024/07/sample
Normal 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
27
2024/07/test.t
Normal 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
33
2024/08/Grid.pm
Normal 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
49
2024/08/Part1.pm
Normal 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
47
2024/08/Part2.pm
Normal 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
12
2024/08/sample
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
............
|
||||||
|
........0...
|
||||||
|
.....0......
|
||||||
|
.......0....
|
||||||
|
....0.......
|
||||||
|
......A.....
|
||||||
|
............
|
||||||
|
............
|
||||||
|
........A...
|
||||||
|
.........A..
|
||||||
|
............
|
||||||
|
............
|
25
2024/08/test.t
Normal file
25
2024/08/test.t
Normal 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
39
2024/09/Part1.pm
Normal 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
71
2024/09/Part2.pm
Normal 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
1
2024/09/sample
Normal file
@ -0,0 +1 @@
|
|||||||
|
2333133121414131402
|
28
2024/09/test.t
Normal file
28
2024/09/test.t
Normal 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
58
2024/10/Part1.pm
Normal 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
20
2024/10/Part2.pm
Normal 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
40
2024/10/Solution.pm
Normal 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
8
2024/10/sample
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
89010123
|
||||||
|
78121874
|
||||||
|
87430965
|
||||||
|
96549874
|
||||||
|
45678903
|
||||||
|
32019012
|
||||||
|
01329801
|
||||||
|
10456732
|
22
2024/10/test.t
Normal file
22
2024/10/test.t
Normal 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();
|
@ -1 +1,3 @@
|
|||||||
requires 'Math::VectorXYZ';
|
requires 'Math::VectorXYZ';
|
||||||
|
requires 'Math::VectorXYZ::XS';
|
||||||
|
requires 'Algorithm::Combinatorics';
|
||||||
|
8
2024/deno.json
Normal file
8
2024/deno.json
Normal 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
56
perl-lib/AoC/Grid.pm
Normal 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;
|
Loading…
Reference in New Issue
Block a user