Numeronアルゴリズムのpythonソースコード
ここで議論したNumeronアルゴリズム(戦術T2')をpythonで実装した。
一行一行注釈を付けていく。*1*2
main.py
# -*- coding: utf-8 -*- N = 3 #ゲームで扱う桁数 import functions P = [] f = [] c = [[]] a = [7,1,9] #setup functions.Generate_P(P) functions.Generate_P0(P) print 'possible = ', functions.Calc_P_size(P) print '' count = 0 while True: functions.Make_call(P, c) count += 1 print 'c = ', c[0] functions.Make_feedback(f, c[0], a) print 'feedback = ', f if f[1] == 3: print 'answer is ...', c[0] print 'try = ', count break else: functions.Generate_next(P,f) f = [] print 'possible = ', functions.Calc_P_size(P) print ''
functions.py
# -*- coding: utf-8 -*- N = 3 #推測値cと正解値aを比べてfeedback fを作成する def Make_feedback(f, c, a): f.append(c) f.append(Eat(c,a)) f.append(Bite(c,a)) #Pから推測値を抽出する #Pの最初の数を推測値とする #推測値はcに格納する def Make_call(P, c): for i in range(10 ** N): if P[2 * i] == True: c[0] = P[2 * i + 1] break else: pass def Generate_next(P, f): for i in range(10 ** N): if P[2 * i] == True: #signがTrueの場合のみ考えれば良い。Falseならそれ以上触れる必要がない。 P[2 * i] = Sign(P[2 * i + 1], f) if P[2 * i] == False: pass #P0を生成する def Generate_P0(P): for i in range(10 ** N): if Judge(P[2 * i + 1]) == True: pass if Judge(P[2 * i + 1]) == False: P[2 * i] = False #numがゲームルールに適合しているか判定する def Judge(num): for i in range(N): for j in range(N): if i != j and num[i] == num[j]: #桁は異なるのに数字は一致する=重複あり return False else: pass return True #Pを生成する def Generate_P(P): num = [] for i in range(10 ** N): P.append(True) Convert_int_to_num(num,i) P.append(num) num = [] #初期化 #integer intを各桁毎に分解してデータ型numへ変換する def Convert_int_to_num(num, int): for i in range(N): num.append(int % 10) int /= 10 num.reverse() #|P|を計算をする #|P|を返す def Calc_P_size(set_num): count = 0 for i in range(10 ** N): if set_num[2 * i] == True: count += 1 else: pass P_size = count return P_size #num gの feedback fに対するsign sを計算する #num gがfeedback fに適合していればTrue, 適合していなければFalseを返す def Sign(num_sbj, feedback): if Eat(num_sbj, feedback[0]) == feedback[1] and Bite(num_sbj, feedback[0]) == feedback[2]: s = True else: s = False return s #num gがnum cに対して返すeatを計算する #eatは桁数も数字も合っているものの数 def Eat(num_sbj, num_src): count = 0 #初期化 for i in range(N): if num_sbj[i] == num_src[i]: count += 1 else: pass eat = count return eat #num gがnum cに対して返すbiteを計算する #biteは数字はあっているが桁数が異なるものの数 def Bite(num_sbj, num_src): count = 0 for i in range(N): for j in range(N): if j != i and num_sbj[j] == num_src[i]: #桁数が一緒の時はたとえ数字が一致してもカウントしない count += 1 else: pass bite = count return bite
実行結果
possible = 720 c = [0, 1, 2] feedback = [[0, 1, 2], 1, 0] possible = 126 c = [0, 3, 4] feedback = [[0, 3, 4], 0, 0] possible = 40 c = [5, 1, 6] feedback = [[5, 1, 6], 1, 0] possible = 9 c = [5, 7, 2] feedback = [[5, 7, 2], 0, 1] possible = 4 c = [7, 1, 8] feedback = [[7, 1, 8], 2, 0] possible = 1 c = [7, 1, 9] feedback = [[7, 1, 9], 3, 0] answer is ... [7, 1, 9] try = 6
main.pyについての注釈
- ソースコードを意味の単位で記述したいので関数を多用する。
- メインのコードをmain.pyとし、関数定義を集めたものをfunctions.pyとする。
- オブジェクト指向プログラミング(OOP)は一切使っていない。そのため完全に意味の単位で記述できていない。
# -*- coding: utf-8 -*-
pythonが日本語と相性が悪いので、最初にこう書いておくと良いおまじない。これ無しだとコメントすら日本語で書けない。
N = 3
Nは桁数を表す。Numeronでは3桁の数を用いているが、別に4桁でも構わない。ソースコードは一般的にN桁の数当てゲームのアルゴリズムとなっている。4桁の数当てがやりたいならN=4とすればよい。
P = [] f = [] c = [[]]
ここでの記法に従う。ソースコードで数学のように簡潔な記号法を使う問題点はコードの視認性の低下である。コードの視認性を高めるためには、変数の名前を一目見て内容が分かるようなものにするのが望ましい。ただ今回はここの議論を前提とするので、あえて数学的な記号法を用いている。本来は、例えばPはPossible、fはfeedbackとか省略しない方が良いのかもしれない。
a = [7,1,9]
正解数を719と設定する。
正解数を設定し直す度にコードを書き直すのは面倒だし、コードを変更すること自体望ましくないように思える。
コンソールから正解数を取得するようなプログラムを書くことも出来るが、(面倒なので)やめておいた。このプログラムにUI等の完成度は求めない。
while True:
数を当てるまで推測をし続けるので、ループを設定する必要がある。
ループの回数は不明なのでwhileを使う。無限ループで、答えが見つかったらループを抜ける。
if f[1] == 3:
fの構造上、f[1]にはeat数が保管されている。
それが3であるということは、推測と答えが一致したことを意味する。
なのでここでbreakによりループを抜ける。