はじめに
昨年から「ゼロからのPython入門講座」(「Python japan」サイト)を使ってPythonの勉強を行っています。
基本的な学習は終了して 「演習」 に進みましたので、独自に課題に取り組みたいと思います。
スイカ割りゲームを作ってみよう
演習 の最初の課題である「スイカ割りゲームを作ってみよう」に取り組みます。
課題の詳しい内容は、「スイカ割りゲームを作ってみよう」を参照してください。
ゲームを簡単に説明すると、下図の様なマス目のフィールドがあり、その何処かのマスにスイカがあるので、プレイヤー(自分)の位置を移動してスイカと同じマス目に入る(スイカを割った状態)と言うものです。
スイカの位置を推理するヒントとしては、スイカとプレイヤー間の距離を表示します。
実際に作ったプログラム
※Google Colaboratory (Colab)の使い方については、「Python の学習を始めました」を御参照ください。
# スイカ割りゲーム
# 算術用モジュール
import math
# 乱数を使用する為のモジュール
import random
# 距離の計算関数
def calc_distance(suika, player):
diff_x = suika[0] - player[0]
diff_y = suika[1] - player[1]
return math.sqrt(diff_x**2 + diff_y**2)
# メイン処理
# スイカとプレイヤーの位置を初期化
suika = (random.randrange(0, 10), random.randrange(0, 10))
player = (random.randrange(0, 10), random.randrange(0, 10))
# スイカが割れるまで、キー入力を繰り返す。
while suika != player:
print("スイカは割れていません。")
print("自分の位置:", player)
print("スイカまでの距離:", calc_distance(suika, player))
key = input("←:a/↑:e or i/↓:c or m/→:l ")
if (key == "a") and (player[0] > 0):
player = (player[0] - 1, player[1])
elif (key == "e" or key == "i") and (player[1] > 0):
player = (player[0], player[1] - 1)
elif (key == "c" or key == "m") and (player[1] < 9):
player = (player[0], player[1] + 1)
elif (key == "l") and (player[0] < 9):
player = (player[0] + 1, player[1])
# ゲーム終了時の表示
print("###########################################")
print("おめでとう御座います。スイカは割れました。")
print("スイカの位置は", suika, "でした。")
print("###########################################")
正解については、「スイカ割りゲームを作ってみよう」を参照して頂くとして、上記は、私がほぼ独自(勝手)に作成したものです。「ゼロからのPython入門講座」の「Python初体験」~「タプルとコレクション」で学んだ知識で作る事ができました。
一部、ゲーム開始時にスイカとプレイヤーの位置をランダムに決める方法と、スイカとプレイヤーの距離を計測する方法は、「スイカ割りゲームを作ってみよう」の各項目を参考にさせて頂きました。
スイカとプレイヤーの位置(情報)を保持する変数には、タプルを使用しました。
ex. suika = (x軸の位置, y軸の位置)
スイカとプレイヤーの位置をランダムに決めるには、randrange()
関数を使用します。この関数を使用するにはrandom
モジュールを読み込む必要があります。
# 乱数を使用する為のモジュール
import random
:
# スイカとプレイヤーの位置を初期化
suika = (random.randrange(0, 10), random.randrange(0, 10))
player = (random.randrange(0, 10), random.randrange(0, 10))
スイカとプレイヤーの距離を計測するには、平方根の計算が必要なのでsqrt()
関数を使用します。この関数を使用するにはmath
モジュールを読み込む必要があります。
# 算術用モジュール
import math
:
# 距離の計算関数
def calc_distance(suika, player):
diff_x = suika[0] - player[0]
diff_y = suika[1] - player[1]
return math.sqrt(diff_x**2 + diff_y**2)
プレイヤーを動かすキーは、←(左):〔a〕
、↑(上):〔e〕
or〔i〕
、↓(下):〔c〕
or〔m〕
、→(右):〔l〕
です。(キーは個人の趣味です )
プログラムを動かしてみる
スイカは割れていません。
自分の位置: (8, 3)
スイカまでの距離: 5.0990195135927845
←:a/↑:e or i/↓:c or m/→:l a
スイカは割れていません。
自分の位置: (7, 3)
スイカまでの距離: 4.123105625617661
←:a/↑:e or i/↓:c or m/→:l a
スイカは割れていません。
自分の位置: (6, 3)
スイカまでの距離: 3.1622776601683795
←:a/↑:e or i/↓:c or m/→:l a
スイカは割れていません。
自分の位置: (5, 3)
スイカまでの距離: 2.23606797749979
←:a/↑:e or i/↓:c or m/→:l e
スイカは割れていません。
自分の位置: (5, 2)
スイカまでの距離: 2.0
←:a/↑:e or i/↓:c or m/→:l a
スイカは割れていません。
自分の位置: (4, 2)
スイカまでの距離: 1.0
←:a/↑:e or i/↓:c or m/→:l a
###########################################
おめでとう御座います。スイカは割れました。
スイカの位置は (3, 2) でした。
###########################################
実際に作ったプログラム(改良版)
一応、上記の様にプログラムは実行できたのですが、文字が表示されるだけで色気がないので少し改良してみました。
10×10のフィールドを、記号(+
,-
,|
)を使ってマトリクスで表示して、プレイヤーの位置を*
で示す様にします。
# スイカ割りゲーム
# 算術用モジュール
import math
# 乱数を使用する為のモジュール
import random
# 色々便利なモジュール
import IPython
# 距離の計算関数
def calc_distance(suika, player):
diff_x = suika[0] - player[0]
diff_y = suika[1] - player[1]
return math.sqrt(diff_x**2 + diff_y**2)
# マトリクスの表示関数
def create_matrix(position):
IPython.display.clear_output(True)
y = 0
print(" 0 1 2 3 4 5 6 7 8 9")
print(" +-+-+-+-+-+-+-+-+-+-+")
while y < 10:
x = 0
row = str(y) + " |"
while x < 10:
if x == position[0] and y == position[1]:
row = row + "*|"
else:
row = row + " |"
x = x + 1
print(row)
y = y + 1
print(" +-+-+-+-+-+-+-+-+-+-+")
# メイン処理
# スイカとプレイヤーの位置を初期化
suika = (random.randrange(0, 10), random.randrange(0, 10))
player = (random.randrange(0, 10), random.randrange(0, 10))
create_matrix(player)
# スイカが割れるまで、キー入力を繰り返す。
while suika != player:
create_matrix(player)
print("スイカは割れていません。")
print("自分の位置:", player)
print("スイカまでの距離:", calc_distance(suika, player))
key = input("←:a/↑:e or i/↓:c or m/→:l ")
if (key == "a") and (player[0] > 0):
player = (player[0] - 1, player[1])
elif (key == "e" or key == "i") and (player[1] > 0):
player = (player[0], player[1] - 1)
elif (key == "c" or key == "m") and (player[1] < 9):
player = (player[0], player[1] + 1)
elif (key == "l") and (player[0] < 9):
player = (player[0] + 1, player[1])
# ゲーム終了時の表示
create_matrix(player)
print("###########################################")
print("おめでとう御座います。スイカは割れました。")
print("スイカの位置は", suika, "でした。")
print("###########################################")
下記がマトリクス(プレイヤー)を表示する関数、create_matrix(position)
です。
引数のposition
には、プレイヤーの位置を示す変数をタプル((x軸の位置, y軸の位置)
)で指定します。
また、print()
関数だけで表示を繰り返すと、前に表示されていた文字がそのままとなってしまうので、clear_output()
関数を使用して前に表示されていた画面をクリアする様にします。この関数を使用するにはIPython
モジュールを読み込む必要があります。
clear_output()
関数については、カックさんが公開している「kakakakakku blog」の「Jupyter Notebook で clear_output() を使って定期的に表示を更新する」を参考にさせて頂きました。
# 色々便利なモジュール
import IPython
:
# マトリクスの表示関数
def create_matrix(position):
IPython.display.clear_output(True)
y = 0
print(" 0 1 2 3 4 5 6 7 8 9")
print(" +-+-+-+-+-+-+-+-+-+-+")
while y < 10:
x = 0
row = str(y) + " |"
while x < 10:
if x == position[0] and y == position[1]:
row = row + "*|"
else:
row = row + " |"
x = x + 1
print(row)
y = y + 1
print(" +-+-+-+-+-+-+-+-+-+-+")
プログラムを動かしてみる(改良版)
実行方法は前回と同じです。
0 1 2 3 4 5 6 7 8 9
+-+-+-+-+-+-+-+-+-+-+
0 | | | | | | | | | | |
1 | | | | | | | | | | |
2 | | | | | | | | | | |
3 | | | | | | | | | | |
4 | | | |*| | | | | | |
5 | | | | | | | | | | |
6 | | | | | | | | | | |
7 | | | | | | | | | | |
8 | | | | | | | | | | |
9 | | | | | | | | | | |
+-+-+-+-+-+-+-+-+-+-+
スイカは割れていません。
自分の位置: (3, 4)
スイカまでの距離: 2.8284271247461903
←:a/↑:e or i/↓:c or m/→:l
0 1 2 3 4 5 6 7 8 9
+-+-+-+-+-+-+-+-+-+-+
0 | | | | | | | | | | |
1 | | | | | | | | | | |
2 | | | | | | | | | | |
3 | | | | | | | | | | |
4 | | | | |*| | | | | |
5 | | | | | | | | | | |
6 | | | | | | | | | | |
7 | | | | | | | | | | |
8 | | | | | | | | | | |
9 | | | | | | | | | | |
+-+-+-+-+-+-+-+-+-+-+
スイカは割れていません。
自分の位置: (4, 4)
スイカまでの距離: 2.23606797749979
←:a/↑:e or i/↓:c or m/→:l l
0 1 2 3 4 5 6 7 8 9
+-+-+-+-+-+-+-+-+-+-+
0 | | | | | | | | | | |
1 | | | | | | | | | | |
2 | | | | | | | | | | |
3 | | | | | | | | | | |
4 | | | | | |*| | | | |
5 | | | | | | | | | | |
6 | | | | | | | | | | |
7 | | | | | | | | | | |
8 | | | | | | | | | | |
9 | | | | | | | | | | |
+-+-+-+-+-+-+-+-+-+-+
スイカは割れていません。
自分の位置: (5, 4)
スイカまでの距離: 2.0
←:a/↑:e or i/↓:c or m/→:l c
0 1 2 3 4 5 6 7 8 9
+-+-+-+-+-+-+-+-+-+-+
0 | | | | | | | | | | |
1 | | | | | | | | | | |
2 | | | | | | | | | | |
3 | | | | | | | | | | |
4 | | | | | | | | | | |
5 | | | | | |*| | | | |
6 | | | | | | | | | | |
7 | | | | | | | | | | |
8 | | | | | | | | | | |
9 | | | | | | | | | | |
+-+-+-+-+-+-+-+-+-+-+
スイカは割れていません。
自分の位置: (5, 5)
スイカまでの距離: 1.0
←:a/↑:e or i/↓:c or m/→:l c
0 1 2 3 4 5 6 7 8 9
+-+-+-+-+-+-+-+-+-+-+
0 | | | | | | | | | | |
1 | | | | | | | | | | |
2 | | | | | | | | | | |
3 | | | | | | | | | | |
4 | | | | | | | | | | |
5 | | | | | | | | | | |
6 | | | | | |*| | | | |
7 | | | | | | | | | | |
8 | | | | | | | | | | |
9 | | | | | | | | | | |
+-+-+-+-+-+-+-+-+-+-+
###########################################
おめでとう御座います。スイカは割れました。
スイカの位置は (5, 6) でした。
###########################################
少し、ゲームっぽくなったかな
2023年3月8日 追記
実際に作ったプログラム(改良版の改良)
上記の投稿の後、「ゼロからのPython入門講座」の「リファクタリング - マジックナンバーを避ける」「適切なデータ型を使う」「関数を活用する」まで学習を進めました(「入門講座」を終了しました)ので、その結果を反映させます。
なお、「適切なデータ型を使う」については、独自のプログラミングで殆ど対応していました。
改良の要点としては、全体的な関数化とフィールドを動的に変更できる様にした事です。
# スイカ割りゲーム
# 算術用モジュール
import math
# 乱数を使用する為のモジュール
import random
# 色々便利なモジュール
import IPython
# 距離の計算関数
def calc_distance(suika, player):
diff_x = suika[0] - player[0]
diff_y = suika[1] - player[1]
return math.sqrt(diff_x**2 + diff_y**2)
# マトリクスの表示関数
def create_matrix(position, keisen, field):
IPython.display.clear_output(True)
y = 0
print(keisen[0])
print(keisen[1])
while y < field[1]:
x = 0
row = str(y) + " |"
while x < field[0]:
if x == position[0] and y == position[1]:
row = row + "*|"
else:
row = row + " |"
x = x + 1
print(row)
y = y + 1
print(keisen[1])
# X軸罫線の作成関数
def create_keisen_x(size):
x = 0
retsu_bango = " "
keisen_x = " +"
while x < size:
retsu_bango = retsu_bango + str(x) + " "
keisen_x = keisen_x + "-+"
x = x + 1
return (retsu_bango, keisen_x)
# 位置の初期化関数
def generate_position(size_x, size_y):
x = random.randrange(0, size_x)
y = random.randrange(0, size_y)
return (x, y)
# プレイヤーの移動関数
def move_position(key, position, field):
x, y = position # タプルのアンパック
if (key == "a") and (x > 0):
x = x - 1
elif (key == "e" or key == "i") and (y > 0):
y = y - 1
elif (key == "c" or key == "m") and (y < (field[1] - 1)):
y = y + 1
elif (key == "l") and (x < (field[0] - 1)):
x = x + 1
return (x, y)
# メイン処理
def suika_wari(field):
# スイカとプレイヤーの位置を初期化
suika = generate_position(field[0], field[1])
player = generate_position(field[0], field[1])
keisen = create_keisen_x(field[0])
create_matrix(player, keisen, field)
# スイカが割れるまで、キー入力を繰り返す。
while suika != player:
create_matrix(player, keisen, field)
print("スイカは割れていません。")
print("自分の位置:", player)
print("スイカまでの距離:", calc_distance(suika, player))
key = input("←:a/↑:e or i/↓:c or m/→:l ")
player = move_position(key, player, field)
# ゲーム終了時の表示
create_matrix(player, keisen, field)
print("###########################################")
print("おめでとう御座います。スイカは割れました。")
print("スイカの位置は", suika, "でした。")
print("###########################################")
# フィールドのサイズ (x: 1~10, y: 1~10)で指定する。
FIELD = (10, 10)
# メイン処理の実行
suika_wari(FIELD)
create_keisen_x(size)
関数で、フィールドのX軸の列番号ならびに罫線を作成(タプル)します。
作成する列数は、引数size
で指定します。
# X軸罫線の作成関数
def create_keisen_x(size):
x = 0
retsu_bango = " "
keisen_x = " +"
while x < size:
retsu_bango = retsu_bango + str(x) + " "
keisen_x = keisen_x + "-+"
x = x + 1
return (retsu_bango, keisen_x)
create_keisen_x(size)
関数の処理結果を次のプログラムで確認します。(size
に1~10を指定して作成された罫線を表示します)
# create_keisen_x()関数の動作確認用処理
i = 1
while i <= 10:
keisen = create_keisen_x(i)
print("i:", i)
print(keisen[0])
print(keisen[1])
i = i + 1
処理結果は次の様になります。
i: 1
0
+-+
i: 2
0 1
+-+-+
i: 3
0 1 2
+-+-+-+
i: 4
0 1 2 3
+-+-+-+-+
i: 5
0 1 2 3 4
+-+-+-+-+-+
i: 6
0 1 2 3 4 5
+-+-+-+-+-+-+
i: 7
0 1 2 3 4 5 6
+-+-+-+-+-+-+-+
i: 8
0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+
i: 9
0 1 2 3 4 5 6 7 8
+-+-+-+-+-+-+-+-+-+
i: 10
0 1 2 3 4 5 6 7 8 9
+-+-+-+-+-+-+-+-+-+-+
列番号も罫線も正しく作成されています。
ゲーム(プログラム)を実行する前にFIELD
の値をを変更しておくと、フィールドの大きさが自動的に変更されます。
FIELD = (10, 5)
0 1 2 3 4 5 6 7 8 9
+-+-+-+-+-+-+-+-+-+-+
0 | | | | | | | | | | |
1 | | | |*| | | | | | |
2 | | | | | | | | | | |
3 | | | | | | | | | | |
4 | | | | | | | | | | |
+-+-+-+-+-+-+-+-+-+-+
スイカは割れていません。
自分の位置: (3, 1)
スイカまでの距離: 2.23606797749979
←:a/↑:e or i/↓:c or m/→:l
FIELD = (3, 8)
0 1 2
+-+-+-+
0 | |*| |
1 | | | |
2 | | | |
3 | | | |
4 | | | |
5 | | | |
6 | | | |
7 | | | |
+-+-+-+
スイカは割れていません。
自分の位置: (1, 0)
スイカまでの距離: 4.0
←:a/↑:e or i/↓:c or m/→:l
勿論、FIELD
を変更する事により、suika
とplayer
の初期化の位置、player
の移動範囲も自動的に変更されています。
以上です。