毕业论文写作

毕业设计需求

免费毕设之基于Java的五子棋游戏的设计(源代码+论文)

目录

 

系统设计


1.1  程序结构说明

既然是JavaApplication,要实现网络对战,故采用C/S模式编写,程序包含7个独立的类文件-ChessWZQ.java、Group.java、Message.java、Player.java、ServerOneClient.java、Server.java、BoardPanel.java

其中BoardPanel.java主要负责棋盘的初始化,鼠标事件的处理,以及判断胜负条件。ChessWZQ.java定义了面板上的其他元素,包括玩家列表,标题栏等。也包括了事件处理和人工智能。ServerOneClient.java则负责网络对战的处理。其他的类都是又这3个主类延伸出去的。在具体实现的时候再介绍其作用。见图13。

系统实现

1.1  棋盘及棋子的类                 

1.1.1   棋盘

棋盘如图1,具体代码如下:

String line = "a b c d e f g h i j k l m n o";

char [] rowNum1 = {'1','2','3','4','5','6','7','8','9'};

char [] rowNum2={'1','0','1','1','1','2','1','3','1','4','1','5'};

这部分为棋盘的边界标识符,是必须要有的。用字符数组存储,最后确定位置放上去即可。

棋盘的绘制:

private static int xp;           // 棋子 X坐标

  private static int yp;           // 棋子Y坐标

 

public void paint(Graphicsgc){

        super.paint(gc);

        //this.setBackground(Color.gray);

        //this.invalidate();

        gc.setColor(Color.blue);

        //gc.setColor(new Color(255, 255,240));

        //画横向标识符

        gc.drawString(line,25,15);

        // 画竖向标识符

        for(int i=0;i<9;i++){

           gc.drawChars(rowNum1,i,1,10,35+i*30);

        }

        for(int i=9,j=0;i<15;i++,j+=2){

           gc.drawChars(rowNum2,j,2,10,35+i*30);

        }

        // 画棋盘

        for (int i = 0; i < 15; i++) {

            gc.drawLine(30, 30 + i * 30, 450, 30 + i* 30); //

            gc.drawLine(30 + i * 30, 30, 30 + i* 30, 450); //

        }

        gc.drawLine(25, 25, 455, 25);

        gc.drawLine(25, 25, 25, 455);

        gc.drawLine(25, 455, 455, 455);

        gc.drawLine(455, 25, 455, 455);

 //面板初始化

        for(int i=0;i<15;i++){

            for (int j = 0; j < 15; j++) {

                xp=16+i*30;

                yp=16+j*30;

                if (board[i][j] == 1){

                    gc.setColor(Color.black);

                    gc.fillOval(xp,yp,28,28);

                    //gc.drawImage(black, 16 +i * 30, 16 + j * 30, this);

                }

                if (board[i][j] == 2){

                    gc.setColor(Color.white);

                    gc.fillOval(xp,yp,28,28);

                    //gc.drawImage(white, 16 +i * 30, 16 + j * 30, this);

            }

          }

        }

      }

 

1.1.2   棋子

确定下子的坐标(xp,yp)画特定大小的椭圆,这里的坐标指棋子相对棋盘的绝对坐标。

private static int xp;           // 棋子 X坐标

private static int yp;           // 棋子Y坐标

        if(col==1) g.setColor(Color.black);

         elseg.setColor(Color.white);

        g.fillOval(xp,yp,10,28,28);

1.2  胜负判断条件

要判断四个方向,横向、竖向、以及2个斜向。思想还是比较简单,相同颜色连成五子即胜利,网上有些网友评论说完整的判断胜负条件包括连五和活四,我觉得完全没必要,活四还要检查两边的棋子,虽然运算量不大,但五子棋的标准就是连五即胜,一步之差,我们既要遵守规则,也要简化代码实现尽完整的功能。

