大佬勿进,小白请进!【智能五子棋】997行代码带你带你畅游棋码

智能五子棋

好久没有写技术博客啦,先附图一张以表喜悦😎
在这里插入图片描述

目录

  • 简单五子棋的实现
  • AI五子棋
  • 技术总结

前言:
回到了长沙,又回归了阴雨天的怀抱。就是想抱怨一下长沙为啥要一直下雨呢!!!就没有几天是晴天。本来还想晚上去南校操场跑跑步,然而设想很美好,现实很苦楚。看着身边强者如云,学习编程语言稍微有一丢丢起色的我打算开始备战面试,而AI五子棋正好作为我半年未打代码的练手项目。希望我的博客可以帮助到正在学习Java的编程小白,希望各位小哥哥,小姐姐可以给我写的博客点个赞( •̀ ω •́ )✧, 你们的赞就是我写博客的无穷动力 !!!

1.简单五子棋的实现(自下)

在编写代码之前我们要理解实现五子棋的简单步骤:

  • 画出棋盘
  • 点击棋盘会在棋盘下一个子
  • 再次点击(白字和黑子交替下)
  • 判断是否连接成五子,得出输赢

技术点:

  • 下子需要鼠标监听器,获取鼠标点击位置并定位到网格上
  • 判断输赢需要检测当前该子上下左右斜边是否连成五子(核心算法)
  • 为了避免下子闪屏和界面刷新,需要提供双缓冲和重绘机制

废话不多的说,先上代码:

