-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathday10.js
126 lines (106 loc) · 3.65 KB
/
day10.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import { asLines } from './util.js';
const map = asLines('../input/day10.txt').map(line => line.split(''));
const findStart = (map) => {
return map.flatMap((row, y) => {
return row.flatMap((char, x) => char === 'S' ? [x, y] : []);
});
}
const start = findStart(map);
const directions = [ [0, -1], [0, 1], [-1, 0], [1, 0] ];
const tileConnections = {
'|': [ [0, -1], [0, 1] ],
'-': [ [-1, 0], [1, 0] ],
'L': [ [0, 1], [-1, 0] ],
'J': [ [0, 1], [1, 0] ],
'7': [ [0, -1], [1, 0] ],
'F': [ [0, -1], [-1, 0] ],
};
const isPossibleFrom = (direction, tile) => {
if (!tileConnections[tile]) {
return false;
}
return tileConnections[tile].find(([dx, dy]) => dx === direction[0] && dy === direction[1]);
}
const isPossibleTo = (direction, tile) => {
if (!tileConnections[tile]) {
return false;
}
return tileConnections[tile].find(([dx, dy]) => dx === -direction[0] && dy === -direction[1]);
}
const isPossibleMove = (map, [x, y], [dx, dy]) => {
const currentTile = map[y][x];
const nextTile = map[y+dy][x+dx];
return isPossibleTo([dx, dy], currentTile) && isPossibleFrom([dx, dy], nextTile);
}
const findNextLocation = (map, [x, y], visited) => {
return directions
.filter(([dx, dy]) => isPossibleMove(map, [x, y], [dx, dy])) // filter possible moves
.map(([dx, dy]) => [x + dx, y + dy]) // map move to coordinates
.find(([nx, ny]) => !visited.find(([x, y]) => x === nx && y === ny)); // accept not visisited
};
const buildPipe = (map, location) => {
const pipe = [];
while (location) {
pipe.push(location);
location = findNextLocation(map, location, pipe);
}
return pipe;
};
const fixStartTile = (map) => {
const [x, y] = findStart(map);
const tile = Object.entries(tileConnections).filter(([tile, connections]) => {
return connections.filter(([dx, dy]) => { // filter possible connections
const nextTile = map[y - dy][x - dx]; // get tile
return isPossibleTo([-dx, -dy], tile) && isPossibleFrom([-dx, -dy], nextTile);
}).length === 2; // accept only 2 connections
}).map(([tile]) => tile).find(() => true); // get filtered tile
map[y][x] = tile; // replace start tile
return map;
}
const fixedMap = fixStartTile(map);
const pipe = buildPipe(fixedMap, start, []);
console.log(`part1: ${pipe.length/2}`);
const isPipe = (x, y, pipe) => !!pipe.find(([px, py]) => px === x && py === y);
const horizontalCorners = [ 'L', 'J', '7', 'F' ];
const raycast = (x, y, pipe) => {
let crossings = 0;
let firstCorner;;
// from position to left, count pipe crossings
for (let dx = x; dx >= 0; dx--) {
if (!isPipe(dx, y, pipe)) continue;
const tile = map[y][dx];
if (tile === '|') { // vertical pipe is always crossing
crossings++;
continue;
}
if (horizontalCorners.includes(tile)) {
// for corners need to check if pipe is going vertical
if (!firstCorner) { // first corner
firstCorner = tile;
} else { // second corder
if (tileConnections[firstCorner][0][1] !== tileConnections[tile][0][1]) {
// vertical if start and end corner have different vertical connection direction
crossings++;
}
firstCorner = null;
}
}
}
return crossings;
};
const enclosedArea = (map, pipe) => {
let area = 0;
// loop through all points
for (let y = 0; y < map.length; y++) {
for (let x = 0; x < map[0].length; x++) {
if (isPipe(x, y, pipe)) {
continue; // point is on the pipe, skip
}
// odd number of raycast crossings means inside the pipe
area += raycast(x, y, pipe) % 2 ? 1 : 0;
}
}
return area;
};
const area = enclosedArea(fixedMap, pipe);
console.log(`part2: ${area}`);