扫雷案例练习
1.启动类
public class StartApp {
public static void main(String[] args) {
new UI();
}
}
2.界面类
import java.awt.Color;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.ImageIcon;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextArea;
public class UI {
private int[][] corrdinatesUI ;//雷的坐标
public static JPanel pane;//棋盘容器
public static JButton[][] listBtn ;//按钮的数组储存按钮
private JFrame mainWindow ;//主窗口
/**
* 雷的个数
*/
public static int numMine;
/**
* 棋盘的行数
*/
private int numRow;
private Color bgColor = new Color(238,233,233);//背景的颜色
private Color color;//方格的颜色
/*
* 设置游戏模式的属性
*/
private JList<String> selList;
private JFrame selFrame;
private JTextArea txt;
/*
* 底部调用多线程不断写入雷的个数和时间
*/
public static JLabel dateMine;
/*
* 判断失败时候的值
*/
public static int loseNum = -1;
Icon ic = new ImageIcon("旗子 (1).png");//右键控制插旗的图标
public UI() {
selRowMine();
}
/**
*
* @Description 设置界面的方法,在选择完游戏难度以后运行
* @Author Mr.chen
* @Date 2020年11月2日上午10:45:55
* return:
*/
public void setUI(int numRow,int numMine) {
this.numRow = numRow;
UI.numMine = numMine;
setWindow();
UI.listBtn = new JButton[numRow][numRow];
setPanel();
setMine(numRow,numMine);
corrdinatesUI = Service.corrdinates;//先将坐标拿过来
setButton();
new WinGameThread().start();
setOtherPane();
mainWindow.setVisible(true);
}
/**
*
* @Description 设置选择游戏模式
* @Author Mr.chen
* @Date 2020年11月2日上午9:30:46
* return:
*/
public void selRowMine() {
selFrame = new JFrame();//设置游戏模式窗口
selFrame.setTitle("选择游戏模式!");
selFrame.setAlwaysOnTop(true);//置顶
JPanel selPane = new JPanel();//设置内部的容器
selPane.setBackground(new Color(253,206,218));
selFrame.setBounds(350, 350, 400, 300);
selList = new JList<>(new String[] {"初级游戏模式-10*10","高级游戏模式-15*15"});//设置选择模式的列表
selList.setFont(new Font("宋体",0,30));
txt = new JTextArea();//设置自定义雷的个数
txt.setBounds(178, 90, 150, 30);
txt.setFont(new Font("宋体",0,25));
JLabel txtMine = new JLabel();
txtMine.setText("雷的个数");
txtMine.setFont(new Font("宋体",0,25));
txtMine.setBounds(55, 90, 100, 30);
//设置选择确定时传入游戏的模式对应的棋盘大小和雷的个数
JButton selBtn = new JButton("确定");//确定按钮
selBtn.setBounds(100, 150, 180, 80);
selBtn.setBackground(new Color(173,196,251));
selBtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int num = selList.getSelectedIndex();
if(num == 0) {
int mine = 20;//默认20个
try {
mine = Integer.parseInt(txt.getText());
} catch (NumberFormatException e1) {
}
setUI(10,mine);
selFrame.dispose();
}else if(num == 1) {
int mine = 40;//默认40个
try {
mine = Integer.parseInt(txt.getText());
} catch (NumberFormatException e1) {
}
setUI(15,mine);
selFrame.dispose();
}
}
});
selPane.add(selList);
selFrame.add(txtMine);
selFrame.add(txt);
selFrame.add(selBtn);
selFrame.add(selPane);
selFrame.setVisible(true);
}
/**
*
* @Description 设置棋盘大小和雷的个数
* @Author Mr.chen
* @Date 2020年11月2日上午10:46:27
* return:
*/
public void setMine(int row,int mine) {
Service s = new Service();
s.setMine(mine,row);
}
/**
*
* @Description 设置窗口
* @Author Mr.chen
* @Date 2020年10月30日下午4:22:37
* return:
*/
public void setWindow() {
mainWindow = new JFrame();
mainWindow.setBounds(100, 100, numRow*50 + 19, numRow*50 + 100);
mainWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainWindow.setTitle("这是一个扫雷游戏");
mainWindow.setLayout(null);
}
/**
*
* @Description 设置容器显示时间和剩余雷的个数
* @Author Mr.chen
* @Date 2020年11月2日上午10:59:31
* return:
*/
public void setOtherPane() {
dateMine = new JLabel();
dateMine.setBounds(0, numRow*50 - 30, numRow*50, 100);
dateMine.setFont(new Font("宋体",0,30));
dateMine.setVisible(true);
mainWindow.add(dateMine);
new DateMineThread().start();
}
/**
*
* @Description 设置容器
* @Author Mr.chen
* @Date 2020年10月30日下午4:31:14
* return:
*/
public void setPanel() {
pane = new JPanel();
pane.setLayout(new GridLayout(numRow,numRow));
pane.setBounds(0, 0, numRow*50, numRow*50);
mainWindow.add(pane);
}
/**
*
* @Description 设置按钮并且将数组中的数字写入
* @Author Mr.chen
* @Date 2020年10月30日下午4:23:20
* return:
*/
public void setButton() {
color = new Color(135,206,250);
for (int i = 0; i < corrdinatesUI.length; i++) {
for (int j = 0; j < corrdinatesUI.length; j++) {
JButton btn = new JButton();
String msg = String.valueOf(corrdinatesUI[i][j]);
btn.setText(msg);//记录按钮是否有雷
btn.setForeground(color);//设置字体的颜色和背景相同
btn.setName(i + "," + j);//记录按钮的坐标
btn.setBackground(color);
btn.addMouseListener(new MouseListener() {//添加鼠标监听事件
@Override
public void mouseReleased(MouseEvent e) {
}
@Override
public void mousePressed(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseClicked(MouseEvent e) {
if(e.getButton() == MouseEvent.BUTTON1) {//左键
/*
* 如果点击到雷 -1
*/
if(btn.getText().equals("-1")) {
new Thread(new Runnable() {
@Override
public void run() {
btnMine();
loseNum = 1;
Integer choice = JOptionPane.showConfirmDialog(pane, "游戏结束!");//选择是否继续重新开始游戏
if(choice == 0) {
mainWindow.dispose();
new UI();
}else {
}
}
}).start();
}
/*
* 点击的是 0
*/
else if(btn.getText().equals("0")){
btnZero(btn,-1,-1);
}
/*
* 点击的不是 0
*/
else {
btnNotMine(btn);
}
}
else if(e.getButton() == MouseEvent.BUTTON3) {//右键
if(btn.isSelected() == false) {
if(btn.getIcon() == null) {
numMine--;
btn.setIcon(ic);
}else {
numMine++;
btn.setIcon(null);
}
}
}
}
});
listBtn[i][j] = btn;
pane.add(btn);
}
}
}
/**
*
* @Description 如果点击的是雷
* @Author Mr.chen
* @Date 2020年11月1日下午2:13:09
* return:
*/
public void btnMine() {
for (int i = 0; i < listBtn.length; i++) {
for (int j = 0; j < listBtn.length; j++) {
if(listBtn[i][j].getText().equals("-1")) {
listBtn[i][j].setBackground(Color.RED);
listBtn[i][j].setSelected(true);
listBtn[i][j].setEnabled(false);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
/**
*
* @Description 如果点击的不是雷 且不为0
* @Author Mr.chen
* @Date 2020年11月1日下午2:13:19
* return:
*/
public void btnNotMine(JButton btn) {
btn.setBackground(bgColor);
btn.setSelected(true);
if(btn.getText().equals("0")) {//设置为0时按钮不可见
btn.setForeground(bgColor);
}
}
/**
*
* @Description 如果选择到为0的,应该向四周递归打开
* @Author Mr.chen
* @Date 2020年11月1日下午2:47:12
* return:
* x-1,y-1 x,y-1 x-1,y+1
* x-1,y x,y x+1,y
*/
public void btnZero(JButton btn,int x,int y) {
btnNotMine(btn);
if(btn.getText().equals("-1")) {
return;
}
if(!(btn.getText().equals("0"))) {
return;
}
if(x==-1 && y==-1) {
String num = btn.getName();//选中按钮的坐标 1,1
String[] str = num.split(",");
x = Integer.parseInt(str[0]);
y = Integer.parseInt(str[1]);
}
for (int i = x - 1; i <= x + 1; i++) {
for (int j = y - 1; j <= y + 1; j++) {
if(i == x && j == y) {
continue;
}
try {
//周围是0打开并递归
if(listBtn[i][j].getText().equals("0") && listBtn[i][j].isSelected() == false) {
btnNotMine(listBtn[i][j]);
btnZero(listBtn[i][j],i,j);
}
//周围打开是-1,则是雷不打开直接跳出当前循环
else if(listBtn[i][j].getText().equals("-1") && listBtn[i][j].isSelected() == false){
continue;
}
//周围打开不是雷,也不是0
else if(listBtn[i][j].isSelected() == false){
btnNotMine(listBtn[i][j]);
}
} catch (Exception e) {
}
}
}
}
}
3.逻辑类
/**
*
* @Description 扫雷的逻辑类
* @Author Mr.chen
* @Date 2020年10月30日下午2:33:35
* 功能:生成雷的坐标,生成棋盘并将期盼中是否有雷和周围雷的个数写入 此处方格是雷 -1 周围没有雷 0 周围有n个雷
* 点击时为0递归 不为0则停止
*/
public class Service {
/**
* 储存的坐标
* 用来保存对应坐标的中是否有雷,或周围有几颗雷
* key:坐标 val:是否有雷或雷的个数
*/
public static int[][] corrdinates ;
/**
*
* @Description 生成传入值个数的雷, 并确定每个坐标处的数字
* @Author Mr.chen
* @Date 2020年10月30日下午2:38:50
* return:
*/
public void setMine(int numMine,int num) {
setArray(num);
int x;
int y;
for(int i = 0 ;i < numMine;i++) {
x = (int)(Math.random() * num);
y = (int)(Math.random() * num);
if(corrdinates[x][y] != -1) {
corrdinates[x][y] = -1;
}else {
i--;
continue;
}
aroundMine(x,y);
}
}
/**
*
* @Description 判断周围有几个雷 并写入数字
* @Author Mr.chen
* @Date 2020年10月30日下午2:42:17
* return:
*/
private void aroundMine(int x, int y) {
for (int i = x - 1; i <= x + 1; i++) {
for (int j = y - 1; j <= y + 1; j++) {
if(x == 0 && y == 0) {
continue;
}
try {
if(corrdinates[i][j] != -1) {
corrdinates[i][j]++;
}
} catch (Exception e) {
}
}
}
}
/**.
*
* @Description 生成一个指定大小的数组,作为棋盘的大小
* @Author Mr.chen
* @Date 2020年10月30日下午3:05:10
* return:
*/
public void setArray(int num) {
corrdinates = new int[num][num];
}
}
4.判断胜利线程类
import javax.swing.JButton;
import javax.swing.JOptionPane;
public class WinGameThread extends Thread{
private JButton[][] listBtn;//按钮的数组
private int numMine;//雷的个数
public static int winNum = -1;//胜利以后停止时间和雷计数线程
@Override
public void run() {
this.numMine = UI.numMine;//获取总雷数
while(true) {
this.listBtn = UI.listBtn;//不断监听按钮状态
int index = 0;
try {
for (JButton[] jButtons : this.listBtn) {
for (JButton jButton : jButtons) {
if(jButton.isSelected() == false) {
index++;
}
}
}
} catch (Exception e1) {
}
if(index == this.numMine) {//当剩余未选择按钮为雷的总数时获胜
JOptionPane.showMessageDialog(UI.pane, "获得胜利!");
winNum = 1;
break;
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
5.底部时间和雷数线程类
import javax.swing.JLabel;
/**
*
* @Description 底部时间和显示雷的个数的线程类
* @Author Mr.chen
* @Date 2020年11月4日上午9:21:45
*/
public class DateMineThread extends Thread{
private JLabel dateMine;//显示时间和雷的个数的容器
private int numMine;//雷的个数
public static int gameTime;//游戏已用时间
@Override
public void run() {
this.dateMine = UI.dateMine;
int index = 0;
while(true) {
this.numMine = UI.numMine;//监听雷的个数
String str = "已用时间:" + index + " 剩余雷的个数为:" + this.numMine;
dateMine.setText(str);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
index++;
if(WinGameThread.winNum == 1) {//胜利时结束该线程
gameTime = index;
break;
}
if(UI.loseNum == 1) {//失败时结束该线程
gameTime = index;
break;
}
}
}
}