122 lines
		
	
	
		
			3.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			122 lines
		
	
	
		
			3.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| import { readFileSync } from 'node:fs';
 | |
| 
 | |
| const input = readFileSync('input', 'utf-8');
 | |
| 
 | |
| const cubes = input
 | |
|   .split('\n')
 | |
|   .filter(line => !!line.trim())
 | |
|   .map(coords => {
 | |
|     const [x, y, z] = coords.split(',').map(n => parseInt(n));
 | |
|     return {x, y, z};
 | |
|   });
 | |
| 
 | |
| const surfaceArea = cubes.map(cubeA => {
 | |
|   const touchingCubes = cubes.filter(cubeB => {
 | |
|     const xDiff = Math.abs(cubeA.x - cubeB.x);
 | |
|     const yDiff = Math.abs(cubeA.y - cubeB.y);
 | |
|     const zDiff = Math.abs(cubeA.z - cubeB.z);
 | |
| 
 | |
|     return (
 | |
|       (xDiff === 1 && yDiff === 0 && zDiff === 0) ||
 | |
|       (yDiff === 1 && zDiff === 0 && xDiff === 0) ||
 | |
|       (zDiff === 1 && xDiff === 0 && yDiff === 0)
 | |
|     );
 | |
|   });
 | |
| 
 | |
|   const nonConnectedSides = 6 - touchingCubes.length;
 | |
|   return nonConnectedSides;
 | |
| }).reduce((total, sides) => total + sides);
 | |
| 
 | |
| console.log(`The surface area of the droplet is: ${surfaceArea}`);
 | |
| 
 | |
| function minMax(dimension) {
 | |
|   return [
 | |
|     cubes.reduce((min, coords) => Math.min(min, coords[dimension]), Infinity),
 | |
|     cubes.reduce((max, coords) => Math.max(max, coords[dimension]), -Infinity),
 | |
|   ];
 | |
| }
 | |
| 
 | |
| function isCoordEqual(coordA, coordB) {
 | |
|   return coordA.every((v, i) => coordB[i] === v);
 | |
| }
 | |
| 
 | |
| function uniqueCoordsFilter(coordA, i, coords) {
 | |
|   return coords.findLastIndex(coordB => isCoordEqual(coordA, coordB)) === i;
 | |
| }
 | |
| 
 | |
| function getAllConnected(startX, startY, startZ, isConnected) {
 | |
|   const connected = [];
 | |
|   let toVisit = [[startX, startY, startZ]];
 | |
| 
 | |
|   while (true) {
 | |
|     connected.push(...toVisit);
 | |
|     toVisit = toVisit.flatMap(([x, y, z]) => [
 | |
|       [x + 1, y,     z    ],
 | |
|       [x - 1, y,     z    ],
 | |
|       [x,     y + 1, z    ],
 | |
|       [x,     y - 1, z    ],
 | |
|       [x,     y,     z + 1],
 | |
|       [x,     y,     z - 1]
 | |
|     ])
 | |
|       .filter(coordA =>
 | |
|         !connected.find(coordB => isCoordEqual(coordA, coordB))
 | |
|       )
 | |
|       .filter(uniqueCoordsFilter)
 | |
|       .filter(([x, y, z]) => isConnected(x, y, z));
 | |
| 
 | |
|     if (!toVisit.length) {
 | |
|       break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return connected;
 | |
| }
 | |
| 
 | |
| const [minX, maxX] = minMax('x');
 | |
| const [minY, maxY] = minMax('y');
 | |
| const [minZ, maxZ] = minMax('z');
 | |
| 
 | |
| // Get all coordinates external to droplet with a border of 2
 | |
| const connected = getAllConnected(minX - 2, minY - 2, minZ - 2, (x, y, z) => {
 | |
|   return (
 | |
|     x >= minX - 2 &&
 | |
|     x <= maxX + 2 &&
 | |
|     y >= minY - 2 &&
 | |
|     y <= maxY + 2 &&
 | |
|     z >= minZ - 2 &&
 | |
|     z <= maxZ + 2 &&
 | |
|     // Not part of the droplet
 | |
|     !cubes.find(coord => coord.x === x && coord.y === y && coord.z === z)
 | |
|   );
 | |
| });
 | |
| 
 | |
| // Calculate internal surface area of inverted droplet
 | |
| const externalSurfaceArea = connected.map(([ax, ay, az]) => {
 | |
|   // Skip coordinates right on the edge
 | |
|   if (
 | |
|     ax < minX - 1 || ax > maxX + 1 ||
 | |
|     ay < minY - 1 || ay > maxY + 1 ||
 | |
|     az < minZ - 1 || az > maxZ + 1
 | |
|   ) {
 | |
|     return 0;
 | |
|   }
 | |
| 
 | |
|   const touchingCubes = connected.filter(([bx, by, bz]) => {
 | |
|     const xDiff = Math.abs(ax - bx);
 | |
|     const yDiff = Math.abs(ay - by);
 | |
|     const zDiff = Math.abs(az - bz);
 | |
| 
 | |
|     return (
 | |
|       (xDiff === 1 && yDiff === 0 && zDiff === 0) ||
 | |
|       (yDiff === 1 && zDiff === 0 && xDiff === 0) ||
 | |
|       (zDiff === 1 && xDiff === 0 && yDiff === 0)
 | |
|     );
 | |
|   });
 | |
|   const nonConnectedSides = 6 - touchingCubes.length;
 | |
|   return nonConnectedSides;
 | |
| }).reduce((total, sides) => total + sides);
 | |
| 
 | |
| console.log(
 | |
|   `The EXTERNAL surface area of the droplet is: ${externalSurfaceArea}`
 | |
| );
 |