ゼロからのPython入門講座演習 スイカ割りゲームを作ってみよう

スイカ割りゲームを作ってみようリファクタリング - マジックナンバーを避ける適切なデータ型を使う関数を活用する

ここまでに学んだPythonの知識を活用して、初心者でも作れる簡単な「スイカ割りゲーム」を作ってみましょう。

「スイカ割りゲーム」は、どこかに隠されているスイカを探して、プレイヤーがゲーム盤上を歩き回るゲームです。スイカを探すヒントとして、プレーヤーとスイカの間の距離が与えられます。

この「スイカ割りゲーム」は、簡単で短いプログラムですが、Pythonで本格的なプログラムを作るために必要な要素をたくさん含んでいます。

スイカ割りゲームを開発する手順

ここでは、次のような手順でスイカ割りゲームを設計し、開発していきます。

  1. 「スイカ割りゲーム」はどんなゲームなのか考える
  2. ゲームを構成する手続きを考える
  3. 手続きごとに、Pythonのプログラムを開発する

「スイカ割りゲーム」はどんなゲームなのか考える

ちゃんと動作するプログラムを開発するには、当然ながら「何を 作るのか?」ということをしっかり理解する必要があります。まず、そのプログラムの目的や使い方をしっかり考えてみましょう。

前述のように、「スイカ割りゲーム」は、どこかに隠されているスイカを探して、プレイヤーがゲーム盤上を歩き回るゲームです。スイカを探すヒントとして、プレーヤーとスイカの間の距離が与えられます。

ゲームは、次の図のようにマス目が入った将棋盤のようなゲーム版で行われます。

image.png

左上のマスの座標は(0, 0)で、x座標は左から右に向かって(1, 0)、(2, 0)、…… と増えていき、y座標は上から下に向かって (0, 1)、(0, 2)、…… と増えていくようにしましょう。

このゲームがどのように進められるのか、もう少し具体的に考えてみましょう。

コンピュータープログラムではなく、本物の目隠しをしたプレーヤーとスイカでゲームを行うとしたら、こんなルールになるでしょうか。

  1. スイカとプレイヤーは、ゲーム盤上のランダムなマスに配置されます。
  2. スイカを探すヒントとして、スイカからプレーヤーまでの距離を報告します。
  3. プレイヤーは、北・南・東・西にそれぞれ一マスずつ移動できます。
  4. スイカとプレイヤーが同じマスに入るまで、2. と 3. を繰り返します。

実際にゲームとして遊べるようになるまで、しっかりとイメージを膨らませてみてください。

ゲームを構成する手続きを考える

前節では、スイカ割りゲームでは「何を」開発するのか、ということを検討しました。

次に、このゲームを、「どのように」プログラムとして実現し、動作するのか、という具体的な手順を考えてみましょう。

さきほどまとめたゲーム内容をもう1段階細かく検討し、どんな処理が必要になるのかをよく考えて、実際にPythonで記述する準備をします。

こういった、プログラムで実際に実行する手順や処理のことを、よく「手続き」といいます。

このゲームの「手続き」を、詳しく考えてみましょう。

  1. スイカとプレイヤーの初期位置を決めます。
  2. スイカとプレイヤーの位置がおなじになるまで、以下の処理を繰り返します。
    1. スイカからプレイヤーまでの距離を出力します。
    2. キーボードから文字をうけとります。
    3. 受け取った文字がnなら北、sなら南、eなら東、wなら西に一マス移動します。
  3. ゲーム終了のメッセージを表示します。

こんな感じでいけそうです。こういったプログラムの構成は、最初はうまくできないかもしれませんが、いろいろなプログラムを読んだり書いたりしているうちに、できるようになってきます。

こういった、プログラムの構造を設計するのは、プログラミング言語の勉強だけではなかなか上達しません。簡単なものでも、アプリケーションを自分で考えて開発してみるのが大事です。

ここからは、それぞれの手続をさらにもう1段階細かく検討してみましょう。

スイカとプレイヤーの初期位置

スイカとプレイヤーの位置は、毎回乱数で違った場所になるようにします。あんまり距離が離れすぎてもゲームになりませんから、ここでは、スイカとプレイヤーのx座標とy座標は 0 から 4 までの範囲に収まるようにしましょう。

乱数を生成する

Pythonでは、乱数を生成するときには random モジュールを インポート します。スイカとプレイヤーの座標は 0 から 4 までの整数ですので、ここではrandomモジュールのrandrange関数を利用します。

