【java】java swing:五子棋人机对弈

最近学习java swing,将上次的人机五子棋做了改进,将其界面化,在调试过程中还发现了上次代码中的一些问题。


问题一:无法应对A落在自己左上方的棋子

【java】java swing:五子棋人机对弈_第1张图片

问题二:计算机不能应对落在边界的棋子,不能应对单个棋子

(即,对于代码中的k值,若该方向上无法再落子,则应该放弃这个k值最大的一串棋子)

【java】java swing:五子棋人机对弈_第2张图片

1.大体结构:

【java】java swing:五子棋人机对弈_第3张图片

2.完整的,又臭又长的代码:
package vaniot.com;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Toolkit;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
public class WuZiQi3 extends JPanel{
	public WuZiQi3(){
		
	}
	//窗口
	public void init(JFrame frame,int formWidth,int formHeight){
        //设置当前窗体可见,默认不可见
        frame.setVisible(true);
        //设置当前窗体的宽和高
        frame.setSize(formWidth+14, formHeight+35);
        frame.setTitle("五子棋人机对弈");
        Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
        //通过Dimension类的对象dim可以获取到屏幕的宽和高
        int screenWidth = dim.width;
        int screenHeight = dim.height;
        System.out.println("当前屏幕的分辨率为:"+screenWidth+"*"+screenHeight); 
        int x = (screenWidth-formWidth)/2;
        int y = (screenHeight-formHeight)/2;;
        //设置当前窗体出现在窗口中坐标位置,即x轴的坐标值和y轴的坐标值
        frame.setLocation(x, y);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		JFrame frame=new JFrame();
		WuZiQi3 wzq=new WuZiQi3();		
		wzq.init(frame,800,600);
		WuZiQi3 qp=new WuZiQi3();
		//初始化棋盘
		qp.initQiPan();
		
		qp.positionOfMouse(frame);
		
		//qp.playGame(frame);
		frame.add(qp);
	}
	private static final long serialVersionUID = 1L;
	int flagB=0;
	int ii=0;int jj=0;//鼠标点击的坐标
	int ss=0;int tt=0;//计算机上一步落子位置	
    public int[][] Qipan1=new int[Y_HEIGHT][X_WIDTH];
    public int[][] Qipan2=new int[Y_HEIGHT][X_WIDTH];
    public int[][] Qipan3=new int[Y_HEIGHT][X_WIDTH];
    public int[][] Qipan4=new int[Y_HEIGHT][X_WIDTH];    
	//假定对应的宽度为40列,对应的是横轴的坐标x
	public static final int X_WIDTH = 40;//假定对应的高度为30列,对应的是纵轴的坐标y
	public static final int Y_HEIGHT = 30;
	//定义一个30*40二维数组 ,表示有30行40列,其中行对应的是y轴的坐标值,列对应的是x轴的坐标值
	int[][] QiPan = new int[Y_HEIGHT][X_WIDTH];
	//初始化棋盘
	void initQiPan(){
		for(int i=0;i=0)&&(QiPan[I][J-1]==0)){
								//向右被占用,向左没有被占用且没到边边上
								QiPan[I][J-1]=2;
								ss=I;tt=J-1;
							}
							else{//A向左被占用或到边边上,再搜寻自己的‘#’
								computerPlayer();
							}
							//这里与上篇有调整-------------------------
							//纠正了原程序中玩家A落子在A上一步的左方,而计算机不落子的错误
						}
						else if(J-1>=0){//向右到边界了,再向左看是否有位置
							if(QiPan[I][J-1]==0){//向左没有被占用
								QiPan[I][J-1]=2;
								ss=I;tt=J-1;
							}
							else{//A向左被占用,再搜寻自己的‘#’
								computerPlayer();
							}
						}
						else{//向左也到边界或者向左被占用,搜寻自己的‘#’或就近任意落子
							computerPlayer();
						}
					}
					else if(flag[2]==2){// ↓
						if(I+flag[4]<30){//向下没有到边边上
							if(QiPan[I+flag[4]][J]==0){
								QiPan[I+flag[4]][J]=2;
								ss=I+flag[4];tt=J;//记录计算机这一步落在哪里
							}
							else if((I-1>=0)&&(QiPan[I-1][J]==0)){
								//向下已被占用,向上未到边界且未被占用
								QiPan[I-1][J]=2;
								ss=I-1;tt=J;
							}
							else{
								computerPlayer();
							}
						}
						else if(I-1>=0){//向下到边界了,再向上看是否有位置
							if(QiPan[I-1][J]==0){//向左没有被占用
								QiPan[I-1][J]=2;
								ss=I-1;tt=J;		
							}
							else{//A向上被占用,再搜寻自己的‘#’
								computerPlayer();
							}
						}
						else{//向上也到边界或者向上被占用,搜寻自己的‘#’或就近任意落子
							computerPlayer();
						}
					}
					else if(flag[2]==3){// ↙
						if((I+flag[4]<30)&&(J-flag[4]>=0)){//向左下没有到边边上
							if(QiPan[I+flag[4]][J-flag[4]]==0){//左下没有被占用
								QiPan[I+flag[4]][J-flag[4]]=2;
								ss=I+flag[4];tt=J-flag[4];//记录计算机这一步落在哪里
							}
							else if((QiPan[I-1][J+1]==0)&&(I-1>=0)&&(J+1<40)){
								//左下已被占用,右上没到边边上且没有被占用
								QiPan[I-1][J+1]=2;
								ss=I-1;tt=J+1;	
							}
							else{//右上到边边上或被占用
								computerPlayer();
							}
						}
						else if((I-1>=0)&&(J+1<40)){//向左下到边界了,向右上没有到边界
							if(QiPan[I-1][J+1]==0){//向右上没有被占用
								QiPan[I-1][J+1]=2;
								ss=I-1;tt=J+1;	
							}
							else{//A向右上被占用,再搜寻自己的
								computerPlayer();
							}
						}
						else{//向右上也到边界或者向右上被占用,搜寻自己的‘#’或就近任意落子
							computerPlayer();
						}
					}
					else if(flag[2]==4){// ↘
						if((I+flag[4]<30)&&(J+flag[4]<40)){//向右下没有到边边上
							if(QiPan[I+flag[4]][J+flag[4]]==0){//右下没有被占用
								QiPan[I+flag[4]][J+flag[4]]=2;
								ss=I+flag[4];tt=J+flag[4];//记录计算机这一步落在哪里
							}
							else if((QiPan[I-1][J-1]==0)&&(I-1>=0)&&(J-1>=0)){
								//右下已被占用,左下没到边边上且没有被占用
								QiPan[I-1][J-1]=2;
								ss=I-1;tt=J-1;
							}
							//这里与上篇有调整-------------------------
							//纠正了原程序中玩家A落子在A上一步的右上方,而计算机不落子的错误
							else{//右下已被占用,左下到边边上或被占用
								computerPlayer();//搜寻自己的k最大值
							}
						}
						else if((I-1>=0)&&(J-1>=0)){//向右下到边界了,再向左上看是否有位置
							if(QiPan[I-1][J-1]==0){//向左上没有被占用
								QiPan[I-1][J-1]=2;
								ss=I-1;tt=J-1;	
							}
							else{//A向右上被占用,再搜寻自己的‘#’
								computerPlayer();
							}
							//这里与上篇有调整-------------------------
							//纠正了原程序中玩家A落子在A上一步的左上方,而计算机不落子的错误
						}
						else{//向右上也到边界或者向右上被占用,搜寻自己的‘#’或就近任意落子
							computerPlayer();
						}
					}
					repaint();//落子完毕,更新棋盘
					flag=scanner(ss,tt);//重新计算棋盘布局
					if(flag[3]==1){
						repaint();
						String message="计算机胜出!";
	                    JOptionPane.showMessageDialog(qipan, message);
	                    System.exit(0);
					}
					flagB=1;
				}
			}
		});
	}   
    public void computerPlayer(){
    	//A的k值最大处都已经落子,此时,寻找计算机自己的k最大值
    	int[] flag={0,0,0,0,0};	
    	flag=scanner(ss,tt);//计算机上一步落子处
    	int I=flag[0];
    	int J=flag[1];//(I,J)处,k值最大
		if(flag[2]==1){
			if((J+flag[4]<40)&&(QiPan[I][J+flag[4]]==0)){//向右没有到边边上,且没有被占用
				QiPan[I][J+flag[4]]=2;//计算机向右落子
				ss=I;tt=J+flag[4];
			}
			else if(J-1>=0){//向右到边边上或者向右被占用,向左没有到边边上
				if(QiPan[I][J-1]==0){//向左没有被占用
					QiPan[I][J-1]=2;
					ss=I;tt=J-1;
				}
				else{//向左被占用了,就近随机落子
					for(int i=0;i<30-I;i++){
						for(int j=0;j<40-J;j++){
							if(QiPan[I+i][J+j]==0){
								QiPan[I+i][J+j]=2;
								j=40-J;i=30-I;//跳出循环
							}
						}
					}
				}
			}
		}
		else if(flag[2]==2){
			if((I+flag[4]<30)&&(QiPan[I+flag[4]][J]==0)){//向下没有到边边上,且没有被占用
				QiPan[I+flag[4]][J]=2;
				ss=I+flag[4];tt=J;
			}
			else if(I-1>=0){//向下到边界或被占用,向上没有到边界
				if(QiPan[I-1][J]==0){
					QiPan[I-1][J]=2;
					ss=I-1;tt=J;
				}
				else{//向上被占用了,就近随机落子
					for(int i=0;i<30-I;i++){
						for(int j=0;j<40-J;j++){
							if(QiPan[I+i][J+j]==0){
								QiPan[I+i][J+j]=2;
								j=40-J;i=30-I;//跳出循环
							}
						}
					}
				}
			}
		}
		else if(flag[2]==3){
			if((I+flag[4]<30)&&(J-flag[4]>=0)){//向左下没有到边边上
				QiPan[I+flag[4]][J-flag[4]]=2;
				ss=I+flag[4];tt=J-flag[4];
			}
			else if((I-1>=0)&&(J+1<40)){//向右上没有到边边上
				if(QiPan[I-1][J+1]==0){
					QiPan[I-1][J+1]=2;
					ss=I-1;tt=J+1;
				}
				else{//向右上被占用了,就近随机落子
					for(int i=0;i<30-I;i++){
						for(int j=0;j<40-J;j++){
							if(QiPan[I+i][J+j]==0){
								QiPan[I+i][J+j]=2;
								j=40-J;i=30-I;//跳出循环
							}
						}
					}
				}
			}
		}
		else if(flag[2]==4){
			if((I+flag[4]<30)&&(J+flag[4]<40)&&(QiPan[I+flag[4]][J+flag[4]]==0)){
				//向右下没有到边边上且没有被占用
				QiPan[I+flag[4]][J+flag[4]]=2;
				ss=I+flag[4];tt=J+flag[4];
			}
			else if((I-1>=0)&&(J-1>=0)){
				//向右下到边界或者被占用,向左上没有到边界
				if(QiPan[I-1][J-1]==0){
					QiPan[I-1][J-1]=2;
					ss=I-1;tt=J-1;
				}
				else{//向左上被占用了,就近随机落子
					for(int i=0;i<30-I;i++){
						for(int j=0;j<40-J;j++){
							if(QiPan[I+i][J+j]==0){
								QiPan[I+i][J+j]=2;
								j=40-J;i=30-I;//跳出循环
							}
						}
					}
				}
			}
		}
    }
    //scanner较上篇有调整-------------------------
    //在找寻k值最大的点的同时,需要考察该方向上是否还有落子位置
    //若没有,则放弃这个k值最大点
    public int[] scanner(int iii,int jjj){
		//每落一子,扫描全盘,看是否有连成功的
		int[] m=new int[5];//返回k值最大的坐标、方向以及是否连成5子、相应坐标的k值
		//从(0,0)开始,先向右扫描
		int flag0=0;
		for(int i=0;i<30;i++){
			for(int j=0;j<40;j++){//不越界的情况下作比较
				int k=1;
				int k0=0;
				while(k0==0){//k=4时,成功
					if(j+k<40){
						if((QiPan[i][j]==QiPan[i][j+k])&&(QiPan[i][j]!=0)){
							k++;
					    }
						else k0=1;	
					}
					else break;
				}
				if(k==5){
					flag0=1;//成功左右连成5子
				}
				if(j+k<40){//右没有到边界
					if(QiPan[i][j+k]==0){//右有位置,可以
						Qipan1[i][j]=k;
					}
					else if(j-1>=0){//右没有到边界,但是向右被占用,左没有到边界
						if(QiPan[i][j-1]==0){//左有位置,可以
							Qipan1[i][j]=k;
						}
						else Qipan1[i][j]=0;//左也被占用,放弃该位置,改点k值记为0
					}
					else Qipan1[i][j]=0;//左到边界,放弃该位置
				}
				else{//右到边界
					if(j-1>=0){//左没有到边界
						if(QiPan[i][j-1]==0){//左有位置,可以
							Qipan1[i][j]=k;
						}
						else Qipan1[i][j]=0;//左被占用,放弃该位置
					}
					else Qipan1[i][j]=0;//左也到边界,放弃该位置				
				}
			}
		}

		//向下扫描
		for(int j=0;j<40;j++){
			for(int i=0;i<30;i++){
				int k=1;
				int k0=0;
				while(k0==0){//k=4时,成功
					if(i+k<30){
						if((QiPan[i][j]==QiPan[i+k][j])&&(QiPan[i][j]!=0)){
							k++;
					    }
						else k0=1;//跳出循环
					}
					else break;
				}
				if(k==5){
					flag0=1;//成功上下连成5子
				}
				if(i+k<30){//下没有到边界
					if(QiPan[i+k][j]==0){//下有位置,可以
						Qipan2[i][j]=k;
					}
					else if(i-1>=0){//下没有到边界,但是向下被占用,上没有到边界
						if(QiPan[i-1][j]==0){//上有位置,可以
							Qipan2[i][j]=k;
						}
						else Qipan2[i][j]=0;//上也被占用,放弃该位置
					}
					else Qipan2[i][j]=0;//上到边界,放弃该位置
				}
				else{//下到边界
					if(i-1>=0){//上没有到边界
						if(QiPan[i-1][j]==0){//上有位置,可以
							Qipan2[i][j]=k;
						}
						else Qipan2[i][j]=0;//上被占用,放弃该位置
					}
					else Qipan2[i][j]=0;//上也到边界,放弃该位置				
				}
			}
		}

		//右上&左下方向
		for(int i=0;i<30;i++){
			for(int j=0;j<40;j++){//j可以直接从4开始,4之前的这个方向连不成5子
				int k=1;
				int k0=0;
				while(k0==0){//k=4时,成功
					if((i+k<30)&&(j-k>=0)){
						if((QiPan[i][j]==QiPan[i+k][j-k])&&(QiPan[i][j]!=0)){
							k++;
					    }
						else k0=1;
					}
					else break;
				}
				if(k==5){
					flag0=1;//成功右上&左下连成5子
				}
				if((i+k<30)&&(j-k>=0)){//左下没有到边界
					if(QiPan[i+k][j-k]==0){//左下有位置,可以
						Qipan3[i][j]=k;
					}
					else if((i-1>=0)&&(j+1<40)){//左下没有到边界,但是被占用,右上没有到边界
						if(QiPan[i-1][j+1]==0){//右上有位置,可以
							Qipan3[i][j]=k;
						}
						else Qipan3[i][j]=0;//右上也被占用,放弃该位置
					}
					else Qipan3[i][j]=0;//右上到边界,放弃该位置
				}
				else{//左下到边界
					if((i-1>=0)&&(j+1<40)){//右上没有到边界
						if(QiPan[i-1][j+1]==0){//右上有位置,可以
							Qipan3[i][j]=k;
						}
						else Qipan3[i][j]=0;//右上被占用,放弃该位置
					}
					else Qipan3[i][j]=0;//右上也到边界,放弃该位置				
				}
			}
		}

		//左上&右下方向
		for(int i=0;i<30;i++){
			for(int j=0;j<40;j++){//j到a-4,a-4之后的这个方向连不成5子
				int k=1;
				int k0=0;
				while(k0==0){//k=4时,成功
					if((i+k<30)&&(j+k<40)){
						if((QiPan[i][j]==QiPan[i+k][j+k])&&(QiPan[i][j]!=0)){
							k++;
					    }
						else k0=1;
					}
					else break;
				}
				if(k==5){
					flag0=1;//成功左上&右下连成5子
				}
				if((i+k<30)&&(j+k<40)){//右下没有到边界
					if(QiPan[i+k][j+k]==0){//右下有位置,可以
						Qipan4[i][j]=k;
					}
					else if((i-1>=0)&&(j-1>=0)){//右下没有到边界,但是被占用,左上没有到边界
						if(QiPan[i-1][j-1]==0){//左上有位置,可以
							Qipan4[i][j]=k;
						}
						else Qipan4[i][j]=0;//左上也被占用,放弃该位置
					}
					else Qipan4[i][j]=0;//左上到边界,放弃该位置
				}
				else{//右下到边界
					if((i-1>=0)&&(j-1>=0)){//左上没有到边界
						if(QiPan[i-1][j-1]==0){//左上有位置,可以
							Qipan4[i][j]=k;
						}
						else Qipan4[i][j]=0;//左上被占用,放弃该位置
					}
					else Qipan4[i][j]=0;//左上也到边界,放弃该位置				
				}
			}
		}
		int[][] m1=new int[Y_HEIGHT][X_WIDTH];//存储每点的k值
		int[][] m2=new int[Y_HEIGHT][X_WIDTH];//存储每点k值最大的那个方向
		for(int i=0;i<30;i++){
			for(int j=0;j<40;j++){
				//同一个点,四个方向k值最大的那个方向
				//m2记录方向
				m1[i][j]=max(Qipan1[i][j],Qipan2[i][j],Qipan3[i][j],Qipan4[i][j]);
				if(m1[i][j]==Qipan1[i][j]){
					m2[i][j]=1;// →
				}
				else if(m1[i][j]==Qipan2[i][j]){
					m2[i][j]=2;// ↓
				}
				else if(m1[i][j]==Qipan3[i][j]){
					m2[i][j]=3;// ↙
				}
				else{
					m2[i][j]=4;// ↘
				}
			}
		}
		//整个棋盘上哪个点的k值最大
		int m3=0;
		int I=0,J=0;//(I,J)处k值最大
		for(int i=0;i<30;i++){
			for(int j=0;j<40;j++){
				if(m1[i][j]>m3){
					m3=m1[i][j];
					I=i;J=j;
				}
				else if(m1[i][j]==m3){//如果两个k相等,取离落子近的那个点
					int dd1=(I-iii)*(I-iii)+(J-jjj)*(J-jjj);
					int dd2=(i-iii)*(i-iii)+(j-jjj)*(j-jjj);
					if(dd1>dd2){
						m3=m1[i][j];
						I=i;J=j;
					}
				}
			}
		}
		m[0]=I;m[1]=J;//确定哪个点的k值最大
		m[2]=m2[I][J];//方向
		m[3]=flag0;//是否连成5子
		m[4]=m3;//该点的k值
		return m;
	}
    public int max(int x,int y,int z,int w){
    	if(x>y){
    		if(x>z) return x>w?x:w;
    		else return z>w?z:w;
    	}
    	else{
    		if(y>z) return y>w?y:w;
    		else return z>w?z:w;
    	}
    }
}

3.效果截图:
【java】java swing:五子棋人机对弈_第4张图片

【java】java swing:五子棋人机对弈_第5张图片

最后,欢迎大家到我的个人主页http://jiaqianli.cn/,有更为详细的介绍,希望一起交流、学习,一起进步~

你可能感兴趣的:(java)