1. 程式人生 > >Python:遊戲:掃雷

Python:遊戲:掃雷

這次我們基於 pygame 來做一個掃雷,上次有園友問我程式碼的 python 版本,我說明一下,我所有的程式碼都是基於 python 3.6 的。

先看截圖,仿照 XP 上的掃雷做的,感覺 XP 上的樣式比 win7 上的好看多了。

原諒我手殘,掃雷基本就沒贏過,測試的時候我是偷偷的把雷的數量從99改到50才贏了。。。

程式碼雖然不多,但加上註釋和空行也有350行,另外還有一些圖片資源,就不全帖上來了,完整的程式碼和資源我放到CSDN上了,感興趣的小夥伴可以去下載一下。下載地址

下面將一下我的實現邏輯。

首先,如何表示雷和非雷,一開始想的是,建立一個二維陣列表示整個區域,0表示非地雷,1表示地雷。後來一想不對,還有標記為地雷,標記為問號,還有表示周邊雷數的數字,好多狀態,乾脆就做個類吧

class BlockStatus(Enum):
    normal = 1  # 未點選
    opened = 2  # 已點選
    mine = 3    # 地雷
    flag = 4    # 標記為地雷
    ask = 5   # 標記為問號
    bomb = 6    # 踩中地雷
    hint = 7    # 被雙擊的周圍
    double = 8  # 正被滑鼠左右鍵雙擊


class Mine:
    def __init__(self, x, y, value=0):
        self._x = x
        self._y = y
        self._value = 0
        self._around_mine_count = -1
        self._status = BlockStatus.normal
        self.set_value(value)

    def __repr__(self):
        return str(self._value)
        # return f'({self._x},{self._y})={self._value}, status={self.status}'

    def get_x(self):
        return self._x

    def set_x(self, x):
        self._x = x

    x = property(fget=get_x, fset=set_x)

    def get_y(self):
        return self._y

    def set_y(self, y):
        self._y = y

    y = property(fget=get_y, fset=set_y)

    def get_value(self):
        return self._value

    def set_value(self, value):
        if value:
            self._value = 1
        else:
            self._value = 0

    value = property(fget=get_value, fset=set_value, doc='0:非地雷 1:雷')

    def get_around_mine_count(self):
        return self._around_mine_count

    def set_around_mine_count(self, around_mine_count):
        self._around_mine_count = around_mine_count

    around_mine_count = property(fget=get_around_mine_count, fset=set_around_mine_count, doc='四周地雷數量')

    def get_status(self):
        return self._status

    def set_status(self, value):
        self._status = value

    status = property(fget=get_status, fset=set_status, doc='BlockStatus')

佈雷就很簡單了,隨機取99個數,從上往下順序排就是了。