インポートしたモジュールの関数は、モジュール名.関数名() の形式で呼び出します。忘れていたら、モジュールとimport を読んで復習してください。

randrange() は、

random.randrange(開始値, 終了値)

の形式で指定すると、開始値以上かつ終了値未満(開始値 ≦ 乱数 < 終了値) となる乱数を生成します。

次の例は、0以上5未満の乱数を3回生成しています。

In [2]:
import random
print(random.randrange(0, 5))
print(random.randrange(0, 5))
print(random.randrange(0, 5))
4
0
1

スイカとプレイヤーの位置を設定する

random.randrange()関数 を使って、スイカとプレイヤーの位置を決定するコードを書いてみましょう。

スイカの位置のx座標とy座標は変数 suika_xsuika_yに、プレイヤーの位置のx座標とy座標は変数 player_xplayer_y に代入することにします。

スイカとプレイヤーの位置を決める処理は、次のようになります。

In [15]:
# スイカの位置を決める
suika_x = random.randrange(0, 5)  # スイカのx座標
suika_y = random.randrange(0, 5)  # スイカのy座標

# プレイヤーの位置を決める
player_x = random.randrange(0, 5)  # プレイヤーのx座標
player_y = random.randrange(0, 5)  # プレイヤーのy座標

これで、スイカとプレイヤーの位置が決まりました。

どちらもランダムに位置を決定しているだけですので、スイカとプレイヤーの初期位置は25分の1の確率では同じところになリます。この場合、ゲームは開始すると同時に終了してしまいますが、とりあえずここでは気にしないでおきましょう

ゲームのループ処理

次に、プレイヤーとスイカの位置が一致するまで繰り返すループ処理を考えましょう。

このループは、スイカとプレイヤーの位置が異なるあいだ繰り返す while文によるループ として記述できます。

while スイカとプレイヤーの位置が異なっている:
    ...

スイカとプレイヤーの位置が異なっている という条件は、

スイカのx座標とプレイヤーのx座標が違う

という条件と、

スイカのy座標とプレイヤーのy座標が違う

という条件の、どちらか一方でも ならば、スイカとプレイヤーの位置が異なっている と言えます。

スイカとプレイヤーのx座標が違う という条件は、比較演算子!= を使って、次のようにかけます。

suika_x != player_x

同様に、y座標が違う という条件は、

suika_y != player_y

となります。

さきほどの while ループ

while スイカとプレイヤーの位置が異なっている:
    ...

スイカとプレイヤーの位置が異なっている という条件は、この2つの条件のうち、どちらか一方でも となった場合ですから、論理演算子の or 演算子 を使って

while (suika_x != player_x) or (suika_y != player_y):
    ...

と書けます。while文には、行末の : が必要ですから忘れないようにしてください。

スイカのx座標 suika_x とプレイヤーのx座標 player_x が等しければ、

suika_x != player_x

という比較式の値は False となります。

また、スイカのy座標 suika_y とプレイヤーのy座標 player_y が等しければ、

suika_y != player_y

False となります。

したがって、x座標とy座標の両方でプレイヤーとスイカの座標が等しい場合は、

(suika_x != player_x) or (suika_y != player_y)

という式は

False or False

となり、or演算子の結果は False となり、while を継続する条件が成立しないため、ループは終了します。

スイカからプレイヤーまでの距離を求める

while ループの中では、まず、スイカからプレイヤーまでの距離を求めて表示します。

スイカからプレイヤーまでの距離は、次の式で求められます。

$$ 距離 = \sqrt{(player\_x-suika\_x)^2 + (player\_y-suika\_y)^2} $$

Pythonでは、mathモジュールのsqrt()関数 で平方根を計算できます。次のように mathモジュールを import して、距離を計算してみましょう。

import math
diff_x = player_x - suika_x
diff_y = player_y - suika_y
distance = math.sqrt(diff_x**2 + diff_y**2)

この処理は、2点間の距離を求める 関数 として定義しましょう。

import math

def calc_distance(x1, y1, x2, y2):
    diff_x = x1 - x2
    diff_y = y1 - y2

    return math.sqrt(diff_x**2 + diff_y**2)

このように関数として calc_distance() を定義しておけば、スイカとプレイヤーの距離は次のように求められます。

distance = calc_distance(player_x, player_y, suika_x, suika_y)

プレイヤーの移動処理

while ループ中では、スイカとプレイヤーの距離を表示してから、input()関数を使ってユーザからのキー入力を読み取り、キーに従ってプレイヤーの位置を移動します。ユーザが入力した文字が nなら北、sなら南、eなら東、wなら西に一マス移動します。それ以外の文字なら、何もしません。

