Help us understand the problem. What is going on with this article?

簡単なゲームを作りながらPythonを学ぶ

はじめに

独学プログラマーという本を参考にしながらPythonについて少し学びました。

Rubyについては学習済みで、Pythonの文法に慣れるために本に記載されているゲームのコードを改良したりしました。
その際に学んだことを記事にしようと思います。

ゲームルール

この本にのっているハングマンというゲームのルールを説明します。

  1. プレイヤー1がプログラム上で単語を決めて、単語の文字の数だけ、アンダースコア(_)を引きます。
  2. プレイヤー2に単語を予想させて、1回に1文字ずつ回答させていきます。
  3. プレイヤー2が回答した文字が隠してある単語に含まれていたら、プレイヤー1が書いておいた下線のその文字があるべきところに、文字が表示されます。1つの単語に同じ文字が2つ以上含まれている場合は、回答1回につき1文字だけ表示されます。プレイヤー2の回答が間違っていた場合、用意してある絵の一部が現れます(上の方から)。
  4. 用意されている絵(吊られた人の絵)が完成する前に、プレイヤー2が隠された単語の文字を全て当てられたら、プレイヤー2の勝ちです。逆に、絵が完成した場合は負けです。



元々のソースコードはこちら

hangman.py
def hangman(word):
    wrong = 0
    stages = ["",
              "_______      ",
              "|            ",
              "|      |     ",
              "|      0     ",
              "|     /|\    ",
              "|     / \    ",
              "|            "
              ]
    rletters = list(word)
    board = ["_"] * len(word)
    win = False
    print("ハングマンへようこそ!")
    while wrong < len(stages) - 1:
        print("\n")
        msg = "1文字を予想してね! "
        char = input(msg)
        if char in rletters:
            cind = rletters.index(char)
            board[cind] = char
            rletters[cind] = '$'
        else:
            wrong += 1
        print(" ".join(board))
        e = wrong + 1
        print("\n".join(stages[0:e]))
        if "_" not in board:
            print("あなたの勝ち!")
            print(" ".join(board))
            win = True
            break
    if not win:
        print("\n".join(stages[0:wrong+1]))
        print("あなたの負け!正解は {}.".format(word))

hangman("cat")



ターミナル上で、python hangman.pyと入力し、実行すると


スクリーンショット 2019-04-06 18.01.03.png
スクリーンショット 2019-04-06 18.01.16.png

という流れになりますが、これだと毎回プログラム上で単語を決めてから実行となるので
ターミナル上でプレイヤー1が単語を決めることができるようにしてみます。

コードを改良してみる

まず、プレイヤー1がターミナル上で単語を決める際に、ターミナル上で入力した内容をわからないようにするためにgetpassというモジュールを使ってみます。
getpassモジュールのgetpass()関数を使うと、入力した内容が表示されなくなります。

ではコードを編集してみましょう。

hangman.py
import getpass  #getpassモジュールを読み込むために記述
import re       #reモジュールを読み込むことで正規表現が使えるようになります

## 以下省略

    if not win:
        print("\n".join(stages[0:wrong+1]))
        print("あなたの負け!正解は {}.".format(word))

## ↓ここから追記↓

while True:
    player1 = "player1: 英単語を入力してね!(3文字以上6文字以内)"
    question = getpass.getpass(player1)
    if not re.match('^[a-z]+$', question):
        print("英字で入力してね")
    else:
        if len(question) >= 3 and len(question) <= 6:
            break
        else:
            print("3文字以上6文字以内だよ!")

hangman(question) #引数をプレイヤー1が入力した値にする。



プレイヤー1が決める英単語が長すぎたら当てるのが難しく、短すぎたらすぐ当たって面白くないので
とりあえず3文字以上6文字以内に制限してみました。この辺は個人のお好みで変えてください。

コードの説明としては、while True:で、if文の条件(プレイヤー1が3文字以上、6文字以内の文字を入力)を満たすまでループするようにしています。
そして、len関数を使用して文字列の長さを取得しています。
記述方法は、len(オブジェクト)となります。

