pythonで作るDiscordでHit&Blowゲームbot

##開発環境
* Windows10
* discord.py 1.2.3
* Python3.7.4

#作成したHit&Blowゲームのルール
1. 4桁の重複していない数字をCPUが隠し持ち、桁一致を表すHitと数値のみ一致を表すBlowの数をヒントに設定された数字を当てる
2. 対戦中にゲームを始めることはできない。CPUといえど2窓行為はしないのだ
3. ニックネーム機能による~~なりすまし~~代打が可能

参考：[@Smoothie1023](https://qiita.com/Smoothie1023/items/2a456f8880ec1ce55cd6)さん

#コード
<details><summary>コード全文</summary><div>

```python:main.py
import discord 
import asyncio
import random
import re

client = discord.Client()
start = time = name = correct = 0

# 起動時に動作する処理。今回のbotには無関係
@client.event
async def on_ready():
	CHANNEL_ID = # 起動確認に使っても良いチャンネルのID
	channel = client.get_channel(CHANNEL_ID)
	await channel.send('ハローワールド！')
	print('Logged in as')
	print(client.user.name)
	print(client.user.id)
	print('------')

# なるべくニックネームで呼ぶようにする処理
def callnick(message):
	nick = message.author.nick
	if nick is None:
		return message.author.name
	else:
		return nick

# 今回のbotの本体
@client.event
async def on_message(message):
	if message.author != client.user:
		global start,name,time,correct,answer,ans,hit,blow,t
		if message.content == "/hb" and start == 0: # ゲーム開始
			start = time = 1
			name = callnick(message)
			await message.channel.send("Let's enjoy Hit & Blow!")
			print(f"{name}がゲームを開始しました。")
			correct = random.sample(list(range(10)),4) # 正解決め
			print(f"correct")
			await message.channel.send(f"{name}さん4桁の数字を入力してください。")

		if re.match(r'[0-9]{4}',message.content) and name == callnick(message):
			time += 1
			answer = message.content[:]
			ans = list(map(int, answer))
			hit = blow = 0
			for num in range(len(ans)):
				t = "x"
				try:
					t = correct.index(ans[num])
				except ValueError:
					pass
				if t != "x":
					if t == num:
						hit += 1
					else:
						blow += 1
			print(f"{ans} {hit} Hit / {blow} Blow {correct}")
			await message.channel.send(f"{hit} Hit / {blow} Blow")
			if hit == 4: # クリア時の処理
				start = 2

		if start == 2:
			await message.channel.send(f"{correct} : Correct! 挑戦回数：{time-1}回")
			print("ゲーム終了")
			start = time = name = correct = 0

		if message.content == "/reset" and start > 0 and callnick(message) == name: # エラーした場合のリセット
				await message.channel.send(f"ゲームをリセットします。正解：{correct}")
				print("ゲームがリセットされました。")
				start,time,name= 0,0,""

client.run("TOKEN")

```
</div></details>


#解説

###ニックネーム
```python
def callnick(message):
	nick = message.author.nick
	if nick is None:
		return message.author.name
	else:
		return nick
```
ニックネームを持たない発言者にまで```message.author.nick```のまま返事をしてしまうと、無い物は文字列として取得できずNoneになり返答文が崩壊するため返事を行わないエラーを起こす。
そのため、ニックネームが無い発言者の場合は代わりに本名を返す処理を行った。
面倒なものは関数(```def```)化する。


###正解決め
```python
correct = random.sample(list(range(10)),4) # 正解決め
```
重複なしの4桁の数字にしなければゲームが成立しないため、```sample```による非復元抽出でリストから取り出す。
0-9の10文字からなるリストの作成に```range```を使用。
桁数の指定4を変数にすれば、ゲーム開始時に任意の桁数にする拡張を行えそう。

###解答受付
正規表現により、4桁の数字であれば0000から9999まで解答として認識する。
同じ数字の重複を弾く処理はカット。ゲームプレイヤーの良心に委ねる。
誤記例```if message.content == "[0-9]{4}":```

###~~なりすまし~~代打可能
```python:
if re.match(r'[0-9]{4}',message.content) and name == callnick(message):
```
and条件で追加した```name == callnick(message)```での判定により、ゲームを開始した者と同じニックネームの者の解答を拾う。

#その他忘備録
```python:トライ＆エラー中は鬱陶しいので5行目だけでもコメントアウトした方が良さそう
@client.event
async def on_ready():
	CHANNEL_ID = # 起動確認に使っても良いチャンネルのID
	channel = client.get_channel(CHANNEL_ID)
	await channel.send('ハローワールド！')
```

```python:インデントの数を間違えると大変な事になる部分
if t != "x":
	if t == num:
		hit += 1
	else:
		blow += 1
```
本体である```if message.content == "/hb" and start == 0: # ゲーム開始```以下も```@client.event```の外に出して処理すれば、他の機能を持たせたbotにHit&Blowの機能を追加することも可能なはず。
実証が済んだら更新する。