〇、目录
一、前言
二、过程中遇到的困难
三、代码
四、成品图
五、代码存在的bug
六、完善建议
七、结语
一、前言
这个学期学习了Java,课程的最后一项作业就是做一个扫雷游戏和一个计算器,经历一段时间的煎熬终于做出来了,就想来分享一下我的喜悦和成果。

在老师布置作业后,我的计算器代码就根据老师黑板上代码微调后很快就收工了,而这个扫雷也是有了明确的思路,但是却找不到相对应的代码来实现,后来在百度上看了一位前辈的扫雷代码终于解决了我问题。这里是那位前辈的扫雷代码链接

其实回过头来看,我写这个代码确实极其简陋,相比我在百度上找的各种大佬做出来的扫雷,感觉差距极大。但是想想是我自己做出来的还是很高兴的,但是也明白我还只是个小白。

二、过程中遇到的困难
老师一布置作业我就想出来了一个做法,那就是做两层组件,底层是label,可以用来显示数字和雷,如下图所示这样的效果,其中"!"表示随机生成的雷。
图1
再在这上面盖一层相同数量button组件,点击一个button,这个button就消失,显示出下层的label。

当我开始写代码时,我就卡在了这一步。我定义一个panel并设置了20*20的gridlayout(网格布局),当我添加进去400个label时,成功达到了上图的底层效果,但是当我再添加400个button时,就出现问题了,这个button不会重叠到label上面,而是继续向下排列,和label处于了同一层,然后百度了好久都没有解决问题,最后决定看看别人的代码是怎么做的。最后看了某位前辈的作品,原来他是清除了panel原有的layout,用坐标放置组件,这样就可以重叠了,于是后面我就很快的写好了。
(也许这个问题也有其他的解决方法,我感觉用gridlayout也可以做出的,知道怎么做的大佬可以说一下)

三、代码
最重要的代码来了

package testone;
import javax.swing.BorderFactory;
 import javax.swing.JButton;
 import javax.swing.JFrame;
 import javax.swing.JLabel;
 import javax.swing.JOptionPane;
 import javax.swing.JPanel;import java.awt.BorderLayout;
 import java.awt.Color;
 import java.awt.GridLayout;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.util.Arrays;public class Try1 {
 JFrame f;
 JPanel p,p1,p2;
 int n = 20; //扫雷正方形界面的的边长(只能手动修改控制难度)
 int boomnumber = 50; //设置雷的个数(只能手动修改控制难度)后续可以添加按键修改难度
 JButton zuobi,restart; //作弊和重新开始的按钮
 JButton b[][] = new JButton[n][n];
 JLabel l[][] = new JLabel[n][n]; //下层显示的label
 int boxy[][]; //存储雷的横纵坐标的数组
 int visited[][] = new int[n][n]; //被访问记录,访问过为1
 Try1() {
f = new JFrame("扫雷");
	f.setBounds(600, 200, 495, 545);
	p = new JPanel();    //扫雷游戏面板
	p.setLayout(null);   //清除原有的layout,后面使用坐标放置组件
	p1 = new JPanel();   //最底层面板
	p2 = new JPanel();   //放置功能按键的面板
	p1.setLayout(new BorderLayout());
	p2.setLayout(new GridLayout(1,3)); //网格布局,一行三列
	p.setBackground(new Color(193, 210, 240));
	makeboom(n, p);   //自己定义的函数,生成雷和数组label的
	makebutton(n, p); //生成扫雷按钮的,会覆盖在底层label上
	zuobi =new JButton("作弊");
	zuobi.addActionListener(new ActionListener() {
		@Override
		public void actionPerformed(ActionEvent e) {
			for (int i[]:boxy) {
				b[i[0]][i[1]].setText(".");
			}
		}
	});
	restart = new JButton("重新开始"); 
	restart.addActionListener(new ActionListener() {
		@Override
		public void actionPerformed(ActionEvent e) {
			f.dispose();  //关闭
			new Try1();
		}
	});
	p1.add(p,BorderLayout.CENTER);
	p1.add(p2,BorderLayout.NORTH);
	p2.add(restart);
	p2.add(zuobi);
	f.add(p1);
	f.setVisible(true);
	f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}

public void makebutton(int n, JPanel a) {
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < n; j++) {
			a.add(b[i][j] = new JButton());
			b[i][j].setBounds(j * 24, i * 24, 24, 24);
			b[i][j].addActionListener(new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					// TODO Auto-generated method stub
					JButton d = (JButton) e.getSource();
					int index = 0;
					int indey = 0;
					for (int i = 0; i < n; i++) { //获取点击的按钮的坐标
						for (int j = 0; j < n; j++) {
							if (d == b[i][j]) {
								index = i;
								indey = j;
							}
						}
					}
					open(index,indey);
				}
			});
		}
	}
}