また、英字かどうかの判定をif not re.match('^[a-z]+$', question):で行なっています。
詳しい正規表現の使い方に関してはこちらを参考にしてください。
英字かどうかの判定についての詳しい記事はこちら

あとは、プレイヤー1が入力した内容( question )を引数として渡してゲーム開始となります。

次に、プレイヤー2が入力する部分を編集しましょう。

hangman.py
##   ↓↓↓      ここの部分を編集        ↓↓↓

    while wrong < len(stages) - 1:
        print("\n")
        msg = "1文字を予想してね! "
        char = input(msg)
        if char in rletters:
            cind = rletters.index(char)
            board[cind] = char
            rletters[cind] = '$'
        else:
            wrong += 1


##   ↓↓↓      以下のように書き換える        ↓↓↓

    while wrong < len(stages) - 1:
        while True:
            print("\n")
            msg = "player2: 1文字(英字小文字)を予想してね! "
            char = input(msg)
            if not re.match('^[a-z]+$', char):
                print("英字(小文字)で入力してね")
            else:
                if len(char) == 1:
                    break
                else:
                    print("1文字ずつだよ!")

プレイヤー1の時と同じ様に、while True:で、if文の条件(プレイヤー2が1文字の英単語を入力)を満たすまでループするようにしています。

変わったこととしては、今までは1文字以上入力していても次の動作を行なってしまっていたために、文字数制限をかけたのと、プレイヤー2が入力する際の、表示を変えたぐらいです。

これでプログラムを実行すると、プレイヤー1の入力した内容がプレイヤー2にわからない様になっています。

スクリーンショット 2019-04-08 14.16.07.png

これで、対戦型ゲームとして遊びやすい内容となりました。
他にもPythonで簡単に作れるお手軽なゲームはたくさんあり、楽しみながら学習できるので是非調べてみてください。
個人的には対戦型のゲームで、ヌメロンというゲームが面白いと感じたので、ヌメロンを作ることをお勧めします。

Pythonには便利なモジュールがたくさん用意されているので、積極的に活用していきましょう。

筆者は、まだエンジニアとして働いたことのないプログラミング学習歴2ヶ月程度の未経験エンジニアなので
まだまだコードに改善の余地があると思いますので、気づいた方はご指摘していただけると嬉しいです。
よろしくお願いします。

完成したコード(対戦型)

hangman.py
import getpass
import re

def hangman(word):
    wrong = 0
    stages = ["",
              "_______      ",
              "|            ",
              "|      |     ",
              "|      |     ",
              "|      |     ",
              "|      |     ",
              "|      |     ",
              "|      0     ",
              "|     /|\    ",
              "|     / \    ",
              "|            "
              ]
    rletters = list(word)
    board = ["_"] * len(word)
    win = False
    print("ハングマンへようこそ!")
    while wrong < len(stages) - 1:
        while True:
            print("\n")
            msg = "player2: 1文字(英字小文字)を予想してね! "
            char = input(msg)
            if not re.match('^[a-z]+$', char):
                print("英字(小文字)で入力してね")
            else:
                if len(char) == 1:
                    break
                else:
                    print("1文字ずつだよ!")
        if char in rletters:
            cind = rletters.index(char)
            board[cind] = char
            rletters[cind] = '$'
        else:
            wrong += 1
        print(" ".join(board))
        e = wrong + 1
        print("\n".join(stages[0:e]))
        if "_" not in board:
            print("あなたの勝ち!")
            print(" ".join(board))
            win = True
            break
    if not win:
        print("\n".join(stages[0:wrong+1]))
        print("あなたの負け!正解は {}.".format(word))

while True:
     player1 = "player1: 英単語を入力してね!(3文字以上6文字以内)"
     question = getpass.getpass(player1)
     if not re.match('^[a-z]+$', question):
         print("英字で入力してね")
     else:
         if len(question) >= 3 and len(question) <= 6:
             break
         else:
             print("3文字以上6文字以内だよ!")

hangman(question)

最後に

このゲームを対戦型ではなく、一人でも遊べる様にしたコードも作ったので、記載しておきます。
興味がある方は、見ていって下さい。

仕様

一人で遊べる様に、自動で単語が選択される様にする
まずは、辞書から3文字の単語を取り出す
取り出した単語の中からランダムで選択
自動で選択された単語をプレイヤーが当てる様にする

