Tip of the Day
Long pressing an item opens multi-selection options in the inventory.

MediaWiki:Gadget-DryCalc.js

From Walkscape Walkthrough
Revision as of 01:25, 29 December 2025 by Bonez565 (talk | contribs) (Created page with "global mw, $: (function () { 'use strict'; function clampNumber(n, min, max) { if (!isFinite(n)) return NaN; return Math.min(Math.max(n, min), max); } function formatPercent(x) { if (!isFinite(x)) return '—'; return (x * 100).toFixed(2) + '%'; } function formatNumber(x, decimals) { if (!isFinite(x)) return '—'; var d = (typeof decimals === 'number') ? decimals : 2; return x.toFixed(d); } function calc(p, t) { /...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
/* global mw, $ */
(function () {
  'use strict';

  function clampNumber(n, min, max) {
    if (!isFinite(n)) return NaN;
    return Math.min(Math.max(n, min), max);
  }

  function formatPercent(x) {
    if (!isFinite(x)) return '—';
    return (x * 100).toFixed(2) + '%';
  }

  function formatNumber(x, decimals) {
    if (!isFinite(x)) return '—';
    var d = (typeof decimals === 'number') ? decimals : 2;
    return x.toFixed(d);
  }

  function calc(p, t) {
    // p: probability per trial, t: trials
    var q = 1 - p;
    var p0 = Math.pow(q, t);       // P(0 drops)
    var pge1 = 1 - p0;             // P(>=1)
    var mu = t * p;                // expected drops
    return { p0: p0, pge1: pge1, mu: mu };
  }

  function trialsForConfidence(p, c) {
    // smallest t such that P(>=1) >= c
    // 1 - (1-p)^t >= c  => (1-p)^t <= 1-c
    // t >= ln(1-c)/ln(1-p)
    if (!(p > 0 && p < 1)) return NaN;
    if (!(c > 0 && c < 1)) return NaN;
    var t = Math.log(1 - c) / Math.log(1 - p);
    return Math.ceil(t);
  }

  function buildUI($host) {
    var defaultMode = ($host.data('default-mode') || 'actions').toString().toLowerCase();
    if (defaultMode !== 'steps') defaultMode = 'actions';

    var $wrap = $('<div>').addClass('ws-drycalc-card');

    var $title = $('<div>').addClass('ws-drycalc-title').text('Drop Chance Calculator');
    var $modeRow = $('<div>').addClass('ws-drycalc-row');

    var $modeLabel = $('<label>').addClass('ws-drycalc-label').text('Mode');
    var $mode = $('<select>').addClass('ws-drycalc-input')
      .append($('<option>').val('actions').text('Actions'))
      .append($('<option>').val('steps').text('Steps'))
      .val(defaultMode);

    $modeRow.append($modeLabel, $mode);

    var $rateRow = $('<div>').addClass('ws-drycalc-row');
    var $rateLabel = $('<label>').addClass('ws-drycalc-label').text('Drop rate');
    var $ratePrefix = $('<span>').addClass('ws-drycalc-inline').text('1 in');
    var $rateN = $('<input>')
      .addClass('ws-drycalc-input')
      .attr({ type: 'number', min: '1', step: '1', value: '128' });

    $rateRow.append($rateLabel, $('<div>').addClass('ws-drycalc-inlinewrap').append($ratePrefix, $rateN));

    var $countRow = $('<div>').addClass('ws-drycalc-row');
    var $countLabel = $('<label>').addClass('ws-drycalc-label');
    var $count = $('<input>')
      .addClass('ws-drycalc-input')
      .attr({ type: 'number', min: '0', step: '1', value: '0' });

    $countRow.append($countLabel, $count);

    var $out = $('<div>').addClass('ws-drycalc-out');

    function render() {
      var mode = $mode.val();
      var n = parseFloat($rateN.val());
      var t = parseFloat($count.val());

      n = clampNumber(n, 1, 1e12);
      t = clampNumber(t, 0, 1e15);

      var unit = (mode === 'steps') ? 'steps' : 'actions';
      $countLabel.text('Completed (' + unit + ')');

      if (!(n >= 1) || !(t >= 0)) {
        $out.empty().append(
          $('<div>').addClass('ws-drycalc-warn').text('Please enter a valid drop rate and count.')
        );
        return;
      }

      var p = 1 / n;
      var r = calc(p, t);

      // confidence milestones
      var t50 = trialsForConfidence(p, 0.50);
      var t90 = trialsForConfidence(p, 0.90);
      var t95 = trialsForConfidence(p, 0.95);

      $out.empty().append(
        $('<div>').addClass('ws-drycalc-metric')
          .append($('<div>').addClass('ws-drycalc-k').text('Expected ' + unit + ' per drop'))
          .append($('<div>').addClass('ws-drycalc-v').text(formatNumber(n, 0))),

        $('<div>').addClass('ws-drycalc-metric')
          .append($('<div>').addClass('ws-drycalc-k').text('Expected drops so far'))
          .append($('<div>').addClass('ws-drycalc-v').text(formatNumber(r.mu, 2))),

        $('<div>').addClass('ws-drycalc-metric')
          .append($('<div>').addClass('ws-drycalc-k').text('Chance of at least 1 drop by now'))
          .append($('<div>').addClass('ws-drycalc-v').text(formatPercent(r.pge1))),

        $('<div>').addClass('ws-drycalc-metric')
          .append($('<div>').addClass('ws-drycalc-k').text('Chance you would be dry (0 drops)'))
          .append($('<div>').addClass('ws-drycalc-v').text(formatPercent(r.p0))),

        $('<div>').addClass('ws-drycalc-subtitle').text('Milestones (chance of ≥1 drop)'),

        $('<div>').addClass('ws-drycalc-milestones').append(
          $('<div>').addClass('ws-drycalc-chip').text('50%: ' + (isFinite(t50) ? t50 : '—') + ' ' + unit),
          $('<div>').addClass('ws-drycalc-chip').text('90%: ' + (isFinite(t90) ? t90 : '—') + ' ' + unit),
          $('<div>').addClass('ws-drycalc-chip').text('95%: ' + (isFinite(t95) ? t95 : '—') + ' ' + unit)
        )
      );
    }

    $mode.on('change', render);
    $rateN.on('input', render);
    $count.on('input', render);

    $wrap.append($title, $modeRow, $rateRow, $countRow, $out);
    $host.empty().append($wrap);

    render();
  }

  function init($content) {
    $content.find('.ws-drycalc').each(function () {
      buildUI($(this));
    });
  }

  mw.hook('wikipage.content').add(init);
})();