PyGame做了一个扫雷

效果

 

 

  • 绿色的F表示右键点击插旗的地方
  • 红色的格子表示点击到了地雷

原理

主时间循环

基本上GUI应用都会有一个主循环,用来接收各种事件,并按照时间类型进行不同的处理。

    while True:
        for event in pygame.event.get():
            if event.type == pygame.MOUSEBUTTONUP:
                if game.is_over():
                    init_game(30)
                    continue
                pos = event.pos
                if event.button == 1:
                    game.clicked(pos)
                elif event.button == 3:
                    game.right_clicked(pos)
                # 获取当前那个格子被点击了
            if event.type == pygame.QUIT:
                sys.exit(0)
            pygame.display.update()

主要自定义处理类 

  • class Game 
  • class Square 

Game

表示一次游戏。当前游戏中所有的地雷分布;保存所有的格子信息;以及每个格子周围的地雷数量。Game点击后需要判断是否是否游戏结束。

如何获胜:

  • 所有的雷都被标注
  • 已经没有空白的格子可以点了

Square

表示一个格子,初始化的时候会传入是否包含地雷的信息,格子作为一圈的地雷数量。

Square本身含有一个状态机,根据点击状态和点击方式画出各种样式(空白,带数字,F,红色)等。

 

主循环中的事件会传给Game,最终根据位置传个某个Square。

PyGame

pygame.Screen 使用pygame.display.set_mode返回一个画布,然后可以将各种Rect,font之类的放到这画布上。

pygame.display.set_mode((total_width, total_height))

pygame.Rect 输出格子

pygame.font 输出字体

 

重点方法

  • pygame.Screen.blit 将绘制的内容叠放到屏幕上。
            self.face = pygame.Surface((self.rect.width, self.rect.height))
            self.face.fill('white')
            game.get_screen().blit(self.face, (self.rect.left, self.rect.top))
            pygame.draw.rect(self.surface, 'gray', self.rect, 1) 
  • pygame.display.update 刷新页面

参考

  • https://www.pygame.org/news 

代码

# 这是一个示例 Python 脚本。

# 按 ⌃R 执行或将其替换为您的代码。
# 按 双击 ⇧ 在所有地方搜索类、文件、工具窗口、操作和设置。
import sys
import pygame
import random

game = None
BOMB_COUNT = 80

class Game(object):
    screen = None
    row_count = 0
    bomb_location = []
    squares_list = []
    squares_val_dict = {}
    win = False

    def __init__(self, count):
        pygame.init()
        total_width = count * 18 + 100 * 2
        total_height = count * 18 + 100 * 2
        self.row_count = count
        self.screen = pygame.display.set_mode((total_width, total_height))
        self.win = False
        self.bomb_location = []
        self.squares_list = []
        self.squares_val_dict = {}

    def __del__(self):
        print('del game')
        pygame.display.quit()

    def is_over(self):
        return self.win

    def get_screen(self):
        return self.screen

    # 初始化随机产生炸弹
    def random_bomb(self):
        # 随机产生炸弹
        for i in range(BOMB_COUNT):
            while 1:
                x = random.randint(0, 29)
                y = random.randint(0, 29)
                if (x, y) in self.bomb_location:
                    continue
                else:
                    self.bomb_location.append((x, y))
                break

        # 计算某个位置范围的数字
        for x in range(self.row_count):
            for y in range(self.row_count):
                count = 0
                if (x - 1, y) in self.bomb_location:
                    count += 1
                if (x - 1, y - 1) in self.bomb_location:
                    count += 1
                if (x - 1, y + 1) in self.bomb_location:
                    count += 1

                if (x , y - 1) in self.bomb_location:
                    count += 1
                if (x , y) in self.bomb_location:
                    count += 1
                if (x , y + 1) in self.bomb_location:
                    count += 1

                if (x + 1 , y - 1) in self.bomb_location:
                    count += 1
                if (x + 1 , y) in self.bomb_location:
                    count += 1
                if (x + 1, y + 1) in self.bomb_location:
                    count += 1

                # print('%s,%s %d' % (x, y, count))
                self.squares_val_dict[(x, y)] = count

    def init_square(self):
        for i in range(self.row_count):
            self.squares_list.append([])
            top = 100 + i * 18
            for j in range(self.row_count):
                left = 100 + j * 18
                width = 18
                height = 18
                exist = False
                if (i, j) in self.bomb_location:
                    # print('%s,%s exists' % (j, i))
                    exist = True

                # 周围的炸弹数量
                around_count = self.squares_val_dict.get((i, j), 0)
                # print('init square %s,%s %d' % (i, j, around_count))
                self.squares_list[i].append(Square(exist, around_count, self.screen, left, top, width, height))
                self.squares_list[i][j].draw()
        pygame.display.update()

    def start_game(self):
        pass

    # 胜利后显示的文字
    def display_win(self):
        font = pygame.font.SysFont("Andale Mono", 32)
        txt = font.render("Winner Winner Winner", True, 'Red')
        self.get_screen().blit(txt, (200, 0))

        font = pygame.font.SysFont("Andale Mono", 16)
        txt = font.render("uploading to dashboard...", True, 'green')
        self.get_screen().blit(txt, (260, 40))

        font = pygame.font.SysFont("Andale Mono", 16)
        txt = font.render("click to continue...", True, 'gray')
        self.get_screen().blit(txt, (280, 60))

        self.win = True

    def display_flag(self):
        for (x, y) in self.bomb_location:
            square = self.squares_list[x][y]
            square.right_click()
            square.draw()

    # 根据所有的旗帜来判断胜利
    def check_win_by_flag(self):
        for (x, y) in self.bomb_location:
            square = self.squares_list[x][y]
            if square.state == 'flag' and square.exist:
                continue
            return False
        self.display_win()
        return True

    # 根据已经没有格子点击了来判断胜利
    def check_win_by_click(self):
        # print('check by click')
        for x in range(self.row_count):
            for y in range(self.row_count):
                square = self.squares_list[x][y]
                if square.state == 'blank' or square.exist:
                    # print(1)
                    continue
                return False
        self.display_flag()
        self.display_win()
        return True

    def right_clicked(self, pos):
        left = pos[0]
        top = pos[1]
        x = int((top - 100) / 18)
        y = int((left - 100) / 18)

        # print('right clicked %s, %s' % (x, y))
        if x in range(0, self.row_count) and y in range(0, self.row_count):
            square = self.squares_list[x][y]
            if not square.right_click():
                return
            # 表示右键生效
            square.draw()

            if square.state == 'flag' and square.exist:
                # 只有当前标记是正确的时候才判断
                # 判断是否已经将所有的炸弹标记出来
                self.check_win_by_flag()
            pygame.display.update()

    def clicked(self, pos):
        left = pos[0]
        top = pos[1]
        x = int((top - 100) / 18)
        y = int((left - 100) / 18)

        def click_square(self, x, y):
            if x not in range(0, self.row_count) or y not in range(0, self.row_count):
                return False

            square = self.squares_list[x][y]
            if square.state != 'new':
                return False

            if not square.click():
                return False

            square.draw()
            if square.around_count == 0:
                # print('around is 0')
                for (x1, y1) in [
                    (x - 1, y), (x - 1, y - 1), (x - 1, y + 1),
                    (x, y - 1), (x, y), (x, y + 1),
                    (x + 1, y - 1), (x + 1, y), (x + 1, y + 1),

                ]:
                    click_square(self, x1, y1)
            return True

        if x in range(0, self.row_count) and y in range(0, self.row_count):
            if click_square(self, x, y):
                # 判断是否成功
                self.check_win_by_click()
            pygame.display.update()

    def refresh(self):
        pygame.display.update()


