📝⭐回憶殺 python實現 賓果(Bingo)連線遊戲 可線上玩

閱讀時間約 23 分鐘
raw-image

賓果的遊戲描述


在一個5x5的方陣上隨機填充1~25的數字。

玩家(使用者) 和 電腦(AI)輪流叫一個號碼最先占據一整條直線連線的獲勝


就像小時候玩的bingo 賓果連線遊戲一樣!

(可以是占據兩條對角線的其中一條,可以是占據水平直線,可以是占據垂直直線)


賓果的遊戲場景:


分別為玩家和電腦建立兩個5x5的板子,各自隨機填充1~25的數字。


玩家(人) 和 電腦(AI)輪流叫一個號碼。

號碼必須是有效號碼1~25之間的數字,而且不可重複。

被叫到的號碼就在各自的板子上做一個記號。


先占據一整條直線連線的那一方宣告獲勝。



上層思考邏輯與框架


建立遊戲場景,分別給電腦和玩家建立兩張5x5的板子,填充1~25的隨機數


玩家 和 電腦AI輪流叫號

號碼必須是有效號碼1~25之間的數字,而且不可重複。

被叫到的號碼就在個子的板子上做一個記號


每回合都會檢查是否有其中一方獲勝。如果還沒有,就繼續輪流叫號的過程。


先占據一整條直線連線的那一方宣告獲勝。


中層功能元件分析


1.怎麼建立5x5的遊戲版,並且填充隨機數?


透過List comprehension列表生成式來建立固定大小的5x5 二維陣列,

並且使用python內建的random.sample()方法,
來對1~25的數字隨機採樣,進行填充。


class BingoCard:

numbers_drawn = set()

def __init__(self, name):
self.name = name

# 5x5 賓果遊戲的數字版
self.card = self.generate_card()

# 記錄這個位置的數字有沒有被叫過
self.marked = [[False for _ in range(5)] for _ in range(5)]

def generate_card(self):

# 隨機生成1~25的數字,並且填充​
numbers = random.sample(range(1, 26), 25)
card = [numbers[i*5:(i+1)*5] for i in range(5)]

2.如何讓使用者扮演的玩家叫號?


讓玩家透過鍵盤輸入1~25之間的數字。

為了確保遊戲正確執行,會檢查是否為1~25區間內的數字,而且不可重複


如果有非法輸入,會輸出提示訊息,要求使用者重新輸入!

背後的檢查機制依賴python內建的try ... except ... 的例外處理。


class HumanPlayer(Agent):

def choose_number(self, numbers_drawn):

number = -1
while True:

try:
number = int( input("Please select a number: ") )
except:
print("Invalid input!")
continue

if number == -1 or ( (25 >= number >= 1) and(number not in numbers_drawn) ):
break

elif number in numbers_drawn:
print("Repeated!")
else:
print("Invalid input! Number out of Range 1 ~ 25.")

return number


3.如何讓電腦扮演的AI叫號?


讓電腦掃描當下的狀態,
計算哪個位置的連線數目最多,優先選擇連線數目最多的那個數字,提高對戰勝率


class SmartAIPlayer(Agent):

def choose_number(self, numbers_drawn):
best_choice = -1
max_marks = -1

# Greedy strategy, select the number with higher win rate based on connection
for number in range(1, 26, 1):
if number not in numbers_drawn:

i, j = self.board.find_number(number)

self.board.marked[i][j] = True
marks = self.count_marks(i, j)
self.board.marked[i][j] = False

if marks > max_marks:
max_marks = marks
best_choice = number

return best_choice

def count_marks(self, i, j):

# Calculate the number of connection grids
marks = 0
for col in range(5):
marks += self.board.marked[i][col]

for row in range(5):
marks += self.board.marked[row][j]

for k in range(5):
marks += self.board.marked[k][k]

for k in range(5):
marks += self.board.marked[k][k-1]

return marks


4.如何記錄賓果遊戲狀態?


透過BingoCard.mark_number()去紀錄被叫的數字,儲存在雙方的板子上。


另外,會依照順序紀錄每個回合被叫到的數字,
儲存在pick_sequence和numbers_drawn。


最後,遊戲結束時,會回放雙方遊戲對戰過程所叫的數字。

    # 依照順序紀錄每個回合被叫到的數字
