参考B站麦叔编程。
package saolei;
import com.sun.scenario.effect.impl.sw.java.JSWBlend_BLUEPeer;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
public class Saolei implements ActionListener {
//创建游戏窗口
JFrame frame = new JFrame(); //创建游戏页面
ImageIcon bannerIcon = new ImageIcon("b.png"); //创建banner按钮
JButton bannerBtn = new JButton(bannerIcon);
ImageIcon guessIcon = new ImageIcon("flag2.jpg");//创建guess按钮
JButton guessBtn = new JButton(guessIcon);
ImageIcon bombIcon = new ImageIcon("bomb.jpg"); //雷图标
ImageIcon failIcon = new ImageIcon("fail.png"); //失败提示
ImageIcon winIcon = new ImageIcon("win.png"); //成功提示
ImageIcon winFlagIcon = new ImageIcon("flag.jpg"); //成功旗帜图标
//数据结构
int ROW = 20; //行
int COL = 20; //列
int[][] data = new int[ROW][COL]; //data[i][j]设计者棋局
JButton[][] btns = new JButton[ROW][COL]; //JButton[i][j]玩家看到的棋局
int LEICOUNT = 60; //雷的总数
int LEICODE = -1; //雷:-1
int unopened = ROW * COL; //未开总数
int opened = 0; //已开总数
int seconds = 0; //时钟计数
JLabel label1 = new JLabel("待开" + unopened); //创建三个状态栏
JLabel label2 = new JLabel("已开" + opened);
JLabel label3 = new JLabel("用时" + seconds + "s");
Timer timer = new Timer(1000,this); //1s触发 1000ms = 1s
//显示界面
public Saolei() { //构造扫雷方法
frame.setSize(600, 700); //设置棋牌大小
frame.setResizable(false); //大小不可变
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //退出
frame.setLayout(new BorderLayout());
setHeader(); //创建状态栏
addLei(); //布雷
setButtons(); //加按钮
timer.start(); //界面显示之后,时钟立刻开始计时
frame.setVisible(true);
}
//布雷
private void addLei() {
Random rand = new Random(); //生成随机数
for (int i = 0; i < LEICOUNT; ){ //布满雷的总数为止
int r = rand.nextInt(ROW);
int c = rand.nextInt(COL);
if (data[r][c] != LEICODE) {
data[r][c] = LEICODE;
i++;
}
}
/*计算周边雷的个数
i-1,j-1 i-1,j i-1,j+1
i,j-1 i,j i,j+1
i+1,j-1 i+1,j i+1,j+1
*/
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 < 19 && data[i - 1][j + 1] == LEICODE) tempCount++;
if (j > 0 && data[i][j - 1] == LEICODE) tempCount++;
if (j < 19 && data[i][j + 1] == LEICODE) tempCount++;
if (i < 19 && j > 0 && data[i + 1][j - 1] == LEICODE) tempCount++;
if (i < 19 && data[i + 1][j] == LEICODE) tempCount++;
if (i < 19 && j < 19 && data[i + 1][j + 1] == LEICODE) tempCount++;
data[i][j] = tempCount;
}
}
}
//设置按钮
private void setButtons() {
Container con = new Container(); //容器
con.setLayout(new GridLayout(ROW, COL)); //设置20*20棋牌
for (int i = 0; i < ROW; i++) { //玩家棋局定义
for (int j = 0; j < COL; j++) {
JButton btn = new JButton(guessIcon);
btn.setOpaque(true);
btn.setBackground(new Color(244,183,113)); //设置背景
btn.addActionListener(this);
//JButton btn = new JButton(data[i][j]+"");
btn.setMargin(new Insets(0, 0, 0, 0)); //设置四周边距,便于数字展现
con.add(btn);
btns[i][j] = btn;
}
}
frame.add(con, BorderLayout.CENTER); //将con添加到frame
}
//设置标题、状态栏
private void setHeader() {
JPanel panel = new JPanel(new GridBagLayout());
GridBagConstraints c1 = new GridBagConstraints(0, 0, 3, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0);
panel.add(bannerBtn, c1);
bannerBtn.addActionListener(this); //使banner具有重新开始的功能
//定义三个状态栏
label1.setOpaque(true);
label1.setBackground(Color.white);
label1.setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY));
label2.setOpaque(true);
label2.setBackground(Color.white);
label2.setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY));
label3.setOpaque(true);
label3.setBackground(Color.white);
label3.setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY));
GridBagConstraints c2 = new GridBagConstraints(0, 1, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0);
panel.add(label1, c2);
GridBagConstraints c3 = new GridBagConstraints(1, 1, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0);
panel.add(label2, c3);
GridBagConstraints c4 = new GridBagConstraints(2, 1, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0);
panel.add(label3, c4);
frame.add(panel, BorderLayout.NORTH); //将panel添加到frame
}
@Override //实现功能
public void actionPerformed(ActionEvent e) {
if(e.getSource() instanceof Timer){ //实现计数功能
seconds++;
label3.setText("用时"+seconds+"s");
timer.start();
return;
}
JButton btn = (JButton)e.getSource();
if(btn.equals(bannerBtn)){ //banner实现重新开始功能
restart();
return;
}
for (int i = 0; i < ROW; i++) {
for (int j = 0; j < COL; j++) {
if (btn.equals(btns[i][j])) {
if(data[i][j] == LEICODE){ //踩雷————游戏失败
lose();
}else{
openCell(i,j); //自动开启相邻的零
checkWin(); //找到所有的雷————游戏成功
}
return;
}
}
}
}
//检查成功(没有开的数等于布雷数)
private void checkWin(){
int count = 0;//记录没有开的数量
for (int i = 0; i < ROW; i++) {
for (int j = 0; j < COL; j++) {
if(btns[i][j].isEnabled()) count++;
}
}
if(count == LEICOUNT) {
timer.stop();
for (int i = 0; i < ROW; i++) {
for (int j = 0; j < COL; j++) {
if(btns[i][j].isEnabled()) {
btns[i][j].setIcon(winFlagIcon);
}
}
}
bannerBtn.setIcon(winIcon);
JOptionPane.showMessageDialog(frame, "你赢了, Yeah!\n点击Banner重新开始", "赢了", JOptionPane.PLAIN_MESSAGE );
}
}
//判断失败(被按下的是雷)
private void lose(){
timer.stop();
bannerBtn.setIcon(failIcon);
for(int i=0;i<ROW;i++){
for(int j=0;j<COL;j++){
if (btns[i][j].isEnabled()) {
JButton btn = btns[i][j];
if (data[i][j]==LEICODE) {
btn.setEnabled(false);
btn.setIcon(bombIcon);
btn.setDisabledIcon(bombIcon);
}else{
btn.setIcon(null);
btn.setEnabled(false);
btn.setOpaque(true);
btn.setText(data[i][j] + "");
}
}
}
}
JOptionPane.showMessageDialog(frame,"可惜你爆雷了!\n点击banner重新开始","爆雷了", JOptionPane.PLAIN_MESSAGE);
}
//将相邻的零开启
private void openCell(int i,int j){
JButton btn = btns[i][j];
if(!btn.isEnabled()) return;
btn.setIcon(null);
btn.setEnabled(false);
btn.setOpaque(true);
btn.setBackground(Color.GREEN);
btn.setText(data[i][j] + "");
addOpenCount();
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<19 && 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<19 && data[i][j+1] == 0) openCell(i, j+1);
if (i<19 && j>0 && data[i+1][j-1] == 0) openCell(i+1, j-1);
if (i<19 && data[i+1][j] == 0) openCell(i+1, j);
if (i<19 && j<19 && data[i+1][j+1] == 0) openCell(i+1, j+1);
}
}
//记录开启和待开的数目
private void addOpenCount(){
opened++;
unopened--;
label1.setText("待开"+unopened);
label2.setText("已开"+opened);
}
//重新开启(1.给数据清零 2.给按钮恢复状态 3.重新启动时钟)
private void restart() {
//恢复了数据和按钮
for (int i = 0; i < ROW; i++) {
for (int j = 0; j < COL; j++) {
data[i][j] = 0; //累计周边雷个数清零
btns[i][j].setBackground(new Color(244,183,113));
btns[i][j].setEnabled(true);
btns[i][j].setText("");
btns[i][j].setIcon(guessIcon);
}
}
//状态栏恢复
unopened = ROW * COL;
opened = 0;
seconds = 0;
label1.setText("待开:" + unopened);
label2.setText("已开:" + opened);
label3.setText("用时:" + seconds + "s");
//重新启动!
addLei();
timer.start();
}
public static void main(String[] args) {
new Saolei();
}
}
在src路径下插入图片,
代码运行界面: