ルーレット必勝法

ネットに転がってるカジノ必勝法について色々調べてた。カジノは色々なゲームがありますが今回は期待値が一番高いヨーロピアンルーレット（0が一つのタイプ）の必勝法についてシュミレーションしてみた

## ルーレットとは？
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2847174/854e501d-a4ad-451f-89a4-355324465351.png)
ルーレット：色や番号(偶数か奇数か、番号単体か、大きい数か小さい数か)で配当倍率異なる。期待値は97%(2*18/37)。緑色の0が存在することで、最終的には胴元が勝つようになっている。
## マーチンゲール法とモンテカルロ法とダランベール法
カジノで勝つためには、有名なこの三つの法則がある。カジノは運なので、掛け金を勝敗によって変えることで儲けを出す方法です

マーチンゲール法：負けたら次のゲームの掛け金を二倍にするだけ。負けるたびに掛け金が二倍になっていくので資金不足に陥りやすい。

モンテカルロ法：数列を用いて掛け金をコントロールすることで、負けた分の損失を取り戻し最終的には利益を出すことを目指す。「勝てば数列の両端を消す・負ければ掛け金を数列に追加する」を繰り返し、数列がなくなるか1個になるまで賭けを続ける。マーチンゲールより上がりかたが緩やかといえ、大連敗を喫すると逆転が難しく、損切りをする必要も出てくる。最小ベットは資金の10％程度を目安にするとよい。
initial_money=200000, count=100, unit=500
initial_money=300000, count=100, unit=700
initial_money=400000, count=100, unit=1000

ダランベール法：勝利した時にベット額を減らし、敗北した時にベット額を増やすだけ。連敗してもそこまでベット額が大きく増えない。また勝率が低くてもタイミングが良ければプラスになるのがメリット。守備型。連敗時に大損する可能性があることを防ぐため、持っている資金の10％以下である方が望ましい。

## 本題
持ち金の10%以下の掛け金のときはモンテカルロ法（MC）、10%以上の掛け金のときはダランベール法（DA）の条件分岐にしたらいいとこ取りで勝率上がるのではないか？
ということでチャットGPTの力も借りつつ実装してみた

```python:MC VS MC+DAモンテカルロ＋ダランベール
import random
import numpy as np
import matplotlib.pyplot as plt

initial_money=300000
count=100


def simulate_roulette(strategy="MC"):
    money = initial_money
    unit=(money//40000)*100
    ML = [1, 2, 3]
    d_alembert_level = 0  # ダランベール法用のカウンター（MC+DA戦略の場合のみ有効）

    for i in range(count):
        # 賭け金は ML の先頭と末尾の和
        bet = ML[0] + ML[-1]
        
        # ML の要素が少なくなったらリセット
        if len(ML) <= 2:
            ML = [1, 2, 3]
        
        num = random.random()
        # 勝ち条件：ここでは num <= (18/37) を勝ちとする
        if num <= (18/37):
            # 勝った場合
            if strategy == "MC":
                if len(ML) >= 2:
                    ML.pop()   # 末尾の要素削除
                    ML.pop(0)  # 先頭の要素削除
                money += unit * bet
            elif strategy == "MC+DA":
                if unit * bet < money * 0.1:
                    branch = "MC"
                else:
                    branch = "DA"
                if branch == "MC":
                    if len(ML) >= 2:
                        ML.pop()
                        ML.pop(0)
                    money += unit * bet
                else:  # ダランベール法の分岐
                    if d_alembert_level > 0:
                        d_alembert_level -= 1
                        delta = ((money * 0.1 * d_alembert_level) // 1000) * 1000
                        money += delta                        
                    if len(ML) >= 2:
                        ML.pop()
                        ML.pop(0)

        else:
            # 負けた場合
            if strategy == "MC":
                ML.append(bet)
                money -= unit * bet
            elif strategy == "MC+DA":
                if unit * bet < money * 0.1:
                    branch = "MC"
                else:
                    branch = "DA"
                ML.append(bet)
                if branch == "MC":
                    money -= unit * bet
                else:
                    d_alembert_level += 1
                    # 損失額は money * 0.1 とダランベールレベルの積を、1000円単位に切り捨て
                    delta = ((money * 0.1 * d_alembert_level) // 1000) * 1000
                    money -= delta
        
        if money < 0:
            return money  # 破産の場合、直ちに返す

    return money

n = int(input("シミュレーション回数を入力してください: "))

# 純粋なモンテカルロ法（MC）のシミュレーション結果
final_money_MC = []
wins_MC = 0      # 初期資金を上回った回数
total_profit_MC = 0  # 利益の総和

for i in range(n):
    final_money = simulate_roulette(strategy="MC")
    final_money_MC.append(final_money)
    if final_money > initial_money:
        wins_MC += 1
    total_profit_MC += (final_money - initial_money)

win_rate_MC = wins_MC / n
expected_profit_MC = total_profit_MC / n

# モンテカルロ法＋ダランベール法（MC+DA）のシミュレーション結果
final_money_MCDA = []
wins_MCDA = 0
total_profit_MCDA = 0

for i in range(n):
    final_money = simulate_roulette(strategy="MC+DA")
    final_money_MCDA.append(final_money)
    if final_money > initial_money:
        wins_MCDA += 1
    total_profit_MCDA += (final_money - initial_money)

win_rate_MCDA = wins_MCDA / n
expected_profit_MCDA = total_profit_MCDA / n

print("シミュレーション回数:", n)
print("【純モンテカルロ法】")
print("  初期資金を上回った回数:", wins_MC, "勝率:", f"{win_rate_MC:.2%}")
print("  期待利益:", expected_profit_MC)
print("【モンテカルロ法＋ダランベール法】")
print("  初期資金を上回った回数:", wins_MCDA, "勝率:", f"{win_rate_MCDA:.2%}")
print("  期待利益:", expected_profit_MCDA)

# ヒストグラムで各戦略の最終資金分布を比較表示
plt.figure(figsize=(12, 5))

plt.subplot(1, 2, 1)
plt.hist(final_money_MC, bins=20, edgecolor='black')
plt.xlabel("money (MC)")
plt.ylabel("times")
plt.title("MC")
plt.grid(True)

plt.subplot(1, 2, 2)
plt.hist(final_money_MCDA, bins=20, edgecolor='black')
plt.xlabel("money (MC+DA)")
plt.ylabel("times")
plt.title("MC+DA")
plt.grid(True)

plt.tight_layout()
plt.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2847174/7ceb1767-05aa-44e3-af97-a3aac84cb3bc.png)



## 結果
とりま1000回回しました。モンテカルロの勝率は6割程度、モンテカルロ＋ダランベールでは4割程度。
期待値利益もモンテカルロ法が高いという結果

モンテカルロ＋ダランベール法は破産しないけどしっかり損する方法ということでした