자유 게시판

전체보기

모바일 상단 메뉴

본문 페이지

[잡담] 저장용 보2지마

토맛토마토
조회: 239
2026-09-02 05:14:35
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>상급 아비도스 제작 계산기</title>
    <style>
        /* [스타일 정의] 다크 테마 및 고정 레이아웃 */
        :root {
            --bg-color: #2d2d2d;
            --tab-bg: #3c3c3c;
            --input-bg: #505050;
            --text-color: #ffffff;
            --label-color: #d3d3d3;
            --btn-color: #007acc;
            --btn-hover: #005f9e;
            --result-ok: #ffffff;
            --result-no: #fa8072;
        }

        body {
            background-color: #1e1e1e;
            color: var(--text-color);
            font-family: 'Malgun Gothic', 'Segoe UI', sans-serif;
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            margin: 0;
        }

        /* 메인 컨테이너 (창 크기 고정) */
        .container {
            width: 500px;
            height: 600px; /* 고정 높이 */
            background-color: var(--bg-color);
            border: 1px solid #444;
            border-radius: 12px;
            box-shadow: 0 10px 20px rgba(0,0,0,0.5);
            display: flex;
            flex-direction: column;
            overflow: hidden; /* 크기 고정 */
        }

        /* 탭 영역 */
        .tabs {
            display: flex;
            height: 35px;
            background-color: #1e1e1e;
            flex-shrink: 0;
        }

        .tab {
            flex: 1;
            background-color: var(--bg-color);
            color: #888;
            border: none;
            outline: none;
            cursor: pointer;
            font-size: 14px;
            transition: background 0.2s;
            border-right: 1px solid #444;
            border-bottom: 1px solid #444;
            padding: 0;
        }

        .tab.active {
            background-color: var(--tab-bg);
            color: #fff;
            border-bottom: none;
            font-weight: bold;
        }

        .tab:last-child {
            border-right: none;
        }

        /* 입력 영역 (스크롤 가능) */
        .input-area {
            flex: 1; /* 남은 공간 차지 */
            padding: 20px;
            background-color: var(--tab-bg);
            overflow-y: auto;
            display: flex;
            flex-direction: column;
            gap: 15px;
        }

        /* 입력 필드 레이아웃 (Grid 사용) */
        .input-group {
            display: grid;
            grid-template-columns: 150px 1fr;
            align-items: center;
            gap: 10px;
        }

        .input-group label {
            color: var(--label-color);
            font-size: 14px;
            text-align: left;
        }

        .input-group input {
            background-color: var(--input-bg);
            border: 1px solid #666;
            border-radius: 12px;
            color: white;
            padding: 8px;
            font-size: 14px;
            outline: none;
            width: 100%;
            box-sizing: border-box; /* 패딩 포함 크기 계산 */
        }

        .input-group input:focus {
            border-color: var(--btn-color);
        }

        /* 버튼 영역 */
        .btn-area {
            padding: 25px 0;
            flex-shrink: 0;
            display: flex;
            justify-content: center;
        }

        .calculate-btn {
            width: 40%;
            height: 50px;
            background-color: var(--btn-color);
            color: white;
            border: none;
            border-radius: 12px;
            font-size: 18px;
            font-weight: bold;
            cursor: pointer;
            transition: background 0.2s;
        }

        .calculate-btn:hover {
            background-color: var(--btn-hover);
        }

        /* 결과 영역 (완벽한 중앙 정렬) */
        .result-area {
            flex: 1; /* 하단 남은 공간 채우기 */
            display: flex;
            justify-content: center; /* 가로 중앙 */
            align-items: center;     /* 세로 중앙 */
            text-align: center;
            padding: 20px;
            white-space: pre-line; /* 줄바꿈 허용 */
            font-size: 15px;
            line-height: 1.6;
            background-color: var(--bg-color);
            overflow-y: auto;
        }

        .result-text {
            color: #ffffff;
        }
        
        .result-ok { color: var(--result-ok); }
        .result-no { color: var(--result-no); }

    </style>
