Java实现五子棋效果

Java实现五子棋效果

要利用Java制作一个简单的五子棋程序,大致分为如下几步
1.绘制棋盘。
2.添加一些如按钮类的组件。
3.让棋子落在网格线的交叉点上。
4.棋子重绘。
5.实现悔棋功能。
6.判断输赢机制。
7.实现人机对战。
接下来我们就按照这个思路来一步步实现五子棋。
一. 绘制棋盘
由于每一次运行程序系统会自动调用paint函数,我们只需要直接把棋盘的绘制代码写在重写后的paint函数内即可。如下

 public void paint(Graphics g){
       	  super.paint(g);
      //绘制棋盘
       	  for(int i=0;i<LINE;i++){
       		  g.drawLine(X1,Y1+i*SIZE,X1+(LINE-1)*SIZE,Y1+i*SIZE);
       		  g.drawLine(X1+i*SIZE,Y1,X1+i*SIZE,Y1+(LINE-1)*SIZE);
       	  }

这其中的SIZE等都是常量,并且将它们设计成接口方便改变棋盘的尺寸等
如下:

public interface wuzi {
    public static final int X1=160; //棋子左上角顶点的横纵坐标
    public static final int Y1=120;
    public static final int SIZE=40;   //网格边长
    public static final int LINE=18;   //所画线条数量
	public static final int CHESS=40;  //棋子直径
}

二.添加组件
下面这段代码是展示五子棋界面的函数:

public void showUI(){
    	   
    	    ChessUI jf=new ChessUI();
    	    jf.setSize(1000,1000);
    	    jf.setTitle("五子棋");   //设置标题
    	    jf.setLocationRelativeTo(null);    //设置居中
    		jf.setDefaultCloseOperation(3);   //设置程序可关闭
    		FlowLayout flow=new FlowLayout();  //采用流式布局
    		jf.setLayout(flow);
    		 ChessMouse mouse =new ChessMouse();
    		 //添加按钮组件
    		String[] name={"黑棋先手","白棋先手","重新开始","悔棋","人机对战","游戏说明"};
    		for(int i=0;i<6;i++){
    			JButton jbu=new JButton(name[i]);
    			jf.add(jbu);
    			jbu.addActionListener(mouse); //给按钮添加动作监听器
    		}
    		
    		jf.setVisible(true); //设置可见
    		Graphics g=jf.getGraphics();   //获取画笔
    	    jf.addMouseListener(mouse);   //添加鼠标监听器
    	   
            mouse.g=g;
            jf.mouse= mouse;      //传数组数据
            mouse.ui=jf;           //传棋盘对象
            
       }

三.让棋子落在网格的交叉点上
在这方面我们的思路是一个格子分为四等份,当然由于需要横竖两次划分,所以需要写两次判断,如下:

if((x1-X1)%SIZE>SIZE/2){
			xx=(x1-X1)/SIZE+1;
		}else{
			xx=(x1-X1)/SIZE;
		}
		if((y1-Y1)%SIZE>SIZE/2){
			yy=(y1-Y1)/SIZE+1;
		}else{
			yy=(y1-Y1)/SIZE;
		}

其中 xx,yy为需要落子的位置坐标。
四.实现棋子重绘
我们的思路是定义一个二维数组chessArray来记录棋局情况,然后由于网格点的坐标实际上是以界面框的左上顶点为坐标原点,以一个像素点为单位的,这样网格点的坐标就会比较大,在上面也可以看到我们将坐标除以一个系数(网格边长),如此一来棋盘的左上顶点的坐标为(0,0),其左边相邻的网格点坐标即为(1,0),以此类推。如此便可以将二维数组的下标作为网格点坐标,然后二位数组所存数据代表棋子,这里我们定义0表示该处没有棋子,1表示该处为黑棋,2表示该处为白棋。每一次下子都更新二维数组中的数据,然后再调用重写之后的paint方法实现重绘。代码如下:

 public void paint(Graphics g){
       	  super.paint(g);
       	  //添加背景图片
       ImageIcon icon =new ImageIcon("C:\\Users\\Pictures\\Camera Roll\\3.png");    
   	g.drawImage( icon.getImage(),160,120,null);
      //重新绘制棋盘
       	  for(int i=0;i<LINE;i++){
       		  g.drawLine(X1,Y1+i*SIZE,X1+(LINE-1)*SIZE,Y1+i*SIZE);
       		  g.drawLine(X1+i*SIZE,Y1,X1+i*SIZE,Y1+(LINE-1)*SIZE);
       	  }
       	  int[][] chessArray=mouse.chessArray;//传入chessArray数组
       	  //重绘棋子
       	  for(int i=0;i<chessArray.length;i++){
       		  for(int j=0;j<chessArray[0].length;j++){
       			  if(chessArray[i][j]==1){
       				  g.setColor(Color.BLACK);
       				  g.fillOval(i*SIZE+X1-CHESS/2,j*SIZE+Y1-CHESS/2,CHESS,CHESS);
       			  }else if(chessArray[i][j]==2){
       				  g.setColor(Color.WHITE);
       				  g.fillOval(i*SIZE+X1-CHESS/2,j*SIZE+Y1-CHESS/2,CHESS,CHESS);
       			  }
       		  }
       	  }
       	  
        	
       	      }

其中有一段操作是给棋盘加上背景图片,当然为了让界面更加美观,也可以选择不用fillOval函数而同样采用drawImage函数画出棋子的图片。
五.实现悔棋功能。
有了上面的基础实现悔棋就很方便了。为了记录下棋的顺序,我们需要定义一个一维数组,然后该一维数组中每一个单元必须记录所落棋子的横坐标,纵坐标这两个数据。由此我们可以考虑将该一维数组定义为一个chess类:

public class chess {
     public int x,y;
     public chess(int x,int y){
    	 this.x=x;
    	 this.y=y;
     }
}

该类中包含横纵坐标,然后定义一维数组chessarray ,其大小为LINE*LINE,然后同样每一次落子都要更新其中的数据。悔棋函数如下:

public void huiqi(){
		if(start==0){
			if(index>0){
				chessArray[chessarray[index-1].x][chessarray[index-1].y]=0;
				index--;  //chessarray数组下标
				num--; //棋子数量
				ui.repaint(); //重绘棋盘组件让最后下的棋子消失
			}
		}else if(start==1){
			if(index>0){
				chessArray[chessarray[index-1].x][chessarray[index-1].y]=0;
				chessArray[chessarray[index-2].x][chessarray[index-2].y]=0;
				index-=2;
				ui.repaint();
			}
		}

	}

值得一提的是在这里写了两个判断,我们定义整型量start来区分当前进行的是人人对战还是人机对战,0表示人人对战,1表示人机对战。然后若为人机对战,则应该一次悔两颗棋(人下的棋和机器人下的棋)。
6.判断输赢机制
这个可以称之为五子棋的核心,我们的思路是每一次下棋都应该进行一次输赢的判断,定义整型变量count记录某一方向上连续与该棋子颜色相同的棋子数量,由于对于单个棋子而言我们需要考虑横竖两种,斜两种共四个方向的棋局情况,这四个方向又以棋子为间断点分为八段。所以应该有四组一共八个循环。如果其中一组遍历后count值为5,则返回true,否则将count初始化为1。

//判断输赢函数
public boolean win(int x,int y){
		int count=1;
		//横
		for(int i=x+1;i<chessArray.length;i++){
			if(chessArray[i][y]==chessArray[x][y]){
				count++;
			}else break;
		}
		for(int i=x-1;i>=0;i--){
			if(chessArray[x][y]==chessArray[i][y]){
				count++;
			}else break;
		}
		if(count>=5){
			return true;
		}else{
			count=1;
		}

		//竖
		for(int i=y+1;i<chessArray.length;i++){
			if(chessArray[x][y]==chessArray[x][i]){
				count++;
			}else break;

		}
		for(int i=y-1;i>=0;i--){
			if(chessArray[x][y]==chessArray[x][i]){
				count++;
			}else break;
		}
		if(count>=5){
			return true;
		}else{
			count=1;
		}
		//斜
		for(int i=x-1,j=y-1;j>=0&&i>=0;i--,j--){
			if(chessArray[x][y]==chessArray[i][j]){
				count++;
			}else break;
		}
		for(int i=x+1,j=y+1;i<chessArray.length&&j<chessArray.length;i++,j++){
			if(chessArray[x][y]==chessArray[i][j]){
				count++;
			}else break;
		}
		if(count>=5){
			return true;
		}else {
			count=1;
		}
		for(int i=x-1,j=y+1;i>=0&&j<chessArray.length;i--,j++){	 
			if(chessArray[x][y]==chessArray[i][j]){
				count++;
			}else break;
		}
		for(int i=x+1,j=y-1;i<chessArray.length&&j>=0;i++,j--){
			if(chessArray[x][y]==chessArray[i][j]){
				count++;
			}else break;
		}
		if(count>=5){
			return true;
		}else{
			count=1;
		}

		return false;
	}

七.人机对战
为了实现人机对战,人每一次下棋我们都得分析整个棋局的情况,确定AI最应该下棋的位置。在这里介绍一下权值算法,就是首先制定自己的权值表,权值由棋局情况而定,当然这里我们只需要考虑未下过棋子的位置,比如对于某个空位,它的左边有三个连续黑棋,将这种情况定义为2000,上面有两个白棋,将这种情况定义为200,诸如此类,然后将该空位八个方向棋局对应的权值相加,作为此处的权值,通过循环遍历棋盘上每一个空位,将权值赋值给二维数组,最后找出二维数组中权值最大的那个单元的下标,就是AI应该下子的位置。
在这里再介绍一个工具:HaspMap <K,V>, 其中的K,V其实是一对键值对,可以通过K值来获得对应的V值,它最大的特点就是没有顺序。其中的<>表示泛型,泛型即引用类型,包括类,数组和接口。在这里使用时为方便定义K为String类型,V为Integer类型。如在某次搜索竖直向上方向棋局情况时,遇到一个黑子一个白子,则将白子对应的数字2接到黑子对应的1后,每个方向搜索完成后通过字符串获取键值,即权值。
代码如下:

public void AI(){
		String code="";
		int color=0;
		HashMap<String,Integer> hm=new HashMap<>();
		//制定权值表
		hm.put("1", 20);
		hm.put("11", 200);
		hm.put("111", 2000);
		hm.put("1111", 3000);
		hm.put("12", 20);
		hm.put("112", 200);
		hm.put("1112", 2000);
		hm.put("11112", 3000);
		hm.put("2", 20);
		hm.put("22", 200);
		hm.put("222", 2000);
		hm.put("2222", 4000);
		hm.put("21", 20);
		hm.put("221", 200);
		hm.put("2221", 1500);
		hm.put("22221", 4000);
		for(int i=0;i<chessValue.length;i++){
			for(int j=0;j<chessValue[0].length;j++){
				if(chessArray[i][j]==0){
					//遍历横向棋局情况
					for(int k=i+1;k<chessArray.length;k++){
						if(chessArray[k][j]==0){
							break;
						}else{
							if(color==0){
								color=chessArray[k][j];
								code+=chessArray[k][j];
							}else if(chessArray[k][j]==color){
								code+=chessArray[k][j];

							}else{
								code+=chessArray[k][j];
								break;
							}
						}
					}
					Integer value=hm.get(code);
					if(value!=null){
						chessValue[i][j]+=value;
					}
					code="";
					color=0;
					for(int k=i-1;k>=0;k--){
						if(chessArray[k][j]==0){
							break;
						}else{
							if(color==0){
								color=chessArray[k][j];
								code+=chessArray[k][j];
							}else if(chessArray[k][j]==color){
								code+=chessArray[k][j];

							}else{
								code+=chessArray[k][j];
								break;
							}
						}
					}
					Integer value1=hm.get(code);
					if(value1!=null){
						chessValue[i][j]+=value1;
					}
					code="";
					color=0;
					//遍历纵向棋局情况
					for(int k=j-1;k>=0;k--){
						if(chessArray[i][k]==0){
							break;
						}else{
							if(color==0){
								color=chessArray[i][k];
								code+=chessArray[i][k];
							}else if(chessArray[i][k]==color){
								code+=chessArray[i][k];

							}else{
								code+=chessArray[i][k];
								break;
							}
						}
					}
					Integer value2=hm.get(code);
					if(value2!=null){
						chessValue[i][j]+=value2;
					}
					code="";
					color=0;
					for(int k=j+1;k<chessArray.length;k++){
						if(chessArray[i][k]==0){
							break;
						}else{
							if(color==0){
								color=chessArray[i][k];
								code+=chessArray[i][k];
							}else if(chessArray[i][k]==color){
								code+=chessArray[i][k];

							}else{
								code+=chessArray[i][k];
								break;
							}
						}
					}
					Integer value3=hm.get(code);
					if(value3!=null){
						chessValue[i][j]+=value3;
					}
					code="";
					color=0;
					for(int k=i+1,z=j+1;k<chessArray.length&&z<chessArray.length;k++,z++){
						if(chessArray[k][z]==0){
							break;
						}else{
							if(color==0){
								color=chessArray[k][z];
								code+=chessArray[k][z];
							}else if(chessArray[k][z]==color){
								code+=chessArray[k][z];
							}else{
								code+=chessArray[k][z];
								break;
							}
						}
					}
					Integer value4=hm.get(code);
					if(value4!=null){
						chessValue[i][j]+=value4;
					}
					code="";
					color=0;
					for(int k=i-1,z=j-1;k>=0&&z>=0;k--,z--){
						if(chessArray[k][z]==0){
							break;
						}else{
							if(color==0){
								color=chessArray[k][z];
								code+=chessArray[k][z];
							}else if(chessArray[k][z]==color){
								code+=chessArray[k][z];
							}else {
								code+=chessArray[k][z];
								break;
							}
						}
					}
					Integer value5=hm.get(code);
					if(value5!=null){
						chessValue[i][j]+=value5;
					}
					code="";
					color=0;
					//遍历斜方向棋局情况
					for(int k=i-1,z=j+1;k>=0&&z<chessArray.length;k--,z++){
						if(chessArray[k][z]==0){
							break;
						}else{
							if(color==0){
								color=chessArray[k][z];
								code+=chessArray[k][z];
							}else if(chessArray[k][z]==color){
								code+=chessArray[k][z];
							}else {
								code+=chessArray[k][z];
								break;
							}
						}
					}
					Integer value6=hm.get(code);
					if(value6!=null){
						chessValue[i][j]+=value6;
					}
					code="";
					color=0;
					for(int k=i+1,z=j-1;k<chessArray.length&&z>=0;k++,z--){
						if(chessArray[k][z]==0){
							break;
						}else{
							if(color==0){
								color=chessArray[k][z];
								code+=chessArray[k][z];
							}else if(chessArray[k][z]==color){
								code+=chessArray[k][z];
							}else {
								code+=chessArray[k][z];
								break;
							}
						}
					}
					Integer value7=hm.get(code);
					if(value7!=null){
						chessValue[i][j]+=value7;
					}
					code="";
					color=0;
					
				}
			}
		}
		//搜索chessValue 数组中最大值,记录该值的位置,用来电脑下棋;
		int max=chessValue[0][0];
		for(int i=0;i<chessValue.length;i++){
			for(int j=0;j<chessValue[0].length;j++){
				if(chessValue[i][j]>max){
					max=chessValue[i][j];
					x2=i;
					y2=j;
				}
			}
		}
		if(chessArray[x2][y2]==0){
			if(num%2==0){
		g.setColor(Color.WHITE);//定义AI执白子后下
		chessArray[x2][y2]=2;
		g.fillOval(x2*SIZE+X1-CHESS/2,y2*SIZE+Y1-CHESS/2,CHESS,CHESS);
		num++;
		chessarray[index++]=new chess(x2,y2);
		if(win(x2,y2)==true){ //调用win函数判断输赢
			JOptionPane.showMessageDialog(ui, "白棋胜利");
			reset();  //重新开始
		}
			}
		}
		for(int i=0;i<chessValue.length;i++){
			for(int j=0;j<chessValue[0].length;j++){
				chessValue[i][j]=0;
			}
		}


	}

虽然这个函数比较长,但是重复的代码部分颇多。至于如何改进,一方面可以丰富完善权值表,调整权值大小;另一方面可以增加对一些特殊情况的判断,比如某时某空位左右两边各有两个黑子,那么就AI就最应该下这个位置了。
以上就是笔者和大家分享的如何用Java实现五子棋了,如有不当,欢迎大家批评指正。

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值