admin管理员组

文章数量:1645532

今天我是小鱼,今天是2021年11月17日,我想自己写一个扫雷,用Python

我用类似日记的方式记录我的过程,想看代码直接拖到最后。

分享出来仅为交流与学习。

前几天儿子说别的小朋友都有电脑,玩游戏。于是就把家里的笔记本给他玩,但那是一台工作本,没有游戏,我就说那你玩扫雷吧,正好锻炼一下怎么用鼠标。

但我发现win11的扫雷跟XP很不同,要去appstore下,画面动画很累赘,很慢。重点是有广告!!!强制看30秒,不能取消。每5分钟就跳出来——孔子不能忍,孟子不能忍,老子也不能忍!

毕竟,XP是2001年出来的,20年过去了——“大人,时代不同了”。

因为最近小半年都在自学Python,于是有了种强烈的要自己写的想法,虽然CSDN上已经有好多人都写过,但我想自己写。不为儿子,就为了练习。

我今天先记录下我的构想:

1.只有初级,中级,高级,没有自定义地图大小功能,地图大小固定;雷数先固定,预留可以自定义雷数的接口。

2.分三个层面,运算层,界面层,控制层。运算层用来生成地图,自动布雷,自动写出雷周边的数字,数字要有不同颜色。预留自定义雷数功能,要增加算法,每个地图最多能放多少个雷,再多就不合理了。界面层是窗口,窗口里的内容,包括计时,雷数,emoj按钮,有下拉的菜单。还有雷上的“土”(我管那些灰色的没有点开的方块叫“泥土”),还有土上的插旗,以及点击到空土(下面没有雷没有数字的)时自动打开周边土直到遇到非空土为止的算法。控制层是收集鼠标的操作,左键,右键。并执行相关的操作,F2是开始新的游戏,F1打开帮助文档。选择初级,中级,高级。稍微与XP有点不同的地方,左键开土,右键插旗,没有插问号,右键双击数字代替原来的左右键同击数字。

3.先不做扫雷排行榜,待上面功能实现之后再做。

运算层中:根据控制层选择的等级,定义二维数组,数组大小来自于控制层定义的等级,获取控制层中第一次开土的坐标,第一次开土作为触发运算层的信号,用随机算法自动布雷,布雷的算法必须避开第一次开土的坐标。数字算法在布雷算法完成之后,用9表示雷,用数字表示周边有多少个9. 用0表示空土(初始所有数组值都为0)

界面层中:根据控制层选择的等级画出地图,窗口只有3种大小(不设计自定义地图)。用类来定义窗口,在类中定义窗口上的各种内容,包括泥土,插旗的泥土。两种开图算法一种是左键空土,一种是右键双击开土数字。

控制层中:捕捉鼠标与键盘动作,鼠标动作是左键,右键,右键双击。F1,F2。退出游戏通过点击右上角红叉和菜单中退出游戏实现。

今天先写这些。

今天是11月18日,昨天和今天将运算层的代码写完了。运行的结果还是比较满意的。我先贴上运算层的代码,如果将来发现我的程序构架有问题,后面再说吧。大不了这个贴就不公开了

# _*_ coding : UTF-8 _*_
# @time: 2021/11/17 13:18
# @file: yunsuan.py
# @Author:yyh
# @Software:PyCharm

import numpy as np
from gongyong import log