我们要事先建立一个盘面数组board[ ][ ],即棋型表,数组的每一个元素对应棋盘上的一个交叉点,用‘0’表示空位,‘1’表示黑棋,‘2’表示白棋。由于代码太多,下面给出了一般状况的判断胜负函数,及以坐标(x,y)为中心的9X9矩形,只能在棋盘的内部,如果超过棋盘,就要另外考虑。下面的代码就是一般情况,整个矩形在棋盘内部的时候的判断胜负条件,如图14。

胜负判断条件以下给出了X方向的代码:

 protectedboolean judge(int x,int y,int clr){

        inti=0,j=0,count=0;

        // x方向

       for(i=0,count=0;x-i>=0&&i<5;i++){

            if(clr==board[x-i][y]){

               count++;

            }

                    else{

               break;

            }

  // System.out.println("("+x+" , "+y+" )"+"count = "+count);

           if(count==5)

               return true;

        }

       for(i=1;x+i<15&&i<5;i++){

           if(clr==board[x+i][y]){

               count++;

           }else{

               break;

            }

           if(count==5)

               return true;

        }

        returnfalse;

}

为保证公平,先下子的就有禁手。但是我们一般没有这个规则限制,都是轮流先下子。理论上是这样的。但很多专家表明,先下子有很大的几率获胜,即使有禁手,先下子的一方还是有很大的优势,我觉得对于我们一般玩家而言,这些规定可以不考虑。

判断胜负的不管是单机还是玩家相互游戏,都必须开服务端,因为判断胜负是放在里面的。如果有一方获胜,弹出提示框,如果确认则清空棋盘继续新游戏。

public void getVictory(Message msg){

       JOptionPane.showMessageDialog(null,

                                     "You  Win  The Game",

                                     "Congratulations",

                             JOptionPane.INFORMATION_MESSAGE);

        //继续新游戏

       label3.setText("Player2");

       newGame();

    }

需要注意的一点是落下的棋子如果离任何一方的边界小于4,则以边界为限制判断是否有一方获胜,这样的话也要考虑多种方向,但原理还是和基本情况是一样的。

1.1  网络对战

这部分也属于网络套接字编程的经典应用,根据服务器地址连接特定端口。

网络部分很简单,但是我也做出了自己的特色,就是事件处理,做工可根据具体情况修改,虽然没有多少实用价值,但是也尽量使程序留有扩展性。下面给出了Message的代码。

/**

 * 消息列表

 * type = 1 // 请求连接,  msg = 连接者名字

 * type = 2 // 放棋子

 * type = 3 // 请求和其他人游戏

 * type = 4 // 拒绝游戏请求

 * type = 5 // 同意请求

 * type = 6 // 发送胜利消息

 * type = 7 // 断开连接请求

 * type = 8 // 保存游戏,但是不放在磁盘上,在下一局开始时将会丢失

 * type = 9 // 添加新的玩家到所有客户端的玩家列表

 * type = 10// 响应信息 type ==1

 * type = 11// 和玩家游戏,msg 保存玩家的名字 ,创建游戏的玩家设置游戏

 * type = 12//信息设置

 * type = 13// 设置玩家颜色

 * type = 14// msg 接受请求的一方设置

 * type = 15// 服务端更新信息

 * type = 16// 发送控制或错误信息

 * type = 17// 游戏失败

 * type = 18// 服务端套接字关闭

 * type = 19// 玩家结束游戏及更新

 * tyep = 20// 电脑获胜玩家获胜

 */

SOCKET监听端口,如果有玩家则更新玩家列表,如果双击除自己以外的玩家,则可以开始游戏,发送游戏请求,如果同意则返回棋子颜色并开始游戏,同时清空玩家列表。每一下子,把坐标传给服务端,并在2端显示出来,并且判断胜负。如果,有一方获胜,则提示消息通知双方,确定则继续开始新游戏。

服务端开启服务监听线程和客户端,如图15,如果有玩家知道服务器IP地址,即可选择在线玩家进行联网游戏,如图16。

双击除自己以外的一个玩家,发出游戏请求,同时要确定自己棋子的颜色,用MSG发回服务端。如果被拒绝,则返回原来的状态。

if(msg.color==1){

            ss = new String("white");

            bpanel.setColor(2);

        }

        else{

            ss = new String("black");

            bpanel.setColor(1);

        } 