class MineBlock:
    def __init__(self):
        self._block = [[Mine(i, j) for i in range(BLOCK_WIDTH)] for j in range(BLOCK_HEIGHT)]

        # 埋雷
        for i in random.sample(range(BLOCK_WIDTH * BLOCK_HEIGHT), MINE_COUNT):
            self._block[i // BLOCK_WIDTH][i % BLOCK_WIDTH].value = 1

我們點選一個格子的時候,只要根據點選的座標,找到對應的 Mine,看它的值是多少,就知道有沒有踩中雷了。

如果沒踩中雷的話,要計算周邊8個位置中有幾個雷,以便顯示對應的數字。

如果周邊有雷,那麼顯示數字,這個簡單,可是如果周邊沒有雷,那就要顯示一片區域,直到有雷出現,如下圖,我只點了當中一下,就出現了那麼大一片區域

這個計算其實也容易,只要用遞迴就可以了,如果計算出周圍的雷數為0,則遞迴計算周邊8個位置的四周雷數,直到雷數不為0。

class MineBlock:
​​​​​​​  def open_mine(self, x, y):
        # 踩到雷了
        if self._block[y][x].value:
            self._block[y][x].status = BlockStatus.bomb
            return False

        # 先把狀態改為 opened
        self._block[y][x].status = BlockStatus.opened

        around = _get_around(x, y)

        _sum = 0
        for i, j in around:
            if self._block[j][i].value:
                _sum += 1
        self._block[y][x].around_mine_count = _sum

        # 如果周圍沒有雷,那麼將周圍8個未中未點開的遞迴算一遍
        # 這就能實現一點出現一大片開啟的效果了
        if _sum == 0:
            for i, j in around:
                if self._block[j][i].around_mine_count == -1:
                    self.open_mine(i, j)

        return True


def _get_around(x, y):
    """返回(x, y)周圍的點的座標"""
    # 這裡注意,range 末尾是開區間,所以要加 1
    return [(i, j) for i in range(max(0, x - 1), min(BLOCK_WIDTH - 1, x + 1) + 1)
            for j in range(max(0, y - 1), min(BLOCK_HEIGHT - 1, y + 1) + 1) if i != x or j != y]

接下來還有一個麻煩的地方,我們經常滑鼠左右鍵同時按下,如果雷被全部標記,則會一下子開啟周圍所有的格子,如果其中有標記錯的,那麼不好意思,GAME OVER。

如果沒有全標記完,會有一個效果顯示周圍一圈未被開啟和標記的格子

class MineBlock:
​​​​​​​   def double_mouse_button_down(self, x, y):
        if self._block[y][x].around_mine_count == 0:
            return True

        self._block[y][x].status = BlockStatus.double

        around = _get_around(x, y)

        sumflag = 0     # 周圍被標記的雷數量
        for i, j in _get_around(x, y):
            if self._block[j][i].status == BlockStatus.flag:
                sumflag += 1
        # 周邊的雷已經全部被標記
        result = True
        if sumflag == self._block[y][x].around_mine_count:
            for i, j in around:
                if self._block[j][i].status == BlockStatus.normal:
                    if not self.open_mine(i, j):
                        result = False
        else:
            for i, j in around:
                if self._block[j][i].status == BlockStatus.normal:
                    self._block[j][i].status = BlockStatus.hint
        return result

    def double_mouse_button_up(self, x, y):
        self._block[y][x].status = BlockStatus.opened
        for i, j in _get_around(x, y):
            if self._block[j][i].status == BlockStatus.hint:
                self._block[j][i].status = BlockStatus.normal

掃雷的主要邏輯就這麼多,剩下來的就是一些雜七雜八的事件了。程式碼也帖一下吧

import sys
import time
from enum import Enum
import pygame
from pygame.locals import *
from mineblock import *


# 遊戲螢幕的寬
SCREEN_WIDTH = BLOCK_WIDTH * SIZE
# 遊戲螢幕的高
SCREEN_HEIGHT = (BLOCK_HEIGHT + 2) * SIZE


class GameStatus(Enum):
    readied = 1,
    started = 2,
    over = 3,
    win = 4


def print_text(screen, font, x, y, text, fcolor=(255, 255, 255)):
    imgText = font.render(text, True, fcolor)
    screen.blit(imgText, (x, y))


def main():
    pygame.init()
    screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
    pygame.display.set_caption('掃雷')

    font1 = pygame.font.Font('resources/a.TTF', SIZE * 2)  # 得分的字型
    fwidth, fheight = font1.size('999')
    red = (200, 40, 40)

    # 載入資源圖片,因為資原始檔大小不一,所以做了統一的縮放處理
    img0 = pygame.image.load('resources/0.bmp').convert()
    img0 = pygame.transform.smoothscale(img0, (SIZE, SIZE))
    img1 = pygame.image.load('resources/1.bmp').convert()
    img1 = pygame.transform.smoothscale(img1, (SIZE, SIZE))
    img2 = pygame.image.load('resources/2.bmp').convert()
    img2 = pygame.transform.smoothscale(img2, (SIZE, SIZE))
    img3 = pygame.image.load('resources/3.bmp').convert()
    img3 = pygame.transform.smoothscale(img3, (SIZE, SIZE))
    img4 = pygame.image.load('resources/4.bmp').convert()
    img4 = pygame.transform.smoothscale(img4, (SIZE, SIZE))
    img5 = pygame.image.load('resources/5.bmp').convert()
    img5 = pygame.transform.smoothscale(img5, (SIZE, SIZE))
    img6 = pygame.image.load('resources/6.bmp').convert()
    img6 = pygame.transform.smoothscale(img6, (SIZE, SIZE))
    img7 = pygame.image.load('resources/7.bmp').convert()
    img7 = pygame.transform.smoothscale(img7, (SIZE, SIZE))
    img8 = pygame.image.load('resources/8.bmp').convert()
    img8 = pygame.transform.smoothscale(img8, (SIZE, SIZE))
    img_blank = pygame.image.load('resources/blank.bmp').convert()
    img_blank = pygame.transform.smoothscale(img_blank, (SIZE, SIZE))
    img_flag = pygame.image.load('resources/flag.bmp').convert()
    img_flag = pygame.transform.smoothscale(img_flag, (SIZE, SIZE))
    img_ask = pygame.image.load('resources/ask.bmp').convert()
    img_ask = pygame.transform.smoothscale(img_ask, (SIZE, SIZE))
    img_mine = pygame.image.load('resources/mine.bmp').convert()
    img_mine = pygame.transform.smoothscale(img_mine, (SIZE, SIZE))
    img_blood = pygame.image.load('resources/blood.bmp').convert()
    img_blood = pygame.transform.smoothscale(img_blood, (SIZE, SIZE))
    img_error = pygame.image.load('resources/error.bmp').convert()
    img_error = pygame.transform.smoothscale(img_error, (SIZE, SIZE))
    face_size = int(SIZE * 1.25)
    img_face_fail = pygame.image.load('resources/face_fail.bmp').convert()
    img_face_fail = pygame.transform.smoothscale(img_face_fail, (face_size, face_size))
    img_face_normal = pygame.image.load('resources/face_normal.bmp').convert()
    img_face_normal = pygame.transform.smoothscale(img_face_normal, (face_size, face_size))
    img_face_success = pygame.image.load('resources/face_success.bmp').convert()
    img_face_success = pygame.transform.smoothscale(img_face_success, (face_size, face_size))
    face_pos_x = (SCREEN_WIDTH - face_size) // 2
    face_pos_y = (SIZE * 2 - face_size) // 2

    img_dict = {
        0: img0,
        1: img1,
        2: img2,
        3: img3,
        4: img4,
        5: img5,
        6: img6,
        7: img7,
        8: img8
    }

    bgcolor = (225, 225, 225)   # 背景色

    block = MineBlock()
    game_status = GameStatus.readied
    start_time = None   # 開始時間
    elapsed_time = 0    # 耗時

    while True:
        # 填充背景色
        screen.fill(bgcolor)

        for event in pygame.event.get():
            if event.type == QUIT:
                sys.exit()
            elif event.type == MOUSEBUTTONDOWN:
                mouse_x, mouse_y = event.pos
                x = mouse_x // SIZE
                y = mouse_y // SIZE - 2
                b1, b2, b3 = pygame.mouse.get_pressed()
                if game_status == GameStatus.started:
                    # 滑鼠左右鍵同時按下,如果已經標記了所有雷,則開啟周圍一圈
                    # 如果還未標記完所有雷,則有一個周圍一圈被同時按下的效果
                    if b1 and b3:
                        mine = block.getmine(x, y)
                        if mine.status == BlockStatus.opened:
                            if not block.double_mouse_button_down(x, y):
                                game_status = GameStatus.over
            elif event.type == MOUSEBUTTONUP:
                if y < 0:
                    if face_pos_x <= mouse_x <= face_pos_x + face_size \
                            and face_pos_y <= mouse_y <= face_pos_y + face_size:
                        game_status = GameStatus.readied
                        block = MineBlock()
                        start_time = time.time()
                        elapsed_time = 0
                        continue

                if game_status == GameStatus.readied:
                    game_status = GameStatus.started
                    start_time = time.time()
                    elapsed_time = 0

                if game_status == GameStatus.started:
                    mine = block.getmine(x, y)
                    if b1 and not b3:       # 按滑鼠左鍵
                        if mine.status == BlockStatus.normal:
                            if not block.open_mine(x, y):
                                game_status = GameStatus.over
                    elif not b1 and b3:     # 按滑鼠右鍵
                        if mine.status == BlockStatus.normal:
                            mine.status = BlockStatus.flag
                        elif mine.status == BlockStatus.flag:
                            mine.status = BlockStatus.ask
                        elif mine.status == BlockStatus.ask:
                            mine.status = BlockStatus.normal
                    elif b1 and b3:
                        if mine.status == BlockStatus.double:
                            block.double_mouse_button_up(x, y)

        flag_count = 0
        opened_count = 0

        for row in block.block:
            for mine in row:
                pos = (mine.x * SIZE, (mine.y + 2) * SIZE)
                if mine.status == BlockStatus.opened:
                    screen.blit(img_dict[mine.around_mine_count], pos)
                    opened_count += 1
                elif mine.status == BlockStatus.double:
                    screen.blit(img_dict[mine.around_mine_count], pos)
                elif mine.status == BlockStatus.bomb:
                    screen.blit(img_blood, pos)
                elif mine.status == BlockStatus.flag:
                    screen.blit(img_flag, pos)
                    flag_count += 1
                elif mine.status == BlockStatus.ask:
                    screen.blit(img_ask, pos)
                elif mine.status == BlockStatus.hint:
                    screen.blit(img0, pos)
                elif game_status == GameStatus.over and mine.value:
                    screen.blit(img_mine, pos)
                elif mine.value == 0 and mine.status == BlockStatus.flag:
                    screen.blit(img_error, pos)
                elif mine.status == BlockStatus.normal:
                    screen.blit(img_blank, pos)

        print_text(screen, font1, 30, (SIZE * 2 - fheight) // 2 - 2, '%02d' % (MINE_COUNT - flag_count), red)
        if game_status == GameStatus.started:
            elapsed_time = int(time.time() - start_time)
        print_text(screen, font1, SCREEN_WIDTH - fwidth - 30, (SIZE * 2 - fheight) // 2 - 2, '%03d' % elapsed_time, red)

        if flag_count + opened_count == BLOCK_WIDTH * BLOCK_HEIGHT:
            game_status = GameStatus.win

        if game_status == GameStatus.over:
            screen.blit(img_face_fail, (face_pos_x, face_pos_y))
        elif game_status == GameStatus.win:
            screen.blit(img_face_success, (face_pos_x, face_pos_y))
        else:
            screen.blit(img_face_normal, (face_pos_x, face_pos_y))

        pygame.display.update()


if __name__ == '__main__':
    main()

最後說一句,CSDN 的貼程式碼看起來比部落格園爽多了,黑色背景跟 IDE 的一樣,

相關推薦

Python遊戲300行程式碼實現俄羅斯方塊 Python遊戲貪吃蛇 Python遊戲掃雷(附原始碼)

本文程式碼基於 python3.6 和 pygame1.9.4。 俄羅斯方塊是兒時最經典的遊戲之一,剛開始接觸 pygame 的時候就想寫一個俄羅斯方塊。但是想到旋轉,停靠,消除等操作,感覺好像很難啊,等真正寫完了發現,一共也就 300 行程式碼,並沒有什麼難的。 先來看一個遊戲截圖,有點醜,好

Python遊戲掃雷

這次我們基於 pygame 來做一個掃雷,上次有園友問我程式碼的 python 版本,我說明一下,我所有的程式碼都是基於 python 3.6 的。 先看截圖,仿照 XP 上的掃雷做的,感覺 XP 上的樣式比 win7 上的好看多了。 原諒我手殘,掃雷基本就沒贏過,測

Python遊戲測試打字速度

來看 let 測試 int 的人 time() port 輸入 style 現在寫書的人真是一點責任心都沒有,最近看了幾本書,其中的代碼都存在錯誤。 最近迷戀 Python 遊戲,買了《Python遊戲編程入門》[美] Jonathan S·Harbour 著 一書來看。

Python遊戲貪吃蛇(附源碼)

false 快速 寬度 技術分享 如何 game 點擊 遊戲 範圍 貪吃蛇是個非常簡單的遊戲,適合練手。 首先分析一下這個遊戲 1、蛇怎麽畫? 蛇是由一個個小方塊組成的,那麽我們可以用一個 list 記錄每一個小方塊的坐標,顯示的時候將所有小方塊畫出來即可。 2、蛇怎麽移動

Mad Libs遊戲熟悉python編程環境,基本輸入輸出

.com inpu int 姓名 基本輸入輸出 輸入 light class pri name1=input(‘請輸入姓名‘) name2=input(‘再輸入姓名‘) print(‘{}說我就算餓死,死這裏,從6樓跳下去,也不吃同德一口飯。\n第二天,{}和{}一起在同

利用python編寫遊戲修改器!俗稱外掛!

最近比較懷舊,在玩一個比較老的PC遊戲。由於遊戲難度太高了,於是就打算自己寫一個修改器。 通過查閱資料,在 Windows 下的修改器主要需要用到四個函式:OpenProcess, CloseHandle, WriteProcessMemory, ReadProcessMemory。 這幾個

Python做個小遊戲環境篇

一、安裝Python和pygame 1、在Windows環境下,安裝Python 略 2、安裝pygame,網址: http://pygame.org 使用Python自帶pip工具即可快速安裝pygame: python3 -m pip install -U py

卜若的程式碼筆記-python系列-遊戲篇-第一章Kivy安裝以及hello world

1.安裝kivy 進入到你的python等根目錄 shift+滑鼠右鍵,喚出power shell 更新或安裝你的pip,如果已經更新完成或者安裝的請忽略 ./python -m pip install --upgrade pip 2.安裝依賴 ./pytho

Python實現簡單遊戲飛機大戰

程式碼只寫到自己發射子彈和敵機自動發射子彈,未完待續… 裡面的飛機圖片檔案需要你們自己下載 import pygame import time from pygame.locals import * import random class plane(obj

python入門篇開發一個簡單的猜數字小遊戲

python是史上最簡潔的語言!(其實就是一個文字遊戲) 今天太晚了,我把程式碼貼出來還有事情忙(其實是想偷個懶,不想打字,反正我有註釋) 看我的文章千萬不要著急,慢慢看完,看到最後。 ****************************** #coding=utf-8 name =

Python程式設計實現猜數遊戲

程式設計實現:猜數遊戲 **由程式隨機產生一個0~100之間的整數,然後讓使用者輸入一個整數,判斷輸入是否正確,若正確,則結束遊戲,輸出資訊;如錯誤,則提示“大了”或“小了”,且還可再輸入一個整數,當猜數次數達到8次時,則結束遊戲。輸出資訊請按如下規則確定:

python遊戲#石頭剪刀布遊戲

#石頭剪刀布遊戲: import random #引入一個隨機模組 print(“1剪刀 2石頭 3布”) men=input(“請您輸入指令”) pc=random.randint(1,3) menwin_lists=[(men1 and pc3),(men2 and

Python 0基礎開發遊戲打地鼠(詳細教程)VS code版本

如果你沒有任何程式設計經驗,而且想嘗試一下學習程式設計開發,這個系列教程一定適合你,它將帶你學習最基本的Python語法,並讓你掌握小遊戲的開發技巧。你所需要的,就是付出一些時間和耐心來嘗試這些程式碼和操作。 @[top] 一、準備工作 1 下載安裝 python 2 下載安裝VS code編輯器 安裝時

unity3D遊戲分解之角色移動和相機跟隨

ini img form static 錄像 void 方法 lda okr 遊戲中,我們經常會有這樣的操作,點擊場景中某個位置,角色自動移動到那個位置,同時角色一直是朝向那個位置移動的,而且相機也會一直跟著角色移動。有些遊戲,鼠標滑動屏幕,相機就會圍繞角色旋

Unity3d修煉之路遊戲開發中,3d數學知識的練習【1】(不斷更新.......)

turn tor rdo pre 長度 scrip 縮放 unity3d float #pragma strict public var m_pA : Vector3 = new Vector3(2.0f, 4.0f, 0.0f); public var m_pB :

【Unity 3D】學習筆記三十遊戲元素——遊戲地形

nbsp 3d遊戲 strong 直觀 分辨率 == 摩擦力 fill 世界 遊戲地形 在遊戲的世界中,必然會有非常多豐富多彩的遊戲元素融合當中。它們種類繁多。作用也不大同樣。一般對於遊戲元素可分為兩種:經經常使用。不經經常使用。經常使用的元素是遊戲中比較重要的元素。一

Python簡明教程基本概念

python1 字面意義上的常量,如2、‘This is ok‘這樣的字符串>>> print(‘a,2,3‘)a,2,32 字符串單引號(‘)使用單引號指示字符串,類似shell中的強引用,所有的空格、制表符照原樣保留。>>> print(‘This is ok‘)Thi

Python簡明教程運算符與表達式

python Python大多數語句都包含表達式,如2+3,一個表達式可以分解為運算符和操作數。在上面的例子中,+為運算符,2和3為操作數。示例:>>> 2+35>>> 3+3*29本文出自 “一萬年太久,只爭朝夕” 博客,請務必保留此出處http://zengwj1

Python-模塊OS,目錄及文件的簡單操作

-1 close pytho print nbsp nco 刪除目錄 os.path window 1.目錄操作 #encoding=UTF-8import unittest,osfrom time import sleep print dir(os)#獲取文件路徑‘‘‘獲

python小工具python操作HP的Quality Center

over cti 步驟 response headers 服務器 登錄 chm format 背景是這樣的:這個組的測試人員每跑一個case都要上傳測試結果附件到QC。每個待測功能模塊可能包含幾十上百的case。於是手工上傳測試結果變成了繁重的體力勞動。令人驚訝的是我們的工