def autobulei(dengj, x, y):
    """
    用随机算法自动布雷,布雷的算法必须避开第一次开土的坐标。
    数字算法在布雷算法完成之后,用9表示雷,用数字表示周边有多少个9.
    用0表示空土
    :param dengj: 用来接收来自控制层的等级选择
    :param x,y:是第一次开土时地图上的坐标位置。
    :return: 返回的应该是自动布完雷的二维数组
    """

    daxiao = {  # daxiao是一个表驱动,用来储存不同等级下二维数组的大小与总的雷数
        "chuj": [9, 9, 10],
        "zhongj": [16, 16, 40],
        "gaoj": [16, 30, 99]
    }

    shuzu_dx = (daxiao[dengj][0], daxiao[dengj][1])  # 获取数组的大小
    shuzu = np.zeros(shuzu_dx, dtype=int)  # 创建二维数组

    i = 0
    while i < daxiao[dengj][2]:

        x_shuzu = np.random.randint(0, daxiao[dengj][0])
        y_shuzu = np.random.randint(0, daxiao[dengj][1])
        if shuzu[x_shuzu][y_shuzu] == 9 or ((x_shuzu, y_shuzu) == (x, y)):
            continue  # 如果随机生成的位置凑巧与第一块土相同,或者已经布过雷了,那就回到循环开头重新随机
        else:  # 如果是空土
            shuzu[x_shuzu][y_shuzu] += 9 #这句应该直接等于9,一开始我用+=发现了自己没有检测是否已经布过雷的bug,所以我就保留了+
        i += 1
    return shuzu


def autoshuzi(shuzu):
    """

    :param shuzu:自动布完雷,但是还没有写雷周围的数字的数组
    :return: 返回在雷周围已经写完数字的数组
    """
    row = len(shuzu)
    col = len(shuzu[0])
    sz = shuzu  # 换个短一点的名字

    for i in range(0, row):
        for j in range(0, col):  # 用两个for循环对数组逐行逐列遍历
            if sz[i][j] == 9:  # 如果是雷,那么我们对雷周围的8个格子都加上1
                for n in range(i - 1, i + 2):
                    for m in range(j - 1, j + 2):  # 再用一个双for循环,对雷附近的3x3的区域遍历,每个格子+1
                        if n >= 0 and m >= 0 and row - n >= 1 and col - m >= 1 and sz[n][m] < 9:
                            sz[n][m] += 1  # 这句if是判断当前操作的元素是否在地图范围内,并且这个元素他不是雷,然后给这个单元格+1
                        else:  # 这句else可以不写,但为了逻辑完整
                            continue
            else:  # 这句也是为了逻辑完整。另外,我是遍历整个数组中的雷,并不是遍历整个数组中每个位置,并且对每个位置统计周边雷数。
                # 所以,只要不是雷我都会跳过,我认为这样执行的语句会比较少。
                continue
    return sz


if __name__ == '__main__':
    ditu = autoshuzi(autobulei('chuj', 3, 6))
    log("地图\n", ditu)

但是这个运算速度实在是感人,以前XP上我点下第一格之后马上就打开了,打开就显示周边雷数了。说明当时地图都已经布局好了,我的运算过程大概得有1~2秒钟。哎,先实现功能再说吧。 

今天先到这里吧。今天还有不少事要做。

今天是11月19日,昨天后来找了一个做窗口的库,叫tkinter,简称TK。之前做自学的时候做过一个“小蜜蜂”,当时用的是pygame,对于图形界面的库,我用且只用过pygame。但是感觉pygame有点复杂,扫雷这个小游戏使用标准的一些窗口与控件就可以了。既然是学习,就学习使用一个新的库,而且TK是属于标准库,不需要再另外安装。另外,昨天在网上找了图片,就是雷的图片,旗子的图片。今天不忙的话就好好看一下TK的说明文档。明天又要上一天的课,只能下周再继续了。

现在已经是下午了,今天还是看了不少TK的说明文档,另外我将yunsuan.py稍微修改了一下,布完雷之后顺便再输出一个列表,里面包含了所有地雷的坐标元组。我发现后面界面层还有要使用这张列表,游戏失败后要显示出所有的雷,但今天就不贴新代码了,等后面完成了一起贴。另外,今天也看了一部分TK中对于键鼠事件的内容。看完我觉得我有信心能完成这个游戏。

今天就到这里吧,不再想扫雷的事了。

今天是11月22日,今天太忙了,没有做什么工作。

今天是11月24日,昨天休假了,没有工作,今天学习了tk中的标签与按钮控件,我准备用标签显示地图,用按钮做泥土。但是按钮的大小与标签的大小我控制不好。CSDN上有一个叫荷蒲的博主关于tk帖子不错,链接如下:

https://so.csdn/so/search?q=tkinter&t=blog&u=hepu8