1.1  电脑AI

广义上来讲,博弈是指在一定的环境条件和一定的规则约束下,依靠自己所能够掌握的信息,从各自选择的行为或是策略进行选择并加以实施,并从各自取得相应结果或收益的过程。冯·诺伊曼John von Neumann,1903-1957)和摩根斯坦恩(Oskar Margenstern,1902-1977)在1944年出版了《博弈论与经济行为》(Theory of Games and Economic Behavior)一书中,最早地提出了关于博弈论的概念。但是,对于非合作、纯竞争型博弈,诺伊曼所解决的只有二人零和博弈。在这里所抽象化后的博弈问题是,已知参与者集合(两方),策略集合(所有棋着),和盈利集合(赢子输子),最终是想去找到一个理论上的解或平衡,也就是对参与双方来说都最合理、最优的具体策略。

而在这里狭义的讲,博弈论主要是研究棋手们落子中理性化、逻辑化的部分,并将其系统化为一门科学。换言之,博弈就是研究个体如何在错综复杂的相互影响中得出最合理的策略,博弈论正是衍生于古老的游戏或曰博弈如象棋、扑克等。数学家们将具体的问题抽象化,通过建立自完备的逻辑框架、体系研究其规律及变化。

参考了很多五子棋算法,大部分思想差不多,就是搜索估值确定重要性,然后选取最大的一个点下子。具体做法如下:为电脑和玩家各建立一张表,用来存放棋型数据,比如“20”代表“冲四”的点,用“15”代表“活三”的点,那么在计算重要性时,就可以根据20>15得出前者比后者重要,下子时电脑便会自动选择“冲四”的点,这里还要说明一点的事,还要考虑四个方向。因为有可能有复合棋型,比如“四三”..从第一步起,不管是哪一方下子,电脑都有以这点为中心搜索9X9的矩阵内的所有空白点上棋子的重要性,一颗棋子对棋型影响的大小有9X9。重要看来虽然说进攻和防守的重要性一样的,但是我认为防守更重要。

在估值的时候,必须要考虑棋子的合法落子情况。不同的棋类博弈,其估值必定有极大的差别,各种因为规则而造成的不同因素影响估值的设计。不同的棋类游戏各有所谓的规则,规则中就有博弈双方都可以走哪些着法。某些博弈游戏很容易就找到合理着法,我所实现的五子棋,它就具有很简单的落子规则,即棋盘上所有的空位都可以落子,它们都是合理的着法。但是有些棋类游戏,比如在中国象棋和国际象棋中,情况就有些复杂了,每个棋子都有它特定的着法,

电脑下子要考虑自己和玩家的棋型,优先防守,如果没有要防的棋型,则搜索自己的棋型下子。

设置重要性,即估值模块,下面只给出横向的代码

private voidsetWeight(int x,int y,int tcolor){

        inti=RectX1,j=RectY1,value=0,k=0,n=0,flag=0;

        // '--' 方向

        for(i=RectX1,j=y;i<=RectX2;i++){

            if(BoardPanel.board[i][j]!=0){

                continue;

            }

            value=0;flag=0;

            for(k=1;i-k>=RectX1 &&k<5;k++){

               if(BoardPanel.board[i-k][j]==tcolor){

                    value++;

                    continue;

                }

               if(BoardPanel.board[i-k][j]==0){//black space

                    flag++;

                    break;

                }

            }

            for(k=1;i+k<RectX2 &&k<5;k++){

                if(BoardPanel.board[i+k][j]==tcolor){

                    value++;

                }

               if(BoardPanel.board[i+k][j]==0){

                    flag++;

                    break;

                }

            }

            n=weight(value,flag);

            if(weightBoard[i][j]<n){

                weightBoard[i][j]=n;

            }

        }

     }

    设定相应空位的重要值以后,选取最大的值下子,代码如下:

