賭け戦略シミュレーション1（定額・定率・マーチンゲール法）

Jupyter Notebookの練習として、各種賭け戦略のシミュレーションを行いました。

## 賭けのルール

- サイコロの奇数・偶数を当てる
- 好きな額を事前に賭けられる
- 当たれば、賭けた額×2が払い戻される
- 外れたら、賭けた額は没収
- 借金禁止
- 開始資金は10000コイン
- 1000回賭けられる
- 各回で、全参加者は同じ側に賭ける（当たるタイミングや回数が同一）

本記事では、勝率を約50％、52％、48％と変え数ラウンドシミュレーションします。
サイコロの振り方が悪いため、各ラウンドの勝率にはブレが出ています。くじ引き式にすべきでした。

## 戦略の説明

- 定額：必ず一定額を賭けます
- 定率：持ち金の一定割合を賭けます

### マーチンゲール法

ある額から賭け始め、負けるたび倍に増やします。勝ったらもとの額に戻します。
必勝法として有名です。負けが続いた場合、たちまち賭け金が増えていくため、実際は次に示すような形で打ち切りとなることが多いでしょう。

- 手持ち資金が尽きて、賭けられなくなる
- 賭場の賭け上限にかかり、倍額を賭けられなくなる

## 結果（勝率約50%）
![484.png](https://qiita-image-store.s3.amazonaws.com/0/142637/85e9ced5-4c8d-bddc-2410-460bff336e10.png)![485.png](https://qiita-image-store.s3.amazonaws.com/0/142637/591a8744-ce40-3060-e818-bb71b72d6996.png)
![501.png](https://qiita-image-store.s3.amazonaws.com/0/142637/5adac7e2-a2ec-a3ef-983f-b5827ae01eba.png)![515.png](https://qiita-image-store.s3.amazonaws.com/0/142637/d99f0cad-8b5b-d5d2-94c3-7aeec4723f98.png)


## 結果（勝率約48%）
![457.png](https://qiita-image-store.s3.amazonaws.com/0/142637/5544ecc4-991d-6158-09b1-bff9e44e8cd7.png)![483.png](https://qiita-image-store.s3.amazonaws.com/0/142637/8c51a97d-16a0-7b1a-41ba-673939db70fe.png)

## 結果（勝率約52%）
![507.png](https://qiita-image-store.s3.amazonaws.com/0/142637/c6b7ce5a-f0b1-1f7d-21ab-5b25948a7f85.png)![541.png](https://qiita-image-store.s3.amazonaws.com/0/142637/0a7bb44e-e5bd-9c91-cdf2-33717d9c62d0.png)

## 実験コード

```python:
import numpy as np
import seaborn as sns
import math
from pprint import pprint as pp

%matplotlib inline
sns.set(style='ticks', font=['Myrica M'])
# 定額
class FixedAmount:
    def __init__(self, name, coin, first_bet):
        self.name = name
        self.coin = coin
        self.first_bet = first_bet
        self.next_bet = first_bet
        self.history = [coin]

    def refound(self, coin):
        self.coin += coin
        self.next_bet = min(self.first_bet, self.coin)
        self.history.append(self.coin)

    def bet(self):
        self.coin -= self.next_bet
        return self.next_bet


# 定率
class FixedRate:
    def __init__(self, name, coin, rate):
        self.name = name
        self.coin = coin
        self.rate = rate
        self.next_bet = math.floor(self.coin * self.rate)
        self.history = [coin]

    def refound(self, coin):
        self.coin += coin
        self.next_bet = math.floor(self.coin * self.rate)
        self.history.append(self.coin)

    def bet(self):
        self.coin -= self.next_bet
        return self.next_bet


# マーチンゲール法（負けたら倍賭け）
class Martingale:
    def __init__(self, name, coin, first_bet):
        self.name = name
        self.coin = coin
        self.first_bet = first_bet
        self.next_bet = first_bet
        self.history = [coin]

    def refound(self, coin):
        self.coin += coin
        if coin == 0:
            self.next_bet *= 2
        else:
            self.next_bet = self.first_bet
        self.next_bet = min(self.next_bet, self.coin)
        self.history.append(self.coin)

    def bet(self):
        self.coin -= self.next_bet
        return self.next_bet


def winning_list(winning_persentage, k):
    return np.random.choice(2, k, p=(1-winning_persentage, winning_persentage))

START_COIN = 10000
K = 1000
strategies = (
    FixedAmount('定額(300)', START_COIN, 300),
    FixedAmount('定額(100)', START_COIN, 100),
    FixedRate('定率(3%)', START_COIN, 0.03),
    FixedRate('定率(1%)', START_COIN, 0.01),
    Martingale('マーチンゲール(10開始)', START_COIN, 10),
    Martingale('マーチンゲール(2開始)', START_COIN, 2),
)

wl = winning_list(0.5, K)
losses = 0
max_losses = 0
for result in wl:
    if result:
        losses = 0
    else:
        losses += 1
    max_losses = max(losses, max_losses)
    for strategy in strategies:
        bet_coin = strategy.bet()
        refound_coin = (0, bet_coin * 2)[result]
        strategy.refound(refound_coin)

x = np.arange(0, K+1)
fig = sns.mpl.pyplot.figure()
fig.clear()
ax = fig.add_subplot(111)
ax.clear()
for strategy in strategies:
    ax.plot(x, np.asarray(strategy.history, dtype=np.int32), label=strategy.name)
ax.legend()
print('勝ち{}/{} 連続負け{}'.format(sum(wl), K, max_losses))
```