Pythonで〇×ゲームのAIを一から作成する　その22　手番の実装

# 目次と前回の記事

https://qiita.com/ysgeso/items/2381dd4e3283cbed49a0

https://qiita.com/ysgeso/items/ad5e7ddb071653fd2ac5

# 実装の進捗状況と前回までのおさらい

## 〇×ゲームの仕様と進捗状況

1. `正方形で区切られた 3 x 3 の 2 次元のゲーム盤上でゲームを行う`
2. `ゲーム開始時には、ゲーム盤の全てのマスは空になっている`
3. `2 人のプレイヤーが遊ぶゲームであり、一人は 〇 を、もう一人は × のマークを受け持つ`
4. __2 人のプレイヤーは、交互に空いている好きなマスに自分のマークを 1 つ置く__
5. 先手は 〇 のプレイヤーである
6. プレイヤーがマークを置いた結果、縦、横、斜めのいずれかの一直線の 3 マスに同じマークが並んだ場合、そのマークのプレイヤーの勝利とし、ゲームが終了する
7. すべてのマスが埋まった時にゲームの決着がついていない場合は引き分けとする

仕様の進捗状況は、以下のように表記します。

* 実装が完了した部分を `背景が灰色の長方形` で記述する
* 実装の一部が完了した部分を、__太字__ で記述する

## これまでに作成したモジュール

以下のリンクから、これまでに作成したモジュールを見ることができます。

https://github.com/ysgeso/marubatsu/blob/master/022/marubatsu.py

## 前回までのおさらい

前回までの記事で、クラスに関する説明を行いました。実装を進めるために必要な知識の説明を行ったため、長らく〇×ゲームの実装が滞っていましたが、重要な知識はかなり説明できたと思います。

# 手番の実装

下記の仕様 4 から、〇×ゲームは、__2 人のプレイヤー__ が遊ぶゲームであり、__交互にマークを置く__ ことから、__どのプレイヤー__ が次にマークを置くかを __表す__、__手番__ があることがわかります。そこで、今回の記事では __手番に関する実装__ を行うことにします。

4\. 2 人のプレイヤーは、交互に空いている好きなマスに自分のマークを 1 つ置く

## 手番を表すデータ構造