private voidgetBiggest(int [][] arr,int x,int y){

        int [] temp=new int[2];

        int swt=arr[0][0],tmp=0;

        for(int i=0;i<15;i++){

            for(int j=0;j<15;j++){

                if(arr[i][j]>swt){

                    temp[0]=i;temp[1]=j;

                    swt=arr[i][j];

                }

            }

        }

        x=temp[0];

        y=temp[1];

        arr[x][y]=0;

    } 

对于特定的棋型,都有一个不同的估值,以此来区别不同棋型的优劣,也以此来决定最终的落子位置。毫无疑问,像已有四子连成一线且还可以继续落子的情况,明显要比只有三个子连成一线的情况要好,或者说优先级要更高,对弈双方对此种棋局,肯定都是把第一种情况放为首要分析的位置上。因此,要使棋手做出这种判断,就要把第一种情况的估值设置得高。

对不同的棋型设置重要值,比如:活四、死四、活三、死三、活二、死二。

同理,和判断胜负一样下子也要考虑边界特殊情况,矩形设置如下: 

private void setRect(int x,int y){

       if(x-4>0)  RectX1=x-4;

       else       RectX1=0;

       if(x+4>14) RectX2=14;

       else       RectX2=x+4;

       if(y-4>0)  RectY1=y-4;

       else       RectY1=0;

       if(y+4>14) RectY2=14;

       else       RectY2=y+4;

       if(RectX1>RectY1) RectX1 = x-(y-RectY1);

       else              RectY1 =y-(x-RectX1);

       if(RectX2>RectY2) RectY2 = y+(RectX2-x);

       else              RectX2 =x+(RectY2-y);

    }             

努力提高电脑AI一直是五子棋游戏关键,第一步的人工智能只是估值和搜索算法的集合,要真正的提高电脑AI还有很多步,例如,我的电脑AI只是片面的分析了双方的器型,没有前瞻性。如果玩家多想几步,电脑就发现不了。不过即使没采用递归算法,要是让电脑先下子的话,你的大部分时间也是花在防守上,可能是我的棋力太菜了,我自己还没有下赢过电脑。

目前有2种方法提高电脑棋力:一是递归算法,二是增加细致的特定棋形的判断,鉴于自己对算法方面欠缺甚多,没有办法完成这个功能,实在是遗憾。我大体说一下这2种算法的思路:递归算法的意思可以说成“今后几步预测法”,首先让电脑分析一个可能的点,如果在这儿下子将会形成对手不得不防守的棋型(例如:“冲四”、“活三”);那么下一步对手就会照你的思路来防守你,如此一来便完成了第一步的预测。这时候在调用盘面分析模块对预测后的棋型进行分析,如果出现了“四三”、“双三”或“双四”等制胜点,那么己方就可以获胜了。否则照同样的方法向下分析,就可以预测出多步。如果盘面上没有对手必须防的棋型,进攻不成的话就得考虑防守了,将自己和对手调换一下位置,然后用上面的方法来预测对手的棋重要防守和攻击都可以平衡,不过缺点是预测的算法量比较大。关于增加细致的特定棋形的判断,前面已经说过,以为不同的棋型,例如“死四”就有不同的几种情况,如果对每一种设置不同的重要值,也可以提高电脑AI,但是要设置合适的分数就要在实践中检验了,因为这个不是大小的问题,而是相差多少。正如前面所说,增加对细致棋型的判断也会提高电脑AI,虽然没有递归算法明显,但的确是一种途径,不过考虑的因素较多。

关于电脑学习,这听起来似乎是算法无法实现的功能。但是在对弈中却是非常有用的,但还只是些理论上的东西,比如棋局结束后,反向搜索,在自己的棋库中设置相关记忆。但我并不以为这是很好的方法。因为用这种方法,很有可能它没有找准原因,又或者进行学习的时候反而把劣等的学习进去;并且这种学习是很片面的,它只会认准一种极相似的情况(或者说一模一样的棋局情况),而不会辨识出相似的棋局情况。

 

源文件


最新毕业设计成品

版权所有© 帮我毕业网 并保留所有权利

QQ 1370405256 微信 biyebang

QQ:629001810微信:biyebang

收缩