關於五子棋程序的實現(二)——添加監聽

   上次的代碼已經能夠讓我們畫出一張五子棋的棋盤界面了,只是它還不能用來下棋。這一次的文章我們將讓這個五子棋盤真正的可以用來下棋。
  一、做之前的思考
      1. 首先要弄明白的是,所謂的下棋,其實質是在GoBang類的對象gb的棋子數組isArrive[ ][ ]存儲一些值,將0變爲1(黑棋)或2(白棋),在根據1或者2讓棋盤繪製出棋子在棋盤面板上。

     2.再者,既然棋盤上有三個按鈕和一個下拉菜單,就自然地想到要添加3+1個動作監聽。ActionListener。用
 if(e.getActionCommand().equals("xxx"))來應對不同的情況。不過,JComboBox的和JButton有點不同,需要如下操作
     JComboBox<String> box = (JComboBox<String>) e.getSource();// 獲取事件源對象
     type = box.getSelectedItem().toString(); // 獲取選擇的對戰模式  
     再用這type去做if判斷。

      3.下棋的動作是用鼠標在棋盤面板上點擊才發生的,所以應該爲其添加鼠標監聽。並通過點擊來獲取該位置的橫縱座標x,y,計算出isArrive[][]的countx,county,才能將數據填入其中;再通過countx,county計算出arrivex, arrivey,也就是在棋盤上應該繪製棋子的位置。

      4 .對於ActionListener應該想到:
         當我們點擊了“開始新遊戲”,棋盤才能下棋,否則是不能的。也就是在發生了這個動作後才爲棋盤面板添加鼠標監聽。而且,所有的原來的棋子都應該清空,(棋子數組清零),棋盤清空(重繪),而且最好能讓“人人對戰”、“人機對戰”的模式選擇了就鎖定(畢竟不可能一盤棋裏下到一半突然換人),即將box鎖定。

    當我們點擊了“悔棋”,我們希望做的是將上一步棋去掉,而且應該讓棋子的顏色變回前一種顏色。爲了記錄每一步棋子的位置,很自然地想到用ArrayList<chess>list來儲存,chess類是定義爲了便於記錄的。此時的具體操作在下面敘述。
        當我們點擊了”認輸“,只需要根據此時輪到誰下棋來判斷誰輸誰贏就行了。同時認輸後讓box解除封印。
public void actionPerformed(ActionEvent e) {
		if (e.getActionCommand().equals("開始新遊戲")) {
			gf.addMouseListener(this);
			for (int i = 0; i < gf.isArrive.length; i++)
				for (int j = 0; j < gf.isArrive[i].length; j++)
					gf.isArrive[i][j] = 0; // 初始化存儲棋子的數組使其恢復到初始狀態
			box.setEnabled(false); // 讓下拉可選框鎖定
			gf.repaint();
		}

		else if (e.getActionCommand().equals("悔棋")) {
			if (list.size() > 0) {
				// 從list列表中獲取最後一顆棋子的位置
				chess lastchess = list.remove(list.size() - 1);   //得到上一步棋的位置
				gf.isArrive[lastchess.r][lastchess.c] = 0;   //讓該位置的數組置零
				if (turn == 1)
					turn++;
				else
					turn--;
				gf.repaint();
			}
		}

		else if (e.getActionCommand().equals("認輸")) {
			if (turn == 1)
				JOptionPane.showMessageDialog(gf, "黑棋認輸,白棋獲勝!");
			else
				JOptionPane.showMessageDialog(gf, "白棋認輸,黑棋獲勝!");
			gf.removeMouseListener(this);  //移除監聽  ( 按道理來說不應該在分出勝負以後棋盤上還能落子,所以應該移除監聽
			box.setEnabled(true);
		} else if (e.getSource() instanceof JComboBox) {
			JComboBox<String> box = (JComboBox<String>) e.getSource();// 獲取事件源對象
			type = box.getSelectedItem().toString(); // 獲取選擇的對戰模式
		}
	}

     5.對於MouseListener應該想到:
          在點擊時獲取座標,然後根據box的文字來用if區分調用“人人對戰”、“人機對戰”的方法
