Skip to content

Commit 2e4531d

Browse files
committed
2024 day 12
1 parent 02681d4 commit 2e4531d

File tree

1 file changed

+124
-0
lines changed

1 file changed

+124
-0
lines changed

2024/day12/fences.js

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
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

Comments
 (0)