关于五子棋程序的实现(二)——添加监听

本文详细介绍如何构建一个可实际使用的五子棋游戏界面,包括添加动作监听、鼠标监听实现下棋逻辑,以及悔棋、认输等功能。并介绍了如何通过数组记录棋子位置和判断胜负。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

   上次的代码已经能够让我们画出一张五子棋的棋盘界面了,只是它还不能用来下棋。这一次的文章我们将让这个五子棋盘真正的可以用来下棋。
  一、做之前的思考
      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...


  
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值