|
| 1 | +const fs = require('fs') |
| 2 | +const path = require('path') |
| 3 | +const { off } = require('process') |
| 4 | + |
| 5 | +let input = fs.readFileSync(path.join(__dirname, 'input.txt'), { encoding: 'utf8' }) |
| 6 | + |
| 7 | +function parseMapIntoList(input) { |
| 8 | + return input |
| 9 | + .split('\n') |
| 10 | + .flatMap( |
| 11 | + (l, row) => l.split('') |
| 12 | + .map((c, col) => { return { c: c, x: col, y: row } })) |
| 13 | +} |
| 14 | + |
| 15 | +function printMap(map, move = '@') { |
| 16 | + let dim = map.reduce((p, c) => [ |
| 17 | + Math.min(p[0], c.x), |
| 18 | + Math.max(p[1], c.x), |
| 19 | + Math.min(p[2], c.y), |
| 20 | + Math.max(p[3], c.y)], [Infinity, -Infinity, Infinity, -Infinity]) |
| 21 | + |
| 22 | + let mapLines = [] |
| 23 | + for (let y = dim[2]; y <= dim[3]; y++) { |
| 24 | + let line = [] |
| 25 | + for (let x = dim[0]; x <= dim[1]; x++) { |
| 26 | + let point = map.find(p => p.x == x && p.y == y) |
| 27 | + if (point) { |
| 28 | + line.push(point.c == '@' ? move : point.c) |
| 29 | + } else { |
| 30 | + line.push('.') |
| 31 | + } |
| 32 | + } |
| 33 | + mapLines.push(line.join('')) |
| 34 | + } |
| 35 | + console.log(mapLines.join('\n')) |
| 36 | +} |
| 37 | + |
| 38 | + |
| 39 | +const directions = { |
| 40 | + '>': { x: 1, y: 0 }, |
| 41 | + '<': { x: -1, y: 0 }, |
| 42 | + 'v': { x: 0, y: 1 }, |
| 43 | + '^': { x: 0, y: -1 }, |
| 44 | +} |
| 45 | + |
| 46 | +function executeMoves(map, moves) { |
| 47 | + let robot = map.find(p => p.c == '@') |
| 48 | + for (const move of moves) { |
| 49 | + let offset = directions[move] |
| 50 | + let newPosition = { x: robot.x + offset.x, y: robot.y + offset.y } |
| 51 | + let newPositionPoint = map.find(p => p.x == newPosition.x && p.y == newPosition.y) |
| 52 | + if (!newPositionPoint) { |
| 53 | + robot.x = newPosition.x |
| 54 | + robot.y = newPosition.y |
| 55 | + } else if (newPositionPoint.c == '#') { |
| 56 | + continue |
| 57 | + } else if (newPositionPoint.c == 'O') { |
| 58 | + let boxes = [newPositionPoint] |
| 59 | + let lookAhead |
| 60 | + do { |
| 61 | + let lookAheadPos = { x: robot.x + offset.x * (boxes.length + 1), y: robot.y + offset.y * (boxes.length + 1) } |
| 62 | + lookAhead = map.find(p => p.x == lookAheadPos.x && p.y == lookAheadPos.y) |
| 63 | + if (!lookAhead || lookAhead.c != 'O') { |
| 64 | + break |
| 65 | + } |
| 66 | + boxes.push(lookAhead) |
| 67 | + } while (true) |
| 68 | + if (!lookAhead) { |
| 69 | + for (const box of boxes) { |
| 70 | + box.x += offset.x |
| 71 | + box.y += offset.y |
| 72 | + } |
| 73 | + robot.x += offset.x |
| 74 | + robot.y += offset.y |
| 75 | + } |
| 76 | + } else { |
| 77 | + throw "Confused" |
| 78 | + } |
| 79 | + } |
| 80 | + |
| 81 | + return robot |
| 82 | +} |
| 83 | + |
| 84 | +function executeMoves2(map, moves) { |
| 85 | + let robot = map.find(p => p.c == '@') |
| 86 | + for (const move of moves) { |
| 87 | + let offset = directions[move] |
| 88 | + let newPosition = { x: robot.x + offset.x, y: robot.y + offset.y } |
| 89 | + let newPositionPoint = map.find(p => p.x == newPosition.x && p.y == newPosition.y) |
| 90 | + if (!newPositionPoint) { |
| 91 | + robot.x = newPosition.x |
| 92 | + robot.y = newPosition.y |
| 93 | + } else if (newPositionPoint.c == '#') { |
| 94 | + continue |
| 95 | + } else if (newPositionPoint.c == '[' || newPositionPoint.c == ']') { |
| 96 | + let boxes = [] |
| 97 | + if (newPositionPoint.c == '[') { |
| 98 | + boxes.push(newPositionPoint, map.find(p => p.x == newPositionPoint.x + 1 && p.y == newPositionPoint.y && p.c == ']')) |
| 99 | + } else { |
| 100 | + boxes.push(newPositionPoint, map.find(p => p.x == newPositionPoint.x - 1 && p.y == newPositionPoint.y && p.c == '[')) |
| 101 | + } |
| 102 | + |
| 103 | + if (move == '<' || move == '>') { |
| 104 | + |
| 105 | + let lookAhead |
| 106 | + do { |
| 107 | + let lookAheadPos = { x: robot.x + offset.x * (boxes.length + 1), y: robot.y + offset.y * (boxes.length + 1) } |
| 108 | + lookAhead = map.find(p => p.x == lookAheadPos.x && p.y == lookAheadPos.y) |
| 109 | + if (!lookAhead || (lookAhead.c != '[' && lookAhead.c != ']')) { |
| 110 | + break |
| 111 | + } |
| 112 | + boxes.push(lookAhead) |
| 113 | + } while (true) |
| 114 | + |
| 115 | + if (!lookAhead) { |
| 116 | + for (const box of boxes) { |
| 117 | + box.x += offset.x |
| 118 | + box.y += offset.y |
| 119 | + } |
| 120 | + robot.x += offset.x |
| 121 | + robot.y += offset.y |
| 122 | + } |
| 123 | + |
| 124 | + } else { |
| 125 | + // GOIN UP or DOWN and have to find boxes in a different way |
| 126 | + |
| 127 | + let line = newPosition.y |
| 128 | + let lookAhead |
| 129 | + do { |
| 130 | + let lookAheadPosList = boxes.filter(b => b && b.y == line) |
| 131 | + line += offset.y |
| 132 | + lookAhead = lookAheadPosList.map(b => map.find(p => p.x == b.x && p.y == line)) |
| 133 | + //has only empty space or no parts of boxes |
| 134 | + if (lookAhead.every(p => !p) || lookAhead.some(p => p && p.c == '#')) { |
| 135 | + break |
| 136 | + } |
| 137 | + // add all boxes |
| 138 | + let newLookAhead = [] |
| 139 | + |
| 140 | + for (const point of lookAhead) { |
| 141 | + // add missing parts for boxes |
| 142 | + newLookAhead.push(point) |
| 143 | + if (point) { |
| 144 | + if (point.c == '[') { |
| 145 | + if (!lookAhead.find(p => p && p.x == point.x + 1 && p.c == ']')) { |
| 146 | + let boxPart = map.find(p => point.y == p.y && p.x == point.x + 1 && p.c == ']') |
| 147 | + if (!boxPart) throw `Missing box part for ${point}` |
| 148 | + newLookAhead.push(boxPart) |
| 149 | + } |
| 150 | + } else if (point.c == ']') { |
| 151 | + if (!lookAhead.find(p => p && p.x == point.x - 1 && p.c == '[')) { |
| 152 | + let boxPart = map.find(p => point.y == p.y && p.x == point.x - 1 && p.c == '[') |
| 153 | + if (!boxPart) throw `Missing box part for ${point}` |
| 154 | + newLookAhead.push(boxPart) |
| 155 | + } |
| 156 | + } |
| 157 | + } |
| 158 | + } |
| 159 | + |
| 160 | + boxes.push(...newLookAhead) |
| 161 | + } while (true) |
| 162 | + |
| 163 | + if (lookAhead.every(p => !p)) { |
| 164 | + for (const box of boxes) { |
| 165 | + if (box) { |
| 166 | + box.x += offset.x |
| 167 | + box.y += offset.y |
| 168 | + } |
| 169 | + } |
| 170 | + robot.x += offset.x |
| 171 | + robot.y += offset.y |
| 172 | + } |
| 173 | + |
| 174 | + } |
| 175 | + } else { |
| 176 | + console.log(newPositionPoint) |
| 177 | + throw "Confused" |
| 178 | + } |
| 179 | + } |
| 180 | +} |
| 181 | + |
| 182 | +function evaluateGPS(map, point = 'O') { |
| 183 | + return map.filter(p => p.c == point).reduce((p, c) => p + (c.x + 100 * c.y), 0) |
| 184 | +} |
| 185 | + |
| 186 | +// input = `####### |
| 187 | +// #...#.# |
| 188 | +// #.....# |
| 189 | +// #...O.# |
| 190 | +// #..O..# |
| 191 | +// #..OO@# |
| 192 | +// #..O..# |
| 193 | +// #.....# |
| 194 | +// ####### |
| 195 | + |
| 196 | +// <vv<<^^<<^^` |
| 197 | + |
| 198 | +// input = `########## |
| 199 | +// #..O..O.O# |
| 200 | +// #......O.# |
| 201 | +// #.OO..O.O# |
| 202 | +// #..O@..O.# |
| 203 | +// #O#..O...# |
| 204 | +// #O..O..O.# |
| 205 | +// #.OO.O.OO# |
| 206 | +// #....O...# |
| 207 | +// ########## |
| 208 | + |
| 209 | +// <vv>^<v^>v>^vv^v>v<>v^v<v<^vv<<<^><<><>>v<vvv<>^v^>^<<<><<v<<<v^vv^v>^ |
| 210 | +// vvv<<^>^v^^><<>>><>^<<><^vv^^<>vvv<>><^^v>^>vv<>v<<<<v<^v>^<^^>>>^<v<v |
| 211 | +// ><>vv>v^v^<>><>>>><^^>vv>v<^^^>>v^v^<^^>v^^>v^<^v>v<>>v^v^<v>v^^<^^vv< |
| 212 | +// <<v<^>>^^^^>>>v^<>vvv^><v<<<>^^^vv^<vvv>^>v<^^^^v<>^>vvvv><>>v^<<^^^^^ |
| 213 | +// ^><^><>>><>^^<<^^v>>><^<v>^<vv>>v>>>^v><>^v><<<<v>>v<v<v>vvv>^<><<>^>< |
| 214 | +// ^>><>^v<><^vvv<^^<><v<<<<<><^v<<<><<<^^<v<^^^><^>>^<v^><<<^>>^v<v^v<v^ |
| 215 | +// >^>>^v>vv>^<<^v<>><<><<v<<v><>v<^vv<<<>^^v^>^^>>><<^v>>v^v><^^>>^<>vv^ |
| 216 | +// <><^^>^^^<><vvvvv^v<v<<>^v<v>v<<^><<><<><<<^^<<<^<<>><<><^^^>^^<>^>v<> |
| 217 | +// ^^>vv<^v^v<vv>^<><v<^v>^^^>>>^^vvv^>vvv<>>>^<^>>>>>^<<^v>^vvv<>^<><<v> |
| 218 | +// v^^>>><<^^<>>^v^<v^vv<>v^<<>^<^v^v><^<<<><<^<v><v<>vv>>v><v^<vv<>v^<<^` |
| 219 | + |
| 220 | +// input = ` |
| 221 | +// ######################## |
| 222 | +// ##..............@.....## |
| 223 | +// ##..##....[]...[].....## |
| 224 | +// ##[]...........[].[]..## |
| 225 | +// ##..####[]..[][][][]..## |
| 226 | +// ##....[][]..[]....[]..## |
| 227 | +// ######........##[][]..## |
| 228 | +// ##............[][][]..## |
| 229 | +// ##..[][][][]......[]..## |
| 230 | +// ##....................## |
| 231 | +// ######################## |
| 232 | + |
| 233 | +// vvv |
| 234 | +// ` |
| 235 | + |
| 236 | +let [mapInput, movesInput] = input.split('\n\n') |
| 237 | + |
| 238 | +let map = parseMapIntoList(mapInput).filter(p => p.c != '.') |
| 239 | +let moves = movesInput.split('\n').flatMap(l => l.split('')) |
| 240 | + |
| 241 | +executeMoves(map, moves) |
| 242 | + |
| 243 | +console.log( |
| 244 | + evaluateGPS(map) |
| 245 | +) |
| 246 | + |
| 247 | +let expansion = { |
| 248 | + '#': ['#', '#'], |
| 249 | + '.': ['.', '.'], |
| 250 | + 'O': ['[', ']'], |
| 251 | + '@': ['@', '.'] |
| 252 | +} |
| 253 | + |
| 254 | +let newMapInput = mapInput |
| 255 | + .split('\n') |
| 256 | + .map(line => line.split('').flatMap(x => expansion[x]).join('')) |
| 257 | + .join('\n') |
| 258 | + |
| 259 | +let map2 = parseMapIntoList(newMapInput).filter(p => p.c != '.') |
| 260 | + |
| 261 | +executeMoves2(map2, moves) |
| 262 | + |
| 263 | +console.log( |
| 264 | + evaluateGPS(map2, '[') |
| 265 | +) |
0 commit comments