扫雷源码剖析(设计思想与实现)

转自:http://www.manongjc.com/article/23738.html

引言:在GitHub上面下载了一个扫雷的源程序,不过只有代码,没有注释和详细说明。以前从来也没玩扫雷,通过这次的学习顺便也弄懂了扫雷的玩法。下面附上github的源码地址:https://github.com/lxf44944/minesweeper_java

一、扫雷的总体设计思想

首先给还不知道的朋友介绍一下它的玩法,此次就以初级难度为例。首先,当你开始运行游戏的时候它是没地雷的,当你鼠标左键第一次点击触发扫雷事件后,才开始生成地雷,这样可以确保你不会一开始就gameover。初级的游戏是九颗地雷,因此会随机在除你点击的格子之外的地方随机生成,随后开始计算每个格子的周围一圈的地雷数量,并且赋值给这个格子对象。下面以一个图片来说明

我画紫色圈的部分就是中心的格子,可以明显看到,格子的周围红色一圈只有一颗雷,所以中心格子的地雷数量显示为1。同理,我们可以看到左下角3那个位置,它的周围已经有三个地雷,所以我们可以充分确定,还未点开的那一个是没地雷的。我们只要记住桌面的数字就是当前格子周围一圈的地雷数就行了。

二、源码剖析

首先给大家看一下程序的大致架构,并对几个重要的类和方法做出说明。

首先是bean目录下的Minelable类,它是一个格子对象。用来判断当前格子是否有地雷,是否被插了旗子,被点了几次等,下面给出几个字段属性

    private static final long serialVersionUID = 1L;
    //地雷标志
    private boolean mineTag;
    //空格标志
    private boolean expendTag;
    //插旗子标志
    private boolean flagTag;
    private int rowx;
    private int coly;
    //周围地雷数
    private int counAround;
    //记录右键点击次数
    private int rightClickCount;

main目录下的MainFrame类就是程序的主入口了,事件的绑定,界面的样式设置、生成都是从这执行,这部分可以自己参看源码,都是生成界面的常规代码。

panle目录下的三个类

BombJMenuBar是用来设置游戏界面的菜单导航条的,通过它可以调整游戏的难度,查看积分榜,帮助等信息,下面给出部分代码
    JMenu menuGame = new JMenu("游戏(G)");
    JMenu menuHelp = new JMenu("帮助(H)");
    JMenuItem menuItemStart = new JMenuItem("开局");
    JMenuItem menuItemC = new JMenuItem("初级");
    JMenuItem menuItemZ = new JMenuItem("中级");
    JMenuItem menuItemG = new JMenuItem("高级");
    JMenu menuHero = new JMenu("英雄榜");
    JMenuItem menuHeroC = new JMenuItem("初级英雄榜");
    JMenuItem menuHeroZ = new JMenuItem("中级英雄榜");
    JMenuItem menuHeroG = new JMenuItem("高级英雄榜");
    JMenuItem menuItemCustom = new JMenuItem("自定义");
    JMenuItem menuItemExit = new JMenuItem("退出");
    JMenuItem menuItemAbout = new JMenuItem("关于扫雷");
    JMenuItem menuItemHole = new JMenuItem("后门进入");
BombJPanel类就是玩家将要点击的格子的载体对象,下面给出重要代码
  listener = new Listener(labels, mainFrame);
  for (int i = 0; i < labels.length; i++) {
    for (int j = 0; j < labels[i].length; j++) {
        labels[i][j] = new MineLable(i, j);
        labels[i][j].setIcon(StaticTool.iconBlank);
        labels[i][j].addMouseListener(listener);
        this.add(labels[i][j]);
            }
        }
 

这段代码的意思遍历所有格子,给每个格子对象附上一个初始图片,也就是我们一开始进入扫雷看到的样子,并且给每个格子绑定上鼠标点解的监听事件,当我们鼠标左击或右击了就会调用相应的方法。接下来我们就来看看监听事件,这可是重中之重,前面这几个只是对游戏的初始化,游戏的逻辑与玩法实现都是在我们的监听事件中

listenner类里有两个类,一个是Listener,另一个是UserDefineListenner,它的作用是给用户自定义游戏规则,这个在此就不做过多赘述,这里着重讲解第一个Listenner类,因为它是我们这个扫雷游戏实现的核心逻辑代码,先给出代码块,当中已经给出关键注释。

