index.js (6301B)
1 const fs = require('fs'); 2 3 function drawMap(map) { 4 for(const row of map) { 5 for(let i=0; i<row.length; i++) { 6 process.stdout.write(directionChars[row[i]] || ' '); 7 } 8 process.stdout.write('\n'); 9 } 10 } 11 12 function mod(n, m) { 13 return ((n%m)+m)%m; 14 } 15 16 function move(position, movement) { 17 return [ 18 position[0] + movement[0], 19 position[1] + movement[1], 20 ]; 21 } 22 23 // Fetch map 24 const lines = fs.readFileSync('input', 'utf-8') 25 // const lines = fs.readFileSync('sample', 'utf-8') 26 .trimEnd() 27 .split('\n'); 28 29 // Fetch directions 30 const path = lines.pop() 31 .match(/(\d+|L|R)/g) 32 .map(stmt => isNaN(stmt) ? stmt : parseInt(stmt)) 33 lines.pop(); 34 35 let mapwidth = lines 36 .map(line => line.length) 37 .reduce((r,a) => Math.max(r,a), 0) 38 39 // Map map into something usable 40 for(let i=0; i<lines.length; i++) { 41 lines[i] = lines[i] 42 .split('') 43 .map(c => ({'.':4,'#':5}[c])); 44 } 45 46 // Define movements of directions 47 const directions = [ 48 [ 1, 0 ], // right 49 [ 0, 1 ], // down 50 [ -1, 0 ], // left 51 [ 0, -1 ], // up 52 ]; 53 const directionChars = ['>','v','<','^','.','#']; 54 55 // How to wrap for step 1 56 function positionStep1(map, position, movement, fallback) { 57 fallback = fallback || position; 58 let newpos = [...position]; 59 let stat = lines[newpos[1]][newpos[0]]; 60 61 // Move until non-undefined 62 do { 63 newpos = move(newpos, movement); 64 newpos[1] = mod(newpos[1], lines.length); 65 newpos[0] = mod(newpos[0], mapwidth); 66 stat = lines[newpos[1]][newpos[0]]; 67 } while('undefined' === typeof stat); 68 69 // Return fallback if blocked 70 if (stat == 5) return [...fallback]; 71 // Return new position, as it's available 72 return newpos; 73 } 74 75 const Dir = { 76 left: '-10', 77 right: '10', 78 up: '0-1', 79 down: '01', 80 }; 81 82 // Cube shape 83 // A-----B-----C 84 // | | | 85 // | a | b | 86 // | | | 87 // D-----E-----F 88 // | | 89 // | c | 90 // | | 91 // D-----G-----F 92 // | | | 93 // | d | e | 94 // | | | 95 // A-----H-----C 96 // | | 97 // | f | 98 // | | 99 // B-----C 100 101 // Calculate wrapping method for step 2 102 // Splits into 9 surfaces (manual edge wrapping) 103 const surfaceSize = lines[0].length / 3; 104 const x=0, y=1; 105 function positionStep2(map, position, movement) { 106 let newpos = move(position, movement); 107 let aDir = movement.join(''); 108 let newdir = directions.indexOf(movement); 109 110 // AB, a > f 111 if (position[x] >= 50 && position[x] < 100 && position[y] == 0 && aDir == Dir.up) { 112 newpos = [0, position[x] - 50 + 150]; 113 newdir = 0; // right 114 } 115 // BC, b > f 116 if (position[x] >= 100 && position[x] < 150 && position[y] == 0 && aDir == Dir.up) { 117 newpos = [ position[x] - 100, 199 ]; 118 newdir = 3; // up 119 } 120 // AD, a > d 121 if (position[x] == 50 && position[y] < 50 && aDir == Dir.left) { 122 newpos = [ 0, 149 - position[y] ]; 123 newdir = 0; // right 124 } 125 // CF, b > e 126 if (position[x] == 149 && position[y] < 50 && aDir == Dir.right) { 127 newpos = [position[x] - 50, 149 - position[y]]; 128 newdir = 2; // left 129 } 130 // EF, b > c 131 if (position[x] >= 100 && position[y] == 49 && aDir == Dir.down) { 132 newpos = [99, position[x] - 50]; 133 newdir = 2; // left 134 } 135 // DG, c > d 136 if (position[x] == 50 && position[y] >= 50 && position[y] < 100 && aDir == Dir.left) { 137 newpos = [position[y] - 50, 100]; 138 newdir = 1; // down 139 } 140 // EF, c > b 141 if (position[x] == 99 && position[y] >= 50 && position[y] < 100 && aDir == Dir.right) { 142 newpos = [position[y] + 50, 49]; 143 newdir = 3; // up 144 } 145 // DG, d > c 146 if (position[x] < 50 && position[y] == 100 && aDir == Dir.up) { 147 newpos = [50, position[x] + 50]; 148 newdir = 0; // right 149 } 150 // AD, d > a 151 if (position[x] == 0 && position[y] >= 100 && position[y] < 150 && aDir == Dir.left) { 152 newpos = [50, 149 - position[y]]; 153 newdir = 0; // right 154 } 155 // CF, e > b 156 if (position[x] == 99 && position[y] >= 100 && position[y] < 150 && aDir == Dir.right) { 157 newpos = [149, 149 - position[y]]; 158 newdir = 2; // left 159 } 160 // CH, e > f 161 if (position[x] >= 50 && position[x] < 100 && position[y] == 149 && aDir == Dir.down) { 162 newpos = [49, position[x] + 100]; 163 newdir = 2; // left 164 } 165 // AB, f > a 166 if (position[x] == 0 && position[y] >= 150 && aDir == Dir.left) { 167 newpos = [position[y] - 100, 0]; 168 newdir = 1; // down 169 } 170 // CH, f > e 171 if (position[x] == 49 && position[y] >= 150 && aDir == Dir.right) { 172 newpos = [position[y] - 100, 149]; 173 newdir = 3; // up 174 } 175 // BC, f > b 176 if (position[x] < 50 && position[y] == 199 && aDir == Dir.down) { 177 newpos = [position[x] + 100, 0]; 178 newdir = 1; // down 179 } 180 181 // Revert if we ran into a wall 182 const stat = lines[newpos[y]][newpos[x]]; 183 if (stat == 5) { 184 newpos = [...position]; 185 newdir = directions.indexOf(movement); 186 } 187 188 return [newpos, newdir]; 189 } 190 191 192 193 // Define our character's position 194 const characters = [ 195 // { 196 // pos: [ 197 // Math.min( 198 // lines[0].indexOf(4), 199 // lines[0].indexOf(5), 200 // ), 201 // 0, 202 // ], 203 // dir: 0, 204 // mv() { 205 // this.pos = positionStep1(lines, this.pos, directions[this.dir]); 206 // } 207 // }, 208 { 209 pos: [ 210 Math.min( 211 lines[0].indexOf(4), 212 lines[0].indexOf(5), 213 ), 214 0, 215 ], 216 surface: surfaceSize == 12 ? 217 [ 2, 0 ] : 218 [ 1, 0 ], 219 dir: 0, 220 mv() { 221 let [pos,dir] = positionStep2(lines, this.pos, directions[this.dir]); 222 this.pos = pos; 223 this.dir = dir; 224 } 225 }, 226 ]; 227 228 // Go over the instructions 229 for(let stmt of path) { 230 for(const character of characters) { 231 lines[character.pos[1]][character.pos[0]] = character.dir; 232 if(isNaN(stmt)) { 233 const c = stmt == 'R' ? 1 : -1; 234 character.dir = mod(character.dir + c, directions.length); 235 lines[character.pos[1]][character.pos[0]] = character.dir; 236 } else { 237 // Move along 238 for(let i=0; i<stmt; i++) { 239 character.mv(); 240 lines[character.pos[1]][character.pos[0]] = character.dir; 241 } 242 } 243 } 244 } 245 246 drawMap(lines); 247 248 // Output characters and their password 249 for(let character of characters) { 250 console.log({ character, password: ((character.pos[1]+1)*1000) + ((character.pos[0]+1)*4) + character.dir }); 251 } 252 253 // let position = [ 254 // 0 255 // ]; 256 // let direction = 0; 257 258 259 260 261 262 // process.stdout.write(input);