「"声"で打つオセロ」の作り方    〜〜オリジナル Google Home づくりを目指して Ep 1. 〜〜

本記事では、**Raspberry Pi**を用いた**「"声"で打つオセロ」**の作り方を紹介します。

言うまでもなくオセロや囲碁、将棋といったボードゲームは、石や駒を使って盤の上で対戦するゲームですが、「"声"で打つオセロ」とは、その名の通り石に触れることなく、声だけで対戦ができるオセロです。

作るきっかけとしては、以前から興味のあった電子工作ともともと勉強してきた人工知能をかけ合わせたい！という気持ちからいろいろと考え、**電子工作×音声認識**を活かせるオセロを思いつきました。(囲碁や将棋などにも応用可能ですが、今回は簡単なオセロで)

~~将来的に「"声"で指す将棋」を作って、羽生さんにプレイしてもらうという夢をひそかに持っています。~~

さらに「"声"で打つオセロ」を作る中で、音声認識や自然応答のアルゴリズムの実装、またクエリに対する検索結果の返却など、機能をリッチにしていくことで、昨年発売され話題になったGoogle Homeもどきが作れるのではないかと考えました。
なので、今回はオセロのプレイに特化していますが、最終的にはGoogle Homeが持つような機能を実装していく予定です。


# 完成品
まずは、完成品を見てみます。

