Pythonで〇×ゲームのAIを一から作成する　その29　文字列で表現する xy 座標

# 目次と前回の記事

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

https://qiita.com/ysgeso/items/6ca248133ff4d59a81d4

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

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

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

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

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

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

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

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

なお、前回の記事で採用しなかった関数は、`test.py` から削除してあります。

https://github.com/ysgeso/marubatsu/blob/master/029/test.py

## 前回までのおさらい

前回の記事では、__複数__ の Excel 座標を __文字列で表現__ する __データ構造__ と、その文字列を __list に変換__ する __アルゴリズム__ を紹介しました。

前回までの記事で、座標を表すデータ構造として、xy 座標と Excel 座標を紹介しました。今回の記事では __それ以外__ の座標を表す __データ構造__ として、__xy 文字座標__ を紹介します。

# xy 文字座標

__Excel 座標__ の __データ構造__ は、__列__ を __アルファベット__、__行__ を __1 から始まる整数__ とし、その __2 つを並べた文字列__ で表現するというものでした。それに対して、__列__ も __行__ も __整数__ で表現し、その __2 つを並べた文字列__ で座標を表現するという __データ構造__ が考えられます。

このデータ構造は、list で表現する __xy 座標__ を、__そのまま文字列で表現__ するのものなので、本記事ではこのデータ構造の事を __xy 文字座標__ と表現することにします。

xy 座標を __そのまま__ 文字列で表現するので、座標を `1` から数える __Excel 座標と異なり__、xy 文字座標では、座標を __`0` から数える__ 点に注意して下さい。

:::note
本記事では採用しませんが、Excel 座標のように、座標を `1` から数えるという方法も考えられます。__自分がわかりやすいと思ったほう__ を __採用__ してもかまいません。
:::


xy 文字座標を、どのような文字列で表現するかについては、いくつかの方法が考えられ、それぞれ利点と欠点があります。

# xy 文字座標のデータ構造その 1（直接並べる）

__最も短く表記__ できる方法として、Excel 座標のように、x 座標と y 座標を表す文字列を __直接並べて記述__ するというデータ構造が考えられます。例えば、(0, 1) のマスの座標を __`"01"`__ のように表記します。

## 利点と欠点

このデータ構造の利点と欠点は以下の通りです。

__利点__

* xy 文字座標の中で、__最も短く__ 表記できる
* xy 座標に __データを変換__ する際に、__テーブルが不要__ になる

__欠点__

* x 座標と y 座標の __区切り__ が __わかりづらい__
* __一見する__ と、__2 桁の数値__ を表すデータのように __見えてしまう__
* x 座標と y 座標の __桁数を決めておかないと__ x 座標と y 座標の __区別がつかない__ 場合がある

2 番目の利点については、xy 文字座標を xy 座標に変換するプログラムの所で説明します。

このデータ構造を利用する際には、__3 番目の欠点__ を __正しく理解__ しておくことが重要です。〇× ゲームの場合は x 座標も y 座標も 1 桁の整数で表現できるので問題ありませんが、囲碁のような __2 桁の座標__ があるゲーム盤の場合、x 座標と y 座標の __桁数を決めておかないと__、例えば __`"110"`__ という xy 文字座標が、(1, 10) を表すのか、(10, 1) を表すのかの __区別が付けられない__ ことになってしまいます。囲碁の場合は、x 座標と y 座標の桁数を __必ず 2 桁で表現する__ ことにすれば、(1, 10) は、 `"0110"`、(10, 1) は `"1001"` のように __区別できるように__ なりますが、あまり __わかりやすいとは言えないでしょう__。

## `xytext_to_xy` の定義

