🎲 퍼펙트 자리 찾기
자리를 섞는 모든 과정을 실시간으로 지켜보세요!
🚫 띄어쓰기로 짝꿍을 짓고, 엔터(줄바꿈)로 추가하세요!
새로운 자리 뽑기!
🔄 듀오가 열심히 자리를 섞는 중이에요... (기록이 실시간으로 아래에 뜹니다)
💻 [투명 공개] 자리 배치 소스 코드
function getCoord(idx) { if (idx < 24) return [Math.floor(idx / 6), idx % 6]; if (idx === 24) return [4, 2]; if (idx === 25) return [4, 3]; } function isAdjacent(idx1, idx2) { const [r1, c1] = getCoord(idx1); const [r2, c2] = getCoord(idx2); return Math.max(Math.abs(r1 - r2), Math.abs(c1 - c2)) <= 1; } // 최적화: 무작위 편향(Modulo Bias)을 수학적으로 완벽하게 제거 function secureShuffle(array) { const randomBuffer = new Uint32Array(1); for (let i = array.length - 1; i > 0; i--) { const maxLimit = 0xFFFFFFFF - (0xFFFFFFFF % (i + 1)); let r; do { window.crypto.getRandomValues(randomBuffer); r = randomBuffer[0]; } while (r >= maxLimit); const j = r % (i + 1); [array[i], array[j]] = [array[j], array[i]]; } } function parseEnemiesInput() { const rawText = document.getElementById('enemies-input').value; const lines = rawText.split('\n'); const enemies = []; for (let line of lines) { line = line.trim(); if (!line) continue; const parts = line.split(/[\s,]+/); if (parts.length >= 2) { enemies.push([parts[0], parts[1]]); } } return enemies; } function assignSeats() { const enemies = parseEnemiesInput(); document.getElementById('start-btn').disabled = true; document.getElementById('loading').style.display = 'block'; document.getElementById('result-box').style.display = 'block'; document.getElementById('result-title').innerText = "⏳ 섞는 중..."; document.getElementById('result-text').innerText = ""; setTimeout(async () => { let people = Array.from({length: 26}, (_, i) => String(i + 1)); let attempt = 1; // 최적화: 문자열 이어붙이기 대신 배열(Array)을 사용해 메모리 성능 극대화 let logArray = []; logArray.push(`=== 듀오 자리 배치 시작 (${new Date().toLocaleString()}) ===\n[이웃 불가 조건]: ${JSON.stringify(enemies)}\n\n`); const MAX_ATTEMPTS = 10000; // 최적화: 무한 루프 방지용 한계치 설정 while (attempt <= MAX_ATTEMPTS) { secureShuffle(people); // 최적화: 사람의 인덱스를 매번 찾지 않고 한 번에 매핑하여 속도 향상 let indexMap = {}; for(let i = 0; i < 26; i++) indexMap[people[i]] = i; let layout = []; for (let i = 0; i < 4; i++) { let row = people.slice(i * 6, (i + 1) * 6); layout.push(row.map(p => `[${p.padStart(2, ' ')}]`).join(" ")); } let lastRow = people.slice(24, 26); layout.push(" " + `[${lastRow[0].padStart(2, ' ')}] [${lastRow[1].padStart(2, ' ')}]`); let layoutText = layout.join("\n"); let conflictFound = null; for (let [p1, p2] of enemies) { let idx1 = indexMap[p1]; let idx2 = indexMap[p2]; if (idx1 !== undefined && idx2 !== undefined && isAdjacent(idx1, idx2)) { conflictFound = [p1, p2]; break; } } if (conflictFound) { logArray.push(`[시도 ${attempt}] ❌ 실패: '${conflictFound[0]}' & '${conflictFound[1]}' 이웃함\n${layoutText}\n\n`); // ✨ 핵심 개선: 브라우저가 멈추지 않도록 30번 시도마다 화면을 실시간으로 그려줌! if (attempt % 30 === 0) { document.getElementById('result-text').innerText = logArray.join(''); const resultBox = document.getElementById('result-box'); resultBox.scrollTop = resultBox.scrollHeight; await new Promise(r => setTimeout(r, 0)); // 브라우저 숨쉬기 딜레이 } attempt++; } else { logArray.push(`🎉 [시도 ${attempt}] ✅ 최종 성공!\n${layoutText}\n====================\n`); document.getElementById('result-title').innerText = `🌟 훌륭해요! (총 ${attempt}번 시도)`; document.getElementById('result-title').style.color = '#1cb0f6'; break; } } // 1만 번을 넘겨 실패한 경우의 예외 처리 if (attempt > MAX_ATTEMPTS) { logArray.push(`🚨 실패: 조건이 너무 까다로워 ${MAX_ATTEMPTS}번 시도 후 안전 중단했습니다!`); document.getElementById('result-title').innerText = `😢 조건이 너무 빡빡해요!`; document.getElementById('result-title').style.color = '#ff4b4b'; } // 최종 결과를 화면에 한 번 더 확실하게 렌더링 document.getElementById('result-text').innerText = logArray.join(''); const resultBox = document.getElementById('result-box'); resultBox.scrollTop = resultBox.scrollHeight; document.getElementById('loading').style.display = 'none'; document.getElementById('start-btn').disabled = false; }, 100); }