年始に作成した『シェリルの誕生日』の処理。
https://qiita.com/hnkyi/items/dbd937a3e11ef7682804
ふと「逆も行けるんじゃね?」と思って、入力された誕生日から問題となる候補日を作成してみました。
create_question_in_sheryl.py
"""
『シェリルの誕生日』の問題作り
- 「m/d形式」で誕生日を受け取り、問題を作成
- 誕生日を基点に「前後4ヶ月、左右6日」を取得
- 「ユニークな日を持つ月」を選定
- ユニークな日が誕生日の場合、バーナードが初見で正解できてしまう
- 「重複する日を持つ月」を選定
- 重複する日が誕生日の場合、アルバートの言葉を聞いてもバーナードは特定できない
"""
import sys
import random
import re
from datetime import datetime, timedelta
from dateutil.relativedelta import relativedelta
# 引数チェック
if len(sys.argv) < 2 or not re.search(r"^[0-9]{1,2}/[0-9]{1,2}$", sys.argv[1]):
print("usage: python create_question_in_sheryl.py mm/dd")
exit()
birthday = datetime.strptime(sys.argv[1], "%m/%d")
birth_month = birthday.strftime("%m")
birth_day = birthday.strftime("%d")
# 前後4ヶ月、左右6日のテーブル作成
months = []
start_month = random.choice(list(range(-3, 1)))
for add_val in range(start_month, start_month + 4):
month = birthday + relativedelta(months=add_val)
months.append(month.strftime("%m"))
days = []
start_day = random.choice(list(range(-5, 1)))
for add_val in range(start_day, start_day + 6):
day = birthday + timedelta(days=add_val)
days.append(day.strftime("%d"))
# ユニークな日を選定
unique_days = []
while len(unique_days) < 2:
choice = random.choice(days)
if choice != birth_day and choice not in unique_days:
unique_days.append(choice)
# ユニークな日を持つ月を選定
unique_months = []
while len(unique_months) < 2:
choice = random.choice(months)
if choice != birth_month and choice not in unique_months:
unique_months.append(choice)
# 重複する日を持つ月を選定
duplicate_month = [month for month in months if month != birth_month and month not in unique_months][0]
# 重複させる日を選定
duplicate_day = ""
while True:
choice = random.choice(days)
if choice != birth_day and choice not in unique_days:
duplicate_day = choice
break
# XXX 20180627 modified ダミー日を使うよう修正
# # 重複させない日を選定
# unduplicate_days = []
# while len(unduplicate_days) < 2:
# choice = random.choice(days)
# if choice != duplicate_day and choice not in unique_days and choice not in unduplicate_days:
# unduplicate_days.append(choice)
#
# # 重複する日を持つ月を選定
# duplicate_month = [month for month in months if month != birth_month and month not in unique_months][0]
#
# # ユニークでない日を選定
# ununique_days = [day for day in days if day not in unique_days and (day == duplicate_day or day in unduplicate_days)]
dummy_days = {birth_day: 1}
for dummy_day in [day for day in days if day not in unique_days and day != birth_day and day != duplicate_day]:
dummy_days[dummy_day] = 2
# 候補日を作成
candidates = []
candidates.append("{}/{}".format(birth_month, birth_day))
candidates.append("{}/{}".format(birth_month, duplicate_day))
candidates.append("{}/{}".format(duplicate_month, duplicate_day))
# XXX 20180627 modified ダミー日を使うよう修正
# for d in unduplicate_days:
# candidates.append("{}/{}".format(duplicate_month, d))
for dummy_day in random.sample([day for day in dummy_days.keys() if dummy_days[day] != 0 and day != birth_day], k=2):
candidates.append("{}/{}".format(duplicate_month, dummy_day))
dummy_days[dummy_day] -= 1
for m, d in zip(unique_months, unique_days):
candidates.append("{}/{}".format(m, d))
for idx in range(len(unique_months)):
unique_month = unique_months[idx]
# XXX 20180627 modified ダミー日を使うよう修正
# if 0 == idx:
# candidates.append("{}/{}".format(unique_month, random.choice(ununique_days)))
# else:
# choices = []
# while True:
# choices = random.choices(ununique_days, k=2)
# if choices[0] != choices[1]:
# break
# candidates.append("{}/{}".format(unique_month, choices[0]))
# candidates.append("{}/{}".format(unique_month, choices[1]))
if 0 == idx:
dummy_day = random.choice([day for day in dummy_days.keys() if dummy_days[day] != 0])
candidates.append("{}/{}".format(unique_month, dummy_day))
dummy_days[dummy_day] -= 1
else:
for dummy_day in random.sample([day for day in dummy_days.keys() if dummy_days[day] != 0], k=2):
candidates.append("{}/{}".format(unique_month, dummy_day))
dummy_days[dummy_day] -= 1
# 結果出力
print(sorted(candidates))
3回ほど実行してみたけど、毎度違う値が生成されてるのが分かる。
それぞれ解いてみて正解まで行き着くから、とりあえず試験もOK。
$ python create_question_in_sheryl.py 9/17
['08/17', '08/21', '09/17', '09/18', '10/17', '10/18', '10/19', '11/17', '11/19', '11/20']
$ python create_question_in_sheryl.py 9/17
['07/13', '07/14', '07/17', '08/14', '08/18', '09/14', '09/17', '10/14', '10/15', '10/17']
$ python create_question_in_sheryl.py 9/17
['08/18', '08/19', '08/22', '09/17', '09/22', '10/18', '10/20', '10/22', '11/20', '11/21']
試験も兼ねて、別の誕生日でも試してみた。
とりあえず最近世話になってるとある4人の誕生日でも試してみた。
それぞれ解いて正解まで辿り着く事は確認した。
$ python create_question_in_sheryl.py 10/30
['08/01', '08/30', '08/31', '09/02', '09/30', '10/29', '10/30', '11/01', '11/29', '11/30']
$ python create_question_in_sheryl.py 4/14
['03/11', '03/13', '03/14', '04/13', '04/14', '05/11', '05/12', '06/10', '06/11', '06/14']
$ python create_question_in_sheryl.py 1/12
['01/11', '01/12', '02/10', '02/12', '02/15', '11/12', '11/14', '12/10', '12/11', '12/12']
$ python create_question_in_sheryl.py 10/5
['08/05', '08/07', '08/09', '09/04', '09/06', '10/05', '10/09', '11/04', '11/05', '11/09']
これ使って候補日を割り出して、酒の席とかで使ったら盛り上がるね🍺🍺
以上。
追記:20190627
@shiracamus さんにご指摘いただきまして、ソースコードを修正しました。
コメントアウトして新しいソースコードを書き足す、古臭い修正方法してますが、ご確認下さい、、、
なお、「内包表記」と「ジェネレーター」も採用すると、全取っ替えになるので、問題箇所のみの修正としております。
- 指摘内容
- 元のソースコードで出力されたリストを、シェリルの誕生日の方に食わせると、正解しない
- 原因
- 問題に関わらないダミー的な立ち位置の
day
の設定誤り - シェリルの誕生日には原因なし
- 問題に関わらないダミー的な立ち位置の
- 改修内容
- ダミー日として「
ユニークな日
と重複する日
以外」を設定 - 誕生日(
birth_day
)となる日は「重複する日
」には用いない
- ダミー日として「
- 試験結果
- 私の誕生日である「9/17」で3回試して、3回とも正解
- すでに上げてる「4人の誕生日」でそれぞれ1回ずつ試して、それぞれ正解
以上です。