</head>
<body>

    <div class="container">
        <div class="tabs" id="tabContainer">
            </div>

        <div class="input-area" id="inputContainer">
            </div>

        <div class="btn-area">
            <button class="calculate-btn" onclick="calculate()">계산하기 (Calculate)</button>
        </div>

        <div class="result-area">
            <div id="resultText" class="result-text">재료를 입력하고 계산 버튼을 눌러주세요.</div>
        </div>
    </div>

    <script>
        // ==========================================
        // [1] 데이터 및 설정
        // ==========================================
        const MATERIAL_TYPES = {
            WL: { name: "벌목", A: "원목", B: "부드러운 목재", S: null, C: "아비도스 원목", P: "벌목의 흔적" },
            AR: { name: "고고학", A: "오래된 고대 유물", B: "은은한 고대 유물", S: null, C: "아비도스 고대 유물", P: "고고학의 흔적" },
            FS: { name: "낚시", A: "싱싱한 피라미", B: "튼튼한 붕어", S: null, C: "아비도스 금빛 잉어", P: "낚시의 흔적" },
            MN: { name: "채광", A: "투박한 철광석", B: "천연 보석", S: null, C: "아비도스 원석", P: "채광의 흔적" },
            HR: { name: "수렵", A: "생고기", B: "고급 생고기", S: null, C: "아비도스 농장 생고기", P: "수렵의 흔적" },
            CL: { name: "채집", A: "소박한 꽃잎", B: "귀여운 꽃잎", S: null, C: "아비도스 꽃잎", P: "채집의 흔적" }
        };

        const inputState = {
            WL: { A: "", B: "", S: "", C: "", P: "" },
            AR: { A: "", B: "", S: "", C: "", P: "" },
            FS: { A: "", B: "", S: "", C: "", P: "" },
            MN: { A: "", B: "", S: "", C: "", P: "" },
            HR: { A: "", B: "", S: "", C: "", P: "" },
            CL: { A: "", B: "", S: "", C: "", P: "" }
        };

        let currentType = 'WL';

        // 교환비 (입력 단위 -> 출력 단위)
        const RATES = {
            StoA: { input: 5n, output: 50n },
            BtoA: { input: 25n, output: 50n },
            AtoP: { input: 100n, output: 80n }, // 100개 넣어야 80개 나옴
            BtoP: { input: 50n, output: 80n },
            PtoC: { input: 100n, output: 10n } // 100개 넣어야 10개 나옴
        };
        const RECIPE = { A: 112n, B: 59n, C: 43n };

        // ==========================================
        // [2] 핵심 로직 (이진 탐색 + 묶음 단위 적용)
        // ==========================================
        function runAlgorithm(type, inputs) {
            const canExchangeSpecial = (type === 'WL' || type === 'MN');
            
            // BigInt 변환 (빈 문자열은 0n으로 처리)
            const res = {
                A: BigInt(inputs.A || 0),
                B: BigInt(inputs.B || 0),
                S: BigInt(inputs.S || 0),
                C: BigInt(inputs.C || 0),
                P: BigInt(inputs.P || 0)
            };

            let low = 0n;
            // 충분히 큰 상한값 설정
            let maxA = res.A + (res.B * 2n) + (res.C * 20n);
            let high = (maxA / RECIPE.A) + 2000000000n;

            let bestOutput = 0n;
            let bestLog = {};

            // 이진 탐색 시작
            while (low <= high) {
                let mid = low + (high - low) / 2n;
                if (mid < 0n) break;

                const check = canProduce(mid, { ...res }, canExchangeSpecial);
                if (check.possible) {
                    bestOutput = mid;
                    bestLog = check.log;
                    low = mid + 1n; // 더 큰 값을 찾아봄
                } else {
                    high = mid - 1n; // 불가능하므로 범위를 줄임
                }
            }

            return { output: bestOutput, logs: bestLog };
        }

        // 100개 단위 묶음(Batch) 교환 규칙을 엄격하게 적용한 검사 함수
        function canProduce(targetCount, inputRes, canExchangeSpecial) {
            let log = { StoA: 0n, BtoA: 0n, AtoP: 0n, BtoP: 0n, PtoC: 0n };
            if (targetCount === 0n) return { possible: true, log };

            let res = { ...inputRes }; // 자원 복사본 사용

            // 1. S -> A 변환 (5개 단위)
            if (canExchangeSpecial && res.S > 0n) {
                let batches = res.S / RATES.StoA.input; // 몫만 취함 (자투리 버림)
                log.StoA = batches;
                res.A += batches * RATES.StoA.output;
            }

            // 2. 목표 제작을 위한 필요량 계산
            let reqA = targetCount * RECIPE.A;
            let reqB = targetCount * RECIPE.B;
            let reqC = targetCount * RECIPE.C;

            // [검증 1] B 재료 절대량 부족 시 불가
            if (res.B < reqB) return { possible: false, log };

            // [검증 2] 부족한 C를 채우기 위한 가루(P) 계산 (100개 단위 묶음 적용)
            let missingC = 0n;
            if (res.C < reqC) {
                missingC = reqC - res.C;
            }

            let neededP = 0n;
            if (missingC > 0n) {
                // C 10개를 얻기 위해 P 100개가 필요함
                let outputPerBatch = RATES.PtoC.output; // 10
                let inputPerBatch = RATES.PtoC.input;   // 100
                
                // 필요 묶음 수 올림 계산: (부족분 + 9) / 10
                let batchesC = (missingC + outputPerBatch - 1n) / outputPerBatch;
                neededP = batchesC * inputPerBatch; // 실제 필요한 가루 양 (100의 배수)
                
                log.PtoC = batchesC;
            }

            // 보유 가루(P) 차감
            if (res.P >= neededP) {
                res.P -= neededP;
                neededP = 0n;
            } else {
                neededP -= res.P;
                res.P = 0n;
            }

            // [검증 3] 남은 재료를 모두 A로 환산하여 최종 비교 (묶음 단위 적용)
            
            // 3-1. 잉여 B -> A 환산 (25개 단위)
            let totalAvailableA = res.A;

            // 3-2. 부족한 가루를 A 또는 남는 B로 제작
let remainingP = neededP;

// 먼저 남는 B를 가루로 교환
let surplusB = res.B - reqB;

if (remainingP > 0n && surplusB >= RATES.BtoP.input) {
    let possibleBatches = surplusB / RATES.BtoP.input;
    let neededBatches = (remainingP + RATES.BtoP.output - 1n) / RATES.BtoP.output;

    let batchesBtoP = possibleBatches < neededBatches
        ? possibleBatches
        : neededBatches;

    log.BtoP = batchesBtoP;

    let producedP = batchesBtoP * RATES.BtoP.output;
    remainingP -= producedP;

    if (remainingP < 0n) remainingP = 0n;
}

// 아직 부족하면 A를 가루로 교환
let aForP = 0n;

if (remainingP > 0n) {
    let batchesAtoP =
        (remainingP + RATES.AtoP.output - 1n)
        / RATES.AtoP.output;

    aForP = batchesAtoP * RATES.AtoP.input;

    log.AtoP = batchesAtoP;
}

// 제작용 철광석 + 가루 제작용 철광석
let totalReqA = reqA + aForP;

if (res.A >= totalReqA) {
    return { possible: true, log };
}

return { possible: false, log };

            return { possible: false, log };
        }

        // ==========================================
        // [3] UI 컨트롤 및 이벤트
        // ==========================================
        function init() {
            renderTabs();
            renderInputs();
        }

        function renderTabs() {
            const tabContainer = document.getElementById('tabContainer');
            tabContainer.innerHTML = '';
            
            Object.keys(MATERIAL_TYPES).forEach(key => {
                const btn = document.createElement('button');
                btn.className = `tab ${key === currentType ? 'active' : ''}`;
                btn.innerText = MATERIAL_TYPES[key].name;
                btn.onclick = () => {
                    currentType = key;
                    renderTabs(); 
                    renderInputs(); 
                    resetResultBox();
                };
                tabContainer.appendChild(btn);
            });
        }

        function renderInputs() {
            const container = document.getElementById('inputContainer');
            container.innerHTML = '';
            const info = MATERIAL_TYPES[currentType];
            const fields = ['A', 'B', 'S', 'C', 'P'];

            fields.forEach(fieldKey => {
                const labelText = info[fieldKey];
                if (!labelText) return;

                const group = document.createElement('div');
                group.className = 'input-group';

                const label = document.createElement('label');
                label.innerText = labelText;

                const input = document.createElement('input');
                input.type = 'text';
                input.value = inputState[currentType][fieldKey];
                input.placeholder = "0";
                
                // 숫자만 입력되도록 처리
                input.oninput = (e) => {
                    e.target.value = e.target.value.replace(/[^0-9]/g, '');
                    inputState[currentType][fieldKey] = e.target.value;
                };

                group.appendChild(label);
                group.appendChild(input);
                container.appendChild(group);
            });
        }

        function resetResultBox() {
            const resultEl = document.getElementById('resultText');
            resultEl.innerText = "재료를 입력하고 계산 버튼을 눌러주세요.";
            resultEl.className = "result-text";
        }

        function calculate() {
            const inputs = inputState[currentType];
            // 입력값이 하나도 없으면 계산하지 않음
            if (Object.values(inputs).every(val => val === "" || val === "0")) {
                resetResultBox();
                return;
            }

            const result = runAlgorithm(currentType, inputs);
            const info = MATERIAL_TYPES[currentType];
            const resultEl = document.getElementById('resultText');

            if (result.output === 0n) {
                resultEl.innerText = "제작 가능한 수량이 없습니다.";
                resultEl.className = "result-text result-no";
            } else {
                resultEl.className = "result-text result-ok";
                const fmt = (n) => n.toLocaleString();
                
                let text = `[ ${info.name} 결과 ]<br><br>`;
text += `총 제작 가능 횟수: ${fmt(result.output)}회<br><br>`;
text += `< 최적 교환 경로 ><br>`;

const logs = result.logs;
// 교환 횟수(묶음 단위)를 표시
if (logs.AtoP > 0n)
    text += `<span style="color:#999999">${info.A}</span> -> <span style="color:#D9A441">${info.P}</span> : ${fmt(logs.AtoP)}회 교환<br>`;

if (logs.BtoP > 0n)
    text += `<span style="color:#4dac00">${info.B}</span> -> <span style="color:#D9A441">${info.P}</span> : ${fmt(logs.BtoP)}회 교환<br>`;

if (logs.PtoC > 0n)
    text += `<span style="color:#D9A441">${info.P}</span> -> <span style="color:#006dc6">${info.C}</span> : ${fmt(logs.PtoC)}회 교환<br>`;

resultEl.innerHTML = text;
            }
        }

        // 초기화 실행
        init();

    </script>
</body>
</html>
<!--
Original: 상급 아비도스 계산기
Original author: @tuijblab-the-decoder
Original: https://codepen.io/tuijblab-the-decoder/pen/azZpWrW
-->

모바일 게시판 하단버튼

댓글

새로고침
새로고침

모바일 게시판 하단버튼

지금 뜨는 인벤

더보기+

모바일 게시판 리스트

모바일 게시판 하단버튼

글쓰기

모바일 게시판 페이징

최근 HOT한 콘텐츠

  • 로아
  • 게임
  • IT
  • 유머
  • 연예
AD