はじめに、hangmanのコードを書いているファイルにコードを追加すると、可読性が下がるので、
自動で単語を選択するコードは別ファイルに書きましょう。
choice.pyを作成します。

choice.py
import random  #Pythonでランダムな処理が必要な場合に活躍する標準モジュール

f = open ('/usr/share/dict/words','r')
lines = f.read().splitlines()
f.close()

input_words = []
for i in range(len(lines)):
    if len(lines[i]) == 3:
        input_words.append(lines[i].lower())

input_word = input_words[random.randint(0,len(input_words))]



こちらの記事を参考にして、辞書ファイルからランダムに単語を抽出する様にしました。

コードの説明としては、まずrandomモジュールを読み込み、randint関数が使える様にしています。

linuxには辞書ファイルがり、Macの場合、辞書ファイルは/usr/share/dict/wordsにある様です。
なので、open()を用いてファイルを開いています。open()f.close()はセットで覚えてください。
f.close()を使わない方法としてwithを用いた書き方もあるみたいなので、興味がある方は調べてみてください。

そして、単語の最後に改行が含まれるので.splitlines()を使用して改行文字(\n)で分割してリストとして返す様にしています。
その他、replaceで置換する方法もあります。

for文とif文を利用して、取り出した単語の中から、3文字の単語にだけ処理をする様にしています。
処理内容は、単語に大文字が含まれているものもあるので、文字列を大文字から小文字へ変換するメソッド.lower()を使用して、input_wordsに追加しています。

最後の行では、randint関数を用いて、0からinput_wordsの要素数の間でランダムな整数を選ぶ様にしています。
そのランダムで選ばれた整数を用いて、3文字の単語のみが格納されたinput_wordsから単語を取り出す様にしています。

あとは、hangman.pyを少し編集して完成です。

hangman.py
# import getpass を削除
# import re も削除
import choice  # この記述でchoice.pyの変数を使える様にする

# while True:
#     player1 = "player1: 英単語を入力してね!(3文字以上6文字以内)"
#     question = getpass.getpass(player1)
#     if not re.match('^[a-z]+$', question):
#         print("英字で入力してね")
#     else:
#         if len(question) >= 3 and len(question) <= 6:
#             break
#         else:
#             print("3文字以上6文字以内だよ!")

#   ↑↑↑     この部分を削除        ↑↑↑
#   ↓↓↓       ここから追記          ↓↓↓

question = choice.input_word
hangman(question)



questionに、choice.pyinput_wordを代入しています。
これで、一人でも遊べる様になりました。

しかし、序盤に1文字当てれないとどんな単語か予想がしづらく、難易度が高めなので
何回か間違えたら1文字表示させてあげる様に親切設計にするといいかもしれません。

完成したコード(一人用)

hangman.py
import choice

def hangman(word):
    wrong = 0
    stages = ["",
              "_______      ",
              "|            ",
              "|      |     ",
              "|      |     ",
              "|      |     ",
              "|      |     ",
              "|      |     ",
              "|      0     ",
              "|     /|\    ",
              "|     / \    ",
              "|            "
              ]
    rletters = list(word)
    board = ["_"] * len(word)
    win = False
    print("ハングマンへようこそ!")
    while wrong < len(stages) - 1:
        while True:
            print("\n")
            msg = "player2: 1文字(英字小文字)を予想してね! "
            char = input(msg)
            if not re.match('^[a-z]+$', char):
                print("英字(小文字)で入力してね")
            else:
                if len(char) == 1:
                    break
                else:
                    print("1文字ずつだよ!")
        if char in rletters:
            cind = rletters.index(char)
            board[cind] = char
            rletters[cind] = '$'
        else:
            wrong += 1
        print(" ".join(board))
        e = wrong + 1
        print("\n".join(stages[0:e]))
        if "_" not in board:
            print("あなたの勝ち!")
            print(" ".join(board))
            win = True
            break
    if not win:
        print("\n".join(stages[0:wrong+1]))
        print("あなたの負け!正解は {}.".format(word))

question = choice.input_word
hangman(question)
Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away