3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Pythonでスイカ割りゲームを作ってみよう

Last updated at Posted at 2023-03-07

はじめに

昨年から「ゼロからのPython入門講座」「Python japan」サイト)を使ってPythonの勉強を行っています。
基本的な学習は終了して 「演習」 に進みましたので、独自に課題に取り組みたいと思います。

スイカ割りゲームを作ってみよう

演習 の最初の課題である「スイカ割りゲームを作ってみよう」に取り組みます。
課題の詳しい内容は、「スイカ割りゲームを作ってみよう」を参照してください。
ゲームを簡単に説明すると、下図の様なマス目のフィールドがあり、その何処かのマスにスイカがあるので、プレイヤー(自分)の位置を移動してスイカと同じマス目に入る(スイカを割った状態)と言うものです。
image.png
スイカの位置を推理するヒントとしては、スイカとプレイヤー間の距離を表示します。

実際に作ったプログラム

※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〕です。(キーは個人の趣味です :sweat:

プログラムを動かしてみる

image.png

スイカは割れていません。
自分の位置: (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) でした。
###########################################

少し、ゲームっぽくなったかな:question: :sweat:


2023年3月8日 追記

実際に作ったプログラム(改良版の改良)

上記の投稿の後、「ゼロからのPython入門講座」「リファクタリング - マジックナンバーを避ける」「適切なデータ型を使う」「関数を活用する」まで学習を進めました(「入門講座」を終了しました)ので、その結果を反映させます。
なお、「適切なデータ型を使う」については、独自のプログラミングで殆ど対応していました。:v::sweat_smile:
改良の要点としては、全体的な関数化とフィールドを動的に変更できる様にした事です。

# スイカ割りゲーム

# 算術用モジュール
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を変更する事により、suikaplayerの初期化の位置、playerの移動範囲も自動的に変更されています。

以上です。:wink:

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?