@Override
    public void mousePressed(MouseEvent e) {
        MineLable mineLable = (MineLable) e.getSource();

        int row = mineLable.getRowx();
        int col = mineLable.getColy();

        if (e.getModifiersEx() == InputEvent.BUTTON1_DOWN_MASK
                + InputEvent.BUTTON3_DOWN_MASK) {
            isDoublePress = true;
            doublePress(row, col);

        }
        //鼠标左击按下事件
        else if (e.getModifiers() == InputEvent.BUTTON1_MASK
                && mineLable.isFlagTag() == false) {
            if (mineLable.isExpendTag() == false) {
                mineLable.setIcon(StaticTool.icon0);

            }
            mainFrame.getFaceJPanel().getLabelFace()
                    .setIcon(StaticTool.clickIcon);
        }
        //鼠标右击按下事件
        else if (e.getModifiers() == InputEvent.BUTTON3_MASK
                && mineLable.isExpendTag() == false) {
            if (mineLable.getRightClickCount() == 0) {
                mineLable.setIcon(StaticTool.flagIcon);
                mineLable.setRightClickCount(1);
                mineLable.setFlagTag(true);
                StaticTool.bombCount--;
                mainFrame.getFaceJPanel().setNumber(StaticTool.bombCount);

            } else if (mineLable.getRightClickCount() == 1) {
                mineLable.setIcon(StaticTool.askIcon);
                mineLable.setRightClickCount(2);
                mineLable.setFlagTag(false);
                StaticTool.bombCount++;
                mainFrame.getFaceJPanel().setNumber(StaticTool.bombCount);

            } else {
                mineLable.setIcon(StaticTool.iconBlank);
                mineLable.setRightClickCount(0);
            }

        }
    }

    @Override
    public void mouseReleased(MouseEvent e) {

        MineLable mineLable = (MineLable) e.getSource();
        int row = mineLable.getRowx();
        int col = mineLable.getColy();
        if (isDoublePress) {
            isDoublePress = false;
            if (mineLable.isExpendTag() == false
                    && mineLable.isFlagTag() == false) {
                backIcon(row, col);
            } else {

                boolean isEquals = isEquals(row, col);
                if (isEquals) {
                    doubleExpend(row, col);
                } else {
                    backIcon(row, col);

                }

            }
            mainFrame.getFaceJPanel().getLabelFace()
                    .setIcon(StaticTool.smileIcon);

        }
          //鼠标左击弹起事件
          else if (e.getModifiers() == InputEvent.BUTTON1_MASK
                && mineLable.isFlagTag() == false) {
            //判断是不是刚开始游戏
            if (StaticTool.isStart == false) {
                //如果是刚开始,置入地雷
                LayBomb.lay(this.mineLable, row, col);
                //设置isStart=true,表示不是第一次点击了
                StaticTool.isStart = true;

            }
            mainFrame.getTimer().start();

            //判断是否踩到地雷
            if (mineLable.isMineTag() == true) {
                //如果踩到地雷,游戏结束,显示全部的地雷
                bombAction(row, col);

                mineLable.setIcon(StaticTool.bloodIcon);
                mainFrame.getFaceJPanel().getLabelFace()
                        .setIcon(StaticTool.faultFaceIcon);
            } else {
                mainFrame.getFaceJPanel().getLabelFace()
                        .setIcon(StaticTool.smileIcon);
                expand(row, col);

            }

        }
        //判断雷是否已全被清除完
        isWin();
    }

当左键键释放后,会触发释放事件,此时如果是第一次左键释放,说明游戏才刚开始,因此放置地雷,具体代码实现请看tool文件夹的LayBoob类,放置完后还需计算每个格子周围的地雷数。

下面重点说一下当格子周围都没有地雷,沿四周自动扩充是怎么实现的,这里用了一个递归的算法思想,首先判断当前格子的周围炸弹数是否为0,如果为0,就显示递归的遍历它的周围几个格子,直到出现炸弹数为止,具体代码实现如下

        private void expand(int x, int y) {

        int count = mineLable[x][y].getCounAround();

        if (mineLable[x][y].isExpendTag() == false
                && mineLable[x][y].isFlagTag() == false) {

            if (count == 0) {
                mineLable[x][y].setIcon(StaticTool.num[count]);
                mineLable[x][y].setExpendTag(true);
                for (int i = Math.max(0, x - 1); i <= Math.min(
                        mineLable.length - 1, x + 1); i++) {
                    for (int j = Math.max(0, y - 1); j <= Math.min(
                            mineLable[x].length - 1, y + 1); j++) {
                        expand(i, j);

                    }

                }

            } else {

                mineLable[x][y].setIcon(StaticTool.num[count]);
                mineLable[x][y].setExpendTag(true);

            }

        }

    }

原文地址:https://www.cnblogs.com/yangf428/p/11072128.html

时间: 2024-11-02 22:23:22

扫雷源码剖析(设计思想与实现)的相关文章

Django对中间件的调用思想、csrf中间件详细介绍、Django settings源码剖析、Django的Auth模块

目录 使用Django对中间件的调用思想完成自己的功能 功能要求 importlib模块介绍 功能的实现 csrf中间件详细介绍 跨站请求伪造 Django csrf中间件 form表单 ajax csrf相关装饰器 在CBV上加csrf装饰器 Django settings源码剖析及模仿使用 Django settings源码剖析 查看内部配置文件 模仿使用 Auth模块 auth简介 auth模块常用方法 创建用户 校验用户名和密码 保存用户登录状态 判断当前用户是否登录 校验原密码 修改密

