创建窗体

我们首先需要创建一个窗体来承载五子棋盘。
具体代码如下:

public void initUI(){
		JFrame jf = new JFrame ();
		jf.setTitle ("AI 五子棋");
		jf.setSize (875, 950);
		jf.setResizable (false);
		jf.setDefaultCloseOperation (WindowConstants.EXIT_ON_CLOSE);

		ChessPanel chessPanel = new ChessPanel ();
		JPanel btnPanel = new JPanel ();

		chessPanel.setBackground (Color.GRAY);
		btnPanel.setBackground (Color.LIGHT_GRAY);
		btnPanel.setPreferredSize (new Dimension (105, 80));

		JButton[] btns = this.initBtnPanel (btnPanel);
		jf.add (chessPanel, BorderLayout.CENTER);
		jf.add (btnPanel, BorderLayout.SOUTH);

		jf.setVisible (true);

		// 实现鼠标监听器
		chessPanel.addMouseListener (goListen);
		// 获取画笔Graphics
		goListen.setG (chessPanel.getGraphics ());
		goListen.setChessPanel (chessPanel);
		goListen.setBtns (btns);

	}

绘制棋盘

第一步,在创建好的窗体上绘制出五子棋的棋盘。需要注意的是,因为如果只是在窗体中绘制出棋盘,那么每次窗体被拖动,刷新后,窗体中的绘制好的棋盘就会被刷新掉,所以需要重绘,确保棋盘永久存在,在Chess Panel中,我们在drawChessPanel的方法中实现了棋盘重绘。

public class ChessPanel extends JPanel implements GoData{
	// 重写面板的绘制方法
	// 窗体面板在显示以及刷新时会调用这个方法
	@Override
	public void paint(Graphics g){
		drawChessPanel (g);
	}

	public void drawChessPanel(Graphics g){
		// 缓存绘制
		BufferedImage buffimg = new BufferedImage (this.getWidth (), this.getHeight (), BufferedImage.TYPE_INT_ARGB);
		Graphics2D bg = (Graphics2D) buffimg.getGraphics ();
		// 抗锯齿
		bg.setRenderingHint (RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		bg.setColor (new Color (153, 100, 0));
		bg.fillRect (0, 0, this.getWidth (), this.getHeight ());

		// 绘制棋盘
		bg.setColor (Color.BLACK);
		for(int i = 0; i <= ROW; i++){
			bg.drawLine (X, Y + i * SIZE, X + COL * SIZE, Y + i * SIZE);
			bg.drawLine (X + i * SIZE, Y, X + i * SIZE, Y + ROW * SIZE);
		}

		// 棋子都是临时的 需要保存起来
		// 刷新时再绘制所有存储的棋子

		for(int i = 0; i < chessArray.length; i++){
			for(int j = 0; j < chessArray[i].length; j++){
				int cnum = chessArray[i][j];
				if(cnum != 0){
					bg.setColor (cnum == 1 ? Color.BLACK : Color.WHITE);
					// 还原坐标
					int x = X + (j * SIZE) - SIZE / 2;
					int y = Y + (i * SIZE) - SIZE / 2;
					bg.fillOval (x, y, SIZE, SIZE);
				}
			}
		}

		Graphics2D g2d = (Graphics2D) g;
		// 通过传入的组件Graphics 一次性绘制整张像素图片
		g2d.drawImage (buffimg, 0, 0, this);

	}


}

第二步,是实现在棋盘上添加一系列按钮。

public JButton[] initBtnPanel(JPanel btnPanel){
		String[] btnStrs = {"开始游戏", "悔棋", "退出", "对局记录", "回放", "人人对战", "人机对战"};
		JButton[] btns = new JButton[btnStrs.length];
		for(int i = 0; i < btnStrs.length; i++){
			JButton btn = new JButton (btnStrs[i]);
			btn.setBackground (Color.WHITE);
			btnPanel.add (btn);
			btn.setPreferredSize (new Dimension (95, 35));
			btn.addActionListener (goListen);
			btns[i] = btn;
		}
		return btns;
	}

绘制棋子

在绘制棋子时需要注意,首先,鼠标点击的位置,这个位置不一定正好是在棋盘的网格交汇处的节点上,我们就要将这个鼠标点击的坐标优化至附近的交汇节点上。原理也很简单,将鼠标点击获取的坐标,计算转化成其在哪两个焦点之间,然后将两个节点之间一分为二,如果坐标在前部分,则优化到前面的节点上,如果在后半部分,则优化到后面的节点上。

public class Chess implements GoData{
	int r, c, chessFlag, index;