今天就到这里,太累了,明天要弄明白按钮的大小与标签大小都是什么因素影响的。

今天是11月25日,今天仔细在研究按钮和标签的大小,我知道按钮是会按文本的大小来变化的,所以,我弄了好多的参数,有字体,字号,有内边距,有button自身属性的width和height。我测试了好多次,希望能找出其中的规律,但是,我失败了。标签的大小与字符像素的大小有很明确的公式关系,但是按钮的我实在找不到。我把我的结果截图放在这里,以后有时间再来研究。我用穷举发凑出了一个可以使标签大小与按钮大小一致的值。

今天先这样吧。明天将按钮与标签都放到frame中,自定义定义一个类。顺便明天看看扫雷的数字都用什么颜色.

今天是11月29日,最近几天是比较忙的,没有做什么事,仅仅看了扫雷数字的颜色,但是我对标签与按钮大小的事,有点过于深入了,我测试了Arial3-19字号的字符大小,并且看了对应的宽度增加的步长与高度增加的步长,然后我发现这是一个挺奇怪的序列,需要用三元一次方程来拟合,我觉得这应该是另外一个话题了,我要开另一个帖子来记录。明天绝对不再去想这个问题了。明天练习frame类。

传送门

今天是11月30日,今天其实做了挺多事,我把另一个帖子写了,另外我也弄了Frame模块,把主窗口做了一个类,把菜单做了一个类,另外做了两个Frame,一个用来放游戏信息,另一个用来放游戏的内容。

今天是12月1日,已经是下午四点多了,今天我把地图上的每一个格子都做成了一个类,是继承Label的类,每一块泥土也做成了一个类,是继承Button的。然后用place布局到游戏内容的框架当中去。但是,今天我遇到一个问题,应该是泥土先生成,然后点击第一块泥土时生成地图,我用Label做地图,但是后生成的组件会覆盖在先生成的组件的上面,也就是说地图被覆盖到泥土的上面了。我不知道tk能不能设置层级,我在百度中没有找到,我想如果不行的话,我不能在这上面浪费时间,我要换种思路了。比如说每开一块泥土然后生成一个Label。明天先把上面信息栏的东西弄完,今天不再想这件事了。对了,明天记得把框架与窗口的大小调一下,比生成的泥土来的小了一点。

今天是12月2日昨天几乎没有做什么工作,昨天在忙自己工作上的事,但是昨天我因为嫌字太细,加粗字体之后发现标签Lable的大小又变化了。然后顺便又测试了几个字号的按钮,发现在某些字号下加粗字体会增大按钮,但在有些字号下又会使按钮变小,这让我太抓狂了。

今天在下午的时候我又用枚举法找出了一系列的加粗后的修正,又是一串毫无规律的数字,小号一点的加不加粗都不影响大小,大号一点的有些影响,有些不影响。总的来说是pad值对加粗修正不影响,边框粗细对加粗修正不影响,边框形式也不影响,但是height与width的值会影响加粗的修正值,并且是成1倍的关系。见下面表格。

另外,今天还修改了窗口的大小,发现geometry参数好像是包含了菜单栏,标题栏的高度的。我对窗口的大小也开始有点迷茫了,明天再弄吧。

今天是12月6日,今天还是做了不少事情,但是今天有点不舒服,状态不太好。今天终于将窗口的大小调整好了,发现了一个有趣的现象,如果我不使用菜单,窗口的高度就是所有内容加起来的高度,如果我使用了菜单,就要额外增加20。即tk.geometry中高度的参数额外加20。另外菜单的字体我是控制不了的,即我不能通过改变菜单字体来实现控制额外增加的高度参数,这个20也是我用眼睛一点一点对比出来的,可能也不太准,菜单的字体就是windows控制面板中设置的那个主题字体。但是如果我将窗口锁定,即我使用tk.resizable方法,将height的参数调整为False的话。我又不需要考虑菜单的那20的高度了,直接将内容的高度加起来就好,真是有趣又意外。今天另外一个收获就是将主窗口的类好好的整理了一下,现在我可以将每一块泥土单独的调用出来了,今后点击泥土时可以显示泥土下的数字,我准备把生成的地图放到主窗口的类里面,与泥土的编号对应起来。同时将地图数字传递给泥土的类。明天先把信息窗口的东西收拾一下吧,我还不知道怎么做计时器,怎么实时更新。我还不知道怎么显示剩余雷数。另外,系统默认的字体里面好像是没有像计算器那样的数字字体的。我决定先用别的字体代替一下,我先用Stencil字体来代替。

