質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

アルゴリズム

アルゴリズムとは、定められた目的を達成するために、プログラムの理論的な動作を定義するものです。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

1回答

2272閲覧

Pythonで数当てゲーム(Hit&Blow)を作りたいのですが...

yomogian

総合スコア8

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

アルゴリズム

アルゴリズムとは、定められた目的を達成するために、プログラムの理論的な動作を定義するものです。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

1クリップ

投稿2018/02/03 09:43

前提・実現したいこと

僕は最近Pythonを独学で勉強しはじめたプログラミング初心者です。
中学生の時、数当てゲームというのがクラスで流行ったことを思い出し作ってみたいと思ったのですが、
コンピュータを対戦できるようにするのはなかなか難しいことのようで、お力を借りたいなと思うのですが、どのようなプログラムを皆様ならお書きになりますか?

Hit&Blowのルール

今回作るものルールは以下のようにしたいと思っています。

・隠れた4桁の数字(重複は無し)を当てる ・回答するとHITとBLOWの数が出る。 ・HITは数字も桁数も同じ、BLOWは数字はあたっているけど桁数が違う

試したこと

素人ながらとりあえずプログラムを書いてみました。
至らない点が多々あると思うので指摘していただけるとありがたいです。

Python3.6

1 2# coding: utf-8 3 4# In[3]: 5 6 7import random 8import copy 9from collections import OrderedDict 10n = 3 11pnum = list(map(lambda i:0,range(0,n)))#player's number 12onum = list(map(lambda i:0,range(0,n))) #opponent's number 13 14# In[4]: 15 16def check_num(pre,onum,n): #プレイヤーの数字チェック 17 hit = 0 18 blow = 0 19 for i in range(0,n): 20 for s in range(0,n): 21 if pre[i] == onum[s]: 22 if i == s: 23 hit += 1 24 else: 25 blow += 1 26 return(hit,blow) 27 28class Opponent(): 29 def __init__(self): 30 self.pres = OrderedDict()#記録順を保持する辞書,{(予想):(結果)} 31 self.count = 0 32 self.pre_hb = [0,0] #前回の予想結果 33 self.change = 0 #H+Bがn/2を超えている時、どの部分を変更したか 34 self.deln = [] #H:0,B:0の時の数字を保持 35 def check(self,pnum,n): 36 same = 1 37 while same == 1:#過去の予想と重複していないか 38 if self.pre_hb[0] + self.pre_hb[1] == n: #数字の組み合わせが揃った時 39 opre = list(list(self.pres.keys())[self.count-1]) 40 random.shuffle(opre) 41 ##リストシャッフル動作 42 hit = 0 43 blow = 0 44 for i in range(0,n): 45 for s in range(0,n): 46 if opre[i] == pnum[s]: 47 if i == s: 48 hit += 1 49 else: 50 blow += 1 51 52 53 54 elif self.count == 0: ##初回はランダム 55 predup = True 56 while predup: #同じ数字になっていないか 57 opre = list(map(lambda i:random.randint(0,9),range(0,n))) 58 if len(opre) == len(set(opre)): 59 predup = False 60 61 hit = 0 62 blow = 0 63 for i in range(0,n): 64 for s in range(0,n): 65 if opre[i] == pnum[s]: 66 if i == s: 67 hit += 1 68 else: 69 blow += 1 70 71 same = 0 #初回は被らない 72 break 73 74 elif self.count == 1:##2回目以降はHBが半分以上かどうかも判定 75 if self.pre_hb[0] + self.pre_hb[1] == 0:#数字の組み合わせが合っている時 76 self.deln.extend(list(list(self.pres.keys())[self.count-1])) 77 self.deln = list(set(self.deln)) 78 if self.pre_hb[0] + self.pre_hb[1] > n/2:#半分以上あっているとき 79 ##半分以上なら前回のから1桁だけ変える 80 dup = True 81 while dup: 82 opre = list(list(self.pres.keys())[self.count-1]) 83 self.change = random.randint(0,n-1) 84 opre[self.change] = random.choice(list(set(range(0,10)) - set(list(self.pres.keys())[self.count-1]))) 85 for i in range(0,n): 86 if opre[i] in self.deln: 87 dup = True 88 break 89 else: 90 dup = False 91 92 else:##違ったらランダム 93 dup = True 94 while dup: 95 predup = True 96 while predup: 97 opre = list(map(lambda i:random.randint(0,9),range(0,n))) 98 if len(opre) == len(set(opre)): 99 predup = False 100 for i in range(0,n): 101 if opre[i] in self.deln: 102 dup = True 103 break 104 else: 105 dup = False 106 107 108 hit = 0 109 blow = 0 110 for i in range(0,n): 111 for s in range(0,n): 112 if opre[i] == pnum[s]: 113 if i == s: 114 hit += 1 115 else: 116 blow += 1 117 118 119 else: ##3回目以降は変えた場所がうまく行ってるかも判定 120 if self.pre_hb[0] + self.pre_hb[1] == 0: 121 self.deln.extend(list(list(self.pres.keys())[self.count-1])) 122 self.deln = list(set(self.deln)) 123 if self.pre_hb[0] + self.pre_hb[1] > n/2: 124 if self.pre_hb[0] + self.pre_hb[1] >= self.pres[list(self.pres.keys())[self.count-2]][0] + self.pres[list(self.pres.keys())[self.count-2]][1]: 125 ##うまくいってたら別のところ 126 dup = True 127 while dup: 128 opre = list(list(self.pres.keys())[self.count-1]) 129 m = random.randint(0,n-1) 130 while m == self.change: 131 m = random.randint(0,n-1) 132 opre[self.change] = random.choice(list(set(range(0,10)) - set(list(self.pres.keys())[self.count-1]))) 133 134 for i in range(0,n): 135 if opre[i] in self.deln: 136 dup = True 137 break 138 else: 139 dup = False 140 else: ##行ってなければランダムの場所 141 dup = True 142 while dup: 143 opre = list(list(self.pres.keys())[self.count-1]) 144 self.change = random.randint(0,n-1) 145 opre[self.change] = random.choice(list(set(range(0,10)) - set(list(self.pres.keys())[self.count-1]))) 146 for i in range(0,n): 147 if opre[i] in self.deln: 148 dup = True 149 break 150 else: 151 dup = False 152 153 else:##違ったらランダム 154 dup = True 155 while dup: 156 predup = True 157 while predup: 158 opre = list(map(lambda i:random.randint(0,9),range(0,n))) 159 if len(opre) == len(set(opre)): 160 predup = False 161 for i in range(0,n): 162 if opre[i] in self.deln: 163 dup = True 164 break 165 else: 166 dup = False 167 168 169 hit = 0 170 blow = 0 171 for i in range(0,n): 172 for s in range(0,n): 173 if opre[i] == pnum[s]: 174 if i == s: 175 hit += 1 176 else: 177 blow += 1 178 179 180 181 for p in range(len(list(self.pres.keys()))): #過去の予想との重複チェック 182 if list(list(self.pres.keys())[p]) == opre: 183 print("it's same") 184 same = 1 185 break 186 else: 187 same = 0 188 189 self.pres[tuple(opre)] = (hit,blow) 190 self.pre_hb = copy.deepcopy([hit,blow]) 191 self.count += 1 192 return (hit,blow,opre) 193 194 195 196 197 198 199 200# In[6]: 201 202 203while len(pnum) != len(set(pnum)) or len(pnum) != n: 204 num = input("{}桁の数字を入力してください:\n ※数字の重複がないように".format(n)) 205 pnum = [int(x) for x in list(num)] 206 207while len(onum) != len(set(onum)): 208 onum = list(map(lambda i:random.randint(0,9),range(0,n))) 209 210 211# In[7]: 212 213 214## ゲーム開始 215r = random.randint(0,1) 216print("順番をランダムで決定します") 217if r == 0: 218 print("あなたは先行です") 219 pre = [0] 220 while len(pre) != n: 221 print("予想する{}桁の数字を入力してください。同じ数字でも構いません".format(n)) 222 pre = input() 223 pre = [int(x) for x in list(pre)] 224 res = check_num(pre,onum,n) 225 print("結果:HIT{},BLOW{}".format(res[0],res[1])) 226 227else: 228 print("あなたは後攻です") 229 res = [0,0] 230 231 232opponent = Opponent() 233ores = [0,0,0] 234while res[0] != n and ores[0] != n: 235 ores = opponent.check(pnum,n) 236 print("敵の結果{}:HIT{},BLOW{} \n".format(ores[2],ores[0],ores[1])) 237 if ores[0] == n: 238 break 239 pre = [0] 240 while len(pre) != n: 241 print("予想する{}桁の数字を入力してください。同じ数字でも構いません".format(n)) 242 pre = input() 243 pre = [int(x) for x in list(pre)] 244 res = check_num(pre,onum,n) 245 print("結果:HIT{},BLOW{}".format(res[0],res[1])) 246 247 if opponent.count % 10 == 0: 248 print("Now,deleted number is {}".format(opponent.deln)) 249 250if res[0] == n: 251 print("正解!") 252else: 253 print("あなたの負け!") 254 255 256

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

