SARSAを用いたHigh&Low

# 目次

1.ハイ＆ロー（Hi-Lo）とは何か
2.使用したライブラリと手法
3.今回の特殊な条件
4.SARSAのプログラム
5.今後の展望

※ミスなどがありましたら報告願います
# 1. ハイ&ロー(Hi-Lo)とは何か
ハイ＆ローは、トランプを用いるカードゲームで、名前の通り、高い(high)か低い(low)かを当てる単純なゲームです。
単純なだけに細かい所で色々ルールが派生したり違ったりすることもあります。
最近では、オンラインカジノサイトなどで有名なため、知っている方も多いと思います。
今回は、オンラインカジノでのハイ＆ローのルールを採用していきます。
### 手順
1.jokerを除いた52枚のトランプをよくシャッフルし、山札を裏向きにしておく
2.ディーラーが山札の一番上を表にして前に置く
3.プレイヤーは次に山札から出るカードが今開示されているカードより高い(high)か低い(low)かを予想する
（カードの大きさは、小さい順に2,3,4,5,6,7,8,9,10,J,Q,K,A）
4.ディーラーは伏せられていたカードを開示する
5.(1)予想が正しかった場合は、オッズ表(リンクにあるものをそのまま使っています)に基づきディーラーがプレイヤーに報酬を支払う
  (2)予想が外れた場合は、ディーラーがプレイヤーの賭け金額を没収
6.先ほど開示されたカードをもとに次のカードの予想を行う
7.52枚のカードがなくなるまで1→6を繰り返す

＜注＞カジノサイトによってはカードを3枚出して戻すというところもあります。

※手順をお借りしたサイト
https://www.casino-winnersclub.com/high-low.php#haiandorono_liure (このサイトのオッズ表です)
https://edgegram.hatenablog.jp/entry/2018/09/05/172349
# 使用したライブラリ
使用した言語はpythonです。環境にJupyter Notebookを使用しました。
```
- python 3
```
次にライブラリを紹介しましょう。
```
- numpy as np
- pandas as pd
```
pandasはデータをまとめるために使用しました。別にimportしなくても構いません
# 使用した手法
AIとは言ってもいくつかの手法のもとに成り立っています。
今回もQ学習の記事と同様、正解のデータがあるわけでもなく、明確な目的は所持金額を増やすことだけなので強化学習を使用。
強化学習の基本とされている「SARSA」を使用しました。もちろんε-greedy法はありです。
ε-greedy法のおかげで今回は、ノイズも含めてなるべく安全な道をとり続けてくれるものが得られそうです。

# 今回の特殊な条件
・今回はわかりやすいようにカードの大きい順を変えます。
本来:(2,3,4,5,6,7,8,9,10,J,Q,K,A)
今回:(A,2,3,4,5,6,7,8,9,10,J,Q,K)
・数字が同じだった場合について"Same"という選択肢のあるものや、"High"に含めるもの、"Low"に含めるものがありますが、今回"Same"は"High"に含めるものを採用します。
・スート(マーク)による大小(例:♠>❤など)はなしです。
・バーストはなしです
・賭けられる金額は1です。(読者の皆さんが変えて遊んでみてください)
# SARSAのプログラム
#### Qテーブルの定義
```ruby:base_Q_table
num_actions = 2
num_total_values = 13
num_remaining_cards = 52 
q_table_shape = (num_actions, num_total_values, num_remaining_cards)
q_table = np.ones(q_table_shape)
```
残りのカードの枚数が学習に大事なので三次元によるQ学習になっています。
#### オッズ表を定義
```ruby:Odds_dict
odds_dict = {
    1: (1.00, 13.4),
    2: (1.04, 6.30),
    3: (1.10, 4.20),
    4: (1.30, 3.30),
    5: (1.45, 2.70),
    6: (2.10, 1.50),
    7: (1.80, 1.80),
    8: (1.50, 2.10),
    9: (2.70, 1.45),
    10: (3.30, 1.30),
    11: (4.20, 1.10),
    12: (6.30, 1.04),
    13: (13.4, 1.00)
}

```
#### ゲームをプレイする関数
```ruby:play_game
# ε-greedy法に基づいて行動を選択
def choose_action(state, epsilon):
    if np.random.rand() < epsilon:
        return np.random.choice([0, 1])  # ランダムに行動を選択
    else:
        return np.argmax(q_table[:, state[1], state[2]])  # Qテーブルに基づいて最適な行動を選択
#ここからgameの関数
def play_game(initial_bet, epsilon):
    deck = list(range(1, 14)) * 4  # AからKまでのジョーカー抜きのデッキ
    np.random.shuffle(deck)

    # カードの初期状態
    total = deck.pop()  # 初めに開示されるカード
    state = (1, total-1, len(deck))  # 初期状態
    action = choose_action(state, epsilon)  # 初期アクション
    reward = 0  # 報酬の初期化

    while deck:
        # カードを引く
        next_card = deck.pop()
        next_state = (action, next_card-1, len(deck))
        next_action = choose_action(next_state, epsilon)

        # プレイヤーの予測に基づいて勝敗判定
        if (action == 1 and next_card > total) or (action == 0 and next_card < total):
            if action == 1:  # 勝利の種類の判定(ハイorロー)oddsもかける
                reward = initial_bet * odds_dict[total][1]
            else:
                reward = initial_bet * odds_dict[total][0]
        else:
            reward = -initial_bet  # 敗北

        # SARSAのQ値更新
        q_table[state[0], state[1], state[2]] += alpha * (
            reward + gamma * q_table[next_action, next_state[1], next_state[2]] - q_table[state[0], state[1], state[2]])

        # 状態と行動を更新
        state = next_state
        action = next_action
        total = next_card

    return reward
```
※前回かなり見にくかったので、ε-greedyを関数にしてわかりやすくしてみました。
#### 学習率、回数、εの値
```ruby:learning_rate
epochs = 100000
bet = 1
epsilon = 0.1  
alpha = 0.1
gamma = 0.9
```
#### モデルの作成
```ruby:making_model
print(q_table)
for _ in range(epochs):
    reward = play_game(bet, epsilon)
    if _ % 100 == 0:
        print(q_table)
print(q_table)
```
# 今後の展望
今までは、説明可能AIを作成してきました。次にhigh&lowゲームの記事を書くときは深層学習のものつまり、我々の視覚では説明不可なものを使っていきたいと思います。

# 雑談（やるきっかけ）
前回のものと少し期間があいてしまって、すみませんでした。前回の記事が100viewを突破したと友人から聞きまして、急いで作った次第です。