# 扫雷中的一个格子
class Square(object):
    exist = False
    surface = None
    rect = None
    state = '' # new, blank, flag, bomed
    face = None
    around_count = 0

    def __init__(self, exist, around_count, surface, left, top, width, height):
        self.rect = pygame.Rect(left, top, width, height)
        self.exist = exist
        self.surface = surface
        self.state = 'new'
        self.around_count = around_count
        # print('%s' % (self.around_count))

    def exists(self):
        return self.exist

    def draw(self):
        global game
        if self.state == 'new':
            self.face = pygame.Surface((self.rect.width, self.rect.height))
            self.face.fill('white')
            game.get_screen().blit(self.face, (self.rect.left, self.rect.top))
            pygame.draw.rect(self.surface, 'gray', self.rect, 1)

        elif self.state == 'blank':
            self.face.fill('gray')
            game.get_screen().blit(self.face, (self.rect.left, self.rect.top))
            pygame.draw.rect(self.surface, 'white', self.rect, 1)

            # 在格子中间画上数字
            font = pygame.font.SysFont("Andale Mono", 16)
            txt = font.render("%s" % (self.around_count if self.around_count > 0 else ''), True, 'blue')
            # print('%s, %s' % (txt.get_rect(), self.around_count))
            game.get_screen().blit(txt, (self.rect.left + 4, self.rect.top))

            pass
        elif self.state == 'flag':
            # 在格子中间画上 F
            font = pygame.font.SysFont("Andale Mono", 16)
            txt = font.render("F", True, 'green')
            # print('%s, %s' % (txt.get_rect(), self.around_count))
            game.get_screen().blit(txt, (self.rect.left + 4, self.rect.top))

        elif self.state == 'boom':
            self.face.fill('red')
            game.get_screen().blit(self.face, (self.rect.left, self.rect.top))
            pygame.draw.rect(self.surface, 'white', self.rect, 1)
            pass

    def click(self):
        need_update = False
        if self.state == 'new':
            if self.exist:
                self.state = 'boom'
                need_update = True
            else:
                self.state = 'blank'
                need_update = True
        return need_update

    def right_click(self):
        need_update = False
        if self.state == 'new':
            self.state = 'flag'
            need_update = True
        elif self.state == 'flag':
            self.state = 'new'
            need_update = True
        return need_update


def init_game(count, x=18, y=18):
    global game
    if game:
        del game
    game = Game(count)
    game.random_bomb()
    game.init_square()


# 按间距中的绿色按钮以运行脚本。
if __name__ == '__main__':
    init_game(30)

    # 主事件循环
    while True:
        for event in pygame.event.get():
            if event.type == pygame.MOUSEBUTTONUP:
                if game.is_over():
                    init_game(30)
                    continue
                pos = event.pos
                if event.button == 1:
                    game.clicked(pos)
                elif event.button == 3:
                    game.right_clicked(pos)
                # 获取当前那个格子被点击了
            if event.type == pygame.QUIT:
                sys.exit(0)
            pygame.display.update()

 

posted @ 2022-10-31 13:15  by_mzy  阅读(181)  评论(0编辑  收藏  举报