	public Chess(int r, int c, int chessFlag, int index){
		this.r = r;
		this.c = c;
		this.chessFlag = chessFlag;
		this.index = index;
	}

	// 绘制棋子
	public void drawChess(Graphics g){
		Graphics2D g2d=(Graphics2D)g;
		// 抗锯齿
		g2d.setRenderingHint (RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		g.setColor (chessFlag == 1 ? Color.BLACK : Color.WHITE);
		// 还原坐标
		int x = X + (c * SIZE) - SIZE / 2;
		int y = Y + (r * SIZE) - SIZE / 2;
		g.fillOval (x, y, SIZE, SIZE);
	}


}

监听器类

在GoListener类中,我们实现了对ActionListener, MouseListener中的一些方法的重写。
首先,在这里我们重写了MouseListener中的mousePressed方法,用于监听鼠标在棋盘上的落子位置,并根据当前的游戏模式,来调用playerChess或AIChess的方法。
其次,我们定义了一个chessFlag变量,将黑棋、白棋分别赋值1和2,且为0时代表游戏未开始。每进行一次下棋的操作chessFlag就会变化一次。并利用一个chessArray[ ][ ]的数组记录棋盘上黑白子的位置。
悔棋方法: 我们通过获取到当前棋子,并将其对应位置的数组数值重新定义为0,并将记录的棋子数减一,最后重绘棋盘,来实现悔棋的功能。

public class GoListener implements ActionListener, MouseListener, GoData{

	private Graphics g;

	public void setG(Graphics g){
		this.g = g;
	}

	private ChessPanel chessPanel;

	public void setChessPanel(ChessPanel chessPanel){
		this.chessPanel = chessPanel;
	}

	private JButton[] btns;

	public void setBtns(JButton[] btns){
		this.btns = btns;
	}

	private int chessFlag = 0;//0 表示不能下棋 1 表示黑棋 2 表示白棋
	private int index = 0;//记录棋子的个数
	private String gameModel = "人人对战";//游戏模式

	@Override
	public void actionPerformed(ActionEvent e){
		// 监听按钮的操作
		JButton btn = (JButton) e.getSource ();
		String action = btn.getText ();
		if(action.equals ("开始游戏")){
			chessFlag = 1;
			btn.setText ("结束游戏");

			for(int i = 0; i < btns.length; i++){
				if(btns[i].getText ().equals ("人人对战") || btns[i].getText ().equals ("人机对战")){
					btns[i].setEnabled (false);
				}
				if(btns[i].getText ().equals (gameModel)){
					btns[i].setBackground (Color.RED);
				}
			}

		} else if(action.equals ("结束游戏")){
			chessFlag = 0;
			// 清空棋盘 清除数据
			btn.setText ("开始游戏");
			for(int i = 0; i < btns.length; i++){
				if(btns[i].getText ().equals ("人人对战") || btns[i].getText ().equals ("人机对战")){
					btns[i].setEnabled (true);
				}
				if(btns[i].getText ().equals (gameModel)){
					btns[i].setBackground (Color.WHITE);
				}
			}

		} else if(action.equals ("悔棋")){
			rbChess ();
		} else if(action.equals ("人人对战")){
			chessFlag =1;
			gameModel = "人人对战";
			btn.setBackground (Color.RED);
			for(int i = 0; i < btns.length; i++){

				if(btns[i].getText ().equals ("人机对战")){
					btns[i].setBackground (Color.WHITE);
				}
			}
		} else if(action.equals ("人机对战")){
			chessFlag =1;
			gameModel = "人机对战";
			btn.setBackground (Color.RED);
			for(int i = 0; i < btns.length; i++){

				if(btns[i].getText ().equals ("人人对战")){
					btns[i].setBackground (Color.WHITE);
				}
			}
		}
	}

