|
| 1 | +class Plot { |
| 2 | + /** @type {number} */ x; |
| 3 | + /** @type {number} */ y; |
| 4 | + /** @type {string} */ value; |
| 5 | + /** @type {Region} */ region; |
| 6 | + |
| 7 | + constructor(x, y, value) { |
| 8 | + this.x = x; |
| 9 | + this.y = y; |
| 10 | + this.value = value; |
| 11 | + this.region = null; |
| 12 | + } |
| 13 | +} |
| 14 | + |
| 15 | +class Region { |
| 16 | + /** @type {Set<Plot>} plots */ plots; |
| 17 | + |
| 18 | + constructor() { |
| 19 | + this.area = 0; |
| 20 | + this.perimeter = 0; |
| 21 | + this.sides = 0; |
| 22 | + this.plots = new Set(); |
| 23 | + } |
| 24 | +} |
| 25 | + |
| 26 | +const Farm = require("fs") |
| 27 | + .readFileSync("./input.txt", "utf-8") |
| 28 | + .split("\n") |
| 29 | + .map((r, y) => r.split("").map((c, x) => new Plot(x, y, c))), |
| 30 | + farmSize = Farm.length - 1; |
| 31 | + |
| 32 | +/** @type {Set<Region>} regions */ |
| 33 | +const regions = new Set(); |
| 34 | + |
| 35 | +// Processes all adjacent plots to define a region |
| 36 | +// Returns a list of adjacent plots from other regions |
| 37 | +function discoverRegion(/** @type {Plot} root */ root) { |
| 38 | + const foreignPlots = new Set(); |
| 39 | + |
| 40 | + /** @type {Plot[]} plotQueue */ |
| 41 | + let plotQueue = []; |
| 42 | + |
| 43 | + root.region = new Region(); |
| 44 | + regions.add(root.region); |
| 45 | + |
| 46 | + plotQueue.push(root); |
| 47 | + |
| 48 | + while (plotQueue.length > 0) { |
| 49 | + const plot = plotQueue.shift(); |
| 50 | + plot.region.area++; |
| 51 | + plot.region.plots.add(plot); |
| 52 | + |
| 53 | + let adjacents = []; |
| 54 | + |
| 55 | + const n = plot.y > 0 ? Farm[plot.y - 1][plot.x] : null; |
| 56 | + const s = plot.y < farmSize ? Farm[plot.y + 1][plot.x] : null; |
| 57 | + const w = plot.x > 0 ? Farm[plot.y][plot.x - 1] : null; |
| 58 | + const e = plot.x < farmSize ? Farm[plot.y][plot.x + 1] : null; |
| 59 | + |
| 60 | + adjacents.push(n, s, w, e); |
| 61 | + |
| 62 | + for (let i = 0; i < adjacents.length; i++) { |
| 63 | + if (!adjacents[i]) plot.region.perimeter++; |
| 64 | + else { |
| 65 | + if (adjacents[i].value !== plot.value) plot.region.perimeter++; |
| 66 | + if (adjacents[i].region === null) { |
| 67 | + if (adjacents[i].value === plot.value) { |
| 68 | + adjacents[i].region = root.region; |
| 69 | + plotQueue.push(adjacents[i]); |
| 70 | + } else foreignPlots.add(adjacents[i]); |
| 71 | + } |
| 72 | + } |
| 73 | + } |
| 74 | + } |
| 75 | + |
| 76 | + return [...foreignPlots]; |
| 77 | +} |
| 78 | + |
| 79 | +let plotsToProcess = discoverRegion(Farm[0][0]); |
| 80 | +while (plotsToProcess.length > 0) { |
| 81 | + const plotToProcess = plotsToProcess.shift(); |
| 82 | + |
| 83 | + if (plotToProcess.region === null) |
| 84 | + plotsToProcess.push(...discoverRegion(plotToProcess)); |
| 85 | +} |
| 86 | + |
| 87 | +for (const region of regions.values()) { |
| 88 | + for (const plot of region.plots.values()) { |
| 89 | + const dir = (x, y) => |
| 90 | + [...region.plots].filter((p) => p.x === x && p.y === y)[0]; |
| 91 | + |
| 92 | + let { x, y } = plot; |
| 93 | + const n = dir(x, y - 1), |
| 94 | + s = dir(x, y + 1), |
| 95 | + w = dir(x - 1, y), |
| 96 | + e = dir(x + 1, y), |
| 97 | + ne = dir(x + 1, y - 1), |
| 98 | + nw = dir(x - 1, y - 1), |
| 99 | + sw = dir(x - 1, y + 1), |
| 100 | + se = dir(x + 1, y + 1); |
| 101 | + |
| 102 | + // Basic corners |
| 103 | + if (!n && !w) region.sides++; |
| 104 | + if (!n && !e) region.sides++; |
| 105 | + if (!s && !w) region.sides++; |
| 106 | + if (!s && !e) region.sides++; |
| 107 | + |
| 108 | + // Inner corners |
| 109 | + if (n && w && !nw) region.sides++; |
| 110 | + if (n && e && !ne) region.sides++; |
| 111 | + if (s && w && !sw) region.sides++; |
| 112 | + if (s && e && !se) region.sides++; |
| 113 | + } |
| 114 | +} |
| 115 | + |
| 116 | +console.log( |
| 117 | + "Fence price:", |
| 118 | + [...regions].reduce((acc, region) => acc + region.area * region.perimeter, 0) |
| 119 | +); |
| 120 | + |
| 121 | +console.log( |
| 122 | + "Discounted price:", |
| 123 | + [...regions].reduce((acc, region) => acc + region.area * region.sides, 0) |
| 124 | +); |
0 commit comments