From aa8154010ad8f9f25bb62e272946741cccb24a6e Mon Sep 17 00:00:00 2001 From: Joe Hildebrand Date: Tue, 3 Dec 2024 22:56:08 -0700 Subject: [PATCH] Day 4 --- day4.peggy | 1 + day4.ts | 50 +++++++++++++++++++++++++++++++ inputs | 2 +- lib/rect.ts | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++ test/day4.js | 1 + 5 files changed, 137 insertions(+), 1 deletion(-) create mode 100644 day4.peggy create mode 100644 day4.ts create mode 100644 test/day4.js diff --git a/day4.peggy b/day4.peggy new file mode 100644 index 0000000..cdcfc7c --- /dev/null +++ b/day4.peggy @@ -0,0 +1 @@ +lines = (@[^\n]+ "\n")* diff --git a/day4.ts b/day4.ts new file mode 100644 index 0000000..43b5a28 --- /dev/null +++ b/day4.ts @@ -0,0 +1,50 @@ +import { type MainArgs, parseFile } from './lib/utils.ts'; +import { AllBoxDirs, BoxDir, Rect } from './lib/rect.ts'; + +type Parsed = string[][]; + +function part1(inp: Parsed): number { + let tot = 0; + const r = new Rect(inp); + const Xs = r.filter((v) => v === 'X'); + + for (const x of Xs) { + for (const dir of AllBoxDirs) { + if (r.ray(x, dir, 4).join('') === 'XMAS') { + tot++; + } + } + } + + return tot; +} + +function part2(inp: Parsed): number { + let tot = 0; + const r = new Rect(inp); + const As = r.filter((v) => v === 'A'); + + const corners = [BoxDir.NW, BoxDir.SE, BoxDir.NE, BoxDir.SW]; + for (const a of As) { + const letters = corners.map((d) => { + const p = a.inBoxDir(d, r); + return p ? r.get(p) : undefined; + }); + if (letters.some((p) => (p !== 'M') && (p !== 'S'))) { + continue; + } + if (letters[0] === letters[1]) { + continue; + } + if (letters[2] === letters[3]) { + continue; + } + tot++; + } + return tot; +} + +export default async function main(args: MainArgs): Promise<[number, number]> { + const inp = await parseFile(args); + return [part1(inp), part2(inp)]; +} diff --git a/inputs b/inputs index b7be029..f9def56 160000 --- a/inputs +++ b/inputs @@ -1 +1 @@ -Subproject commit b7be02964df0f17226d6d73ab316e1817183a61e +Subproject commit f9def56e294696296806761bc751e9cddc7a79be diff --git a/lib/rect.ts b/lib/rect.ts index 6cfac10..30aa188 100644 --- a/lib/rect.ts +++ b/lib/rect.ts @@ -26,6 +26,28 @@ export const OppositeDir: Record = { [Dir.N]: Dir.S, }; +export enum BoxDir { + NW, + N, + NE, + E, + SE, + S, + SW, + W, +} + +export const AllBoxDirs: BoxDir[] = [ + BoxDir.NW, + BoxDir.N, + BoxDir.NE, + BoxDir.E, + BoxDir.SE, + BoxDir.S, + BoxDir.SW, + BoxDir.W, +]; + export class Point implements PointLike { static CARDINAL: [dx: number, dy: number][] = [ [1, 0], @@ -33,6 +55,17 @@ export class Point implements PointLike { [-1, 0], [0, -1], ]; + static BOX: [dx: number, dy: number][] = [ + [-1, -1], + [0, -1], + [1, -1], + [1, 0], + [1, 1], + [0, 1], + [-1, 1], + [-1, 0], + ]; + x: number; y: number; @@ -60,6 +93,15 @@ export class Point implements PointLike { return this.xlate(dx, dy); } + inBoxDir(dir: BoxDir, rect?: Rect): Point | undefined { + const [dx, dy] = Point.BOX[dir]; + const p = this.xlate(dx, dy); + if (rect && !rect.check(p)) { + return undefined; + } + return p; + } + stretch(len: number): Point { return new Point(this.x * len, this.y * len); } @@ -88,6 +130,17 @@ export class Point implements PointLike { return ret; } + *box(r?: Rect): Generator<[Point, BoxDir], undefined, undefined> { + for (const dir of AllBoxDirs) { + const [dx, dy] = Point.BOX[dir]; + const p = this.xlate(dx, dy); + if (r && !r.check(p)) { + continue; + } + yield [p, dir]; + } + } + toString(): string { return `${this.x},${this.y}`; } @@ -124,6 +177,13 @@ export type RectEachCallback = ( r: Rect, ) => void; +export type RectFilterCallback = ( + value: T, + x: number, + y: number, + r: Rect, +) => boolean; + export class Rect { #vals: T[][]; @@ -319,6 +379,30 @@ export class Rect { return prev; } + filter(callbackFn: RectFilterCallback): Point[] { + const res: Point[] = []; + this.forEach((v, x, y) => { + if (callbackFn(v, x, y, this)) { + res.push(new Point(x, y)); + } + }); + return res; + } + + ray(origin: Point, dir: BoxDir, len: number): T[] { + const res: T[] = []; + let p: Point = origin; + for (let i = 0; i < len; i++) { + res.push(this.get(p)); + const q = p.inBoxDir(dir, this); + if (!q) { + break; + } + p = q; + } + return res; + } + /** * @returns The values in the rect. */ diff --git a/test/day4.js b/test/day4.js new file mode 100644 index 0000000..42a068d --- /dev/null +++ b/test/day4.js @@ -0,0 +1 @@ +export default [2569, 1998];