From 1ef581e1a66979d6e76ce84c8992c09d83158164 Mon Sep 17 00:00:00 2001 From: Sven Palberg Date: Fri, 20 Dec 2024 10:46:57 +0100 Subject: [PATCH 1/2] day 12 part 1 --- days/12/main.ts | 106 +++++++++++++++++++++++++++++++++++++++++++ days/12/main_test.ts | 37 +++++++++++++++ days/mod.ts | 2 +- inputs | 2 +- 4 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 days/12/main.ts create mode 100644 days/12/main_test.ts diff --git a/days/12/main.ts b/days/12/main.ts new file mode 100644 index 0000000..e516861 --- /dev/null +++ b/days/12/main.ts @@ -0,0 +1,106 @@ +import { Grid } from "@pal/datastructures"; +import { loadInput } from "utils"; + +export function part1(input: Array) { + let total = 0; + for (const region of extractRegions(input)) { + total += region.size * calculatePerimeter(region); + } + return total; +} + +export function part2(input: Array) { + let total = 0; + for (const region of extractRegions(input)) { + total += region.size * calculatePerimeter(region); + } + return total; +} + +type Coord = { x: number; y: number }; +type CoordId = string; +type Region = Set; + +function* extractRegions(input: Array): Generator { + const grid = Grid.fromStrings(input); + const regionsByType = new Map>(); + function addToRegion( + x: number, + y: number, + dx: number, + dy: number, + type: string, + regionsOfType: Set, + ): Region | null { + const neighbourType = grid.at(x + dx, y + dy); + if (neighbourType === type) { + for (const region of regionsOfType) { + if (region.has(coordId({ x: x + dx, y: y + dy }))) { + region.add(coordId({ x, y })); + return region; + } + } + } + return null; + } + for (let y = 0; y < grid.height; y++) { + for (let x = 0; x < grid.width; x++) { + const type = grid.at(x, y)!; + let regionsOfType = regionsByType.get(type); + if (regionsOfType == null) { + regionsOfType = new Set(); + regionsByType.set(type, regionsOfType); + } + const leftRegion = addToRegion(x, y, -1, 0, type, regionsOfType); + const rightRegion = addToRegion(x, y, 0, -1, type, regionsOfType); + if ( + leftRegion != null && rightRegion != null && leftRegion !== rightRegion + ) { + for (const coord of rightRegion) { + leftRegion.add(coord); + } + regionsOfType.delete(rightRegion); + } + if (leftRegion == null && rightRegion == null) { + regionsOfType.add(new Set([coordId({ x, y })])); + } + } + } + for (const regions of regionsByType.values()) { + yield* regions; + } +} + +function calculatePerimeter(region: Region): number { + let perimeter = 0; + for (const c of region) { + const { x, y } = fromCoordId(c); + if (region.has(coordId({ x: x - 1, y })) === false) { + perimeter++; + } + if (region.has(coordId({ x: x + 1, y })) === false) { + perimeter++; + } + if (region.has(coordId({ x, y: y - 1 })) === false) { + perimeter++; + } + if (region.has(coordId({ x, y: y + 1 })) === false) { + perimeter++; + } + } + return perimeter; +} + +function coordId(coord: Coord): CoordId { + return `${coord.x},${coord.y}`; +} + +function fromCoordId(coordId: CoordId): Coord { + const [x, y] = coordId.split(",").map(Number); + return { x, y }; +} + +if (import.meta.main) { + console.log(part1(await loadInput(12))); + console.log(part2(await loadInput(12))); +} diff --git a/days/12/main_test.ts b/days/12/main_test.ts new file mode 100644 index 0000000..1e823ad --- /dev/null +++ b/days/12/main_test.ts @@ -0,0 +1,37 @@ +import { expect } from "@std/expect"; +import { describe, it } from "@std/testing/bdd"; +import { part1, part2 } from "./main.ts"; +import { loadInput } from "utils"; + +describe("day 12 example", () => { + const input = [ + "RRRRIICCFF", + "RRRRIICCCF", + "VVRRRCCFFF", + "VVRCCCJFFF", + "VVVVCJJCFE", + "VVIVCCJJEE", + "VVIIICJJEE", + "MIIIIIJJEE", + "MIIISIJEEE", + "MMMISSJEEE", + ]; + + it("part 1", () => { + expect(part1(input)).toBe(1930); + }); + + it("part 2", () => { + expect(part2(input)).toBe(1206); + }); +}); + +describe("day 12 solution", () => { + it("part 1", async () => { + expect(part1(await loadInput(12))).toBe(1467094); + }); + + it.ignore("part 2", async () => { + expect(part2(await loadInput(12))).toBe(-1); + }); +}); diff --git a/days/mod.ts b/days/mod.ts index 620d2e1..b531c80 100644 --- a/days/mod.ts +++ b/days/mod.ts @@ -3,7 +3,7 @@ type Solution = { part1: PartFunction; part2: PartFunction }; const days = new Map(); -for (const day of [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) { +for (const day of [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]) { days.set(day, await import(`./${day}/main.ts`)); } diff --git a/inputs b/inputs index 94fe764..d325ce4 160000 --- a/inputs +++ b/inputs @@ -1 +1 @@ -Subproject commit 94fe7640ff0c710170a620f5596acd3738cf303d +Subproject commit d325ce4a9f73afd0b033d07cc1f2fde2ccef52e9 From 6c693b305c1eacc61f25dfbb666a3a74d747f569 Mon Sep 17 00:00:00 2001 From: Sven Palberg Date: Fri, 20 Dec 2024 14:54:54 +0100 Subject: [PATCH 2/2] day 12 part 2 --- days/12/main.ts | 56 +++++++++++++++++++++++++++++++++++++++++++- days/12/main_test.ts | 34 +++++++++++++++++++++++++-- 2 files changed, 87 insertions(+), 3 deletions(-) diff --git a/days/12/main.ts b/days/12/main.ts index e516861..472a072 100644 --- a/days/12/main.ts +++ b/days/12/main.ts @@ -12,7 +12,7 @@ export function part1(input: Array) { export function part2(input: Array) { let total = 0; for (const region of extractRegions(input)) { - total += region.size * calculatePerimeter(region); + total += region.size * calculateNumberOfSides(region); } return total; } @@ -91,6 +91,34 @@ function calculatePerimeter(region: Region): number { return perimeter; } +function calculateNumberOfSides(region: Region): number { + const verticalLeftSides = new Map>(); + const verticalRightSides = new Map>(); + const horizontalTopSides = new Map>(); + const horizontalBottomSides = new Map>(); + for (const c of region) { + const { x, y } = fromCoordId(c); + if (region.has(coordId({ x: x - 1, y })) === false) { + computeIfAbsent(verticalLeftSides, x, () => []).push(y); + } + if (region.has(coordId({ x: x + 1, y })) === false) { + computeIfAbsent(verticalRightSides, x, () => []).push(y); + } + if (region.has(coordId({ x, y: y - 1 })) === false) { + computeIfAbsent(horizontalTopSides, y, () => []).push(x); + } + if (region.has(coordId({ x, y: y + 1 })) === false) { + computeIfAbsent(horizontalBottomSides, y, () => []).push(x); + } + } + return [ + ...verticalLeftSides.values(), + ...verticalRightSides.values(), + ...horizontalTopSides.values(), + ...horizontalBottomSides.values(), + ].map(getNumberOfContinousBlocks).reduce((a, b) => a + b, 0); +} + function coordId(coord: Coord): CoordId { return `${coord.x},${coord.y}`; } @@ -100,6 +128,32 @@ function fromCoordId(coordId: CoordId): Coord { return { x, y }; } +function computeIfAbsent( + map: Map, + key: K, + defaultValue: () => V, +): V { + let value = map.get(key); + if (value == null) { + value = defaultValue(); + map.set(key, value); + } + return value; +} + +function getNumberOfContinousBlocks(numbers: Array) { + const sorted = numbers.toSorted(); + let count = 1; + let last = sorted[0]; + for (let i = 1; i < sorted.length; i++) { + if (sorted[i] !== last + 1) { + count++; + } + last = sorted[i]; + } + return count; +} + if (import.meta.main) { console.log(part1(await loadInput(12))); console.log(part2(await loadInput(12))); diff --git a/days/12/main_test.ts b/days/12/main_test.ts index 1e823ad..d13f334 100644 --- a/days/12/main_test.ts +++ b/days/12/main_test.ts @@ -24,6 +24,36 @@ describe("day 12 example", () => { it("part 2", () => { expect(part2(input)).toBe(1206); }); + + it("part 2 small example 1", () => { + expect(part2([ + "AAAAAA", + "AAABBA", + "AAABBA", + "ABBAAA", + "ABBAAA", + "AAAAAA", + ])).toBe(368); + }); + + it("part 2 small example 2", () => { + expect(part2([ + "EEEEE", + "EXXXX", + "EEEEE", + "EXXXX", + "EEEEE", + ])).toBe(236); + }); + + it("part 2 small example 3", () => { + expect(part2([ + "AAAA", + "BBCD", + "BBCC", + "EEEC", + ])).toBe(80); + }); }); describe("day 12 solution", () => { @@ -31,7 +61,7 @@ describe("day 12 solution", () => { expect(part1(await loadInput(12))).toBe(1467094); }); - it.ignore("part 2", async () => { - expect(part2(await loadInput(12))).toBe(-1); + it("part 2", async () => { + expect(part2(await loadInput(12))).toBe(881182); }); });