🎲 퍼펙트 자리 찾기! (개정증보판)
자리를 섞는 모든 과정을 실시간으로 지켜보세요!
🚫 띄어쓰기로 짝꿍을 짓고, 엔터(줄바꿈)로 추가하세요!
새로운 자리 뽑기!
🔄 듀오가 클라우드에서 자리를 섞는 중이에요...
💻 [투명 공개] 자리 배치 소스 코드
export default { async fetch(request, env, ctx) { const allowedOrigin = "https://seats.tehoha.com"; // 1. CORS 처리 (프론트엔드에서 API를 호출할 수 있도록 허용) if (request.method === "OPTIONS") { return new Response(null, { headers: { "Access-Control-Allow-Origin": allowedOrigin, "Access-Control-Allow-Methods": "POST, OPTIONS", "Access-Control-Allow-Headers": "Content-Type", } }); } if (request.method !== "POST") { return new Response("Method Not Allowed", { status: 405 }); } // 2. 프론트엔드에서 보낸 짝꿍 데이터 파싱 const body = await request.json(); const enemiesInput = body.enemiesText || ""; let enemies = []; let lines = enemiesInput.split("\n"); for (let line of lines) { line = line.trim(); if (!line) continue; let parts = line.split(/[\s,]+/); if (parts.length >= 2) enemies.push([parts[0], parts[1]]); } // 3. 자리 배치 핵심 로직 (기존 _gC, _iA, _sS 함수 대체) const getCoords = i => i < 24 ? [Math.floor(i / 6), i % 6] : (i === 24 ? [4, 2] : [4, 3]); const isAdjacent = (i, j) => { let [a, b] = getCoords(i), [c, d] = getCoords(j); return Math.max(Math.abs(a - c), Math.abs(b - d)) <= 1; }; // 암호학적으로 안전한 난수 생성기(CSPRNG)를 사용한 피셔-예이츠 셔플 알고리즘 const shuffle = a => { const validBuffer = new Uint32Array(a.length); crypto.getRandomValues(validBuffer); // 암호학적으로 안전한 난수 채우기 for (let c = a.length - 1; c > 0; c--) { let f = validBuffer[c] % (c + 1); [a[c], a[f]] = [a[f], a[c]]; } }; let seats = Array.from({ length: 26 }, (_, i) => String(i + 1)); let attempts = 1; let logs = []; // 한국 시간 기준으로 기록 const kstTime = new Date(new Date().getTime() + 9 * 60 * 60 * 1000).toISOString().replace('T', ' ').substring(0, 19); logs.push(`=== 듀오 자리 배치 시작 (${kstTime}) ===\n[이웃 불가 조건]: ${JSON.stringify(enemies)}\n\n`); const MAX_ATTEMPTS = 100; let success = false; // 4. 자리 섞기 반복문 while (attempts <= MAX_ATTEMPTS) { shuffle(seats); let seatMap = {}; for (let e = 0; e < 26; e++) seatMap[seats[e]] = e; let board = []; for (let e = 0; e < 4; e++) { let row = seats.slice(6 * e, 6 * (e + 1)); board.push(row.map(x => `[${x.padStart(2, " ")}]`).join(" ")); } let lastRow = seats.slice(24, 26); board.push(" " + `[${lastRow[0].padStart(2, " ")}] [${lastRow[1].padStart(2, " ")}]`); let boardStr = board.join("\n"); let conflict = null; for (let [e1, e2] of enemies) { let idx1 = seatMap[e1]; let idx2 = seatMap[e2]; if (idx1 !== undefined && idx2 !== undefined && isAdjacent(idx1, idx2)) { conflict = [e1, e2]; break; } } if (conflict) { logs.push(`[시도 ${attempts}] ❌ 실패: '${conflict[0]}' & '${conflict[1]}' 이웃함\n${boardStr}\n\n`); attempts++; } else { logs.push(`🎉 [시도 ${attempts}] ✅ 최종 성공!\n${boardStr}\n====================\n`); success = true; break; } } if (!success) { logs.push(`🚨 실패: 조건이 너무 까다로워 ${MAX_ATTEMPTS}번 시도 후 안전 중단했습니다!`); } const finalLogText = logs.join(""); // 5. 프론트엔드로 최종 결과 반환 return new Response(JSON.stringify({ success, attempts: success ? attempts : MAX_ATTEMPTS, logText: finalLogText }), { headers: { "Content-Type": "application/json", "Access-Control-Allow-Origin": allowedOrigin } }); } };