	@Override
	public void mousePressed(MouseEvent e){
		// 下棋
		// 获取坐标
		int x = e.getX ();
		int y = e.getY ();
		// 通过坐标 获取当前点击的行列值
		int r = (y - Y + SIZE / 2) / SIZE;
		int c = (x - X + SIZE / 2) / SIZE;
		System.out.println ("当前落子位置是:" + r + " " + c);
		// 检查下棋的条件
		checkChessFlag (r, c);
		if(gameModel.equals ("人人对战")){
			// 玩家人机对战
			playerChess (r, c);
		} else if(gameModel.equals ("人机对战")){
			// 电脑下棋
			AIChess (r, c);
		}


	}


	@Override
	public void mouseClicked(MouseEvent e){

	}

	@Override
	public void mouseReleased(MouseEvent e){

	}

	@Override
	public void mouseEntered(MouseEvent e){

	}

	@Override
	public void mouseExited(MouseEvent e){

	}
	
	public void rbChess(){
		// 先判断游戏是否在进行中
		if(chessFlag == 0){
			JOptionPane.showMessageDialog (null, "游戏未开始~", "提醒", JOptionPane.WARNING_MESSAGE);
			return;
		}
		if(index == 0){
			JOptionPane.showMessageDialog (null, "没有棋子可悔~", "提醒", JOptionPane.WARNING_MESSAGE);
			return;
		}
		// 悔棋
		// 查找棋子
		Chess chess = chessList[index - 1];
		chessArray[chess.r][chess.c] = 0;// 矩阵中对应位置 数据清除
		chessFlag = chess.chessFlag;// 棋子颜色还原
		index--;// 棋子个数减少
		// 重绘棋盘
		chessPanel.drawChessPanel (g);
	}
	public boolean checkChessFlag(int r, int c){
		if(chessFlag == 0){
			JOptionPane.showMessageDialog (null, "请先点击开始游戏~", "提醒", JOptionPane.WARNING_MESSAGE);
			return false;
		}
		if(r < 0 || r > COL || c < 0 || c > ROW){
			JOptionPane.showMessageDialog (null, "此处不是下棋的范围~", "提醒", JOptionPane.WARNING_MESSAGE);
			return false;
		}
		if(chessArray[r][c] != 0){
			JOptionPane.showMessageDialog (null, "此处已经有棋子了~", "提醒", JOptionPane.WARNING_MESSAGE);
			return false;
		}
		return true;
	}

	public void playerChess(int r, int c){
		// 下棋
		// 存储棋子
		chessArray[r][c] = chessFlag;
		Chess chess = new Chess (r, c, chessFlag, index);
		chessList[index++] = chess;
		chess.drawChess (g);
		// 判断输赢
		if(ChessJudgeWin.isWin (r, c)){
			JOptionPane.showMessageDialog (null, chessFlag == 1 ? "黑棋胜利~" : (chessFlag == 2 ? "白棋胜利~":"请先点击开始游戏~"), "恭喜",
				  JOptionPane.INFORMATION_MESSAGE);
			chessFlag = 0;
			return;
		}
		// 交换手
		if(chessFlag == 1){
			chessFlag = 2;
		} else if(chessFlag == 2){
			chessFlag = 1;
		}
	}

