再び、Pythonで作るビンゴゲーム

はじめに

以前、以下のような記事を書いた。

今読み返してみると、わかりにくかったので、Pythonの実務経験を経た今の私が解説していこう。

今日も指と腕の痛みを理由にして会社を休んでいる。

もちろん実際に痛みが残っている。

しかし、家だと休憩の時間をたっぷりとれる。

そのため、腕と指を労わりながらブログを書いていこう。

全ソースコード

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()

実行結果

hiroki@shibatahiroshitakanoiMac python %  cd /Users/hiroki/Downloads/python ; /usr/bin/env /usr/local/bin/python3.12 /Users/hiroki/.vscode/extensions/ms-python.debugpy-2025.10.0-darwin-x64/bundled/libs/debugpy/adapter/../../debugpy/launcher 58612 -- /Users/
hiroki/Downloads/python/bingo.py 

B  I  N  G  O
 8 29 43 59 75 
 7 18 40 60 62 
 5 17 FREE 58 67 
11 24 41 49 69 
 4 21 35 51 64 
Enterで次の番号を引きます...
引かれた番号: 57

B  I  N  G  O
 8 29 43 59 75 
 7 18 40 60 62 
 5 17 FREE 58 67 
11 24 41 49 69 
 4 21 35 51 64 
Enterで次の番号を引きます...

途中省略

引かれた番号: 29

B  I  N  G  O
 8 29 43 59 75 
 7 18 40 60 62 
 5 17 FREE 58 67 
11 24 41 49 69 
 4 21 35 51 64 
ビンゴ!おめでとうございます!

hiroki@shibatahiroshitakanoiMac python % 

仕様

BINGOの各列に指定範囲内のランダムな数値を5つずつ配置(B : 1~15、I : 16~30、N : 31~45、G : 46~60、O : 61~75)

中央マスは、「FREE」

引かれた番号がカード上にあればマーク

横、縦、斜めのいずれか一列すべてがマーク済みでビンゴ!

Enterキー押下でランダムに番号を引き、ビンゴになるまで繰り返す

同じ番号が2回引かれないように制御

フローチャート

スタート→カードとマーク状態の初期化

カードの表示

プレイヤーにカードを引いてもらう→引かれた番号をマークする→カードを表示

ビンゴ判定

ビンゴの場合

ビンゴではない場合

スタート→カードとマーク状態の初期化

play_bingo関数を実行

# ゲームを開始
play_bingo()

BingoCardクラスのインスタンス生成

def play_bingo():
    card = BingoCard()

cardの初期化

class BingoCard:
    def __init__(self):
        self.card = self.generate_card()
    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

randomモジュール使用

import random

sampleメソッド

random.sample(range(1, 15), 5)
[1, 2, 7, 12, 3]
random.sample(range(1, 15), 3)
[2, 7, 13]

戻り値のcard

import pprint 
pprint.pprint(card)
[[5, 13, 12, 3, 14],
 [23, 20, 26, 25, 30],
 [38, 33, 'FREE', 42, 44],
 [56, 54, 49, 57, 59],
 [70, 67, 63, 68, 62]]

毎回異なる

import pprint 
pprint.pprint(card)
[[1, 2, 12, 11, 6],
 [19, 21, 30, 20, 29],
 [35, 40, 'FREE', 37, 43],
 [53, 59, 49, 58, 57],
 [68, 65, 75, 66, 72]]

cardインスタンスを生成すると、cardが初期化されている

card = BingoCard()
card.card
[[15, 10, 4, 7, 6], [23, 21, 22, 26, 27], [36, 32, 'FREE', 39, 45], [57, 46, 52, 60, 49], [75, 72, 73, 74, 69]]

こちらも毎回異なる

card = BingoCard()
card.card
[[6, 14, 7, 9, 3], [19, 21, 25, 22, 18], [39, 43, 'FREE', 37, 32], [57, 49, 56, 46, 60], [71, 75, 73, 64, 72]]

数字のマーク状態を保持

self.marked = [[False] * 5 for _ in range(5)]

リスト内包表記

import pprint 
pprint.pprint([[False] * 5 for _ in range(5)] )
[[False, False, False, False, False],
 [False, False, False, False, False],
 [False, False, False, False, False],
 [False, False, False, False, False],
 [False, False, False, False, False]]
