2015年12月29日火曜日

グラブルのビンゴの期待値をシミュレートしてみた

プログラムは下の方に添付。バグがあればメールかコメントで言ってください。

1000万回試行、チャンスボールの出し方は3種類

押せたらすぐ
NO BINGO: 8284552 (82.8455%)
BINGO: 1710841 (17.1084%)
SUPER BINGO: 4607 (0.0461%)

押せてリーチしてたら出す
NO BINGO: 8260057 (82.6006%)
BINGO: 1735077 (17.3508%)
SUPER BINGO: 4866 (0.0487%)

絶対ラストボールのあとに出す
NO BINGO: 8198231 (81.9823%)
BINGO: 1796909 (17.9691%)
SUPER BINGO: 4860 (0.0486%)

部屋毎の期待値もシミュレートしてみた
パネルとボールの変異が逆転してるけど多分理論上は同じ

20人部屋 期待値約1.1倍+SB期待値約0.36倍(MAX BET時)
NO BINGO: 8283840 (82.8384%)
10x BINGO: 692406 (6.9241%)
5x BINGO: 630924 (6.3092%)
3x BINGO: 322916 (3.2292%)
2x BINGO: 65091 (0.6509%)
0x BINGO: 4717 (0.0472%)
SUPER BINGO: 4702 (0.0470%)

10人部屋 期待値約1.33倍+SB期待値約0.36倍(MAX BET時)
NO BINGO: 8283393 (82.8339%)
10x BINGO: 1033286 (10.3329%)
5x BINGO: 547034 (5.4703%)
3x BINGO: 125915 (1.2592%)
2x BINGO: 10119 (0.1012%)
0x BINGO: 251 (0.0025%)
SUPER BINGO: 4584 (0.0458%)

5人部屋 期待値約1.51倍+SB期待値約0.36倍(MAX BET時)
NO BINGO: 8284857 (82.8486%)
10x BINGO: 1345011 (13.4501%)
5x BINGO: 340146 (3.4015%)
3x BINGO: 29339 (0.2934%)
2x BINGO: 641 (0.0064%)
0x BINGO: 6 (0.0001%)
SUPER BINGO: 4718 (0.0472%)

いやーきついっす
ていうかSB率って0.0458%とかよく引けたなおい!二回目を引けるイメージがねぇぞ!
そしてチャンスボールのタイミングは誤差レベルじゃねぇか!!!
さて期待値が1以上であることがわかったので仕事しながらビンゴに帰るか(白目を剥きながら)
この全然面白くない作業苦行のくせに大事すぎるクソコンテンツなんとかしろやHRTァ!!!!


おまけ、20人部屋時のフリーボール数による勝率

6個
NO BINGO: 747285 (74.7285%)
10x BINGO: 107912 (10.7912%)
5x BINGO: 90627 (9.0627%)
3x BINGO: 44736 (4.4736%)
2x BINGO: 8818 (0.8818%)
0x BINGO: 611 (0.0611%)
SUPER BINGO: 639 (0.0639%)

5個
NO BINGO: 836896 (83.6896%)
10x BINGO: 63843 (6.3843%)
5x BINGO: 60751 (6.0751%)
3x BINGO: 31550 (3.1550%)
2x BINGO: 6460 (0.6460%)
0x BINGO: 488 (0.0488%)
SUPER BINGO: 440 (0.0440%)

4個
NO BINGO: 900567 (90.0567%)
10x BINGO: 35357 (3.5357%)
5x BINGO: 38483 (3.8483%)
3x BINGO: 20887 (2.0887%)
2x BINGO: 4392 (0.4392%)
0x BINGO: 306 (0.0306%)
SUPER BINGO: 338 (0.0338%)

4個引いただけで絶望だな!
最後までやると1分20秒かかるけど3個めで上がると40秒。1分計算だと2200分=36時間プレイでSB期待値だぞ!やったな!


import java.util.ArrayList;
import java.util.Random;

public class Main {

 static final int NO_BINGO = 0;
 static final int BINGO = 1;
 static final int SUPER_BINGO = 2;

 static final int PLAYER = 20;

 static ArrayList<ArrayList<Integer>> pool = new ArrayList<ArrayList<Integer>>(PLAYER);
 static int[][] panel = new int[PLAYER][25];
 static int[][] lines = new int[12][5];
 static boolean[] finished = new boolean[PLAYER];
 static Random r = new Random();

 static {
  lines[0] = new int[] { 0, 1, 2, 3, 4 };
  lines[1] = new int[] { 5, 6, 7, 8, 9 };
  lines[2] = new int[] { 10, 11, 12, 13, 14 };
  lines[3] = new int[] { 15, 16, 17, 18, 19 };
  lines[4] = new int[] { 20, 21, 22, 23, 24 };
  lines[5] = new int[] { 0, 5, 10, 15, 20 };
  lines[6] = new int[] { 1, 6, 11, 16, 21 };
  lines[7] = new int[] { 2, 7, 12, 17, 22 };
  lines[8] = new int[] { 3, 8, 13, 18, 23 };
  lines[9] = new int[] { 4, 9, 14, 19, 24 };
  lines[10] = new int[] { 0, 6, 12, 18, 24 };
  lines[11] = new int[] { 4, 8, 12, 16, 20 };
 }