プレイヤーを北に移動する場合は、プレイヤーのy座標(player_y)を -1 して、ボードの上方に移動します。逆に、南に移動する場合はy座標を +1 して下方に移動します。

同様に、プレイヤーを西に移動する場合にはプレイヤーのx座標(player_x)を -1 し、東に移動する場合には +1 します。

この処理をまとめると、次のようになります。

c = input("n:北に移動 s:南に移動 e:東に移動 w:西に移動")
if c == "n":
    player_y = player_y - 1
elif c == "s":
    player_y = player_y + 1
elif c == "w":
    player_x = player_x - 1
elif c == "e":
    player_x = player_x + 1

input()で入力された文字列は変数 c に代入されます。 まず、if文 の条件として c == "n" という比較演算を指定して、c が 文字列 "n" と等しいかどうかをチェックしています。c の値が "n" の場合は比較演算子 == の結果は となり、プレイヤーを北に移動しています。

c == "n" の場合は、次にelif節 に 指定した c == "s" という比較を行い、c が 文字列 "n" と等しいかどうかをチェックしています。この演算子が であればプレイヤーを南に移動します。

同様に、c == "s" の場合には、次の elif節の c == "w" の真偽を判定します。これも なら最後に c == "e" を判定して、入力に応じたプレイヤーの位置を移動します。

「スイカ割りゲーム」を完成させ動かしてみる

これで、スイカ割りゲームを構成する部品が一通り揃いました。まとめると次のようになります。

早速実行してみましょう。

In [1]:
import random
import math

def calc_distance(x1, y1, x2, y2):
    # 2点間の距離を求める
    diff_x = x1 - x2
    diff_y = y1 - y2
    
    return math.sqrt(diff_x**2 + diff_y**2)


suika_x = random.randrange(0, 5)  # スイカのx座標
suika_y = random.randrange(0, 5)  # スイカのy座標

player_x = random.randrange(0, 5) # プレイヤーのx座標
player_y = random.randrange(0, 5) # プレイヤーのy座標

# スイカとプレイヤーの位置が異なる間、処理を繰り返す
while (suika_x != player_x) or (suika_y != player_y):

    # スイカとプレイヤーの距離を表示する
    distance = calc_distance(player_x, player_y, suika_x, suika_y)
    print("スイカへの距離:", distance)
    
    # キー入力に応じて、プレイヤーを移動する
    c = input("n:北に移動 s:南に移動 e:東に移動 w:西に移動")
    if c == "n":
        player_y = player_y - 1
    elif c == "s":
        player_y = player_y + 1
    elif c == "w":
        player_x = player_x - 1
    elif c == "e":
        player_x = player_x + 1

print("スイカを割りました!")
スイカへの距離: 3.1622776601683795
n:北に移動 s:南に移動 e:東に移動 w:西に移動 n
スイカへの距離: 3.605551275463989
n:北に移動 s:南に移動 e:東に移動 w:西に移動 s
スイカへの距離: 3.1622776601683795
n:北に移動 s:南に移動 e:東に移動 w:西に移動 s
スイカへの距離: 3.0
n:北に移動 s:南に移動 e:東に移動 w:西に移動 e
スイカへの距離: 2.0
n:北に移動 s:南に移動 e:東に移動 w:西に移動 e
スイカへの距離: 1.0
n:北に移動 s:南に移動 e:東に移動 w:西に移動 e
スイカを割りました!

一般的なPythonの慣習で、import 文はプログラムの先頭に記述します。また、calc_distance() などの関数は、import 文に続けて記述します。

これで「スイカ割りゲーム」の完成です。

まとめ

この例題は短いプログラムですが、本格的なプログラムを書くために必要な要素がたくさん含まれています。

ここでは、プログラムをゼロから開発する時の考え方を、すこし細かく説明してみました。こんなに短いプログラムでも、開発手順を文章で説明するとけっこうな長さになってしまいます。

最初のうちはちょっと大変かもしれませんが、このぐらいのプログラムをスラスラ書けるようになると、初歩的なプログラミングの考え方をマスターしたと言えるでしょう。

これが、自分自身のアイデアをPythonプログラミングで実現するための第一歩になります。時間がかかっても、しっかり理解しましょう。

Copyright © 2001-2021 python.jp Privacy Policy python_japan
Amazon.co.jpアソシエイト
Amazonで他のPython書籍を検索