本文链接:http://blog.csdn.net/e_one/article/details/52088983

1.  游戏规则

扫雷是玩法极其简单的小游戏,点击玩家认为不存在雷的区域,标记出全部地雷所在的区域,即可获得胜利。当点击不包含雷的块的时候,可能它底下存在一个数,也可能是一个空白块。当点击中有数字的块时,游戏会展现当前点击块所包含的数字。当点击空白块时,地图会展开,形成一个大小和形状不规则的图形,该图形的边界是数字块,也可以想成展开的是一个被数字包围着的不规则图形。

1.1  数字生成规则

扫雷游戏中是通过数字来判断雷的位置的,那么,数字的生成规则是什么呢?假设游戏中只有一个雷,那么,他的将被1这个数字包围着,如果遇到边界就忽略。如图:

1

1

1

1

1

1

1

1

1

1

1

可见,游戏是先生成雷然后再根据雷的位置生成数字的,我们再看下面的图:

1

1

1

1

2

1

2

1

1

1

在上图中,块中有两个数字为2的块,它是数字叠加的结果,围绕着雷的区域重合了,重合的区域块的数字相加,该块的数字就会变成相加后的数字。

1.2  本博文的例子扫雷的规则

玩家需要把所有的空白块点开,留下玩家认为有雷的块,当所剩余的块数和雷的数量相等时,玩家胜利。如果在此之前,点到有雷的方块,玩家失败。

2.   游戏的算法和数据结构

2.1  空白块展开算法

空白块的展开几乎是扫雷游戏的核心了。上面说到,扫雷游戏时,点中空白块,游戏的地图块就会展开,我们可以观察到:空白块是一层一层展开的,所以,地图展开算法我们就用广度优先搜索。也许有人会问:可以用深度优先搜索算法吗?答案是可以的,但是如果在这里用的话,效率会比广度优先搜索算法效率低。

2.2 扫雷的数据结构

(1)方向数组

int[][] dir={

{-1,1},//左上角

{0,1},//正上

{1,1},//右上角

{-1,0},//正左

{1,0},//正右

{-1,-1},//左下角

{0,-1},//正下

{1,-1}//右下角

};

方向数组在展开空白块的时候回用到,因为广度优先遍历就是在地图中朝各个方向走。

(2)Tile类

该类表示游戏中的“块”,我们给它声明三个成员。

short    value;

boolean flag;

boolean open;

value存储该块的值。-1表示雷块;0表示空白块;>0代表数字块。

flag存储该雷是否被玩家标记(在本例子中无作用,保留,方便扩展)。

open存储该块是否被用户点开过。

(3)Tile数组

Tile数组代表块的集合,及游戏的地图,存储着游戏的主要数据。

(4)Point类

Point类代表“位置”,声明Point类方便我们在地图中生成随机位置的雷。Point类还要重写hashCode和equals方法,为了比较位置与位置是否相同。

(5)Mine类

对上面的数据结构的封装。

Mine构造函数:对游戏地图的参数设置,比如绘制的位置,绘制的大小,块的大小,生成的雷数等。

init()方法:清空并初始化游戏地图。

create(Point p)方法:在地图中随机生成雷的位置,并产生数字。参数p是不产生雷的位置,p点可以传入用户第一次点击时的位置。生成随机位置的雷比较快速的办法是:先把地图中除p位置外所有的位置加入到链表中,然后生成0到链表大小-1之间的随机数,根据生成的随机数在链表中取元素,取完元素就把该位置从链表中移除,并把Tile数组中该位置的Tile的value设为-1。重复执行以上操作,直到生成的雷个数满足要求。产生数字的办法:遍历Tile数组,遇到雷就将他身边的八个的位置的value值加1,如果八个位置中有雷,或者该位置不存在,不执行任何操作。

open(Point p,boolean isFirst)方法:p代表点开某个位置的块,即Tile数组的索引。isFirst传入是否是第一次点击屏幕。该方法要对是不是第一次点击而作不同的操作,当玩家第一次点击块时,调用create函数生成地图。否则就进行展开地图等操作。

