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_*
|
||||
*_REMOTE_*
|
||||
*.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::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