pick_sequence.append(number)
numbers_drawn.add(number)

# 顯示哪一方玩家 叫了哪個數字
print(f"{cur_player.name} pick {number}.")

# 雙方玩家把這次叫到的數字做個記號​
human_player.board.mark_number(number)
ai_player.board.mark_number(number)

5.如何顯示賓果遊戲狀態?


由BingoCard.display()和BingoCard.show_status()這兩之function負責。


display()會顯示開局的初始狀態

show_status()會顯示當下的遊戲狀態,被叫過的號碼會以★號顯示。


  def display(self):

print(f"{self.name}'s Bingo Card:")
for row in self.card:
print("\t".join(str(num).rjust(2) for num in row))
print()
return

def show_status(self):

print(f"\n{self.name}'s status")
for y in range(5):
for x in range(5):
if self.marked[y][x]:
print("★".rjust(2), end="\t")
else:
print(str(self.card[y][x]).rjust(2), end="\t")
print()

print()
return


6.如何判斷已經有一方獲勝?


檢查每一條直線,看哪一方玩家先取得完整的一條連線。

(可以是占據對角線,可以是占據水平直線,可以是占據垂直直線)


  def check_bingo(self):

for row in self.marked:
if all(row):
return True

for col in zip(*self.marked):
if all(col):
return True

if all(self.marked[i][i] for i in range(5)) or all(self.marked[i][4-i] for i in range(5)):
return True

return False



7.如何在兩個玩家之間切換,實現輪流的機制?


由turn 回和數決定,0代表使用者扮演的玩家,1代表電腦AI。

每回合turn輪流在0, 1之間。

while True:

# Player's input
cur_player = players[turn]
print(f"{cur_player.name}'s turn")
number = cur_player.choose_number(numbers_drawn)

# ... 遊戲的叫號、標記遊戲版、和檢查連線的處理機制 ...

# Go to next turn
turn = (turn + 1) % 2

8.如何使用OOP物件導向程式設計,實現模組化和程式碼共用?


BingoCard 定義一塊5x5的遊戲版,並且帶有對應的遊戲操作函數。
負責隨機生成遊戲版,標記被叫過的號碼,檢查是否已經連成一直線。


Agent代表 遊戲玩家,並且擁有一塊專屬的5x5遊戲版。

Agent定義 choose_number 叫號的抽象函數介面要求繼承者實作


HumanPlayer繼承 Agent,代表 人扮演的玩家,透過鍵盤輸入叫號。


SmartAIPlayer繼承 Agent,代表 電腦AI,但過演算法計算,
選擇連線數目最高的號碼叫號。


底層的完整實作: 賓果遊戲 (Bingo連線遊戲)

import random
import abc

class BingoCard:

numbers_drawn = set()

def __init__(self, name):
self.name = name
self.card = self.generate_card()
self.marked = [[False for _ in range(5)] for _ in range(5)]


def generate_card(self):
numbers = random.sample(range(1, 26), 25)
card = [numbers[i*5:(i+1)*5] for i in range(5)]

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

return self.marked[i][j]


def find_number(self, number):

for i in range(5):
for j in range(5):
if self.card[i][j] == number:
return i, j
return


def check_bingo(self):
for row in self.marked:
if all(row):
return True

for col in zip(*self.marked):
if all(col):
return True

if all(self.marked[i][i] for i in range(5)) or all(self.marked[i][4-i] for i in range(5)):
return True

return False


def display(self):

print(f"{self.name}'s Bingo Card:")
for row in self.card:
print("\t".join(str(num).rjust(2) for num in row))
print()
return


def show_status(self):

print(f"\n{self.name}'s status")
for y in range(5):
for x in range(5):
if self.marked[y][x]:
print("★".rjust(2), end="\t")
else:
print(str(self.card[y][x]).rjust(2), end="\t")
print()

print()
return


class Agent:
def __init__(self, name):
self.name = name
self.board = BingoCard(name)

@abc.abstractmethod
def choose_number(self, numbers_drawn):
pass
raise NotImplementedError
return


class HumanPlayer(Agent):

def choose_number(self, numbers_drawn):

number = -1
while True:

try:
number = int( input("Please select a number: ") )
except:
print("Invalid input!")
continue

if number == -1 or ( (25 >= number >= 1) and(number not in numbers_drawn) ):
break

