# -*- coding: utf-8 -*-
# @Time : 2020/12/18 12:57
# @Author : Zhenghui Lyu
# @File : Main.py
# @Software: PyCharm
from tkinter import *
from tkinter.messagebox import *
import random
import os
import sys



def new():
    """重新开始游戏"""
    global m
    m.grid_remove()
    global model
    model = Model(10, 10)
    m = Mines(model, root)


class Model:
    """Model类用于存储所有方块所在的(r,c)信息,有雷为1,无为0,还可检查周围雷的情况"""

    def __init__(self, row, col):
        self.width = col  #
        self.height = row  #
        self.items = [[0 for c in range(col)] for r in range(row)]

    def setItemValue(self, r, c, value):
        """设置某个位置的值为value"""
        self.items[r][c] = value  # model.setItemValue(r, rancol, 1)

    def checkValue(self, r, c, value):
        """检查某个位置的值是否为value"""
        if self.items[r][c] == value:
            return True
        else:
            return False

    def countValue(self, r, c, value):
        """统计位置(r,c)周围8个位置,值为value的个数"""
        count = 0
        if r - 1 >= 0 and c - 1 >= 0:
            if self.items[r - 1][c - 1] == 1:
                count += 1
        if r - 1 >= 0 and c >= 0:
            if self.items[r - 1][c] == 1:
                count += 1
        if r - 1 >= 0 and c + 1 <= self.width - 1:
            if self.items[r - 1][c + 1] == 1:
                count += 1
        if c - 1 >= 0:
            if self.items[r][c - 1] == 1:
                count += 1
        if c + 1 < self.width - 1:
            if self.items[r][c + 1] == 1:
                count += 1
        if r + 1 <= self.height - 1 and c - 1 >= 0:
            if self.items[r + 1][c - 1] == 1:
                count += 1
        if r + 1 <= self.height - 1:
            if self.items[r + 1][c] == 1:
                count += 1
        if r + 1 <= self.height - 1 and c + 1 <= self.width - 1:
            if self.items[r + 1][c + 1] == 1:
                count += 1
        return count


class Mines(Frame):
    def __init__(self, m, master=None):
        Frame.__init__(self, master)
        self.model = m
        self.initmine()
        self.grid()
        self.createWidget()

    def createWidget(self):
        self.rowconfigure(self.model.height * 2, weight=1)
        self.columnconfigure(self.model.width * 2, weight=1)
        self.buttongroups = [[Button(self, height=1, width=2) for j in range(self.model.width)] for j in
                             range(self.model.height)]
        for r in range(self.model.width):
            for c in range(self.model.height):
                self.buttongroups[r][c].grid(row=r, column=c, sticky=(W, E, N, S))
                self.buttongroups[r][c].bind('<Button-1>', self.clickevent)
                self.buttongroups[r][c].bind('<Button-3>', self.Rightclickevent)
                self.buttongroups[r][c]['padx'] = r
                self.buttongroups[r][c]['pady'] = c

    def clickevent(self, event):
        r = int(str(event.widget['padx']))
        c = int(str(event.widget['pady']))
        if model.checkValue(r, c, 1):
            self.showall()
        else:
            self.recureshow(r, c)
            if (self.Victory()):
                showinfo(title='提示', message='你赢了')

    def Rightclickevent(self, event):
        r = int(str(event.widget['padx']))
        c = int(str(event.widget['pady']))
        if self.buttongroups[r][c]['text'] == 'X':
            self.buttongroups[r][c]['image'] = askImage
        else:
            self.buttongroups[r][c]['image'] = flagImage
            self.buttongroups[r][c]['text'] = 'X'
        if self.Victory():
            showinfo(title='提示', message='你赢了')

    def Victory(self):
        for r in range(model.height):
            for c in range(model.width):
                if self.buttongroups[r][c]['state'] == NORMAL and self.buttongroups[r][c]['text'] != 'X':
                    return False
                if model.checkValue(r, c, 0) and self.buttongroups[r][c]['text'] == 'X':
                    return False
        return True

    def showall(self):
        for r in range(model.height):
            for c in range(model.width):
                self.showone(r, c)

    def showone(self, r, c):
        if model.checkValue(r, c, 0):
            self.buttongroups[r][c]['text'] = model.countValue(r, c, 1)
        else:
            self.buttongroups[r][c]['text'] = 'Q'
            self.buttongroups[r][c]['image'] = mineImage

    def recureshow(self, r, c):
        if 0 <= r <= self.model.height - 1 and 0 <= c <= self.model.width - 1:
            if model.checkValue(r, c, 0) and self.buttongroups[r][c]['state'] == NORMAL and model.countValue(r, c,
                                                                                                             1) == 0:
                self.buttongroups[r][c]['state'] = DISABLED
                self.buttongroups[r][c]['bd'] = 4
                self.buttongroups[r][c]['disabledforeground'] = 'red'
                self.buttongroups[r][c]['text'] = '0'
                self.recureshow(r - 1, c - 1)
                self.recureshow(r - 1, c)
                self.recureshow(r - 1, c + 1)
                self.recureshow(r, c - 1)
                self.recureshow(r, c + 1)
                self.recureshow(r + 1, c - 1)
                self.recureshow(r + 1, c)
                self.recureshow(r + 1, c + 1)
            elif model.countValue(r, c, 1) != 0:
                self.buttongroups[r][c]['text'] = model.countValue(r, c, 1)
                self.buttongroups[r][c]['state'] = DISABLED
                self.buttongroups[r][c]['bd'] = 4
                self.buttongroups[r][c]['disabledforeground'] = 'red'
            else:
                pass

    def initmine(self):
        """埋雷,每行埋(1,height/width)个随机数量雷"""
        n = random.randint(1, model.height / model.width)
        for r in range(model.height):
            for i in range(n):
                rancol = random.randint(0, model.width - 1)

                model.setItemValue(r, rancol, 1)

    def printf(self):
        print('地图')
        for r in range(model.height):
            for c in range(model.width):
                print(model.items[r][c], end='')
            print('')


if __name__ == '__main__':
    model = Model(10, 10)
    root = Tk()
    mineImage = PhotoImage(file='mine.gif')
    flagImage = PhotoImage(file='flag.gif')
    askImage = PhotoImage(file='ask.gif')
    menu = Menu(root)
    root.config(menu=menu)
    filemenu = Menu(menu)
    menu.add_cascade(label='File', menu=filemenu)
    filemenu.add_command(label='New', command=new)
    filemenu.add_separator()
    filemenu.add_command(label='Exit', command=root.quit)
    m = Mines(model, root)
    m.printf()
    root.mainloop()

 

扫雷游戏逻辑:

如果点击的是空白块,即周围没有地雷的块即空白块,此块编号为0,直接打开

如果点击的是周围有雷的块,则打开块并计算周围雷的数量并显示数字

如果点击的是地雷,则游戏失败

在方块上右击,显示旗帜,再次右击去掉旗帜

使用二维列表buttongroups表示若干方块按钮

二维列表items存储行列的地雷信息

用state表示方块是否被翻开

用text属性表示是否插上旗帜