今天是12月7日今天我的问答有位高手告诉了我tkraise()函数,可以将先生成的部件提升到前面来。于是今天我今天的时间都花在了修改之前的策略上了。现在的状态是能出现泥土,点击泥土,泥土会消失显露出下面的数字,如果是0就什么都不显示,如果是9,还是显示9。我测试了一下,生成的时间实在是太感人了,能有明显的看到码方块的一个过程。今天我累了,今天状态还是不太好,我想明天再按我原来的策略试试,即生成的地图在背后,点击某一块泥土的时候才生成那一块地图。这个版本保存为view0.2,试玩之后再弄时间和剩余雷数那两个标签吧。后面再把9换成雷的图片。等把这两个弄好了,我就贴一张图片上来,我觉得可以算是把界面层的工作告一段落了。

今天是12月8日,今天我按我昨天的想法把地图生成在背后,点击泥土时再生成这一小块地图,初始化的过程并没有变快,还是能明显看到码方块的过程,但我还是想继续按这个思路做下去,我觉得这样更加合理一些。昨天的那个版本被保存为了view0.11.另外,今天把剩余雷数的标签弄上去了,就是字体丑,我又换成了Consolas字体。明天弄上时间框和中间的emoj按钮

今天是12月9日,发薪日,心情不错,身体也觉得舒服多了,状态也不错。今天算是把界面层的东西都弄好了,有时间框,但时间框的时间不会动,有中间按钮,但是按钮背后的动作还没做,有雷数框,但因为右键的动作还没有做,所以雷数不会随着右键而减少。菜单也弄好了,但后面的命令都没有做。现在地图上的数字9已经可以显示成地雷了。我给图片涂颜色图了好久,涂了两张,一张的背景色与地图背景色一致,另一张地雷的背景色是红色的,作为引爆的那颗雷。先上两张图片吧。我觉得可以算是界面层的一个小节点。

    

 另外,再说一下这两天在按钮和标签上贴图的发现,我发现当按钮和标签上面是文本的时候height与width属性会以参数的形式进入部件的大小的运算,但如果部件上面是图片的时候,这两个属性好像是直接表示部件的高和宽,为什么好像要加粗,因为我没有验证过。等做完扫雷了有时间了我再去验证一下,我再去补充我的另一篇文章。

 我是11月17日开始做的,到今天正好是完整的3周,做运算层只花了1、2天的时间,后面一直都在弄界面。细想一下,是我对tk不熟悉,为了控制标签与按钮大小我花了不少的时间,相信后面应该会越来越顺利的。明天开始做控制层。

今天是12月10日,今天又开始不舒服了,今天还有一些别的事,今天不做了。下周一还要出差一趟。下周二再说吧。周二之前先不去想了。

今天是12月20日,上周因为出差,出了一个星期,什么都没有做,感觉自己懈怠了,有了点成绩而放松了。今天回看之前的代码,我想做emoj按钮的开始功能,也想做计时器,也想做菜单的命令。但是脑子里一团浆糊,什么都做不好。再看看之前做过的《小蜜蜂》的游戏,是不是我对窗口内的东西的构架方式不对,我应该给每件东西都设为类,包括计时器,包括emoj按钮。之后的工作是要再梳理一下自己的代码。我现在的很多功能是直接做在窗口类中的一个方法,可能这样的构架就不行。另外,今天看到几个帖子是关于鼠标操作的,把链接贴在这里,方便后面查

Python笔记之Tkinter(鼠标事件)——潇洒哥的CSDN博客

知乎上的一篇回答“实现一个按钮左右键各触发一种操作”

本文标签: 我想Python