1/** 2 * 游æçå®é ç»åçæå¨æ¨¡å 3 * åè½ï¼æ ¹æ®å¡ç»ä¿¡æ¯åæ¡ä»¶çæç¬¦åæ¡ä»¶çå®é ç»å 4 * æ ¸å¿æ¹æ³ï¼generateCombinations() - æ´åæä¸¾ç»å 5 * ä¾èµï¼ui-utils.js æä¾çåéå转æ¢åè½ 6 */ 7(function (global) { 8 // çæå¨ç¶æåé 9 let generatorWorker = null; 10 let isGenerating = false; 11 let generationStartTime = 0; 12 let progressUpdateInterval = null; 13 let pauseTimes = [10, 30, 60, 90, 120, 180, 240, 300]; // æåæ¶é´èç¹(ç§) 14 let nextPauseIndex = 0; 15 let foundCombinations = 0; 16 17 // è·åå½åçæå·²ç¨ç§æ° 18 function getElapsedSeconds() { 19 return Math.floor((Date.now() - generationStartTime) / 1000); 20 } 21 22 // çæç»åä¸»å ¥å£ 23 function generateCombinations() { 24 if (isGenerating) { 25 alert("ç»åçææ£å¨è¿è¡ä¸ï¼è¯·ç¨å..."); 26 return; 27 } 28 try { 29 generationStartTime = Date.now(); 30 nextPauseIndex = 0; 31 foundCombinations = 0; 32 33 // æ£æ¥å¡åéå¤ 34 let cardNames = []; 35 let duplicateNames = new Set(); 36 for (let i = 0; i < 52; i++) { 37 const name = document.getElementById(`cardName${i}`).value.trim(); 38 if (name) { 39 if (cardNames.includes(name)) duplicateNames.add(name); 40 cardNames.push(name); 41 } 42 } 43 if (duplicateNames.size > 0) { 44 throw new Error(`å¡åéå¤ï¼${Array.from(duplicateNames).join(', ')}`); 45 } 46 47 // 读åå¡çæ°é 48 const cardCounts = []; 49 for (let i = 0; i < 52; i++) { 50 cardCounts.push(parseInt(document.getElementById(`card${i}`).value) || 0); 51 } 52 const draws = parseInt(document.getElementById('draws').value); 53 const deckSize = parseInt(document.getElementById('total').value); 54 if (draws <= 0) throw new Error("æ½å¡æ°å¿ 须大äº0"); 55 if (deckSize <= 0) throw new Error("å¡ç»ä¸è³å°è¦æ1å¼ å¡"); 56 if (draws > deckSize) throw new Error("æ½å¡æ°ä¸è½è¶ è¿å¡ç»æ»æ°"); 57 58 // è·åæ¡ä»¶è¡¨è¾¾å¼ 59 let condition = document.getElementById('condition').value.trim(); 60 if (!condition) throw new Error("请è¾å ¥é»è¾å¤ææ¡ä»¶"); 61 62 // å¡åæ å°ä¸ºåéå 63 const cardNameMap = {}; 64 const sortedNames = []; 65 for (let i = 0; i < 52; i++) { 66 const name = document.getElementById(`cardName${i}`).value.trim(); 67 if (name) { 68 cardNameMap[name] = global.UIUtils.getVarName(i); 69 sortedNames.push(name); 70 } 71 } 72 sortedNames.sort((a, b) => b.length - a.length); 73 for (const name of sortedNames) { 74 const regex = new RegExp(global.UIUtils.escapeRegExp(name), 'g'); 75 condition = condition.replace(regex, cardNameMap[name]); 76 } 77 78 // è·åçæé项 79 const combinationType = document.querySelector('input[name="combinationType"]:checked').value; 80 const combinationCount = parseInt(document.getElementById('combinationCount').value) || 10; 81 82 // å建Web Workeræ§è¡ç»åçæ 83 generatorWorker = new Worker(URL.createObjectURL(new Blob([` 84 // Workerå é¨ï¼ç»åçæä¸æ¡ä»¶éªè¯ 85 let deck = []; 86 let conditionFunc; 87 let combinationType; 88 let maxCombinations; 89 let draws; 90 // å·²åç°ï¼å¹¶å·²åéç»ä¸»çº¿ç¨ï¼çç»åå»ééåï¼ç»ç®æ¶ä¿çå¹¶è·³è¿ï¼é¿å éå¤è¾åº 91 let sentKeys = new Set(); 92 let sentCount = 0; 93 let shouldStop = false; 94 95 function varToIndex(varName) { 96 const lc = varName.toLowerCase(); 97 if (lc === 'true' || lc === 'false') return lc; 98 if (lc.length === 1) {
99 let code = lc.charCodeAt(0) - 97; 100 if (code >= 0 && code < 26) return code; 101 } 102 if (lc.length === 2 && lc[0] === 'a') { 103 let code = lc.charCodeAt(1) - 97; 104 if (code >= 0 && code < 26) return 26 + code; 105 } 106 throw new Error("æ æçå¡åç§°: " + varName); 107 } 108 109 function initialize(cardCountsArr, condition, type, count, drawCount) { 110 // æå»ºçç»æ°ç» 111 deck = []; 112 for (let i = 0; i < cardCountsArr.length; i++) { 113 for (let j = 0; j < cardCountsArr[i]; j++) { 114 deck.push(i); 115 } 116 } 117 draws = drawCount; 118 // å¤çPROB彿° 119 const probMatches = condition.match(/PROB\((\\d+(?:\.\d+)?)\)/g) || []; 120 const probValues = probMatches.map(m => parseFloat(m.match(/PROB\((\\d+(?:\.\d+)?)\)/)[1])); 121 122 // æå»ºåºç¡æ¡ä»¶è¡¨è¾¾å¼ 123 const baseCondition = condition 124 .replace(/PROB\((\\d+(?:\.\d+)?)\)/g, "__PROB__") 125 .replace(/\\b([a-z]{1,2})\\b/g, function(m) { 126 return (m === 'true' || m === 'false') ? m : "counts[" + varToIndex(m) + "]"; 127 }); 128 129 // ä¼åï¼æ eval æ¹ä¸ºä¸æ¬¡ç¼è¯ç new Function 130 // æ¯ä¸ª __PROB__ å ä½ç¬¦å±å¼ä¸ºç/å两ç§åæ¯ç表达å¼ï¼åå¨ä»»ä¸åæ¯æ»¡è¶³å³è§ä¸ºæ»¡è¶³ï¼ 131 function expandProbs(expr, index) { 132 if (index >= probValues.length) return expr; 133 const branchFalse = expandProbs(expr.replace('__PROB__', 'false'), index + 1); 134 const branchTrue = expandProbs(expr.replace('__PROB__', 'true'), index + 1); 135 return '(' + branchFalse + ') || (' + branchTrue + ')'; 136 } 137 const expanded = expandProbs(baseCondition, 0); 138 conditionFunc = new Function("counts", "return " + expanded); 139 140 combinationType = type; 141 maxCombinations = count; 142 } 143 144 // éå½ç»åæä¸¾ï¼ä» deck ä¸é draws 个ä½ç½®ï¼ç´¢å¼ä¸¥æ ¼éå¢ï¼ï¼è®¡æ°å»
144éï¼ææ¡ä»¶è¿æ»¤ 145 // ç»ç®ï¼reset=falseï¼æ¶ä¿ç sentKeysï¼éæ«æ¶è·³è¿å·²åéç»åï¼ä¸éå¤è¾åº 146 function generateUniqueCombinations() { 147 const result = []; 148 const currentCounts = Array(52).fill(0); 149 150 function backtrack(start, remaining) { 151 if (shouldStop) return; 152 if (remaining === 0) { 153 const key = currentCounts.join(','); 154 if (!sentKeys.has(key)) { 155 sentKeys.add(key); 156 const isValid = conditionFunc(currentCounts); 157 if ((combinationType === 'valid' && isValid) || 158 (combinationType === 'invalid' && !isValid)) { 159 result.push(currentCounts.slice()); 160 sentCount++; 161 postMessage({ 162 type: 'combination', 163 counts: currentCounts.slice(), 164 totalFound: sentCount 165 }); 166 } 167 } 168 return; 169 } 170 for (let i = start; i < deck.length; i++) { 171 currentCounts[deck[i]]++; 172 backtrack(i + 1, remaining - 1); 173 currentCounts[deck[i]]--; 174 if (sentCount >= maxCombinations) { shouldStop = true; return; } 175 if (shouldStop) return; 176 } 177 } 178 179 backtrack(0, draws); 180 return result; 181 } 182 183 onmessage = function(e) { 184 if (e.data.type === 'start') { 185 if (e.data.reset) { 186 // å ¨æ°å¼å§ï¼æ¸ 空已åéè®°å½ 187 sentKeys = new Set(); 188 sentCount = 0; 189 shouldStop = false; 190 initialize( 191 e.data.cardCounts, 192 e.data.condition, 193 e.data.combinationType, 194 e.data.combinationCount, 195 e.data.draws 196 ); 197 } else { 198 // ç»ç®ï¼ä¿ç sentKeys/sentCountï¼éç½®åæ¢æ å¿ï¼éæ«è·³è¿å·²åéç»å 199 shouldStop = false; 200 } 201 // è¥å·²è¾¾å°ç®æ æ°éï¼æ éç»§ç»æä¸¾ï¼ç´æ¥å®æ 202 if (sentCount >= maxCombinations) { 203 postMessage({ type: 'complete', totalFound: sentCount }); 204 return; 205 } 206 const result = generateUniqueCombinations(); 207 if (!shouldStop) { 208 postMessage({ type: 'complete', totalFound: sentCount }); 209 } 210 } else if (e.data.type === 'stop') { 211 shouldStop = true; 212 } 213 }; 214 `], { type: 'text/javascript' }))); 215 216 generatorWorker.onmessage = function (e) { 217 if (e.data.type === 'combination') { 218 foundCombinations = e.data.totalFound; 219 // å°ç»å转æ¢ä¸ºå¯è¯»æ ¼å¼å¹¶è¿½å å°ç»æææ¬æ¡ 220 const combinationText = formatCombination(e.data.counts, cardNames); 221 const resultsTextarea = document.getElementById('combinationResults'); 222 resultsTextarea.value += combinationText + '\n'; 223 resultsTextarea.scrollTop = resultsTextarea.scrollHeight; 224 } else if (e.data.type === 'complete') { 225 finalizeGeneration(); 226 } 227 }; 228 229 // å¯å¨è¿åº¦æ´æ°å®æ¶å¨ 230 progressUpdateInterval = setInterval(updateGenerationProgress, 1000); 231 232 // å¯å¨çæå¨ 233 generatorWorker.postMessage({ 234 type: 'start', 235 reset: true, // å ¨æ°å¼å§ï¼æ¸ 空 Worker å å·²åéç»åè®°å½ 236 cardCounts, 237 cardNames, 238 draws, 239 condition, 240 combinationType, 241 combinationCount 242 }); 243 244 isGenerating = true; 245 document.getElementById('combinationResults').value = ''; 246 } catch (error) { 247 showGenerationError(error.message); 248 } 249 } 250 251 // æ ¼å¼åç»å为å¯è¯»ææ¬ 252 function formatCombination(counts, cardNames) { 253 let result = []; 254 for (let i = 0; i < counts.length; i++) { 255 if (counts[i] > 0) { 256 const name = cardNames[i] || global.UIUtils.getVarName(i).toUpperCase() + 'ç±»å¡'; 257 result.push(`${name}Ã${counts[i]}`); 258 } 259 } 260 return result.join(' + ') || '空ç»å'; 261 } 262 263 // æ´æ°çæè¿åº¦ 264 function updateGenerationProgress() { 265 const elapsedSeconds = getElapsedSeconds(); 266 document.getElementById('progressText').textContent = 267 `çæä¸: å·²æ¾å° ${foundCombinations} ç»å ç¨æ¶: ${elapsedSeconds}ç§`; 268 269 // æ£æ¥æ¯å¦éè¦æå 270 if (nextPauseIndex < pauseTimes.length && elapsedSeconds >= pauseTimes[nextPauseIndex]) { 271 pauseGeneration(); 272 nextPauseIndex++; 273 } 274 } 275 276 // æåçæå¹¶è¯¢é®ç¨æ· 277 function pauseGeneration() { 278 if (!isGenerating) return; 279 280 generatorWorker.postMessage({ type: 'stop' }); 281 clearInterval(progressUpdateInterval); 282 progressUpdateInterval = null; 283 284 const elapsedSeconds = getElapsedSeconds(); 285 const shouldContinue = confirm( 286 `å·²çæ ${foundCombinations} 个ç»åï¼ç¨æ¶ ${elapsedSeconds} ç§ã\n` +
287 `å¯è½å·²æ æ´å¤ææç»åï¼æ¯å¦ç»§ç»çæï¼` 288 ); 289 290 if (shouldContinue) { 291 // ç»ç®ï¼ä¿ç Worker å å·²åéç»åè®°å½ï¼è·³è¿å·²çæç»åï¼ç»§ç»æä¸¾ 292 progressUpdateInterval = setInterval(updateGenerationProgress, 1000); 293 generatorWorker.postMessage({ type: 'start', reset: false }); 294 } else { 295 finalizeGeneration(); 296 } 297 } 298 299 // çæå®æåæ¸ ç 300 function finalizeGeneration() { 301 clearInterval(progressUpdateInterval); 302 progressUpdateInterval = null; 303 cleanupGeneration(); 304 305 const elapsedSeconds = getElapsedSeconds(); 306 const resultsTextarea = document.getElementById('combinationResults'); 307 resultsTextarea.value += `\nçæå®æ: å ±æ¾å° ${foundCombinations} ç»å ç¨æ¶: ${elapsedSeconds}ç§\n`; 308 document.getElementById('progressText').textContent = 'çæå®æ'; 309 } 310 311 // æ¾ç¤ºçæé误 312 function showGenerationError(message) { 313 clearInterval(progressUpdateInterval); 314 progressUpdateInterval = null; 315 cleanupGeneration(); 316 317 document.getElementById('combinationResults').value = 'çæé误: ' + message; 318 document.getElementById('progressText').textContent = 'çæé误'; 319 alert('çæé误: ' + message); 320 } 321 322 // æ¸ ççæç¶æ 323 function cleanupGeneration() { 324 isGenerating = false; 325 if (generatorWorker) { 326 generatorWorker.terminate(); 327 generatorWorker = null; 328 } 329 } 330 331 // åæ¶çæ 332 function cancelGeneration() { 333 if (!isGenerating) return; 334 335 generatorWorker.postMessage({ type: 'stop' }); 336 clearInterval(progressUpdateInterval); 337 progressUpdateInterval = null; 338 cleanupGeneration(); 339 340 const elapsedSeconds = getElapsedSeconds(); 341 document.getElementById('progressText').textContent = 342 `çæå·²åæ¶ ç¨æ¶: ${elapsedSeconds}ç§`; 343 alert("çæå·²åæ¶"); 344 } 345 346 // å¯¼åºæ¥å£ 347 global.CombinationGenerator = { 348 generateCombinations, 349 cancelGeneration 350 }; 351})(window);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.