 public static void main(String[] args) throws Exception {
  int trial = 1000000;
  // int result[] = new int[] { 0, 0, 0 };
  // for (int t = 0; t < trial; t++) {
  // result[trial()]++;
  // }
  // System.out.println(String.format("NO BINGO: %d (%.4f%%)",
  // result[NO_BINGO], 100.0 * result[NO_BINGO] / trial));
  // System.out.println(String.format("BINGO: %d (%.4f%%)", result[BINGO],
  // 100.0 * result[BINGO] / trial));
  // System.out.println(String.format("SUPER BINGO: %d (%.4f%%)",
  // result[SUPER_BINGO], 100.0 * result[SUPER_BINGO] / trial));
  int result[] = new int[] { 0, 0, 0, 0, 0, 0, 0 };
  for (int t = 0; t < trial; t++) {
   result[trial()]++;
   if (isBingo(0) == SUPER_BINGO) {
    result[6]++;
   }
  }
  System.out.println(String.format("NO BINGO: %d (%.4f%%)",
    result[0], 100.0 * result[0] / trial));
  System.out.println(String.format("10x BINGO: %d (%.4f%%)",
    result[1], 100.0 * result[1] / trial));
  System.out.println(String.format("5x BINGO: %d (%.4f%%)",
    result[2], 100.0 * result[2] / trial));
  System.out.println(String.format("3x BINGO: %d (%.4f%%)",
    result[3], 100.0 * result[3] / trial));
  System.out.println(String.format("2x BINGO: %d (%.4f%%)",
    result[4], 100.0 * result[4] / trial));
  System.out.println(String.format("0x BINGO: %d (%.4f%%)",
    result[5], 100.0 * result[5] / trial));
  System.out.println(String.format("SUPER BINGO: %d (%.4f%%)",
    result[6], 100.0 * result[6] / trial));
 }

 static int trial() {
  // Initialize
  int div = 1;
  int myDiv = 0;
  int chance[] = new int[PLAYER];
  pool.clear();
  for (int p = 0; p < PLAYER; p++) {
   pool.add(new ArrayList<Integer>(25));
   finished[p] = false;
   chance[p] = 0;
   for (int i = 0; i < 25; i++) {
    pool.get(p).add(i);
    panel[p][i] = 0;
   }
   panel[p][12] = 9;
  }

  // Draw free balls
  boolean hasBingoInFree = false;
  for (int p = 0; p < PLAYER; p++) {
   int freeBalls = 4 + r.nextInt(3);
   for (int i = 0; i < 6; i++) {
    int n = draw(p);
    int star = n / 100;
    int pos = n % 100;
    if (i < freeBalls && pos != 12) {
     panel[p][pos] = star;
    }
   }
   if (isBingo(p) != NO_BINGO) {
    hasBingoInFree = true;
    finished[p] = true;
    if (p == 0) {
     myDiv = 1;
    }
   }
  }
  if (hasBingoInFree) {
   div++;
  }

  // Five draws and chance ball
  for (int i = 0; i < 5; i++) {
   boolean hasBingo = false;
   for (int p = 0; p < PLAYER; p++) {
    if (finished[p]) {
     continue;
    }
    int n = draw(p);
    int star = n / 100;
    int pos = n % 100;
    if (pos != 12) {
     panel[p][pos] = star;
     chance[p]++;
    }
    // if (chance >= 3 && isReach()) {
    // if (chance >= 3 && i == 4) {
    if (chance[p] >= 3) {
     int c = drawChance(p);
     panel[p][c] = 2;
     chance[p] = -999;
    }
    if (isBingo(p) != NO_BINGO) {
     hasBingo = true;
     if (p == 0) {
      myDiv = div;
     }
     finished[p] = true;
    }
   }
   if (hasBingo) {
    div++;
   }
  }

  // // Output
  // StringBuilder b = new StringBuilder();
  // for (int j = 0; j < 25; j++) {
  // b.append(panel[j]);
  // if (j % 5 == 4) {
  // b.append("\n");
  // }
  // }
  // System.out.println(b.toString());
  // System.out.println("Result is " + isBingo());

  return myDiv;
 }

 static int draw(int player) {
  int index = r.nextInt(pool.get(player).size());
  int num = pool.get(player).remove(index);
  int color = r.nextInt(5) == 0 ? 200 : 100;
  return num + color;
 }

 static int drawChance(int player) {
  int index;
  do {
   index = r.nextInt(pool.get(player).size());
  } while (pool.get(player).get(index) == 12);
  return pool.get(player).get(index);
 }

 static int isBingo(int player) {
  for (int[] line : lines) {
   boolean isBingo = true;
   boolean isSuperBingo = true;
   for (int i = 0; i < 5; i++) {
    if (panel[player][line[i]] == 0) {
     isBingo = false;
     isSuperBingo = false;
     break;
    } else if (panel[player][line[i]] == 1) {
     isSuperBingo = false;
    }
   }
   if (isSuperBingo) {
    return SUPER_BINGO;
   } else if (isBingo) {
    return BINGO;
   }
  }
  return NO_BINGO;
 }

 static boolean isReach(int player) {
  for (int[] line : lines) {
   int c = 0;
   for (int i = 0; i < 5; i++) {
    if (panel[player][line[i]] > 0) {
     c++;
    }
   }
   if (c == 4) {
    return true;
   }
  }
  return false;
 }
}