这是我第一次写博客,初衷是想把我学到的东西展示出来,通过写博客的方式再捋一遍自己的思路。希望自己的一点点想法能够给其他人启发,我也要把自己存在的问题提出来,以此文为起点,树立写博客的习惯,在之后的日子里不断见证自己的成长。
因为win10系统没有自带的扫雷游戏我很难受,就决定自己要写一个扫雷出来。
需求:
实现扫雷的要点:
思路:
随机生成雷&空地添加附近雷的个数:
使用两个随机数得到坐标,若该坐标上的点没有埋雷就在该点埋一个雷。一直循环该过程直到雷的个数够需求为止。
原来设想的是在点开空地后再搜索附近8个点雷的个数,但感觉不是很方便就换个思路,在一个点埋雷的时候就把这个雷周围8个点的附近雷的个数加一,在这个过程中需要注意不能越界。
while(flag<mineNum){
//随机添加雷
int i,j;
i=(int)(Math.random()*x-1); //随机数乘小于x-1控制范围在0~x-1,不会超过数组范围
j=(int)(Math.random()*y-1); //随机数乘小于y-1控制范围在0~y-1,不会超过数组范围
if(!mine[i][j].ismine){
//该点没有雷就埋雷
flag++;
mine[i][j].ismine=true; //该点是雷
int [][]next= {
{
-1,0},{
-1,1},{
0,1},{
1,1},{
1,0},{
1,-1},{
0,-1},{
-1,-1}}; //周围8个坐标
for(int k=0;k<8;k++) {
//循环雷周围8个点为每个点周围的雷数加一
int tx=i+next[k][0];
int ty=j+next[k][1];
if(tx<0||tx>x-1||ty<0||ty>y-1) //越界则直接进入下一个
continue;
mine[tx][ty].aroundMine++; //该点的周围雷数加一
}
}
}
自动打开空地:
在打开了空地之后,分为两类:附近有雷的空地和附近没雷的空地。附近有雷的空地不能直接双击打开周围8不是雷的空地,需要先把周围8个点中是雷的所有点都插上旗才能双击自动打开,若插错旗则直接结束游戏;附近没有雷的空地可直接打开周围8个空地。在打开了周围的空地之后,新打开的空地继续判断:若附近有雷则不继续打开其周围的空地,若附近没有雷则直接打开周围8个空地,然后继续重复该操纵直到所有的点附近都有雷为止。
为实现该操作需要用到广度优先搜索。通过广搜将符合上述条件的所有点都入队,然后逐一打开其附近的空地,并且要注意越界问题,在打开的点中如果存在符合上述条件的点,就将该点也入队。不断重复该操作直到该队列结束。
if(e.getClickCount()==2&&e.getButton()==e.BUTTON1&&t.isfound&&!t.ismine) {
//双击打开的点可自动打开周围没有雷的点
int [][]map=new int[x*y][]; //用于存放于队列中每个点的坐标
for(int i=0;i<x*y;i++)
map[i]=new int[2];
map[0][0]=t.x; //首先将被双击的点进入队列以其为中心向四周进行搜索
map[0][1]=t.y;
int head=0,tail=1;
int [][]next= {
{
-1,0},{
-1,1},{
0,1},{
1,1},{
1,0},{
1,-1},{
0,-1},{
-1,-1}}; //周围8个坐标
int tx,ty,flag=0;
while(head<tail) {
for(int w=0;w<8;w++) {
tx=map[head][0]+next[w][0];
ty=map[head][1]+next[w][1];
if(tx<0||tx>x-1||ty<0||ty>y-1) //越界则进入下一个
continue;
if(!mine[tx][ty].canSearch) //周围雷的数量要和插的旗数量一样才能进行自动打开
flag++;
}
if(flag==mine[map[head][0]][map[head][1]].aroundMine) {
for(int k=0;k<8;k++) {
//循环周围8个方向
tx=map[head][0]+next[k][0];
ty=map[head][1]+next[k][1];
if(tx<0||tx>x-1||ty<0||ty>y-1) //越界则进入下一个
continue;
if(mine[tx][ty].ismine&&mine[tx][ty].canSearch) {
//差错旗直接结束
for(int i=0;i<x;i++) {
for(int j=0;j<y;j++) {
if(mine[i][j].ismine) {
ImageIcon icon=new ImageIcon(".//photo//雷.jpg");
icon.setImage(icon.getImage().getScaledInstance(mine[i][j].getWidth(), mine[i][j].getHeight(), Image.SCALE_AREA_AVERAGING));
mine[i][j].setIcon(icon);
}
}
}
time.stop();
JOptionPane.showMessageDialog(this,"不好意思 您输了 下次好运");
game.main(null);
}
if(!mine[tx][ty].isfound&&!mine[tx][ty].ismine&&mine[tx][ty].canSearch) {
//将没有被打开并且没插旗的空地打开
mine[tx][ty].setBackground(Color.LIGHT_GRAY); //当被打开后变色
mine[tx][ty].isfound=true; //已经被点开
if(mine[tx][ty].aroundMine>0&&!mine[tx][ty].ismine) {
//周围有雷就标出周围有雷数
mine[tx][ty].setText(""+mine[tx][ty].aroundMine);
mine[tx][ty].setFont(new java.awt.Font("24",20,20)) ;
}
if(mine[tx][ty].aroundMine==0) {
//周围8个点都没雷就将该点入队之后以该点为中心搜
map[tail][0]=tx;
map[tail][1]=ty;
tail++;
}
}
}
}
head++; //每搜完一个点后该就出队
}
}
空地&雷类:
创建一个继承了JButton类的子类,用于表示每个单元格中的点,该类需要判断是雷还是空地、是否被打开、是否插了旗、记录该点的坐标、记录周围8个点雷的个数。
在游戏开始前应该默认该点没有被打开,默认该点不是雷(因为大多数的点都是空地),默认该点可以被打开(因为没有插旗)
public class Mine extends JButton {
//空地&雷类
boolean isfound; //是否被打开
boolean ismine; //是否藏雷
boolean canSearch; //是否插旗,插旗则该点不会被搜索
int x,y; //确定坐标
int aroundMine=0; //周围的雷数
Mine(int x,int y) {
this.x=x;
this.y=y;
isfound=false;
ismine=false;
canSearch=true;
setBackground(Color.pink); //设置该点的背景颜色作为没有打开的点
}
}
游戏面板类:
创建一个继承JPanel类的子类,用于创建游戏画面并在此面板上进行游戏的操作。可通过传入游戏的行、列、雷数创建相应难度的游戏。
public class Jpanel extends JPanel implements MouseListener,ActionListener {
int x, y ; //行列
Mine [][]mine; //每个点
JLabel flagPhoto; //雷图片
JLabel blank1; //空白占网格
JLabel blank2; //空白占网格
JLabel blank3; //空白占网格
JLabel blank4; //空白占网格
JLabel flagNum; //记录剩余雷数标签
JLabel timeShow; //显示用时标签
Timer time; //计时
int mineNum=0; //剩余雷数
int second=0; //用时
Jpanel(int x, int y, int mineNum){
//传入网格的行列和雷数
this.x = x ;
this.y = y ;
this.mineNum = mineNum;
GridLayout grid=new GridLayout(x+1,y); //网格布局每一格是一个点
setLayout(grid);
setFocusable(true);
mine=new Mine[x][y];
for(int i=0;i<x;i++){
for(int j=0;j<y;j++){
mine[i][j]=new Mine(i,j); //创建每一个点
add(mine[i][j]);
mine[i][j].addMouseListener(this); //为每一个点都添加鼠标事件
}
}
blank1=new JLabel(); //空白标签用于占一个网格便于计时计数不会挤在一起影响美观
add(blank1);
flagPhoto=new JLabel();
add(flagPhoto);
ImageIcon icon=new ImageIcon(".//photo//雷.jpg");
icon.setImage(icon.getImage().getScaledInstance(40, 40, Image.SCALE_AREA_AVERAGING));
flagPhoto.setIcon(icon);
flagNum=new JLabel(":"+mineNum);
flagNum.setFont(new java.awt.Font("24",20,23));
add(flagNum);
blank2=new JLabel();
add(blank2);
blank3=new JLabel();
add(blank3);
blank4=new JLabel();
add(blank4);
time=new Timer(1000,this); //每1秒发生一次事件
timeShow=new JLabel(""+second);
add(timeShow);
timeShow.setFont(new java.awt.Font("24",20,30));
int flag=0; //用于标记雷的数量是否够
while(flag<mineNum){
//随机添加雷
int i,j;
i=(int)(Math.random()*x-1); //随机数乘小于x-1控制范围在0~x-1,不会超过数组范围
j=(int)(Math.random()*y-1); //随机数乘小于y-1控制范围在0~y-1,不会超过数组范围
if(!mine[i][j].ismine){
//该点没有雷就埋雷
flag++;
mine[i][j].ismine=true; //该点是雷
int [][]next= {
{
-1,0},{
-1,1},{
0,1},{
1,1},{
1,0},{
1,-1},{
0,-1},{
-1,-1}}; //周围8个坐标
for(int k=0;k<8;k++) {
//循环雷周围8个点为每个点周围的雷数加一
int tx=i+next[k][0];
int ty=j+next[k][1];
if(tx<0||tx>x-1||ty<0||ty>y-1) //越界则直接进入下一个
continue;
mine[tx][ty].aroundMine++; //该点的周围雷数加一
}
}
}
}
public void mouseClicked(MouseEvent e) {
time.start();
Mine t=new Mine(0,0); //用于寻找当前点击的点是哪一个
for(int i=0;i<x;i++) {
//循环整个对象数组,找到被点击的那个点
for(int j=0;j<y;j++) {
if(e.getSource()==mine[i][j]) {
t=(Mine) e.getSource(); //获得点击到的点的地址
break;
}
}
}
if(e.getButton()==e.BUTTON3){
//点击右键插旗排雷
if(t.canSearch&&!t.isfound){
//该点未被打开并且没有插着旗就有资格插旗
t.canSearch=false; //插旗后点击左键不可对该点进行搜索
ImageIcon icon=new ImageIcon(".\\photo\\旗.jpg");
icon.setImage(icon.getImage().getScaledInstance(t.getWidth(), t.getHeight(), Image.SCALE_DEFAULT));
t.setIcon(icon);
mineNum--;
flagNum.setText(": "+mineNum); //插旗后显示的雷数减一
}
else if(!t.canSearch&&!t.isfound){
///该点未被打开并且插着旗就有资格拆掉旗
t.canSearch=true; //插旗后再点一次右键可左键搜索
t.setIcon(null); //撤掉旗就删掉图标
mineNum++;
flagNum.setText(": "+mineNum); //拆旗后显示的雷数加一
}
}
if(e.getClickCount()==1&&e.getButton()==e.BUTTON1){
//单击左键打开该点
if(t.canSearch) {
//该点没插旗就可以打开该点
t.setBackground(Color.LIGHT_GRAY); //当被点开后变色
t.isfound=true; //已经被打开
if(t.aroundMine>0&&!t.ismine) {
//不是雷且周围有雷就标出周围有雷数
t.setText(""+t.aroundMine);
t.setFont(new java.awt.Font("24",20,20));
}
}
if(t.ismine&&t.canSearch){
//若该点有雷且没有插旗打开则结束游戏
for(int i=0;i<x;i++) {
for(int j=0;j<y;j++) {
if(mine[i][j].ismine) {
//将所有是雷的点都打开
ImageIcon icon=new ImageIcon(".//photo//雷.jpg");
icon.setImage(icon.getImage().getScaledInstance(mine[i][j].getWidth(), mine[i][j].getHeight(), Image.SCALE_AREA_AVERAGING));
mine[i][j].setIcon(icon);
}
}
}
time.stop(); //停止计时
JOptionPane.showMessageDialog(this,"不好意思 您输了 下次好运"); //弹出消息对话框提示游戏结束
game.main(null); //重新开始回到选择模式界面
}
}
if(e.getClickCount()==2&&e.getButton()==e.BUTTON1&&t.isfound&&!t.ismine) {
//双击打开的点可自动打开周围没有雷的点
int [][]map=new int[x*y][]; //用于存放于队列中每个点的坐标
for(int i=0;i<x*y;i++)
map[i]=new int[2];
map[0][0]=t.x; //首先将被双击的点进入队列以其为中心向四周进行搜索
map[0][1]=t.y;
int head=0,tail=1;
int [][]next= {
{
-1,0},{
-1,1},{
0,1},{
1,1},{
1,0},{
1,-1},{
0,-1},{
-1,-1}}; //周围8个坐标
int tx,ty,flag=0;
while(head<tail) {
for(int w=0;w<8;w++) {
tx=map[head][0]+next[w][0];
ty=map[head][1]+next[w][1];
if(tx<0||tx>x-1||ty<0||ty>y-1) //越界则进入下一个
continue;
if(!mine[tx][ty].canSearch) //周围雷的数量要和插的旗数量一样才能进行自动打开
flag++;
}
if(flag==mine[map[head][0]][map[head][1]].aroundMine) {
for(int k=0;k<8;k++) {
//循环周围8个方向
tx=map[head][0]+next[k][0];
ty=map[head][1]+next[k][1];
if(tx<0||tx>x-1||ty<0||ty>y-1) //越界则进入下一个
continue;
if(mine[tx][ty].ismine&&mine[tx][ty].canSearch) {
//差错旗直接结束
for(int i=0;i<x;i++) {
for(int j=0;j<y;j++) {
if(mine[i][j].ismine) {
ImageIcon icon=new ImageIcon(".//photo//雷.jpg");
icon.setImage(icon.getImage().getScaledInstance(mine[i][j].getWidth(), mine[i][j].getHeight(), Image.SCALE_AREA_AVERAGING));
mine[i][j].setIcon(icon);
}
}
}
time.stop();
JOptionPane.showMessageDialog(this,"不好意思 您输了 下次好运");
game.main(null);
}
if(!mine[tx][ty].isfound&&!mine[tx][ty].ismine&&mine[tx][ty].canSearch) {
//将没有被打开并且没插旗的空地打开
mine[tx][ty].setBackground(Color.LIGHT_GRAY); //当被打开后变色
mine[tx][ty].isfound=true; //已经被点开
if(mine[tx][ty].aroundMine>0&&!mine[tx][ty].ismine) {
//周围有雷就标出周围有雷数
mine[tx][ty].setText(""+mine[tx][ty].aroundMine);
mine[tx][ty].setFont(new java.awt.Font("24",20,20)) ;
}
if(mine[tx][ty].aroundMine==0) {
//周围8个点都没雷就将该点入队之后以该点为中心搜
map[tail][0]=tx;
map[tail][1]=ty;
tail++;
}
}
}
}
head++; //每搜完一个点后该就出队
}
}
if(mineNum==0) {
//插的旗数和雷的数量相同时就开始判断是否胜利
boolean a=false; //记录是否插错旗
for(int i=0;i<x;i++) {
for(int j=0;j<y;j++) {
//循环整个对象数组
if(!mine[i][j].isfound&&!mine[i][j].ismine&&mine[i][j].canSearch) {
//该点不是雷并且没有被打开
a=true;
break; //游戏继续
}
if(mine[i][j].ismine&&mine[i][j].canSearch){
//该点是雷并且么有插旗
a=true;
break; //游戏继续
}
}
}
if(!a) {
//游戏胜利
time.stop(); //停止计时
JOptionPane.showMessageDialog(this,"胜利!"); //弹出消息对话框提示游戏结束
game.main(null); //重新开始回到选择模式界面
}
}
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
public void mousePressed(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {
}
public void actionPerformed(ActionEvent e) {
if(e.getSource()==time) {
//计时
second++; //每秒加一
timeShow.setText(""+second);
}
}
}
地图类:
创建一个继承了JFrame类的子类,用于选择游戏的难易程度并进入游戏画面。分为简单(9x9 10个雷)、困难 (25x25 120个雷)、自定义。
public class Map extends JFrame implements ActionListener{
Jpanel panel; //游戏画面
JPanel home ; //初始界面
JButton easy, difficult, diy; //选择模式
Map() {
home = new JPanel() ;
add(home) ;
home.setLayout(null) ; //设置布局
easy = new JButton("简单 (9x9 10个雷)") ;
difficult = new JButton("困难 (25x25 120个雷)") ;
diy = new JButton("自定义") ;
home.add(easy) ;
home.add(difficult) ;
home.add(diy);
easy.setBounds(100, 50, 190, 70); //设置按钮大小
difficult.setBounds(100, 170, 190, 70);
diy.setBounds(100, 290, 190, 70);
easy.addActionListener(this) ; //按钮添加监听器
difficult.addActionListener(this) ;
diy.addActionListener(this);
easy.setBackground(Color.GRAY); //按钮设置背景颜色
difficult.setBackground(Color.gray);
diy.setBackground(Color.gray);
easy.setFont(new java.awt.Font("24",20,15)) ; //按钮设置字大小
difficult.setFont(new java.awt.Font("24",20,15)) ;
diy.setFont(new java.awt.Font("24",20,15)) ;
setVisible(true) ;
setBounds(400,100,400,450) ;
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE) ;
}
public void actionPerformed(ActionEvent e) {
if(e.getSource()==easy) {
//简单模式
setBounds(300,100,800,800) ; //重新设置游戏界面大小
panel=new Jpanel(9, 9, 10);
home.setVisible(false) ; //先将当前的面板不可视否则不会跳转到游戏画面
this.remove(home) ; //将当前的面板从该窗口移除并加入游戏面板
this.add(panel) ;
}
if(e.getSource()==difficult) {
//困难模式
setBounds(100,0,1600,1000) ;
panel=new Jpanel(25, 25, 120);
home.setVisible(false) ;
this.remove(home) ;
this.add(panel) ;
}
if(e.getSource()==diy) {
//自定义模式通过三个输入对话框输入自定义的地图大小和雷数
String x = JOptionPane.showInputDialog(this, "请输入行数:", null, JOptionPane.INFORMATION_MESSAGE);
String y = JOptionPane.showInputDialog(this, "请输入列数:", null, JOptionPane.INFORMATION_MESSAGE);
String num = JOptionPane.showInputDialog(this, "请输入雷数:", null, JOptionPane.INFORMATION_MESSAGE);
setBounds(100,0,1600,1000) ;
panel=new Jpanel(Integer.parseInt(x), Integer.parseInt(y), Integer.parseInt(num));
home.setVisible(false) ;
this.remove(home) ;
this.add(panel) ;
}
}
}
完整代码:
public class game {
public static void main(String[] args)
{
Map a=new Map();
}
}
public class Map extends JFrame implements ActionListener{
Jpanel panel; //游戏画面
JPanel home ; //初始界面
JButton easy, difficult, diy; //选择模式
Map() {
home = new JPanel() ;
add(home) ;
home.setLayout(null) ; //设置布局
easy = new JButton("简单 (9x9 10个雷)") ;
difficult = new JButton("困难 (25x25 120个雷)") ;
diy = new JButton("自定义") ;
home.add(easy) ;
home.add(difficult) ;
home.add(diy);
easy.setBounds(100, 50, 190, 70); //设置按钮大小
difficult.setBounds(100, 170, 190, 70);
diy.setBounds(100, 290, 190, 70);
easy.addActionListener(this) ; //按钮添加监听器
difficult.addActionListener(this) ;
diy.addActionListener(this);
easy.setBackground(Color.GRAY); //按钮设置背景颜色
difficult.setBackground(Color.gray);
diy.setBackground(Color.gray);
easy.setFont(new java.awt.Font("24",20,15)) ; //按钮设置字大小
difficult.setFont(new java.awt.Font("24",20,15)) ;
diy.setFont(new java.awt.Font("24",20,15)) ;
setVisible(true) ;
setBounds(400,100,400,450) ;
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE) ;
}
public void actionPerformed(ActionEvent e) {
if(e.getSource()==easy) {
//简单模式
setBounds(300,100,800,800) ; //重新设置游戏界面大小
panel=new Jpanel(9, 9, 10);
home.setVisible(false) ; //先将当前的面板不可视否则不会跳转到游戏画面
this.remove(home) ; //将当前的面板从该窗口移除并加入游戏面板
this.add(panel) ;
}
if(e.getSource()==difficult) {
//困难模式
setBounds(100,0,1600,1000) ;
panel=new Jpanel(25, 25, 120);
home.setVisible(false) ;
this.remove(home) ;
this.add(panel) ;
}
if(e.getSource()==diy) {
//自定义模式通过三个输入对话框输入自定义的地图大小和雷数
String x = JOptionPane.showInputDialog(this, "请输入行数:", null, JOptionPane.INFORMATION_MESSAGE);
String y = JOptionPane.showInputDialog(this, "请输入列数:", null, JOptionPane.INFORMATION_MESSAGE);
String num = JOptionPane.showInputDialog(this, "请输入雷数:", null, JOptionPane.INFORMATION_MESSAGE);
setBounds(100,0,1600,1000) ;
panel=new Jpanel(Integer.parseInt(x), Integer.parseInt(y), Integer.parseInt(num));
home.setVisible(false) ;
this.remove(home) ;
this.add(panel) ;
}
}
}
public class Jpanel extends JPanel implements MouseListener,ActionListener {
int x, y ; //行列
Mine [][]mine; //每个点
JLabel flagPhoto; //雷图片
JLabel blank1; //空白占网格
JLabel blank2; //空白占网格
JLabel blank3; //空白占网格
JLabel blank4; //空白占网格
JLabel flagNum; //记录剩余雷数标签
JLabel timeShow; //显示用时标签
Timer time; //计时
int mineNum=0; //剩余雷数
int second=0; //用时
Jpanel(int x, int y, int mineNum){
//传入网格的行列和雷数
this.x = x ;
this.y = y ;
this.mineNum = mineNum;
GridLayout grid=new GridLayout(x+1,y); //网格布局每一格是一个点
setLayout(grid);
setFocusable(true);
mine=new Mine[x][y];
for(int i=0;i<x;i++){
for(int j=0;j<y;j++){
mine[i][j]=new Mine(i,j); //创建每一个点
add(mine[i][j]);
mine[i][j].addMouseListener(this); //为每一个点都添加鼠标事件
}
}
blank1=new JLabel(); //空白标签用于占一个网格便于计时计数不会挤在一起影响美观
add(blank1);
flagPhoto=new JLabel();
add(flagPhoto);
ImageIcon icon=new ImageIcon(".//photo//雷.jpg");
icon.setImage(icon.getImage().getScaledInstance(40, 40, Image.SCALE_AREA_AVERAGING));
flagPhoto.setIcon(icon);
flagNum=new JLabel(":"+mineNum);
flagNum.setFont(new java.awt.Font("24",20,23));
add(flagNum);
blank2=new JLabel();
add(blank2);
blank3=new JLabel();
add(blank3);
blank4=new JLabel();
add(blank4);
time=new Timer(1000,this); //每1秒发生一次事件
timeShow=new JLabel(""+second);
add(timeShow);
timeShow.setFont(new java.awt.Font("24",20,30));
int flag=0; //用于标记雷的数量是否够
while(flag<mineNum){
//随机添加雷
int i,j;
i=(int)(Math.random()*x-1); //随机数乘小于x-1控制范围在0~x-1,不会超过数组范围
j=(int)(Math.random()*y-1); //随机数乘小于y-1控制范围在0~y-1,不会超过数组范围
if(!mine[i][j].ismine){
//该点没有雷就埋雷
flag++;
mine[i][j].ismine=true; //该点是雷
int [][]next= {
{
-1,0},{
-1,1},{
0,1},{
1,1},{
1,0},{
1,-1},{
0,-1},{
-1,-1}}; //周围8个坐标
for(int k=0;k<8;k++) {
//循环雷周围8个点为每个点周围的雷数加一
int tx=i+next[k][0];
int ty=j+next[k][1];
if(tx<0||tx>x-1||ty<0||ty>y-1) //越界则直接进入下一个
continue;
mine[tx][ty].aroundMine++; //该点的周围雷数加一
}
}
}
}
public void mouseClicked(MouseEvent e) {
time.start();
Mine t=new Mine(0,0); //用于寻找当前点击的点是哪一个
for(int i=0;i<x;i++) {
//循环整个对象数组,找到被点击的那个点
for(int j=0;j<y;j++) {
if(e.getSource()==mine[i][j]) {
t=(Mine) e.getSource(); //获得点击到的点的地址
break;
}
}
}
if(e.getButton()==e.BUTTON3){
//点击右键插旗排雷
if(t.canSearch&&!t.isfound){
//该点未被打开并且没有插着旗就有资格插旗
t.canSearch=false; //插旗后点击左键不可对该点进行搜索
ImageIcon icon=new ImageIcon(".\\photo\\旗.jpg");
icon.setImage(icon.getImage().getScaledInstance(t.getWidth(), t.getHeight(), Image.SCALE_DEFAULT));
t.setIcon(icon);
mineNum--;
flagNum.setText(": "+mineNum); //插旗后显示的雷数减一
}
else if(!t.canSearch&&!t.isfound){
///该点未被打开并且插着旗就有资格拆掉旗
t.canSearch=true; //插旗后再点一次右键可左键搜索
t.setIcon(null); //撤掉旗就删掉图标
mineNum++;
flagNum.setText(": "+mineNum); //拆旗后显示的雷数加一
}
}
if(e.getClickCount()==1&&e.getButton()==e.BUTTON1){
//单击左键打开该点
if(t.canSearch) {
//该点没插旗就可以打开该点
t.setBackground(Color.LIGHT_GRAY); //当被点开后变色
t.isfound=true; //已经被打开
if(t.aroundMine>0&&!t.ismine) {
//不是雷且周围有雷就标出周围有雷数
t.setText(""+t.aroundMine);
t.setFont(new java.awt.Font("24",20,20));
}
}
if(t.ismine&&t.canSearch){
//若该点有雷且没有插旗打开则结束游戏
for(int i=0;i<x;i++) {
for(int j=0;j<y;j++) {
if(mine[i][j].ismine) {
//将所有是雷的点都打开
ImageIcon icon=new ImageIcon(".//photo//雷.jpg");
icon.setImage(icon.getImage().getScaledInstance(mine[i][j].getWidth(), mine[i][j].getHeight(), Image.SCALE_AREA_AVERAGING));
mine[i][j].setIcon(icon);
}
}
}
time.stop(); //停止计时
JOptionPane.showMessageDialog(this,"不好意思 您输了 下次好运"); //弹出消息对话框提示游戏结束
game.main(null); //重新开始回到选择模式界面
}
}
if(e.getClickCount()==2&&e.getButton()==e.BUTTON1&&t.isfound&&!t.ismine) {
//双击打开的点可自动打开周围没有雷的点
int [][]map=new int[x*y][]; //用于存放于队列中每个点的坐标
for(int i=0;i<x*y;i++)
map[i]=new int[2];
map[0][0]=t.x; //首先将被双击的点进入队列以其为中心向四周进行搜索
map[0][1]=t.y;
int head=0,tail=1;
int [][]next= {
{
-1,0},{
-1,1},{
0,1},{
1,1},{
1,0},{
1,-1},{
0,-1},{
-1,-1}}; //周围8个坐标
int tx,ty,flag=0;
while(head<tail) {
for(int w=0;w<8;w++) {
tx=map[head][0]+next[w][0];
ty=map[head][1]+next[w][1];
if(tx<0||tx>x-1||ty<0||ty>y-1) //越界则进入下一个
continue;
if(!mine[tx][ty].canSearch) //周围雷的数量要和插的旗数量一样才能进行自动打开
flag++;
}
if(flag==mine[map[head][0]][map[head][1]].aroundMine) {
for(int k=0;k<8;k++) {
//循环周围8个方向
tx=map[head][0]+next[k][0];
ty=map[head][1]+next[k][1];
if(tx<0||tx>x-1||ty<0||ty>y-1) //越界则进入下一个
continue;
if(mine[tx][ty].ismine&&mine[tx][ty].canSearch) {
//差错旗直接结束
for(int i=0;i<x;i++) {
for(int j=0;j<y;j++) {
if(mine[i][j].ismine) {
ImageIcon icon=new ImageIcon(".//photo//雷.jpg");
icon.setImage(icon.getImage().getScaledInstance(mine[i][j].getWidth(), mine[i][j].getHeight(), Image.SCALE_AREA_AVERAGING));
mine[i][j].setIcon(icon);
}
}
}
time.stop();
JOptionPane.showMessageDialog(this,"不好意思 您输了 下次好运");
game.main(null);
}
if(!mine[tx][ty].isfound&&!mine[tx][ty].ismine&&mine[tx][ty].canSearch) {
//将没有被打开并且没插旗的空地打开
mine[tx][ty].setBackground(Color.LIGHT_GRAY); //当被打开后变色
mine[tx][ty].isfound=true; //已经被点开
if(mine[tx][ty].aroundMine>0&&!mine[tx][ty].ismine) {
//周围有雷就标出周围有雷数
mine[tx][ty].setText(""+mine[tx][ty].aroundMine);
mine[tx][ty].setFont(new java.awt.Font("24",20,20)) ;
}
if(mine[tx][ty].aroundMine==0) {
//周围8个点都没雷就将该点入队之后以该点为中心搜
map[tail][0]=tx;
map[tail][1]=ty;
tail++;
}
}
}
}
head++; //每搜完一个点后该就出队
}
}
if(mineNum==0) {
//插的旗数和雷的数量相同时就开始判断是否胜利
boolean a=false; //记录是否插错旗
for(int i=0;i<x;i++) {
for(int j=0;j<y;j++) {
//循环整个对象数组
if(!mine[i][j].isfound&&!mine[i][j].ismine&&mine[i][j].canSearch) {
//该点不是雷并且没有被打开
a=true;
break; //游戏继续
}
if(mine[i][j].ismine&&mine[i][j].canSearch){
//该点是雷并且么有插旗
a=true;
break; //游戏继续
}
}
}
if(!a) {
//游戏胜利
time.stop(); //停止计时
JOptionPane.showMessageDialog(this,"胜利!"); //弹出消息对话框提示游戏结束
game.main(null); //重新开始回到选择模式界面
}
}
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
public void mousePressed(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {
}
public void actionPerformed(ActionEvent e) {
if(e.getSource()==time) {
//计时
second++; //每秒加一
timeShow.setText(""+second);
}
}
}
public class Mine extends JButton {
//空地&雷类
boolean isfound; //是否被打开
boolean ismine; //是否藏雷
boolean canSearch; //是否插旗,插旗则该点不会被搜索
int x,y; //确定坐标
int aroundMine=0; //周围的雷数
Mine(int x,int y) {
this.x=x;
this.y=y;
isfound=false;
ismine=false;
canSearch=true;
setBackground(Color.pink); //设置该点的背景颜色作为没有打开的点
}
}