Pythonで〇×ゲームのAIを一から作成する　その3　ゲームの仕様の整理

# 目次と前回の記事

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

https://qiita.com/ysgeso/items/44c65c4bbec49cff7d77

# 〇×ゲームの仕様
プログラムを実装する際には、はじめにそのプログラムの __仕様__ をおおざっぱにでも決める必要があります。一般的なプログラムに必要な仕様としては、以下の 2 つが挙げられます。それぞれについて説明します。

* プログラムの機能
* ユーザーインターフェース

## プログラムの機能の仕様

プログラムがどんな機能を持つかの仕様のことです。これが決まらなければ次のユーザーインターフェースの仕様を決めることはできないので、厳密ではないにせよ、ある程度はこの部分の仕様をはっきりさせておく必要があります。

〇×ゲームの場合は、〇×ゲームのルールがこれに該当し、これは厳密に決まっているので列挙してみます。

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

:::note
このような仕様の記述は、はじめは難しいと感じるかもしれませんが、初心者のうちは __プログラムをどのような順番で実装していくかなどの重要な手掛かり__ となります。

仕様をうまく正確に記述できるようになるためには経験が重要です。時間に余裕がある方は、じゃんけん、ババ抜き、オセロなどの、__よく知っていて__ 比較的 __ルールが簡単__ なゲームの仕様を実際に記述してみて __経験を積む__ ことをお勧めします。

なお、プログラミングの技術が向上すれば、オセロ程度のゲームであれば頭の中だけで仕様を列挙してプログラムを実装することもできるようになります。
:::

上記は、〇×ゲームというゲームの __ルール__ の仕様ですが、それ以外にも例えば以下のような仕様を追加して、実装する〇×ゲームを独自の機能で味付けすることもできます。なお、下記は例なので、この記事で下記の機能をすべて実装するわけではありません。

* ゲームの途中や、終了後に新しいゲームを開始できるようにする
* 通算成績を数えて表示する
* マークを置く際に、時間制限を設ける

ただし、これらはどちらかといえばこのゲームの本筋の機能ではなく、オプション的な機能なので、ゲームを作り始める前に __厳密に決めておく必要はない__ でしょう。また、ゲームを作っている最中にいろんなアイディアが思い浮かぶことはよくあることです。そのような場合は、思いついた時にそのアイディアをメモしておいて、後で必要に応じて仕様に加えていくと良いでしょう。本記事でもプログラムの実装中に思いついた機能をどんどん仕様に追加していくという形でプログラムを実装していきます。

:::note
グループでアプリケーションを実装する場合などでは、個人個人が好き勝手な方法でプログラムを実装すると後で収集がつかなくなってしまうため、実装したいアプリケーションの機能を __厳密に記述した仕様書__ をあらかじめ作成してからアプリケーションを実装するのが一般的です。

一方、本記事のプログラムのように、個人で小規模なプログラムを実装する際は、あらかじめ厳密な仕様書を作成するのは困難なので、__大雑把な仕様__ を決めてからプログラム実装し、__思いつくまま__ に仕様を追加しながらプログラムを実装することが多いようです。

前者の方法は、厳密な仕様を元にプログラムを実装するので、どうしても仕事でプログラミングを行っているという感じになりがちですが、仕様がきちんと作られており、プログラマーが仕様通りにプログラムを実装する腕を持っていれば、確実に仕様で想定した通りのプログラムが実装できます。現実の世界では、厳密な設計図を元にビルなどを建てる場合に似ているでしょう。

後者の方法は、料理の創作レシピのように、思いつくまま自分の好きなようにプログラムを実装することができます。その結果、うまくいけば最初に想定していた以上のプログラムが実装できることもありますが、もちろん想定以下のプログラムになってしまうこともあるでしょう。個人的には、この __自由奔放さ__ が個人でプログラムを行う際の __醍醐味__ の一つだと思っています。ただし、自分の思い通りにプログラムを記述するためには、__プログラミングのスキル__ がどうしても __必要__ になります。
:::

## ユーザーインターフェース

ユーザインターフェース（以下、user interface の頭文字をとって __UI__ と表記します）とは、プログラムの操作環境（interface）のことで、主な UI としてはプログラムの __出力__ と __入力__ があります。〇×ゲームの場合は具体的には「どのようにゲームを画面に表示するか」という __出力の方法__、「どのようにユーザーがゲームを操作するか」という __入力の方法__ などがあります。

UI には様々な種類があり、それぞれ利点と欠点があります。そのため、状況にあわせて必要に応じて使い分ける必要があります。また、UI の良し悪しは、プログラムの使いやすさや遊びやすさなどに大きく関係するので非常に重要です。

:::note
文字を入力する UI として、キーボード、スマートフォンのフリック入力、音声入力などが挙げられますが、それぞれ以下のような利点と欠点があります。そのため、__状況によって使い分ける__ 必要があります。例えば、いくら入力が速いからといって、スマートフォンでキーボード入力する人がほとんどいないのは、小さくて持ち運びが便利というスマートフォンの利点と、大きくて持ち運びに向かないというキーボードの欠点が両立しないからでしょう。

|  | 利点 | 欠点 |
|:--|:--|:--|
| キーボード  | 習熟すれば一番入力が速い  | 習熟するのが大変<br>持ち運ぶ場合は不便  |
| フリック入力  | 小さなタッチパネルで入力できる<br>キーボードよりは習熟が楽  | キーボードより入力が遅い  |
| 音声入力  | 練習なしで利用できる<br>入力が速い  | 周りがうるさいと使えない<br>図書館などで使えない<br>入力ミスの修正が面倒  |
:::

例えば、〇×ゲームの場合は以下のような UI が考えられます。

