元のソースコードでは、スイカとプレイヤーの位置を、x座標とy座標それぞれ別々の変数として保持しています。
suika_x = random.randrange(0, BOARD_SIZE) # スイカのx座標
suika_y = random.randrange(0, BOARD_SIZE) # スイカのy座標
player_x = random.randrange(0, BOARD_SIZE) # プレイヤーのx座標
player_y = random.randrange(0, BOARD_SIZE) # プレイヤーのy座標
しかし、スイカやプレイヤーの座標は本来一つの情報ですので、x座標とy座標で2つの変数に分ける意味はありません。こういった場合、
(x座標, y座標)
という形式の、2つの整数からなる タプル にするように、リファクタリング するのが良いでしょう。
ここでは、タプルを利用する練習も兼ねて、位置情報をタプルに置き換えていきましょう。
座標の生成方法を変更¶
現在、スイカとプレイヤーの座標は、x座標とy座標に分けて、四つの変数に代入されています。これを、suika_pos
、player_pos
という名前の、タプルを参照する変数に変更しましょう。
import random
# スイカの位置のタプル
suika_pos = (random.randrange(0, BOARD_SIZE), random.randrange(0, BOARD_SIZE))
# プレイヤーの位置のタプル
player_pos = (random.randrange(0, BOARD_SIZE), random.randrange(0, BOARD_SIZE))
変数名 suika_pos
と player_pos
の pos
は、英語の 位置(position)
から取っています。
生成した値を表示してみましょう。
print("スイカの位置は", suika_pos)
print("プレイヤーの位置は", player_pos)
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)
この関数を呼び出すときは、四つの数字を指定します。
x1 = 10
y1 = 10
x2 = 20
y2 = 20
calc_distance(x1, y1, x2, y2) # 座標 (10, 10) から 座標 (20, 20) までの距離を計算する
この関数の 引数 を、別々に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)
呼び出すときは、次のようにタプルを指定します。
pos1 = (10, 10)
pos2 = (20, 20)
calc_distance(pos1, pos2) # 座標 (10, 10) から 座標 (20, 20) までの距離を計算する
データを2つのタプルにまとめることで、2つの位置のx座標とy座標の数値のまとまりが明確になり、シンプルになっているのがわかると思います。
while文を修正¶
ゲームの中心となるwhile
文の、ループ継続条件は次のようになっています。
# スイカとプレイヤーの位置が異なる間、処理を繰り返す
while (suika_x != player_x) or (suika_y != player_y):
...
修正前の書き方では、スイカとプレイヤーの位置を、x座標とy座標で別々に比較しています。
修正後は、座標をタプルに変更していますから、次のように、2つの値を一度に比較できます。
# 修正後
while suika_pos != player_pos:
...
x座標とy座標を別々に比較するのに比べて、すっきりとしていますね。2つのタプルを ==
を比較する方法については、タプルの比較演算子 を復習しておいてください。
プレイヤー移動処理を変更¶
元の処理では、キー入力に応じてプレイヤーを移動する処理は次のようになっています。
# キー入力に応じて、プレイヤーを移動する
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
タプルオブジェクトの操作 で説明したように、タプルは、リスト と違って要素を別々に変更できません。
プレイヤーの位置を player_pos
から取り出して、座標を更新してから新しいタプルを作るように修正します。
# 修正後
# キー入力に応じて、プレイヤーを移動する
c = input("n:北に移動 s:南に移動 e:東に移動 w:西に移動")
current_x, current_y = player_pos
if c == "n":
current_y = current_y - 1
elif c == "s":
current_y = current_y + 1
elif c == "w":
current_x = current_x - 1
elif c == "e":
current_x = current_x + 1
player_pos = (current_x, current_y)
タプルのアンパック¶
この処理では、次のように変数 player_pos
からx座標とy座標の値を取り出しています。
current_x, current_y = player_pos
これは、コレクションのアンパック で紹介した、タプルをアンパックした代入文です。player_pos
の最初の要素と次の要素を、それぞれ変数 current_x
と current_y
に代入しています。
まとめると¶
以上で、スイカとプレイヤーの座標をタプルとして保持するための修正が終わりました。最終的なプログラムは次のようになります。
import random
import math
BOARD_SIZE = 5 # ボードの初期サイズ
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)
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 (suika_pos != player_pos):
# スイカとプレイヤーの距離を表示する
distance = calc_distance(suika_pos, player_pos)
print("スイカへの距離:", distance)
# キー入力に応じて、プレイヤーを移動する
c = input("n:北に移動 s:南に移動 e:東に移動 w:西に移動")
current_x, current_y = player_pos
if c == "n":
current_y = current_y - 1
elif c == "s":
current_y = current_y + 1
elif c == "w":
current_x = current_x - 1
elif c == "e":
current_x = current_x + 1
player_pos = (current_x, current_y)
print("スイカを割りました!")