xy 文字座標を利用するために、[以前の記事](https://qiita.com/ysgeso/items/51c2eba867aa560808c6#%E9%96%A2%E6%95%B0%E3%81%AE%E4%BB%95%E6%A7%98) で、Excel 座標を __xy 座標に変換する関数を定義__ した場合と同様に、xy 文字座標を __xy 座標に変換__ する、__`xytext_to_xy`__ という名前の __関数を定義__ することにします。

`xttext_to_xy` は、[以前の記事](https://qiita.com/ysgeso/items/51c2eba867aa560808c6#excel_to_xy-%E3%81%AE%E5%AE%9A%E7%BE%A9%E3%81%9D%E3%81%AE-4y-%E5%BA%A7%E6%A8%99%E3%82%92%E8%A8%88%E7%AE%97%E3%81%A7%E5%A4%89%E6%8F%9B%E3%81%99%E3%82%8B) で紹介した、下記の `excel_to_xy` の定義その 4 を __ほぼそのまま流用__ することで、定義することが出来ます。

```python
1  def excel_to_xy(coord):
2      excel_to_x_table = {
3          "A": 0,
4          "B": 1,
5          "C": 2,
6      }
7      x, y = coord
8      return excel_to_x_table[x], int(y) - 1
```

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

```python
def excel_to_xy(coord):
    excel_to_x_table = {
        "A": 0,
        "B": 1,
        "C": 2,
    }
    x, y = coord
    return excel_to_x_table[x], int(y) - 1
```
</details>

上記のプログラムは、7 行目で、Excel 座標の __x 座標と y 座標__ を表す文字列を __`x`, `y` に代入__ しています。また、8 行目で __`int(y) - 1`__ を記述することで、__y 座標を計算__ しています。

xy 文字座標も、__同様の方法__ で xy 文字座標の __x 座標と y 座標__ を表す文字列を __`x`, `y` に代入__ することができます。xy 文字座標は、Excel 座標と異なり、__座標を `0` から数える__ ので、__y 座標__ は、__`int(y)` で計算__ することができます。また、xy 文字座標は、__x 座標__ も y 座標と __同様__ に __整数を表す文字列__ で表現されているので、__x 座標__ は __`int(x)` で計算__ することができます。従って、`xytext_to_xy` は、下記のプログラムのように定義できます。

```python
def xytext_to_xy(coord):
    x, y = coord
    return int(x), int(y)
```

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

```diff_python
- def excel_to_xy(coord):
+ def xytext_to_xy(coord):
-   excel_to_x_table = {
-       "A": 0,
-       "B": 1,
-       "C": 2,
-   }
    x, y = coord
-   return excel_to_x_table[x], int(y) - 1
+   return int(x), int(y)
```
</details>

__xy 文字座標__ の __利点__ の一つに、`excel_to_xy` の場合と異なり、アルファベットを数値に変換するための __テーブルが不要になる__ という性質があります。

:::note
上記の `xytext_to_xy` は、〇× ゲームのような、x 座標と y 座標が __1 文字で表現__ される __場合でのみ__ 利用できる点に注意が必要です。
:::

## 複数の xy 文字座標を表すデータ構造

次に、__複数__ の xy 文字座標を __まとめて扱う__ データ構造を考えることにします。xy 文字座標は、Excel 座標と同様に __文字列で表現__ されるので、前回の記事で説明したように、__座標を直接連結する__ 方法と、__`","`__  などの __文字列で区切って連結__ する方法が考えられます。

この 2 つのデータ構造の __利点と欠点__ については、前回の記事の [こちら](https://qiita.com/ysgeso/items/6ca248133ff4d59a81d4#%E5%88%A9%E7%82%B9%E3%81%A8%E6%AC%A0%E7%82%B9) と [こちら](https://qiita.com/ysgeso/items/6ca248133ff4d59a81d4#%E5%88%A9%E7%82%B9%E3%81%A8%E6%AC%A0%E7%82%B9-1) を参照して下さい。今回の記事では、そのことを踏まえた上でのそれぞれの性質を説明します。

### xy 文字座標を直接連結するデータ構造

下記は、xy 文字座標を __直接連結__ するデータ構造でテストケースを記述するプログラムです。下記のように、xy 文字座標は __すべて数字で表現__ されるので、xy 文字座標の __区切り__ がどこにあるのかが __非常に分かりづらく__ なっています。そのため、筆者は下記のプログラムをかなり __わかりづらいと思いながら__ 記述しました。また、このデータ構造を利用すると、データの __入力ミスが多発__ すると思いますので、個人的には __お勧めしません__。__実際に__ 筆者は下記のテストケースを入力する際に __一部のデータを間違えました__。

```python
from marubatsu import Marubatsu

testcases = [
    # 〇 の勝利のテストケース
    [                                  # 着手順とゲーム盤
        "0001101120",                  # 135  ooo
        Marubatsu.CIRCLE,              # 24.  xx. 
    ],                                 # ...  ...
    # × の勝利のテストケース
    [                                  # 着手順とゲーム盤
        "010011100220",                # 246  xxx 
        Marubatsu.CROSS,               # 13.  oo.
    ],                                 # 5..  ...
    # 引き分けのテストケース
    [                                  # 着手順とゲーム盤
        "000110112120021222",          # 136  oox 
        Marubatsu.DRAW,                # 245  xxo
    ],                                 # 789  oxo
]
```

このデータ構造を、xy 文字座標を要素とする __list に変換__ する処理は、[前回の記事](https://qiita.com/ysgeso/items/6ca248133ff4d59a81d4#excels_to_list-%E3%81%AE%E5%AE%9A%E7%BE%A9-%E3%81%9D%E3%81%AE-3) の `excels_to_list` と __全く同じ__ です。そのため、このデータ構造を list に変換する処理を、以下の __いずれかの方法__ で行うことが出来ます。

1. __`excels_to_list`__ を、名前を変えずに __そのまま利用__ する
2. __`xytext_to_list`__ という名前で、`excels_to_list` と __同じ処理__ を行う __関数を定義__ する
3. __文字列を list に変換__ する処理を行うので、`excels_to_list` を __`text_to_list`__ という __名前に変更__ して利用する。この場合は、__前回のプログラムも修正__ する必要がある
4. __名前だけ__ が気になる場合は、下記のプログラムのように、__`xytext_to_list`__ に __`excels_to_list`__ を __代入__ する

```python
xytext_to_list = excels_to_list
```

上記の中の 2 番目の、__全く同じ処理__ を行う __関数__ を __複数定義__ する方法は、その関数の処理を __修正する必要__ が生じた場合に、__両方を修正する必要__ があるため、お勧めしません。4 番目の方法であればそのような欠点は生じませんが、__`excel_to_list` の定義を修正__ した場合は、`xytext_to_list = excels_to_list` を __実行しなおさない__ と、その修正が `xytext_to_list` に __反映されない__ ので、4 番目の方法もあまり __良い方法ではありません__。

そこで、本記事では、3 番目の方法を取ることにします。なお、関数の __名前だけを修正__ すると、関数の __仮引数__ の __`excels`__ という __名前が変__ になってしまうので、__仮引数の名前__ と __ローカル変数の名前__ の __`excels`__ を __`coords`__ に、下記のプログラムのように __修正__ します。
```python
def text_to_list(coords):
    coord_list = [ coords[i:i+2] for i in range(0, len(coords), 2) ]
    return coord_list
```

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

```diff_python
- def excels_to_list(excels):
+ def text_to_list(coords):
-   excel_list = [ excels[i:i+2] for i in range(0, len(excels), 2) ]
+   coord_list = [ coords[i:i+2] for i in range(0, len(coords), 2) ]
-   return excel_list
+   return coord_list
```
</details>

データ構造が変わったので、[前回の記事](https://qiita.com/ysgeso/items/6ca248133ff4d59a81d4#test_judge-%E3%81%AE%E4%BF%AE%E6%AD%A3) と同様の方法で、`test_judge` を修正します。修正箇所は、__Excel 座標に関連__ する `excels_to_list` と `excel_to_xy` の __2 箇所__ です。

```python
def test_judge(testcases):
    for testcase in testcases:
        testdata, winner = testcase
        mb = Marubatsu()
        for coord in text_to_list(testdata):
            x, y = xytext_to_xy(coord)            
            mb.move(x, y)
        print(mb)

        if mb.judge() == winner:
            print("ok")
        else:
            print("error!")
```

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

```diff_python
def test_judge(testcases):
    for testcase in testcases:
        testdata, winner = testcase
        mb = Marubatsu()
-       for coord in excels_to_list(testdata):
+       for coord in text_to_list(testdata):
-           x, y = excel_to_xy(coord)            
+           x, y = xytext_to_xy(coord)            
            mb.move(x, y)
        print(mb)

        if mb.judge() == winner:
            print("ok")
        else:
            print("error!")
```
</details>

下記のプログラムを実行することで、修正したデータ構造で、`test_judge` が正しく動作することが確認できます。

```python
test_judge(testcases)
```
実行結果
```
Turn x
ooo
xx.
...

ok
Turn o
xxx
oo.
o..

ok
Turn x
oox
xxo
oxo

ok
```

### xy 文字座標を `","` で区切って連結するデータ構造

下記は、xy 文字座標を __`","` で区切って連結__ するデータ構造でテストケースを記述するプログラムです。こちらは、先程よりかなり __わかりやすく記述__ できます。__xy 文字座標__ の入力に __違和感を感じない人__ は、このデータ構造を __利用しても良い__ と思います。

```python
testcases = [
    # 〇 の勝利のテストケース
    [                                  # 着手順とゲーム盤
        "00,01,10,11,20",              # 135  ooo
        Marubatsu.CIRCLE,              # 24.  xx. 
    ],                                 # ...  ...
    # × の勝利のテストケース
    [                                  # 着手順とゲーム盤
        "01,00,11,10,02,20",           # 246  xxx 
        Marubatsu.CROSS,               # 13.  oo.
    ],                                 # 5..  ...
    # 引き分けのテストケース
    [                                  # 着手順とゲーム盤
        "00,01,10,11,21,20,02,12,22",  # 136  oox 
        Marubatsu.DRAW,                # 245  xxo
    ],                                 # 789  oxo
]
```

データ構造が変わったので、[前回の記事](https://qiita.com/ysgeso/items/6ca248133ff4d59a81d4#test_judge-%E3%81%AE%E4%BF%AE%E6%AD%A3-1) と同様の方法で、`test_judge` を修正します。修正箇所は、`text_to_list(testdata)` で、`testdata.split(",")` に修正します。

```python
def test_judge(testcases):
    for testcase in testcases:
        testdata, winner = testcase
        mb = Marubatsu()
        for coord in testdata.split(","):
            x, y = xytext_to_xy(coord)            
            mb.move(x, y)
        print(mb)

        if mb.judge() == winner:
            print("ok")
        else:
            print("error!")
```

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

```diff_python
def test_judge(testcases):
    for testcase in testcases:
        testdata, winner = testcase
        mb = Marubatsu()
-       for coord in text_to_list(testdata):
+       for coord in testdata.split(","):
            x, y = xytext_to_xy(coord)            
            mb.move(x, y)
        print(mb)

        if mb.judge() == winner:
            print("ok")
        else:
            print("error!")
```
</details>

下記のプログラムを実行することで、修正したデータ構造で、`test_judge` が正しく動作することが確認できます。実行結果は、先ほどと同じなので省略します。

```python
test_judge(testcases)
```

# xy 文字座標のデータ構造その 2（`","` で区切る）

先程の、x 座標と y 座標を __直接並べて記述__ するデータ構造は、x 座標と y 座標の __区別がつきづらい__ などの欠点がありました。そこで、x 座標と y 座標を __`","`__ などの __文字列で区切って`"0,1"`__ のように記述するというデータ構造が考えられます。

### 利点と欠点

このデータ構造の利点と欠点は以下の通りです。

__利点__

* x 座標と y 座標の __区切り__ が __わかりやすい__
* x 座標と y 座標に、__任意の桁数__ の数字を記述できる

__欠点__

* __記述__ が `","` 等の区切りの文字の分だけ __長くなる__
* 複数の xy 座標を __連結して記述__ する際に、__工夫が必要__ になる

2 番目の x 座標と y 座標の __桁数に制限がなくなる__ という利点は、囲碁のような __大きなゲーム盤__ の __座標を記述__ する際には __大きな利点__ となります。

一方で、このデータ構造では、2 番目の欠点が __新しく生じて__ しまいます。2 番目の欠点については、複数の xy 文字座標を表すデータ構造の所で説明します。

## `xytext_to_xy` の定義

x 座標と y 座標を `","` などの文字列で区切るというデータ構造は、__複数__ の __Excel座標__ を `","` などの __文字列で区切る__ データ構造と __同じ__ なので、[前回の記事](https://qiita.com/ysgeso/items/6ca248133ff4d59a81d4#split-%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89%E3%81%AB%E3%82%88%E3%82%8B-list-%E3%81%B8%E3%81%AE%E5%A4%89%E6%8F%9B) で紹介した、__`split` メソッド__ を使って `xytext_to_xy` を下記のプログラムのように定義できます。

```python
def xytext_to_xy(coord):
    x, y = coord.split(",")
    return int(x), int(y)
```

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

```diff_python
def xytext_to_xy(coord):
-   x, y = coord
+   x, y = coord.split(",")
    return int(x), int(y)
```
</details>

__`split` メソッド__ で __文字列__ を __list に変換__ した場合は、先程の `x, y = coord` の場合と異なり、__`x`__ と __`y`__ には、__任意の文字数__ のデータが代入されます。また、__`int`__ は __任意の文字数__ の文字列を __整数に変換__ できるので、このデータ構造は、下記のプログラムの実行結果からわかるように、__任意の桁数__ の x 座標と y 座標を __記述__ することができます。

```python
print(xytext_to_xy("10,200"))
```
実行結果
```
(10, 200)
```

:::note
__`split` メソッド__ で __文字列__ を __list に変換__ した場合は、__数値の前後__ に __空白文字[^1]を記述__ することが __できる__ という利点も得られます。その理由は、組み込み関数 __`int`__ が、__先頭__ と __末尾__ の __空白文字を無視__ するからです。下記は、先頭と末尾に半角の __空白文字が 5 つ__ 記述された __文字列__ を、__整数型__ のデータに __変換__ するプログラムです。

```python
print(int("     100     "))
```
実行結果
```
100
```

従って、下記のプログラムのように、中に __空白文字が記述__ された、__`"10 , 20"`__ を `xytext_to_xy` で __xy 座標に変換__ することが出来ます。

```python
print(xytext_to_xy("10 , 20"))
```
実行結果
```
(10, 20)
```

なお、`split` メソッドを __使わない場合__ は、xy 文字座標の中に、__空白文字を記述__ することは __できない__ 点に注意して下さい。

:::

## xy 文字座標を直接連結するデータ構造

__先程と同様__ に複数の xy 文字座標をまとめて扱うデータ構造として、xy 文字座標を直接連結する方法と、`","` 等の文字列で区切って連結する方法を考えることにします。

下記は、xy 文字座標を __直接連結__ するデータ構造でテストケースを記述するプログラムです。見ればわかると思いますが、このデータ構造は __非常に分かりづらい__ ので、__使わないほうが良い__ と思います。例えば、`"0,00,11,01,12,0"` は、(0, 0)、(0, 1)、(1, 0)、(1, 1)、(2, 0) の __5 つの座標__ を表していますが、このデータ構造の事を知らない人は、`0`、`00`、`11`、`01`、`12`、`0` の __6 つのデータが記述__ されている __ようにしか見えない__ と思います。

また、このことは、xy 文字座標を区切る文字列を __別の文字列に変えて__ も __改善__ することは __ありません__。例えば、区切る文字列を `":"` にすると `"0:00:11:01:12:0"` のようになりますが、上記の欠点は全く __改善されません__。

```python
testcases = [
    # 〇 の勝利のテストケース
    [                                  # 着手順とゲーム盤
        "0,00,11,01,12,0",             # 135  ooo
        Marubatsu.CIRCLE,              # 24.  xx. 
    ],                                 # ...  ...
    # × の勝利のテストケース
    [                                  # 着手順とゲーム盤
        "0,10,01,11,00,22,0",          # 246  xxx 
        Marubatsu.CROSS,               # 13.  oo.
    ],                                 # 5..  ...
    # 引き分けのテストケース
    [                                  # 着手順とゲーム盤
        "0,00,11,01,12,12,00,21,22,2", # 136  oox 
        Marubatsu.DRAW,                # 245  xxo
    ],                                 # 789  oxo
]
```

__参考まで__ に、この方法を採用する場合の __`text_to_list` の修正方法__ について説明します。〇×ゲームの xy 文字座標は、__必ず 3 文字__ で表現されるので、元の 2 文字ではなく、__3 文字ずつ__ データを取り出したものを list の要素にするという修正を行います。具体的には、下記のプログラムのように、元の `text_to_list` のブロック内の __`2`__ を __`3`__ に修正します。

```python
def text_to_list(coords):
    coord_list = [ coords[i:i+3] for i in range(0, len(coords), 3) ]
    return coord_list
```

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

```diff_python
def text_to_list(coords):
-   coord_list = [ coords[i:i+2] for i in range(0, len(coords), 2) ]
+   coord_list = [ coords[i:i+3] for i in range(0, len(coords), 3) ]
    return coord_list
```
</details>

`test_judge` を下記のように、xy 文字座標を __直接連結__ する場合の __処理に戻してから__、`test_judge` を実行することで、修正したデータ構造で、`test_judge` が正しく動作することが確認できます。実行結果は、先ほどと同じなので省略します。

```python
def test_judge(testcases):
    for testcase in testcases:
        testdata, winner = testcase
        mb = Marubatsu()
        for coord in text_to_list(testdata):
            x, y = xytext_to_xy(coord)            
            mb.move(x, y)
        print(mb)

        if mb.judge() == winner:
            print("ok")
        else:
            print("error!")

test_judge(testcases)
```

## xy 文字座標を `","` で区切って連結するデータ構造

下記は、xy 文字座標を __`","` で区切って連結__ するデータ構造でテストケースを記述するプログラムです。見ればわかると思いますが、__x 座標と y 座標__ を __区切る文字列__ と、__xy 文字座標__ を __区切る文字列__ に、__同じ `","` を記述__ しているので、どの `","` が __何を区切っているか__ が __非常にわかりづらく__ なっています。従ってこのデータ構造も、__お勧めできません__。

```python
testcases = [
    # 〇 の勝利のテストケース
    [                                  # 着手順とゲーム盤
        "0,0,0,1,1,0,1,1,2,0",         # 135  ooo
        Marubatsu.CIRCLE,              # 24.  xx. 
    ],                                 # ...  ...
    # × の勝利のテストケース
    [                                  # 着手順とゲーム盤
        "0,1,0,0,1,1,1,0,0,2,2,0",     # 246  xxx 
        Marubatsu.CROSS,               # 13.  oo.
    ],                                 # 5..  ...
    # 引き分けのテストケース
    [                                           # 着手順とゲーム盤
        "0,0,0,1,1,0,1,1,2,1,2,0,0,2,1,2,2,2",  # 136  oox 
        Marubatsu.DRAW,                         # 245  xxo
    ],                                          # 789  oxo
]
```

参考までに、この方法を採用する場合の __`text_to_list` の修正方法__ について説明します。`","` を __2 つ__ の __異なる意味__ の __区切り記号__ として使っているので、__`split`__ を __使うだけ__ ではうまく `text_to_list` を実装することは __できません__。

一つの方法として、__`split`__  で __list に変換__ したデータを、__2 つずつ__ 集めて __xy 座標を作成__ し、__それらを要素__ とする __list を作成する__ という方法が考えられます。下記のプログラムは、xy 文字座標 __ではなく__、__xy 座標__ を __要素__ として持つ __list を作成__ する関数なので、`text_to_list` を修正するのではなく、__新しく `text_to_xylist`__ という名前の __関数を定義__ しています。

```python
1  def text_to_xylist(coords):
2      coord_list = coords.split(",")
3      xy_list = []
4      for i in range(0, len(coord_list), 2):
5          x, y = coord_list[i:i+2]
6          xy_list.append([int(x), int(y)])
7      return xy_list
```

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

```python
def text_to_xylist(coords):
    coord_list = coords.split(",")
    xy_list = []
    for i in range(0, len(coord_list), 2):
        x, y = coord_list[i:i+2]
        xy_list.append([int(x), int(y)])
    return xy_list
```
</details>

上記のプログラムで行われる処理は以下のようになります。

* __2 行目__：`","` で区切られた文字列を要素とする list を `coord_list` に代入する
* __4 行目__：`coord_list` の要素には先頭から順番に、x 座標、y 座標 の順番で文字列のデータが代入されているので、先頭から 2 つずつ要素を取り出すことで、1 つの xy 座標を表すデータを得ることができる。4 行目は、そのような繰り返しを行う for 文である
* __5 行目__：スライス表記を使って、x 座標と y 座標を表す文字列を `x` と `y` に代入する
* __6 行目__：`int` を使ってそれぞれを整数型のデータに変換したものを要素とする xy 座標を表す list を `xy_list` に追加する
* __7 行目__：xy 座標を要素として持つ list を返り値として返る

`text_to_xylist` は、__xy 座標__ を要素として持つ __list を返す__ 関数なので、5 行目の for 文に記述する変数に、__直接 `x, y` を記述__ することができ、その次の行にあった __`x, y = xytext_to_xy(coord)` を削除__ することができます。

```python
 1  def test_judge(testcases):
 2      for testcase in testcases:
 3          testdata, winner = testcase
 4          mb = Marubatsu()
 5          for x, y in text_to_xylist(testdata):
 6              mb.move(x, y)
 7          print(mb)
 8
 9          if mb.judge() == winner:
10              print("ok")
11          else:
12              print("error!")
```

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

```python
def test_judge(testcases):
    for testcase in testcases:
        testdata, winner = testcase
        mb = Marubatsu()
        for x, y in text_to_xylist(testdata):
            mb.move(x, y)
        print(mb)

        if mb.judge() == winner:
            print("ok")
        else:
            print("error!")
```
</details>

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

```diff_python
def test_judge(testcases):
    for testcase in testcases:
        testdata, winner = testcase
        mb = Marubatsu()
-       for coord in text_to_list(testdata):
+       for x, y in text_to_xylist(testdata):
-           x, y = xytext_to_xy(coord)            
            mb.move(x, y)
        print(mb)

        if mb.judge() == winner:
            print("ok")
        else:
            print("error!")
```
</details>

下記のプログラムを実行することで、修正したデータ構造で、`test_judge` が正しく動作することが確認できます。実行結果は、先ほどと同じなので省略します。

```python
test_judge(testcases)
```

具体例は紹介しませんが、正規表現を使う方法など、他にも様々な方法があります。

:::note
`text_to_xylist` を、リスト内包表記を使って、下記のように記述することができますが、__見た目がわかりづらくなる__ ので、本記事では採用しないことにします。なお、__リスト内包表現__ は、3、4 行目のように __途中で改行を行う__ ことができます。

```python
def text_to_xylist(coords):
    coord_list = coords.split(",")
    xy_list = [[int(coord_list[i]), int(coord_list[i+1])]
               for i in range(0, len(coord_list), 2)]
    return xy_list
``````

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

```diff_python
def text_to_xylist(coords):
    coord_list = coords.split(",")
-   xy_list = []
-   for i in range(0, len(coord_list), 2):
-       x, y = coord_list[i:i+2]
-       xy_list.append([int(x), int(y)])
+   xy_list = [int(coord_list[i]), int(coord_list[i+1]) 
+              for i in range(0, len(coord_list), 2)]
    return xy_list
```
</details>

下記のプログラムを実行することで、修正した `text_to_xylist` で`test_judge` が正しく動作することが確認できます。実行結果は、先ほどと同じなので省略します。

```python
test_judge(testcases)
```
:::

## xy 文字座標を `":"` で区切って連結するデータ構造

上記の方法の __問題点__ は、x 座標と y 座標を __区切る文字列__ と、xy 文字座標を __区切る文字列__ に、__同じ文字列を使う__ ことが __原因__ でした。従って、この問題は、その 2 つの文字列を __別の文字列にする__ ことで __改善__ することができます。

下記は、__xy 文字座標__ を __`":"` で区切って連結__ するデータ構造でテストケースを記述するプログラムです。

```python
testcases = [
    # 〇 の勝利のテストケース
    [                                  # 着手順とゲーム盤
        "0,0:0,1:1,0:1,1:2,0",         # 135  ooo
        Marubatsu.CIRCLE,              # 24.  xx. 
    ],                                 # ...  ...
    # × の勝利のテストケース
    [                                  # 着手順とゲーム盤
        "0,1:0,0:1,1:1,0:0,2:2,0",     # 246  xxx 
        Marubatsu.CROSS,               # 13.  oo.
    ],                                 # 5..  ...
    # 引き分けのテストケース
    [                                           # 着手順とゲーム盤
        "0,0:0,1:1,0:1,1:2,1:2,0:0,2:1,2:2,2",  # 136  oox 
        Marubatsu.DRAW,                         # 245  xxo
    ],                                          # 789  oxo
]
```

上記では、xy 文字座標を区切る文字列を `":"` に変更していますが、__x 座標と y 座標__ を __区切る文字列__ を変えても、__`":"` 以外__ の __別の文字__ を使うように __変えてもかまわない__ ので、興味がある方は試してみると良いでしょう。例えば、x 座標と y 座標を区切る文字列を __`"_"` に変更__ した場合、〇の勝利のテストケースのデータは、__`"0_0,0_1,1_0,1_1,2_0"`__ のようになります。どの文字列を使えば __わかりやすくなる__ かは、人によって __感じ方が異なる__ と思いますので、このデータ構造を採用したいと思った場合は、__様々な__ 区切りの __文字を試して__ 下さい。

__個人的__ にはどの区切り文字の組み合わせを使っても、あまり __わかりやすくはならない__ ような __気がします__ ので、本記事ではこのデータ構造は採用しません。

このデータ構造の場合は、__`test_judge`__ の __修正__ は、下記のプログラムのように、__`split(",")`__ を __`split(":")`__ に __修正するだけ__ で済みます。

```python
def test_judge(testcases):
    for testcase in testcases:
        testdata, winner = testcase
        mb = Marubatsu()
        for coord in testdata.split(":"):
            x, y = xytext_to_xy(coord)            
            mb.move(x, y)
        print(mb)

        if mb.judge() == winner:
            print("ok")
        else:
            print("error!")
```

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

```diff_python
def test_judge(testcases):
    for testcase in testcases:
        testdata, winner = testcase
        mb = Marubatsu()
-       for coord in testdata.split(","):
+       for coord in testdata.split(":"):
        x, y = xytext_to_xy(coord)            
            mb.move(x, y)
        print(mb)

        if mb.judge() == winner:
            print("ok")
        else:
            print("error!")
```
</details>

下記のプログラムを実行することで、修正したデータ構造で、`test_judge` が正しく動作することが確認できます。実行結果は、先ほどと同じなので省略します。

```python
test_judge(testcases)
```

# xy 文字座標のデータ構造その 3（数学の座標）

今回の記事で紹介した xy 座標のデータ構造は、いずれも x 座標と y 座標の __区別がつきづらかったり__、xy 文字座標を連結する際に、__区切りの文字列を工夫__ する必要があるなどの __欠点__ がありました。その問題を解決する方法として、__数学の座標__ と同様に、`"(1,0)"` のように、__前後__ を __`"("`__ と __`")"`__ で __囲った文字列__ で記述するという __データ構造__ が考えられます。

## 利点と欠点

このデータ構造の利点と欠点は以下の通りです。

__利点__

* __x 座標と y 座標__ の __区切りがわかりやすい__
* x 座標と y 座標に、__任意の桁数の数字__ を記述できる
* __xy 文字座標__ を __連結__ した際の __区切りがわかりやすい__
* 区切りの記号を __`","` で統一__ しても __わかりづらくならない__

__欠点__

* 記述が `","`、`"("`、`")"` を記述する分だけ __かなり長くなる__。具体的には、__複数__ の __xy 文字座標__ を __文字列で記述__ する場合と、__複数__ の __xy 座標__ を __list で記述__ する場合の __記述量__ は __ほとんど変わらない__
* __データの変換__ の __アルゴリズムが複雑__ になる

上記の利点と欠点については、この後で具体例を挙げて詳しく説明します。

## `xytext_to_xy` の定義

上記のデータ構造は、先程の x 座標と y 座標を __`","` で区切って連結__ したデータ構造に対して、__両端__ に __`"("`__ と __`")"`__ を __追加__ したものです。そのため、__`xytext_to_xy`__ の __最初__ に、__両端の文字を削除__ する __処理を追加__ するという __修正__ を行うだけで済みます。

文字列の __最初の文字__ と __最後の文字__ を __削除__ する処理は、スライス表記を使って、下記のプログラムのように記述することができます。[以前の記事](https://qiita.com/ysgeso/items/6ca248133ff4d59a81d4#%E3%82%B7%E3%83%BC%E3%82%B1%E3%83%B3%E3%82%B9%E5%9E%8B%E3%81%AE--%E3%81%AE%E4%B8%AD%E3%81%AE%E8%A1%A8%E8%A8%98) で説明したように、__`[1:-1]`__ は、__シーケンス型__ の __1 番__ の要素、すなわち __先頭（0 番）の次__ の要素 __から__、__最後（-1 番）の直前__ の要素 __まで__ を要素として持つ、__新しい__ 同じ種類の __データを作成する__ という意味を持つので、これを使うことで、__両端の文字を除いた__ 文字列を得ることが出来ます。

```python
print("ABCDE"[1:-1])
```
実行結果
```
BCD
```

下記は、スライス表記を使って、`xytext_to_xy` を修正したプログラムです。

```python
def xytext_to_xy(coord):
    x, y = coord[1:-1].split(",")
    return int(x), int(y)
```

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

```diff_python
def xytext_to_xy(coord):
-   x, y = coord.split(",")
+   x, y = coord[1:-1].split(",")
    return int(x), int(y)
```
</details>

下記のプログラムは、`"(1,2)"` を、上記の `xytext_to_xy` を使って xy 座標に変換するプログラムです。実行結果から正しく動作することが確認できます。

```python
print(xytext_to_xy("(1,2)"))
```
実行結果
```
(1, 2)
```

## xy 文字座標を直接連結するデータ構造

先程と同様に、複数の xy 文字座標をまとめて扱うデータ構造として、xy 文字座標を直接連結する方法と、`","` 等の文字列で区切って連結する方法を考えることにします。

下記は、xy 文字座標を __直接連結__ するデータ構造でテストケースを記述するプログラムです。__xy 座標__ どうしの __区別__ は __わかりやすく__ なりますが、__記述がかなり長く__ なります。

```python
testcases = [
    # 〇 の勝利のテストケース
    [                                  # 着手順とゲーム盤
        "(0,0)(0,1)(1,0)(1,1)(2,0)",   # 135  ooo
        Marubatsu.CIRCLE,              # 24.  xx. 
    ],                                 # ...  ...
    # × の勝利のテストケース
    [                                      # 着手順とゲーム盤
        "(0,1)(0,0)(1,1)(1,0)(0,2)(2,0)",  # 246  xxx 
        Marubatsu.CROSS,                   # 13.  oo.
    ],                                     # 5..  ...
    # 引き分けのテストケース
    [                                                    # 着手順とゲーム盤
        "(0,0)(0,1)(1,0)(1,1)(2,1)(2,0)(0,2)(1,2)(2,2)", # 136  oox 
        Marubatsu.DRAW,                                  # 245  xxo
    ],                                                   # 789  oxo
]
```

下記は、〇の勝利のテストケースを、__上記のデータ構造__ で記述した場合、__間に `","` を記述して連結__ した場合、__xy 座標を要素とする list__ で記述した場合を並べたものです。

3 行目のように、__間に空白を記述せず__ に __list で記述__ した場合の __記述の長さ__ は __1 行目__ の __長さ__ と __あまり変わりません__。また、__2 行目の記述__ と __同じ長さ__ になります。このように、__数学の座標と同じ文字列__ で座標を記述する方法は、__記述の短さ__ という __利点__ は __得られません__。

```python
"(0,0)(0,1)(1,0)(1,1)(2,0)"
"(0,0),(0,1),(1,0),(1,1),(2,0)"
[[0,0],[0,1],[1,0],[1,1],[2,0]]
```

〇×ゲームの場合は、x 座標と y 座標を表す文字列が __必ず 1 文字__ になるので、__xy 文字座標__ の文字列の __長さ__ は `"("`、`","`、`")"` の __3 文字__ を足すと、__必ず 5 文字__ になります。従って、`text_to_list` は、下記のプログラムのように、__`2`__ を __`5`__ に __修正__ する __だけで済みます__。

```python
def text_to_list(coords):
    coord_list = [ coords[i:i+5] for i in range(0, len(coords), 5) ]
    return coord_list
```

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

```diff_python
def text_to_list(coords):
―   coord_list = [ coords[i:i+2] for i in range(0, len(coords), 2) ]
+   coord_list = [ coords[i:i+5] for i in range(0, len(coords), 5) ]
    return coord_list
```
</details>

`test_judge` を下記のように、__xy 文字座標を直接連結__ する場合の __処理に戻して__ から、`test_judge` を実行することで、修正したデータ構造で、`test_judge` が正しく動作することが確認できます。実行結果は、先ほどと同じなので省略します。

```python
def test_judge(testcases):
    for testcase in testcases:
        testdata, winner = testcase
        mb = Marubatsu()
        for coord in text_to_list(testdata):
            x, y = xytext_to_xy(coord)            
            mb.move(x, y)
        print(mb)

        if mb.judge() == winner:
            print("ok")
        else:
            print("error!")

test_judge(testcases)
```

## 2 桁以上の場合の xy 文字座標を直接連結するデータ構造

上記の、`text_to_list` は、囲碁のように、x 座標や y 座標の桁数が __2 桁以上__ になる場合は、xy 座標の文字列が __6 文字以上になる__ ため __使うことが出来ません__。〇×ゲームには必要はありませんが、参考までに、そのような場合の `text_to_list` の定義を紹介します。

__`"(0,0)(0,1)(1,0)(1,1)(2,0)"`__ をよく見ると、それぞれの xy 文字座標が、__`")("`__ という __2 文字__ の文字列で __区切られている__ ことがわかります。従って、__`split`__ メソッドの __実引数__ に __`")("` を記述__ することで、この文字列を、__xy 文字座標__ を要素として持つ __list に変換__ することができます。下記のプログラムは、実際に `split` メソッドを使って `"(0,0)(0,1)(1,0)(1,1)(2,0)"` を list に変換するプログラムです。

```python
print("(0,0)(0,1)(1,0)(1,1)(2,0)".split(")("))
```
実行結果
```
['(0,0', '0,1', '1,0', '1,1', '2,0)']
```

__一見する__ とうまくいっているように __見えるかも__ しれませんが、__よく見る__ と、__最初__ と __最後__ の要素に、__`"(0,0"`__ と __`2,0)"`__ のように余計な __`(`__ と __`)`__ がデータに __入っている__ ことがわかります。ピンとこない方は、下記の、__各要素を表示__ するプログラムを実行してみて下さい。

```python
xytext_list = "(0,0)(0,1)(1,0)(1,1)(2,0)".split(")(")
for xytext in xytext_list:
    print(xytext)
```
実行結果
```
(0,0
0,1
1,0
1,1
2,0)
```

この問題は、__元の文字列__ の __先頭__ の __`(`__ と __最後__ の __`)`__ を __削除__ することで解決でき、その方法は、先程の `xytext_to_xy` で紹介済で、下記のプログラムのように __`[1:-1]`__ を記述します。

```python
xytext_list = "(0,0)(0,1)(1,0)(1,1)(2,0)"[1:-1].split(")(")
for xytext in xytext_list:
    print(xytext)
```

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

```diff_python
- xytext_list = "(0,0)(0,1)(1,0)(1,1)(2,0)".split(")(")
+ xytext_list = "(0,0)(0,1)(1,0)(1,1)(2,0)"[1:-1].split(")(")
for xytext in xytext_list:
    print(xytext)
```
</details>


実行結果
```
0,0
0,1
1,0
1,1
2,0
```

従って、`text_to_list` は、下記のプログラムのように定義することが出来ます。

```python
def text_to_list(coords):
    coord_list = coords[1:-1].split(")(")
    return coord_list
```

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

```diff_python
def text_to_list(coords):
-   coord_list = [ coords[i:i+5] for i in range(0, len(coords), 5) ]
+   coord_list = coords[1:-1].split(")(")
    return coord_list
```
</details>

ただし、__このまま__ `test_judge` を __実行__ すると、下記のプログラムのように __エラーが発生__ してしまいます。その理由について少し考えてみて下さい。

```python
test_judge(testcases)
```
実行結果
```
略
c:\Users\ys\ai\marubatsu\029\marubatsu.ipynb セル 35 line 3
      1 def xytext_to_xy(coord):
      2     x, y = coord[1:-1].split(",")
----> 3     return int(x), int(y)

ValueError: invalid literal for int() with base 10: ''
```

:::note
上記のエラーメッセージは、以下のような意味を持ちます。

* __ValueError__
値（value）に関するエラー
* __invalid literal for int() with base 10: ''__
`int()` の実引数に、基数（base）を 10 とした場合の無効な（invalid）リテラルである `''` が記述されている

注：with base 10 の「基数を 10 とする」は、__10 進数__ のことを表します。説明していませんでしたが、組み込み関数 __`int`__ は、__10 進数以外__ の文字列を __数値型__ のデータに __変換__ することができます。詳細は下記のリンク先を参照して下さい。
:::

https://docs.python.org/ja/3/library/functions.html#int

エラーの原因は、__修正後__ の `text_to_list` では、xy 文字座標の先頭と最後にあった __`"("` と `")"` を削除__ しているからです。`xytext_to_xy` は、xy 文字座標の先頭と最後に `"("` と `")"` が ___記述__ されているという __前提__ で処理を行っているので、下記のプログラムのように `xytext_to_xy` を __元に戻す__ 必要があります。

```python
def xytext_to_xy(coord):
    x, y = coord.split(",")
    return int(x), int(y)
```

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

```diff_python
def xytext_to_xy(coord):
-   x, y = coord[1:-1].split(",")
+   x, y = coord.split(",")
    return int(x), int(y)
```
</details>

下記のプログラムを実行することで、修正したデータ構造で、`test_judge` が正しく動作することが確認できます。実行結果は、先ほどと同じなので省略します。

```python
test_judge(testcases)
```

## xy 文字座標を `","` で区切って連結するデータ構造

下記は、xy 文字座標を __`","` で区切って連結__ するデータ構造でテストケースを記述するプログラムです。このデータ構造の場合は、引き分けのテストケースのように、__座標の数が増える__ と文字列の __長さ__ が __かなり長く__ なってしまいます。

```python
testcases = [
    # 〇 の勝利のテストケース
    [                                      # 着手順とゲーム盤
        "(0,0),(0,1),(1,0),(1,1),(2,0)",   # 135  ooo
        Marubatsu.CIRCLE,                  # 24.  xx. 
    ],                                     # ...  ...
    # × の勝利のテストケース
    [                                           # 着手順とゲーム盤
        "(0,1),(0,0),(1,1),(1,0),(0,2),(2,0)",  # 246  xxx 
        Marubatsu.CROSS,                        # 13.  oo.
    ],                                          # 5..  ...
    # 引き分けのテストケース
    [                                                            # 着手順とゲーム盤
        "(0,0),(0,1),(1,0),(1,1),(2,1),(2,0),(0,2),(1,2),(2,2)", # 136  oox 
        Marubatsu.DRAW,                                          # 245  xxo
    ],                                                           # 789  oxo
]
```

データ構造が変わったので、`test_judge` を __修正__ する必要がありますが、x 座標と y 座標を区切る場合と、xy 文字座標を区切る場合で、__同じ `","`__  が __使われている__ ので __工夫が必要__ になります。まず、`split` を使う方法について紹介します。

### `split` を使った `text_to_list` の定義

__`"(0,0),(0,1),(1,0),(1,1),(2,0)"`__ をよく見ると、__xy 文字座標__ を __区切る__ `","` の __前後__ に、必ず __`")"`__ と __`"("`__ が __記述__ されていることがわかります。そのため、__先程と同様__ に、__`"),("`__ という __3 文字__ の文字列を __実引数__ に記述して __`split` メソッド__ を呼び出すことで、__xy 文字座標__ を __要素__ とする __list を作成する__ ことができます。全体の文字列の __先頭__ の `"("` と __末尾__ の `")"` を __削除する必要がある__ 点も __先ほどと同様__ です。下記は、そのように `text_to_list` を修正したプログラムです。なお、他の関数を修正する必要はありません。

```python
def text_to_list(coords):
    coord_list = coords[1:-1].split("),(")
    return coord_list
```

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

```diff_python
def text_to_list(coords):
-   coord_list = coords[1:-1].split(")(")
+   coord_list = coords[1:-1].split("),(")
    return coord_list
```
</details>

下記のプログラムを実行することで、修正したデータ構造で、`test_judge` が正しく動作することが確認できます。実行結果は、先ほどと同じなので省略します。

```python
test_judge(testcases)
```

### `split` を使わない `text_to_list` の定義

`split` メソッドを使った修正の方が簡単ですが、参考までに`split` を使わない場合の `text_to_list` の定義の方法について紹介します。

xy 文字座標を __直接連結__ した場合は、__xy 文字座標__ が __5 文字の文字列__ であることに __注目__ して、下記のような `text_to_list` を定義して利用しました。下記のプログラムは、文字列の先頭から、__5 文字おき__ に、__5 文字分__ を取り出すという処理を行っています。

```python
def text_to_list(coords):
    coord_list = [ coords[i:i+5] for i in range(0, len(coords), 5) ]
    return coord_list
```

xy 文字座標を直接連結した場合は、__間__ に __`","`__ という __1 文字が入る__ ので、__6 文字おき__ に、__5 文字分__ を取り出すように修正すれば良いことがわかります。下記は、`text_to_list` をそのように修正したプログラムです。

```python
def text_to_list(coords):
    coord_list = [ coords[i:i+5] for i in range(0, len(coords), 6) ]
    return coord_list
```

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

```diff_python
def text_to_list(coords):
-   coord_list = [ coords[i:i+5] for i in range(0, len(coords), 5) ]
+   coord_list = [ coords[i:i+5] for i in range(0, len(coords), 6) ]
    return coord_list
```
</details>

__このまま__ `test_judge` を実行すると __エラーが発生__ します。その理由は、__先程__ `xytext_to_xy` を、xy 文字座標の __先頭__ と __最後__ に `"("` と `")"` が __記述されていない場合__ の処理を行うように __修正__ してしまったためです。下記のように `xytext_to_xy` を __修正し直して__ ください。

```python
def xytext_to_xy(coord):
    x, y = coord[1:-1].split(",")
    return int(x), int(y)
```

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

```diff_python
def xytext_to_xy(coord):
-   x, y = coord.split(",")
+   x, y = coord[1:-1].split(",")
    return int(x), int(y)
```
</details>

下記のプログラムを実行することで、修正したデータ構造で、`test_judge` が正しく動作することが確認できます。実行結果は、先ほどと同じなので省略します。

```python
test_judge(testcases)
```

:::note
__1 行に記述__ するプログラムが __長くなる__ とプログラムが __見づらく__ なります。また、__1 画面に収まらない__ ような長さになった場合は、__スクロールが必要__ になります。

そのような場合は、__途中で改行__ を行うことで、プログラムを __読みやすくする__ ことが出来ます。ただし、__`""`__ で囲ったリテラルで __文字列を記述__ する場合は、__途中で改行__ を行うことが __出来ない__ ので、__工夫が必要__ になります。具体的には、文字列を __いくつかに分けて__、それぞれを __`+` 演算子__ を使って __連結__ することで、__`+` 演算子__ の __前後で改行__ を行うことができるようになります。下記は、引き分けのテストケースの部分の __文字列__ を __2 つに分けて改行__ した場合のプログラムです。

```python
    [                                                            # 着手順とゲーム盤
        "(0,0),(0,1),(1,0),(1,1),(2,1)," + 
        "(2,0),(0,2),(1,2),(2,2)",                               # 136  oox 
        Marubatsu.DRAW,                                          # 245  xxo
    ],    
```

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

```diff_python
    [                                                            # 着手順とゲーム盤
-       "(0,0),(0,1),(1,0),(1,1),(2,1),(2,0),(0,2),(1,2),(2,2)", # 136  oox 
+       "(0,0),(0,1),(1,0),(1,1),(2,1)," + \
+       "(2,0),(0,2),(1,2),(2,2)",                               # 136  oox 
        Marubatsu.DRAW,                                          # 245  xxo
    ],  
```
</details>

なお、Python では、__文の途中で改行__ する際に、__改行の直前__ で __`\` を記述する必要__ がありますが、__list のリテラル__ の中で __要素を記述__ する際は、__`\` を記述せずに__ 途中で __改行__ することが __できます__。下記のプログラムは、__代入文の途中で改行__ しているので実行すると __エラーが発生__ しますが、上記のプログラムは、__list の要素を記述__ する際に __改行__ を行っているので、__エラーにはなりません__。

なお、list の要素を記述する際に、改行の前に `\` を記述しても __エラーにはならない__ ので、__覚えられない__ 場合は、__常に__ 改行の前に __`\` を記述__ しても __構いません__。

```python
a = "ABC" +
"DEF"
```
実行結果
```
  Cell In[45], line 1
    a = "ABC" +
               ^
SyntaxError: invalid syntax
```

上記のエラーメッセージは、以下のような意味を持ちます。

* __SyntaxError__
構文（文法のこと）（syntax）に関するエラー
* __invalid syntax__
（文の途中で改行されるという）不正な（invalid）文法（syntax）
:::

# 今回の記事のまとめ

今回の記事では、__列__ も __行__ も __整数__ で __表現__ し、その __2 つを並べた文字列__ で座標を表現するという __データ構造__ と、それを扱う __アルゴリズム__ に関して紹介しました。

下記は、今回の記事で紹介した __データ構造__ を __表にまとめた__ ものです。参考までに、一番下の行に、__複数の xy 座標を list で表現__ したデータ構造を入れてあります。

__短さ__ と、__分かりやすさ__ の __記号__ は、◎、〇、△、×、×× の順で評価が高いという意味を表します。分かりやすさの記号は __筆者の主観__ なので、人によって感じ方が違うでしょう。

桁数の列で、__1 桁のみ__ と書かれている場合でも、__工夫次第__ では複数の桁数や、任意の桁数を記述することが出来る場合があります。


| xy座標 | 複数のxy 座標 | 記述例 | 短さ | 分かりやすさ | 桁数 |
|:-:|:-:|:-:|:-:|:-:|:-:|
| 直接連結  | 直接連結  | `"0011"`  | ◎  | △  | 1 桁のみ |
| 直接連結  | `","` で区切る  | `"00,11"`  | 〇  | 〇  | 1 桁のみ |
| `","` で区切る  | 直接連結  | `"0,01,1"`  | 〇  | ××  | 1 桁のみ |
| `","` で区切る  | `","` で区切る  | `"0,0,1,1"`  | △  | ×  | 任意 |
| `","` で区切る  | `":"` で区切る  | `"0,0:1,1"`  | △  | △  | 任意 |
| 数学と同じ  | 直接連結  | `"(0,0)(1,1)"`  | ×  | 〇  | 任意 |
| 数学と同じ  | `","` で区切る  | `"(0,0),(1,1)"`  | ××  | 〇  | 任意 |
| list  | list  | `[[0,0],[1,1]]`  | ××  | △  | 任意 |

__個人的__ には、__テストケース__ のデータを __プログラム内に直接記述__ するためのデータ構造としては、上記の表の中では 2 番目の __`"00,11"`__  が __最も優れている__ のではないかと __思います__ が、前回の記事で紹介した __Excel 座標__ を __`","` で区切る__ `"A1,B2"` __のほう__ が __さらに優れている__ のではないかと __思います__ ので、本記事ではそちらの方を採用することにします。

__採用しないデータ構造__ を 1 回分の記事を使って __紹介__ したのは、__無駄ではないか__ と思っている人がいるかもしれません。今回の記事で紹介したデータ構造は、__確かに__ テストケースを記述する際は __大きな利点はない__ かもしれませんが、__別の目的__ で利用する際に、__今回紹介__ したデータ構造 __のほうが__、__Excel 座標よりも適している__ 場合が実際にあります。

__具体例__ として、例えば __囲碁のゲーム盤__ のサイズは __19 x 19__ ですが、__Excel 座標__ で __(15, 16)__ をどのように __表記するか__ を __すぐにわかる人__ は __どれだけいる__ でしょうか？__〇×ゲーム__ であれば、`"A"`、`"B"`、`"C"` の __3 つしか__ アルファベットが __使われない__ ので __すぐに慣れる__ ことができますが、__アルファベット__ の __15 番目の文字__ が何であるかを __すぐに思い浮かべる__ ことは __困難__ なので、__Excel 座標__ で __囲碁の座標を記述__ するのは __容易ではない__ でしょう。

個人的には、チェスや将棋以上の大きさのゲーム盤の座標を記述する場合は、Excel 座標よりも、xy 文字座標の方が扱いやすくなるような気がしますが、チェスの棋譜に親しんでいる人にとっては Excel 座標のような[^2]座標の方が扱いやすいのではないかと思います。

これまでの記事で何度も説明しているように、__様々なデータ構造__ と、そのデータ構造を処理するための __様々なアルゴリズム__ を知っておくことは、__状況に応じて__ 自分にとって最も適した __アルゴリズムとデータ構造を選択__ できるようになるために __重要__ なことなのです。

これまでの記事では、座標を表すデータを、x 座標と y 座標を使って表現するというデータ構造で表現してきましたが、次回の記事では __それらとは異なるデータ構造__ について紹介します。次回で座標を表すデータ構造の紹介を終え、テストの続きを行う予定です。

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

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

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

今回の記事では、marubatsu.py は更新していないので、marubatsu_new.py はありません。

以下のリンクは、今回の記事で作成した test.py です。データ構造ごとに名前を変えてそれぞれの関数を定義しています。また、docstring などは省略します。

https://github.com/ysgeso/marubatsu/blob/master/029/test_new.py

# 次回の記事

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

[^1]:`int` は、__半角__ と、__全角__ の __両方__ の空白文字を __無視__ します
[^2]:チェスでは、一番下の行の y 座標を 1 と表記するので、Excel 座標と比べて __上下が逆__ になります