特殊な条件下でのハイ＆ローゲーム（Hi-Lo）を行うAIモデル

# 目次

1.ハイ＆ロー（Hi-Lo）とは何か
2.使用したライブラリと手法
3.今回の特殊な条件
4.Q学習のプログラム
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とは言ってもいくつかの手法のもとに成り立っています。
今回は、正解のデータがあるわけでもなく、明確な目的は所持金額を増やすことだけなので強化学習を使用。
強化学習の基本とされている「Ｑ学習」を使用しました。もちろんε-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です。(読者の皆さんが変えて遊んでみてください)
# Q学習のプログラム
#### 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
def play_game(initial_bet, epsilon):
    global bet
    global total
    global remaining_cards

    deck = list(range(1, 14)) * 4  # AからKまでのジョーカー抜きのデッキ
    np.random.shuffle(deck)

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

    while deck:
        # カードを引く前に状態を取得
        state = (action, total-1, len(deck))

        # ε-greedy法に基づいて次のアクションを決定
        if np.random.rand() < epsilon:
            next_action = np.random.randint(2)  # ランダムに行動を選択
        else:
            next_action = 1 if q_table[1, total-1, len(deck)] > q_table[0, total-1, len(deck)] else 0  # Q値が最大となる行動を選択

        # カードを引く
        next_card = deck.pop()

        # 次の状態の取得
        next_state = (next_action, next_card-1, len(deck))

        # プレイヤーの予測に基づいて勝敗判定
        next_card_value = q_table[:, total-1, len(deck)]
        max_next_card_value = next_card_value[next_action]

        if (action == 1 and next_card >= total):
            reward = initial_bet * odds_dict[total][0]
        elif (action == 0 and next_card < total):
            reward = initial_bet * odds_dict[total][1]
        else:
            reward = -initial_bet

        # Q値の更新
        q_table[state] = (1 - alpha) * q_table[state] + alpha * (reward + gamma * max_next_card_value)

        # カードを更新
        total = next_card
        action = next_action

    return reward
```
#### 学習率、回数、εの値
```ruby:learning_rate
epochs = 100000
bet = 1
epsilon = 0.1 
alpha = 0.1
gamma = 0.9
```
alphaとgammaを書き忘れてました。すみません(2024/05/17)
#### モデルの作成
```ruby:making_model
print(q_table)
for _ in range(epochs):
    reward = play_game(bet, epsilon)
    if _ % 100 == 0:
        print(q_table)
print(q_table)
```
# 今後の展望
今度は、安全手をなるべく多くとれるように、もう一つの有名なものであるSARSAを行いたいと思います。また、賭け金を予測に基づいて変動させるAIを作ることもよいかと思います。