elif number in numbers_drawn:
print("Repeated!")
else:
print("Invalid input! Number out of Range 1 ~ 25.")

return number


class SmartAIPlayer(Agent):

def choose_number(self, numbers_drawn):
best_choice = -1
max_marks = -1

# Greedy strategy, select the number with higher win rate based on connection
for number in range(1, 26, 1):
if number not in numbers_drawn:

i, j = self.board.find_number(number)

self.board.marked[i][j] = True
marks = self.count_marks(i, j)
self.board.marked[i][j] = False

if marks > max_marks:
max_marks = marks
best_choice = number

return best_choice


def count_marks(self, i, j):

# Calculate the number of connection grids
marks = 0
for col in range(5):
marks += self.board.marked[i][col]

for row in range(5):
marks += self.board.marked[row][j]

for k in range(5):
marks += self.board.marked[k][k]

for k in range(5):
marks += self.board.marked[k][k-1]

return marks


def check_game_finished(player_board, ai_board):

for board in [player_board, ai_board]:
if board.check_bingo():
print(f"Congratulation! {board.name} got Bingo!")

print()
ai_board.show_status()
ai_board.display()
return True

return False


def play_bingo():

human_player = HumanPlayer("Player")
ai_player = SmartAIPlayer("AI")

pick_sequence = []
numbers_drawn = BingoCard.numbers_drawn

human_player.board.display()

players = [human_player, ai_player]
turn = 0

while True:

# Player's input
cur_player = players[turn]
print(f"{cur_player.name}'s turn")
number = cur_player.choose_number(numbers_drawn)

if number == -1:
if cur_player == human_player:
print("You give up")
else:
print("Tie")

print('Game over')
break

pick_sequence.append(number)
numbers_drawn.add(number)
print(f"{cur_player.name} pick {number}.")

human_player.board.mark_number(number)
ai_player.board.mark_number(number)

# Show player's Status
human_player.board.show_status()

# Check game is finished or not
if check_game_finished(human_player.board, ai_player.board):
print(f"Game sequence replay: {pick_sequence}")
break

# Go to next turn
turn = (turn + 1) % 2

if __name__ == "__main__":
play_bingo()

試玩畫面

raw-image

點這行 線上執行 與 試玩(在新的頁面按Run開始玩)


延伸思考:


如果對程式或者演算法有興趣的同學,

可以試著觀察bingo遊戲的規律,

開發更強的AI叫號演算法!

甚至可以建立兩個AI,讓電腦去對戰,看哪種叫號演算法的勝率較高。

很有趣喔~

