diff --git a/2023/day/22/part/1/solve.test.ts b/2023/day/22/part/1/solve.test.ts new file mode 100644 index 0000000..18184af --- /dev/null +++ b/2023/day/22/part/1/solve.test.ts @@ -0,0 +1,16 @@ +import solve from "./solve.ts"; + +import { assertEquals } from "../../../../../lib/testing/asserts.ts"; + +Deno.test("example", () => { + const input = `\ +1,0,1~1,2,1 +0,0,2~2,0,2 +0,2,3~2,2,3 +0,0,4~0,2,4 +2,0,5~2,2,5 +0,1,6~2,1,6 +1,1,8~1,1,9`; + + assertEquals(solve(input), 5); +}); diff --git a/2023/day/22/part/1/solve.ts b/2023/day/22/part/1/solve.ts new file mode 100644 index 0000000..13e3bc5 --- /dev/null +++ b/2023/day/22/part/1/solve.ts @@ -0,0 +1,68 @@ +type Vector = { x: number; y: number; z: number }; +type Brick = { label: string; bottomLeft: Vector; topRight: Vector }; + +export default function solve(input: string) { + const bricks = input.split("\n").map((line, index) => { + const label = String.fromCharCode("A".charCodeAt(0) + index); + const [a, b] = line.split("~").map((word) => { + const [x, y, z] = word.split(",").map(Number); + return { x, y, z }; + }); + return { + label, + bottomLeft: { + x: Math.min(a.x, b.x), + y: Math.min(a.y, b.y), + z: Math.min(a.z, b.z), + }, + topRight: { + x: Math.max(a.x, b.x), + y: Math.max(a.y, b.y), + z: Math.max(a.z, b.z), + }, + }; + }); + const bricksInFront = new Map>(); + bricks.sort(({ bottomLeft: { z: a } }, { bottomLeft: { z: b } }) => a - b); + const dependencies = bricks.reduce( + (map, brick) => map.set(brick, new Set()), + new Map>(), + ); + const dependents = bricks.reduce( + (map, brick) => map.set(brick, new Set()), + new Map>(), + ); + for (const brick of bricks) { + let falling = true; + while (falling && brick.bottomLeft.z > 1) { + for (let y = brick.bottomLeft.y; y <= brick.topRight.y; y++) { + if (!bricksInFront.has(y)) bricksInFront.set(y, new Map()); + const row = bricksInFront.get(y)!; + for (let x = brick.bottomLeft.x; x <= brick.topRight.x; x++) { + const brickBehind = row.get(x); + if (brickBehind === undefined) continue; + if (brick.bottomLeft.z - 1 > brickBehind.topRight.z) continue; + falling = false; + dependencies.get(brick)!.add(brickBehind); + dependents.get(brickBehind)!.add(brick); + } + } + if (falling) brick.bottomLeft.z--, brick.topRight.z--; + } + for (let y = brick.bottomLeft.y; y <= brick.topRight.y; y++) { + if (!bricksInFront.has(y)) bricksInFront.set(y, new Map()); + const row = bricksInFront.get(y)!; + for (let x = brick.bottomLeft.x; x <= brick.topRight.x; x++) { + row.set(x, brick); + } + } + } + return bricks + .filter((brick) => { + for (const dependent of dependents.get(brick)!) { + if (dependencies.get(dependent)!.size === 1) return false; + } + return true; + }) + .length; +} diff --git a/2023/day/22/part/2/solve.test.ts b/2023/day/22/part/2/solve.test.ts new file mode 100644 index 0000000..9f7d674 --- /dev/null +++ b/2023/day/22/part/2/solve.test.ts @@ -0,0 +1,16 @@ +import solve from "./solve.ts"; + +import { assertEquals } from "../../../../../lib/testing/asserts.ts"; + +Deno.test("example", () => { + const input = `\ +1,0,1~1,2,1 +0,0,2~2,0,2 +0,2,3~2,2,3 +0,0,4~0,2,4 +2,0,5~2,2,5 +0,1,6~2,1,6 +1,1,8~1,1,9`; + + assertEquals(solve(input), 7); +}); diff --git a/2023/day/22/part/2/solve.ts b/2023/day/22/part/2/solve.ts new file mode 100644 index 0000000..303cc96 --- /dev/null +++ b/2023/day/22/part/2/solve.ts @@ -0,0 +1,76 @@ +type Vector = { x: number; y: number; z: number }; +type Brick = { label: string; bottomLeft: Vector; topRight: Vector }; + +export default function solve(input: string) { + const bricks = input.split("\n").map((line, index) => { + const label = String.fromCharCode("A".charCodeAt(0) + index); + const [a, b] = line.split("~").map((word) => { + const [x, y, z] = word.split(",").map(Number); + return { x, y, z }; + }); + return { + label, + bottomLeft: { + x: Math.min(a.x, b.x), + y: Math.min(a.y, b.y), + z: Math.min(a.z, b.z), + }, + topRight: { + x: Math.max(a.x, b.x), + y: Math.max(a.y, b.y), + z: Math.max(a.z, b.z), + }, + }; + }); + const bricksInFront = new Map>(); + bricks.sort(({ bottomLeft: { z: a } }, { bottomLeft: { z: b } }) => a - b); + const dependencies = bricks.reduce( + (map, brick) => map.set(brick, new Set()), + new Map>(), + ); + const dependents = bricks.reduce( + (map, brick) => map.set(brick, new Set()), + new Map>(), + ); + for (const brick of bricks) { + let falling = true; + while (falling && brick.bottomLeft.z > 1) { + for (let y = brick.bottomLeft.y; y <= brick.topRight.y; y++) { + if (!bricksInFront.has(y)) bricksInFront.set(y, new Map()); + const row = bricksInFront.get(y)!; + for (let x = brick.bottomLeft.x; x <= brick.topRight.x; x++) { + const brickBehind = row.get(x); + if (brickBehind === undefined) continue; + if (brick.bottomLeft.z - 1 > brickBehind.topRight.z) continue; + falling = false; + dependencies.get(brick)!.add(brickBehind); + dependents.get(brickBehind)!.add(brick); + } + } + if (falling) brick.bottomLeft.z--, brick.topRight.z--; + } + for (let y = brick.bottomLeft.y; y <= brick.topRight.y; y++) { + if (!bricksInFront.has(y)) bricksInFront.set(y, new Map()); + const row = bricksInFront.get(y)!; + for (let x = brick.bottomLeft.x; x <= brick.topRight.x; x++) { + row.set(x, brick); + } + } + } + let sum = -0; + for (const brick of bricks) { + const fallenBricks = new Set().add(brick); + for (const fallenBrick of fallenBricks) { + falling: for (const dependent of dependents.get(fallenBrick)!) { + const dependentDependencies = dependencies.get(dependent)!; + for (const dependentDependency of dependentDependencies) { + if (fallenBricks.has(dependentDependency)) continue; + continue falling; + } + fallenBricks.add(dependent); + } + } + sum += fallenBricks.size - 1; + } + return sum; +}