JAVA實現簡單掃雷遊戲

這是我第一次寫博客,初衷是想把我學到的東西展示出來,通過寫博客的方式再捋一遍自己的思路。希望自己的一點點想法能夠給其他人啓發,我也要把自己存在的問題提出來,以此文爲起點,樹立寫博客的習慣,在之後的日子裏不斷見證自己的成長。

因爲win10系統沒有自帶的掃雷遊戲我很難受,就決定自己要寫一個掃雷出來。
在這裏插入圖片描述
在這裏插入圖片描述

需求:

  • 懂得一定的JAVA圖形化界面知識
  • 懂得一定的搜索算法知識(如果不懂的最好先學習一下廣度優先搜索)

實現掃雷的要點:

  1. 如何隨機生成雷
  2. 如何自動打開空地
  3. 如何在空地添加附近雷的個數

思路:

  • 使用網格佈局,每一個單元格代表一個點,每個點可能是空地或者是雷。
  • 鼠標左鍵單擊打開一個點,是雷就結束,是空地就顯示周圍8個點雷的個數,右鍵單擊在一個點上插旗,雙擊左鍵可自動打開符合條件的所有空地,所有雷都插旗並且剩下的所有點都被打開就勝利。
  • 每個點用一個繼承了JButton類的類的對象代表,則可用一個二維的對象數組對應網格佈局生成每一個點。
  • 通過鼠標事件監控每一個點從而進行遊戲的操作。

隨機生成雷&空地添加附近雷的個數:
使用兩個隨機數得到座標,若該座標上的點沒有埋雷就在該點埋一個雷。一直循環該過程直到雷的個數夠需求爲止。
原來設想的是在點開空地後再搜索附近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);		//設置該點的背景顏色作爲沒有打開的點
	}	
}

這個掃雷只是完成了簡單的遊戲工程,許多東西還可以進行改進美化,如果有人想簡借我的代碼我會很開心但希望能夠在評論裏和我說一下。

發佈了22 篇原創文章 · 獲贊 58 · 訪問量 7292
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章