python入門を終えて、スイカゲームでアウトプットしてみよう!
はじめに
ドットインストールのpython入門を活用して、勉強しました。
基本的な内容を終えて、学んだことをアウトプットして血肉にしたい思います。
スイカゲームを作ってみよう
「Python japan」サイトの演習問題にある、
「スイカ割りゲームを作ってみよう」に取り組みます。
課題の詳しい内容は、「Python japan」サイトを参照してみてください。
ゲームを簡単に説明すると、
下図の様なマス目のフィールドがあり、その何処かのマスにスイカがあります。
プレイヤー(自分)も位置を移動して、スイカと同じマス目に入りスイカを割ります。
ゲームの構成を考える(大まかに)
実際にコードを書く前に構成を考えることがとても重要と理解しているので、この段階でどういういものを作っていくのかを丁寧に考えるように癖付けます。
1. スイカとプレイヤーの初期位置を決めます。(ランダムになるように)
2. スイカとプレイヤーの位置がおなじになるまで、以下の処理を繰り返します。
-スイカからプレイヤーまでの距離を出力します。
-キーボードから文字をうけとります。
-受け取った文字がNなら北、Sなら南、Eなら東、Wなら西に1マス移動します。
3. ゲーム終了のメッセージを表示します。
1. スイカとプレイヤーの初期位置
スイカとプレイヤーの位置は、randomモジュールを使います。
スイカとプレイヤーのx座標とy座標は、randrange関数をつかって、
0 から 4 までのように範囲を指定します。
import random
print(random.randrange(0,9))
print(random.randrange(0,9))
print(random.randrange(0,9))
print(random.randrange(0,9))
#出力するとこんな感じでランダムに生成されます。
7
1
1
7
この時に注意したいのが、マジックナンバーです。
randrange(0,9)のように、後から人や、将来見たときにこの数字の意味するものが非常にわかりづらいです。
したがって、randrange(0,9)を
以下のように、randrange(0,BOX_SIZE)のようにしておくことが望ましいです。
import random
BOX_SIZE = 9
print(random.randrange(0,BOX_SIZE))
print(random.randrange(0,BOX_SIZE))
print(random.randrange(0,BOX_SIZE))
print(random.randrange(0,BOX_SIZE))
#出力するとこんな感じでランダムに生成されます。
7
1
1
7
これだと、BOXのサイズだろうなと想像もつきますし、サイズの変更があった場合も1か所の変更だけですみます。
スイカとプレイヤーの位置を設定する
スイカとプレイヤーの位置を決めるために、以下のように変数にそれぞれの座標を設定します。
#スイカの位置:
suika_x: スイカのx座標(横の位置)
suika_y: スイカのy座標(縦の位置)
#プレイヤーの位置:
player_x: プレイヤーのx座標
player_y: プレイヤーのy座標
print(suika_x,suika_y)
print(player_x,player_y)
#出力するとこんな感じでランダムに生成されます。
2 5
2 1
しかし、スイカやプレイヤーの座標は本来一つの情報ですので、x座標とy座標で2つの変数に分ける意味はありません。こういった場合、
(x,y)
のように、
suika_posという変数にタプルでスイカの位置(x座標とy座標)を格納します。
# スイカの位置のタプル
suika_pos = (random.randrange(0, BOARD_SIZE), random.randrange(0, BOARD_SIZE))
# プレイヤーの位置のタプル
player_pos = (random.randrange(0, BOARD_SIZE), random.randrange(0, BOARD_SIZE))
このように、それぞれの位置情報を変数に代入することで、スイカとプレイヤーがどこにいるのかをプログラム上で管理できるようにします。
ゲームのループ処理
次に、プレイヤーとスイカの位置が一致するまで繰り返すwhile文によるループ処理を考えます。
この場合、
スイカのx座標とプレイヤーのx座標が違うという条件と、
スイカのy座標とプレイヤーのy座標が違うという条件の、
どちらか一方でもTRUEならば、
スイカとプレイヤーの位置が異なっていると言えます。
スイカとプレイヤーのx座標が違うという条件は、比較演算子の!= を使って、次のようにかけます。
while(suika_pos != player_pos)
Falseになれば、条件が成立しないために、ループは終了します。
スイカからプレイヤーまでの距離を求める
まず、スイカからプレイヤーまでの距離を求めて表示します。
スイカからプレイヤーまでの距離は、mathモジュールのsqrt()関数 で平方根を計算してみましょう。
def calc_distance(pos1,pos2):#仮引数名は何でもよいがわかりやすく
diff_x = pos1[0] - pos2[0]
diff_y = pos2[1] -pos2[1]
return math.sqrt(diff_x**2 + diff_y**2)
こんな感じです。
プレイヤーの移動処理
1.距離の表示と入力待機
while ループの中で、プレイヤーとスイカの距離を表示します。
次に、input() 関数でユーザーからのキー入力を待ち、どの方向に移動したいかを聞きます。
2.入力したキーに従って移動
n なら北 (y 座標を -1) に1マス移動します。
s なら南 (y 座標を +1) に移動します。
e なら東 (x 座標を +1) に移動し、
w なら西 (x 座標を -1) に移動します。
n, s, e, w 以外の文字が入力された場合は、移動せず、そのまま次のループに進みます。
これにより、ユーザーが指定した方向にプレイヤーの位置が更新されます。
「スイカ割りゲーム」を完成させ動かしてみる
早速ですが、動かしてみましょう。
import random
import math
BOARD_SIZE = 9
# スイカの位置のタプル
suika_pos = (random.randrange(0, BOARD_SIZE),
random.randrange(0, BOARD_SIZE))
# プレイヤーの位置のタプル
player_pos = (random.randrange(0, BOARD_SIZE),
random.randrange(0, BOARD_SIZE))
def calc_distance(pos1,pos2):#仮引数名は何でもよいがわかりやすく
diff_x = pos1[0] - pos2[0]
diff_y = pos2[0] - pos2[1]
return math.sqrt(diff_x**2 + diff_y**2)
while(suika_pos != player_pos):
distance = calc_distance(suika_pos,player_pos)
print('すいかまでの距離:', distance)
current_x, current_y = player_pos
c = input('n:北に移動 s:南に移動 e:東に移動 w:西に移動')
if c == 'n':
current_y -= 1
elif c== 's':
current_y += 1
elif c == 'e':
current_x += 1
elif c == 'w':
current_x -= 1
player_pos = (current_x, current_y)
print('すいかを割りました!')
#実際に動かしてみます
すいかまでの距離: 5.385164807134504
n:北に移動 s:南に移動 e:東に移動 w:西に移動s
すいかを割りました!
すいかまでの距離: 4.47213595499958
n:北に移動 s:南に移動 e:東に移動 w:西に移動s
すいかを割りました!
すいかまでの距離: 3.605551275463989
n:北に移動 s:南に移動 e:東に移動 w:西に移動s
すいかを割りました!
すいかまでの距離: 2.8284271247461903
n:北に移動 s:南に移動 e:東に移動 w:西に移動s
すいかを割りました!
すいかまでの距離: 2.23606797749979
n:北に移動 s:南に移動 e:東に移動 w:西に移動s
すいかを割りました!
すいかまでの距離: 2.0
n:北に移動 s:南に移動 e:東に移動 w:西に移動s
すいかを割りました!
すいかまでの距離: 2.23606797749979
n:北に移動 s:南に移動 e:東に移動 w:西に移動n
すいかを割りました!
すいかまでの距離: 2.0
n:北に移動 s:南に移動 e:東に移動 w:西に移動e
すいかを割りました!
すいかまでの距離: 1.0
n:北に移動 s:南に移動 e:東に移動 w:西に移動e
すいかを割りました!
こんな感じです。
とはいえ、ここまで全く関数を使わずに作ってきました。そうすると、私以外の人が見ると何がなにを意味しているか非常にわかりづらいものになっています。
関数を活用する
こちらは関数を使って作った全くおなじスイカゲームです。
関数名の、generate_position,move_positionが加わり、わかりやすくなったと思います。
import random
import math
BOARD_SIZE = 5 # ボードの初期サイズ
def generate_position(size):
# 0以上size未満の範囲で、x座標とy座標を生成する
x = random.randrange(0, size) # x座標
y = random.randrange(0, size) # y座標
return (x, y)
def calc_distance(pos1, pos2):
# 2点間の距離を求める
diff_x = pos1[0] - pos2[0]
diff_y = pos1[1] - pos2[1]
return math.sqrt(diff_x**2 + diff_y**2)
def move_position(direction, pos):
# direction にしたがって、posを移動する
current_x, current_y = pos
if direction == "n":
current_y = current_y - 1
elif direction == "s":
current_y = current_y + 1
elif direction == "w":
current_x = current_x - 1
elif direction == "e":
current_x = current_x + 1
return (current_x, current_y)
def suika_wari():
suika_pos = generate_position(BOARD_SIZE) # スイカの座標
player_pos = generate_position(BOARD_SIZE) # プレイヤーの座標
# スイカとプレイヤーの位置が異なる間、処理を繰り返す
while (suika_pos != player_pos):
# スイカとプレイヤーの距離を表示する
distance = calc_distance(player_pos, suika_pos)
print("スイカへの距離:", distance)
# キー入力に応じて、プレイヤーを移動する
c = input("n:北に移動 s:南に移動 e:東に移動 w:西に移動")
player_pos = move_position(c, player_pos)
print("スイカを割りました!")