![qvQqQ5b019e26cb81bbc1607b2e34.jpg](https://qiita-image-store.s3.amazonaws.com/0/221139/2126d753-3a3f-e52f-5509-669491980bf5.jpeg)

配線がとんでもないことになっていますね。
画面中央に見えるのが、$8 \times 8$のオセロの盤面をLEDで表現しているもので、左下のマイクから座標を声で入力することで、対応する座標が点灯し、挟まれているLEDが反転します。（例えば、「F5赤」と発声すれば、左上から数えて5行6列のLEDが赤色に変わります。）

デモ動画も公開しているので、ぜひご覧ください。


<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">「&quot;声&quot;で打つオセロ」作りました【Voice Othello Demo Movie】 <a href="https://t.co/WpVXZGHfjz">pic.twitter.com/WpVXZGHfjz</a></p>&mdash; ひよこ (@hiyoko9t) <a href="https://twitter.com/hiyoko9t/status/1005403067901095936?ref_src=twsrc%5Etfw">2018年6月9日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>



# 準備する部品
「"声"で打つオセロ」に必要となる部品は以下となります。

ディスプレイの有無などにも依りますが、おおよそ2~4万程度で揃えることができます。
一応、例として型番も示しておきましたが、特にこれでないとダメということはないのでお好みで。

（設計が固まるまでに何度も買い直したりしたので、必要なものが固まるまでかなりつらかったです...。）

| グループ | 名称 | 型番 | 個数 |
|:-----------:|:-----------:|:------------:|:------------:|
| Raspberry Pi 本体関連 | Raspberry Pi             | Raspberry Pi 3 Model B | 1 |
|                      | 電源アダプタ               |                        | 1 |
|                      | micro SD 32GB           |                        | 1 |
|                      | Raspberry Pi ケース(任意) |                        | 1 |
| Raspberry Pi 周辺機器 | ディスプレイ                |                        | 1 |
|                      | HDMIケーブル              |                        | 1 |
|                      | USBマウス                 |                        | 1 |
|                      | USBキーボード              |                        | 1 |
| 回路関連              | ミニブレッドボード            | ＢＢ－６０１                 | 16 |
|                      | ブレッドボード               | ＥＩＣ－８０１                | 6 |
|                      | 赤色LED                  | ＯＳＤＲ５１１３Ａ             | 64 |
|                      | 青色LED                  | ＯＳＵＢ５１１１Ａ－ＳＴ（１５度）１０ｃｄ         | 64 |
|                      | 抵抗                     | カーボン抵抗（炭素皮膜抵抗）　１／４Ｗ　１５０Ω | 128 |
|                      | ジャンパーワイヤー           |                        | 大量 |
|                      | シフトレジスタ              | ＳＮ７４ＨＣ５９５Ｎ             | 16 |
|                      | 丸ピンICソケット(任意)       |                        | 16 |

今回は、オセロの白黒を赤青LEDで表現していますが、もちろん別の色でも可能です。
後から気付いたのですが、フルカラーLEDを用いれば後で示す回路が少し単純化できるかもしれません。(作る方はぜひ試してみてください)

また、はんだ付けが出来る方はブレッドボードやジャンプワイヤーなど節約することが出来るので、より綺麗により安価に回路を作れると思います。


# Raspberry Pi とは
実際の作り方に入る前に、簡単に今回の作品の鍵となるRaspberry Pi(以下、ラズパイ)について見ていきましょう。

実物は以下のようなものです。

![GCjEG5b002730edef02bc601fb3fe.jpg](https://qiita-image-store.s3.amazonaws.com/0/221139/590e578e-6f26-51f0-aba0-39ad06502631.jpeg)



名刺と同じくらいで、小さいですね。
イメージとしては、2000年代の性能のパソコンが、とても小さくなったもので良いと思います。

この手軽さから、ラズパイは電子工作でよく用いられます。
他に有名なものとしては、Arduinoも挙げられますね。どちらも特徴があるので、気になる方は調べて見てください。

ラズパイには、**GPIO(General Purpose Input/Output)ポート**という様々な出力を行うピンがあります。([1]より)

![Raspberry-Pi-GPIO-Layout-Model-B-Plus-rotated-2700x900-1024x341.png](https://qiita-image-store.s3.amazonaws.com/0/221139/34d0154f-d5fb-ce10-b8e8-b97ceea94b50.png)

例えば、1番のピンは3.3Vの電源、6番のピンはグラウンドとなるので、1番のピンと6番のピンの間にLEDを繋ぐことで、LEDを光らせることができます。

覚える必要はありませんが、後で使用することになるので、図は参照できるようにしておきましょう。

# 手順
実際に作り方を見ていきます。

大まかな流れとしては、以下の3つとなります。

1. OSのインストール
2. 回路を組み立てる
3. コーディングする

それでは、一つ一つ細かく見ていきましょう。


## OSのインストール
ラズパイは、そのままで使用することはできません。
まずは、ラズパイを動かすためのOSをインストールする必要があります。

はじめに、microSDに以下の手順でOSをインストールします。

1. https://www.raspberrypi.org/downloads/ へアクセス
2. 「NOOBS」のアイコンをクリック
3. 「Download ZIP」をクリック
4. ダウンロードされた圧縮ファイルを展開
5. microSDに展開後のファイルをコピー

<img width="454" alt="スクリーンショット 2018-05-14 9.48.18.png" src="https://qiita-image-store.s3.amazonaws.com/0/221139/017fdbf1-e890-57d8-1ca8-0dbc2980fc4b.png">

ここまでできたら、いよいよラズパイ本体を動かす準備ができました。
以下の手順で、ラズパイ本体にOSをインストールします。


1. microSDをラズパイにセット
2. マイク、キーボード、ディスプレイを接続
3. マイクロUSBの電源を接続
4. 「Raspbian」にチェックを入れ、「Install」ボタンをクリック
5. 起動後、左上の「Menu」から「Preferences」→「Raspberry Pi Configuration」で言語、タイムゾーン、キーボードなど各種設定



細かい設定などは、こちらの本「[カラー図解 最新 Raspberry Piで学ぶ電子工作 作って動かしてしくみがわかる](
https://www.amazon.co.jp/%E3%82%AB%E3%83%A9%E3%83%BC%E5%9B%B3%E8%A7%A3-Raspberry-Pi%E3%81%A7%E5%AD%A6%E3%81%B6%E9%9B%BB%E5%AD%90%E5%B7%A5%E4%BD%9C-%E4%BD%9C%E3%81%A3%E3%81%A6%E5%8B%95%E3%81%8B%E3%81%97%E3%81%A6%E3%81%97%E3%81%8F%E3%81%BF%E3%81%8C%E3%82%8F%E3%81%8B%E3%82%8B-%E3%83%96%E3%83%AB%E3%83%BC%E3%83%90%E3%83%83%E3%82%AF%E3%82%B9/dp/4062579774/ref=sr_1_2?s=books&ie=UTF8&qid=1525836350&sr=1-2&keywords=raspberry+pi)」を参照しました。


## 回路を組み立てる
今回、物理的な動作としては、LEDを光らせるだけなので、それほど難しい回路は必要ありません。
ただし、盤面の状況に応じて、赤色/青色LEDを切り替える必要があります。
そこで、今回はLEDを制御するためにシフトレジスタを使用します。


### シフトレジスタ(74HC595)
今回はシフトレジスタとして、74HC595を用います。
74HC595は8ビットシフトレジスタなので、8個の出力を制御することができます。(光らせたり、光らせなかったり)

74HC595は以下の図のように16個のピンを持っています。
各ピンの配置は以下の通りとなります。([2]より)

<img width="488" alt="スクリーンショット 2018-05-04 13.09.44.png" src="https://qiita-image-store.s3.amazonaws.com/0/221139/a4ea836a-8287-e89c-b497-a29a121b105b.png">

各ピンの役割は次の通りです。

| ピン名称 | 役割 |
|:-----------:|:------------:|
| QA~QH    | 出力先(8個) |
| GND   | グラウンド |
| QH'   | 次のシフトレジスタへの接続 |
| SCLR  | リセットクロック |
| SCK   | シフトレジスタクロック |
| RCK   | ラッチクロック |
| G     | HiにするとQA~QHがハイインピーダンス |
| SI    | シリアルデータ入力 |
| VCC   | 電源 |

シフトレジスタの接続先は、以下のように接続します。(参考: [少ない出力ピンで、大量のLEDを制御する（シフトレジスタ使用）](http://www.hiramine.com/physicalcomputing/arduino/shiftregister.html))

| ピン番号 | ピン名称 | 接続先(一つ目) | 接続先(二つ目以降) | 接続先(最後) |
|:-----------:|:-----------:|:------------:|:------------:|:------------:|
| 1| QB    | 2つ目のLEDのアノード | 2つ目のLEDのアノード | 2つ目のLEDのアノード |
| 2| QC    | 3つ目のLEDのアノード | 3つ目のLEDのアノード | 3つ目のLEDのアノード |
| 3| QD    | 4つ目のLEDのアノード | 4つ目のLEDのアノード | 4つ目のLEDのアノード |
| 4| QE    | 5つ目のLEDのアノード | 5つ目のLEDのアノード | 5つ目のLEDのアノード |
| 5| QF    | 6つ目のLEDのアノード | 6つ目のLEDのアノード | 6つ目のLEDのアノード |
| 6| QG    | 7つ目のLEDのアノード | 7つ目のLEDのアノード | 7つ目のLEDのアノード |
| 7| QH    | 8つ目のLEDのアノード | 8つ目のLEDのアノード | 8つ目のLEDのアノード |
| 8| GND   | GND | GND | GND |
| 9| QH'   | 次のシフトレジスタのSI | 次のシフトレジスタのSI | なし |
|10| SCLR  | 3.3V | 3.3V | 3.3V |
|11| SCK   | GPIO 19 <br> 次のシフトレジスタのSCK | 次のシフトレジスタのSCK | 前のシフトレジスタのSCK |
|12| RCK   | GPIO 20 <br> 次のシフトレジスタのRCK | 次のシフトレジスタのRCK | 前のシフトレジスタのRCK |
|13| G     | GND | GND | GND |
|14| SI    | GPIO 21 | 前のシフトレジスタのQH' | 前のシフトレジスタのQH' |
|15| QA    | 1つ目のLEDのアノード | 1つ目のLEDのアノード | 1つ目のLEDのアノード |
|16| VCC   | 3.3V | 3.3V | 3.3V |

複雑に見えるかもしれませんが、ピンを一つ一つ見ていけば、それほど難しくないと思います。

### 完成した回路
シフトレジスタについて理解できたら、後はLEDと抵抗を繋ぐだけです。
抵抗は150Ωのものを用いましたが、使用するLEDとオームの法則から、適当な抵抗を使用してください。

完成した回路は以下となります。（再掲）

![qvQqQ5b019e26cb81bbc1607b2e34.jpg](https://qiita-image-store.s3.amazonaws.com/0/221139/2126d753-3a3f-e52f-5509-669491980bf5.jpeg)

はんだ付けを嫌ってこのような設計にしてしまいましたが、素直にはんだ付けすれば良かったです。

## コーディングする
コーディングでポイントとなるのは以下の三点です。

* シフトレジスタを用いたLEDの制御
* 音声認識
* オセロの処理

一つ一つ見ていきましょう。

### シフトレジスタを用いたLEDの制御
シフトレジスタを組み込んだ回路を設計しただけでは、自由にLEDを制御することができません。
(参考: [RaspberryPiにシフトレジスタ【74HC595】 を使って、複数のLEDをチカチカさせてパーリィーナイしてみた【Python使用】](http://raspi.seesaa.net/article/417590857.html))

実際にLEDを制御するためには、各ピンにシリアルデータを伝えなければなりません。
今回は$8 \times 8$の盤面に3つの状態(0なら消灯、1なら赤LED点灯、2なら青LED点灯)を情報として保持させます。
この盤面からLEDを点灯させるためのコードが以下となります。

```
#!/usr/local/bin/python
# -*- coding: utf-8 -*-
import RPi.GPIO as GPIO
import time

# 各GPIOの番号
SCK = 19
RCK = 20
SI = 21

# GPIOポートを使用
GPIO.setmode(GPIO.BCM)
 
# 初期化
GPIO.setup(SCK, GPIO.OUT)
GPIO.setup(RCK, GPIO.OUT)
GPIO.setup(SI, GPIO.OUT)

def reset():
    """
    盤面をリセットする
    """
    GPIO.output(SCK, GPIO.LOW)
    GPIO.output(RCK, GPIO.LOW)
    GPIO.output(SI, GPIO.LOW)
 
def shift(PIN_NUM):
    """
    受け取ったピン番号をシフトする

    @param ピン番号
    """
    GPIO.output(PIN_NUM, GPIO.HIGH)
    GPIO.output(PIN_NUM, GPIO.LOW)

def all_high():
    """
    すべてのLEDを点灯させる（動作確認用）
    """
    reset()

    for j in range(16):
        for i in range(8):
            GPIO.output(SI, GPIO.HIGH)
            shift(SCK)
        shift(RCK)

def board_flash(board):
    """
    受け取った盤面の状態をLEDに反映させる

    @param 盤面の状態
    """
    #reset()
    BOARD_SIZE = 8
    
    for i in range(BOARD_SIZE):
        for j in range(BOARD_SIZE):
            if board[abs(7-i)][abs(7-j)] == 0: # 消灯状態
                GPIO.output(SI, GPIO.LOW)
                shift(SCK)
                GPIO.output(SI, GPIO.LOW)
                shift(SCK)
            elif board[abs(7-i)][abs(7-j)] == 1: # 赤LED
                GPIO.output(SI, GPIO.LOW)
                shift(SCK)
                GPIO.output(SI, GPIO.HIGH)
                shift(SCK)
            elif board[abs(7-i)][abs(7-j)] == 2: # 青LED
                GPIO.output(SI, GPIO.HIGH)
                shift(SCK)
                GPIO.output(SI, GPIO.LOW)
                shift(SCK)

            if j == 3 or j == 7: # 8個出力するたびにラッチクロックをシフト
                shift(RCK)
```

### 音声認識
次に声を認識する手順を紹介します。
ラズパイで音声認識を行うもっとも有名な方法は、汎用大語彙連続音声認識エンジン**Julius**を用いる方法です。

Juliusのダウンロードからデモの実行は、こちらのサイト「[Julius の使い方（version 4.4.2 対応版）&nbsp;|&nbsp;Feijoa.jp](http://www.feijoa.jp/laboratory/raspberrypi/julius442/)」が詳しいです。
設定ファイルはこちらのサイト「[ラズパイに、人の声を理解させる。](http://www.asahi-net.or.jp/~cb9i-kn/geek/20170513totext.html)」に従いました。


#### 音声認識の精度を高める
ここまででJuliusによる音声認識ができたと思いますが、そのままだと認識精度があまりよくありません。
そこで、Juliusの記述文法を用いて、特定のルールを覚えさせることにより認識精度を高めます。(参考: [Julius の記述文法を用いて音声認識精度をあげてみた - 凹みTips](http://tips.hecomi.com/entry/20120112/1326390200)、[第7章 言語モデル](http://julius.osdn.jp/juliusbook/ja/desc_lm.html#id2537489))

- grammarファイル

grammarファイルは構文の制約を記述することができます。
すなわち、どのような文章の言葉が、入力として与えられるかを定義します。

オセロでは「A1」のように、一つ目の単語として「アルファベット」、二つ目の単語として「数字」が入力として与えられます。今回は、それにわかりやすくするためにLEDの「色」を追加するので、grammarファイルは以下のように書きます。

```
voice.grammar

S          :  NS_B ALPHABET_N NUMBER_N COLOR_N NS_E
# アルファベット
ALPHABET_N :  ALPHABET
# 数字
NUMBER_N   :  NUMBER
# 色
COLOR_N    :  COLOR
```

NS_B、NS_Eはそれぞれ無音区間をあらわします。
これで文法が定義できました。

- vocaファイル

vocaファイルは、単語表記と単語の読みを定義することができます。

オセロでは、アルファベットは「A」から「H」までの8個あれば十分なので、それ以外のアルファベットは定義する必要がありません。
設定ファイルを見るとわかりやすいと思います。

```
voice.voca

% ALPHABET
A         e i
B         b i:
C         s i:
D         d e:
E         i:
F         e h u
G         g i:
H         e i ch i
% NUMBER
1         i ch i
2         n i:
3         s a N
4         y o N
5         g o:
6         r o k u
7         n a n a
8         h a ch i
% COLOR
赤        a k a
青        a o
% NS_B

            silB
% NS_E

           silE
```

これで単語が定義できました。

後は、参考にあるようにこれらのファイルをコンパイルし、実行時に指定することで認識精度が上がります。
自分の手元では以下のように実行しています。

```
# grammar/vocaファイルをコンパイル
mkdfa.pl voice

# 実行
julius -C conf/vo_main.jconf -gram grammar/voice
```

#### モジュールモード
Juliusをモジュールモードで実行することで、音声認識サーバとして使用し、TCP/IP 経由でクライアントと接続することで、認識した音声を用いて実際の処理を書くことができます。(参考: [ラズパイで音声認識つかってLチカしてみる](https://ueqareer.net/2463)、[ただいまシステムの中身②　Socketを介した認識結果のXMLパース](http://hira-hide.hatenablog.com/entry/20170423/1492939230))

```
julius -C conf/vo_main.jconf -gram grammar/voice -gram grammar/settings -module &
```

この状態で以下のコードを実行することで、マイクから入力された音声により、異なる処理を実行することができます。

```
#!/usr/bin/python
# -*- coding: utf-8 -*-
import socket
import string
import copy
import othello
import flash
    
host = 'localhost'
port = 10500

client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect((host, port))

# 盤面の初期化
prev_board = othello.zero_board()
curr_board = othello.zero_board()

# マイク入力文字列の初期化
data = ""

def split_string(string):
    """
    入力された3文字を分解

    @param string :マイクから入力された文字列
    @return マイクから入力された列
    @return マイクから入力された行
    @return マイクから入力された色
    """
    string_uni = string.decode('utf-8')
    chars = list(unicode(string_uni))
    chars = [char.encode('utf-8') for char in chars]

    column = chars[0]
    row = chars[1]
    color = chars[2]

    return column, row, color
    
def convert_to_coodinate(column, row, color):
    """
    入力された列、行を配列のインデックスに変換

    @param  column :マイクから入力された列
    @param  row    :マイクから入力された行
    @param  color  :マイクから入力された色
    @return 配列の列インデックス
    @return 配列の行インデックス
    @return 色
    """
    COLUMN_LIST = ["A", "B", "C", "D", "E", "F", "G", "H"]
    COLOR_LIST = ["赤", "青"]

    column_tmp = COLUMN_LIST.index(column)
    row_tmp = int(row) - 1
    if color == "赤":
        color_tmp = "RED"
    elif color == "青":
        color_tmp = "BLUE"

    return column_tmp, row_tmp, color_tmp

  
try:
    while True:
        # マイクから入力された文字列の読み込み
        while (string.find(data, "\n.") == -1):
            data = data + client.recv(1024)

        strTemp = ""

        for line in data.split('\n'):
            index = line.find('WORD="')
 
            if index != -1:
                line = line[index + 6:line.find('"', index + 6)]
                if line != "
" and line != "
": 
                    strTemp = strTemp + line

        print(strTemp)
        if strTemp == "START":
            print("ボード初期化")
            prev_board = curr_board
            curr_board = othello.initialize_board()
            flash.board_flash(curr_board)
            print(curr_board)
        elif strTemp == "BACK":
            print("一つ戻る")
            tmp_board = copy.copy(curr_board)
            curr_board = prev_board
            flash.board_flash(curr_board)
            print(curr_board)
        elif strTemp == "END":
            print("終了")
            tmp_board = copy.copy(curr_board)
            curr_board = prev_board
            print(curr_board)
        elif strTemp != "":
            # 文字列のsplit
            input_column, input_row, input_color = split_string(strTemp)
            column, row, color = convert_to_coodinate(
                input_column, input_row, input_color)

            # オセロの処理
            new_board, change_flag = othello.next_board(column, row, color, curr_board)

            # 盤面の更新
            if change_flag == True:
                print("更新")
                prev_board = curr_board
                curr_board = new_board
            else:
                curr_board = new_board
                
            print(curr_board)

            # LEDの制御
            flash.board_flash(curr_board)

        data = ""
    
except KeyboardInterrupt:
    print("KeyboardInterrupt")
    pass
```

### オセロの処理
最後にオセロの処理を書きます。

オセロの処理は、特別なことはしていないのでこちらのサイト「[「プログラミング初心者がpythonの勉強がてら、オセロAIを作ってみた」をリファクタリングさせていただいた](https://qiita.com/shiracamus/items/f03127819fff0f1a4349)」を参考に書きました。

```
#!/usr/bin/python
# -*- coding: utf-8 -*-
import numpy as np
import copy

def zero_board():
    """
    消灯状態の盤面を返す

    @return 8*8のnp.array
    """
    BOARD_SIZE = 8
    
    return np.zeros((BOARD_SIZE, BOARD_SIZE))

def initialize_board():
    """
    初期化された盤面を返す

    @return 8*8のnp.array
    """
    BOARD_SIZE = 8
    INITIAL_RED   = [(4, 3), (3, 4)] 
    INITIAL_BLUE = [(3, 3), (4, 4)] 

    tmp_board = np.zeros((BOARD_SIZE, BOARD_SIZE))

    for coo in INITIAL_RED:
        tmp_board[coo] = 1

    for coo in INITIAL_BLUE:
        tmp_board[coo] = 2

    return tmp_board

def next_board(column, row, color, board):
    """
    打つ手から次の盤面を返す

    @param column   : int型の列番号
    @param row      : int型の行番号
    @param color    : str型の色
    @param board    : 8*8のnp.array型の現在の盤面
    @return 8*8のnp.array
    @return 正しい置石であったかどうかのboolean
    """
    BOARD_SIZE = 8
    DIRECTIONS_XY = ((-1, -1), (+0, -1), (+1, -1),
                     (-1, +0),           (+1, +0),
                     (-1, +1), (+0, +1), (+1, +1))

    tmp_board = copy.copy(board)
    reverse_flag = False

    # 既に石が置かれている場合無効
    if tmp_board[row][column] != 0:
        print("既に石が置かれています。")
        return board, False

    # 石を置く
    if color == "RED":
        stone = 1
    elif color == "BLUE":
        stone = 2

    tmp_board[row][column] = stone

    # 反転
    for dx, dy in DIRECTIONS_XY:
        x = row
        y = column
        target = 0
        for n in range(BOARD_SIZE):
            x += dx
            y += dy
            if not (0 <= x < BOARD_SIZE and 0 <= y < BOARD_SIZE): # 盤外に出る場合
                target = 0
                break
            if tmp_board[x][y] == 0: # 石が置いていない場合
                target = 0
                break
            if tmp_board[x][y] == stone: # 新しく置いた石と同じ色の石が置いている場合
                target =  n
                break

        for i in range(1, target + 1):
            reverse_flag = True
            tmp_board[row + i * dx][column + i * dy] = stone

    # 一つも反転していない場合無効
    if reverse_flag == False:
        print("石の座標、もしくは色が適切ではありません。")
        return board, False

    return tmp_board, True
```

これらを実行することで、「"声"で打つオセロ」が動きます！

### オプション：効果音、BGMの付与
ここまでで、オセロとしての機能は完成しましたが、このままだと少し寂しいので効果音やBGMをつけます。

今回はmp3ファイルを再生することのできるpygameを用いました。デフォルトでインストールされているライブラリなので、簡単に使用することができます。

ソースコードとしては、以下のように実装します。(参考：「[Raspberry Piで音楽(wav/mp3)ファイルを再生する方法 python編](https://qiita.com/Nyanpy/items/cb4ea8dc4dc01fe56918)」)

```
#!/usr/bin/env python
#-*- cording: utf-8 -*-

import pygame.mixer
import time

# mixerモジュールの初期化
pygame.mixer.init()
# 音楽ファイルの読み込み
pygame.mixer.music.load("ファイル名.mp3")
# 音楽再生、および再生回数の設定(-1はループ再生)
pygame.mixer.music.play(-1)

time.sleep(60)
# 再生の終了
pygame.mixer.music.stop()
```

これにより、盤面の更新時やゲームの終了時に適切な効果音やBGMを再生することで、よりゲームっぽく仕上げることができます。


# おわりに
長い記事になってしまいましたが、見ていただきありがとうございます。

筆者はGWから電子工作を始め、三週間ぐらいで今回の「"声"で打つオセロ」を作ることができました。(デモ動画の収録関連で記事は遅れてしまいましたが...)
慣れが早い人なら1、２週間で作ることもできると思うので、ぜひ作って遊んでみてください。

今後の課題としては、オセロ以外のゲームへの応用、回路の簡素化、音声認識精度の向上などが挙げられます。
将来的には、一つのボードでたくさんのゲームができるようになるかもしれませんね。

また、タイトルにもある通り、今後は検索機能や自然応答などの機能を追加することで、よりリッチなデバイスの完成を目指します。
期間は開いてしまうと思いますが、よろしくお願いします。

記事に関してお気付きの点、またご感想がありましたら、遠慮なくコメントや[Twitter](https://twitter.com/hiyoko9t)の方でお声がけください。

# 参考文献
[1] "Simple Guide to the Raspberry Pi GPIO Header and Pins - Raspberry Pi Spy", 
 2012, Available at https://www.raspberrypi-spy.co.uk/2012/06/simple-guide-to-the-rpi-gpio-header-and-pins/#prettyPhoto.
[2] "TC74HC595AP_07 datasheet(1/11 Pages) TOSHIBA | CMOS Digital Integrated Circuit Silicon Monolithic 8-Bit Shift Register/Latch (3-state)", 2007, Available at http://html.alldatasheet.com/html-pdf/214419/TOSHIBA/TC74HC595AP_07/301/1/TC74HC595AP_07.html.