实现五子棋的简单人人对战和人机对战的万字详细教程

创建一个窗体来承载五子棋

如何创建一个窗体,以及如何在窗体上添加必要的按钮组件等操作,可以查看我的第一篇文章,里面有详细的讲解,这里就不在重新累述。具体的代码如下:

public class DrawUI{
   
   
      public void ShowUI(){
   
   
      MyFrame jf=new MyFrame();//这里原本应该是使用JFrame但,后续重绘中创建了子类MyFrame继承父类JFrame,这里就换成了MyFrame
  //设置窗体属性
      jf.setSize(500,500);
      jf.setLocationRelativeTo(null);
      jf.setTitle("五子棋");
      jf.setResizable(true);
      jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      FlowLayout fl=new FlowLayout();//设置窗体格式
      jf.setLayout(fl);
      JButton jb=new JButton("人人对战");//添加按钮
      JButton jb1=new JButton("悔棋");
      JButton jb2=new JButton("清空棋盘");
      JButton jb3=new JButton("人机对战");
      jf.add(jb);
      jf.add(jb1);
      jf.add(jb2);
      jf.add(jb3);
      jf.setVisible(true);
      Graphics g=jf.getGraphics();//给窗体添加画笔
      UIListener ul=new UIListener();
      jb.addActionListener(ul);//给按钮添加监听器
      jb1.addActionListener(ul);
      jb2.addActionListener(ul);
      jb3.addActionListener(ul);
      jf.addMouseListener(ul);//给窗体添加监听器
      ul.gr=g;//将画笔倒给UIlistener中的gr
      jf.ul=ul;//为了在Myframe中使用ChessArray,将UIistener中的ul倒给Myframe中的ul
      ul.jb11=jb;//将按钮jb倒给ul中的jb11,方便其使用jb
      ul.mf=jf;//为了在UIlistener中重绘,将jf倒给其中的mf
  }
}

五子棋基本程序编写

绘制棋盘

第一步肯定是要在创建好的窗体上绘制出五子棋的棋盘。思路也很简单,就是画出横竖两条线,然后设置一个固定间隔,不断循环,绘制出一个棋盘。

这里注意,因为如果只是在窗体中绘制出棋盘,那么每次窗体被拖动,刷新后,窗体中的绘制好的棋盘就会被刷新掉,所以需要重绘,确保棋盘永久存在,所以在这里,我们就直接将棋盘绘制在窗体重绘方法中。

public class MyFrame extends JFrame{
   
   //MyFrame子类继承JFrame父类
	public void paint(Graphics g) {
   
   //重绘棋盘,确保刷新网页时候,棋盘还在
		super.paint(g);
		int x1,x2,y1,y2;
		int X1,X2,Y1,Y2;
		x1=50;x2=450;y1=70;y2=470;
		X1=50;X2=450;Y1=70;Y2=470;
		for(int i=0; i<21; i++) {
   
   
		       g.drawLine(x1,y1,x2,y1);
		       y1=y1+20;
		       
		}
		for(int i=0; i<21; i++) {
   
   
			  g.drawLine(X1,Y1,X1,Y2);
			  X1=X1+20;
		}
	}
}

这里也需要注意:因为重绘方法是需要写在MyFrame类中的,由于后续还会有需要重绘的东西,所以后续会在这部分继续扩充需要重绘的内容代码。

绘制棋子(人人对战中棋子黑白交替出现,人机对战中玩家下黑子,电脑下白子)

绘制棋子时候,我们应该能清楚思路。

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

其次,将鼠标点击的坐标优化到棋盘网格节点上后,则需要在这个网格节点上绘制棋子即可,但这里我们会发现,程序中绘制的圆形图形的时候,输入的绘制坐标点是绘制出来圆形的外切长方形的左上角的坐标,并非是圆形的圆心坐标。这时同样需要优化,将坐标分别减去它的半径,就能优化在鼠标点击转化过来的网格点上。

最后,我们需要存储已经下过的棋子的信息,方便后续判定输赢,已经棋子重绘时使用。这里为了存储棋子信息,我们就可以设立一个二维数组,二维数组正好对应横竖棋盘的网格,我们可以将黑棋的数组值定为1,白棋的数组值定为2。那么没有棋子的地方的数组值就为默认的0.

具体来看代码:

public class UIListener implements MouseListener ,ActionListener{
   