HashMap(2) 源码剖析(推荐)

今天看代码,想到去年发生的HashMap发生的CPU使用率100%的事件,转载下当时看的三个比较不错的博客(非常推荐) 参考:http://coolshell.cn/articles/9606.html   http://github.thinkingbar.com/hashmap-analysis/ http://developer.51cto.com/art/201102/246431.htm 在 Java 集合类中,使用最多的容器类恐怕就是 HashMap 和 ArrayList 了,所以

菜鸟nginx源码剖析数据结构篇(六) 哈希表 ngx_hash_t(上)

Author:Echo Chen(陈斌) Email:[email protected] Blog:Blog.csdn.net/chen19870707 Date:October 31h, 2014 1.哈希表ngx_hash_t的优势和特点 哈希表是一种典型的以空间换取时间的数据结构,在没有冲突的情况下,对任意元素的插入.索引.删除的时间复杂度都是O(1).这样优秀的时间复杂度是通过将元素的key值以hash方法f映射到哈希表中的某一个位置来访问记录来实现的,即键值为key的元素必定存储在哈希

SpringMVC源码剖析(二)- DispatcherServlet的前世今生

上一篇文章<SpringMVC源码剖析(一)- 从抽象和接口说起>中,我介绍了一次典型的SpringMVC请求处理过程中,相继粉墨登场的各种核心类和接口.我刻意忽略了源码中的处理细节,只列出最简单的类甚至是接口类,目的就是让大家先从最高层次的抽象意义上来审视SpringMVC这个框架:我也刻意将SpringMVC和Struts2做对比,目的是让大家看到,SpringMVC究竟吸取了Sturts2设计思想中的哪些精华,又弥补了它的哪些遗憾. DispatcherServlet作为SpringMV

SpringMVC源码剖析(四)- DispatcherServlet请求转发的实现

SpringMVC完成初始化流程之后,就进入Servlet标准生命周期的第二个阶段,即“service”阶段.在“service”阶段中,每一次Http请求到来,容器都会启动一个请求线程,通过service()方法,委派到doGet()或者doPost()这些方法,完成Http请求的处理. 在初始化流程中,SpringMVC巧妙的运用依赖注入读取参数,并最终建立一个与容器上下文相关联的spring子上下文.这个子上下文,就像Struts2中xwork容器一样,为接下来的Http处理流程中各种编程

《Apache Spark源码剖析》

Spark Contributor,Databricks工程师连城,华为大数据平台开发部部长陈亮,网易杭州研究院副院长汪源,TalkingData首席数据科学家张夏天联袂力荐1.本书全面.系统地介绍了Spark源码,深入浅出,细致入微2.提供给读者一系列分析源码的实用技巧,并给出一个合理的阅读顺序3.始终抓住资源分配.消息传递.容错处理等基本问题,抽丝拨茧4.一步步寻找答案,所有问题迎刃而解,使读者知其然更知其所以然 内容简介 书籍计算机书籍 <Apache Spark源码剖析>以Spark

boost.asio源码剖析(五) ---- 泛型与面向对象的完美结合

有人说C++是带类的C:有人说C++是面向对象编程语言:有人说C++是面向过程与面向对象结合的语言.类似的评论网上有很多,虽然正确,却片面,是断章取义之言. C++是实践的产物,C++并没有为了成为某某类型的语言而设计,而是一切以工程实践为目的,一切以提升语言能力为目的. 1983年C++诞生之时,由于兼容C语言而天生拥有了面向过程编程的能力:       1989年推出的2.0版,C++完善了对面向对象编程范式的支持:       1993年的3.0版,C++中引入了模板(template),

豆瓣Redis解决方案Codis源码剖析:Dashboard

豆瓣Redis解决方案Codis源码剖析:Dashboard 1.不只是Dashboard 虽然名字叫Dashboard,但它在Codis中的作用却不可小觑.它不仅仅是Dashboard管理页面,更重要的是,它负责监控和指挥各个Proxy的负载均衡(数据分布和迁移).并且,所有API都以RESTFul接口的形式对外提供,供Proxy和codis-config(Codis的命令行工具)调用.下面就来看一下数据分布和迁移的代码执行流程. Dashboard涉及到的知识点比较多,包括Martini框架

STL源码剖析 读书总结

<<STL源码剖析>> 侯捷著 很早就买了这本书, 一直没看, 现在在实验室师兄代码的时候发现里面使用了大量泛型编程的内容, 让我有了先看看这本书的想法. 看之前我对于泛型编程了解甚少, STL倒使用的比较熟练. 看完这本书之后, 只能表示以前对于STL的使用真是跟小孩玩似得, 只懂其冰山一角. 在真正的深入到源码之后, 对于STL中不容易理解的部分如 迭代器(iterator), 仿函数(functor), 配接器(adapter)才有了一个彻彻底底的了解, 这种东西不看源码光看