OpenAI Gym で OXゲーム

# はじめに

OpenAI Gym で OXゲーム をする方法の備忘録です。
このGymをUIと連携したり、強化学習と使ったり、発展したゲームを作ったりする予定です。

ChatGPTに手伝ってもらって1時間くらいで作成しました。

# 出来たもの

Gymの環境をつくりました。
大人モードとランダムなエージェントとの対戦をできるようにしました。

領地の獲得は次のように番号と場所を対応させました。

```
|0|1|2|
|3|4|5|
|6|7|8|
```

下記のように、コマンドラインから動くようにしました。

```
% python gym_oxgame.py
Enter your play mode (0 or 1)
 0: 2 Player
 1: VS Random agent
input : 1
|_|_|_|
|_|_|_|
|_|_|_|

It is the turn of X.
Enter your move (0-8): 4
Player's action: 4
|_|_|_|
|_|X|_|
|_|_|_|

It is the turn of O.
Agent's action: 0
|O|_|_|
|_|X|_|
|_|_|_|

It is the turn of X.
Enter your move (0-8): 2
Player's action: 2
|O|_|X|
|_|X|_|
|_|_|_|

It is the turn of O.
Agent's action: 1
|O|O|X|
|_|X|_|
|_|_|_|

It is the turn of X.
Enter your move (0-8): 6
Player's action: 6
You win!
```

# 作ったもの

## Gymの環境

```python
import gym
from gym import spaces
from gym.utils import seeding
import numpy as np


class OXGameEnv(gym.Env):
    def __init__(self):
        self.board = np.zeros((3, 3))  # 3x3の盤面を作成
        self.current_player = 1  # 最初のプレイヤーは1番
        self.action_space = spaces.Discrete(9)  # 行動空間は0から8までの整数
        self.observation_space = spaces.Box(low=-1, high=1, shape=(3, 3), dtype=np.int32)  # 盤面の状態を表す
        self.seed()

    def seed(self, seed=None):
        self.np_random, seed = seeding.np_random(seed)
        return [seed]

    def reset(self):
        self.board = np.zeros((3, 3))
        self.current_player = 1
        return self.board

    def step(self, action):
        row = action // 3
        col = action % 3

        if self.board[row][col] != 0:
            return self.board, -10, True, {}  # すでに占有された場所には置けない

        self.board[row][col] = self.current_player

        if self._is_winner(self.current_player):
            reward = 10
            done = True
        elif self._is_board_full():
            reward = 0
            done = True
        else:
            reward = 0
            done = False

        self.current_player = -self.current_player  # プレイヤーを交代する

        return self.board, reward, done, {}

    def _is_winner(self, player):
        # 行で勝利したかどうか
        for i in range(3):
            if all(self.board[i] == player):
                return True
        # 列で勝利したかどうか
        for i in range(3):
            if all(self.board[:, i] == player):
                return True
        # 対角線で勝利したかどうか
        if all(self.board.diagonal() == player) or all(np.fliplr(self.board).diagonal() == player):
            return True
        return False

    def _is_board_full(self):
        return not (self.board == 0).any()

    def render(self, mode='human'):
        for row in self.board:
            print('|', end='')
            for col in row:
                if col == 1:
                    print('X', end='|')
                elif col == -1:
                    print('O', end='|')
                else:
                    print('_', end='|')
            print('')
        print('')
```

## Gymを動作せる部分

```python
class RandomAgentV1:
    def __init__(self, env):
        self.action_space = env.action_space

    def choose_action(self):
        return self.action_space.sample()  # 行動空間からランダムに行動を選択する


class RandomAgentV2:
    def __init__(self, env):
        self.env = env

    def choose_action(self):
        valid_actions = np.where(self.env.board == 0)[0]  # 空の位置を取得
        return np.random.choice(valid_actions)  # 空の位置からランダムに行動を選択


def vs_player(env):
    _ = env.reset()

    done = False
    while not done:
        env.render()
        print("It is the turn of {}.".format('X' if env.current_player == 1 else 'O'))
        action = int(input("Enter your move (0-8): "))
        observation, reward, done, info = env.step(action)
        print(f"Reward: {reward}")
        if done:
            if reward == 10:
                print("You win!")
            elif reward == 0:
                print("It's a draw!")


def vs_random_agent(env):
    agent = RandomAgentV2(env)

    _ = env.reset()
    done = False
    while not done:
        env.render()
        print("It is the turn of {}.".format('X' if env.current_player == 1 else 'O'))
        if env.current_player == 1 :
            action = int(input("Enter your move (0-8): "))
            print(f"Player's action: {action}")
        else:
            action = agent.choose_action()
            print(f"Agent's action: {action}")
        obs, reward, done, info = env.step(action)
        if done:
            if reward == 10:
                print("You win!")
            elif reward == 0:
                print("It's a draw!")


def main():
    env = OXGameEnv()
    _ = env.reset()

    play_mode = int(input("Enter your play mode (0 or 1)\n 0: 2 Player\n 1: VS Random agent\ninput : "))
    if play_mode == 0:
        vs_player(env)
    elif play_mode == 1:
        vs_random_agent(env)


if __name__ == '__main__':
    main()
```