最近学习java swing,将上次的人机五子棋做了改进,将其界面化,在调试过程中还发现了上次代码中的一些问题。
问题一:无法应对A落在自己左上方的棋子
问题二:计算机不能应对落在边界的棋子,不能应对单个棋子
(即,对于代码中的k值,若该方向上无法再落子,则应该放弃这个k值最大的一串棋子)
1.大体结构: 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.效果截图: 最后,欢迎大家到我的个人主页http://jiaqianli.cn/,有更为详细的介绍,希望一起交流、学习,一起进步~