index.js (6411B)
1 const fs = require('fs'); 2 const through = require('through2'); 3 const line_by_line = require('../common/line-for-line'); 4 5 String.prototype.startsWith = function(subject) { 6 return this.substr(0, subject.length) === subject; 7 }; 8 9 function s(nodes, l) { 10 return nodes.map(n => [n[0]+2,n[1]+l+3]); 11 } 12 13 let cave = []; 14 // const cave = [ // Test pattern 15 // 0b000000000, 16 // 0b001110100, 17 // 0b000000000, 18 // 0b001000100, 19 // 0b001111100, 20 // 0b001000100, 21 // 0b000000000, 22 // 0b001111100, 23 // 0b000010000, 24 // 0b001111100, 25 // 0b000000000, 26 // ]; 27 const rocks = [ 28 [ 29 0b1111000, 30 ], 31 [ 32 0b010000, 33 0b111000, 34 0b010000, 35 ], 36 [ 37 0b111000, // Yes, it's flipped XY 38 0b100000, // LSB = left 39 0b100000, // i0 = bottom 40 ], 41 [ 42 0b1000, 43 0b1000, 44 0b1000, 45 0b1000, 46 ], 47 [ 48 0b11000, 49 0b11000, 50 ], 51 ]; 52 53 function printCave() { 54 let i = cave.length + 6; 55 while(--i >= 0) { 56 process.stdout.write('|'); 57 for(let x=1; x<=7; x++) { 58 if ( (rock[i - rp]|0) & (1<<x) ) { 59 process.stdout.write('@'); 60 } else if ( (cave[i]|0) & (1<<x) ) { 61 process.stdout.write('#'); 62 } else { 63 process.stdout.write('.'); 64 // process.stdout.write(' '); 65 } 66 } 67 process.stdout.write('|\n'); 68 } 69 process.stdout.write('+-------+\n'); 70 } 71 72 // How large should our cache key be 73 const lookback = 40; 74 75 // Fetch jet pattern 76 const pattern = fs.readFileSync('input', 'utf-8'); 77 // const pattern = fs.readFileSync('sample', 'utf-8'); 78 const jets = []; 79 for(const c of pattern.split('')) { 80 if (c == '>') jets.push(2); 81 if (c == '<') jets.push(0); 82 } 83 84 // Tracker for the next jet 85 let j = 0; 86 const jl = jets.length; 87 const J = pattern.trim().repeat(3); 88 89 // Rock statistics 90 let rockCount = 0; 91 // const rockTarget = 10000000000; 92 const rockTarget = 1000000000000; 93 // const rockTarget = 2022; 94 let rockHeight = 0; 95 96 97 // Tracker for falling rocks 98 let r = 0; 99 const rl = rocks.length; 100 let rp = 0; 101 let rock = false; 102 103 let storeCache = 0; 104 const cache = {}; 105 const cycle = jets.length * rocks.length; 106 let cc = 0; 107 108 // Time tracking to give an estimate & start 109 const startTime = Date.now(); 110 while(rockCount < rockTarget) { 111 112 let cacheKey = r.toString(16) + cave.map(l => ('0'+l.toString(16)).substr(-2)).join(''); 113 if (cache[cacheKey]) { 114 115 // Merge 2 cache keys, skipping a simulation step 116 const nextKey = cache[cacheKey].n; 117 if (cache[cache[cacheKey].n]) { 118 const nextKey = cache[cacheKey].n; 119 cache[cacheKey].cave = cache[nextKey].cave; 120 cache[cacheKey].j += cache[nextKey].j; 121 cache[cacheKey].c += cache[nextKey].c; 122 cache[cacheKey].h += cache[nextKey].h; 123 cache[cacheKey].n = cache[nextKey].n; 124 } 125 126 if (rockCount + cache[cacheKey].c <= rockTarget) { 127 cave = [...cache[cacheKey].cave]; 128 j = (j + cache[cacheKey].j) % jl; 129 r = (r + cache[cacheKey].c) % rl; 130 rockCount += cache[cacheKey].c; 131 rockHeight += cache[cacheKey].h; 132 cacheKey = cache[cacheKey].n; 133 continue; 134 } else { 135 // console.log("FAIL", cacheKey, cache[cacheKey]); 136 } 137 } else { 138 // console.log("MISS", cacheKey); 139 } 140 141 if (cc > cycle) { 142 storeCache = true; 143 } else { 144 cc += 1; 145 } 146 147 148 // let stateKey = r.toString(16); 149 // for(let i=0; i<cave.length; i++) { 150 // stateKey += ('0' + cave[i].toString(16)).substr(-2); 151 // stateKey += cave[i].toString(16); 152 // } 153 // if ((cave.length == lookback) && cache[stateKey]) { 154 // let hit = false; 155 // // console.log({ J, sub: J.substr(j, cache[stateKey].J.length), j: cache[stateKey].J }) 156 // if (J.substr(j, cache[stateKey].j) == cache[stateKey].J) { 157 // cave = [...cache[stateKey].cave]; 158 // j = (j + cache[stateKey].j) % jl; 159 // rockCount += 1; 160 // rockHeight += cache[stateKey].h; 161 // // console.log({ c: cache[stateKey] }); 162 // continue; 163 // } 164 // // printCave(); 165 // // continue; 166 // } 167 168 let rockJets = ''; 169 170 // if (cache[stateKey]) console.log(`Got state cache for ${stateKey} (${Object.values(cache).length})`); 171 // console.log( stateKey ); 172 // cache[stateKey] = true; 173 174 // Spawn new rock 175 rock = rocks[r]; 176 r = (r+1) % rl; 177 rp = cave.length + 3; 178 179 // console.log(`\nA new rock begins falling:`); 180 // printCave(); 181 182 while(rock) { 183 184 // Fetch jet movement 185 const jet = jets[j]; 186 j = (j+1) % jl; 187 rockJets += jet ? '>' : '<'; 188 189 // Calculate jet-based movement 190 let newRock = []; 191 for(let i=0; i<rock.length; i++) { 192 newRock[i] = (rock[i] >> 1) << jet; 193 } 194 195 // Wall collision 196 let hitWall = false; 197 for(let i=0; i<newRock.length; i++) { 198 const check = 1 | (cave[i+rp]|0) | 256; // Bitmap of current height 199 if (newRock[i] & check) { hitWall = true; break; } 200 } 201 202 // Move if possible 203 if (!hitWall) rock = newRock; 204 205 // console.log(`\nJet of gas pushes the rock ${jet ? 'right' : 'left'}${(rock === newRock) ? '' : ', but nothing happens'}:`); 206 // printCave(); 207 208 // Calculate gravity-based movement 209 rp--; 210 let hitBottom = rp < 0; // Floor check 211 if (!hitBottom) { 212 for(let i=0; i<rock.length; i++) { 213 const check = 1 | (cave[i+rp]|0) | 256; // Bitmap of current height 214 if (rock[i] & check) { hitBottom = true; break; } // Rock check 215 } 216 } 217 218 // Move or block 219 if (hitBottom) { 220 rp++; // Move back 221 222 // Solidify 223 const before = cave.length; 224 for(let i=0; i<rock.length; i++) { 225 if (!cave[rp+i]) cave[rp+i] = 0; // Initialize cave area 226 cave[rp+i] |= rock[i]; // Overlay rock 227 } 228 rock = false; 229 230 rockCount += 1; 231 const diff = cave.length - before; 232 rockHeight += diff; 233 234 cave = cave.slice(-lookback); 235 let newKey = (r%rl).toString(16) + cave.map(l => ('0'+l.toString(16)).substr(-2)).join(''); 236 237 if (storeCache) { 238 cache[cacheKey] = { 239 n: newKey, 240 c: 1, 241 cave: [...cave], 242 h: diff, 243 j: rockJets.length, 244 }; 245 } 246 247 248 // console.log(`\nRock falls 1 unit, causing it to come to rest:`); 249 // printCave(); 250 251 } else { 252 // console.log(`\nRock falls 1 unit:`); 253 // printCave(); 254 } 255 256 } 257 258 } 259 260 console.log(`\nStatus report:`); 261 console.log(` Rock count - ${rockCount}`); 262 console.log(` Height - ${rockHeight}`);