* __出力__ （ゲーム盤の表示）

|  | 利点 | 欠点 |
|:--|:--|:--|
| 文字でゲーム盤を表示する CUI | 実装しやすい<br>小さく表示できる<br>表示にかかる時間が短い  | 見栄えが悪い<br>マウスで座標の入力ができない  |
| 画像でゲーム盤を表示する GUI | 見栄えが良い<br>マウスで座標を指定できる  | 実装が少し面倒<br>表示場所がある程度必要[^1]<br>表示にかかる時間が長い[^2]  |

:::note
入出力を文字（character）で行う UI のことを __CUI__（character user interface）と呼びます。CUI では出力は画像を使わずに __文字だけ__ で行い、入力も __キーボードのみ__ を使って文字で行います。

入出力を画像（graphic）で行う UI のことを __GUI__（graphical user interface）と呼びます｡GUI は多くのアプリケーションで使われている UI で、入力は画面に表示されたメニューやボタンなどの __画像__ に対して、マウスやタッチパネルなどを使って行います。一般的に、CUI と比べて __直観的でわかりやすい__ という利点があります。

CUI は一般的なアプリケーションであまり使われないので、文字だけでゲーム盤を表示するということの意味が分からない人もいるかと思いますので、下図に〇×ゲームのゲーム盤を文字だけで表示する例を示します[^3]。下図では、半角の . （ピリオド）が空白のマスを表しています。
 
```
oxo
.ox
..x
```

本記事では 〇×ゲームの UI を実装する際に、最初はより簡単に実装できる CUI で実装し、その後で GUI で実装する予定です。

:::

* __入力__ （マークを配置するマスの指定方法）[^4]

|  | 利点 | 欠点 |
|:--|:--|:--|
| キーボードで文字で座標を入力する  | CUI で利用できる<br>（CUIではこの方法しか利用できない）  | 座標の入力が面倒<br>入力したいマスの座標が何かがわかりづらい<br>(-1, 100) のような存在しない座標を入力できてしまう  |
| メニューで選択する | 一覧で表示されるので、存在しないマスが選択されることがない  | メニューの選択操作が面倒<br>入力したいマスの座標に対応するメニュー何かがわかりづらい  |
| カーソルキーや十字キーでカーソルを動かして選択する[^5]  | マウスやタッチパネルがなくても利用できる<br>直観的でわかりやすい  | 選択したいマスがカーソルから遠い場合、選択するための操作が面倒 |
| マウスで直接選択する  | 直観的でわかりやすい<br>素早く座標を選択できる  | マウスやタッチパネルがないと利用できない  |

:::note
UI は、厳密にはプログラムの機能の一種ではありますが、プログラムの機能と UI を分けて考えたほうがプログラムを作りやすいことが多いので、本記事でも分けています。

〇×ゲームの場合は、ゲームのルールの仕様と、ゲーム盤を画面にどのように表示し、どのような入力方法でマスにマークを配置するかという UI は __完全に独立している__ ので分けて考えたほうがよいでしょう。

なお、キーボードの入力を前提とするタッチタイピングの練習アプリなど、ゲームの機能と UI をうまく切り離して考えることが難しい場合もあります。
:::

〇×ゲームの UI の候補をいくつか示しましたが、ある程度〇×ゲームの実装を行わなければ、UI を実装することはできません。例えば、〇×ゲームのゲーム盤を表すデータがなければ、そもそも画面にゲーム盤を表示することは不可能です。そこで、どの UI を採用するかについては後回しにし、先に〇×ゲームの機能の実装から始めることにします。UI は必要になった時点で実装します。

# ゲーム盤の表現

先程列挙した〇×ゲームの機能の仕様を再掲します。

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

数が多いので、初心者のうちはどこから手を付けてよいかわからないことが多いと思いますが、一般的には以下のような機能から実装を始めると良いでしょう。

* その機能がなければ、そもそも他の機能を実装できない機能
* そのような機能が複数ある場合は、最も作りやすそうな機能

〇×ゲームの場合は、そもそもゲーム盤の情報がなければ他の機能を実装することは不可能なので、ゲーム盤の情報を表現する、下記の仕様の 1. から実装することにします。

__1. 正方形で区切られた 3 x 3 の 2 次元のゲーム盤上でゲームを行う__

最初に何を実装するかが決まりましたので、次回の記事から〇×ゲームの実装を開始します。

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

本記事で入力したプログラムはありません。

# 次回の記事

https://qiita.com/ysgeso/items/9efd60c7a28381e6cbf7

[^1]:普通にゲームをする場合は全く問題にはなりませんが、例えば複数のゲーム盤を並べて表示して比較したい場合などでは、表示に必要なスペースが大きいことが問題になる場合があります
[^2]:長いといっても、人間が知覚できないほど短い時間で表示できるので、〇×ゲームの場合は表示の遅さが問題になることは全くないでしょう。ただし、アニメーションなど、大量のゲーム盤の画像を短時間に表示する必要がある場合などでは、表示が遅く感じることがあるかもしれません
[^3]:1980 年頃のコンピューターは画像が使えないものが多く、このような画面のゲームが多かったようです。文字だけでゲームを表示する有名なゲームに [ローグ](https://ja.wikipedia.org/wiki/%E3%83%AD%E3%83%BC%E3%82%B0) というゲームがあり、このゲームを原点とするゲームのジャンルとして、ローグライクゲームというゲームのジャンルがあります。日本ではトルネコの大冒険や不思議のダンジョンシリーズが有名です
[^4]:他にも音声入力などが考えられますが、音声入力を行うプログラムを実装するのは簡単ではないので、本記事では取り上げません
[^5]:ゲーム機など、コントローラーのみで操作するゲームの場合など