	private void AIChess(int r, int c){

		// 下棋
		// 存储棋子
		chessFlag = 1;
		chessArray[r][c] = chessFlag;
		Chess chess = new Chess (r, c, chessFlag, index);
		chessList[index++] = chess;
		chess.drawChess (g);
		// 判断输赢
		if(ChessJudgeWin.isWin (r, c)){
			JOptionPane.showMessageDialog (null, "玩家胜利~~", "恭喜",
				  JOptionPane.INFORMATION_MESSAGE);
			chessFlag = 0;
			return;
		}

		// AI下棋
		chessFlag=2;
		// 获取AI下棋的位置
//		int[] aiPoint = AI.getAIChess ();
//		int aiR = aiPoint[0];
//		int aiC = aiPoint[1];

		AI ai = new AI(chessArray);
		ai.ai();

		//ai的xy

		int aiR = (ai.gety()- Y + SIZE / 2) / SIZE;
		int aiC = (ai.getx()- X + SIZE / 2) / SIZE;
//		checkChessFlag(aiR,aiC);
		// 存储棋子
		chessArray[aiR][aiC] = chessFlag;
		chess = new Chess (aiR, aiC, chessFlag, index);
		chessList[index++] = chess;
		chess.drawChess (g);
		// 判断输赢
		if(ChessJudgeWin.isWin (aiR, aiC)){
			JOptionPane.showMessageDialog (null, "AI胜利~~", "恭喜",
				  JOptionPane.INFORMATION_MESSAGE);
			chessFlag = 0;
			return;
		}

	}


}

判断输赢

当棋子都下好了之后,我们就需要在每下一颗棋子后进行输赢的判定。五子棋中,有五颗棋子连在一起即为获胜。因此我们就需要对横竖斜四个大方向上的棋子,进行查找判定。
由于在数组中,黑棋的值都为1,白棋都为2,那么就在每一次所下棋子的地方为起点,向各个方向查找,每有一个相连的棋子是同色,设置一个变量count,count就加1,当count变为5时候,就证明有五个同色棋子相连,游戏就结束,如果所下棋子是黑棋,黑棋就获胜,如果所下棋子是白棋,白棋就获胜。代码如下:

public class ChessJudgeWin implements GoData{

	public static boolean isWin(int r, int c){
		if(row (r, c) >= 5 | col (r, c) >= 5 || leftUp_RightDown (r, c) >= 5 || rightUp_leftDown (r, c) >= 5){
			System.out.println ("win");
			return true;
		}
		return false;


	}

	 //纵向判断

	private static int col(int r, int c){
		int count = 0;
		int chess = chessArray[r][c];
		// 向下
		for(int i = r; i < chessArray.length; i++){
			if(chess == chessArray[i][c]){
				count++;
			} else{
				break;
			}

		}
		// 向上
		for(int i = r - 1; i >= 0; i--){
			if(chess == chessArray[i][c]){
				count++;
			} else{
				break;
			}
		}

		return count;
	}

	// 横向判断
	private static int row(int r, int c){
		int count = 0;
		int chess = chessArray[r][c];
		// 向右
		for(int i = c; i < chessArray[0].length; i++){
			if(chess == chessArray[r][i]){
				count++;
			} else{
				break;
			}

		}
		// 向左
		for(int i = c - 1; i >= 0; i--){
			if(chess == chessArray[r][i]){
				count++;
			} else{
				break;
			}
		}

		return count;
	}

	private static int rightUp_leftDown(int r, int c){
		int count = 0;
		int chess = chessArray[r][c];
		// 向右上
		for(int i = r, j = c; i >= 0 && j < chessArray[0].length; i--, j++){
			if(chess == chessArray[i][j]){
				count++;
			} else{
				break;
			}

		}
		// 向左下
		for(int i = r + 1, j = c - 1; i < chessArray.length && j >= 0; i++, j--){
			if(chess == chessArray[i][j]){
				count++;
			} else{
				break;
			}
		}

		return count;


	}

	private static int leftUp_RightDown(int r, int c){
		int count = 0;
		int chess = chessArray[r][c];
		// 向左上
		for(int i = r, j = c; i >= 0 && j >= 0; i--, j--){
			if(chess == chessArray[i][j]){
				count++;
			} else{
				break;
			}

		}
		// 向右下
		for(int i = r + 1, j = c + 1; i < chessArray.length && j < chessArray[0].length; i++, j++){
			if(chess == chessArray[i][j]){
				count++;
			} else{
				break;
			}
		}

		return count;

	}

}

AI方法及人机对战

这里的人机方法我们利用HashMap建立一个权值表,赋予不同棋盘情况不同的权值,然后让电脑选择在权值最大的地方下棋。
在之前数组中,我们将黑棋、白棋分别赋值1和2,可以根据此情况来设置权值表。