public void mouseClicked(MouseEvent e) { //取得橫縱座標int x = e.getX();
int y = e.getY();
if (type.equals("人人對戰"))
{this.PERSONPLAY(x, y);}
else if (type.equals("人機對戰"))
{this.AIPLAY(x, y);}

   二、人人對戰
    人人對戰的思路我不多加贅述了,用代碼來邊寫別解釋吧
public void PERSONPLAY(int x, int y) {

		// 人下的方法
		// 計算棋子要落的交叉點
		int countx = (y - 20 + size / 2) / size;
		int county = (x - 20 + size / 2) / size;
		g = (Graphics2D) gf.getGraphics();
		g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
				RenderingHints.VALUE_ANTIALIAS_ON); // 抗鋸齒,讓棋子更加圓潤
		int arrivex, arrivey; // 棋盤上的落點
		arrivex = 20 + county * size;
		arrivey = 20 + countx * size;

		if (gf.isArrive[countx][county] != 0) // 有棋子,不可下棋
		{
			JOptionPane.showMessageDialog(gf, "此處已有棋子,請換一個地方");
		} else {// 當前位置可以下棋
			if (turn == 1) {
				// 設置顏色
				g.setColor(Color.black);

				// 下棋
				g.fillOval(arrivex - size / 2, arrivey - size / 2, size, size);
				gf.isArrive[countx][county] = 1;
				turn++;
			} else {
				// 設置顏色
				g.setColor(Color.white);

				// 下棋
				g.fillOval(arrivex - size / 2, arrivey - size / 2, size, size);
				gf.isArrive[countx][county] = 2;
				turn--;

			}
			//

			list.add(new chess(countx, county));// 有序地存儲每個棋子的行列,爲悔棋做準備

			// 判斷輸贏
			if (Gobangwin.judge(gf.isArrive, countx, county)) {
				if (turn == 2)
					JOptionPane.showMessageDialog(gf, "黑棋勝利");
				else
					JOptionPane.showMessageDialog(gf, "白棋勝利");
				gf.removeMouseListener(this); //移除監聽  ( 按道理來說不應該在分出勝負以後棋盤上還能落子,所以應該移除監聽
			}
		}

	}

         上面代碼中缺少判斷輸贏的算法,我建了一個Gobangwin類來進行操作。在這個類裏面就定義判斷輸贏的算法:
從水平,豎直,左斜,右斜的方向來分別判斷是否五子相連。這個類比較簡單,不多贅述
package GoBang;

public class Gobangwin {

	public static boolean judge(int[][]isArrive, int r, int c){ //如果五子相連就返回true,否則就返回false
		if(countx(isArrive,r,c)>=5||county(isArrive,r,c)>=5||countxy1(isArrive,r,c)>=5||countxy2(isArrive,r,c)>=5)
			return true;
		
		else return false;
	}
	
	
	//計算豎直方向是否五子相連
	private static int countx(int[][] isArrive, int r, int c) {
		int count = 1;
		for (int r1 = r - 1; r1 >= 0; r1--)
			if (isArrive[r][c] == isArrive[r1][c])
				count++;
			else
				break;

		for (int r1 = r + 1; r1 < isArrive.length; r1++)
			if (isArrive[r][c] == isArrive[r1][c])
				count++;
			else
				break;
		return count;
	}
	
	
	
	//計算水平方向是否五子相連
		private static int county(int[][] isArrive, int r, int c) {
			int count = 1;
			for (int c1 = c - 1; c1 >= 0; c1--)
				if (isArrive[r][c] == isArrive[r][c1])
					count++;
				else
					break;

			for (int c1 = c + 1; c1 < isArrive[r].length; c1++)
				if (isArrive[r][c] == isArrive[r][c1])
					count++;
				else
					break;
			return count;
		}
		
		
		
		//計算左上至右下斜的方向是否五子相連
		private static int countxy1(int[][] isArrive, int r, int c) {
			int count = 1;  //往左上角走
			for (int r1 = r - 1,c1=c-1; r1 >= 0&&c1>=0; r1--,c1--)
				if (isArrive[r][c] == isArrive[r1][c1])
					count++;
				else
					break;
           //往右下角走
			for (int r1 = r + 1,c1=c+1; r1 < isArrive.length&&c1<isArrive[r].length; r1++,c1++)
				if (isArrive[r][c] == isArrive[r1][c1])
					count++;
				else
					break;
			return count;
		}
		
		
		
		//計算右上至左下斜的方向是否五子相連
		private static int countxy2(int[][] isArrive, int r, int c) {
			int count = 1;  //往右上角走
			for (int r1 = r - 1,c1=c+1; r1 >= 0&&c1<isArrive[r].length; r1--,c1++)
				if (isArrive[r][c] == isArrive[r1][c1])
					count++;
				else
					break;
			  //往左下角走
			for (int r1 = r + 1,c1=c-1; r1 < isArrive.length&&c1>=0; r1++,c1--)
				if (isArrive[r][c] == isArrive[r1][c1])
					count++;
				else
					break;
			return count;
		}
		

}
 
         上面的代碼思路很簡單,需要注意的是左斜右斜時r,c的變化是往下r增大,往右是c在增大。(這裏有點不同,也可能只是我一個人覺得。。)

          tip1.爲了方便時刻改變棋子的大小和行列,我們把size和row,column封裝在一個接口然後去繼承它。
package GoBang;


public interface GoBangconfig {
  public static int size=30,column=15,row=15,x=20,y=20; 
  
  
}

               
     tip2.這個程序還是蠻大的,建議寫一部分就調試一部分,確認沒錯時再繼續往下寫。(血和淚的教訓)


        至此。這個棋盤已經可以用來和朋友下棋了。
        下一部分將實現人機對戰,也就是AI算法
 
                                                                                             To be continued...


  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章