かなりの力作ですねー。。
全部読み切れてませんが、気づいた点だけ

1,# In[4]:の行があることから推測しますが、pythonの対話モード(インタラクティブモード)で開発と実行をしていませんか?
無料のエディタやIDE(統合開発環境)をインストールして、.pyのスクリプトファイルを作成し実行することをお勧めします。

2,以下の関数 check_numについて

Python

1def check_num(pre,onum,n): #プレイヤーの数字チェック 2 hit = 0 3 blow = 0 4 for i in range(0,n): 5 for s in range(0,n): 6 if pre[i] == onum[s]: 7 if i == s: 8 hit += 1 9 else: 10 blow += 1 11 return(hit,blow)

行っているのはhit&blowの判定なため、
2-1,HitAndBlowクラスを新規作成
2-2,関数名をcheck_numからHitAndBlow#Judgeに変更
2-3,答えの数字を生成するHitAndBlow#generate関数を追加。
0~9の数字配列を生成後にrandom#shuffleを使用して生成する。

Python

1class HitAndBlow(object): 2 def __init__(self, n: int=3): 3 assert 10 >= n 4 self.N = n 5 # HitAndBlowクラスが正解情報(onum)を管理するほうがいいです。 6 # ※self.answerを使ったjudge関数は宿題にしておきます。 7 self.answer = self.generate() 8 9 def generate(self) -> list: 10 """ 11 答えの数字を生成。 12 :return: 13 """ 14 digits = list(range(10)) 15 random.shuffle(digits) 16 return digits[:self.N] 17 18 def judge(self, guess: list, answer: list) -> tuple: 19 """ 20 Hit&Blowの判定処理 21 :param guess: 予測した数字 22 :param answer: 正解の数字 23 :return: 24 """ 25 # 引数で渡されたpreとonumの桁数が同じでないといけない制約がありますよね。その時はassert文で表明します。 26 assert len(guess) == len(answer) 27 hit = sum(g == a for g, a in zip(guess, answer)) 28 blow = sum(g in answer for g in guess) - hit 29 return hit, blow 30

Python

1# 使い方 2game = HitAndBlow() 3hit, blow = game.judge(pnum, onum)

3,check関数内で以下のようにcheck_num関数を使用せずに、hit&blowの判定を行っている箇所が4箇所あります。check_num関数の呼び出しに統一するとコードの行が削減できます。

Python

1 ##リストシャッフル動作 2 hit = 0 3 blow = 0 4 for i in range(0,n): 5 for s in range(0,n): 6 if opre[i] == pnum[s]: 7 if i == s: 8 hit += 1 9 else: 10 blow += 1

■余談
Opponentクラスのcheck関数が158行と長すぎるためデバックしずらいです。
機能を絞った関数を作成して呼び出すようにするか、各数字を生成する部分/ループ処理/条件判定を分けたほうが、個々でテストできるようになります。

■参考情報
ヒットアンドブローをPythonで書いてみる

投稿2018/02/03 14:53

編集2018/02/04 06:20
umyu

総合スコア5846

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問