  • 活连: 两端都是空位
  • 010 10
  • 0110 50
  • 01110 1000
  • 011110 5000
  • 020
  • 0220
  • 02220
  • 022220
  • 半活: 一端是空位,一端是边界或者对方棋子
  • 01 8
  • 011 40
  • 0111 800
  • 01111 5000
  • 02 8
  • 022 40
  • 0222 800
  • 02222 5000

这之后,AI需要遍历棋盘,在每一个未下棋子的位置上,以这个位置为起点,去看每个方向上棋盘的情况,然后通过定义一个chessValue[ ][ ] 数组来存储每一个空位置的最终各个方向上棋盘情况的权值总和,那么权值最大的地方,就是AI需要下棋的地方。
在AI下棋之后,同样要调用我们之前写过的判定输赢的方法来进行判定,确保电脑和玩家每下一次棋子就可以进行一次判定。
具体的AI下棋的方法代码如下:

public class AI implements GoData{
    private HashMap<String, Integer> map = new HashMap<>();
    int[][] codeArray;
    int[][] chessValue = new int[16][16];
    private int max_i, max_j;
    public AI(int[][] codeArray) {
        this.codeArray = codeArray;
    }

    public void ai(){
        // 活连
        map.put ("010", 10);
        map.put ("0110", 50);
        map.put ("01110", 1000);
        map.put ("011110", 5000);
        map.put ("020", 10);
        map.put ("0220", 50);
        map.put ("02220", 1000);
        map.put ("022220", 5000);
        //半活
        map.put ("01", 8);
        map.put ("011", 40);
        map.put ("0111", 800);
        map.put ("01111", 5000);
        map.put ("02", 8);
        map.put ("022", 40);
        map.put ("0222", 800);
        map.put ("02222", 5000);

        for (int i = 0; i < codeArray.length; i++) {
            for (int j= 0; j < codeArray[i].length; j++) {
                System.out.print("  "+codeArray[i][j]);
            }
            System.out.println();
        }
        for (int i = 0; i < codeArray.length; i++) {
            for (int j = 0; j < codeArray[i].length; j++) {
                if (codeArray[i][j] == 0) {
                    // 向右
                    String code = "0";
                    int color = 0;
                    for (int k = j + 1; k < codeArray.length; k++) {
                        if (codeArray[i][k] == 0) {
                            break;
                        }else {
                            // 有棋子
                            if (color == 0) {
                                color = codeArray[i][k];// 保存第一颗棋子的颜色
                                code += codeArray[i][k];// 保存棋子相连的情况
                            } else if (codeArray[i][k] == color){
                                code += codeArray[i][k];}
                            else {// 不同棋子跳出循环

                                code += codeArray[i][k];
                                break;
                            }
                        }
                    }
                    Integer value = map.get(code);
                    if (value != null){
                        chessValue[i][j] += value; // 权值累加
                         }
                    // 向左
                    code = "0";
                    color = 0;
                    for (int k = j - 1; k >= 0; k--) {
                        if(codeArray[i][k] == 0) {
                            break;
                        }else {
                            //有棋子
                            if(color == 0) {
                                color = codeArray[i][k];
                                code += codeArray[i][k];
                            }else if(codeArray[i][k] == color) {
                                code +=codeArray[i][k];
                            }else {
                                code += codeArray[i][k];
                                break;
                            }
                        }
                    }
                    value = map.get(code);
                    if (value != null) {
                        chessValue[i][j] += value; // 权值累加
                    }
                    //向下
                    code = "0";
                    color = 0;
                    for (int k = i + 1; k < codeArray.length; k++) {
                        if(codeArray[k][j] == 0) {
                            break;
                        }else {
                            //有棋子
                            if(color == 0) {
                                color = codeArray[k][j];
                                code += codeArray[k][j];
                            }else if(codeArray[k][j] == color) {
                                code +=codeArray[k][j];
                            }else {
                                code += codeArray[k][j];
                                break;
                            }
                        }

                    }
                    value = map.get(code);
                    if (value != null) {
                        chessValue[i][j] += value; // 权值累加
                    }

                    // 向上
                    code = "0";
                    color = 0;
                    for (int k = i - 1; k >= 0; k--) {
                        if(codeArray[k][j] == 0) {
                            break;
                        }else {
                            //有棋子
                            if(color == 0) {
                                color = codeArray[k][j];
                                code += codeArray[k][j];
                            }else if(codeArray[k][j] == color) {
                                code +=codeArray[k][j];
                            }else {
                                code += codeArray[k][j];
                                break;
                            }
                        }
                    }
                    value = map.get(code);
                    if (value != null) {
                        chessValue[i][j] += value; // 权值累加
                    }

                    // 左上
                    code = "0";
                    color = 0;
                    for (int m = i - 1, n = j - 1; m >= 0 && n >= 0; m--, n--) {
                        if(codeArray[m][n] == 0) {
                            break;
                        }else {
                            if(color == 0) {
                                color = codeArray[m][n];
                                code += codeArray[m][n];
                            }else if(codeArray[m][n] == color){
                                code += codeArray[m][n];
                            }else {
                                code += codeArray[m][n];
                                break;
                            }
                        }
                    }
                    value = map.get(code);
                    if (value != null) {
                        chessValue[i][j] += value; // 权值累加
                    }
                    // 左下
                    code = "0";
                    color = 0;
                    for (int m = i + 1, n = j - 1;n >= 0 && m < codeArray.length; m++, n--) {
                        if(codeArray[m][n] == 0) {
                            break;
                        }else {
                            if(color == 0) {
                                color = codeArray[m][n];
                                code += codeArray[m][n];
                            }else if(codeArray[m][n] == color){
                                code += codeArray[m][n];
                            }else {
                                code += codeArray[m][n];
                                break;
                            }
                        }
                    }
                    value = map.get(code);
                    if (value != null) {
                        chessValue[i][j] += value; // 权值累加
                    }
                    // 右下
                    code = "0";
                    color = 0;
                    for (int m = i + 1, n = j + 1;n < codeArray.length && m < codeArray.length; m++, n++) {
                        if(codeArray[m][n] == 0) {
                            break;
                        }else {
                            if(color == 0) {
                                color = codeArray[m][n];
                                code += codeArray[m][n];
                            }else if(codeArray[m][n] == color){
                                code += codeArray[m][n];
                            }else {
                                code += codeArray[m][n];
                                break;
                            }
                        }
                    }
                    value = map.get(code);
                    if (value != null) {
                        chessValue[i][j] += value; // 权值累加
                    }
                    // 右上
                    code = "0";
                    color = 0;
                    for (int m = i - 1, n = j + 1;m >= 0 && n < codeArray.length; m--, n++) {
                        if(codeArray[m][n] == 0) {
                            break;
                        }else {
                            if(color == 0) {
                                color = codeArray[m][n];
                                code += codeArray[m][n];
                            }else if(codeArray[m][n] == color){
                                code += codeArray[m][n];
                            }else {
                                code += codeArray[m][n];
                                break;
                            }
                        }
                    }
                    value = map.get(code);
                    if (value != null){
                        chessValue[i][j] += value; // 权值累加
                }
                }
            }
        }

        //找权值最大的空处
        int max_v = 0;

        for (int i = 0; i < ROW +1; i++) {
            for(int j = 0;j <COL+1;j++) {
                if (max_v < chessValue[i][j]) {
                    max_v = chessValue[i][j];
                    max_i = i;
                    max_j = j;
                }
            }
        }

        for (int j = 0; j < ROW+1; j++)
            for (int i = 0; i < COL+1; i++)
                chessValue[i][j] = 0;
    }
    public int getx() {
        return X+SIZE*max_j;
    }

    public int gety() {
        return Y + max_i * SIZE;
    }