Skip to content

Commit

Permalink
Merge pull request #14 from hildjj/day4
Browse files Browse the repository at this point in the history
Day 4
  • Loading branch information
hildjj authored Dec 4, 2024
2 parents e54faad + aa81540 commit 76028ab
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 1 deletion.
1 change: 1 addition & 0 deletions day4.peggy
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
lines = (@[^\n]+ "\n")*
50 changes: 50 additions & 0 deletions day4.ts
Original file line number Diff line number Diff line change
@@ -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<Parsed>(args);
return [part1(inp), part2(inp)];
}
2 changes: 1 addition & 1 deletion inputs
84 changes: 84 additions & 0 deletions lib/rect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,46 @@ export const OppositeDir: Record<Dir, Dir> = {
[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],
[0, 1],
[-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;

Expand Down Expand Up @@ -60,6 +93,15 @@ export class Point implements PointLike {
return this.xlate(dx, dy);
}

inBoxDir<T>(dir: BoxDir, rect?: Rect<T>): 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);
}
Expand Down Expand Up @@ -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}`;
}
Expand Down Expand Up @@ -124,6 +177,13 @@ export type RectEachCallback<T> = (
r: Rect<T>,
) => void;

export type RectFilterCallback<T> = (
value: T,
x: number,
y: number,
r: Rect<T>,
) => boolean;

export class Rect<T = string> {
#vals: T[][];

Expand Down Expand Up @@ -319,6 +379,30 @@ export class Rect<T = string> {
return prev;
}

filter(callbackFn: RectFilterCallback<T>): 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.
*/
Expand Down
1 change: 1 addition & 0 deletions test/day4.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default [2569, 1998];

0 comments on commit 76028ab

Please sign in to comment.