〇、目录
一、前言
二、过程中遇到的困难
三、代码
四、成品图
五、代码存在的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.设置插旗子的功能。如果可以的话,最好用图片而不是字符。
七、结语
欢迎评论留言交流,有错误或者修改建议请评论留言,可能回复不及时请见谅。