メモ帳

python, juliaで機械学習をやっていく

Hit and BlowをPythonで作る

Python 3でHit and Blowという簡単なゲームを作ってみます。(多分2系でも動きます)




Hit and Blowは有名な数字当てゲームです。出題者は0から9までの数字を重複しないように決められた桁数選びます。解答者は何度でも回答できます。外れた場合は、

  • Hit(当たっている数)
  • Blow(数字は当たっているが桁が違う)
それぞれの数を教えてもらえます。
例.
正解 = "146"
回答(1) = "241" 1 Hit, 1 Blow
これをヒントに正解を目指すというゲームです。2人で出題しあい、対決することもできます。少ない回答数で正解した方が勝利です。

暇つぶしに一人でもできるようにしていきます。

まず、Hit and Blowの判定をしなければいけません。答え(command)と正解(ans)が入力されると、Hitの数とBlowの数を返すものがほしいです。数字の比較を行わなければいけませんね。
そこで、今回、数字一文字ずつを要素とするリストとして扱います。すると以下のように一文字ずつリストの要素として取り出せます。桁数と要素番号が対応するので、同じ桁同士の比較が容易になります。

answer = [1, 2, 3, 4]
print(answer[0]) # 1
print(answer[1]) # 2

これさえ分かればあとは単純です。Hit and Blow判定の関数はこんな感じで書けます。

def hitblow(command, ans):
    hit = 0
    blow = 0
    for i in range(len(command)):
        if command[i] == ans[i]:
            hit += 1
        else:
            if command[i] in ans:
                blow += 1
    return [hit, blow]

同じ桁の数字が一致していたらhitカウンタを増やす。hitでない、かつ、正解の中に同じ数字があればblowカウンタを増やす。この2ステップを繰り返しています。
blow判定ではinを使っています。for文で毎回でてくるので慣れているかと思います。条件式でのinは特定の要素を含んでいればTrueを返すものです。いわゆる部分一致ですね。

これでHit and Blowは遊べますが、せっかくなので正解をランダムに生成させましょう。乱数を扱うためにはrandomモジュールをインポートするのが便利です。

random.randint(a, b)

を使えばa以上b以下の整数をランダムに返してくれます。これを使うと、以下のような乱数生成関数がつくれます。重複しないように一手間かけています。

import random
def generator(length):
    ans = []
    while len(ans) < length:
        rand = random.randint(0, 9)
        if rand in ans:
            pass
        else:
            ans += [rand]
    return ans

コンソールで遊べるようにテキストを用意してみましょう。

print("Hit & Blow Game")
digit = input("Please, enter the number of digits you want to play: ")
ans = generator(int(digit))
cont = True
count = 0
print("Game Start!")
while cont:
    count += 1
    command = input("enter the {} digits number > ".format(digit))
    your_ans = [int(command[i]) for i in range(len(command))]
    [hit, blow] = hitblow(your_ans, ans)
    print("{}: {} Hit, {} Blow".format(command, hit, blow))
    if hit == len(ans):
        print("Congratulations!!! You took {} steps.".format(count))
        cont = False

ゲームを始める前に桁数を入力してもらい、正解の数列をランダムに生成します。そこからゲーム(ループ)が始まります。入力された答えを今回用いるリストの形式に変換するためにリスト内包表記というものを使っています。

    your_ans = [int(command[i]) for i in range(len(command))]

やっていることは、整数に変換された文字を一文字ずつリストの要素に加えているだけです。for文でやっていることと同じですね。ただ、リスト内包表記のほうが処理が速いみたいです。また、見た目もスッキリするのでこちらを使ったほうがいいと思います。
最後に、正解すればループから抜け出させるのは忘れないようにしてくださいね。

これで任意の桁数でのHit and Blowが遊べます。3桁が多分一番難しいです。
単純なゲームですが癖になる面白さがあるのでぜひ遊んでみてください!