diff --git a/2024/01/Part1.pm b/2024/01/Part1.pm new file mode 100644 index 0000000..d2d1b8d --- /dev/null +++ b/2024/01/Part1.pm @@ -0,0 +1,22 @@ +package Day1::Part1; + +use 5.36.0; + +use List::Util qw/ sum pairmap /; +use List::MoreUtils qw/ part zip /; + +sub read_file(@lines) { + my $i = 0; + my @lists = part { $i++ % 2} map { split } @lines; + return @lists; +} + + +sub solve(@lists) { + for my $x (1,0) { + $lists[$x] = [ sort { $a <=> $b } $lists[$x]->@* ]; + } + return sum pairmap { abs $a - $b } zip $lists[0]->@*, $lists[1]->@*; +} + +1; diff --git a/2024/01/Part2.pm b/2024/01/Part2.pm new file mode 100644 index 0000000..3e1a550 --- /dev/null +++ b/2024/01/Part2.pm @@ -0,0 +1,17 @@ +package Day1::Part2; + +use 5.36.0; + +use List::Util qw/ sum pairmap /; +use List::MoreUtils qw/ part zip /; + +sub solve($l1,$l2) { + my %occur; + + $occur{$_}++ for @$l2; + + no warnings qw/ uninitialized /; + sum map { $occur{$_}*$_ } @$l1; +} + +1; diff --git a/2024/01/main.ts b/2024/01/main.ts new file mode 100644 index 0000000..292ce5f --- /dev/null +++ b/2024/01/main.ts @@ -0,0 +1,8 @@ +export function add(a: number, b: number): number { + return a + b; +} + +// Learn more at https://docs.deno.com/runtime/manual/examples/module_metadata#concepts +if (import.meta.main) { + console.log("Add 2 + 3 =", add(2, 3)); +} diff --git a/2024/01/part1.ts b/2024/01/part1.ts new file mode 100644 index 0000000..d5a9fe1 --- /dev/null +++ b/2024/01/part1.ts @@ -0,0 +1,34 @@ +import * as fs from "node:fs/promises"; + +export async function readFile(filename: string): Promise { + const lines = await fs + .readFile(filename, { encoding: "utf8" }) + .then((lines: string) => lines.split(/\n/)) + .then((lines: string[]) => + lines.filter((x) => x).map((x) => + x.split(/ +/).map((x) => Number(x)) + ) + ); + + const l1: number[] = []; + const l2: number[] = []; + + lines.forEach(([a, b]) => { + l1.push(a); + l2.push(b); + }); + + return [l1, l2]; +} + +export function solve(...lists: number[][]) { + lists.forEach((l) => l.sort()); + + let sum = 0; + + for (const i in lists[0]) { + sum += Math.abs(lists[0][i] - lists[1][i]); + } + + return sum; +} diff --git a/2024/01/test.t b/2024/01/test.t new file mode 100644 index 0000000..03211ea --- /dev/null +++ b/2024/01/test.t @@ -0,0 +1,15 @@ +use lib qw/ . /; + +use Part1; +use Part2; + +use Test2::V0; +use Path::Tiny; + +my @lists = Day1::Part1::read_file( path('input')->lines ); + +is Day1::Part1::solve(@lists) => 1889772; + +is Day1::Part2::solve(@lists) => 23228917; + +done_testing(); diff --git a/2024/01/test.ts b/2024/01/test.ts new file mode 100644 index 0000000..c031cb9 --- /dev/null +++ b/2024/01/test.ts @@ -0,0 +1,9 @@ +import { expect } from 'jsr:@std/expect'; + +import { readFile, solve as d1Solve } from './part1.ts'; + +const input = readFile( import.meta.dirname + '/input'); + +Deno.test( 'part 1', async () => { + expect(d1Solve(...((await input) as any))).toEqual(1889772); +}); diff --git a/2024/02/Part1.pm b/2024/02/Part1.pm new file mode 100644 index 0000000..eec6966 --- /dev/null +++ b/2024/02/Part1.pm @@ -0,0 +1,32 @@ +package Day2::Part1; + +use 5.36.0; + +use List::Util qw/ sum pairmap /; +use List::MoreUtils qw/ part zip /; + +sub read_file(@lines) { + return map { [split] } @lines; +} + +sub is_safe(@report) { + my $asc = $report[1] - $report[0]; + + my $level = shift @report; + + while(@report) { + my $next = shift @report; + my $delta = $next - $level; + return 0 unless $delta*$asc >= 0; + return 0 unless 1 <= abs($delta) <= 3; + $level = $next; + } + + return 1; +} + +sub solve(@reports) { + return sum map { is_safe(@$_) } @reports; +} + +1; diff --git a/2024/02/Part2.pm b/2024/02/Part2.pm new file mode 100644 index 0000000..e8bf0a6 --- /dev/null +++ b/2024/02/Part2.pm @@ -0,0 +1,25 @@ +use lib qw/ . /; + +use Part1; + +package Day2::Part2; + +use 5.36.0; + +use List::Util qw/ sum pairmap /; +use List::MoreUtils qw/ part zip /; + +sub is_safe_dampened(@report) { + for my $i ( 0..$#report) { + my @copy = @report; + splice @copy, $i, 1; + return 1 if Day2::Part1::is_safe(@copy); + } + return 0; +} + +sub solve(@reports) { + return sum map { is_safe_dampened(@$_) } @reports; +} + +1; diff --git a/2024/02/part1.ts b/2024/02/part1.ts new file mode 100644 index 0000000..bb1e3dd --- /dev/null +++ b/2024/02/part1.ts @@ -0,0 +1,34 @@ +import * as fs from "node:fs/promises"; + +export function readInput(filename: string): Promise { + return fs + .readFile(filename, { encoding: "utf8" }) + .then((lines: string) => lines.split(/\n/)) + .then((lines: string[]) => + lines.filter((x) => x).map((x) => + x.split(/ +/).map((x) => Number(x)) + ) + ); +} + +export function isSafe(report: readonly number[]): boolean { + const r = [ ...report ]; + + const asc = r[1] - r[0]; + + let level = r.shift(); + + while (r.length) { + const next = r.shift(); + const delta = next! - level!; + if (delta * asc < 0) return false; + if (Math.abs(delta) < 1 || Math.abs(delta) > 3) return false; + level = next; + } + + return true; +} + +export function solve(reports: number[][]): number { + return reports.filter(isSafe).length; +} diff --git a/2024/02/part2.ts b/2024/02/part2.ts new file mode 100644 index 0000000..b63fbed --- /dev/null +++ b/2024/02/part2.ts @@ -0,0 +1,13 @@ +import { isSafe } from './part1.ts'; + +function isSafeDampened(report: number[]) { + for( let i =0; i < report.length; i++) { + const copy = [...report]; + copy.splice( i, 1); + if( isSafe(copy) ) return true; + } + return false; +} +export function solve(reports: number[][]) { + return reports.filter(isSafeDampened).length; +} diff --git a/2024/02/sample b/2024/02/sample new file mode 100644 index 0000000..b49c10d --- /dev/null +++ b/2024/02/sample @@ -0,0 +1,6 @@ +7 6 4 2 1 +1 2 7 8 9 +9 7 6 2 1 +1 3 2 4 5 +8 6 4 4 1 +1 3 6 7 9 diff --git a/2024/02/test.t b/2024/02/test.t new file mode 100644 index 0000000..d122a33 --- /dev/null +++ b/2024/02/test.t @@ -0,0 +1,22 @@ +use lib qw~ . ~; + +use Part1; +use Part2; + +use Test2::V0; +use Path::Tiny; + +my @sample_reports = Day2::Part1::read_file( path('sample')->lines ); +my @reports = Day2::Part1::read_file( path('input')->lines ); + +subtest 'part 1' => sub { + is Day2::Part1::is_safe(qw/ 7 6 4 2 1 /) => 1; + is Day2::Part1::solve(@sample_reports) => 2; + is Day2::Part1::solve(@reports) => 598; +}; + +subtest 'part 2' => sub { + is Day2::Part2::solve(@reports) => 634; +}; + +done_testing(); diff --git a/2024/02/test.ts b/2024/02/test.ts new file mode 100644 index 0000000..5f930aa --- /dev/null +++ b/2024/02/test.ts @@ -0,0 +1,14 @@ +import { expect } from 'jsr:@std/expect'; + +import { readInput, solve as p1Solve } from './part1.ts'; +import { solve as p2Solve } from './part2.ts'; + +const reports = await readInput( import.meta.dirname + '/input'); + +Deno.test( 'part 1', () => { + expect( p1Solve(reports)).toEqual(598); +}); + +Deno.test( 'part 2', () => { + expect( p2Solve(reports)).toEqual(634); +}); diff --git a/2024/03/Part1.pm b/2024/03/Part1.pm new file mode 100644 index 0000000..1c40870 --- /dev/null +++ b/2024/03/Part1.pm @@ -0,0 +1,11 @@ +use 5.36.0; + +package Day3::Part1; + +sub solve($program) { + my $total = 0; + $total += $1 * $2 while $program =~ /mul \( (\d+) , (\d+) \)/xg; + return $total; +} + +1; diff --git a/2024/03/Part2.pm b/2024/03/Part2.pm new file mode 100644 index 0000000..007b77b --- /dev/null +++ b/2024/03/Part2.pm @@ -0,0 +1,15 @@ +use lib qw/ . /; + +use Part1; + +package Day3::Part2; + +use 5.36.0; + +sub solve($program) { + my $p = $program; + $p =~ s/ don't\(\) .*? (do\(\)|\Z) //smgx; + return Day3::Part1::solve($p); +} + +1; diff --git a/2024/03/test.t b/2024/03/test.t new file mode 100644 index 0000000..cd75664 --- /dev/null +++ b/2024/03/test.t @@ -0,0 +1,20 @@ +use lib qw~ . ~; + +use Part1; +use Part2; + +use Test2::V0; +use Path::Tiny; + +# my @sample_reports = Day2::Part1::read_file( path('sample')->slurp ); +my $program = path('input')->slurp ; + +subtest 'part 1' => sub { + is Day3::Part1::solve($program) => 184576302; +}; + +subtest 'part 2' => sub { + is Day3::Part2::solve($program) => 118173507; +}; + +done_testing(); diff --git a/2024/04/Part1.pm b/2024/04/Part1.pm new file mode 100644 index 0000000..c7755fa --- /dev/null +++ b/2024/04/Part1.pm @@ -0,0 +1,47 @@ +package Part1; + +use 5.36.0; + +no warnings qw/ uninitialized /; + +use Math::VectorXYZ::2D; +use List::Util qw/ sum pairmap /; +use List::MoreUtils qw/ part zip /; + +sub read_file (@lines) { + return [ map { [ split '' ] } @lines ]; +} + +my @directions; +for my $x ( -1 .. 1 ) { + for my $y ( -1 .. 1 ) { + push @directions, Vec( $x, $y ) if $x or $y; + } +} + +sub find_word ( $grid, $letters, $loc, $dir ) { + + return 1 unless @$letters; + + $loc += $dir; + return 0 if $loc->[0] < 0 or $loc->[1] < 0; + + return 0 if $grid->[ $loc->[0] ][ $loc->[1] ] ne shift @$letters; + + return find_word( $grid, $letters, $loc, $dir ); +} + +sub solve ($grid) { + my $total = 0; + for my $x ( 0 .. $grid->$#* ) { + for my $y ( 0 .. $grid->[0]->$#* ) { + if ( $grid->[$x][$y] eq 'X' ) { + $total += find_word( $grid, [qw/M A S/], Vec( $x, $y ), $_ ) + for @directions; + } + } + } + return $total; +} + +1; diff --git a/2024/04/Part2.pm b/2024/04/Part2.pm new file mode 100644 index 0000000..594aae7 --- /dev/null +++ b/2024/04/Part2.pm @@ -0,0 +1,42 @@ +package Part2; + +use 5.36.0; + +no warnings qw/ uninitialized /; + +use Math::VectorXYZ::2D; +use List::Util qw/ sum pairmap /; +use List::MoreUtils qw/ part zip /; + +sub find_word ( $grid, @locs ) { + + for (@locs) { + return 0 if $_->[0] < 0 or $_->[1] < 0; + } + + my $letters = join '', sort map { $grid->[ $_->[0] ][ $_->[1] ] } @locs; + + return $letters eq 'MS'; + +} + +sub solve ($grid) { + + my $total = 0; + for my $x ( 0 .. $grid->$#* ) { + for my $y ( 0 .. $grid->[0]->$#* ) { + if ( $grid->[$x][$y] eq 'A' ) { + $total++ if 2 == grep { + find_word( + $grid, + Vec( $x, $y ) - Vec(@$_), + Vec( $x, $y ) + Vec(@$_) + ) + } [ 1, 1 ], [ -1, 1 ]; + } + } + } + return $total; +} + +1; diff --git a/2024/04/sample b/2024/04/sample new file mode 100644 index 0000000..1f4eda2 --- /dev/null +++ b/2024/04/sample @@ -0,0 +1,10 @@ +MMMSXXMASM +MSAMXMSMSA +AMXSXMAAMM +MSAMASMSMX +XMASAMXAMM +XXAMMXXAMA +SMSMSASXSS +SAXAMASAAA +MAMMMXMMMM +MXMXAXMASX diff --git a/2024/04/test.t b/2024/04/test.t new file mode 100644 index 0000000..f2553a9 --- /dev/null +++ b/2024/04/test.t @@ -0,0 +1,22 @@ +use lib qw~ . ~; + +use Part1; +use Part2; + +use Test2::V0; +use Path::Tiny; + +my $sample = Part1::read_file( path('sample')->lines ); +my $input= Part1::read_file( path('input')->lines ); + +subtest 'part 1' => sub { + is Part1::solve($sample) => 18; + is Part1::solve($input) => 2336; +}; + +subtest 'part 2' => sub { + is Part2::solve($sample) => 9; + is Part2::solve($input) => 1831; +}; + +done_testing(); diff --git a/2024/cpanfile b/2024/cpanfile new file mode 100644 index 0000000..b1653aa --- /dev/null +++ b/2024/cpanfile @@ -0,0 +1 @@ +requires 'Math::VectorXYZ'; diff --git a/2024/settings.json b/2024/settings.json new file mode 100644 index 0000000..f56dd35 --- /dev/null +++ b/2024/settings.json @@ -0,0 +1 @@ +{ "deno.enable": true}