Pythonで作る「ビンゴゲーム」

はじめに

chat GPTの力も借りつつ、Pythonで「ビンゴゲーム」を作ってみた。

ビンゴゲームの仕様

スクリプトを実行すると、以下の画面が表示される。

Enterを押すと、引かれた番号が表示される。

何回かEnterを押すと、最終的に以下の画面になってゲームが終了する。

処理の順番

大きく分けて以下の順番で処理が進んでいく。

  1. play_bingoメソッドが呼ばれる
  2. BingoCardクラスのインスタンスを生成し、変数cardに格納
  3. display_cardメソッドを使用して、BINGOカードを表示
  4. Enterを押すと次の番号を引き、引かれた番号を表示する
  5. mark_numberメソッドで、カードの数字に穴をあける
  6. 再び、BINGOカードを表示
  7. ビンゴかどうかをcheck_bingoメソッドで判定し、ビンゴの場合はメッセージを表示

2~7は、play_bingoメソッド内で行われる。display_cardメソッド、mark_numberメソッド、check_bingoメソッドは、BingoCardクラスで定義されている。関数、クラスとインスタンスの概念を理解していることが試されるプログラムだ。

1. play_bingoメソッドが呼ばれる

順番に見ていこう。一番下のplay_bingo()で、play_bingoメソッドを呼び出している。呼び出し元のすぐ上に、play_bingoメソッドが定義されている。

def play_bingo():
    card = BingoCard()
    card.display_card()

    drawn_numbers = set()
    while True:
        input("Enterで次の番号を引きます...")
        number = random.randint(1, 75)
        while number in drawn_numbers:  # 重複しないようにする
            number = random.randint(1, 75)
        drawn_numbers.add(number)

        print(f"引かれた番号: {number}")
        card.mark_number(number)
        card.display_card()

        if card.check_bingo():
            print("ビンゴ!おめでとうございます!")
            break

# ゲームを開始
play_bingo()

2. BingoCardクラスのインスタンスを生成し、変数cardに格納

play_bingoメソッドの以下の部分で、BingoCardクラスのインスタンスを生成している。これで、cardという変数に、BingoCardクラスの情報が入ったということになる。

card = BingoCard()

そして、一番重要なBingoCardクラスは以下のようになっている。関数の中身は、後で取り上げるため、省略する。ここでは、どんな名前の関数が定義されているかだけ確認して欲しい。ただし、コンストラクタ(__init__メソッド)は、インスタンス生成時に実行されるメソッドのため、中身も残しておく。ここでは、cardとmarkedという変数の初期化を行なっている。

import random

class BingoCard:
    def __init__(self):
        self.card = self.generate_card()
        self.marked = [[False] * 5 for _ in range(5)]  # 数字のマーク状態を保持

    def generate_card(self):
        # 各列 (B, I, N, G, O) ごとに範囲内のランダムな数字を選ぶ
        card = []
        ranges = [(1, 15), (16, 30), (31, 45), (46, 60), (61, 75)]
        for start, end in ranges:
            column = random.sample(range(start, end + 1), 5)
            card.append(column)

        card[2][2] = "FREE"  # 中央のフリー枠
        return card

    def mark_number(self, number):
  

    def check_bingo(self):
  

    def display_card(self):
 

コンストラクタのcardには、generate_cardメソッドの戻り値がセットされる。generate_cardでは、cardのリストに、B列〜O列のランダムな数字5つをそれぞれ代入している。card = [[4, 3, 6, 12, 15], [23, 20, 17, 18, 30], [31, 33, FREE, 44, 45,], [47, 60, 55, 51, 57], [61, 65, 75, 71, 74]]のような戻り値になる。

for文では、タプルが入っているリストから、startとendという形で値を取り出している。randomモジュールのsampleメソッドを使うために、最初の行で「import random」を行なっている。sampleメソッドでは、例えば、range(1, 16)からランダムに5つの値を生成してリストに格納している。そして、appendメソッドで、cardにリストを追加している。

3. display_cardメソッドを使用して、BINGOカードを表示

display_cardメソッドの処理を見ていこう。まずは、ビンゴカードの列名である「BINGO」を出力する。Bの前についている「\n」は、改行を示している。これがあることで、出力結果が見やすくなる。二重for文では、0から4までの数値を順に返す範囲オブジェクトを、iとjに代入している。「2. BingoCardクラスのインスタンスを生成し、変数cardに格納」で解説したcardにself.cardアクセスして、valueに数字を一つずつ代入している。

class BingoCard:

    def display_card(self):
        print("\nB  I  N  G  O")
        for i in range(5):
            for j in range(5):
                value = self.card[j][i]  # 列ごとに表示
                if isinstance(value, int):
                    print(f"{value:2d}", end=" ")
                else:
                    print(f"{value:>2}", end=" ")
            print()

if文では、valueが数値型かどうかを判定し、真の場合は、数値に二桁分のスペースを確保し、偽の場合は、二桁右寄せにフォーマットしている。これで、ビンゴカードの表示が整う。なお、end=” “があることで、隣接するビンゴカードの空間が開く。

4. Enterを押すと次の番号を引き、引かれた番号を表示する