(6)MainView类

视图类,负责绘图和操作Mine对象。

3.   代码示例

Mine.java

public class Mine {public   int x;//地图的在屏幕上的坐标点public   int y;//地图的在屏幕上的坐标点public    int mapCol;//矩阵宽public   int mapRow;//矩阵高private  int mineNum ;public static short EMPTY=0;//空public static short MINE=-1;//雷public Tile[][] tile;//地图矩阵public   int tileWidth;//块宽private  Paint textPaint;private Paint bmpPaint;private  Paint tilePaint;private  Paint rectPaint;private  Paint minePaint;private Random rd=new Random();public  int mapWidth;//绘图区宽public int mapHeight;//绘图区高public boolean isDrawAllMine=false;//标记是否画雷private  int[][] dir={{-1,1},//左上角{0,1},//正上{1,1},//右上角{-1,0},//正左{1,0},//正右{-1,-1},//左下角{0,-1},//正下{1,-1}//右下角};//表示八个方向public   class Tile{short value;boolean flag;boolean open;public Tile(){this.value=0;this.flag=false;this.open=false;}}public static class Point{private int x;private int y;public Point(int x,int y){this.x=x;this.y=y;}@Overridepublic int hashCode() {// TODO Auto-generated method stubreturn 2*x+y;}@Overridepublic boolean equals(Object obj) {// TODO Auto-generated method stubreturn this.hashCode()==((Point)(obj)).hashCode();}}//表示每个雷块public Mine(int x, int y, int mapCol, int mapRow, int mineNum, int tileWidth){this.x=x;this.y=y;this.mapCol = mapCol;this.mapRow = mapRow;this.mineNum=mineNum;this.tileWidth=tileWidth;mapWidth=mapCol*tileWidth;mapHeight=mapRow*tileWidth;textPaint=new Paint();textPaint.setAntiAlias(true);textPaint.setTextSize(MainActivity.W/10);textPaint.setColor(Color.RED);bmpPaint=new Paint();bmpPaint.setAntiAlias(true);bmpPaint.setColor(Color.DKGRAY);tilePaint =new Paint();tilePaint.setAntiAlias(true);tilePaint.setColor(0xff1faeff);minePaint =new Paint();minePaint.setAntiAlias(true);minePaint.setColor(0xffff981d);rectPaint =new Paint();rectPaint.setAntiAlias(true);rectPaint.setColor(0xff000000);rectPaint.setStyle(Paint.Style.STROKE);tile=new Tile[mapRow][mapCol];}/*** 初始化地图*/public  void init(){for (int i = 0; i< mapRow; i++){for (int j = 0; j< mapCol; j++){tile[i][j]=new Tile();tile[i][j].value=EMPTY;tile[i][j].flag=false;tile[i][j].open=false;isDrawAllMine=false;}}}/*** 生成雷* @param exception 排除的位置,该位置不生成雷*/public void create(Point exception){List<Point> allPoint=new LinkedList<Point>();//把所有位置加入链表for (int i = 0; i< mapRow; i++)//y{for (int j = 0; j < mapCol; j++)//x{Point point=new Point(j,i);if(!point.equals(exception)){allPoint.add(point);}}}List<Point> minePoint=new LinkedList<Point>();//随机产生雷for (int i=0; i< mineNum; i++){int idx=rd.nextInt(allPoint.size());minePoint.add(allPoint.get(idx));allPoint.remove(idx);//取了之后,从所有集合中移除}//在矩阵中标记雷的位置for(Iterator<Point> it=minePoint.iterator();it.hasNext();){Point p=it.next();tile[p.y][p.x].value=MINE;}//给地图添加数字for (int i = 0; i< mapRow; i++)//y{for (int j = 0; j< mapCol; j++)//x{short t=tile[i][j].value;if(t==MINE){for (int k=0;k<8;k++){int offsetX=j+dir[k][0],offsetY=i+dir[k][1];if(offsetX>=0&&offsetX< mapCol &&offsetY>=0&&offsetY< mapRow ) {if (tile[offsetY][offsetX].value != -1)tile[offsetY][offsetX].value += 1;}}}}}}/*** 打开某个位置* @param op* @param isFirst 标记是否是第一次打开*/public void open(Point op,boolean isFirst){if(isFirst){create(op);}tile[op.y][op.x].open=true;if( tile[op.y][op.x].value==-1)return;else if( tile[op.y][op.x].value>0)//点中数字块{return;}//广度优先遍历用队列Queue<Point> qu=new LinkedList<Point>();//加入第一个点qu.offer(new Point(op.x,op.y));//朝8个方向遍历for (int i=0;i<8;i++){int offsetX=op.x+dir[i][0],offsetY=op.y+dir[i][1];//判断越界和是否已访问boolean isCan=offsetX>=0&&offsetX< mapCol &&offsetY>=0&&offsetY< mapRow;if(isCan){if(tile[offsetY][offsetX].value==0 &&!tile[offsetY][offsetX].open) {qu.offer(new Point(offsetX, offsetY));}else if(tile[offsetY][offsetX].value>0){tile[offsetY][offsetX].open=true;}}}while(qu.size()!=0){Point p=qu.poll();tile[p.y][p.x].open=true;for (int i=0;i<8;i++){int offsetX=p.x+dir[i][0],offsetY=p.y+dir[i][1];//判断越界和是否已访问boolean isCan=offsetX>=0&&offsetX< mapCol &&offsetY>=0&&offsetY< mapRow;if(isCan){if( tile[offsetY][offsetX].value==0&&!tile[offsetY][offsetX].open) {qu.offer(new Point(offsetX, offsetY));}else if(tile[offsetY][offsetX].value>0){tile[offsetY][offsetX].open=true;}}}}}/*** 绘制地图* @param canvas*/public  void draw(Canvas canvas){for (int i = 0; i< mapRow; i++){for (int j = 0; j< mapCol; j++){Tile t=tile[i][j];if(t.open){if(t.value>0){canvas.drawText(t.value+"",x+j*tileWidth,y+i*tileWidth+tileWidth,textPaint);}}else{//标记,备用if(t.flag){}else{//画矩形方块RectF reactF=new RectF(x+j*tileWidth,y+i*tileWidth,x+j*tileWidth+tileWidth,y+i*tileWidth+tileWidth);canvas.drawRoundRect(reactF,0,0, tilePaint);}}//是否画出所有雷if( isDrawAllMine&&tile[i][j].value==-1) {canvas.drawCircle((x + j * tileWidth) + tileWidth / 2, (y + i * tileWidth) + tileWidth / 2, tileWidth / 2, bmpPaint);}}}//画边框canvas.drawRect(x,y,x+mapWidth,y+mapHeight, rectPaint);//画横线for (int i = 0; i< mapRow; i++) {canvas.drawLine(x,y+i*tileWidth,x+mapWidth,y+i*tileWidth, rectPaint);}//画竖线for (int i = 0;i < mapCol; i++) {canvas.drawLine(x+i*tileWidth,y,x+i*tileWidth,y+mapHeight, rectPaint);}}}

MainView.java

public class MainView extends View {private   Mine mine;private  boolean isFirst=true;//标记是否是本局第一次点击屏幕private  Context context;private final int mineNum=10;//产生的雷的个数private  final int ROW=15;//要生成的矩阵高private  final int COL=8;//要生成的矩阵宽private   int TILE_WIDTH=50;//块大小private  boolean isFalse=false;public  MainView(Context context){super(context);this.context=context;TILE_WIDTH=MainActivity.W/10;mine=new Mine((MainActivity.W-COL*TILE_WIDTH)/2,(MainActivity.H-ROW*TILE_WIDTH)/2,COL,ROW,mineNum,TILE_WIDTH);try {mine.init();}catch (Exception e){e.printStackTrace();}}/*** 游戏逻辑*/public void logic(){int count=0;for (int i=0;i<mine.mapRow;i++){for (int j=0;j<mine.mapCol;j++){if(!mine.tile[i][j].open){count++;}}}//逻辑判断是否胜利if(count==mineNum){new AlertDialog.Builder(context).setMessage("恭喜你,你找出了所有雷").setCancelable(false).setPositiveButton("继续", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {mine.init();invalidate();isFirst=true;}}).setNegativeButton("退出", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {System.exit(0);}}).create().show();}}/*** 刷新View* @param canvas*/@Overrideprotected void onDraw(Canvas canvas) {mine.draw(canvas);}/*** 点击屏幕事件* @param event* @return*/@Overridepublic boolean onTouchEvent(MotionEvent event) {if(event.getAction()==MotionEvent.ACTION_DOWN){int x=(int)event.getX();int y=(int)event.getY();//判断是否点在范围内if(x>=mine.x&&y>=mine.y&&x<=(mine.mapWidth+mine.x)&&y<=(mine.y+mine.mapHeight)){int idxX=(x-mine.x)/mine.tileWidth;int idxY=(y-mine.y)/mine.tileWidth;mine.open(new Mine.Point(idxX,idxY),isFirst);isFirst=false;if(mine.tile[idxY][idxX].value==-1){mine.isDrawAllMine=true;new AlertDialog.Builder(context).setCancelable(false).setMessage("很遗憾,你踩到雷了!").setPositiveButton("继续", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {mine.init();isFalse=true;isFirst=true;invalidate();}}).setNegativeButton("退出", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {System.exit(0);}}).create().show();}if(isFalse){isFalse=false;invalidate();return true;}logic();invalidate();}}return true;}
}

MainActivity.java

public class MainActivity extends Activity {public  static  int W;public  static  int H;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);DisplayMetrics dm = new DisplayMetrics();getWindowManager().getDefaultDisplay().getMetrics(dm);W = dm.widthPixels;//宽度H = dm.heightPixels ;//高度setContentView(new MainView(this));new AlertDialog.Builder(this).setCancelable(false).setTitle("游戏规则").setMessage("把你认为不是雷的位置全部点开,只留着有雷的位置,每局游戏有10个雷。\n\n--卧槽工作室").setPositiveButton("我知道了",null).create().show();}
}