   //监听器是接口
//后续可能会用到的一些变量
	Graphics gr;
	int[][] ChessArray=new int[22][22];//黑白棋子数组
	int[][] ChessValue=new int[22][22];//AI人机中权值数组
	JButton jb11;
	int nb=0;
	int count=0;
	int a,b,x,y,X,Y;
	JFrame mf;
	HashMap<String,Integer> hm=new HashMap<String,Integer>();//设置AI人机中权值量表
	public void mouseClicked(MouseEvent e) {
   
   
		int z=50,c=20;
		Color c1=new Color(0,0,0);
		Color c2=new Color(255,255,255);
	    a=e.getX();b=e.getY();//获得鼠标点击坐标
	    if(nb==1) {
   
   //确定是否可以下棋
	    if((a-z)%c>c/2) {
   
   //帮助x轴上点击点优化到坐标格子上
	    	x=(a-z)/c+1;
	    }else {
   
   
	    	x=(a-z)/c;
	    }
	    if((b-z)%c>c/2) {
   
   //帮助y轴上点击点优化到坐标格子上
	    	y=(b-z)/c+1;
	    }else {
   
   
	    	y=(b-z)/c;
	    }
	    if(ChessArray[x][y]==0) {
   
   //确保棋子只能下在没有棋子的地方
	    		if(zifu.equals("人机对战")) {
   
   
	    			gr.setColor(c1);
	    	    	gr.fillOval(x*c+z-10, y*c+z-10, 20, 20);
	    	    	ChessArray[x][y]=1;
	    	    	gr.setColor(c2);
	    	    	AI();
	    		}else if(zifu.equals("人人对战")) {
   
   
	    			if(count==0) {
   
   
	    	    	gr.setColor(c1);
	    	    	count++;
	    	    	gr.fillOval(x*c+z-10, y*c+z-10, 20, 20);
	    	    	ChessArray[x][y]=1;//黑子数组值为1,方便重绘使用
	    	        }else if(count==1) {
   
   
	    	    	gr.setColor(c2);
	    	    	count--;
	    	    	gr.fillOval(x*c+z-10, y*c+z-10, 20, 20);
	    	    	ChessArray[x][y]=2;//白子数组值为21,方便重绘使用
	    	        }
	    		}
	    }else{
   
   
	    	System.out.println("该位置已有棋子");
	    }
	 }else {
   
   
		 System.out.println("请选择对战类型");
	 }
}

重绘棋子

在绘制出棋子后,我们同样需要去意识到,拖动窗体或者任何会让窗体刷新的操作后,已经下好的棋子都会消失,那么我们就需要在重绘方法中,同样加入棋子的重绘,也就是正在绘制棋盘这个标题下的代码行中加入重绘棋子的代码。

原理则是,因为我们设置了数组保存棋子,黑棋都赋值了1,白棋都赋值了2,那么我就遍历整个棋盘上上的所有数组,然后把数组等于1和2的都重绘出来即可。

加入了棋子重绘的代码后,整个重绘部分的代码就如下:

public class MyFrame extends JFrame{
   
   //MyFrame子类继承JFrame父类
	public UIListener ul;
//	int[][] ChessArray;
	int z=50,c=20;
	public void paint(Graphics g) {
   
   //重绘棋盘,确保刷新网页时候,棋盘还在
		super.paint(g);
		int x1,x2,y1,y2;
		int X1,X2,Y1,Y2;
		x1=50;x2=450;y1=70;y2=470;
		X1=50;X2=450;Y1=70;Y2=470;
		for(int i=0; i<21; i++) {
   
   
		       g.drawLine(x1,y1,x2,y1);
		       y1=y1+20;
		       
		}
		for(int i=0; i<21; i++) {
   
   
			  g.drawLine(X1,Y1,X1,Y2);
			  X1=X1+20;
		}

		int[][] ChessArray = ul.ChessArray;
		for(int i=0;i<ChessArray.length;i++) {
   
   //重绘棋子,网页刷新后,之前下过的棋子还在
			for(int j=0;j<ChessArray.length;j++) {
   
   
				if(ChessArray[i][j]==1) {
   
   
					g.setColor(Color.black);
					g.fillOval(i*c+z-10, j*c+z-10, 20, 20);
				}
				if(ChessArray[i][j]==2) {
   
   
					g.setColor(Color.white);
					g.fillOval(i*c+z-10, j*c+z-10, 20, 20);
				}
			}
		}
	}

}

判定输赢

当棋子都下好了之后,我们就需要在每下一颗棋子后进行输赢的判定。五子棋中,有五颗棋子连在一起即为获胜。因此我们就需要对横竖斜四个大方向上的棋子,进行查找判定。

原理则是因为黑棋的数组值都为1,白棋都为2,那么就在每一次所下棋子的地方为起点,向各个方向查找,每有一个相连的棋子是同色,设置一个变量count,count就加1,当count变为5时候,就证明有五个同色棋子相连,游戏就结束,如果所下棋子是黑棋,黑棋就获胜,如果所下棋子是白棋,白棋就获胜。

下列代码是判定寻找各个方向的同色棋子的方法:

public int Win1(int xx,int yy) {
   
   //判定横向棋子是否有相连的
		int count1=0;
		for(int i=xx-1;i>=0;i--) {
   
   
			if(ChessArray[i][yy]==ChessArray[xx][yy]) {
   
   
				count1++;
			}else break;
		}
		for(int j=xx;j<ChessArray.length;j++) {
   
   
			if(ChessArray[j][yy]==ChessArray[xx][yy]) {
   
   
				count1++;
			}else break;
		}
		return count1;
	}
	
	public int Win2(int xx,int yy) {
   
   //判定纵向棋子是否有相连的
			int count2=0;
		for(int n=yy-1;n>=0;n--) {
   
   
			if(ChessArray[xx][n]==ChessArray[xx][yy]) {
   
   
				count2++;
			}else break;
		}
		for(int m=yy;m<ChessArray.length;m++) {
   
   
			if(ChessArray[xx][m]==ChessArray[xx][yy]) {
   
   
				count2++;
			}else break;
		}
		return count2;
	}
	
	public int Win3(int xx,int yy) {
   
   //判定左下右上棋子是否有相连的
		int count3=0;
		for(int o=xx-1,p=yy+1;o>=0&&p<ChessArray.length;o--,p++) {
   
   
			if(ChessArray[o][p]==ChessArray[xx][yy]) {
   
   
				count3++;
			}else break;
		}
		for(int o=xx,p=yy;o<ChessArray.length&&p>=0;o++,p--) {
   
   
			if(ChessArray[o][p]==ChessArray[xx][yy]) {
   
   
				count3++;
			}else break;
		}
		return count3;
	}
	
	public int Win4(int xx,int yy) {
   
   //判定左上右下棋子是否有相连的
评论 41
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值