86會員
425內容數
由有業界實戰經驗的演算法工程師, 手把手教你建立解題的框架, 一步步寫出高效、清晰易懂的解題答案。 著重在讓讀者啟發思考、理解演算法,熟悉常見的演算法模板。 深入淺出地介紹題目背後所使用的演算法意義,融會貫通演算法與資料結構的應用。 在幾個經典的題目融入一道題目的多種解法,或者同一招解不同的題目,擴展廣度,並加深印象。
留言0
查看全部
發表第一個留言支持創作者!
前言 相傳有一個故事, 數學家高斯的小學數學老師出了一道從1+2+3+...+100的習題, 想讓活潑好動的小學生們算一整節課,消耗一下多餘的體力, 結果老師剛說完題目沒過多久,小高斯就算出了答案。 原來,他發現數列兩端可以兩兩配對:1+100,2+99……每一對的和都是101,共有50對,所
在程式語言裡,對應到多重選擇路徑判斷的語法, 最通俗也最常見的就是if ... else ... 語法。 今天,我們將從最基本的 若A條件成立 則...否則 ... 的 if ... else ...開始講起, 搭配幾個範例做說明,最後以一個經典的閏年判定最為結尾的Demo
河內塔的遊戲描述 有三個柱子A柱,B柱,C柱。 A柱上有 N 個 (N>1) 穿孔圓盤,盤的尺寸由下到上依次變小。 要求按下列規則透過合法移動,將所有圓盤移至 C 柱: 1. 每次只能移動頂端的一個圓盤; 2. 大圓盤不能疊在小圓盤上面。
今天要實作和體驗的是拼單字的小遊戲,類似小時候在報紙、英文童書、或著電子辭典的小遊戲,一開始都是空白,隨著使用者拼對而逐漸顯示原本的單字樣貌,直到整個單字拼出來為止。 場景: 電腦隨機從單字庫裡面撈一個單字出來。 讓使用者扮演玩家去玩拼單字的遊戲。
相信大家小時候都有和朋友或玩伴玩過一個猜數字的小遊戲,一個人先在1~100裡面設定一個隱藏數字,其他的人去猜,看誰是最後一個猜中的就算輸,或者看誰最快猜中就算贏。 今天要示範如何用Python寫一個猜數字遊戲, 並且會從上層的思考邏輯開始,一步步構建出這個猜數字的小遊戲。
在流程控制中,最常用的就是for loop 或是 while loop 語法了。 最常見的場景就是根據條件判斷式,重複執行特定的指令。 如果要在python寫出類似C/C++ for loop,可以怎麼寫呢? 透過索引去進行迭代 for var in range( start=0, sto
前言 相傳有一個故事, 數學家高斯的小學數學老師出了一道從1+2+3+...+100的習題, 想讓活潑好動的小學生們算一整節課,消耗一下多餘的體力, 結果老師剛說完題目沒過多久,小高斯就算出了答案。 原來,他發現數列兩端可以兩兩配對:1+100,2+99……每一對的和都是101,共有50對,所
在程式語言裡,對應到多重選擇路徑判斷的語法, 最通俗也最常見的就是if ... else ... 語法。 今天,我們將從最基本的 若A條件成立 則...否則 ... 的 if ... else ...開始講起, 搭配幾個範例做說明,最後以一個經典的閏年判定最為結尾的Demo
河內塔的遊戲描述 有三個柱子A柱,B柱,C柱。 A柱上有 N 個 (N>1) 穿孔圓盤,盤的尺寸由下到上依次變小。 要求按下列規則透過合法移動,將所有圓盤移至 C 柱: 1. 每次只能移動頂端的一個圓盤; 2. 大圓盤不能疊在小圓盤上面。
今天要實作和體驗的是拼單字的小遊戲,類似小時候在報紙、英文童書、或著電子辭典的小遊戲,一開始都是空白,隨著使用者拼對而逐漸顯示原本的單字樣貌,直到整個單字拼出來為止。 場景: 電腦隨機從單字庫裡面撈一個單字出來。 讓使用者扮演玩家去玩拼單字的遊戲。
相信大家小時候都有和朋友或玩伴玩過一個猜數字的小遊戲,一個人先在1~100裡面設定一個隱藏數字,其他的人去猜,看誰是最後一個猜中的就算輸,或者看誰最快猜中就算贏。 今天要示範如何用Python寫一個猜數字遊戲, 並且會從上層的思考邏輯開始,一步步構建出這個猜數字的小遊戲。
在流程控制中,最常用的就是for loop 或是 while loop 語法了。 最常見的場景就是根據條件判斷式,重複執行特定的指令。 如果要在python寫出類似C/C++ for loop,可以怎麼寫呢? 透過索引去進行迭代 for var in range( start=0, sto
你可能也想看
Google News 追蹤
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
分享一個猜數字的遊戲題目,給予提示讓玩家找出正確的四位數密碼。
Thumbnail
winbet小編將介紹21點遊戲規則,並分享親身實測得出的21點教學,包括21點莊家優勢、21點算牌技巧和21點術語。此外,也會提供玩家在實戰中累積經驗的建議。另外,玩家也會得知21點線上遊戲的推薦。歡迎來winbet試試看!
Thumbnail
24點數學遊戲是一款適合小朋友與想動動腦的朋友們的小遊戲,遊戲規則簡單易懂,可訓練邏輯能力。遊戲分為單人與多人模式,可以讓玩家自行挑戰高分或是與其他玩家競爭。算式中不同的數學符號會對應不同的加分機制。遊戲網站連結與專案 repo 也都提供在文章中。
Thumbnail
賓果這款遊戲我想大家應該都不陌生才對,常常會出現在各種活動場合,看誰最快達到指定的連線數量,誰就能得到獎品 賓果遊戲基本概念 這種賓果遊戲使用的卡片由一個5x5的方格組成,每個方格中填入1到25之間的數字,每張卡片上的數字排列是隨機的。 🕹️遊戲規則 準備工作:每個玩家獲得一張或多
Thumbnail
原版的官方規則導入記分機制,但因為計算過於繁複,所以一般遊玩時較少採用。本變體規則旨在還原原規則的策略性,並保留平常的遊玩樂趣。 1. 配件準備 4枚不同顏色的棋子(紅、藍、黃、綠),以及一張標記0~15的場地。 2. 記分方式 一開始所有棋子都在0的位置。每一局結束時,贏家以外的所有人拿出
圍棋,是一種古老棋類遊戲,兩位玩家輪流在棋盤上放置黑白兩色的圓形石子,目標是在棋盤上形成多個區域,以擴大自己的領地並同時圍堵對手的石子。儘管規則簡單,但卻擁有極其豐富的戰術和戰略,每一步都是挑戰與機遇的交錯。加入我們,一同探索、學習、成長,讓圍棋成為你生活中不可或缺的一部分!
Thumbnail
幣安平時除了可以買賣加密貨幣之外,其實還有一些小遊戲可以玩。今天就來介紹幣安的小遊戲「每日一詞」,別看它名稱好像平平無奇,其實每週都有上萬人在玩呢!
Thumbnail
此為三文併一文,原是參與平台活動,後來發現收到薯友贈禮。特地寫這文是要「吐槽」平台的活動方式非常「不科技」,我用excel就解決選號的問題了,但方格不能上傳錄影檔,我還在研究其它方式。 尚缺「向荒荒大人感恩」 下注同樂~~🎲錢兔似錦BINGO樂🎱 早上,看到薯小編的「兔來運轉�
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
分享一個猜數字的遊戲題目,給予提示讓玩家找出正確的四位數密碼。
Thumbnail
winbet小編將介紹21點遊戲規則,並分享親身實測得出的21點教學,包括21點莊家優勢、21點算牌技巧和21點術語。此外,也會提供玩家在實戰中累積經驗的建議。另外,玩家也會得知21點線上遊戲的推薦。歡迎來winbet試試看!
Thumbnail
24點數學遊戲是一款適合小朋友與想動動腦的朋友們的小遊戲,遊戲規則簡單易懂,可訓練邏輯能力。遊戲分為單人與多人模式,可以讓玩家自行挑戰高分或是與其他玩家競爭。算式中不同的數學符號會對應不同的加分機制。遊戲網站連結與專案 repo 也都提供在文章中。
Thumbnail
賓果這款遊戲我想大家應該都不陌生才對,常常會出現在各種活動場合,看誰最快達到指定的連線數量,誰就能得到獎品 賓果遊戲基本概念 這種賓果遊戲使用的卡片由一個5x5的方格組成,每個方格中填入1到25之間的數字,每張卡片上的數字排列是隨機的。 🕹️遊戲規則 準備工作:每個玩家獲得一張或多
Thumbnail
原版的官方規則導入記分機制,但因為計算過於繁複,所以一般遊玩時較少採用。本變體規則旨在還原原規則的策略性,並保留平常的遊玩樂趣。 1. 配件準備 4枚不同顏色的棋子(紅、藍、黃、綠),以及一張標記0~15的場地。 2. 記分方式 一開始所有棋子都在0的位置。每一局結束時,贏家以外的所有人拿出
圍棋,是一種古老棋類遊戲,兩位玩家輪流在棋盤上放置黑白兩色的圓形石子,目標是在棋盤上形成多個區域,以擴大自己的領地並同時圍堵對手的石子。儘管規則簡單,但卻擁有極其豐富的戰術和戰略,每一步都是挑戰與機遇的交錯。加入我們,一同探索、學習、成長,讓圍棋成為你生活中不可或缺的一部分!
Thumbnail
幣安平時除了可以買賣加密貨幣之外,其實還有一些小遊戲可以玩。今天就來介紹幣安的小遊戲「每日一詞」,別看它名稱好像平平無奇,其實每週都有上萬人在玩呢!
Thumbnail
此為三文併一文,原是參與平台活動,後來發現收到薯友贈禮。特地寫這文是要「吐槽」平台的活動方式非常「不科技」,我用excel就解決選號的問題了,但方格不能上傳錄影檔,我還在研究其它方式。 尚缺「向荒荒大人感恩」 下注同樂~~🎲錢兔似錦BINGO樂🎱 早上,看到薯小編的「兔來運轉