はじめに
独学プログラマーという本を参考にしながらPythonについて少し学びました。
Rubyについては学習済みで、Pythonの文法に慣れるために本に記載されているゲームのコードを改良したりしました。
その際に学んだことを記事にしようと思います。
ゲームルール
この本にのっているハングマンというゲームのルールを説明します。
- プレイヤー1がプログラム上で単語を決めて、単語の文字の数だけ、アンダースコア(_)を引きます。
- プレイヤー2に単語を予想させて、1回に1文字ずつ回答させていきます。
- プレイヤー2が回答した文字が隠してある単語に含まれていたら、プレイヤー1が書いておいた下線のその文字があるべきところに、文字が表示されます。1つの単語に同じ文字が2つ以上含まれている場合は、回答1回につき1文字だけ表示されます。プレイヤー2の回答が間違っていた場合、用意してある絵の一部が現れます(上の方から)。
- 用意されている絵(吊られた人の絵)が完成する前に、プレイヤー2が隠された単語の文字を全て当てられたら、プレイヤー2の勝ちです。逆に、絵が完成した場合は負けです。
元々のソースコードはこちら
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`と入力し、実行すると
という流れになりますが、これだと毎回プログラム上で単語を決めてから実行となるので
ターミナル上でプレイヤー1が単語を決めることができるようにしてみます。
コードを改良してみる
まず、プレイヤー1がターミナル上で単語を決める際に、ターミナル上で入力した内容をわからないようにするためにgetpass
というモジュールを使ってみます。
getpass
モジュールのgetpass()
関数を使うと、入力した内容が表示されなくなります。
ではコードを編集してみましょう。
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が入力する部分を編集しましょう。
## ↓↓↓ ここの部分を編集 ↓↓↓
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にわからない様になっています。
これで、対戦型ゲームとして遊びやすい内容となりました。
他にもPythonで簡単に作れるお手軽なゲームはたくさんあり、楽しみながら学習できるので是非調べてみてください。
個人的には対戦型のゲームで、ヌメロンというゲームが面白いと感じたので、ヌメロンを作ることをお勧めします。
Pythonには便利なモジュールがたくさん用意されているので、積極的に活用していきましょう。
筆者は、まだエンジニアとして働いたことのないプログラミング学習歴2ヶ月程度の未経験エンジニアなので
まだまだコードに改善の余地があると思いますので、気づいた方はご指摘していただけると嬉しいです。
よろしくお願いします。
完成したコード(対戦型)
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
を作成します。
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))]
[こちら](https://www.cyamax.com/entry/2017/02/04/111905)の記事を参考にして、辞書ファイルからランダムに単語を抽出する様にしました。
コードの説明としては、まず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
を少し編集して完成です。
# 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.py`の`input_word`を代入しています。 これで、一人でも遊べる様になりました。
しかし、序盤に1文字当てれないとどんな単語か予想がしづらく、難易度が高めなので
何回か間違えたら1文字表示させてあげる様に親切設計にするといいかもしれません。
完成したコード(一人用)
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)