【シノアリス】相手前衛5人全員に防デバフをしたい話 本題:プログラミングしてみた

はじめに

敵前衛5人の全員にデバフを刺せる確率はどれくらいか。
例えば下記の場合でどれくらい違のか。
 ・3人が3~4体武器(Ⅷの豪武器や秤)を振った場合
 ・3人が4確武器(マナコ)を振った場合
といったことを考えてきました。
紙とペンで計算し挫折し、エクセルでごり押して挫折し、今回はプログラムでシミュレータを作って導き出すアプローチをしてみようと思います。

挫折1

挫折2


言語の選定

プログラミングするなら、どんな言語を使うか最初に考えると思います。
サクッとちょろっとシミュレータを作ってみようと思っているので
・開発環境の構築をしたくない(インストールは避けたい)
・個人的にメジャーと思える言語を使いたい(VB、JScriptは避けたい)
・ゆくゆくはUIも作りたい(という気持ちはある)
あたりを考えて、今回はJavaScriptで行こうと思います。

ファイル作成

作成するファイルは
・index.html
・simulate.js
の2つ。
※コピペして使ってみたい方は、文字コードはuft-8(non bom)です。意味わからないよって人は、コード内の日本語を英語にしてください。変えなくても文字化けする程度なので特段問題ないです。

index.html

<html>
<head> 
  <title>ソサシミュレータ</title>
</head>
<body> 
  <p id="result"></p> 
  <script src="simulate.js"></script>
</body>
</html>

詳細は書くまでもない内容ですね。

simulate.js

"use strict";

const Sorcerer = class {
  constructor(a,b,c,d,e){
    this.targetNumMin = a; // 対象人数の最小、3~4体なら3、3確なら3
    this.targetNumMax = b; // 対象人数の最大、3~4体なら4、3確なら3
    this.targetNum    = 0; // 最終的な対象人数(整数値)
    this.target       = 0; // ターゲットとなる敵前衛の番号。0はタゲなし、-1は武器を振っていない
    this.gunyu        = c; // 群雄割拠発動率
    this.tousotsu     = d; // 統率発動有無。true:発動中、false:発動無し
    this.debuffRate   = e; // デバフの倍率(いったん1固定)
    this.debuffResult = [0, 0, 0, 0, 0]; // 敵前衛の誰にデバフしたかを格納する
  }

  // ターゲット数を計算する
  calcTargetNum(){
    
    // メインスキルの対象人数を確定
    this.targetNum = Math.floor( Math.random() * (this.targetNumMax + 1 - this.targetNumMin) ) + this.targetNumMin 

    // 群雄割拠の処理
    if(Math.random() < this.gunyu) this.targetNum++;

    // 統率の処理
    if(this.tousotsu) this.targetNum--;

    // 補正(対象が0や6体となった場合の処理)
    if(this.tagetNum == 0) this.tagetNum = 1;
    if(this.tagetNum == 6) this.tagetNum = 5;

  }

  // 配列をシャッフルする
  shuffleArr(array){
    for (let i = array.length; 1 < i; i--) {
      let k = Math.floor(Math.random() * i);
      [array[k], array[i - 1]] = [array[i - 1], array[k]];
    }
    return array;
  }

  // デバフ対象を決定する
  debuff(target){
    this.target = target;
    this.calcTargetNum(); // 対象人数を取得

    let arr = [];
    if(this.target == 0){

      // 対象人数に合わせてデバフ状況を仮作成
      if(this.targetNum == 1){ arr = [1, 0, 0, 0, 0]; }else
      if(this.targetNum == 2){ arr = [1, 1, 0, 0, 0]; }else   
      if(this.targetNum == 3){ arr = [1, 1, 1, 0, 0]; }else
      if(this.targetNum == 4){ arr = [1, 1, 1, 1, 0]; }else
      if(this.targetNum == 5){ arr = [1, 1, 1, 1, 1]; }

      // デバフ対象をランダムにする
      this.debuffResult = this.shuffleArr(arr);

    }else if(this.target == -1){
      // 武器を振らないのですべて0
      arr = [0, 0, 0, 0, 0];
    }else{

      // 対象人数に合わせてデバフ状況を仮作成(タゲを除く敵4人分)
      if(this.targetNum == 1){ arr = [0, 0, 0, 0]; }else
      if(this.targetNum == 2){ arr = [1, 0, 0, 0]; }else
      if(this.targetNum == 3){ arr = [1, 1, 0, 0]; }else   
      if(this.targetNum == 4){ arr = [1, 1, 1, 0]; }else
      if(this.targetNum == 5){ arr = [1, 1, 1, 1]; }

      // デバフ対象をランダムにする
      this.debuffResult = this.shuffleArr(arr);

      // タゲをデバフ対象とする
      this.debuffResult.splice(this.target -1 , 0, 1);
    }
  }
}

