以下每行代码,文字均为原创,转载请注明出处.
程序一共分为7个文件,每个文件为一个类
文件名 | 功能描述 |
Test.java | 测试类,包含main()函数 |
Mine.java | 设计主界面, |
Calmine.java | 随机雷的位置.计算雷区点击后应该显示的数字 |
My_button.java | 继承自button类,添加按钮的坐标x,y. |
Num_Mine.java | 雷数,包括用户以标记的雷数,标记正确的雷数,以及总雷数 |
ClickLone.java | 鼠标左击处理 |
ClickRone.java | 鼠标右击处理 |
Test.java
测试类,Mine()接受的三个参数分别为雷区的长,宽,总雷数
package mine_sweep; public class Test { public static void main(String[] args) { // TODO Auto-generated method stub new Mine(16,16,20); } }
Mine.java
继承自JFame类,采用BorderLayout()布局,.NORTH区域放置显示面板JPanel,提示用户以标记的雷数,.SOUTH区域放置雷区,采用GridLayout布局
用一个双重循环向雷区minepanel添加按钮,同时在buttons[]中储存该按钮的引用.
同时添加按钮监听器..在用户点击了右键后,负责更新标记的类数
package mine_sweep; import java.awt.*; import java.awt.event.*; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JTextArea; @SuppressWarnings("serial") public class Mine extends JFrame { Integer rn; //用户已标记的雷数 Integer sn; //标记对的雷数 public Mine(int rows,int cols,int n) //雷区的行数,列数,雷数 { My_button buttons[]=new My_button[rows*cols]; //记录每个按钮的引用 Integer mine[]= new Integer[rows*cols]; //每个位置的数字 Boolean ismine[]=new Boolean[rows*cols]; //是否有雷的标志 new Calmine(mine,ismine,rows,cols,n); this.setSize(500, 450); this.setTitle("mine_sweep"); this.setLayout(new BorderLayout()); rn=new Integer(0); //用户标记的雷数 sn=new Integer(0); //标记对的雷数 JPanel showpanel=new JPanel(); //设置控制面板 showpanel.setLayout(new FlowLayout(FlowLayout.LEFT)); JTextArea showArea=new JTextArea("sum of mine: "+n); JTextArea showrmArea=new JTextArea("num of marked mine: "+rn.intValue()); showArea.setEditable(false); showrmArea.setEditable(false); showpanel.add(showrmArea ); showpanel.add(showArea); this.add(showpanel,BorderLayout.NORTH); JPanel minepanel=new JPanel(); //设置雷区 minepanel.setLayout(new GridLayout(rows,cols)); minepanel.setSize(500, 450); Mine t=this; for(int i=0;i<rows;i++) { for(int j=0;j<cols;j++) { My_button btn=new My_button(i,j); buttons[i*cols+j]=btn; minepanel.add(buttons[i*cols+j] ); btn.setBackground(Color.blue); btn.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { if(e.getButton()==MouseEvent.BUTTON1) { int ans=rn.intValue(); if(btn.getLabel()!="#") ans = new ClickLone(buttons,btn.x,btn.y,rows,cols,mine,ismine,t,rn).rep(); showrmArea.setText("num of marked mine: "+ans); //更新已标记雷数; rn=new Integer(ans); } if(e.getButton()==MouseEvent.BUTTON3) { Num_Mine num_mine=new Num_Mine(rn,sn,n); num_mine=new ClickRone(buttons,btn.x,btn.y,rows,cols,num_mine,t,ismine).repnum(); rn=new Integer(num_mine.mark_mine); showrmArea.setText("num of marked mine: "+rn); //更新已标记雷数 sn=new Integer(num_mine.mark_correct_mine); //更新标记正确雷数 } } }); //添加鼠标点击事件 } } this.addWindowListener(new WindowAdapter(){ //添加窗口关闭监听器 public void windowClosing(WindowEvent e) { t.dispose(); } }); this.add(minepanel,BorderLayout.SOUTH); minepanel.setVisible(true); this.setVisible(true); } }
Calmine.java
随机雷的位置.储存在ismine[]中. 并通过dx[].dy[]遍历一个按钮周围的8个位置.计算出每个按钮点击后应该显示的值,计算出的结果存储在mine[]中
注意:随机数可能产生重复的,导致雷数不够.所以,要加一个while循环放置产生重复的位置
package mine_sweep; import java.util.Random; public class Calmine { Integer mine[]; Boolean ismine[]; int rows,cols; int dx[]={-1,1,0}; int dy[]={-1,1,0}; int num; Random rd; Calmine(Integer m[],Boolean ism[],int r,int l,int n) { rows=r; cols=l; num=n; mine=m; ismine=ism; rd=new Random(); for(int i=0;i<rows*cols;i++) //初始化 { ismine[i]=new Boolean(false); mine[i]=new Integer(0); } for(int i=0;i<num;i++) //随机产生n雷 { int rom=rd.nextInt(rows*cols); while(ismine[rom]==true) //防止生成重复的随机数 rom=rd.nextInt(rows*cols); ismine[rom]=new Boolean(true); } for(int i=0;i<rows;i++) //计算每个按钮按下后应该显示的数字 for(int j=0;j<cols;j++) { int t=0; for(int x=0;x<3;x++) //周围8个位置 for(int y=0;y<3;y++) { int nx=dx[x]+i,ny=dy[y]+j; if(nx>=0&&ny>=0&&nx<rows&&ny<cols&&(!ismine[i*cols+j].booleanValue())) { t+=ismine[nx*cols+ny].booleanValue()?1:0; } } mine[i*cols+j]=new Integer(t); } } }
My_button.java
继承自Button类,添加x,y.标记该按钮是处以雷区的的第几行第几列
package mine_sweep; import java.awt.Button; @SuppressWarnings("serial") public class My_button extends Button { int x; int y; My_button() { super(); } My_button(int a,int b) { super(); x=a; y=b; } }
Num_Mine.java
存储用户以标记的雷数,标记正确的雷数,以及雷区的总雷数
package mine_sweep; public class Num_Mine { Integer mark_mine; Integer mark_correct_mine; Integer sum_mine; Num_Mine(Integer rn,Integer sn,Integer n) { mark_mine=new Integer(rn); mark_correct_mine=new Integer(sn); sum_mine=new Integer(n); } }
ClickLone.java
左击事件的处理. 分两种情况:
1,点击的位置是雷,这时,弹出一个模态对话框显示GAME OVER,结束游戏
2.点击的地方不是雷,这时,需要把该按钮设置为setEnabled(false).并通过mine[]数组中预先计算出应该显示的数字设置按钮标签,如果为零,这设置为空格..然后,如果点击的位置对应的mine[]的值为0,则调用BFS,不断向周围扩散,直到遇见数字或雷为止(广度优先搜索,队列)
package mine_sweep; import java.awt.Color; import java.awt.FlowLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.util.LinkedList; import java.util.Queue; import javax.swing.JButton; import javax.swing.JDialog; public class ClickLone { My_button buttons[]; //按钮的引用 int i,j; int rows,cols; Integer mine[]; Boolean ismine[]; boolean used[]; Integer rn; //已经标记的雷数 Mine sjm; int dx[]={1,-1,0}; int dy[]={1,-1,0}; ClickLone(My_button[] tem,int a,int b,int r,int l,Integer mines[],Boolean ismines[],Mine Jm,Integer n) { buttons=tem; rn=n; mine=mines; ismine=ismines; rows=r; cols=l; i=a; j=b; sjm=Jm; used=new boolean[rows*cols]; for(int i=0;i<rows*cols;i++) used[i]=false; if(ismine[i*cols+j].booleanValue()) //如果选中地雷 { buttons[i*cols+j].setEnabled(false); buttons[i*cols+j].setLabel("*"); buttons[i*cols+j].setBackground(Color.red); JButton btc=new JButton("GAME OVER!"); JDialog dialog=new JDialog(sjm,"game_over"); dialog.setLayout(new FlowLayout()); dialog.addWindowListener(new WindowAdapter() //对话框监听器 { @Override public void windowClosing( WindowEvent e) { // TODO Auto-generated method stub super.windowClosing(e); dialog.dispose(); sjm.dispose(); } }); btc.addActionListener(new ActionListener(){ @Override public void actionPerformed( ActionEvent e) { // TODO Auto-generated method stub dialog.dispose(); sjm.dispose(); } }); dialog.add(btc); dialog.setModal(true); //弹出game_over窗口 dialog.setSize(220,150 ); dialog.setLocation(350, 250); dialog.setVisible(true); return; } Bfs(i,j); // buttons[i*cols+j].setLabel(mine[i*cols+j].toString()); // if(ismine[i*cols+j].booleanValue()) // { // buttons[i*cols+j].setLabel("*"); // } } int rep() { return rn.intValue(); } void Bfs(int i,int j) //广度优先搜索 { Queue<Integer> que=new LinkedList<Integer>(); que.offer(new Integer(i*cols+j)); while(!que.isEmpty()) { int t=que.poll().intValue(); if(new String("#").equals(buttons[t].getLabel())) //如果该位置标记为雷,说明用户标记错误 rn=new Integer(rn.intValue()-1); //用户标记的雷数-1 if(new String("0").equals(mine[t].toString()) ) buttons[t].setLabel(" "); else { buttons[t].setLabel(mine[t].toString()); } buttons[t].setEnabled(false); buttons[t].setBackground(Color.gray); if(mine[t].intValue()!=0) continue; i=t/cols; j=t%cols; for(int x=0;x<3;x++) for(int y=0;y<3;y++) { int nx=i+dx[x],ny=j+dy[y]; if(nx>=0&&ny>=0&&nx<rows&&ny<cols&& //没有越界 mine[i*cols+j].intValue()==0 // 是空白区域且没有雷 &&(!ismine[nx*cols+ny].booleanValue()) &&(!(nx==i&&ny==j)) &&(!used[nx*cols+ny]) ) { used[nx*cols+ny]=true; que.offer(new Integer(nx*cols+ny)); } } } } }
ClickRone.java
处理右键事件,如果该位置还没有被标记,点击后将按钮的标签设置为"#" ,如果已经被标记为"#" ,则将标记清除.
如果标记的雷数==标记对的雷数==总雷数,接结束游戏,弹出YOU WIN 对话框
package mine_sweep; import java.awt.Color; import java.awt.FlowLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import javax.swing.JButton; import javax.swing.JDialog; public class ClickRone { My_button buttons[]; int x,y; int rows,cols; Mine sjm; Boolean ismine[]; Num_Mine num_mine; ClickRone(My_button button[],int a,int b,int r,int l,Num_Mine num,Mine jm,Boolean ism[]) { ismine=ism; buttons=button; num_mine=num; sjm=jm; x=a; y=b; rows=r; cols=l; if(buttons[x*cols+y].getLabel()=="#") //该位置以被标记为雷,取消标记 { num_mine.mark_mine=new Integer(num_mine.mark_mine.intValue()-1); buttons[x*cols+y].setBackground(Color.blue); buttons[x*cols+y].setLabel(" "); if(ismine[x*cols+y].booleanValue()) //如果该位置是雷 num_mine.mark_correct_mine=new Integer(num_mine.mark_correct_mine.intValue()-1); } else //加上雷的标记 { num_mine.mark_mine=new Integer(num_mine.mark_mine.intValue()+1); buttons[x*cols+y].setBackground(Color.yellow); buttons[x*cols+y].setLabel("#"); if(ismine[x*cols+y].booleanValue()) //如果标记正确 num_mine.mark_correct_mine=new Integer(num_mine.mark_correct_mine.intValue()+1); } if(num_mine.mark_correct_mine.intValue()==num_mine.mark_mine.intValue() &&num_mine.mark_mine.intValue()==num_mine.sum_mine.intValue()) { JButton btc=new JButton("YOU WIN"); JDialog dialog=new JDialog(sjm,"you_win"); dialog.setLayout(new FlowLayout()); dialog.addWindowListener(new WindowAdapter() //对话框监听器 { @Override public void windowClosing( WindowEvent e) { // TODO Auto-generated method stub super.windowClosing(e); dialog.dispose(); sjm.dispose(); } }); btc.addActionListener(new ActionListener(){ @Override public void actionPerformed( ActionEvent e) { // TODO Auto-generated method stub dialog.dispose(); sjm.dispose(); } }); dialog.add(btc); dialog.setModal(true); //弹出you_win窗口 dialog.setSize(220,150 ); dialog.setLocation(350, 250); dialog.setVisible(true); } } Num_Mine repnum() { return num_mine; } }
下面是运行截图:
从拿到题目,到基本完成,一共用了整整一天的时间(早上八点~第二天凌晨1点).
后来用了3天改了十数个bug.终于做成了现在比较完善的程序.