//代码基本都给了注释ヾ(•ω•`)o
//初始化下棋界面
	public void paint(Graphics g){
		//搭载双缓冲技术
		BufferedImage bufferedImage=new BufferedImage(jf.getWidth(),jf.getHeight(),BufferedImage.TYPE_INT_BGR);
		Graphics g1=bufferedImage.createGraphics();//创建双缓冲画笔
		g1.setColor(Color.BLACK);
		g1.setFont(new Font("黑体", Font.BOLD, 50));
		//画背景
		g1.drawImage(imageBackGround, 0, 0, 1050, 1020, this);
		//上边功能区背景
		g1.setColor(Color.darkGray);
		g1.fill3DRect( 0, 0, 1050, 100, true);
		//设置线的粗细(全局)
		Graphics2D g2=(Graphics2D)g1;
		g2.setStroke(new BasicStroke(2f));
		//画棋盘,具体位置根据自己的背景来
		g1.setColor(Color.BLACK);
		for(int i=1;i<=ROW;i++){
			g1.drawLine( 180, 110+i*50, 880, 110+i*50);//画横线
			g1.drawLine( 130+i*50, 160, 130+i*50, 860);//画竖线
		}
		//上方状态信息区
		g1.setColor(Color.WHITE);
		g1.setFont(new Font("粗体", Font.BOLD,40));
		g1.drawString("游戏信息:",110,80);
		g1.setColor(Color.BLUE);
		g1.drawString(message,300,80);
		
		//右边功能区
		g1.setColor(Color.BLUE);
		g1.drawRect(950,130,85,30);
		g1.setColor(Color.BLACK);
		g1.setFont(new Font("粗体", Font.BOLD,18));
		g1.drawString(isRestart, 955, 150);
		
		g1.setColor(Color.BLUE);
		g1.drawRect(950,170,85,30);
		g1.setColor(Color.BLACK);
		g1.setFont(new Font("粗体", Font.BOLD,18));
		g1.drawString("游戏说明", 955, 190);
		
		g1.setColor(Color.BLUE);
		g1.drawRect(950,210,85,30);
		g1.setColor(Color.BLACK);
		g1.setFont(new Font("粗体", Font.BOLD,18));
		g1.drawString("游戏设置", 955, 230);
		
		g1.setColor(Color.BLUE);
		g1.drawRect(950,250,85,30);
		g1.setColor(Color.BLACK);
		g1.setFont(new Font("粗体", Font.BOLD,18));
		g1.drawString("悔棋", 955, 270);
		
		g1.setColor(Color.BLUE);
		g1.drawRect(950,290,85,30);
		g1.setColor(Color.BLACK);
		g1.setFont(new Font("粗体", Font.BOLD,18));
		g1.drawString("认输", 955, 310);
		
		g1.setColor(Color.BLUE);
		g1.drawRect(950,330,85,30);
		g1.setColor(Color.BLACK);
		g1.setFont(new Font("粗体", Font.BOLD,18));
		g1.drawString("退出游戏", 955, 350);

先看效果:
在这里插入图片描述
当然界面不是最主要的,但是界面做的好看也是我们程序员对美的追求,你们可以自己换上一些好看的背景图片,棋子也可以用图标代替,实现多样棋子的选择。
接下来就是关键的部分,下子和判断输赢,核心代码:

		//画棋子
		for(int i=0;i<ROW;i++){
			for(int j=0;j<Column;j++){
				//[1画黑子,2画白子]
				if(allChess[i][j]==1){
					int x=i*50+180-chessSize/2;
					int y=j*50+160-chessSize/2;
					g1.setColor(Color.BLACK);
					g1.fillOval( x, y, chessSize, chessSize);
				}
				//[1画黑子,2画白子]
				if(allChess[i][j]==2){
					int x=i*50+180-chessSize/2;
					int y=j*50+160-chessSize/2;
					g1.setColor(Color.WHITE);
					g1.fillOval( x, y, chessSize, chessSize);
				}
			}
		}
//判断输赢
	public boolean isWin(){
    	boolean  flag=false;
    	//连成棋子初始个数
    	int count=1;
    	int color=allChess[X][Y];
    	//米字形判断
    	count=this.checkChess(1,0,color);
    	if(count>=5){
    		flag=true;
    	}else{
    	count=this.checkChess(0,1,color);
    	if(count>=5){
    		flag=true;
    	}else{
    	count=this.checkChess(1,-1,color);
    	if(count>=5){
    		flag=true;
    	}else{
    	count=this.checkChess(1,1,color);
    	if(count>=5){
    		flag=true;
    		  }
    		}
    	  }
    	}
    	//返回是否连成五子
    	return flag;
	}
	//检测棋子是否连成五子
	public int checkChess(int xChange,int yChange,int color){
	  	int count=1;
    	int temX=xChange;
    	int temy=yChange;
    	//米字型遍历
    	while(X+xChange>=0 && X+xChange<ROW && Y+yChange>=0 && Y+yChange<Column && color==allChess[X+xChange][Y+yChange]){
    		count++;
    		if(xChange!=0) {
    			if(xChange>0){
    				xChange++;
    			}else{
    				xChange--;
    			}
    		}
    		if(yChange!=0){
    			if(yChange!=0){
    				if(yChange>0){
    					yChange++;
    				}else{
    					yChange--;
    				}
    			}
    		}
    	}
    	//反方向,恢复初始值
    	xChange=temX;
    	yChange=temy;
    	//米字型遍历
    	while(X-xChange>=0 && X-xChange<ROW && Y-yChange>=0 && Y-yChange<Column && color==allChess[X-xChange][Y-yChange]){
    		count++;
    		if(xChange!=0){
    			if(xChange>0){
    				xChange++;
    			}else{
    				xChange--;
    			}
    		}
    		if(yChange!=0){
    			if(yChange>0){
    				yChange++;
    			}else{
    				yChange--;
    			}
    		}
    	}
    	return count;
	}

可能大家看了上面的代码觉得是不是有很多地方有缺失,完整代码我会放在后面!!!只希望各位代码爱好者给个收藏。上面代码是整个下棋的核心,接下来就是一些下棋界面的配置代码:如播放音乐,悔棋,认输等等,下面就是完整代码希望大家能多多关注我(一个不懂编程还假装自己很强的菜鸡)。

全部代码

package ChineseChess;

import java.applet.Applet;
import java.applet.AudioClip;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.net.MalformedURLException;

import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
/**
 * 智能五子棋
 * @author 梦想少年
 * 1.界面
 * 2.基本下棋
 * 3.悔棋、认输、重来
 * 4.音乐、更换下棋背景和棋子
 * 5.人机对战
 */
@SuppressWarnings("serial")
public class MainUI extends JFrame implements MouseListener{
	//棋盘的相关属性设定
	Image imageBackGround=new ImageIcon("D:\\常用\\图片\\Camera Roll\\image\\back.png").getImage();//图片
	public static final int ROW=15;//行数
	public static final int Column=15;//列数
	public static final int chessSize=40;//棋子大小
	//下棋及棋子相关属性
	String isRestart="开始游戏";
	boolean isStartGame=false;//是否开始游戏
	String message="黑方先行";//游戏提示信息
	boolean isBlack=true;//当前是否下黑子
	//保存已下棋子的位置数组,悔棋的时候使用
	int[] chessX=new int[225];
	int[] chessY=new int[225];
	//已下棋子数
	int countX;
	int countY;
	//记录棋子位置
	int X=1;
	int Y=1;
	//保存棋盘上目前棋子的状态数组[0表示无棋子,1表示黑子,2表示白子]
	int[][] allChess=new int[15][15];
	//音乐类
	AudioClip audioClip;
	//主方法
	public static void main(String[] args){
		new MainUI().initUI();
	}
	//初始化窗体
	JFrame jf=this;
	public void initUI(){
		jf.setTitle("AI五子棋|2021.3.10【梦想少年】");
		jf.setSize(1050,1020);
		jf.setResizable(false);
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		jf.setLocationRelativeTo(null);
		jf.setVisible(true);

		//重绘机制
		this.repaint();
		//添加鼠标监听器
		jf.addMouseListener(this);
	}
	//初始化下棋界面
	public void paint(Graphics g){
		//搭载双缓冲技术
		BufferedImage bufferedImage=new BufferedImage(jf.getWidth(),jf.getHeight(),BufferedImage.TYPE_INT_BGR);
		Graphics g1=bufferedImage.createGraphics();//创建双缓冲画笔
		g1.setColor(Color.BLACK);
		g1.setFont(new Font("黑体", Font.BOLD, 50));
		//画背景
		g1.drawImage(imageBackGround, 0, 0, 1050, 1020, this);
		//上边功能区背景
		g1.setColor(Color.darkGray);
		g1.fill3DRect( 0, 0, 1050, 100, true);
		//设置线的粗细(全局)
		Graphics2D g2=(Graphics2D)g1;
		g2.setStroke(new BasicStroke(2f));
		//画棋盘
		g1.setColor(Color.BLACK);
		for(int i=1;i<=ROW;i++){
			g1.drawLine( 180, 110+i*50, 880, 110+i*50);//画横线
			g1.drawLine( 130+i*50, 160, 130+i*50, 860);//画竖线
		}
		//上方状态信息区
		g1.setColor(Color.WHITE);
		g1.setFont(new Font("粗体", Font.BOLD,40));
		g1.drawString("游戏信息:",110,80);
		g1.setColor(Color.BLUE);
		g1.drawString(message,300,80);
		
		//右边功能区
		g1.setColor(Color.BLUE);
		g1.drawRect(950,130,85,30);
		g1.setColor(Color.BLACK);
		g1.setFont(new Font("粗体", Font.BOLD,18));
		g1.drawString(isRestart, 955, 150);
		
		g1.setColor(Color.BLUE);
		g1.drawRect(950,170,85,30);
		g1.setColor(Color.BLACK);
		g1.setFont(new Font("粗体", Font.BOLD,18));
		g1.drawString("游戏说明", 955, 190);
		
		g1.setColor(Color.BLUE);
		g1.drawRect(950,210,85,30);
		g1.setColor(Color.BLACK);
		g1.setFont(new Font("粗体", Font.BOLD,18));
		g1.drawString("游戏设置", 955, 230);
		
		g1.setColor(Color.BLUE);
		g1.drawRect(950,250,85,30);
		g1.setColor(Color.BLACK);
		g1.setFont(new Font("粗体", Font.BOLD,18));
		g1.drawString("悔棋", 955, 270);
		
		g1.setColor(Color.BLUE);
		g1.drawRect(950,290,85,30);
		g1.setColor(Color.BLACK);
		g1.setFont(new Font("粗体", Font.BOLD,18));
		g1.drawString("认输", 955, 310);
		
		g1.setColor(Color.BLUE);
		g1.drawRect(950,330,85,30);
		g1.setColor(Color.BLACK);
		g1.setFont(new Font("粗体", Font.BOLD,18));
		g1.drawString("退出游戏", 955, 350);
		//画棋子
		for(int i=0;i<ROW;i++){
			for(int j=0;j<Column;j++){
				//[1画黑子,2画白子]
				if(allChess[i][j]==1){
					int x=i*50+180-chessSize/2;
					int y=j*50+160-chessSize/2;
					g1.setColor(Color.BLACK);
					g1.fillOval( x, y, chessSize, chessSize);
				}
				//[1画黑子,2画白子]
				if(allChess[i][j]==2){
					int x=i*50+180-chessSize/2;
					int y=j*50+160-chessSize/2;
					g1.setColor(Color.WHITE);
					g1.fillOval( x, y, chessSize, chessSize);
				}
			}
		}
		//画出缓冲图象
		g.drawImage(bufferedImage, 0, 0, this);
	}
	//鼠标按下下棋及其他监听
	@Override
	public void mousePressed(MouseEvent e){
		//获得鼠标点击坐标
		int x=e.getX();
	    int y=e.getY();
	    //定位鼠标点击位置到最近的格点并判断是否越界
	    if(x>=180 && x<=880 && y>=160 && y<=860){
	    	 if((x-180)%50>25){
	    		 x=(x-180)/50+1;
	    	 }else{
	    		 x=(x-180)/50;
	    	 }
	    	 if((y-160)%50>25){
	    		 y=(y-160)/50+1;
	    	 }else{
	    		 y=(y-160)/50;
	    	 }
	    	 //赋值给XY保存当前下子位置
	    	 X=x;
	    	 Y=y;
	    	 //下子
	    	 if(isStartGame){
		    	 //落子
		    	 if(allChess[x][y]==0){
		    		 chessX[countX++]=x;
		    		 chessY[countY++]=y;
		    		 //判断落白字还是黑子
		    		 if(isBlack){
		    			 allChess[x][y]=1;
		    			 isBlack=false;
		    			 message="白方下子";
		    			 this.openMusic();
		    		 }else{
		    			 allChess[x][y]=2;
		    			 isBlack=true;
		    			 message="黑方下子";
		    			 this.openMusic();
		    		 }
		    	 }
		    	 //保证在判断输赢前实现刷新
		    	 this.repaint();
		 		//下完一个子立即判断输赢
		 		if(isWin()){
		 			if(isBlack){
		 				JOptionPane.showMessageDialog(this, "游戏结束,白方胜利");
		 			}else{
		 				JOptionPane.showMessageDialog(this, "游戏结束,黑方胜利");
		 			}
		 			isStartGame=false;
		 			isRestart="重新开始";
		 		}
	    	 }else{
	    		 JOptionPane.showMessageDialog(this,"游戏已结束!,请重新开始游戏!");
	    	 }
	    }else if(x>=950 && x<=1035 && y>=130 && y<=160){
	    	if(isRestart.equals("重新开始")){
	    		this.reStart();
	    	}
	    	isStartGame=true;
	    	isRestart="游戏中";
	    }else if(x>=950 && x<=1035 && y>=170 && y<=200){
	    	JOptionPane.showMessageDialog(this,"黑方先行,连成五子者胜出!");
	    }else if(x>=950 && x<=1035 && y>=210 && y<=240){
	    	this.openBackMusic();
	    }else if(x>=950 && x<=1035 && y>=250 && y<=280){
	    	this.withDrawChess();
	    }else if(x>=950 && x<=1035 && y>=290 && y<=320){
			int result=JOptionPane.showConfirmDialog(getParent(),(isBlack==true?"白方认输,黑方是否同意?":"黑方认输,白方是否同意?"));
	    	if(result==0){
	    		isStartGame=false;
	    		isRestart="重新开始";
	    	}
	    }else if(x>=950 && x<=1035 && y>=330 && y<=360){
	    	int result=JOptionPane.showConfirmDialog(getParent(), "是否退出游戏?");
	    	if(result==0){
	    		System.exit(0);
	    	}
	    }else{
	    	JOptionPane.showMessageDialog(this,"警告!鼠标点击位置超出边界!");
	    }
	    
	    //重绘界面,每次点击都刷新一次界面
		this.repaint();		
	}
	//判断输赢
	public boolean isWin(){
    	boolean  flag=false;
    	//连成棋子初始个数
    	int count=1;
    	int color=allChess[X][Y];
    	//米字形判断
    	count=this.checkChess(1,0,color);
    	if(count>=5){
    		flag=true;
    	}else{
    	count=this.checkChess(0,1,color);
    	if(count>=5){
    		flag=true;
    	}else{
    	count=this.checkChess(1,-1,color);
    	if(count>=5){
    		flag=true;
    	}else{
    	count=this.checkChess(1,1,color);
    	if(count>=5){
    		flag=true;
    		  }
    		}
    	  }
    	}
    	//返回是否连成五子
    	return flag;
	}
	//检测棋子是否连成五子
	public int checkChess(int xChange,int yChange,int color){
	  	int count=1;
    	int temX=xChange;
    	int temy=yChange;
    	//米字型遍历
    	while(X+xChange>=0 && X+xChange<ROW && Y+yChange>=0 && Y+yChange<Column && color==allChess[X+xChange][Y+yChange]){
    		count++;
    		if(xChange!=0) {
    			if(xChange>0){
    				xChange++;
    			}else{
    				xChange--;
    			}
    		}
    		if(yChange!=0){
    			if(yChange!=0){
    				if(yChange>0){
    					yChange++;
    				}else{
    					yChange--;
    				}
    			}
    		}
    	}
    	//反方向,恢复初始值
    	xChange=temX;
    	yChange=temy;
    	//米字型遍历
    	while(X-xChange>=0 && X-xChange<ROW && Y-yChange>=0 && Y-yChange<Column && color==allChess[X-xChange][Y-yChange]){
    		count++;
    		if(xChange!=0){
    			if(xChange>0){
    				xChange++;
    			}else{
    				xChange--;
    			}
    		}
    		if(yChange!=0){
    			if(yChange>0){
    				yChange++;
    			}else{
    				yChange--;
    			}
    		}
    	}
    	return count;
	}
	//悔棋
	public void withDrawChess(){
		int result=JOptionPane.showConfirmDialog(getParent(),(isBlack==true?"白方悔棋,黑方是否同意?":"黑方悔棋,白方是否同意?"));
		if(result==0){
			allChess[chessX[--countX]][chessY[--countY]]=0;
		}
		if(isBlack){
			isBlack=false;
			message="白方下子";
		}else{
			isBlack=true;
			message="黑方下子";
		}
	}
	//打开背景音乐
	public void openBackMusic(){
		int result=JOptionPane.showConfirmDialog(getParent(),"是否打开背景音乐?");
		if(result==0){
			try {
				audioClip=Applet.newAudioClip(new File("C:\\Users\\梦想少年\\Music\\AudioConvert\\昼夜 - 晴天 (钢琴版).wav").toURI().toURL());
				audioClip.loop();
			} catch (MalformedURLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	//打开下子音乐
	public void openMusic(){
		try {
			audioClip=Applet.newAudioClip(new File("C:\\Users\\梦想少年\\Music\\AudioConvert\\chess.wav").toURI().toURL());
			audioClip.play();
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	//重新开始游戏
	public void reStart(){
		//恢复初始状态
		for(int i=0;i<ROW;i++){
			for(int j=0;j<Column;j++){
				allChess[i][j]=0;
			}
		}
		for(int i=0;i<ROW;i++){
			chessX[i]=0;
			chessY[i]=0;
		}
		countX=0;
		countY=0;
		isBlack=true;
		isRestart="开始游戏";
		isStartGame=false;
		message="黑方先行";
	}
	//α-β剪枝算法(MinMax)极大极小值
	
	//剩下的监听方法
	@Override
	public void mouseClicked(MouseEvent e) {
		// TODO Auto-generated method stub
		
	}
	@Override
	public void mouseReleased(MouseEvent e) {
		// TODO Auto-generated method stub
		
	}
	@Override
	public void mouseEntered(MouseEvent e) {
		// TODO Auto-generated method stub
		
	}
	@Override
	public void mouseExited(MouseEvent e) {
		// TODO Auto-generated method stub
		
	}
}

现在可以来看一看效果:
在这里插入图片描述
感觉还不错,代码写到这个是不是感觉自己和自己下棋特无聊,于是你会想到AI五子棋,下面我们一起来看看AI五子棋是如何实现的:—————————————————————————————

2.AI五子棋(人机对战)

想必大家是不是想尝试一下自己和电脑下棋,而且还是自己写的代码,简直very happy!下面我们思考一下几个问题:

  • 要和电脑下棋,首先电脑拥有智慧
  • 智慧从何而来呢?快来了解极大极小博弈树
  • 我们先让电脑评估当前局势下每个空格点下棋后的优势
  • 评估值=下子后对自己有利+下子后对对方不利
  • 经过双重考虑,嘿嘿,干掉你亦如反掌

下面我们先介绍估值算法和:
所谓估值就是我们先预测目前所有未下子位置,假如我们来判断某一点的估值,我们就遍历其上下左右斜边的棋子,根据棋局,最终给予打分。那么,根据网上的通称,我们先来规定几种棋局:

  1. 五子连珠
  2. 活四
  3. 眠四
  4. 活三
  5. 眠三
  6. 活二
  7. 眠二

其实还可以细分,如果想让自己的算法更智能大家就可以考虑更多的情况给予打分。对于α-β减枝算法,它是一种优化算法,当我们用极大极小法遍历棋盘的时候,我们把一些没有必要去估值的点也去考虑了,而这是完全没有必要的,而α-β减枝算法把下一层估值出现比原来还大或小的分支剪去,提高了算法的效率,大大地降低时间复杂度。可能讲的不够详细,大家可以自己去搜索理解,这里我也发现一篇写的挺好的文章:

α-β减枝算法、极大极小法、估值标准: 点此进

说了这么多,想必大家已经迫不及待地想肝代码了,以下是基于极大极小算法,由于棋盘不大,没有去写对于α-β减枝算法,大家可自行研究:

//找出最大分数
    public int GetMax(int a, int b) {
        return a < b ? b : a;
    }
    //预先设定一些规则估值,对已连成一片的
    public int GetValue(int flag, int num){
        int ret = 0;
        if(1 == num)
            ret = 0;
        if(2 == num){
            if (0 == flag)//死2
                ret = 3;
            else if (1 == flag)//单活2
                ret = 50;
            else
                ret = 100;//双活2
        }else if (3 == num){
            if (0 == flag)//死3
                ret = 5;
            else if (1 == flag)//单活3
                ret = 200;
            else
                ret = 5000;//双活3
        }else if (4 == num){
            if (0 == flag)//死4
                ret = 10;
            else if (1 == flag)//单活4
                ret = 8000;
            else
                ret = 500000;
        }else if (5 == num){
            ret = 10000000;
        }
        return ret;
    }
    // 对未连成一片但通过再下一颗子就能连成一片的局面进行估值
    public int GetPredictValue(int flag, int num){
        int ret=0;
        if (0 == flag || num <= 2)
            ret=0;
        else{
            if(1 == flag){
                if (3 == num)
                    ret=10;
                else if (4 == num)
                    ret=50;
                else
                    ret=200;
            } else {
                if (3 == num)
                    ret=100;
                else if (4 == num)
                    ret=5000;
                else
                    ret=8000;
            }
        }
        return ret;
    }
	//以下棋点为中心对棋盘进行估值,并返回总得分
	public int Evaluate(int[][] Array,int x,int y){
		//得分
		int ret=0;
		//连成棋子数,遍历值,是否活
		int num,k,tag;
		//左边,右边是否拦截
		boolean lflag,rflag;
		//赋初值
        k=1;
        num=1;
        //左右判别(活或死)
        lflag=true;
        rflag=true;
		
		//先估值一连成一片的
        
        //水平线
        while(x-k>=0 && Array[x][y]==Array[x-k][y]){
            num++;
            k++;
        }
        if (!(x-k>=0 && 0==Array[x-k][y])){
            lflag = false;
        }
        k = 1;
        while (x+k<15 &&Array[x][y]==Array[x+k][y]){
            num++;
            k++;
        }
        if(!(x+k<15 && 0==Array[x+k][y])){
            rflag = false;
        }
        //横直线检查完情况
        num=(num<5?num:5);
        if (lflag && rflag){
            tag=2;
        }else{
            if (lflag||rflag)
                tag=1;
            else
                tag=0;
        }
        ret+=GetValue(tag,num);
        //竖直线
        //恢复初值
        k=1;
        num=1;
        lflag=true;
        rflag=true;
        while(y-k>=0 && Array[x][y]==Array[x][y-k]){
            num++;
            k++;
        }
        if(!(y-k>=0 && 0==Array[x][y-k])){
            lflag=false;
        }
        k=1;
        while(y+k<15 && Array[x][y]==Array[x][y+k]){
            num++;
            k++;
        }
        if (!(y+k<15 && 0==Array[x][y+k])){
            rflag=false;
        }
      //竖直线检查完情况
        num=(num<5 ? num:5);
        if(lflag && rflag){
            tag=2;
        }else{
            if(lflag || rflag)
                tag=1;
            else
                tag=0;
        }
        ret+=GetValue(tag, num);
        
        //135度
        //恢复初值
        k=1;
        num=1;
        lflag=true;
        rflag=true;
        while(y-k>=0 && x-k>=0 && Array[x][y]==Array[x-k][y-k]){
            num++;
            k++;
        }
        if (!(y-k>=0 && x-k>=0 && 0==Array[x-k][y-k])){
            lflag=false;
        }
        k=1;
        while(y+k<15 && x+k<15 && Array[x][y]==Array[x+k][y+k]){
            num++;
            k++;
        }
        if(!(y+k<15 && x+k<15 && 0==Array[x+k][y+k])){
            rflag=false;
        }
        //135度直线检查完情况
        num=(num<5?num:5);
        if (lflag && rflag) {
            tag=2;
        }else{
            if(lflag || rflag)
                tag=1;
            else
                tag=0;
        }
        ret+=GetValue(tag,num);
        
        //45度
        //恢复初值
        k=1;
        num=1;
        lflag=true;
        rflag=true;
        while(y+k<15 && x-k>= 0 && Array[x][y]==Array[x-k][y+k]){
            num++;
            k++;
        }
        if(!(y+k<15 && x-k>=0 &&0==Array[x-k][y+k])){
            lflag=false;
        }
        k=1;
        while(y-k>=0 && x+k<15 && Array[x][y]==Array[x+k][y-k]){
            num++;
            k++;
        }
        if(!(y-k>=0 && x+k<15 && 0==Array[x+k][y-k])){
            rflag = false;
        }
       //45度直线检查完情况
        num=(num<5?num:5);
        if(lflag&&rflag){
            tag=2;
        }else{
            if(lflag||rflag)
                tag=1;
            else
                tag=0;
        }
        ret+=GetValue(tag, num);
        
        //能成连成一片的
        //水平线
        int add;
        int leftadd,rightadd;
        boolean leftflag,rightflag;
        int lvalue,rvalue;
        k=1;
        num=1;
        lflag=true;
        rflag=true;
        leftflag=true;
        rightflag=true;
        leftadd=0;
        rightadd=0;
        while(x-k>=0&& Array[x][y]==Array[x-k][y]){
            num++;
            k++;
        }
        if(!(x-k>=0 && 0==Array[x-k][y])){
            lflag = false;
        }
        else{
            add=k+1;//跳过空格
            while(x-add>=0 && Array[x][y]==Array[x-add][y]){
                leftadd++;
                add++;
            }
            if(!(x-add >=0 && 0==Array[x-add][y])){//堵死
                leftflag=false;
            }
        }
        k=1;
        while(x+k<15 && Array[x][y]==Array[x+k][y]){
            num++;
            k++;
        }
        if(!(x+k<15 && 0 ==Array[x+k][y])){
            rflag=false;
        }
        else{
            add=k+1;//跳过空格
            while(x+add<15 && Array[x][y]==Array[x+add][y]){
                rightadd++;
                add++;
            }
            if(!(x+add<15 && 0==Array[x+add][y]))//堵死
                rightflag = false;
        }
        if (leftflag&&rflag){
            tag=2;
        }else{
            if(leftflag||rflag)
                tag=1;
            else
                tag=0;
        }
        lvalue=GetPredictValue(tag,num+1+leftadd);
        if(lflag && rightflag){
            tag=2;
        }else{
            if(lflag||rightflag)
                tag=1;
            else
                tag=0;
        }
        rvalue=GetPredictValue(tag,num+1+rightadd);
        ret+=GetMax(lvalue, rvalue);
        
        // 竖直线
        k = 1;
        num = 1;
        lflag = true;
        rflag = true;
        leftflag = true;
        rightflag = true;
        leftadd = 0;
        rightadd = 0;
        while (y - k >= 0 && Array[x][y] == Array[x][y - k]) {
            num++;
            k++;
        }
        if (!(y - k >= 0 && 0 == Array[x][y - k]))
            lflag = false;
        else {
            add = k + 1;//跳过空格
            while (y - add >= 0 && Array[x][y] == Array[x][y - add]) {
                leftadd++;
                add++;
            }
            if (!(y - add >= 0 && 0 == Array[x][y - add]))// 堵死了
                leftflag = false;
        }
        k = 1;
        while (y + k < 15 && Array[x][y] == Array[x][y + k]) {
            num++;
            k++;
        }
        if (!(y + k < 15 && 0 == Array[x][y + k]))
            rflag = false;
        else {
            add = k + 1;//跳过空格
            while (y + add < 15 && Array[x][y] == Array[x][y + add]) {
                rightadd++;
                add++;
            }
            if (!(y + add < 15 && 0 == Array[x][y + add]))// 堵死了
                rightflag = false;
        }
        if (leftflag && rflag) {
            tag = 2;
        } else {
            if (leftflag || rflag)
                tag = 1;
            else
                tag = 0;
        }
        lvalue = GetPredictValue(tag, num + 1 + leftadd);
        if (lflag && rightflag) {
            tag = 2;
        } else {
            if (lflag || rightflag)
                tag = 1;
            else
                tag = 0;
        }
        rvalue = GetPredictValue(tag, num + 1 + rightadd);
        ret+=GetMax(lvalue, rvalue);
        
        //135度
        k=1;
        num=1;
        lflag=true;
        rflag=true;
        leftflag=true;
        rightflag=true;
        leftadd=0;
        rightadd=0;
        while(y-k>=0 && x-k>=0 && Array[x][y]==Array[x-k][y-k]){
            num++;
            k++;
        }
        if(!(y-k>=0 && x-k>=0 && 0==Array[x-k][y-k]))
            lflag = false;
        else{
            add=k+1;//跳过空格
            while(y-add>=0 && x-add>=0 && Array[x][y]==Array[x-add][y-add]){
                rightadd++;
                add++;
            }
            if(!(y-add>=0 && x-add>=0 && 0==Array[x-add][y-add]))//堵死
                rightflag = false;
        }
        k=1;
        while(y+k<15 && x+k<15 && Array[x][y]==Array[x+k][y+k]){
            num++;
            k++;
        }
        if(!(y+k<15 && x+k<15 && 0==Array[x+k][y+k])){
            rflag = false;
        }
        else{
            add=k+1;//跳过空格
            while(y+add<15 && x+add<15 && Array[x][y]==Array[x+add][y+add]){
                rightadd++;
                add++;
            }
            if(!(y+add<15 && x+add<15 && 0==Array[x+add][y+add])){//堵死
                rightflag = false;
            }
        }
        if(leftflag && rflag){
            tag=2;
        }else{
            if(leftflag||rflag)
                tag=1;
            else
                tag=0;
        }
        lvalue=GetPredictValue(tag,num+1+leftadd);
        if (lflag && rightflag) {
            tag=2;
        }else{
            if(lflag||rightflag)
                tag=1;
            else
                tag=0;
        }
        rvalue=GetPredictValue(tag,num+1+rightadd);
        ret+= GetMax(lvalue, rvalue);
        
        //45度
        k=1;
        num=1;
        leftflag=true;
        rightflag=true;
        leftadd=0;
        rightadd=0;
        while(y+k<15 && x-k>=0 && Array[x][y]==Array[x-k][y+k]){
            num++;
            k++;
        }
        if(!(y+k<15 && x-k>=0 && 0==Array[x-k][y+k])){
            lflag = false;
        }
        else{
            add=k+1;//跳过空格
            while(y+add<15 && x-add>=0 && Array[x][y]==Array[x-add][y+add]){
                rightadd++;
                add++;
            }
            if(!(y+add<15 && x-add>=0 && 0==Array[x-add][y+add]))//堵死
                rightflag=false;
        }
        k=1;
        while(y-k>=0 && x+k<15 && Array[x][y]==Array[x+k][y-k]){
            num++;
            k++;
        }
        if(!(y-k>=0 && x+k<15 && 0==Array[x+k][y-k])){
            rflag = false;
        }
        else {
            add=k+1;//跳过空格
            while(y-add>=0 && x+add<15 && Array[x][y]==Array[x+add][y-add]){
                rightadd++;
                add++;
            }
            if(!(y-add>=0 && x+add<15 && 0==Array[x+add][y-add]))//堵死
                rightflag = false;
        }
        if (leftflag && rflag){
            tag=2;
        }else{
            if(leftflag||rflag)
                tag=1;
            else
                tag=0;
        }
        lvalue=GetPredictValue(tag, num+1+leftadd);
        if(lflag && rightflag){
            tag=2;
        }else{
            if(lflag||rightflag)
                tag=1;
            else
                tag=0;
        }
        rvalue=GetPredictValue(tag, num+1+rightadd);
        ret+=GetMax(lvalue,rvalue);
        
        //返回得分
		return ret;
	}
	//α-β剪枝算法(MinMax)极大极小值
    //极大极小法搜索下棋,向前看LookLength步
    public void GetMinMaxsearchNext(int LookLength) {
        chessOneStep Option = MinMaxsearch(allChess,true,LookLength);
        X = Option.GetX();
        Y = Option.GetY();
        if(-1==X && -1==Y){
        	JOptionPane.showMessageDialog(this,"我太难了!");
        	isRestart="重新开始";
        	isStartGame=false;
            return;
        }	 
		 allChess[X][Y]=2;
		 chessX[countX++]=X;
		 chessY[countY++]=Y;
		 isBlack=true;
		 message="黑方下子";
		 this.openMusic();
		 this.repaint();
    }
	// 极大极小博弈搜索
    public chessOneStep MinMaxsearch(int[][] Array, boolean who, int deepth){
        if (0==deepth)//返回当前局面的评估函数值
        {
            int MaxWei=-INF;
            int idX=-1,idY=-1;
            for(int i=0; i<15;i++) {
                for (int j=0; j<15;j++){
                    if (0==Array[i][j]){
                        Array[i][j]=2;
                        int tmp=Evaluate(Array,i,j);
                        if(tmp>=MaxWei){
                            MaxWei=tmp;
                            idX=i;
                            idY=j;
                        }
                        Array[i][j]=1;
                        tmp=Evaluate(Array,i,j);
                        if(tmp>MaxWei){
                            MaxWei=tmp;
                            idX=i;
                            idY=j;
                        }
                        Array[i][j]=0;
                    }
                }
            }
            return new chessOneStep(idX, idY, MaxWei);
        }
        if (who)//轮到己方,取极大值
        {
            chessOneStep ret = new chessOneStep(-1,-1,-INF);
            ArrayList<chessOneStep> TmpList=new ArrayList<chessOneStep>();
            for (int i=0; i<15;i++){
                for (int j=0; j<15;j++){
                    if (0 == Array[i][j]){
                        Array[i][j]=2;
                        TmpList.add(new chessOneStep(i,j,Evaluate(Array,i,j)));
                        Array[i][j]=0;
                    }
                }
            }
            Collections.sort(TmpList, new Comparator<chessOneStep>(){
				@Override
				public int compare(chessOneStep o1, chessOneStep o2){
					// TODO Auto-generated method stub
					return o1.GetWeight()-o2.GetWeight();
				}
			});
            int num=TmpList.size()<5?TmpList.size():5;
            for (int i=0;i<num;i++) {
                chessOneStep t = TmpList.get(i);
                Array[t.GetX()][t.GetY()]=2;
                chessOneStep tmp =MinMaxsearch(Array,!who,deepth-1);
                if(tmp.GetWeight()>ret.GetWeight()){
                    ret=tmp;
                }
                Array[t.GetX()][t.GetY()] = 0;
            }
            return ret;
        }else//轮到对手,取极小值
        {
            chessOneStep ret=new chessOneStep(-1,-1,INF);
            ArrayList<chessOneStep> TmpList = new ArrayList<chessOneStep>();
            for (int i = 0; i < 15; i++){
                for (int j = 0; j < 15; j++){
                    if (0 == Array[i][j]){
                        Array[i][j] = 1;
                        TmpList.add(new chessOneStep(i,j,
                        Evaluate(Array,i,j)));
                        Array[i][j] = 0;
                    }
                }
            }
            Collections.sort(TmpList, new Comparator<chessOneStep>() {
				@Override
				public int compare(chessOneStep o1, chessOneStep o2) {
					// TODO Auto-generated method stub
					return o1.GetWeight()-o2.GetWeight();
				}
			});
            int num = TmpList.size() < 5 ? TmpList.size():5;
            for (int i = 0; i < num; i++){
                chessOneStep t = TmpList.get(i);
                Array[t.GetX()][t.GetY()] = 1;
                chessOneStep tmp = MinMaxsearch(Array,!who,deepth-1);
                if (tmp.GetWeight()<ret.GetWeight()){
                    ret=tmp;
                }
                Array[t.GetX()][t.GetY()]=0;
            }
            return ret;
        }
    }

还有一个类解释一下下:
chessOneStep类:电脑下一步走棋的位置估值类,直接看源码:

package ChineseChess;
/**
 * 下棋类
 * @author 梦想少年
 *
 */
public class chessOneStep {
	//属性
	private int X;
	private int Y;
	private int ret;
	//构造器
	public chessOneStep(int X,int Y,int ret){
		this.X=X;
		this.Y=Y;
		this.ret=ret;
	}
	//方法
	public int GetX(){
		return this.X;
	}
	public int GetY(){
		return this.Y;
	}
	public int GetWeight(){
		return this.ret;
	}
}

到此为此,我们的代码就全部编写成功啦!!!你就可以把你做的五子棋给你的室友下下,不瞒你说,我的室友被我的程序打败两次,嘿嘿,简直太逗了!

3.技术总结

此次AI五子棋从星期一开始写,查资料到最后搞定耗时四天,文章制作不易,希望大家多多收藏。如果还有不懂的小伙伴我们可以一起讨论相互学习呀!最后附图一张,以此勉励!
在这里插入图片描述
再来一个预告,下一篇博客为网络通信方面,如何实现视频聊天,网络通信协议如何制定?随时欢迎大家的关注!😎

  • 4
    点赞
  • 2
    收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:深蓝海洋 设计师:CSDN官方博客 返回首页
评论 1

打赏作者

梦想少年number

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值