イントロ
先日Quizknockさんの「出題のない部屋」を眺めていたら,どうやら正誤判定をプログラミングで判定していたようだ.今回はその判定プログラムを,pythonとQiitaの練習がてら作成してみたいと思う.
この記事はネタバレ満載なので,記事を見る前にぜひ以下の動画を見ていただきたい.
- 本編
- 解説編
問題
動画本編で出されていた問題は以下の通り.
以下に示すような3×3のボックスと,9つの文字がある.
この文字たちをボックスの各マスに当てはめた時,縦(もしくは横,斜め)一列の文字を使って漢字を作れることがある.
例えば,一,木,イ が一列にそろっていれば 体 が作れる.
すべてのマスに文字を埋めたうえで,縦横斜め全ての列で同時に漢字を成立させると正解となる.
正解例
後で動作確認を行うために,ひとまず正解例を明らかにしておこう.
正解例
心 目 木
音 一 口
イ 八 十
縦一列は左から,億,具,枯
横一列は上から,想,暗,休
斜めは,体(/),志(\)
という風になって完成である.
もちろん,この組み合わせを回転させても,縦と横の列を反転させても正解.
設計と実装
では,ルールがわかったところで,実際にこのクイズの正誤判定を設計してみよう.
入力:
各文字の並び
処理:
文字の並びに対して成立漢字判定,および正誤判定.
出力:
成立している漢字を表示,正解不正解の表示.
(まあすでに動画を見ていると思うのでわかると思うが)一応補足する.
実際は箱に文字が書いてあり,その箱を棚に並べ,早押しボタンを押すことで回答とする.早押しボタンが押された段階で(おそらく裏側では)文字の並びを入力し,プログラムを実行する.すると,テレビ画面に,成立している漢字を連想するイラストを順に表示,最後に音で「正解」もしくは「不正解」を示していた.
今回は成立漢字判定も正誤判定も文字で行うこととする.
では実装してみよう.
import hakonoheyafunk
print('1:一 2:口 3:目 4:音 5:イ 6:心 7:木 8:八 9:十 →')
a,b,c,d,e,f,g,h,i = input().split()
list = [[int(a),int(b),int(c)], [int(d),int(e),int(f)], [int(g),int(h),int(i)]]
hakonoheyafunk.list_all_explore(list)
名前が「ハコノヘヤ」になっているのはご容赦いただきたい.(出題のない部屋はすでにシリーズ化されていて,「シュツダイノナイヘヤ」という名前では区別できなくなるんだもん)
ここでやりたいのは,文字の並びを入力してもらい,それをlistに代入する.そして,後述するlist_all_explore関数でlistに対し正誤判定を行うということだ.このプログラムを実行すると,以下が表示される.
PS C:\Python\hakonoheya> python hakonoheya.py
1:一 2:口 3:目 4:音 5:イ 6:心 7:木 8:八 9:十 →
入力する際は,各文字に対応している番号を1文字ずつスペース区切りで入力していくのだが,実際の並び方とターミナルへの入力の仕方,およびlist変数の対応は以下のとおりである.
PS C:\Python\hakonoheya> python hakonoheya.py
1:一 2:口 3:目 4:音 5:イ 6:心 7:木 8:八 9:十 → A B C D E F G H I
具体例を示すと,
PS C:\Python\hakonoheya> python hakonoheya.py
1:一 2:口 3:目 4:音 5:イ 6:心 7:木 8:八 9:十 → 9 8 7 6 5 4 3 2 1
と入力したとき,以下の状態と同じであることを示す.
続いて,list変数をもとに正誤判定するlist_all_explore関数のファイルを見てみよう.
import time
def list_all_explore(list):
number = 8;
#縦の調査
for i in range(0,3):
ex_list = sorted([list[0][i],list[1][i],list[2][i]])
number = list_result(ex_list,number)
#横斜めも同様,
judge(number)
def list_result(list,number): #listに対して漢字が成立しているか判定
#後述
def judge(i): #正誤判定
#後述
list_all_exploreでは,listの縦,横,斜めの各列において,対象の要素をex_listに取り出してくることで,ex_listの組み合わせで漢字が成立しているかどうかを調べる.では実際にそれを実装しようではないか.
ん?
ここで一つ悩みが生じる.
ex_listの数字の並びの組み合わせ6通りあるやん…
そうなのだ,例えば,1:一,5:イ,7:木を用いて「体」という漢字を作るとき,ex_listの中身は,
1 5 7,1 7 5,5 1 7 ,5 7 1,7 1 5,7 5 1
という6通りの並び方が存在しうる.当然,いずれの組み合わせだったとしても「漢字が成立している」と判定しなければならない.ほかの漢字も然り.これでは,条件分岐を書くのが大変めんどくさくなる.さてどうしたものか.論理和で全通り書いてしまおうか.うーん.
あっ,
拾ってきた要素を小さい順に並べてみればよくね?
そう,こうすることで,
1 5 7,1 7 5,5 1 7 ,5 7 1,7 1 5,7 5 1
のいずれが来たとしても,「1 5 7」と一意に表すことができるではないか.
6通りだった並びが1通りに絞れるではないか!
ということで,私は高揚感にあふれながら,sorted関数を用いて3つの数字を小さい順に並べ替えた.それをex_listとし,漢字の成立判定をするlist_resultに渡している.
ではそのlist_resultを見てみよう.
def list_result(list,number):
if(list == [4,5,6]):
print('億')
time.sleep(2)
number = number - 1;
return number;
#以下,「具」「枯」「想」「暗」「休」「体」「志」について同様.
else:
return number;
先ほどのex_listに対し,漢字が成立する組み合わせであれば,その漢字を出力する.
time.sleepは,動画本編と同じように,一気に表示させるのではなく,一つずつ(絵を)表示している状態を再現したかったため.
また,numberは最後の正誤判定に用いる.
numberはlist_all_exploreを見てもらうとわかるように最初8に初期化されている.漢字が成立するごとにnumberは1ずつ減少する.(成立していなければnumberをそのまま返す.これを忘れたがために30分無駄にしたのは内緒の話).最終的に漢字がすべての列で成立すればnumberは0,1つでも成立していなければ1以上の数字となる.すべての列で漢字判定が終わった後,numberを以下のjudgeに渡す.
def judge(number):
if (number==0) : print('正解')
else : print('不正解')
とくに説明は不要であろう.numberが0なら「正解」,そうでなければ「不正解」を表示するだけだ.
実行
ということで,実際に実行してみようと思う.
- 漢字が1つも成立しておらず不正解の場合
PS C:\Python\hakonoheya> python hakonoheya.py
1:一 2:口 3:目 4:音 5:イ 6:心 7:木 8:八 9:十 →
1 5 9 3 4 7 2 8 6
不正解
PS C:\Python\hakonoheya> python hakonoheya.py
1:一 2:口 3:目 4:音 5:イ 6:心 7:木 8:八 9:十 →
1 2 4 3 6 7 5 8 9
暗
想
休
志
億
不正解
正しく動作できている.狙ってないが,たまたま右側に偏った.
- 正解の場合
PS C:\Python\hakonoheya> python hakonoheya.py
1:一 2:口 3:目 4:音 5:イ 6:心 7:木 8:八 9:十 →
6 3 7 4 1 2 5 8 9
億
具
枯
想
暗
休
志
体
正解
正しく動作できている.
という風に実行することができた.
ただし,不正な入力に対する対処などは行っていない.
終わりに
Quizknock面白いんでぜひともチャンネル登録して観ていただきたい.
(ちなみに案件でも回し者でもございません.)
ソースリスト
最後に,省略してしまったhakonoheyafunk.pyの全文を上げておく.
import time
def list_all_explore(list):
number = 8;
#縦の調査
for i in range(0,3):
ex_list = sorted([list[0][i],list[1][i],list[2][i]])
number = list_result(ex_list,number)
#横の調査
for i in range(0,3):
ex_list = sorted([list[i][0],list[i][1],list[i][2]])
number = list_result(ex_list,number)
#左斜めの調査
ex_list = sorted([list[0][0],list[1][1],list[2][2]])
number = list_result(ex_list,number)
#右斜めの調査
ex_list = sorted([list[2][0],list[1][1],list[0][2]])
number = list_result(ex_list,number)
#正誤判定
judge(number)
def list_result(list,number):
if(list == [4,5,6]):
print('億')
time.sleep(2)
number = number - 1;
return number;
if(list == [3,6,7]):
print('想')
time.sleep(2)
number = number - 1;
return number;
if(list == [1,5,7]):
print('体')
time.sleep(2)
number = number - 1;
return number;
if(list == [1,2,4]):
print('暗')
time.sleep(2)
number = number - 1;
return number;
if(list == [5,8,9]):
print('休')
time.sleep(2)
number = number - 1;
return number;
if(list == [1,3,8]):
print('具')
time.sleep(2)
number = number - 1;
return number;
if(list == [1,6,9]):
print('志')
time.sleep(2)
number = number - 1;
return number;
if(list == [2,7,9]):
print('枯')
time.sleep(2)
number = number - 1;
return number;
else:
return number;
def judge(i):
if (i==0) : print('正解')
else : print('不正解')