let tousotsuFlag = true;  // ★統率有無 true:あり flase:なし
let gunyuRate = 0.5000;    // ★群雄発動率(全員共通にしちゃう)
let own1 = new Sorcerer(4, 4, gunyuRate, tousotsuFlag, 1);  // ★後衛1の情報
let own2 = new Sorcerer(3, 4, gunyuRate, tousotsuFlag, 1);  // ★後衛2の情報(以下略)
let own3 = new Sorcerer(3, 3, gunyuRate, tousotsuFlag, 1);
let own4 = new Sorcerer(3, 3, gunyuRate, tousotsuFlag, 1);
let own5 = new Sorcerer(3, 3, gunyuRate, tousotsuFlag, 1);
let own6 = new Sorcerer(3, 3, gunyuRate, tousotsuFlag, 1);
let own7 = new Sorcerer(3, 3, gunyuRate, tousotsuFlag, 1);
let own8 = new Sorcerer(3, 3, gunyuRate, tousotsuFlag, 1);
let own9 = new Sorcerer(3, 3, gunyuRate, tousotsuFlag, 1);
let own10= new Sorcerer(3, 3, gunyuRate, tousotsuFlag, 1);
let resultMessage = ""

let totalTrialNum = 100000;    // ★試行回数
let totalUndebuffNum = 0;
for(let j=0; j<totalTrialNum; j++){
  own1.debuff(0);
  own2.debuff(0);
  own3.debuff(0);
  own4.debuff(0);
  own5.debuff(-1);
  own6.debuff(-1);
  own7.debuff(-1);
  own8.debuff(-1);
  own9.debuff(-1);
  own10.debuff(-1);

  let debuffEnemy = [0,0,0,0,0];
  let nonDebuffEnemyNum = 0;
  for(let i=0; i<5; i++){
    debuffEnemy[i] = own1.debuffResult[i]
                  + own2.debuffResult[i]
                  + own3.debuffResult[i]
                  + own4.debuffResult[i]
                  + own5.debuffResult[i]
                  + own6.debuffResult[i]
                  + own7.debuffResult[i]
                  + own8.debuffResult[i]
                  + own9.debuffResult[i]
                  + own10.debuffResult[i];
  }

  if(debuffEnemy[0] == 0 ) nonDebuffEnemyNum++;
  if(debuffEnemy[1] == 0 ) nonDebuffEnemyNum++;
  if(debuffEnemy[2] == 0 ) nonDebuffEnemyNum++;
  if(debuffEnemy[3] == 0 ) nonDebuffEnemyNum++;
  if(debuffEnemy[4] == 0 ) nonDebuffEnemyNum++;

  if(nonDebuffEnemyNum > 0)totalUndebuffNum++;
}

resultMessage += "デバフ失敗率 :" + (totalUndebuffNum / totalTrialNum)*100 +"<br>";
resultMessage += " 非デバフ回数:" + totalUndebuffNum +"<br>";
resultMessage += " 試行回数  :" + totalTrialNum +"<br>";
document.getElementById("result").innerHTML = resultMessage;

コメントをたくさん書いたので詳細は割愛します。
★がついている部分を修正すると、前提条件を色々変えられます。
※プログラミングは趣味レベルなので、アドバイスがあればコメントいただきたいです。

まとめ

これつくるの大体2時間程度だったので、エクセルで頑張るよりなんだかんだ早かったです。
が、手計算、エクセル、プログラムと検算しながら進められたので、前段の話は無駄でなかったはずと思いたいです。

計算例は下記の通り

例1:
・統率あり
・ソサ1:タゲなしで4確武器を振る。群雄率0.5
・ソサ2:タゲなしで3-4体武器を振る。群雄率0.5
・ソサ3:タゲなしで3確武器を振る。群雄率0.5
でデバフを漏らす確率は28.8%

例2:
・統率あり
・ソサ1:敵1タゲで3-4確武器を振る。群雄率0.5
・ソサ2:敵2タゲで3-4体武器を振る。群雄率0.5
でデバフを漏らす確率は62.5%

でした。シミュレータなので誤差はある(試行回数100万回でも0.1~0.2%程度はズレていそうな)ものの、この程度は無視していいと思います。
シミュレータ作るのはプログラミングのスキルがちょっと必要ですが、いろんなパターンを試せるので非常に便利です。

これを機にプログラミングをしてみるのはいかがでしょう。

また、そもそも5人にデバフする必要あるんだっけ?とも思っていて、何人/誰をデバフしたいかは状況次第だと思うので、そういった話をまた後日考えてみようと思います。

ーーーー
一応当初の目的のものも書きます。
群雄率0.5、統率あり、タゲなしの場合、敵5人にデバフができない確率は以下の取りでした。
・3人が3~4体武器(Ⅷの豪武器や秤)を振った場合:30.1%
・3人が4確武器(マナコ)を振った場合:13.4%

この記事が気に入ったらサポートをしてみませんか?