以下の赤字部分について解説していく。draen_numbersは、重複のないデータ。numberには、randomモジュールのrandint関数に1, 75を渡すことで、1以上75以下のランダムな数字が入る。drawn_numbersにnumberを引数にしたadd関数を使用することで、引かれた番号が格納される。重複を防ぐために、while文で、drawn_numbersにすでにある値の場合は、numberを再生成している。

def play_bingo():
    card = BingoCard()
    card.display_card()

    drawn_numbers = set()
    while True:
        input("Enterで次の番号を引きます...")
        number = random.randint(1, 75)
        while number in drawn_numbers:  # 重複しないようにする
            number = random.randint(1, 75)
        drawn_numbers.add(number)

        print(f"引かれた番号: {number}")
        card.mark_number(number)
        card.display_card()

        if card.check_bingo():
            print("ビンゴ!おめでとうございます!")
            break

# ゲームを開始
play_bingo()

5. mark_numberメソッドで、カードの数字に穴をあける

次に、cardクラスのmark_numberメソッドが、引数numberを渡された状態で呼ばれる。

for文の二重ループで、cardの行と列を指定して値を取り出し、引かれた値と同じであれば、markedの該当箇所をTrueにしている。cardとmarkedは、BingoCardクラスのコンストラクタで初期化されているので、「2. BingoCardクラスのインスタンスを生成し、変数cardに格納」を再確認していただきたい。

class BingoCard:

    def mark_number(self, number):
        for i in range(5):
            for j in range(5):
                if self.card[i][j] == number:
                    self.marked[i][j] = True

6. 再び、BINGOカードを表示

引かれた番号がマークされた後は、display_cardが再び呼び出されて、ビンゴカードが表示される。

def play_bingo():
    card = BingoCard()
    card.display_card()

    drawn_numbers = set()
    while True:
        input("Enterで次の番号を引きます...")
        number = random.randint(1, 75)
        while number in drawn_numbers:  # 重複しないようにする
            number = random.randint(1, 75)
        drawn_numbers.add(number)

        print(f"引かれた番号: {number}")
        card.mark_number(number)
        card.display_card()

        if card.check_bingo():
            print("ビンゴ!おめでとうございます!")
            break

# ゲームを開始
play_bingo()

7. ビンゴかどうかをcheck_bingoメソッドで判定し、ビンゴの場合はメッセージを表示

check_bingoメソッドは、真偽値を返す関数。最初はFalseだが、条件を満たすとTrueを返す。この条件は、行と列、左上から右下と右上から左下がすべて埋まっているかどうか。一つでもビンゴがあったら、Trueが返る。そしたら、play_bingoメソッドのif文内が実行される。

def play_bingo():
  
        if card.check_bingo():
            print("ビンゴ!おめでとうございます!")
            break

# ゲームを開始
play_bingo()
class BingoCard:

    def check_bingo(self):
        # 行と列でビンゴかどうかをチェック
        for i in range(5):
            if all(self.marked[i]):  # 行のビンゴ
                return True
            if all([self.marked[j][i] for j in range(5)]):  # 列のビンゴ
                return True

        # 対角線のビンゴをチェック
        if all([self.marked[i][i] for i in range(5)]):  # 左上から右下
            return True
        if all([self.marked[i][4 - i] for i in range(5)]):  # 右上から左下
            return True

        return False

全てのソースコード

import random

class BingoCard:
    def __init__(self):
        self.card = self.generate_card()
        self.marked = [[False] * 5 for _ in range(5)]  # 数字のマーク状態を保持

    def generate_card(self):
        # 各列 (B, I, N, G, O) ごとに範囲内のランダムな数字を選ぶ
        card = []
        ranges = [(1, 15), (16, 30), (31, 45), (46, 60), (61, 75)]
        for start, end in ranges:
            column = random.sample(range(start, end + 1), 5)
            card.append(column)

        card[2][2] = "FREE"  # 中央のフリー枠
        return card

    def mark_number(self, number):
        for i in range(5):
            for j in range(5):
                if self.card[i][j] == number:
                    self.marked[i][j] = True

    def check_bingo(self):
        # 行と列でビンゴかどうかをチェック
        for i in range(5):
            if all(self.marked[i]):  # 行のビンゴ
                return True
            if all([self.marked[j][i] for j in range(5)]):  # 列のビンゴ
                return True

        # 対角線のビンゴをチェック
        if all([self.marked[i][i] for i in range(5)]):  # 左上から右下
            return True
        if all([self.marked[i][4 - i] for i in range(5)]):  # 右上から左下
            return True

        return False

    def display_card(self):
        print("\nB  I  N  G  O")
        for i in range(5):
            for j in range(5):
                value = self.card[j][i]  # 列ごとに表示
                if isinstance(value, int):
                    print(f"{value:2d}", end=" ")
                else:
                    print(f"{value:>2}", end=" ")
            print()

def play_bingo():
    card = BingoCard()
    card.display_card()

    drawn_numbers = set()
    while True:
        input("Enterで次の番号を引きます...")
        number = random.randint(1, 75)
        while number in drawn_numbers:  # 重複しないようにする
            number = random.randint(1, 75)
        drawn_numbers.add(number)

        print(f"引かれた番号: {number}")
        card.mark_number(number)
        card.display_card()

        if card.check_bingo():
            print("ビンゴ!おめでとうございます!")
            break

# ゲームを開始
play_bingo()

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

投稿ID : 25642