public void makeboom(int n, JPanel a) {
	boxy = boom(n, boomnumber);
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < n; j++) {
			l[i][j] = new JLabel("0", JLabel.CENTER);
			l[i][j].setBounds(j * 24, i * 24, 24, 24);
			l[i][j].setBorder(BorderFactory.createLineBorder(Color.GRAY));
			l[i][j].setOpaque(true);
			l[i][j].setVisible(false);
			a.add(l[i][j]);
		}
	}
	for (int i = 0; i < boomnumber; i++) {
		l[boxy[i][0]][boxy[i][1]].setText("!");
		visited[boxy[i][0]][boxy[i][1]] = 1;
	}
	setshuzi();
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < n; j++) {
			if(l[i][j].getText().equals("0")) {
				l[i][j].setText("");
			}
		}
	}
}


public  int[][] boom(int n, int m) { // n是网格最大长宽,m是雷的个数
	int[][] boxy = new int[m][2];
	int cishu = 0;
	while (cishu != m) {
		int x = (int)(Math.random() * n);
		int y = (int)(Math.random() * n);
		if(isrepeat(x,y,boxy,cishu) == 0){
			boxy[cishu][0] = x;
			boxy[cishu][1] = y;
			cishu++;
		}
	}
	return boxy;
}

public int isrepeat(int x, int y, int a[][], int cishu) {
	int is = 0;
	for (int i = 0; i < cishu; i++) {
		if (a[i][0] == x && a[i][1] == y) {
			is = 1;
			break;
		} else {
			is = 0;
		}
	}
	return is;
}

public void setshuzi() {
	for (int[] i:boxy) {
		int x = i[0];
		int y = i[1];
		for(int u = x-1;u<x+2;u++) {
			for(int v = y-1;v<y+2;v++) {
				
				if(u>=0&&u<n&&v>=0&&v<n) {
					String s = l[u][v].getText();
					if(!s.equals("!")) {
						
						int lnumb = Integer.parseInt(s);
						lnumb ++;
						l[u][v].setText(lnumb+"");
					}
				}
			}
		}
	}
}

public void open(int x,int y) {
	b[x][y].setVisible(false);
    l[x][y].setVisible(true);
    visited[x][y]=1;
    if(iswin()) {
    	JOptionPane.showMessageDialog(null,"游戏获胜","游戏获胜",JOptionPane.PLAIN_MESSAGE);
    }else {
    	if(l[x][y].getText().equals("!")) {
    		JOptionPane.showMessageDialog(null,"您失败了","游戏结束",JOptionPane.PLAIN_MESSAGE);
    	}else if(l[x][y].getText().equals("")){
    		
    		for(int u = x-1;u<x+2;u++) {
    			for(int v = y-1;v<y+2;v++) {
    				if(u>=0&&u<n&&v>=0&&v<n&&visited[u][v]!=1) {
    					open(u,v);
    				}
    			}
    		}
    	}else {
    		
    	}
    }
}

public boolean iswin() {
	int last =-1;
	for(int i[]:visited) {
		if(Arrays.binarySearch(i, 0)< 0) {
			last = 1;
		}else {//只要存在1个没有访问的,就判定没有赢,退出循环
			last = -1;
			break;
		}
	}
	if(last==1) {
		return true;
	}else {
		return false;
	}
}
public static void main(String[] args) {
	Try1 t = new Try1();
}

}
四、成品图
在这里插入图片描述

五、代码存在的bug
1.游戏获胜判断提前:
游戏获胜判断每次点击按钮都要判断的,如果所有的位置都被访问过即可获胜(其中生成雷的位置提在生成雷时编辑为访问过的状态),但是在还未全部点击的情况,就会提醒游戏获胜。
2.游戏失败还能继续游戏:
这个问题可以解决,但是由于本人太懒了没写,只要在踩雷后使所有button不可见即可。
3.未知…(等待读者发现)

六、完善建议
1.雷的颜色可以设置高亮颜色,踩雷后更醒目。
2.添加可控难度的按钮,可以放到菜单栏。
3.设置插旗子的功能。如果可以的话,最好用图片而不是字符。

七、结语
欢迎评论留言交流,有错误或者修改建议请评论留言,可能回复不及时请见谅。