【所要時間1h】俺の彼女作ったった【クリぼっち回避】

こんにちは！
エンターテインメント事業本部にて、普段はサーバ/インフラ辺りの作業をしている @exit_00 と申します。

この度、**エイチーム引越し侍**の**Advent Calendar 2018**にお邪魔させていただきました。
ということで、この記事は [エイチームブライズ/エイチームコネクト/エイチーム引越し侍 Advent Calendar 2018](https://qiita.com/advent-calendar/2018/hikkoshi) 3日目の記事です。

# あらまし
- クリスマスの祝いとして日々作り上げていくアドベントカレンダー。
- だが私は今年もクリスマスを1人で過ごす予定……。
- 皆と明るく楽しく元気よくクリスマスを祝うには、***自分で恋人を造るしかない！***

# この記事で得られること
- ターミナル上に理想の恋人を造る入口に立つ
- Python初心者が人工無能を実装するキッカケ

# 彼女の要件
- Python使用
- 人工無脳
- ターミナル上で動作
- 単語記憶
- 恋愛感情のパラメータを保持

# Let's 彼女を実装！

## 環境
- Mac 10.14.1
- Python 3.7.1
- iTerm2

## 手順
1. Pythonインストール
2. 入力に対して予め定められた返答をランダムに返すクラスを実装
3. 入力されたテキストを記憶
4. 恋愛感情パラメータ保持

## 手順詳細

### Pythonインストール
- 各種記事参照
- Homebrewでインストールしました

### 入力に対して予め定められた返答をランダムに返すクラスを実装

#### 実装

```python:main.py
  1 import random
  2
  3 class Main():
  4     _your_name = "";
  5     _word_list = ["お腹空いた", "ピザ頼んで", "それ何？"];
  6
  7     def __init__(self):
  8         global _your_name;
  9
 10         print("girlfriend: あなたの名前、教えて？");
 11         _your_name = input("your_name: ");
 12         print("おはよ、" + _your_name);
 13         print("帰るときは『バイバイ』って言って。");
 14
 15     def Response(self):
 16         while True:
 17             your_phrase = input("> ");
 18             if your_phrase == "バイバイ":
 19                 print(_your_name + "、またね。");
 20                 break;
 21             else:
 22                 print(random.choice(_word_list));
 23
 24 main = Main();
 25 main.Response();
```
#### 実行

```bash:
$ python main.py
girlfriend: あなたの名前、教えて？
your_name: exit_00
おはよ、exit_00
帰るときは『バイバイ』って言って。
> ok
ピザ頼んで
> わかった
お腹空いた
> ちょっとまって
それ何？
> 出前表
お腹空いた
> まってよ
お腹空いた
> バイバイ
exit_00、またね。
```

ちぐはぐですが、延々としゃべり続けてくれる彼女が誕生しました。

### 入力されたテキストを記憶

- 1/3の確率で話しかけた内容について意味を問う場面を追加しました
- 意味との結びつきは一旦置いておいて、少しずつ語彙が増えるようになります
- ファイルに保存するため、次回起動時に保持されます

#### 実装

```python:main.py
  1 import random
  2
  3 class Main():
  4     PHRASE_PASS = 'phrase.txt';
  5     _your_name = "";
  6     _girlfriend_name = "girlfriend";
  7     _word_list = [];
  8
  9     def __init__(self):
 10         global _word_list;
 11         with open(self.PHRASE_PASS) as f:
 12             _word_list = [s.strip() for s in f.readlines()];
 13
 14     def SetName(self):
 15         global _your_name;
 16         print(self._girlfriend_name + ": あなたの名前、教えて？");
 17         _your_name = input("your_name: ");
 18         print(self._girlfriend_name + ": おはよ、" + _your_name);
 19         print(self._girlfriend_name + ": 帰るときは『バイバイ』って言って。");
 20
 21     def Response(self):
 22         while True:
 23             your_phrase = input("> ");
 24             is_what_mean = random.choice([True, False, False]);
 25             if your_phrase == "バイバイ":
 26                 print(self._girlfriend_name + ": " + _your_name + "、またね。");
 27                 break;
 28             elif is_what_mean:
 29                 print(self._girlfriend_name + ": " + your_phrase + "ってなに？");
 30                 your_phrase_sub = input("> ");
 31                 self.LearnPhrase(your_phrase, your_phrase_sub);
 32                 print(self._girlfriend_name + ": " + your_phrase_sub + "ってことなの？ふーん。");
 33             else:
 34                 print(self._girlfriend_name + ": " + random.choice(_word_list));
 35
 36     def LearnPhrase(self, your_phrase, your_phrase_sub):
 37         global _word_list;
 38         _word_list.append(your_phrase);
 39         f = open(self.PHRASE_PASS, "a");
 40         f.writelines(your_phrase);
 41         f.close();
 42
 43 main = Main();
 44 main.SetName();
 45 main.Response();
```

#### 実行

```bash:
$ python main.py
girlfriend: あなたの名前、教えて？
your_name: exit_00
girlfriend: おはよ、exit_00
girlfriend: 帰るときは『バイバイ』って言って。
> わかった
girlfriend: お腹空いた
> ピザ食べる？
girlfriend: ピザ頼んで
> わかった
girlfriend: わかったってなに？
> 了解
girlfriend: 了解ってことなの？ふーん。
> そうだよ
girlfriend: わかった
> バイバイ
girlfriend: exit_00、またね。

```

彼女が少しだけ賢くなりました！
次は恋愛感情への影響を実装します。

### 恋愛感情パラメータ保持

- 喋る度に高感度が変化するように実装します
- NGワード、GOODワードにより好感度の上下が変化します
- 高感度により、喋る内容が変わります

#### 実装

```python:main.py
  1 import random
  2 import json
  3
  4 class Main():
  5     PHRASE_PASS = 'phrase.json';
  6     _girlfriend_name = "girlfriend";
  7     _emotional_mode = "GOOD";
  8     _love_score = 0;
  9     _your_name = "";
 10     _word_list = [];
 11
 12     def __init__(self):
 13         global _word_list;
 14         try:
 15             # ローカルJSONファイルの読み込み
 16             with open(self.PHRASE_PASS, 'r') as f:
 17                 _word_list = json.load(f);
 18         except json.JSONDecodeError as e:
 19             print('JSONDecodeError: ', e);
 20
 21     def SetName(self):
 22         global _your_name;
 23         print(self._girlfriend_name + ": あなたの名前、教えて？");
 24         _your_name = input("your_name: ");
 25         print(self._girlfriend_name + ": おはよ、" + _your_name);
 26         print(self._girlfriend_name + ": 帰るときは『バイバイ』って言って。");
 27
 28     def Response(self):
 29         global _emotional_mode;
 30         while True:
 31             your_phrase = input("> ");
 32             is_what_mean = random.choice([True, False, False]);
 33             if your_phrase == "バイバイ":
 34                 print(self._girlfriend_name + ": " + _your_name + "、またね。");
 35                 break;
 36             elif is_what_mean:
 37                 print(self._girlfriend_name + ": " + your_phrase + "ってなに？");
 38                 your_phrase_sub = input("> ");
 39                 self.LearnPhrase(your_phrase, your_phrase_sub);
 40                 print(self._girlfriend_name + ": " + your_phrase_sub + "ってことなの？ふーん。");
 41             else:
 42                 print(self._girlfriend_name + ": " + random.choice(_word_list[self._emotional_mode]));
 43             self.EmotionMoved(your_phrase);
 44
 45     def LearnPhrase(self, your_phrase, your_phrase_sub):
 46         global _emotional_mode;
 47         global _word_list;
 48         _word_list[self._emotional_mode].append(your_phrase);
 49         with open(self.PHRASE_PASS, 'w') as outfile:
 50             json.dump(_word_list, outfile);
 51
 52     def EmotionMoved(self, your_phrase):
 53         global _love_score;
 54         global _emotional_mode;
 55         if your_phrase in _word_list["GOOD"]:
 56             self._love_score += 1.0;
 57         elif your_phrase in _word_list["BAD"]:
 58             self._love_score -= 1.0;
 59         else:
 60             self._love_score += 0.5;
 61         print("現在の高感度: " + str(self._love_score));
 62         if self._love_score > 0:
 63             _emotional_mode = "GOOD";
 64         else:
 65             _emotional_mode = "BAD";
 66
 67
 68 main = Main();
 69 main.SetName();
 70 main.Response();
```

#### 実行

```bash:
$ python main.py
girlfriend: あなたの名前、教えて？
your_name: exit_00
girlfriend: おはよ、exit_00
girlfriend: 帰るときは『バイバイ』って言って。
> わかった
girlfriend: わかったってなに？
> 了解
girlfriend: 了解ってことなの？ふーん。
現在の高感度: 1.0
> そうだよ
girlfriend: かわいい？
現在の高感度: 1.5
> かわいい
girlfriend: かわいい？
現在の高感度: 2.0
> かわいい！
girlfriend: かわいい？
現在の高感度: 2.5
> かわいい！！！！
girlfriend: かわいい！！！！ってなに？
> 聞かれたから
girlfriend: 聞かれたからってことなの？ふーん。
現在の高感度: 3.5
> バイバイ
girlfriend: exit_00、またね。
```

う〜ん、大分彼女に近づいたのではないでしょうか！
しかし、お察しの通り、まだ単語の意味に対して覚えられておらず、彼女の気分で言葉の良し悪しを決めてしまっています。

# 今後チューニングしたい箇所
- 文章の学習精度向上(句で切り分ける、部分一致等)
- 文章の意味付け強化(言葉の良し悪し)
- 人工知能らしさを追求(既存サービスとの連携？Web上から言葉を拾う？)
- 見た目の部分改善（彼女の顔グラがほしい！）
- Twitterや各種チャットにAPI連携することで恋人彼女をいつでも携帯できるようにしたい
- いつしか、クリスマスにレストランを2人(1人と1つ)で予約できるような存在に…………。
- （クリスマスデートまでこぎつけた方は是非ご連絡ください。）

# まとめ
- クリスマスに延々と話ができるかけがえのない存在ができました!
- Python初心者でしたが、人工無能の身近さを感じ取れたように思います。
- 宜しければ、皆さんも是非会話内容や実装のチューニングを施してみてください。プログラミングを叩いている間は1人の虚しさが紛れます。

---

# お知らせ
エイチームグループでは一緒に活躍してくれる優秀な人材を募集中です。
興味のある方はぜひとも[エイチームグループ採用ページ](https://www.a-tm.co.jp/recruit/)（[Webエンジニア詳細ページ](https://www.a-tm.co.jp/recruit/requirements/career/lifestylesupport-webengineer/)）よりお問い合わせ下さい。

# 明日
明日は @aoi_coro さんの記事です！楽しみですね！

# 参考
- [恋するプログラム―Rubyでつくる人工無脳 著：秋山 智俊](http://amzn.asia/d/fcg5tIL)