写在开始
上一篇博客的链接为:用java实现扫雷小游戏,不再枯燥学语法(三)
任务清单
博主做这个项目给自己列的任务清单如下:
任务一:创建窗口(已完成)
任务二:窗口布局(已完成)
任务三:定义数据结构(已完成)
任务四:实现游戏的基础功能(布雷,查雷,排雷,判定输赢)
任务五:辅助功能(待开数,已开数,游戏重开,计时器)
代码实现
package game;
import javax.swing.*;
import java.awt.*;
import java.util.Random;
import static game.Game.*;
public class Function{
// 添加地雷
public void add(){
Random random=new Random();
for(int i=0;i<LEICOUNT;){
// 使用随机数生成地雷
int r=random.nextInt(ROW);
int c=random.nextInt(COL);
// 避免生成位置相同
if(data[r][c]!=LEICODE){
data[r][c]=LEICODE;
i++;
}
}
// 计算周围雷的数量
for (int i=0;i< ROW;i++){
for(int j=0;j<COL;j++){
if(data[i][j]==LEICODE) continue;
int tempcount=0;
if(i>0 && j>0 && data[i-1][j-1]==LEICODE)
tempcount++;
if(i>0 && data[i-1][j]==LEICODE)
tempcount++;
if(i>0 && j<COL-1 && data[i-1][j+1]==LEICODE)
tempcount++;
if(j>0 && data[i][j-1]==LEICODE)
tempcount++;
if(j<COL-1 && data[i][j+1]==LEICODE)
tempcount++;
if(i<ROW-1 && j>0 && data[i+1][j-1]==LEICODE)
tempcount++;
if(i<ROW-1 && data[i+1][j]==LEICODE)
tempcount++;
if(i<ROW-1 && j<COL-1 && data[i+1][j+1]==LEICODE)
tempcount++;
// 格子中记录周围炸弹数
data[i][j]=tempcount;
}
}
}
// 检验是否胜利
public void checkWin(){
int count=0;
for(int i=0;i<ROW;i++){
for(int j=0;j<COL;j++){
if(buttons[i][j].isEnabled())count++;
}
}
if(count==LEICOUNT){
// 输出提示信息
JOptionPane.showMessageDialog(Windows.frame,"你成功了!");
}
}
// 打开格子
public void openCell(int i,int j){
JButton btn=buttons[i][j];
// 去除已经打开的格子
if(!btn.isEnabled())
return;
// 设置打开格子的效果
btn.setEnabled(false);
btn.setOpaque(true);
btn.setBackground(Color.green);
btn.setText(data[i][j]+"");
// 使用递归的思想,实现级联打开
if(data[i][j]==0){
if(i>0 && j>0 && data[i-1][j-1]==0)
openCell(i-1,j-1);
if(i>0 && data[i-1][j]==0)
openCell(i-1,j);
if(i>0 && j<COL-1 && data[i-1][j+1]==0)
openCell(i-1,j+1);
if(j>0 && data[i][j-1]==0)
openCell(i,j-1);
if(j<COL-1 && data[i][j+1]==0)
openCell(i,j+1);
if(i<ROW-1 && j>0 && data[i+1][j-1]==0)
openCell(i+1,j-1);
if(i<ROW-1 && data[i+1][j]==0)
openCell(i+1,j);
if(i<ROW-1 && j<COL-1 &&data[i+1][j+1]==0)
openCell(i+1,j+1);
}
}
// 检验是否踩雷
public void Lose(){
for(int i=0;i<ROW;i++){
for(int j=0;j<COL;j++){
JButton btn= buttons[i][j];
if(data[i][j]==LEICODE){
btn.setBackground(Color.red);
btn.setEnabled(false);
}else{
btn.setEnabled(false);
btn.setOpaque(true);
btn.setText(data[i][j]+"");
}
}
}
// 输出提示信息
JOptionPane.showMessageDialog(Windows.frame,"你踩雷了!");
}
}
总结
这个任务里用到的最重要的编程思想是递归。
那么什么是递归呢?
举个栗子:从前有座山,山里有座庙,庙里有个和尚,和尚在讲故事,从前有座山,山里有座庙,庙里有个和尚,和尚在讲故事,从前有座山…
这是我们小时候都听到过的故事吧!其实这个故事就是一个简单的递归哦!
那么我们这里用到的递归函数,递归函数,顾名思义就要分成三个部分,正确的断句应该是递 | 归 | 函数。
最好理解的就是“函数”:我们写的就是一个函数,在面向对象的编程中通常成为方法。
接下来是“递”:“递”的意思呢就是这个函数运行的过程中调用自身,不断地传递。
最重要的便是“归”了:只有“递”,没有“归”的函数是个死循环,没有灵魂!所以在“递”的同时也要有判断的条件,产生返回值,这样才能“归”。
有了上述的三部分,便构成了完整的递归函数啦!
回到我们的实际应用中,打开格子中使用的级联打开中用到了递归的思想,既打开一个格子的同时判断该格子周围有没有数值为零未打开的格子,如果有就一起打开。
在openCell()方法中调用了自身方法来判断是否级联打开周围的格子。
有些小伙伴可以发现在判断条件中写了很多的if()语句,这是为什么呢?可以先来看看下附的这张图。
每次判断一个格子周围的时候,就要用到这么一个结构,那么这个结构会导致什么问题呢?
没错!就是发生数组越界的情况!
比如说:当判断的格子 a[i][j] 是这个地图的左上角,会发现 a[i-1] 这一行和 a[j-1] 这一列是不存在的,会产生数组越界的情况,所以必须要增加判断条件来避免这种情况的发生。
结语
如果说这篇文章有让你学到一定的知识的话,不妨点个赞和关注,让博主能够看到。如果讲解中有什么错误和疏忽,也劳烦在评论中指出或提问,博主会第一时间进行更新和答复,谢谢!