pprint.pprint([[False] * 3 for _ in range(5)] )
[[False, False, False],
 [False, False, False],
 [False, False, False],
 [False, False, False],
 [False, False, False]]
pprint.pprint([[a] * 3 for a in range(5)] ) 
[[0, 0, 0], [1, 1, 1], [2, 2, 2], [3, 3, 3], [4, 4, 4]]
pprint.pprint([_ * 3 for _ in range(5)] ) 
[0, 3, 6, 9, 12]

カードの表示

    card.display_card()
    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()

BINGO

        print("\nB  I  N  G  O")
hiroki@shibatahiroshitakanoiMac python %  cd /Users/hiroki/Downloads/python ; /usr/bin/env /usr/local/bin/python3.12 /Users/hiroki/.vscode/extensions/ms-python.debugpy-2025.10.0-darwin-x64/bundled/libs/debugpy/adapter/../../debugpy/launcher 58870 -- /Users/hiroki/Downloads/
python/bingo.py 

B  I  N  G  O

外側for文

        for i in range(5):

内側for文

            for j in range(5):

value

                value = self.card[j][i]

self.card

import pprint
pprint.pprint(self.card)
[[3, 9, 8, 2, 7],
 [27, 29, 30, 20, 18],
 [38, 44, 'FREE', 34, 39],
 [50, 55, 53, 46, 54],
 [65, 67, 66, 70, 69]]

self.card[4][0]

self.card[4][0]
65

数値の場合、右寄せ、右に空白を開けて表示

                if isinstance(value, int):
                    print(f"{value:2d}", end=" ")
print(f"{self.card[0][0]:2d}", end=" ")
 3 
print(self.card[0][0])
3

数値以外(FREE)の場合、右寄せ(右寄せしなくても結果は変わらない)

                else:
                    print(f"{value:>2}", end=" ")

プレイヤーにカードを引いてもらう→引かれた番号をマークする→カードを表示

    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}")
hiroki@shibatahiroshitakanoiMac python %  cd /Users/hiroki/Downloads/python ; /usr/bin/env /usr/local/bin/python3.12 /Users/hiroki/.vscode/extensions/ms-python.debugpy-2025.10.0-darwin-x64/bundled/libs/debugpy/adapter/../../debugpy/launcher 59249 -- /Users/hiroki/Downloads/
python/bingo.py 

B  I  N  G  O
 9 28 42 53 67 
 8 16 40 54 61 
 5 25 FREE 46 70 
 2 30 43 57 69 
13 29 32 51 75 
Enterで次の番号を引きます...

Enterを押す

B  I  N  G  O
 9 28 42 53 67 
 8 16 40 54 61 
 5 25 FREE 46 70 
 2 30 43 57 69 
13 29 32 51 75 
Enterで次の番号を引きます...
引かれた番号: 9

引かれた番号をマークする

        card.mark_number(number)
    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
import pprint
pprint.pprint(self.marked)
[[False, False, False, False, False],
 [False, False, False, False, False],
 [False, False, False, False, False],
 [False, False, False, False, False],
 [False, False, False, False, False]]

pprint.pprint(self.marked)
[[True, False, False, False, False],
 [False, False, False, False, False],
 [False, False, False, False, False],
 [False, False, False, False, False],
 [False, False, False, False, False]]

カードを表示

        card.display_card()
B  I  N  G  O
 9 28 42 53 67 
 8 16 40 54 61 
 5 25 FREE 46 70 
 2 30 43 57 69 
13 29 32 51 75 

ビンゴ判定

        if card.check_bingo():
            print("ビンゴ!おめでとうございます!")
            break
    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

allで判定

all([True, True])
True
all([True, True, False])
False
all(self.marked[i])
False
self.marked[i] # i = 0(初回ループ)
[True, False, False, False, False]

ビンゴではない

card.check_bingo()
False

While文の最初に戻る

ビンゴの場合は、「ビンゴ!おめでとうございます!」を出力

B  I  N  G  O
 2 18 38 48 69 
 5 29 37 59 62 
 7 19 FREE 52 73 
 8 24 34 51 64 
14 22 42 53 72 
ビンゴ!おめでとうございます!


hiroki@shibatahiroshitakanoiMac python % 

コメントを残す

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