Greasy Fork

Greasy Fork is available in English.

alchemy-calculator

by Redbean

当前为 2025-07-09 提交的版本,查看 最新版本

此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.greasyfork.icu/scripts/541343/1621601/alchemy-calculator.js

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

(function() {
    const ac_prefix = 'ac_';

    // 获取DOM元素 v148
    const ac_getElement = (id) => document.getElementById(id);

    // 格式化时间显示(秒 → 分钟/小时)
    function ac_formatTime(seconds) {
        if (seconds < 60) {
            return `${seconds.toFixed(1)}秒`;
        }
        const minutes = Math.floor(seconds / 60);
        const remainingSeconds = seconds % 60;
        if (minutes < 60) {
            return `${minutes}分${remainingSeconds > 0 ? remainingSeconds.toFixed(1) + '秒' : ''}`;
        }
        const hours = Math.floor(minutes / 60);
        const remainingMinutes = minutes % 60;
        return `${hours}小时${remainingMinutes > 0 ? remainingMinutes + '分' : ''}`;
    }
	// 新增函数:计算实际动作次数
	function calculateActualAttempts(baseAttempts, efficiencyRate) {
    let actualAttempts = 0;
    for (let i = 0; i < baseAttempts; i++) {
        // 基础动作
        actualAttempts += 1;
        // 基础重复(效率≤100%部分)
        if (efficiencyRate > 0) {
            const baseChance = Math.min(efficiencyRate, 100) / 100;
            if (Math.random() < baseChance) {
                actualAttempts += 1;
            }
            // 额外重复(效率>100%部分)
            if (efficiencyRate > 100) {
                const extraChance = (efficiencyRate - 100) / 100;
                if (Math.random() < extraChance) {
                    actualAttempts += 1;
                }
            }
        }
    }
    return actualAttempts;
}

    // 模拟炼金过程
    function ac_simulateAlchemy() {
		document.querySelectorAll('.output-allquantity').forEach(input => {
        input.value = '0';
    });
        const simulateTimes = parseInt(ac_getElement('simulateTimes').value) || 0;
		const efficiencyRate = parseInt(ac_getElement('efficiencyRate').value) || 0;
		const successRate = parseInt(ac_getElement('successRate').value) || 0;
		const baseTimePerTry = parseInt(ac_getElement('timePerTry').value) || 0;

        // 验证输入
        if (simulateTimes <= 0 || simulateTimes > 1000000) {
            alert('请输入1-1000000之间的模拟次数');
            return;
        }

        if (successRate < 0 || successRate > 100) {
            alert('成功率必须在0-100之间');
            return;
        }

        if (efficiencyRate < 0 || efficiencyRate > 200) {
            alert('效率必须在0-200之间');
            return;successCount
        }


        // 获取产出物品信息
        const outputRows = document.querySelectorAll('#outputItems tr');
        const items = [];
        let totalProbability = 0;

        outputRows.forEach(row => {
            const probability = parseFloat(row.querySelector('.output-probability').value) || 0;
            const itemName = row.querySelector('.output-item').value;
            const price = parseInt(row.querySelector('.output-price').value) || 0;
			const baseQuantity = parseInt(row.querySelector('.output-quantity').value) || 1;

            items.push({
                name: itemName,
				probability,
				price,
				baseQuantity, // 单次掉落数量(output-quantity)
				currentQuantity: 0, // 本次模拟的数量
				total: 0
            });

            totalProbability += probability;
        });

        // 验证概率总和
        const roundedTotal = Math.round(totalProbability * 100) / 100;
        if (roundedTotal !== 100) {
            alert(`所有物品的概率总和必须为100%,当前为${totalProbability.toFixed(2)}%`);
            return;
        }

        // 重置物品数量
        items.forEach(item => {
            item.quantity = 0;
            item.total = 0;
        });

        // 开始模拟
		const totalActions = calculateActualAttempts(simulateTimes, efficiencyRate);
		const baseTotalTime = simulateTimes * baseTimePerTry;
		const totalTime = baseTotalTime * (simulateTimes / totalActions);
		let successCount = 0;
        let totalAttempts = 0;
		for (let i = 0; i < simulateTimes; i++) {  // 保持用基础次数
            totalAttempts++; // 无论成功与否,都算一次尝试
        if (Math.random() * 100 < successRate) {
            successCount++;
            // 随机选择产出物品(保持不变)
            let random = Math.random() * 100;
            let accumulated = 0;
            for (const item of items) {
                accumulated += item.probability;
                if (random <= accumulated) {
                    item.currentQuantity += item.baseQuantity;
                    item.total += item.price * item.baseQuantity;
                    break;
                }
            }
        }
    }
         const ac_allCostPerTry = parseFloat(ac_getElement('allcostPerTry').value) || 0;
        const ac_catalystCost = parseFloat(ac_getElement('Catalyst').value) || 0;

        // 计算期望利润
        let expectedProfit = 0;
        items.forEach(item => {
            const expectedQuantity = (successRate / 100) * (item.probability / 100) * simulateTimes * item.baseQuantity;
            expectedProfit += expectedQuantity * item.price;
        });
        const expectedCost = simulateTimes * ac_allCostPerTry - (simulateTimes * (1 - successRate / 100) * ac_catalystCost);
        const expectedValue = expectedProfit - expectedCost;
        // 计算标准差(需要计算方差)
        let variance = 0;
        items.forEach(item => {
            const p = (successRate / 100) * (item.probability / 100);
            const value = item.price * item.baseQuantity;
            variance += p * (1 - p) * Math.pow(value, 2) * simulateTimes;
        });
        // 添加催化剂返还的方差(假设是固定返还)
        const catalystVariance = (successRate / 100) * (1 - successRate / 100) * Math.pow(ac_catalystCost, 2) * simulateTimes;
        variance += catalystVariance;
        const stdDeviation = Math.sqrt(variance);
        // 更新UI
        ac_getElement('expectation').textContent = Math.round(expectedValue).toLocaleString();
        ac_getElement('stdDeviation').textContent = Math.round(stdDeviation).toLocaleString();


        // 更新UI显示物品数量
        outputRows.forEach((row, index) => {
			const item = items[index];
            // 显示本次模拟结果
			row.querySelector('.output-allquantity').value =
            (parseInt(row.querySelector('.output-allquantity').value) || 0) + item.currentQuantity;
            // 显示本次模拟的总价
			row.querySelector('.output-total').value = item.total;
        });
        // 计算并显示结果
        ac_getElement('timeCost').textContent = ac_formatTime(totalTime);
        // 存储成功次数
        if (!ac_getElement('successCount')) {
        const hiddenInput = document.createElement('input');
        hiddenInput.type = 'hidden';
        hiddenInput.id = 'successCount';
        document.body.appendChild(hiddenInput);
    }
        ac_getElement('successCount').value = successCount;
        // 触发盈亏计算
        ac_calculateProfit();
        // 在
        const actualSuccessRate = (successCount / simulateTimes) * 100;
        ac_getElement('ActualSuccessrate').textContent = actualSuccessRate.toFixed(2) + '%';
        // 同时更新消耗显示
        const catalystConsumed = successCount;
        ac_getElement('Consumable').textContent = `${successCount}|${totalAttempts}`;
        // 计算变异系数(CV) = 标准差/期望值
        let cv = 0;
        if (expectedValue !== 0) {
            cv = stdDeviation / Math.abs(expectedValue);
        }
        // 根据CV值确定风险等级
        let riskLevel = "";
        let riskClass = "";
        if (cv >= 0 && cv < 1) {
            riskLevel = "低风险";
            riskClass = "profit";
        } else if (cv >= 1 && cv < 2) {
            riskLevel = "较高风险";
            riskClass = "loss";
        } else if ((cv >= 2 && cv < 3) || cv < 0) {
            riskLevel = "高风险";
            riskClass = "high-risk";
        } else if (cv >= 3 || (cv <= -1 && cv > -3)) {
            riskLevel = "超高风险";
            riskClass = "extreme-risk";
        } else {
            riskLevel = "未知风险";
            riskClass = "";
        }

// 更新风险显示
const riskElement = ac_getElement('Risk');
riskElement.textContent = `${riskLevel} (CV: ${cv.toFixed(2)})`;
riskElement.className = 'result-value ' + riskClass;
    }
    // 模拟炼金过程2
    function ac_simulateUntilTarget() {
    // 重置所有数量
    document.querySelectorAll('.output-allquantity').forEach(input => {
        input.value = '0';
    });
    const efficiencyRate = parseInt(ac_getElement('efficiencyRate').value) || 0;
    const successRate = parseInt(ac_getElement('successRate').value) || 0;
    const baseTimePerTry = parseInt(ac_getElement('timePerTry').value) || 0;
    // 获取产出物品信息
    const outputRows = document.querySelectorAll('#outputItems tr');
    const items = [];
    let totalProbability = 0;
    let maxValueItem = null;
    let maxValue = 0;
    outputRows.forEach(row => {
        const probability = parseFloat(row.querySelector('.output-probability').value) || 0;
        const itemName = row.querySelector('.output-item').value;
        const price = parseInt(row.querySelector('.output-price').value) || 0;
        const baseQuantity = parseInt(row.querySelector('.output-quantity').value) || 1;
        const item = {
            name: itemName,
            probability,
            price,
            baseQuantity,
            currentQuantity: 0,
            total: 0,
            row: row
        };
        items.push(item);
        totalProbability += probability;
        // 找出价值最高的物品
        const itemValue = price * baseQuantity;
        if (itemValue > maxValue) {
            maxValue = itemValue;
            maxValueItem = item;
        }
    });
    // 验证概率总和
    const roundedTotal = Math.round(totalProbability * 100) / 100;
    if (roundedTotal !== 100) {
        alert(`所有物品的概率总和必须为100%,当前为${totalProbability.toFixed(2)}%`);
        return;
    }
    // 开始模拟直到获得最高价值物品
    let successCount = 0;
    let totalAttempts = 0;
    let gotMaxValueItem = false;
    let totalTime = 0;
    while (!gotMaxValueItem) {
        const actualAttempts = calculateActualAttempts(1, efficiencyRate);
        totalAttempts += actualAttempts;
        totalTime += baseTimePerTry * (1 / actualAttempts);
        if (Math.random() * 100 < successRate) {
            successCount++;
            // 随机选择产出物品
            let random = Math.random() * 100;
            let accumulated = 0;
            for (const item of items) {
                accumulated += item.probability;
                if (random <= accumulated) {
                    item.currentQuantity += item.baseQuantity;
                    item.total += item.price * item.baseQuantity;
                    // 检查是否获得了最高价值物品
                    if (item === maxValueItem) {
                        gotMaxValueItem = true;
                    }
                    break;
                }
            }
        }
    }
    // 更新UI显示物品数量
    items.forEach(item => {
        item.row.querySelector('.output-allquantity').value = item.currentQuantity;
        item.row.querySelector('.output-total').value = item.total;
    });
    // 计算新成本(总尝试次数*物品价格 + 成功次数*催化剂价格)
    const itemPrice = parseFloat(ac_getElement('itemPrice').value) || 0;
    const catalystPrice = parseFloat(ac_getElement('Catalyst').value) || 0;
    const newTotalCost = totalAttempts * itemPrice + successCount * catalystPrice;
    // 计算产出物品总价
    let totalOutputValue = 0;
    items.forEach(item => {
        totalOutputValue += item.total;
    });
    // 计算盈亏(新成本 - 产出物品价格)
    const profitLoss = totalOutputValue - newTotalCost;
    // 更新UI显示
    ac_getElement('timeCost').textContent = ac_formatTime(totalTime);
    ac_getElement('successCount').value = successCount;
    ac_getElement('Consumable').textContent = `${successCount}|${totalAttempts}`;
    ac_getElement('totalCost').textContent = newTotalCost.toLocaleString();
    const profitElement = ac_getElement('profitLoss');
    profitElement.textContent = profitLoss.toLocaleString();
    profitElement.className = 'result-value ' + (profitLoss >= 0 ? 'profit' : 'loss');
    // 计算利润率
    let profitMargin = 0;
    if (newTotalCost > 0) {
        profitMargin = (profitLoss / newTotalCost) * 100;
    }
    const profitMarginElement = ac_getElement('profit_margin');
    profitMarginElement.textContent = profitMargin.toFixed(2) + '%';
    profitMarginElement.className = 'result-value ' + (profitMargin >= 0 ? 'profit' : 'loss');

    const actualSuccessRate = (successCount / totalAttempts) * 100;
    ac_getElement('ActualSuccessrate').textContent = actualSuccessRate.toFixed(2) + '%';
}
    // 计算盈亏
    // 计算盈亏
    // 计算盈亏
    function ac_calculateProfit() {
        // 计算总成本
        const ac_simulateTimes = parseFloat(ac_getElement('simulateTimes').value) || 0;
        const ac_allCostPerTry = parseFloat(ac_getElement('allcostPerTry').value) || 0;
        const ac_catalystCost = parseFloat(ac_getElement('Catalyst').value) || 0;
        // 获取成功次数(从模拟结果中获取)
        const successCount = parseInt(ac_getElement('successCount')?.value) || 0;
        const failCount = ac_simulateTimes - successCount;
        const ac_totalCost = (ac_simulateTimes * ac_allCostPerTry) - (failCount * ac_catalystCost);
        ac_getElement('totalCost').textContent = ac_totalCost.toLocaleString();
        // 计算产出物品总价
        let ac_totalOutputValue = 0;
		document.querySelectorAll('#outputItems tr').forEach(row => {
        const quantity = parseInt(row.querySelector('.output-allquantity').value) || 0;
        const price = parseInt(row.querySelector('.output-price').value) || 0;
        ac_totalOutputValue += quantity * price;
    });
        // 计算盈亏
        const ac_profitLoss = ac_totalOutputValue - ac_totalCost;
		const ac_profitElement = ac_getElement('profitLoss');
        ac_profitElement.textContent = ac_profitLoss.toLocaleString();
		ac_profitElement.className = 'result-value ' + (ac_profitLoss >= 0 ? 'profit' : 'loss');
        // 新增:计算利润率
        let profitMargin = 0;
        if (ac_totalCost > 0) { // 避免除以0
        profitMargin = (ac_profitLoss / ac_totalCost) * 100;
    }
    const profitMarginElement = ac_getElement('profit_margin');
    profitMarginElement.textContent = profitMargin.toFixed(2) + '%';
    profitMarginElement.className = 'result-value ' + (profitMargin >= 0 ? 'profit' : 'loss');
    }
    // 计算单次成本
    function ac_calculateSingleCost() {
        const ac_itemPrice = parseFloat(ac_getElement('itemPrice').value) || 0;
        const ac_catalystCost = parseFloat(ac_getElement('Catalyst').value) || 0;
        const ac_costPerTry = parseFloat(ac_getElement('costPerTry').value) || 0;
        const ac_singleCost = ac_itemPrice + ac_catalystCost + ac_costPerTry;
        ac_getElement('allcostPerTry').value = ac_singleCost;
        // 触发重新计算
        ac_calculateProfit();
    }
    // 初始化事件监听器
    function ac_initEventListeners() {
        // 监听所有相关输入的变化
        const ac_inputsToWatch = [
            'simulateTimes', 'itemPrice', 'Catalyst', 'costPerTry',
            'timePerTry', 'successRate', 'efficiencyRate'
        ];

        ac_inputsToWatch.forEach(id => {
            ac_getElement(id).addEventListener('input', ac_calculateSingleCost);
        });

        // 监听产出物品表格的变化
        document.querySelectorAll('#outputItems .output-price, #outputItems .output-probability').forEach(input => {
            input.addEventListener('input', ac_calculateProfit);
        });

        // 监听开始炼金按钮
        ac_getElement('startBtn').addEventListener('click', ac_simulateAlchemy);
    }

    // 初始化计算器
    function ac_initCalculator() {
        ac_initEventListeners();
        ac_calculateSingleCost(); // 初始计算
    }

    // 当DOM加载完成后初始化
    document.addEventListener('DOMContentLoaded', ac_initCalculator);

    // 暴露必要的函数到全局
    window.alchemyCalculator = {
        simulateAlchemy: ac_simulateAlchemy,
        simulateUntilTarget: ac_simulateUntilTarget,
        calculateProfit: ac_calculateProfit,
        calculateSingleCost: ac_calculateSingleCost,
        formatTime: ac_formatTime
    };
})();