新しい放置ゲームで目標までの放置時間を表示する

新しい放置ゲームで目標までの放置時間を表示します

当前为 2021-10-01 提交的版本,查看 最新版本

// ==UserScript==
// @name         新しい放置ゲームで目標までの放置時間を表示する
// @namespace    http://tampermonkey.net/
// @version      0.6.0
// @description  新しい放置ゲームで目標までの放置時間を表示します
// @author       toomer
// @match        https://dem08656775.github.io/newincrementalgame/
// @grant        none
// ==/UserScript==

// jshint esversion: 6
(() => {
  'use strict';
  const matrix_mul = (a, b) => {
    let c = Array.from(new Array(a.length), () => new Array(b[0].length).fill(new Decimal(0)));
    for (let i = 0; i < a.length; i++) {
      for (let j = 0; j < b[0].length; j++) {
        for (let k = 0; k < a[0].length; k++) {
          c[i][j] = c[i][j].add(a[i][k].mul(b[k][j]));
        }
      }
    }
    return c;
  };
  const matrix_eye = (n) => {
    let a = Array.from(new Array(n), () => new Array(n).fill(new Decimal(0)));
    for (let i = 0; i < n; i++) {
      a[i][i] = new Decimal(1);
    }
    return a;
  };
  class NIG {
    constructor() {
      this.game = NIG.load();
    }
    static load() {
      let game = {
        player: {
          money: new Decimal(document.getElementById('coinamount').textContent.slice(6)),
          level: (() => {
            const contents = document.getElementsByClassName('levelrcontents');
            if (contents.length == 0) return new Decimal(0);
            const res = contents[0].textContent.match(/段位: (?<level>[e\d\+\.]+)/);
            return new Decimal(res === null ? 0 : res.groups.level);
          })(),
          levelresettime: (() => {
            const contents = document.getElementsByClassName('levelrcontents');
            if (contents.length == 0) return new Decimal(0);
            const res = contents[0].textContent.match(/段位リセット: (?<levelresettime>[e\d\+\.]+)/);
            return new Decimal(res === null ? 0 : res.groups.levelresettime);
          })(),
          maxlevelgained: (() => {
            const res = document.getElementsByClassName('challenges-container')[0].parentElement.textContent.match(/最大取得段位:(?<maxlevelgained>[e\d\+\.]+)/);
            return new Decimal(res === null ? 1 : res.groups.maxlevelgained);
          })(),
          rank: (() => {
            const contents = document.getElementsByClassName('rankrcontents');
            if (contents.length == 0) return new Decimal(0);
            const res = contents[0].textContent.match(/階位: (?<rank>[e\d\+\.]+)/);
            return new Decimal(res === null ? 0 : res.groups.rank);
          })(),
          rankresettime: (() => {
            const contents = document.getElementsByClassName('rankrcontents');
            if (contents.length == 0) return new Decimal(0);
            const res = contents[0].textContent.match(/階位リセット: (?<rankresettime>[e\d\+\.]+)/);
            return new Decimal(res === null ? 0 : res.groups.rankresettime);
          })(),

          generators: (() => {
            let generators = new Array(8).fill(null).map(() => new Decimal(0));
            const generatorElements = document.getElementsByClassName('generator');
            for (let i = 0; i < generatorElements.length; i++) {
              const res = generatorElements[i].textContent.match(/発生器(\d)+: (?<n>[e\d\+\.]+)/);
              if (res !== null) generators[i] = new Decimal(res.groups.n);
            }
            return generators;
          })(),
          generatorsBought: (() => {
            let generatorsBought = new Array(8).fill(null).map(() => new Decimal(0));
            const generatorElements = document.getElementsByClassName('generator');
            for (let i = 0; i < generatorElements.length; i++) {
              const res = generatorElements[i].textContent.match(/購入数: (?<n>[e\d\+\.]+)/);
              if (res !== null) generatorsBought[i] = new Decimal(res.groups.n);
            }
            return generatorsBought;
          })(),
          generatorsCost: (() => {
            let generatorsCost = [new Decimal(1), new Decimal('1e4'), new Decimal('1e9'), new Decimal('1e16'), new Decimal('1e25'), new Decimal('1e36'), new Decimal('1e49'), new Decimal('1e64')];
            const gbuttons = document.getElementsByClassName('gbutton');
            for (let i = 0; i < gbuttons.length; i++) {
              const res = gbuttons[i].textContent.match(/購入 コスト: (?<n>[e\d\+\.]+)/);
              generatorsCost[i] = new Decimal(res.groups.n);
            }
            return generatorsCost;
          })(),
          generatorsMode: (() => {
            let generatorsMode = new Array(8).fill(null).map((_, i) => i);
            const generatorElements = document.getElementsByClassName('generator');
            for (let i = 0; i < generatorElements.length; i++) {
              const res = generatorElements[i].textContent.match(/モード: (?<n>\d+)/);
              if (res !== null) generatorsMode[i] = Number(res.groups.n);
            }
            return generatorsMode;
          })(),

          accelerators: (() => {
            let accelerators = new Array(8).fill(null).map(() => new Decimal(0));
            const acceleratorElements = document.getElementsByClassName('accelerator');
            for (let i = 0; i < acceleratorElements.length; i++) {
              const res = acceleratorElements[i].textContent.match(/時間加速器(\d)+: (?<n>[e\d\+\.]+)/);
              if (res !== null) accelerators[i] = new Decimal(res.groups.n);
            }
            return accelerators;
          })(),
          acceleratorsBought: (() => {
            let acceleratorsBought = new Array(8).fill(null).map(() => new Decimal(0));
            const acceleratorElements = document.getElementsByClassName('accelerator');
            for (let i = 0; i < acceleratorElements.length; i++) {
              const res = acceleratorElements[i].textContent.match(/購入数: (?<n>[e\d\+\.]+)/);
              if (res !== null) acceleratorsBought[i] = new Decimal(res.groups.n);
            }
            return acceleratorsBought;
          })(),
          acceleratorsCost: (() => {
            let acceleratorsCost = [new Decimal(10), new Decimal('1e10'), new Decimal('1e20'), new Decimal('1e40'), new Decimal('1e80'), new Decimal('1e160'), new Decimal('1e320'), new Decimal('1e640')];
            const abuttons = document.getElementsByClassName('abutton');
            for (let i = 0; i < abuttons.length; i++) {
              const res = abuttons[i].textContent.match(/購入 コスト: (?<n>[e\d\+\.]+)/);
              acceleratorsCost[i] = new Decimal(res.groups.n);
            }
            return acceleratorsCost;
          })(),

          tickspeed: (() => {
            const res = document.getElementById('tickspeed').textContent.match(/: (?<tickspeed>\d+)/);
            return new Decimal(res === null ? 1000 : res.groups.tickspeed);
          })(),

          onchallenge: document.getElementsByClassName('challengeconfigbutton')[8].textContent == ' 挑戦放棄 ',
          challenges: (() => {
            let challenges = [];
            const challengeconfigbutton = document.getElementsByClassName('challengeconfigbutton');
            for (let i = 0; i < 8; i++) {
              if (challengeconfigbutton[i].classList.contains('selected')) {
                challenges.push(i);
              }
            }
            return challenges;
          })(),
          challengebonuses: (() => {
            let challengebonuses = [];
            const rewardbuttons = document.getElementsByClassName('rewardbutton');
            for (let i = 0; i < 15; i++) {
              if (rewardbuttons[i].classList.contains('selected')) {
                challengebonuses.push(i);
              }
            }
            return challengebonuses;
          })(),
          rankchallengebonuses: (() => {
            let rankchallengebonuses = [];
            const rewardbuttons = document.getElementsByClassName('rewardbutton');
            for (let i = 15; i < 30; i++) {
              if (rewardbuttons[i].classList.contains('selected')) {
                rankchallengebonuses.push(i - 15);
              }
            }
            return rankchallengebonuses;
          })(),

          levelitems: (() => {
            let levelitems = [0, 0, 0, 0, 0];
            const levelitemElements = document.getElementsByClassName('levelitem');
            for (let i = 0; i < levelitemElements.length; i++) {
              const res = levelitemElements[i].textContent.match(/取得数: (?<n>\d+)/);
              if (res !== null) levelitems[i] = Number(res.groups.n);
            }
            return levelitems;
          })(),
          levelitembought: 0,
        },
        activechallengebonuses: [],
        memory: (() => {
          const res = document.getElementsByClassName('worlds')[0].parentElement.textContent.match(/記憶: (?<memory>[e\d\+\.]+)/);
          return res === null ? 0 : res.groups.memory;
        })(),
      };

      game.player.onchallenge |= document.getElementById('enablechalcheck').checked;
      if (game.player.challengebonuses.includes(4) || !game.player.onchallenge) {
        game.activechallengebonuses = game.player.challengebonuses;
      }
      return game;
    }
    static softCap(num, cap) {
      if (num.lessThanOrEqualTo(cap)) return num;
      let capped = num.div(cap);
      capped = new Decimal(capped.log2()).add(1);
      return cap.mul(capped).min(num);
    }

    calcincrementmult(i, to) {
      let mult = new Decimal(1);
      if (!(this.game.player.onchallenge && this.game.player.challenges.includes(4))) {
        mult = mult.mul(new Decimal(10).pow((i + 1) * (i - to)));
      }
      if (!(this.game.player.onchallenge && this.game.player.challenges.includes(7))) {
        let cap = new Decimal(100).mul(this.game.player.levelitems[2] + 1);
        mult = mult.mul(NIG.softCap(this.game.player.levelresettime.add(1), cap));
      }
      mult = mult.mul(new Decimal(this.game.player.level.add(2).log2()).pow(i - to));
      let highest = 0;
      for (let j = 0; j < 8; j++) {
        if (this.game.player.generators[j].greaterThan(0)) {
          highest = j;
        }
      }
      if (!(this.game.player.onchallenge && this.game.player.challenges.includes(2))) {
        let mm = this.game.player.generatorsBought[i];
        if (this.game.activechallengebonuses.includes(11)) {
          mm = mm.mul(new Decimal(mm.add(2).log2()).round());
        }
        if (i < highest && this.game.player.generatorsBought[i].greaterThan(0)) {
          mult = mult.mul(mm);
        } else {
          if (this.game.activechallengebonuses.includes(2) && this.game.player.generatorsBought[i].greaterThan(0)) {
            mult = mult.mul(mm);
          }
        }
      }
      if (this.game.activechallengebonuses.includes(3)) {
        mult = mult.mul(new Decimal(2));
      }
      if (this.game.player.rankchallengebonuses.includes(3)) {
        mult = mult.mul(new Decimal(3))
      }
      if (i == 0 && this.game.activechallengebonuses.includes(7)) {
        mult = mult.mul(this.game.player.maxlevelgained.min(100000));
      }
      mult = mult.mul(1 + this.game.memory * 0.25);
      return mult;
    }

    multmatrix() {
      let multmat = matrix_eye(9);
      for (let i = 0; i < 8; i++) {
        if (!this.game.activechallengebonuses.includes(13)) {
          const to = this.game.player.generatorsMode[i];
          multmat[i + 1][to] = multmat[i + 1][to].add(this.calcincrementmult(i, to));
        } else if (this.game.player.onchallenge && this.game.player.challenges.includes(3)) {
          multmat[i + 1][0] = multmat[i + 1][0].add(this.calcincrementmult(i, 0).mul(i + 1));
        } else {
          for (let to = 0; to <= i; to++) {
            multmat[i + 1][to] = multmat[i + 1][to].add(this.calcincrementmult(i, to));
          }
        }
      }
      return multmat;
    }
    amount() {
      let a = new Array(1);
      let b = new Array(9).fill(new Decimal(0));
      b[0] = this.game.player.money;
      for (let i = 0; i < this.game.player.generators.length; i++) {
        b[i + 1] = this.game.player.generators[i];
      }
      a[0] = b;
      return a;
    }

    accmultmatrix() {
      let multmat = matrix_eye(8);
      for (let i = 1; i < 8; i++) {
        let mult = new Decimal(1);
        if (this.game.activechallengebonuses.includes(10)) {
          mult = mult.mul(this.game.player.acceleratorsBought[i]);
        }
        multmat[i][i - 1] = multmat[i][i - 1].add(mult);
      }
      return multmat;
    }
    accamount() {
      let a = new Array(1);
      let b = new Array(8).fill(new Decimal(0));
      for (let i = 0; i < this.game.player.accelerators.length; i++) {
        b[i] = this.game.player.accelerators[i];
      }
      a[0] = b;
      return a;
    }

    targetmoney(target) {
      let tmoney = new Decimal('1e+100');
      if (target == 'lowest') {
        for (let i = 0; i < this.game.player.generatorsCost.length; i++) {
          if (i >= 3 && this.game.player.onchallenge && this.game.player.challenges.includes(6)) break;
          tmoney = tmoney.min(this.game.player.generatorsCost[i]);
        }
        if (!(this.game.player.onchallenge && this.game.player.challenges.includes(5))) {
          for (let i = 0; i < this.game.player.acceleratorsCost; i++) {
            tmoney = tmoney.min(this.game.player.acceleratorsCost[i]);
          }
        }
      } else if (target == 'newgen') {
        for (let i = 0; i < this.game.player.generatorsCost.length; i++) {
          if (i >= 3 && this.game.player.onchallenge && this.game.player.challenges.includes(6)) break;
          if (this.game.player.generators[i].eq(0)) {
            tmoney = tmoney.min(this.game.player.generatorsCost[i]);
          }
        }
      } else if (target == 'levelreset') {
        if (this.game.player.onchallenge && this.game.player.challenges.includes(0)) {
          tmoney = new Decimal('1e+24');
        } else {
          tmoney = new Decimal('1e+18');
        }
      } else if (target == 'rankreset') {
        tmoney = new Decimal('1e+72');
      } else if (target == 'input') {
        try {
          tmoney = (new Decimal(document.getElementById('targetcfg').value)).max(0);
        } catch (e) {
        }
      } else {
        tmoney = new Decimal('1e+18');
      }
      return tmoney;
    }

    calcgoalticks(tmoney) {
      const multmat = this.multmatrix();
      const amount = this.amount();

      if (amount[0][0].greaterThanOrEqualTo(tmoney)) return new Decimal(0);
      if (amount[0].slice(1).every((a) => a.eq(0))) return new Decimal('Infinity');
      let base = [multmat, new Decimal(1)];
      let bases = [];
      while (matrix_mul(amount, base[0])[0][0].lessThan(tmoney)) {
        bases.push(base);
        base = [matrix_mul(base[0], base[0]), base[1].add(base[1])];
      }
      base = [matrix_eye(multmat.length), new Decimal(0)];
      while (bases.length > 0) {
        const bb = bases.pop();
        const m = matrix_mul(base[0], bb[0]);
        if (matrix_mul(amount, m)[0][0].lessThan(tmoney)) {
          base = [m, base[1].add(bb[1])];
        }
      }
      return base[1].add(new Decimal(1));
    }

    tick2sec(tick) {
      const accmultmat = this.accmultmatrix();
      const accamount = this.accamount();
      if (tick.lessThanOrEqualTo(0)) return new Decimal(0);
      if (tick.eq(new Decimal('Infinity'))) return new Decimal('Infinity');
      // const delay = tick.mul('1e-6').max(new Decimal('1e-3'));
      const delay = new Decimal('1e-3');
      let base = [accmultmat, new Decimal(1)];
      let bases = [];
      while (base[1].lessThanOrEqualTo(tick)) {
        bases.push(base);
        base = [matrix_mul(base[0], base[0]), base[1].add(base[1])];
      }
      const basetick = new Decimal(1000 - this.game.player.levelitems[1] * this.game.player.challengebonuses.length).div(1000);
      let amult = this.game.activechallengebonuses.includes(6) ? this.game.player.acceleratorsBought[0].max(1) : new Decimal(1);
      base = [matrix_eye(accmultmat.length), new Decimal(0)];
      let prevdt = this.game.player.tickspeed.mul(new Decimal('1e-3'));
      let sec = new Decimal(0);
      while (base[1].lessThan(tick)) {
        const prevtick = base[1];
        for (let i = bases.length; i-- > 0;) {
          if (base[1].add(bases[i][1]).lessThanOrEqualTo(tick)) {
            const m = matrix_mul(base[0], bases[i][0]);
            if (i == 0 && prevtick.eq(base[1]) || basetick.div(matrix_mul(accamount, m)[0][0].add(10).mul(amult).log10()).add(delay).greaterThan(prevdt)) {
              base = [m, base[1].add(bases[i][1])];
            }
          }
        }
        if (prevtick.eq(base[1])) break;
        const dt = basetick.div(matrix_mul(accamount, base[0])[0][0].add(10).mul(amult).log10());
        sec = sec.add((prevdt.add(dt)).div(2).mul(base[1].sub(prevtick)));
        prevdt = dt;
      }
      return sec;
    }
  }

  let prevupdtime = 0;
  let prev = null;
  let tickspeed = new Decimal(1000);
  const update = (force, repeat) => {
    if (force || (Date.now() - prevupdtime >= 1000)) {
      let nig = new NIG();

      const sel = document.getElementById('targetSelector');
      const tmoney = nig.targetmoney(sel.options[sel.selectedIndex].value);
      const goalticks = nig.calcgoalticks(tmoney);
      const goalsec = nig.tick2sec(goalticks);

      tickspeed = nig.game.player.tickspeed;
      prevupdtime = Date.now();
      prev = {
        tmoney: tmoney,
        goalticks: goalticks,
        goalsec: goalsec,
      };
    }
    if (prev !== null) {
      const updategoal = () => {
        const elapsed = Date.now() - prevupdtime;
        const goalticks = prev.goalticks.sub(new Decimal(elapsed).div(tickspeed).floor()).max(0);
        const goalsec = prev.goalsec.sub(elapsed / 1000).max(0);
        let goalstr = prev.tmoney.toExponential(3) + ' ポイントまで ' + goalticks.toExponential(3) + ' ticks';
        goalstr += ' (' + goalsec.toExponential(3) + ' sec)';
        goalstr += ' ' + (new Date(Date.now() + Number(goalsec.mul(1000).toExponential(20)))).toLocaleString() + ' に達成';
        document.getElementById('targetmoney').textContent = goalstr;
      };
      updategoal();
    }

    if (repeat) {
      setTimeout(() => update(false, repeat), 200);
    }
  };

  let div = document.createElement('div');
  div.id = 'calculator';
  {
    let selectdiv = document.createElement('div');
    let select = document.createElement('select');
    select.id = 'targetSelector';
    [
      ['lowest', '最低コスト'],
      ['newgen', '新しい発生器'],
      ['levelreset', '段位リセット'],
      ['rankreset', '階位リセット'],
      ['input', '入力'],
    ].forEach(cfg => {
      let option = document.createElement('option');
      option.value = cfg[0];
      option.textContent = cfg[1];
      select.appendChild(option);
    });
    select.addEventListener('change', () => {
      const sel = document.getElementById('targetSelector');
      const input = document.getElementById('targetcfg');
      if (sel.options[sel.selectedIndex].value == 'input') {
        input.style.visibility = 'visible';
      } else {
        input.style.visibility = 'hidden';
      }
    });
    selectdiv.appendChild(select);

    let input = document.createElement('input');
    input.id = 'targetcfg';
    input.type = 'text';
    input.value = '1e18';
    input.size = 10;
    input.style.visibility = 'hidden';
    selectdiv.appendChild(input);

    div.appendChild(selectdiv);
  }
  {
    let inputdiv = document.createElement('div');
    let input = document.createElement('input');
    input.id = 'enablechalcheck';
    input.type = 'checkbox';
    inputdiv.appendChild(input);
    inputdiv.append('挑戦内容を反映');
    div.appendChild(inputdiv);
  }
  {
    let targetmoneydiv = document.createElement('div');
    targetmoneydiv.id = 'targetmoney';
    targetmoneydiv.textContent = '1e18';
    div.appendChild(targetmoneydiv);
  }
  document.getElementsByClassName('container')[0].appendChild(div);

  update(true, true);
})();