For a description of this problem, please check the Advent of Code website.
Example Input:
RRRRIICCFFRRRRIICCCFVVRRRCCFFFVVRCCCJFFFVVVVCJJCFEVVIVCCJJEEVVIIICJJEEMIIIIIJJEEMIIISIJEEEMMMISSJEEE
Part 1 Solution:
const fs = require('fs');const input = fs.readFileSync('./example-input.txt', 'utf8').trim().split('\r\n');const grid = input.map(line => line.split(''));const directions = [{ x: 0, y: -1 },{ x: 1, y: 0 },{ x: 0, y: 1 },{ x: -1, y: 0 },];function isInBounds(x, y) {return x >= 0 && y >= 0 && x < grid[0].length && y < grid.length;}function exploreRegion(startX, startY, symbol) {const regionSet = new Set();const stack = [{ x: startX, y: startY }];regionSet.add(`${startX},${startY}`);while (stack.length > 0) {const { x, y } = stack.pop();for (const { x: dx, y: dy } of directions) {const newX = x + dx;const newY = y + dy;if (isInBounds(newX, newY) && grid[newY][newX] === symbol && !regionSet.has(`${newX},${newY}`)) {regionSet.add(`${newX},${newY}`);stack.push({ x: newX, y: newY });}}}return regionSet;}function calculatePerimeter(regionSet) {let perimeter = 0;regionSet.forEach(point => {const [x, y] = point.split(',').map(Number);let adjacentCount = 0;for (const { x: dx, y: dy } of directions) {const newX = x + dx;const newY = y + dy;if (regionSet.has(`${newX},${newY}`)) {adjacentCount++;}}perimeter += (4 - adjacentCount);});return perimeter;}let totalPrice = 0;const visited = new Set();for (let y = 0; y < grid.length; y++) {for (let x = 0; x < grid[0].length; x++) {const symbol = grid[y][x];if (!visited.has(`${x},${y}`)) {const regionSet = exploreRegion(x, y, symbol);regionSet.forEach(point => visited.add(point));const area = regionSet.size;const perimeter = calculatePerimeter(regionSet);const price = area * perimeter;totalPrice += price;}}}console.log(totalPrice);
Part 2 Solution:
const fs = require('fs');const input = fs.readFileSync('./example-input.txt', 'utf8').trim().split('\r\n');const grid = input.map(line => line.split(''));const directions = [{ x: 0, y: -1 },{ x: 1, y: 0 },{ x: 0, y: 1 },{ x: -1, y: 0 },];function isInBounds(x, y) {return x >= 0 && y >= 0 && x < grid[0].length && y < grid.length;}function exploreRegion(startX, startY, symbol) {const regionSet = new Set();const stack = [{ x: startX, y: startY }];regionSet.add(`${startX},${startY}`);while (stack.length > 0) {const { x, y } = stack.pop();directions.forEach(direction => {const newX = x + direction.x;const newY = y + direction.y;if (isInBounds(newX, newY) && grid[newY][newX] === symbol && !regionSet.has(`${newX},${newY}`)) {regionSet.add(`${newX},${newY}`);stack.push({ x: newX, y: newY });}})}return regionSet;}function calculateSides(regionSet) {let totalSides = 0;regionSet.forEach(point => {const [x, y] = point.split(',').map(Number);let sides = 4;for (const { x: dx, y: dy } of directions) {const newX = x + dx;const newY = y + dy;if (regionSet.has(`${newX},${newY}`)) {sides--;}}const leftX = x - 1;if (regionSet.has(`${leftX},${y}`)) {if (!regionSet.has(`${leftX},${y - 1}`) && !regionSet.has(`${x},${y - 1}`)) {sides--;}if (!regionSet.has(`${leftX},${y + 1}`) && !regionSet.has(`${x},${y + 1}`)) {sides--;}}const aboveY = y - 1;if (regionSet.has(`${x},${aboveY}`)) {if (!regionSet.has(`${x - 1},${aboveY}`) && !regionSet.has(`${x - 1},${y}`)) {sides--;}if (!regionSet.has(`${x + 1},${aboveY}`) && !regionSet.has(`${x + 1},${y}`)) {sides--;}}totalSides += sides;});return totalSides;}let totalPrice = 0;const visited = new Set();for (let y = 0; y < grid.length; y++) {for (let x = 0; x < grid[0].length; x++) {const symbol = grid[y][x];if (!visited.has(`${x},${y}`)) {const regionSet = exploreRegion(x, y, symbol);regionSet.forEach(point => visited.add(point));const area = regionSet.size;const sides = calculateSides(regionSet);const price = area * sides;totalPrice += price;}}}console.log(totalPrice);