# Projects : telegram上的踩地雷小遊戲 與telegram進行連結,並實作一個可以在telegram實際操作的踩地雷小遊戲 ## 說明 ### 輸入說明 - `/start` 指令輸入欲產生盤面資訊。 第一個輸入數字為盤面的列 (row) 數 RR。 第二個輸入數字為盤面的行 (col) 數 CC。 第三個輸入數字為地雷總數 kk。 範例:`/start 10 10 8` 但只有輸入 `/start` 時,請內定 R=8R=8, C=9C=9, k=10k=10。 其餘狀況需顯示錯誤訊息,並套用前述內定值。 - `/open` 指令輸入欲打開盤面之座標。 每對數字 m,nm,n 代表有一個地雷的座標為 (m,n)(m,n), mm 為第幾列, nn 為第幾行,其中 mm 和 nn 都從 11 開始。 範例:`/open (8, 7)` 其餘狀況需顯示錯誤訊息,並套用前述內定值。 - 一名稱為 `重玩` 按鈕可重玩這一局(不重新 Random 地雷座標)。 ### 輸出說明 產生 R×CR×C 的格子盤面,以`@`表示尚未挖出的地面,使用 `/open` 輸入座標,挖該區塊地雷,觸發以下狀況: 留言 - 該區塊沒有地雷 - 顯示以其為中心的周圍 8 格中,所有的地雷總數。如至少有一個,請顯示阿拉伯數字;否則顯示 `+` ,並挖周圍 8 格地雷,反覆到周圍有發現地雷為止。 - 尚有未挖過的無地雷區塊:更新盤面。 - 已沒有未挖過的安全區塊:結束遊戲,顯示遊戲勝利,並顯示完整盤面(包含地雷座標及數量)。 - 該區塊有地雷 - 該區塊顯示`*`,結束遊戲,顯示遊戲失敗,並顯示完整盤面(包含地雷座標及數量)。 點擊一區塊可翻開: - 如果是地雷,翻開所有地雷區塊,將他們改為`*`,並結束遊戲。 - 如果周圍的區塊均無地雷,則除了翻開該區塊外,繼續翻開周圍 8 個區塊。 - 如周圍 8 格 中有 ss 個地雷,則除了翻開該區域外,需顯示 ss 至該區塊上。 ### 輸入規格 + $8 \le R \le 15$ + $8 \le C \le 15$ + $0 \le k \le R\times C$ + $1 \le m \le R$ + $1 \le n \le C$ + 多顆地雷不被放置在同一個格子 ## 實作 ### import ```python= import secret, random from telegram import InlineKeyboardMarkup, InlineKeyboardButton from telegram.ext import Updater, CommandHandler, MessageHandler, Filters ``` ### 連接 telegram ```python= updater = Updater(token=secret.access_token, use_context=True) dispatcher = updater.dispatcher ``` ### Define function 初始化場地 ```python= def init_board(): global board #普通的版面 global revealed #翻開的版面 board = [[0] * C for i in range(R)] revealed = [[False] * C for i in range(R)] mines = random.sample([(x, y) for x in range(R) for y in range(C)], k) for x, y in mines: #檢查周邊有幾個地雷 board[x][y] = '*' for X in range(x-1, x+2): for Y in range(y-1, y+2): if 0 <= X < R and 0 <= Y < C and board[X][Y] != '*': board[X][Y] += 1 for X in range(0,R): for Y in range(0,C): if board[X][Y] == 0: board[X][Y]='+' ``` 每回合show出目前的盤面 ```python= def show_board(): for board_row, revealed_row in zip(board, revealed): columns = [board_row[i] if revealed_row[i] else '@' for i in range(C)] print(*columns) # 以下部分不該顯示在遊戲畫面上 # real_board() ``` 每回合把目前的盤面給使用者看 ```python= def show_board(): for board_row, revealed_row in zip(board, revealed): columns = [board_row[i] if revealed_row[i] else '@' for i in range(C)] print(*columns) # 以下部分不該顯示在遊戲畫面上 # real_board() ``` 實際的盤面(沒被遮蓋的) ```python= def real_board(): print('real board:') for board_row, revealed_row in zip(board, revealed): columns = [board_row[i] for i in range(C)] print(*columns) ``` 打開後會發生的事情 ```python= def open_cell(r, c): #打開這個位置會發生的事情 if r <= 0 or r > R or c <= 0 or c > C: return r, c = r-1, c-1 if revealed[r][c]: #如果翻開過的情況 return #print(f'revealing row {r+1} col {c+1}') revealed[r][c] = True if board[r][c] == '*': started = False print('爆啦~~~') for X in range(0, R): for Y in range(0, C): if board[X][Y] == "*": revealed[X][Y] = True show_board() elif board[r][c] == 0 : #如果是0,則要翻開其他格子 for i in range(r-1, r+2): for j in range(c-1, c+2): open_cell(i+1, j+1) #print(f'open_cell({r + 1}, {c + 1}) is done.') ``` 給予參數,並開始遊戲 ```python= R,C,k = 8, 9, 10 init_board() ``` --- 定義開始遊戲的情況,以下的function為實際在telegram操作上的情形 ```python= def start(update, context): tokens = update.message.text[7:].split() if len(tokens) == 0: global R global C global k global sum R, C, k = 8, 9, 10 elif len(tokens) == 3 and all(x.isdigit() for x in tokens[0:]): #如果使用者有指定數字 R, C, k = [int(x) for x in tokens[0:]] if R < 8 or R > 15 or C < 8 or C > 15 or k < 0 or k > R * C: #規定各數字範圍 R, C, k = 8, 9, 10 context.bot.send_message( chat_id=update.effective_chat.id, text="數字超出規定範圍" ) else: R, C, k = 8, 9, 10 context.bot.send_message( chat_id=update.effective_chat.id, text="你亂打!" ) global board global revealed board = [[0] * C for i in range(R)] revealed = [[False] * C for i in range(R)] mines = random.sample([(x, y) for x in range(R) for y in range(C)], k) for x, y in mines: board[x][y] = '*' for X in range (x-1, x+2): for Y in range (y-1, y+2): if 0 <= X < R and 0 <= Y < C and board[X][Y] != '*': board[X][Y] += 1 for board_row, revealed_row in zip(board, revealed): columns = [board_row[i] for i in range(C)] str1 = "".join([str(_) for _ in columns]) context.bot.send_message( chat_id=update.effective_chat.id, text=str1 ) context.bot.send_message( chat_id=update.effective_chat.id, text="上面是答案啦哈哈,不要偷看(demo方便)" ) for board_row, revealed_row in zip(board, revealed): columns = [board_row[i] if revealed_row[i] else '@' for i in range(C)] str1 = "".join([str(_) for _ in columns]) context.bot.send_message( chat_id=update.effective_chat.id, text=str1 ) context.bot.send_message( chat_id=update.effective_chat.id, text="請輸入/open (x,y) 代表想開的座標點" ) ``` 在telegram輸入想打開的座標後會發生的情形 ```python= def open(update, context): if update.message.text == "/open" : context.bot.send_message( chat_id=update.effective_chat.id, text="座標呢..." ) r, c = eval(update.message.text[6:]) if board[r-1][c-1] == '*': #如果爆了 open_cell(r,c) context.bot.send_message( chat_id=update.effective_chat.id, text="爆了..." ) for board_row, revealed_row in zip(board, revealed): columns = [board_row[i] if revealed_row[i] else '@' for i in range(C)] str1 = "".join([str(_) for _ in columns]) context.bot.send_message( chat_id=update.effective_chat.id, text=str1 ) quit() open_cell(r, c) for board_row, revealed_row in zip(board, revealed): columns = [board_row[i] if revealed_row[i] else '@' for i in range(C)] str1 = "".join([str(_) for _ in columns]) context.bot.send_message( chat_id=update.effective_chat.id, text=str1 ) sum = 0 for X in range(0, R): for Y in range(0,C): if revealed[X][Y] == True: sum = sum + 1 if sum == R*C - k: #全部找完了 context.bot.send_message( chat_id=update.effective_chat.id, text="太屌了吧..." ) for board_row, revealed_row in zip(board, revealed): columns = [board_row[i] for i in range(C)] str1 = "".join([str(_) for _ in columns]) context.bot.send_message( chat_id=update.effective_chat.id, text=str1 ) ``` 在telegram上想重玩一遍 ```python= def replay(update, context): revealed = [[False] * C for i in range(R)] for board_row, revealed_row in zip(board, revealed): columns = [board_row[i] if revealed_row[i] else '@' for i in range(C)] str1 = "".join([str(_) for _ in columns]) context.bot.send_message( chat_id=update.effective_chat.id, text=str1 ) ``` 防呆 ```pyyhon= def remind(update, context): context.bot.send_message( chat_id=update.effective_chat.id, text="你在幹嘛== 請輸入 /start, /open or /replay") ``` 開始在telegrame上進行遊戲 ```python= start_handler = CommandHandler('start', start) dispatcher.add_handler(start_handler) open_handler = CommandHandler('open', open) dispatcher.add_handler(open_handler) replay_handler = CommandHandler('replay', replay) dispatcher.add_handler(replay_handler) repeat_handler = MessageHandler(Filters.text & (~Filters.command), remind) dispatcher.add_handler(repeat_handler) updater.start_polling() ```