github:https://github.com/luoyesiqiu/Mine

【Android】自己动手做个扫雷游戏相关推荐

  1. [Android]自己动手做个拼图游戏

    目标 在做这个游戏之前,我们先定一些小目标列出来,一个一个的解决,这样,一个小游戏就不知不觉的完成啦.我们的目标如下: 1. 游戏全屏,将图片拉伸成屏幕大小,并将其切成若干块. 2. 将拼图块随机打乱 ...

  2. 自己动手 做rpg小游戏

    我是一位非常狂热的玩家,可以说我的业余时间大部分都用在玩游戏上了.有一天,我突发奇想,决定自己也做一款游戏来试试.后来上网一查,发现市面上有非常多的游戏制作工具,但绝大多数需要美工和程序的基础.就在我 ...

  3. 扫雷程序设计Android答辩,基于QT的扫雷游戏设计与实现扫雷游戏答辩.ppt

    基于QT的扫雷游戏设计与实现扫雷游戏答辩.ppt 程序设计 基于QT语言的扫雷游戏,答辩学生,一.分析扫雷游戏的基本功能,1.从外观上分析 方块 笑脸 计时器 有雷标识 无雷标识 胜利画面 失败画面, ...

  4. 自己动手做个小游戏(1)

    我是一个编程新人,完全没有编程经验,正在自学编程,最近看了网易公开课的cs50,看到讲师展示的一个类似华容道的小游戏,于是决定自己写出来,也算是对前几节视频内容的一个巩固. 小游戏根据输入数字形成一个 ...

  5. 用python自己动手做一个小游戏01

    今天我们来用python做一款属于自己的小游戏--贪吃蛇.众所周知哈,pygame入门级游戏应该是非贪吃蛇莫属了,只需要一点简单的算法就能够完美还原童年的感觉.当然了,目前网上的贪吃蛇数量众多,质量参 ...

  6. 自己动手做个小游戏(2)

    随机显示矩阵已经完成了,接下来就是怎么根据输入移动数字 1.首先需要一个issort函数,判断是否排序完成,如果否,则printf输入需要移动的数字,然后根据输入找到要移动的数字,找到下划线的位置,判 ...

  7. 自做贪吃蛇游戏的android实现

    一直想自己动手做一个手机游戏,安装在自己的手机上,尽管应用或许会看起来很简单效果也不是很拉风,可是自己做的,那心情那感觉终究是不一样.今天,让我们一起探秘贪吃蛇游戏,用自己的双手玩转java代码打造属 ...

  8. Java写一个简单的扫雷游戏

    〇.目录 一.前言 二.过程中遇到的困难 三.代码 四.成品图 五.代码存在的bug 六.完善建议 七.结语 一.前言 这个学期学习了Java,课程的最后一项作业就是做一个扫雷游戏和一个计算器,经历一 ...

  9. HTML5实现经典Windows扫雷游戏

    上周末突然想尝试着做一个扫雷游戏.扫雷游戏我是很喜欢玩的,更年轻时候,晚上晚上几点钟的时候都有... 虽然之前没有写过HTML5游戏,不过我感觉肯定能写出来的.虽然对于写H5游戏有哪些步骤什么的都不是 ...

