メガテンのコードブレイカーご存知でしょうか?
今回はその、コードブレイカーで
プログラミングを考えていきたいと思います。
解説動画はこちら
コードブレーカーは
ルール
例:
今回はその、コードブレイカーで
プログラミングを考えていきたいと思います。
解説動画はこちら
コードブレーカーは
RPG「女神転生」シリーズのミニゲームで
正式名称は「Hit&Blow」という数当てゲームです。
ルール
各桁の数字(0-9までの数字)が全て違う
3桁の数字を当てるゲーム
3桁の数字を当てるゲーム
数字を入力して、桁ごとに下記を判定
「正解と場所と数字が一致する(ヒット)」
「場所が違うがその数字が含まれる(ブロー)」
1回答ごとにHitとBlowの数を言い
全て当たる(3H)まで行う。
全て当たる(3H)まで行う。
例:
正解の数 957
1回目「915」→ 「1H1B」
2回目「234」→ 「0H0B」
3回目「795」→ 「0H3B」
4回目「957」→ 「3H0B」→ 当たり
def check_call(call , solve):
・・・
・・
・
初級編解答:
・・・
・・
・
中級編の解答:
コードはこんな感じになりました。
入力に数字を打ち込んであげると
判定結果が返ってきます。
ぐう、ダメでしたね。
当てられないとは
0H0Bをうまく使うのが
当てるコツですね。
・・・
・・
・
上級編の解答:
まず、チェック関数を使って予想数字から
せっかくなんで
何回で当てれるものなのか
シミュレーションしてみましょう。
先ほどのプログラムを改良して
解いた時の回数を出すようにして
1000回試行して何回で解けるのか
集計します。
だいたい5回以内には解けそうですね。
多くても7回ほどあれば解答に
たどり着けそうです。
10回以上かかると
やばそうですね。
とほほほ
コードブレイカーは
ヌメロンと言う名前で
テレビ番組用のゲームとして
使われていたこともあったようです。
遊びたい方はみてみてください。
今回はこれまでです
それでは。
初級編:
コードブレイカーのチェック関数を作る
コードブレイカーのチェック関数を作る
予想の数字と正解の数字を入力して
HitとBlowの数を返す関数check_callを作ってみよう
def check_call(call , solve):
処理
return (hit , blow)
引数:call=予想数字 , solve=正解数字
戻り値:(hit , blow)のタプル型(両方数値)
例:
check_call('564','987') →(0,0)
check_call('564','687') →(0,1)
check_call('564','546') →(1,2)
check_call('564','564') →(3,0)
・・・
・・
・
初級編解答:
ポイント:
数字が他の桁に有るかどうかは
in で簡単に判定できる
in で簡単に判定できる
'検索したい文字' in '文字列'
答え用の変数に0を入れておき
hit , blowがあれば足す。
コードはこんな感じになりました。
hit , blowがあれば足す。
コードはこんな感じになりました。
# 予想と正解をチェックする関数
def check_call(call , solve):
hit , blow = 0,0
for i,n in enumerate(call):
if solve[i]==n:
hit+=1
elif n in solve:
blow +=1
return hit , blow
print(check_call('564','987'))
print(check_call('564','687'))
print(check_call('564','546'))
print(check_call('564','564'))
(0, 0)
(0, 1)
(1, 2)
(3, 0)
中級編:
コードブレイカーのゲームを作ってみよう
3桁の数値をランダムで生成して正解数として
正解数を当てに行くミニゲームを作ってみよう。
inputで数字入力を受け付け
数字とhit , blowを出力し続ける。
数字とhit , blowを出力し続ける。
3Hになるか、10回当てられなければゲーム終了
ヒント:
まず正解の数字を1つ作る
チェックする部分は初級編の関数を利用する
正解の数字と入力数字が合うまで
各回の予想とhit&blowの出力する
各回の予想とhit&blowの出力する
・・・
・・
・
中級編の解答:
まず最初に正解の数字を作る
(10個の数字を使った3桁でダブりのない数字)
(10個の数字を使った3桁でダブりのない数字)
10個の数字を用いた順列を作るには
itertools.permutations(seq, 3)
itertools.permutations(seq, 3)
順列の中から1つ選ぶには
random.choice(リストなど)
random.choice(リストなど)
繰り返しはWhile文かfor文を使い
hit==3か10回当てられない場合に抜ける
hit==3か10回当てられない場合に抜ける
コードはこんな感じになりました。
入力に数字を打ち込んであげると
判定結果が返ってきます。
import random
import itertools
# 数字をチェックする
def check_call(call , solve):
hit , blow = 0,0
for i,n in enumerate(call):
if solve[i]==n:
hit+=1
elif n in solve:
blow +=1
return hit , blow
# ゲームの開始
# 正解数値の生成
seq = (0,1,2,3,4,5,6,7,8,9)
nums = [str(a)+str(b)+str(c) for a,b,c in itertools.permutations(seq, 3)]
solve = random.choice(nums)
# 10回繰り返すか、正解で終了
count = 0
while True:
count+=1
call = input()
hit , blow = check_call(call , solve)
print(count , call , '{0}H{1}B'.format(hit , blow))
if hit==3:
print('You Win , Solve = {0}'.format(solve))
break
if count==10:
print('You Lose , Solve = {0}'.format(solve))
break
123
1 123 0H1B
789
2 789 0H1B
567
3 567 0H1B
712
4 712 0H1B
254
5 254 0H0B
710
6 710 1H1B
910
7 910 1H0B
817
8 817 0H1B
709
9 709 0H2B
098
10 098 0H1B
You Lose , Solve = 370
ぐう、ダメでしたね。
当てられないとは
0H0Bをうまく使うのが
当てるコツですね。
上級編:
正解の数字を導き出すアルゴリズム
正解の数字を導き出すアルゴリズム
正解の数字をあらかじめ決めておき
チェック関数の結果を用いて
チェック関数の結果を用いて
正解の数字を予想するプログラムを作成してみよう。
ヒント:
チェック関数をうまく使うと
正解の候補を考える事が出来るよ!!
正解の候補を考える事が出来るよ!!
・・・
・・
・
上級編の解答:
まず、チェック関数を使って予想数字から
正解数字の判定結果を全通り求めておきます。
集合をうまく使うと候補を絞り込む事が出来ます。
(Pythonではsetが集合のデータ型)
(Pythonではsetが集合のデータ型)
積集合(intersection)を使うと
候補同士の重なる部分を抽出できます。
候補同士の重なる部分を抽出できます。
1回目:
正解 XYZ , 予想 123 , 判定結果 1H1B
正解 XYZ , 予想 123 , 判定結果 1H1B
予想が1H1Bになる数字の組み合わせは
124 , 125 , ・・・・
124 , 125 , ・・・・
2回目:
予想が1H1Bになる数字の組み合わせの中から
次の予想をする
予想が1H1Bになる数字の組み合わせの中から
次の予想をする
1回目と2回目の結果の
積集合を求める
積集合を求める
以降繰り返し・・・
どんどん集合を使って候補を絞り込みます。
コードはこんな感じになりました。
どんどん集合を使って候補を絞り込みます。
コードはこんな感じになりました。
# 数字をチェックする
def check_call(call , solve):
hit , blow = 0,0
for i,n in enumerate(call):
if solve[i]==n:
hit+=1
elif n in solve:
blow +=1
return hit , blow
# 候補を絞り込むための組み合わせの辞書を作る
def all_combination_calc(call):
result_dict = {}
for i in list(itertools.permutations((0,1,2,3,4,5,6,7,8,9), 3)):
num = str(i[0])+str(i[1])+str(i[2])
hit , blow = check_call(call,num)
key = '{0}H{1}B'.format(hit , blow)
if key in result_dict:
tmp = result_dict[key]
tmp.append(num)
else:
tmp = [num]
result_dict[key] = tmp
return result_dict
# 数字当てをする
seq = (0,1,2,3,4,5,6,7,8,9)
s_nums = [str(a)+str(b)+str(c) for a,b,c in itertools.permutations(seq, 3)]
solve = random.choice(s_nums)
print('Solve = ', solve)
# 正解の数字を解く
def solve_code_break(solve):
sets = set([])
call = random.choice([str(a)+str(b)+str(c) for a,b,c in itertools.permutations(seq, 3)])
hit , blow = check_call(call , solve)
key = '{0}H{1}B'.format(hit , blow)
print(call,key)
if call==solve:
print('End')
else:
while True:
conbination_dict = all_combination_calc(call)
second_choice = conbination_dict[key]
sets = set(second_choice) if len(sets) ==0 else sets.intersection(set(second_choice))
call = random.choice(list(sets))
s_hit , s_blow = check_call(call , solve)
key = '{0}H{1}B'.format(s_hit , s_blow)
print(call,key)
if call==solve:
break
print('End')
solve_code_break(solve)
Solve = 736
936 2H0B
931 1H0B
906 1H0B
836 2H0B
236 2H0B
736 3H0B
End
せっかくなんで
何回で当てれるものなのか
シミュレーションしてみましょう。
先ほどのプログラムを改良して
解いた時の回数を出すようにして
1000回試行して何回で解けるのか
集計します。
# 正解の数字を解くシミュレーションをする
def code_break_sim():
seq = (0,1,2,3,4,5,6,7,8,9)
s_nums = [str(a)+str(b)+str(c) for a,b,c in itertools.permutations(seq, 3)]
solve = random.choice(s_nums)
term,sets = 1,set([])
call = random.choice([str(a)+str(b)+str(c) for a,b,c in itertools.permutations(seq, 3)])
hit , blow = check_call(call , solve)
key = '{0}H{1}B'.format(hit , blow)
if call==solve:
return term
else:
while True:
conbination_dict = all_combination_calc(call)
second_choice = conbination_dict[key]
sets = set(second_choice) if len(sets) ==0 else sets.intersection(set(second_choice))
call = random.choice(list(sets))
s_hit , s_blow = check_call(call , solve)
key = '{0}H{1}B'.format(s_hit , s_blow)
term+=1
if call==solve:
return term
calc_dict = {}
for i in range(1000):
key = code_break_sim()
if key in calc_dict:
calc_dict[key]+=1
else:
calc_dict[key]=1
for k,v in sorted(calc_dict.items()):
print(k,v)
1 2
2 10
3 63
4 196
5 335
6 277
7 86
8 28
9 3
だいたい5回以内には解けそうですね。
多くても7回ほどあれば解答に
たどり着けそうです。
10回以上かかると
やばそうですね。
とほほほ
コードブレイカーは
ヌメロンと言う名前で
テレビ番組用のゲームとして
使われていたこともあったようです。
遊びたい方はみてみてください。
今回はこれまでです
それでは。

コメントする