From 74280f129d0168313785e2b082ea64b70ac709e4 Mon Sep 17 00:00:00 2001 From: Shahar Talmi Date: Thu, 12 Dec 2024 10:38:25 +0200 Subject: [PATCH] add examples and refactor --- src/2024/day12.js | 116 ++++++++++++++++++----------------------- src/2024/day12.spec.js | 54 +++++++++++++++++++ 2 files changed, 105 insertions(+), 65 deletions(-) diff --git a/src/2024/day12.js b/src/2024/day12.js index 5c6c692e..bfc5b755 100644 --- a/src/2024/day12.js +++ b/src/2024/day12.js @@ -1,75 +1,61 @@ -// AAAA -// BBCD -// BBCC -// EEEC +function countSides(walls) { + let sides = 0; + walls.forEach(cells => { + const points = [...cells].sort((a, b) => a - b); + for (let i = 0; i < points.length; i++) { + if (points[i] + 1 !== points[i + 1]) sides++; + } + }); + return sides; +} + +function walk(map, x, y) { + const queue = [{ x, y }]; + const cells = new Set([`${x},${y}`]); + const walls = new Map(); + let perimeter = 0; + while (queue.length > 0) { + const p = queue.shift(); + const neighbors = [ + { x: p.x - 1, y: p.y }, + { x: p.x + 1, y: p.y }, + { x: p.x, y: p.y - 1 }, + { x: p.x, y: p.y + 1 }, + ]; + neighbors.forEach(o => { + if (map[o.y]?.[o.x] === map[y][x]) { + if (!cells.has(`${o.x},${o.y}`)) { + cells.add(`${o.x},${o.y}`); + queue.push(o); + } + } else { + perimeter++; + if (o.x === p.x) { + const wall = `h,${p.y},${o.y}`; + walls.set(wall, (walls.get(wall) || new Set()).add(p.x)); + } else { + const wall = `v,${p.x},${o.x}`; + walls.set(wall, (walls.get(wall) || new Set()).add(p.y)); + } + } + }); + } + return { cells, perimeter, sides: countSides(walls) }; +} export function part1(input, part2 = false) { - let regionId = 0; const map = input.split("\n").map(line => line.split("")); - const cell2region = new Map(); - const region2cells = new Map(); - const region2walls = new Map(); - const cell2perimeter = new Map(); + let visited = new Set(); + let sum = 0; for (let y = 0; y < map.length; y++) { for (let x = 0; x < map[y].length; x++) { - if (cell2region.has(`${x},${y}`)) continue; - const walls = new Map(); - regionId++; - region2walls.set(regionId, walls); - region2cells.set(regionId, new Set()); - let currentRegion = map[y][x]; - const queue = [{ x, y }]; - while (queue.length > 0) { - const p = queue.shift(); - cell2region.set(`${p.x},${p.y}`, regionId); - region2cells.get(regionId).add(`${p.x},${p.y}`); - const neighbors = [ - { x: p.x - 1, y: p.y }, - { x: p.x + 1, y: p.y }, - { x: p.x, y: p.y - 1 }, - { x: p.x, y: p.y + 1 }, - ]; - neighbors.forEach(o => { - if (map[o.y]?.[o.x] === currentRegion) { - if (!cell2region.has(`${o.x},${o.y}`)) { - cell2region.set(`${o.x},${o.y}`, regionId); - queue.push(o); - } - } else { - const perimeter = cell2perimeter.get(`${p.x},${p.y}`) || 0; - cell2perimeter.set(`${p.x},${p.y}`, perimeter + 1); - - if (o.x === p.x) { - let wall = `horizontal,${p.y},${o.y}`; - if (!walls.has(wall)) walls.set(wall, new Set()); - walls.get(wall).add(p.x); - } else { - let wall = `vertical,${p.x},${o.x}`; - if (!walls.has(wall)) walls.set(wall, new Set()); - walls.get(wall).add(p.y); - } - } - }); - } + if (visited.has(`${x},${y}`)) continue; + const { cells, perimeter, sides } = walk(map, x, y); + visited = visited.union(cells); + if (!part2) sum += cells.size * perimeter; + else sum += cells.size * sides; } } - let sum = 0; - for (const regionId of region2cells.keys()) { - const cells = region2cells.get(regionId); - const area = cells.size; - let perimeter = 0; - cells.forEach(cell => (perimeter += cell2perimeter.get(cell) || 0)); - const walls = region2walls.get(regionId); - let sides = 0; - walls.forEach(cells => { - const points = [...cells].sort((a, b) => a - b); - for (let i = 0; i < points.length; i++) { - if (points[i] + 1 !== points[i + 1]) sides++; - } - }); - if (!part2) sum += area * perimeter; - else sum += area * sides; - } return sum; } diff --git a/src/2024/day12.spec.js b/src/2024/day12.spec.js index efeee311..0103990a 100644 --- a/src/2024/day12.spec.js +++ b/src/2024/day12.spec.js @@ -7,6 +7,27 @@ describe("day12 2024", () => { describe("part1", () => { test("it should work for part 1 examples", () => { expect(part1(["AAAA", "BBCD", "BBCC", "EEEC"].join("\n"))).toEqual(140); + + expect( + part1(["OOOOO", "OXOXO", "OOOOO", "OXOXO", "OOOOO"].join("\n")), + ).toEqual(772); + + expect( + part1( + [ + "RRRRIICCFF", + "RRRRIICCCF", + "VVRRRCCFFF", + "VVRCCCJFFF", + "VVVVCJJCFE", + "VVIVCCJJEE", + "VVIIICJJEE", + "MIIIIIJJEE", + "MIIISIJEEE", + "MMMISSJEEE", + ].join("\n"), + ), + ).toEqual(1930); }); test("it should work for part 1 input", () => { @@ -17,6 +38,39 @@ describe("day12 2024", () => { describe("part2", () => { test("it should work for part 2 examples", () => { expect(part2(["AAAA", "BBCD", "BBCC", "EEEC"].join("\n"))).toEqual(80); + + expect( + part2(["OOOOO", "OXOXO", "OOOOO", "OXOXO", "OOOOO"].join("\n")), + ).toEqual(436); + + expect( + part2(["EEEEE", "EXXXX", "EEEEE", "EXXXX", "EEEEE"].join("\n")), + ).toEqual(236); + + expect( + part2( + ["AAAAAA", "AAABBA", "AAABBA", "ABBAAA", "ABBAAA", "AAAAAA"].join( + "\n", + ), + ), + ).toEqual(368); + + expect( + part2( + [ + "RRRRIICCFF", + "RRRRIICCCF", + "VVRRRCCFFF", + "VVRCCCJFFF", + "VVVVCJJCFE", + "VVIVCCJJEE", + "VVIIICJJEE", + "MIIIIIJJEE", + "MIIISIJEEE", + "MMMISSJEEE", + ].join("\n"), + ), + ).toEqual(1206); }); test("it should work for part 2 input", () => {