[過去の記事](https://qiita.com/ysgeso/items/0d64d1ddba1982b5a5d3#%E3%82%B2%E3%83%BC%E3%83%A0%E7%9B%A4%E3%81%AE%E3%83%87%E3%83%BC%E3%82%BF%E6%A7%8B%E9%80%A0) で説明したように、__データ__ をプログラムで __表現__ するための __形式__ の事を __データ構造__ と呼びます。手番を実装するためには、__手番を表すデータ構造__ を __決める__ 必要があります。

初心者の方は、手番のデータ構造を決めろと言われてもうまく思いつかない人が多いのではないかと思います。そのような場合は、__手番__ をプログラムではなく、__人間の言葉で表現__ する方法について考えてみると良いでしょう。下記は、筆者がぱっと思いついた手番の表現方法ですが、他にも様々な方法が考えられるでしょう。

* 仕様 3 から、プレイヤーは __〇__ か __×__ の __マークを受け持つ__ ので、このマークを使って「__〇 の手番__」、「__× の手番__」のように表現する
* 最初にマークを配置するプレイヤーを「__先手__」、もう片方のプレイヤーを「__後手__」とし、「__先手の手番__」、「__後手の手番__」のように表現する
* __プレイヤーの名前__ を使って手番を表現する

上記の方法は、いずれも手番を __文字__ を使って __表現__ しています。従って、上記の表現方法を __プログラムで行う__ 場合は、手番を __文字列__ を使って __表現__ することになります。上記のいずれの方法を採用しても〇×ゲームを実装することは可能ですが、これまでに実装してきた〇×ゲームでは、マスに配置した 〇 や × の __マーク__ を、__文字列型__ の __`"o"`__ と __`"x"`__ で表現しました。__同じ種類のデータ__ を __異なる方法__ で表現すると、プログラムが __わかりづらくなる__ ので、__手番__ を表すデータも __同じ方法__ で表現することにします。

:::note warn
本記事では、手番を __文字列型のデータ__ で、ゲーム盤のマスに配置した __マークと同じデータ構造__（〇 は `"o"`、× は `"x"`）で表現することにします。
:::

:::note
プログラムで手番を表現するデータ構造として良く使われる方法に、__整数__ を使って表現する方法があります。例えば、__先手を `0`__、__後手を `1`__ で表現するという方法です。今回の記事ではこの方法は採用しませんが、この方法については今後の記事で実際に紹介します。

他にも、プレイヤーが __2 人の__ ゲームの場合は、__先手__ を論理型の __`True`__、__後手__ を __`False`__ で表現するという方法などがあります。このように、手番を表すデータ構造は、扱いやすさの事を考慮しなければ __いくらでも考える__ ことができます。
:::



## 手番を管理する属性と手番の初期化

手番を表すデータ構造が決まったので、次は手番を表すデータを __どこに記録するか__ について __決める__ 必要があります。これまでに実装したプログラムでは、__ゲーム盤を表すデータ__ を `Marubatsu` クラスの __インスタンスの `board` 属性__ に代入ました。そこで、手番を表すデータも同様に、`Marubatsu` クラスの __インスタンスの属性__ に代入することにします。手番は英語で __turn__ と表記するので、__属性の名前__ を __`turn`__ にすることにします。

次に、インスタンスの `turn` 属性の値を __いつ__、__どのような値__ に __設定する__ かについて考える必要があります。下記の仕様 5 から、〇×ゲームの __開始時__ に __〇 の__ プレイヤーの __手番になる__ ことがわかるので、__`__init__` メソッドの中__ でその処理を行うことにします。

5\. 先手は 〇 のプレイヤーである

下記のプログラムは 1 行目で `marubatsu` モジュールから `Marubatsu` クラスをインポートし、8 行目で `Marubatsu` クラスの __`__init__` メソッド__ を、3 ~ 6 行目で定義した関数で __上書きして修正__ しています。

元の `__init__` メソッド からの修正箇所は、6 行目で `Marubatsu` クラスのインスタンスの __`turn` 属性__ に、〇 を表す __`Marubatsu.CIRCLE` を代入__ している点です。

```python
1  from marubatsu import Marubatsu
2
3  def __init__(self, board_size=3):
4      self.BOARD_SIZE = board_size
5      self.initialize_board()
6      self.turn = Marubatsu.CIRCLE
7
8  Marubatsu.__init__ = __init__
```

<details><summary><strong>行番号のないプログラム</strong></summary>

```python
from marubatsu import Marubatsu

def __init__(self, board_size=3):
    self.BOARD_SIZE = board_size
    self.initialize_board()
    self.turn = Marubatsu.CIRCLE

Marubatsu.__init__ = __init__
```
</details>

<details><summary><strong>修正箇所</strong></summary>

```diff_python
def __init__(self, board_size=3):
    self.BOARD_SIZE = board_size
    self.initialize_board()
+   self.turn = Marubatsu.CIRCLE
```
</details>

下記のプログラムは、1 行目で `Marubatsu` クラスのインスタンスを作成し、2 行目でその `turn` 属性を表示しています。実行結果に、〇 を表す文字列である `o` が表示されていることから、上記の修正が正しく実装されていることが確認できます。

```python
mb = Marubatsu()
print(mb.turn)
```
実行結果
```
o
```

## 〇×ゲームの初期化を行うメソッドの定義

ゲーム盤の初期化と、手番の初期化は、いずれも __〇×ゲーム__ を __新しく開始__ する際に __必要となる__ 処理です。このような処理は、`Marubatsu` クラスからインスタンスを __作成した直後以外__ でも、例えば〇×ゲームの __決着がついた後__ で、__新しく〇×ゲームを始める__ 場合など __でも__ 必要となります。そこで、この 2 つの処理を記述した、__〇×ゲームの初期化__ を行う __メソッドを定義__ する事にします。

初期化は英語で `initialize` ですが、これをそのままメソッドの名前にすると、`__init__` と __名前が似ていて__ 若干 __紛らわしい__ 気がするので本記事では __再起動__ を表す __`restart`__ という名前のメソッドにすることにします。

:::note
他の名前の候補としては、`reset` などが挙げられるでしょう。変数、関数、メソッドなどの名前をどのように付けるかは、__人によって個性が出る__ 部分だと思います。また、名前の付け方に __唯一の正解はありません__。このメソッドの名前に限らず、他の名前の方が良いと思った方は、変更するのが面倒でなければ自由に変更してもらってもかまいません。ただし、プログラムがわかりにくくなるので、名前は __一貫した法則__ で付けることを強くお勧めします。
:::

`restart` メソッドで行う処理は、現時点では __ゲーム盤の初期化__ と __手番の初期化__ なので、下記のプログラムのように定義することが出来ます。

```python
def restart(self):
    self.initialize_board()
    self.turn = Marubatsu.CIRCLE

Marubatsu.restart = restart
```

また、`__init__` メソッドを `restart` を使って下記のように定義し直すことにします。

:::note
〇×ゲームの初期化を行う際に、ゲーム盤のサイズを変更する必要はないので、`self.BOARD_SIZE = board_size` の処理は、インスタンスを作成する際に `__init__` メソッドの中 __一度だけ実行__ すれば十分です。
:::

```python
def __init__(self, board_size=3):
    self.BOARD_SIZE = board_size
    self.restart()

Marubatsu.__init__ = __init__
```

<details><summary><strong>修正箇所</strong></summary>

```diff_python
def __init__(self, board_size=3):
    self.BOARD_SIZE = board_size
-   self.initialize_board()
-   self.turn = Marubatsu.CIRCLE
+   self.restart()
```
</details>


下記は、Marubatsu クラスが正しく動作するかどうかを確認するプログラムです。実行結果から、〇×ゲームの開始時に 〇 の手番になっていることが確認できます。

```python
mb = Marubatsu()
print(mb.turn)
```
実行結果
```
o
```

## 手番の表示

ここまでの修正で、手番を表す属性を追加しましたが、__`display_board`__ は __修正していません__。そのため、ゲーム盤を表示した際に、下記のプログラムの実行結果のように、ゲーム盤の __マスの情報しか表示されない__ ので、__どちらの手番であるかがわからない__ という問題があります。

```python
mb.display_board()
```
実行結果
```
...
...
...
```

そこで、`display_board` を、下記のプログラムのように __ゲーム盤の上__ にどちらの __手番__ であるかを __表示__ するように修正することにします。下記のプログラムでは、__ゲーム盤を表示する前__ の 2 行目で、__手番を表す文字列__ を __表示__ しています。

```python
1  def display_board(self):
2      print("Turn", self.turn)
3      for y in range(self.BOARD_SIZE):
4          for x in range(self.BOARD_SIZE):
5              print(self.board[x][y], end="")
6          print()
7
8  Marubatsu.display_board = display_board
```

<details><summary><strong>行番号のないプログラム</strong></summary>

```python
def display_board(self):
    print("Turn", self.turn)
    for y in range(self.BOARD_SIZE):
        for x in range(self.BOARD_SIZE):
            print(self.board[x][y], end="")
        print()

Marubatsu.display_board = display_board
```
</details>

<details><summary><strong>修正箇所</strong></summary>

```diff_python
def display_board(self):
+   print("Turn", self.turn)
    for y in range(self.BOARD_SIZE):
        for x in range(self.BOARD_SIZE):
            print(self.board[x][y], end="")
        print()

```
</details>

上記の修正を行うことで、下記のプログラムの実行結果のように、`display_board` メソッドでゲーム盤の上に手番を表す情報が表示されるようになります。

```python
mb.display_board()
```
実行結果
```
Turn o
...
...
...
```

:::note
手番を表す文字列をどこにどのように表示するかは、自由に変更してもらってもかまいません。例えばゲーム盤の下に表示する場合は、上記の 2 行目のプログラムを 6 行目の後に移動します。
:::

### メソッドの修正と名前の変更

上記の修正によって、`display_board` メソッドが、手番とゲーム盤の両方を表示するようになりました。`display_board` という名前は、ゲーム盤（board）を表示（display）するという意味なので、手番を表示するように修正したことで、__メソッドの名前__ とメソッドが行う __処理の内容__ が __一致しなくなります__。

このような場合は、メソッドの __名前を__ 例えば `display_game` のように __修正する__ という方法が考えられます。また、そのような細かい違いは __重要ではないと考えて__、メソッドの __名前を変更しない__ という方法も考えられるでしょう。どちらを選ぶかは自由ですが、メソッドの名前と処理の内容が __大きくかけ離れてしまう__ ような場合は、メソッドの __名前を修正したほうが良い__ でしょう。

なお、`display_board` メソッドに関しては、次で行う別の修正によって、このメソッドそのものが必要となくなり __削除することになる__ ので、メソッドの名前は変更しません。

### `__str__` メソッド

ところで、__〇×ゲームを表示__ する際に、`mb.display_board()` のように、__メソッドの呼び出しを記述__ するのは __面倒__ だと思いませんか？また、この方法で〇×ゲームを表示するためには、〇×ゲームを表示するためのメソッドが `display_board` であるということを __覚えておく必要__ があります。

それに対して、数値型、文字列型、list などの、Python の __組み込みデータ型を表示__ する場合は、組み込み関数 `print` を使うことで、何らかのメソッドを呼び出すことなく、__共通の記述方法__ でその内容を表示することが出来ます。

`Marubatsu` クラスのインスタンスも同様に、__`print(mb)`__ のように記述することで〇×ゲームが表示されるようになると __便利__ です。しかし、実際には `print(mb)` を実行すると下記の実行結果のような表示が行われてしまいます。

```python
print(mb)
```
実行結果
```
<marubatsu.Marubatsu object at 0x0000027E7D436550>
```

Python では、__`__str__`__ という名前の __特殊メソッド__ を __クラスに定義__ する事で、そのクラスから作成された __インスタンスを表す文字列__ を __自分で設定__ し、組み込み関数 `print` を使って __`print(mb)`__ のように記述して表示することが __できるように__ なります。

特殊メソッド __`__str__`__ は、__仮引数__ に __`self` だけ__ を持つメソッドで、__文字列型__ のデータを __返り値として返す__ ように __定義__ します。`__str__` メソッドが定義されたクラスから作成されたインスタンスを、実引数として組み込み関数 __`print` を呼び出す__ と、インスタンスから __`__str__` メソッドが呼び出され、その返り値が表示__ されるようになります。

具体例を挙げて説明します。下記のプログラムでは、`Marubatsu` クラスに、__常に `"〇×ゲーム"`__  という __文字列__ を返す `__str__` メソッドを定義しています。

```python
def __str__(self):
    return "〇×ゲーム"

Marubatsu.__str__ = __str__
```

上記の修正を行った結果、下記のプログラムの実行結果のように、__`Marubatsu` クラス__ から作成された __インスタンス__ を `print` の実引数に記述して表示すると、__常に__ `__str__` メソッドの返り値である __`"〇×ゲーム"` が表示__ されるようになります。


```
print(mb)
```
実行結果
```
〇×ゲーム
```

### `Marubatsu` クラスの `__str__` メソッドの定義

下記のプログラムは、__`print(mb)`__ によって __`mb.display_board()` と同じ表示__ が行われるように、`Marubatsu` クラスの `__str__` メソッドを修正したプログラムです。

`__str__` メソッドは、〇×ゲームを表す __文字列を返す__ 必要があるので、__`text`__ という名前の __ローカル変数__ にその文字列を代入することにします。2 ~ 6 行目で行われる処理の具体的な内容についてはこの後で説明しますが、下記のプログラムでは、2 ~ 6 行目で __ローカル変数 `text`__ に __〇×ゲームを表す文字列__ を計算して __代入__ し、7 行目で `text` を __返り値として返しています__。


```python
1  def __str__(self):
2      text = "Turn " + self.turn + "\n"
3      for y in range(self.BOARD_SIZE):
4          for x in range(self.BOARD_SIZE):
5              text += self.board[x][y]
6          text += "\n"
7      return text
8
9  Marubatsu.__str__ = __str__
```

<details><summary><strong>行番号のないプログラム</strong></summary>

```python
def __str__(self):
    text = "Turn " + self.turn + "\n"
    for y in range(self.BOARD_SIZE):
        for x in range(self.BOARD_SIZE):
            text += self.board[x][y]
        text += "\n"
    return text

Marubatsu.__str__ = __str__
```
</details>

上記のプログラムの 2 行目では、__手番を表す文字列__ を __`text` に代入__ しています。文字列を表すデータは、__`+` 演算子__ を使うことで、__結合__ することが出来ます。また、2 行目の最後に記述している __`"\n"`__ は、この __2 文字__ で __1 つの改行を表す__ 文字列です。__`\`__ は日本語のキーボードでは __`￥` が印字されたキー__ を押すことで入力できる、__バックスラッシュ__ と呼ばれる __半角の文字__ です。

__バックスラッシュ__ の __直後__ に __特定の文字を記述__ するという、__エスケープシーケンス__ という方法で、`"` や `'` で囲われた __文字列のリテラルの中__ に、改行などの __特殊な意味を持つ文字__ を記述することが出来ます。良く使われるエスケープシーケンスには、__改行を表す `\n`__ があります。エスケープシーケンスの一覧については、下記のリンク先を少し下にスクロールした所に記述されている表[^1]を参照して下さい。

https://docs.python.org/ja/3/reference/lexical_analysis.html#string-and-bytes-literals

5、6 行目に記述されている __`+=`__ は、前回の記事で使いましたが、前回の記事では簡単な説明しかしなかったので、もう少し詳しく説明します。

`+=` を使った代入文は、__累算代入文__ と呼ばれ、「__左に記述した変数の値__」と「__右に記述した式__」を __`+` 演算子__ で演算し、その __計算結果__ を左に記述した __変数に代入__ するという処理を行います。`-=`、`*=`、`/=` などを使って、同様の処理を行う累算代入文を記述することが出来ます。累算代入文の詳細については、下記のリンク先を参照して下さい。

https://docs.python.org/ja/3/reference/simple_stmts.html#augmented-assignment-statements

例えば、下記の 1 行目と 2 行目のプログラムは、同じ処理を行うプログラムです。累算代入文は、変数の値を加算するような処理を __簡潔に記述__ できるので __良く使われます__。

```python
test += self.board[x][y]
test = test + self.board[x][y]
```

:::note
`+=` の左に記述した変数に代入されている __データ型の種類__ によっては、上記の 2 つの式が __異なる処理を行う__ 場合があります。例えば、変数に list が代入されている場合は、`+=` は list の __拡張__、`+` 演算子は list の __結合__ という異なる処理を行います。list の結合と拡張に関する詳細は [過去の記事](https://qiita.com/ysgeso/items/59e51d983a32ac4bc5db#-%E6%BC%94%E7%AE%97%E5%AD%90%E3%81%AB%E3%82%88%E3%82%8B-list-%E3%81%AE%E7%B5%90%E5%90%88) を参照して下さい。

__数値型__、__文字列型__、__tuple__ のデータに対しては、上記の 2 つの式は __同一の処理__ が行われますが、__それ以外__ のデータ型に関しては __異なる処理__ が行われる __可能性がある__ ので、`+=` を利用する際には __注意が必要__ です。
:::

`__str__` メソッドに記述した、2 重の for 文のブロックの中に記述したプログラムは、`display_board` メソッドのプログラムに似ていますが、`print` の代わりに、ローカル変数 `text` に `print` で表示していた __文字列を結合する__ という処理を行う点が異なります。下記の `display_board` からの修正箇所と見比べて下さい。

__`display_board` からの修正箇所__

```diff_python
for y in range(self.BOARD_SIZE):
    for x in range(self.BOARD_SIZE):
-       print(self.board[x][y], end="")
+       text += self.board[x][y]
-   print()
+   text += "\n"
return text

```

上記の修正によって、下記のプログラムの実行結果のように、`print(mb)` を記述することで、〇×ゲームを表示することが出来るようになります。また、この修正によって、__`display_board` メソッド__ は __必要がなくなった__ ので __削除する__ ことにします。

```python
print(mb)
```

実行結果
```
Turn o
...
...
...
```

:::note
`__str__` に似た __文字列を返す__ 特殊メソッドに __`__repr__`__ メソッドがあります。この 2 つのメソッドは以下のような違いがあります。

* `__str__` メソッドは、非公式の、あるいは __表示に適した文字列__ 表現（人間にとってわかりやすい文字列）を返すように定義する。主に __`print` の実引数__ に記述した際などで利用される
* `__repr__` メソッドは、オブジェクトを表す公式の文字列を返すように定義する。`print` の実引数に記述した際には基本的には __利用されない__

詳細は、下記のリンク先を参照して下さい。

`__repr__` メソッドの説明の、「オブジェクトを表す公式の文字列」が何であるかについての説明は長くなるので省略しますが、`__str__` と `__repr__` は __異なる目的__ で定義する特殊メソッドであることは覚えておいてください。なお、`__str__` が __表示に適した文字列表現__ を返すと説明してあるように、〇×ゲームのゲーム盤を __`print` で表示__ する場合は、__`__str__` メソッドを定義すべき__ です。また、本記事では必要がないので `__repr__` メソッドは定義しません。
:::

https://docs.python.org/ja/3/reference/datamodel.html?highlight=__str__#special-method-names

## 着手の実装

手番を表す属性と、〇×ゲームの表示が実装できたので、次は __自分の手番__ でゲーム盤に __マークを配置する__ という処理を実装します。一般的に、ゲームで自分の手番で何らかの操作を行うことを __着手__ と呼びます。

これまでは、ゲーム盤のマスに __マークを配置する処理__ を __`mb.place_mark(0, 0, Marubatsu.CIRCLE)`__ のように記述してきましたが、配置する __マーク__ は __手番__ を表す、__`turn` 属性に代入__ されているので、下記のプログラムのように記述すことが出来ます。

下記のプログラムは、(0, 0) のマスに手番のマークを配置しており、実行結果から (0, 0) のマスに先手である 〇 のマークが配置されていることが確認できます。

実は、下記の実行結果には __おかしな点が 1 つ__ あります。それが何であるかについて __考えてみて下さい__。

```python
mb.place_mark(0, 0, mb.turn)
print(mb)
```
実行結果
```
Turn o
o..
...
...
```

おかしな点は、〇 のマークを配置後に、__手番が 〇のまま__ である点です。これは、マークの配置後に必要となる、__手番を入れ替える処理__ が __記述されていない__ ことが原因です。

手番を入れ替える処理は、__手番__ が __〇 の場合は ×__ に、__× の場合は 〇__ にする処理なので、__if 文__ を使って下記のプログラムの 1 ~ 4 行目のように記述することが出来ます。

6 行目の実行結果から、手番が × に変わったことを確認することが出来ます。

```python
1  if mb.turn == Marubatsu.CIRCLE:
2      mb.turn = Marubatsu.CROSS
3  else:
4      mb.turn = Marubatsu.CIRCLE
5
6  print(mb)
```


<details><summary><strong>行番号のないプログラム</strong></summary>

```python
if mb.turn == Marubatsu.CIRCLE:
    mb.turn = Marubatsu.CROSS
else:
    mb.turn = Marubatsu.CIRCLE

print(mb)
```
</details>

実行結果
```
Turn x
o..
...
...
```

上記のプログラムの後で、__続けて__  × の手番で (1, 0) のマスにマークを配置する処理は、先程と同様に、下記のプログラムの 1 ~ 5 行目のように記述することが出来ます。実行結果から × の手番でもマークを配置後に手番が正しく変化することが確認できます。

```python
1  mb.place_mark(1, 0, mb.turn)
2  if mb.turn == Marubatsu.CIRCLE:
3      mb.turn = Marubatsu.CROSS
4  else:
5      mb.turn = Marubatsu.CIRCLE
6
7  print(mb)
```

<details><summary><strong>行番号のないプログラム</strong></summary>

```python
mb.place_mark(1, 0, mb.turn)
if mb.turn == Marubatsu.CIRCLE:
    mb.turn = Marubatsu.CROSS
else:
    mb.turn = Marubatsu.CIRCLE

print(mb)
```
</details>

実行結果
```
Turn o
ox.
...
...
```

### 着手を行うメソッドの定義

着手を行う処理を記述することが出来るようなりましたが、着手を __行うたび__ に毎回上記のような 5 行分のプログラムを記述することは __大変__ なので、着手の処理を行う __メソッドを定義__ する事にします。そのためには __メソッドの名前を決める__ 必要があります。

色々と調べてみた所、将棋やチェスのように、ゲーム盤上でコマを動かすような場合は、英語で着手を表す用語は __move__ と表現されるようです。また、囲碁やオセロのように、ゲーム盤上にコマを（動かさずに）配置するような場合でも、下記のリンク先のように着手は place ではなく、__move__ という用語が使われているようなので、本記事でも着手を行うメソッドの名前を `move` とすることにします。

https://en.wikipedia.org/wiki/Go_(game)

https://en.wikipedia.org/wiki/Reversi

下記は、先ほどの着手を行うプログラムを再掲したものです。下記で __着手を行う際__ に __利用するデータ__ は、マークを配置する __座標__ と、__`mb.turn`__ ですが、`mb.turn` は __`mb` の属性__ なので、`move` メソッドの中で __`self.turn`__ と記述することで利用することが出来ます。そのため、`mb.turn` を `move` メソッドの __仮引数__ で受け取る __必要はありません__。従って、 `move` メソッドの仮引数は、座標を表す __`x`__ と __`y`__ の __2 つ__ になります。

```python
mb.place_mark(1, 0, mb.turn)
if mb.turn == Marubatsu.CIRCLE:
    mb.turn = Marubatsu.CROSS
else:
    mb.turn = Marubatsu.CIRCLE
```

下記のプログラムが `move` メソッドの定義です。メソッドのブロックの中に記述するプログラムでは、上記のプログラムの __`mb` を `self` に置き替えて__ います。

```python
def move(self, x, y):
    self.place_mark(x, y, self.turn)
    if self.turn == Marubatsu.CIRCLE:
        self.turn = Marubatsu.CROSS
    else:
        self.turn = Marubatsu.CIRCLE    

Marubatsu.move = move
```

下記のプログラムは、1 行目で `restart` メソッドを呼び出して〇×ゲームを再起動し、`move` メソッドを使って 2 行目で 〇 の手番で (0, 0) にマークを配置し、4 行目で × の手番で (1, 0) にマークを配置するプログラムです。実行結果から、正しいマスにマークが配置され、手番も正しく変化することが確認できます。


```python
mb.restart()
mb.move(0, 0)
print(mb)
mb.move(1, 0)
print(mb)
```
実行結果
```
Turn x
o..
...
...

Turn o
ox.
...
...
```


これで、`move` メソッドが正しく動作することが確認できたように __見えるかもしれません__ が、この `move` メソッドには __重大なバグ__ があります。そのバグが何であるかについて __考えてみて下さい__。

### `move` メソッドのバグ

`move` メソッドのバグは、__既にマークが配置されているマス__ にマークを __配置しようとした場合__ でも、__手番が変わってしまう__ というものです。下記は、先ほどのプログラムに続けて、〇 の手番で __もう一度__ (0, 0) のマスにマークを配置するプログラムです。

実行結果で表示されるメッセージから、(0, 0) のマスにマークが配置済であることがわかり、実際にその下の表示から (0, 0) のマークは 〇 のままであることを確認することが出来ます。しかし、マークを配置できなかったにも関わらず、`Turn x` の表示から、__手番が × に変わっている__ ことがわかります。

```python
mb.move(0, 0)
print(mb)
```
実行結果
```
( 0 , 0 ) のマスにはマークが配置済です
Turn x
ox.
...
...
```

このバグは、`move` メソッドの中で、__`place_mark`__ を __呼び出した後__ で、__必ず手番を入れ替える__ 処理を行っていることが原因です。従って、このバグを修正するためには、`place_mark` で __マークを配置したときだけ__、手番を入れ替えるようにプログラムを修正する必要があります。ただし、現時点での `place_mark` は __値を返さない関数__ なので、このままでは、`place_mark` を呼び出した結果、マークが配置されたかどうかを __知ることはできません__。

そこで、下記のプログラムのように、`place_mark` を、マークを __配置できた場合__ は __True__ を、__配置できなかった場合__ は __False__ を __返り値として返す__ ように修正することにします。

修正した部分は、__マークを配置した後__ の 4 行目に __return 文で `True` を返す__ 処理を追加した部分と、__マークを配置できなかった場合__ に 7 行目に __return 文で `False` を返す__ 処理を追加した部分です。

```python
1  def place_mark(self, x, y, mark):
2      if self.board[x][y] == Marubatsu.EMPTY:
3          self.board[x][y] = mark
4          return True
5      else:
6          print("(", x, ",", y, ") のマスにはマークが配置済です")
7          return False
8
9  Marubatsu.place_mark = place_mark
```

<details><summary><strong>行番号のないプログラム</strong></summary>

```python
def place_mark(self, x, y, mark):
    if self.board[x][y] == Marubatsu.EMPTY:
        self.board[x][y] = mark
        return True
    else:
        print("(", x, ",", y, ") のマスにはマークが配置済です")
        return False

Marubatsu.place_mark = place_mark
```
</details>

<details><summary><strong>修正箇所</strong></summary>

```diff_python
def place_mark(self, x, y, mark):
    if self.board[x][y] == Marubatsu.EMPTY:
        self.board[x][y] = mark
+       return True
    else:
        print("(", x, ",", y, ") のマスにはマークが配置済です")
+       return False
```
</details>


次に、`move` メソッドを下記のプログラムのように、__2 行目に if 文を追加__ することで、`place_mark` が __`True` を返した時だけ手番を変更__ するように修正します。

```python
1  def move(self, x, y):
2      if self.place_mark(x, y, self.turn):
3          if self.turn == Marubatsu.CIRCLE:
4              self.turn = Marubatsu.CROSS
5          else:
6              self.turn = Marubatsu.CIRCLE    
7
8  Marubatsu.move = move
```

<details><summary><strong>行番号のないプログラム</strong></summary>

```python
def move(self, x, y):
    if self.place_mark(x, y, self.turn):
        if self.turn == Marubatsu.CIRCLE:
            self.turn = Marubatsu.CROSS
        else:
            self.turn = Marubatsu.CIRCLE    

Marubatsu.move = move
```
</details>

<details><summary><strong>修正箇所</strong></summary>

```diff_python
def move(self, x, y):
+   if self.place_mark(x, y, self.turn):
-   if self.turn == Marubatsu.CIRCLE:
-       self.turn = Marubatsu.CROSS
-   else:
-       self.turn = Marubatsu.CIRCLE   
+       if self.turn == Marubatsu.CIRCLE:
+           self.turn = Marubatsu.CROSS
+       else:
+           self.turn = Marubatsu.CIRCLE   
```
</details>


下記のプログラムは、1 行目で `restart` メソッドを呼び出して〇×ゲームを再起動した後で、2 ~ 4 行目で `move` メソッドを呼び出して (0, 0) のマスに __3 回マークを着手__ する処理を行っています。実行結果から、__最初の着手__ では (0, 0) のマスに __〇 を配置__ し、__手番が変わっています__ が、__それ以降の着手__ では (0, 0) のマスのマークは変化せず、手番も変わっていないことが確認できます。

```python
mb.restart()
for i in range(3):
    mb.move(0, 0)
    print(mb)
```
実行結果
```
Turn x
o..
...
...

( 0 , 0 ) のマスにはマークが配置済です
Turn x
o..
...
...

( 0 , 0 ) のマスにはマークが配置済です
Turn x
o..
...
...
```

## 三項演算子

`move` メソッドでは、手番の交代を行う処理を、下記のプログラムで記述しました。

```python
if self.turn == Marubatsu.CIRCLE:
    self.turn = Marubatsu.CROSS
else:
    self.turn = Marubatsu.CIRCLE   
```

このような、__特定の条件__ によって、__特定の変数__ に __異なる値を代入__ する処理は、__三項演算子__ を使って __1 行のプログラム__ で簡潔に __記述する__ ことが出来ます。

三項演算子は下記のように記述し、実行すると、__条件式が True__ の場合は __式1 の計算結果__、__条件式が False__ の場合は __式2 の計算結果__ になります。

```python
式1 if 条件式 else 式2
```

三項演算子を利用することで、先ほどの手番の交代を行う処理は、下記のプログラムのように、__1 行で記述__ することが出来ます。

```python
self.turn = Marubatsu.CROSS if self.turn == Marubatsu.CIRCLE else Marubatsu.CIRCLE
```

:::note
式の中に記述する `+` などの記号のことを __演算子__、__演算子以外の項目__ の事を、__項__ と呼びます。三項演算子の場合は、__`if`__ と __`else`__ も __演算子__ とみなされます。

三項演算子の名前の由来は、計算を行う際に、「式1」、「条件式」、「式2」の __3 つの項__ を使って演算を行うことから来ています。`+` や `-` のような __前後の 2 つの項__ を使って演算を行う演算子は __二項演算子__、__後ろの 1 つの項__ の符号を反転する `-` 演算子[^2]のことを __単項演算子__ と呼びます。
:::

下記が、三項演算子を使うように修正した `__move__` メソッドです。

```python
def move(self, x, y):
    if self.place_mark(x, y, self.turn):
        self.turn = Marubatsu.CROSS if self.turn == Marubatsu.CIRCLE else Marubatsu.CIRCLE 
```

<details><summary><strong>修正箇所</strong></summary>

```diff_python
def move(self, x, y):
    if self.place_mark(x, y, self.turn):
-       if self.turn == Marubatsu.CIRCLE:
-           self.turn = Marubatsu.CROSS
-       else:
-           self.turn = Marubatsu.CIRCLE  
+       self.turn = Marubatsu.CROSS if self.turn == Marubatsu.CIRCLE else Marubatsu.CIRCLE 
```
</details>


__三項演算子__ を使うと、上記の例のように、プログラムを __短く記述する__ ことが出来ますが、if 文を使った場合と比べてプログラムの __見た目がわかりづらくなる__ という欠点があります。本記事で上記のプログラムより見た目がわかりづらくなるよう場合は、三項演算子を使わないことにしますが、実際に三項演算子が __使われることはよくある__ ので、その __意味と使い方は覚えておいたほうが良い__ でしょう。

# 本記事で入力したプログラム

以下のリンクから、本記事で入力して実行した JupyterLab のファイルを見ることができます。

https://github.com/ysgeso/marubatsu/blob/master/022/marubatsu.ipynb

以下のリンクは、今回の記事で更新した marubatsu_new.py です。なお、`__str__` などの __特殊メソッド__ は、基本的に直接 __呼び出して実行することはない__[^3] ので __docstring__ は一般的に __記述しません__。ただし、`__init__` メソッドは例外で、インスタンスを作成する際に __実引数を記述__ して呼び出す場合は docstring を __記述したほうが良い__ でしょう。

https://github.com/ysgeso/marubatsu/blob/master/022/marubatsu_new.py

# 次回の記事

https://qiita.com/ysgeso/items/1fdd19037bbf64082a42

[^1]:リンク先の表の `\n` の行に表記されている「行送り」は、「改行」と同じ意味を表します
[^2]:`-` 演算子は、状況によって二項演算子であったり、単項演算子であったりします
[^3]:docstring は、関数やメソッドの呼び出しを __記述する際のヒント__ として記述します