最新文章

  1. Android LayoutInflater 的使用
  2. 群晖套件来源_群晖套件系列五:Moments的安装与使用
  3. 5G频谱相争“兵戎相见”各相部署风起云涌
  4. shop--10.商品--商品添加(前端)及问题的解决
  5. ios开发 多人语音聊天_手游语音市场的现状、机遇与挑战
  6. Samba Linux 和windows 共享
  7. 【软件工程】软件文档
  8. 域服务器批量修改用户密码,Windows域控制器批量创建用户
  9. pcb设计单点接地示意图_EMC设计之接地、PCB布局布线、屏蔽设计
  10. Pandas官方文档(中文版)Pdf
  11. xposed 入门之修改手机 IMEI
  12. mtk android 源代码下载,MTK手机开发之路--MTK源代码下载
  13. 电脑版微信多开的三种方法
  14. ios8 xcode设置launchimage
  15. 移动魔百盒CM211-1_YS代工_S905L3B_RTL8822C_线刷固件包
  16. python判断手机号运营商_匹配手机号码及运营商校验
  17. SOLIDWORKS如何简单高效地自动生成模型视图和标注
  18. Unity UI跟随鼠标移动
  19. UVa 12325 - Zombie's Treasure Chest(暴力枚举+预处理)
  20. elasticsearch: 查询过滤某个字段值的长度

热门文章

  1. [C# VSTO Word]word中段落回车符到底是什么?来看看它的真面目,是否和你想象的一样?
  2. word2vec初步使用
  3. 《小狗钱钱》--chapter14~18--生活顺风顺水了起来
  4. 推荐一个轻松搞定数据加密解密神器!
  5. slides.com 导出PDF
  6. javaScript面试高频技术点(多为原生基础+框架集合)
  7. 三菱FX3U/FX1N底层源码,PLSR源码, 总体功能和指令可能支持在RUN中下载程序,支持注释的写入和读取,支持脉冲输出与定位指令(包括PLSY /PWM/PLSR/PLSV/DRVI /DRV
  8. MyBatis-Plus 扩展篇 > 自动填充功能
  9. 9个Excel小技巧,提高你的数据分析效率
  10. 记录下我磕磕碰碰的三个月找工作经历,好文推荐