Skip to content

Commit

Permalink
Merge pull request #26 from hildjj/day12
Browse files Browse the repository at this point in the history
Day 12
  • Loading branch information
hildjj authored Dec 13, 2024
2 parents b2fd91d + cf73545 commit 24f3ce6
Show file tree
Hide file tree
Showing 5 changed files with 149 additions and 3 deletions.
2 changes: 2 additions & 0 deletions day12.peggy
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
lines = (@[a-z]i+ "\n")*

114 changes: 114 additions & 0 deletions day12.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { BoxDir, Point, PointSet, Rect } from './lib/rect.ts';
import { type MainArgs, parseFile } from './lib/utils.ts';

type Parsed = string[][];

function flood(
r: Rect,
val: string,
p: Point,
visited: PointSet,
found: PointSet,
): number {
if (visited.has(p)) {
return 0;
}
visited.add(p);
found.add(p);
let count = 4;
for (const q of p.cardinal(r)) {
if (r.get(q) === val) {
count--; // Once from each side
count += flood(r, val, q, visited, found);
}
}
return count;
}

function part1(inp: Parsed): number {
const r = new Rect(inp);
const visited = new PointSet();
let tot = 0;
r.forEach((v, x, y) => {
const found = new PointSet();
const p = new Point(x, y);
const perim = flood(r, v, p, visited, found);
if (found.size > 0) {
tot += found.size * perim;
}
});
return tot;
}

function sides(found: PointSet): number {
// Number of sides = number of corners

// This code could be compressed and sped up by creating a table with 256
// entries. Left as an exercise.

let tot = 0;
for (const p of found) {
const box = p.boxSet(found);
if (!box.has(BoxDir.N) && !box.has(BoxDir.W)) {
// .. X.
// .X .X
tot++;
} else if (!box.has(BoxDir.NW) && box.has(BoxDir.N) && box.has(BoxDir.W)) {
// .X
// XX
tot++;
}

if (!box.has(BoxDir.N) && !box.has(BoxDir.E)) {
// .. .X
// X. X.
tot++;
} else if (!box.has(BoxDir.NE) && box.has(BoxDir.N) && box.has(BoxDir.E)) {
// X.
// XX
tot++;
}

if (!box.has(BoxDir.S) && !box.has(BoxDir.E)) {
// X. X.
// .. .X
tot++;
} else if (!box.has(BoxDir.SE) && box.has(BoxDir.S) && box.has(BoxDir.E)) {
// XX
// X.
tot++;
}

if (!box.has(BoxDir.S) && !box.has(BoxDir.W)) {
// .X .X
// .. X.
tot++;
} else if (!box.has(BoxDir.SW) && box.has(BoxDir.S) && box.has(BoxDir.W)) {
// XX
// .X
tot++;
}
}

return tot;
}

function part2(inp: Parsed): number {
const r = new Rect(inp);
const visited = new PointSet();
let tot = 0;
r.forEach((v, x, y) => {
const found = new PointSet();
const p = new Point(x, y);
flood(r, v, p, visited, found);
if (found.size > 0) {
tot += found.size * sides(found);
}
});
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
33 changes: 31 additions & 2 deletions lib/rect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,17 +138,42 @@ export class Point implements PointLike {
return ret;
}

*box(r?: Rect): Generator<[Point, BoxDir], undefined, undefined> {
*box(r?: Rect | PointSet): 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)) {
if (r && !r.has(p)) {
continue;
}
yield [p, dir];
}
}

boxMap(r?: Rect | PointSet): Map<BoxDir, Point> {
const ret = new Map<BoxDir, Point>();
for (const dir of AllBoxDirs) {
const [dx, dy] = Point.BOX[dir];
const p = this.xlate(dx, dy);
if (r && !r.has(p)) {
continue;
}
ret.set(dir, p);
}
return ret;
}

boxSet(r: Rect | PointSet): Set<BoxDir> {
const ret = new Set<BoxDir>();
for (const dir of AllBoxDirs) {
const [dx, dy] = Point.BOX[dir];
const p = this.xlate(dx, dy);
if (r.has(p)) {
ret.add(dir);
}
}
return ret;
}

toString(): string {
return `${this.x},${this.y}`;
}
Expand Down Expand Up @@ -266,6 +291,10 @@ export class Rect<T = string> {
(x >= 0) && (x < this.#vals[y].length);
}

has(p: PointLike): boolean {
return this.check(p);
}

/**
* Assume that the rectangle is uniform, so the length of the first row
* is the width.
Expand Down
1 change: 1 addition & 0 deletions test/day12.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default [1371306, 805880];

0 comments on commit 24f3ce6

Please sign in to comment.