10天开发的两国军旗

    技术2022-05-11  82

    两国军旗

    一、界面设计

    下棋需要有棋盘,程序中通过在窗体激活时在图片框控件qi_pan上显示军旗棋盘图片文件。有了盘还需要有棋子,使用了以下如图5-6,图5-7中的图片表示棋子。另外还有R.bmpG.bmp两个图片表示红方和绿方暗子。使用控件数组Qizi_Pic[]Add方法加载了我们所需要的棋子图片控件并使用相应图片。Qizi_Pic[]中下标i的含义是:如果i小于24,那么说明它属于红方的棋子,否则是绿方的棋子。同时Qizi_Pic[i]tag属性保存了棋子在控件数组的索引号。

    5-6  红方军棋棋子图片

    5-7  绿方军棋棋子图片

    在界面中要求用户输入对方IP、端口(本地及对方远端)。同时按图5-8添加“联机”、“重新开始”、“退出”、“保存布阵”、“读取布阵”、“开始对战”6个按钮以及一些题使用的标签。

         

    5-8  网络两人对战军棋设计界面

    二、通讯协议设计

    网络程序设计的难点在于与对方需要通讯。这里使用了UDP(User Data Protocol)UDP是用户数据文报协议的简称,两台计算机之间的传输类似于传递邮件;两台之间没有明确的连接,使用UDP协议建立对等通信。这里虽然两者两台计算机不分主次,但我们设计时假设一台做主机(红方),等待其他人加入。其他人想加入的时候输入主机的IP。为了区分通信中传送的是“输赢信息”,“下的棋子位置信息”,“重新开始”等,在发送信息的首部加上代号。定义了如下协议:

    命令|参数|参数……

    1)联机功能

    join|

    2)对方棋子移动信息

    move|x , y , idx, old_x, old_y,old_idx

    其中:棋子移动的目标位置坐标(x,y),棋子移动的起始位置坐标(old_x,old_y);

    old_idx是被移动棋子的控件数组索引号,

    idx是目标位置留下的棋子的控件数组索引号。

    3)游戏结束

    over|+赢方代号 (赢了此局)

    4)表示要重新开始

    reset|

    5)布阵信息

    layout|布阵棋子信息

    布阵棋子信息形式为x,y,idx,x,y,idx,x,y,idx……

    idx是(x,y)处的棋子的控件数组索引号。

    注意本程序在传递布阵信息时,我们默认了是绿方在棋盘上方,红方在下。但实际下棋如果棋手使用绿方,这样看非累死不可。所以这里我们采取了个小技巧,在发送布阵数据的时候我们把坐标在颠倒(把自己的棋盘颠倒)。即(x,y)坐标以(18-x)(18-y) 坐标发给对方。

    在下棋过程中,为了保存下过的棋子的位置使用了Map数组,Map数组初值为101,表示此处无棋子。Map数组可以有049值,其中024代表红方棋子;2549代表绿方棋子。

    关于024的棋子含义如下在qi_index()规定:

                  private void qi_index()

                  {

                         Q=new int[25];

                         Q[0]=29; //军旗29

                         Q[1]=30;Q[2]=30;Q[3]=30;//地雷30

                      Q[4]=31;Q[5]=31;//炸弹31

                         Q[6]=32;Q[7]=32;Q[8]=32;//工兵32

                         Q[9]=33;Q[10]=33;Q[11]=33;//排长33

                         Q[12]=34;Q[13]=34;Q[14]=34;//连长34

                         Q[15]=35;Q[16]=35;//营长35

                         Q[17]=36;Q[18]=36;//团长36

                         Q[19]=37;Q[20]=37;//旅长37

                         Q[21]=38;Q[22]=38;//师长38

                         Q[23]=39;//军长39

                         Q[24]=40;//司令40

                  }

    也即是0军旗,1—3地雷,4—5炸弹,6—8工兵,9—11排长,12—14连长,15—16营长,17—18团长,19-20旅长,21—22师长,23军长,24司令。

    Q数组中Q[24]=40而不直接写为“司令”,原因是棋子图片的命名时采用40.bmp

    所以为显示图片时的方便,这里Q[24]=40

    2549代表绿方棋子。绿方只要减去25(每方25个棋子)即可利用Q数组得知绿方棋子的含义。例如27代表的绿方地雷。

    三、走棋规则设计

    对于军旗游戏来说,规则非常简单,就是按照先后顺序在棋盘上走棋吃子,直到最先一方将对方的“军旗”挖掉为胜。

    但在走棋过程中,需要考虑以下情况:

    1)是否为非棋子区;

    2)目标处是否是自己的棋子;

    3)判断目标是否实行营,如果是则需判断是否有子,如果没有可以如“士”一样斜线走棋。

    4)判断起始位置是否是铁道线,如果是则考虑弯道、直道、棋盘正中间的3*3“田子”,否则只能移动一步。

    5)“军旗”棋子、地雷棋子不能走动;

    6)在“大本营”中的棋子不能走动;

    可以看出走棋规则实行比较复杂。在设计时Go_Juge(int old_x,int old_y,int x,int y)判断走棋的位置是否适当。

           private bool Go_Juge(int old_x,int old_y,int x,int y)

           {

                         //是否是棋子区域

                         if( (x<=6 && y>=1 && y<=6) || (x>=12 && y>=1 && y<=6) ||

                                (x<=6 && y>=12 && y<=17) || (x>=12 && y>=12 && y<=17)|| y>17 )

                                return false;

                         //目标位置是自己方的棋子

                         if(IsmyChess(x,y)) return false;

                         //到行营,行营是否有子

                         if(Is_Home( x, y)&& Map[x,y]!=101)return false;

                         //如“士”斜线从行营中出来**********

                         if(Is_Home( old_x, old_y)&& Map[x,y]==101&& Math.Abs(x-old_x)*Math.Abs(y-old_y)==1 )return true;

                         //如“士”斜线走入行营************

                         if(Is_Home( x, y)&& Map[x,y]==101 && Math.Abs(x-old_x)*Math.Abs(y-old_y)==1 )return true;

                         //移动一步

                         if( Math.Abs(x-old_x)==1 && y==old_y ||Math.Abs(y-old_y)==1 && x==old_x) return  true;

                         //铁道线

                         if(T_Juge(old_x,old_y,x,y))   return true;                  

                         return false;          

                  }

    本程序中go_chess(int old_x,int old_y,int x,int y,int idx)完成走棋吃子功能,其中判断以下几种情况:

    1)双发均为工兵到司令的棋子,则按大小比较决定保留那方棋子;

    2)其中一方式炸弹(31),则同时去掉。

    3)其中一方是地雷(30),对方为工兵,则留兵,否则留雷。

    4)其中一方是军旗(29),则可以判断输赢。

    注意:在发送走棋数据的时候我们仍然需要把坐标在颠倒(把自己的棋盘颠倒)。即(x,y)坐标以(18-x)(18-y) 坐标发给对方。

    四、布阵规则设计

    注意布阵是采用两次单击不同棋子来决定对调的,所以(old_x, old_y)是第一次单击的棋子坐标,(x1y1)是第二次单击的棋子坐标。

    判断布局棋子的位置是否适当是使用Layout_Juge(int old_x,int old_y,int x1,int y1)实现以下情况判断:

    1)第一排不允许放置炸弹;由于布阵均在南方,所以第一排即(y1=12)

    2)自己的军旗只能放置在大本营;由于布阵均在南方,所以大本营即x1==8&&y1==17||x1==10&&y1==17

    3)后两排允许放置地雷。由于布阵均在南方,后两排即y1==16||y1==17

    具体实现如下:

           //炸弹控件编号45,第一排(y1=12)不允许放置炸弹

           if(Q[Map[old_x,old_y]%]==31 && y1==12)   return false;

           if(Q[Map[x1,y1]%]==31 && old_y==12)      return false;                  if(Q[Map[old_x,old_y]%]==29 && !(x1==8&&y1==17||x1==10&&y1==17))

                  return false;//自己的军旗控件,只能放置在大本营

           if(Q[Map[x1,y1]%]==29 && !(old_x==8&&old_y==17||old_x==10&&old_y==17))

                  return false;//自己的军旗控件,只能放置在大本营

           if(Q[Map[x1,y1]%]==30 && !(old_y==16||old_y==17))

                  return false;//1234排不允许放置地雷,

           if(Q[Map[old_x,old_y]%]==30 && !(y1==16||y1==17))

                  return false;//1234排不允许放置地雷,      

           return true;//其余情况均可以

     

    五、通讯过程        游戏开始后,创建一个线程th:        th = new Thread ( new ThreadStart ( read ) ) ;        th_flag=true;        th.Start ( )启动线程后,通过read ( )实现不断侦听本机设定的端口,得到对方发送来的信息,根据自己定义的通信协议通信中传送的是“输赢信息”,“下的棋子位置信息”,“重新开始”等信息而分别处理。               private void read ( )               {                      //侦听本地的端口号                      udpclient = new UdpClient ( Convert.ToInt32(txt_port.Text) ) ;                      remote = null ;                      //设定编码类型                      Encoding enc = Encoding.Unicode ;                      int x,y,old_x,old_y,idx,old_idx;                      while ( ReadFlag == true )             //一个While(ture)循环,不断判断是否有信息流入,有就接收                      {                             try{                             Byte[] data = udpclient.Receive ( ref remote ) ;                             //得到对方发送来的信息                             String strData = enc.GetString ( data ) ;                             string []a=new string[5];                             a=strData.Split('|');                             switch(a[0])                             {                          case "join":                          case "begin_layout"://允许对方布阵                          case "layout"://布阵信息                          case "move"://对方棋子移动信息                          case "over":                          case "reset":                             }                             }                             catch{                                    //退出循环,结束线程                                    break;                             }                      }               } 发送信息send(string info)较为简单,主要实现创建UDP网络服务,传送信息到指定计算机的txt_remoteport端口号后,关闭UDP网络服务。               private void send(string info)               {                      //创建UDP网络服务                      UdpClient SendUdp = new UdpClient ( ) ;                      IPAddress remoteIP ;                      //判断IP地址的正确性                      try{                             remoteIP = IPAddress.Parse ( txt_IP.Text );                      }                      catch{                             MessageBox.Show ( "请输入正确的IP地址!" , "错误" ) ;                             return ;                      }                      IPEndPoint remoteep = new IPEndPoint ( remoteIP , Convert.ToInt32 (txt_remoteport.Text )) ;                      Byte [] buffer = null ;                      Encoding enc = Encoding.Unicode ;                      string str = info ;                      buffer = enc.GetBytes ( str.ToCharArray ( ) ) ;                      //传送信息到指定计算机的txt_remoteport端口号                      SendUdp.Send ( buffer , buffer.Length , remoteep ) ;                      //关闭UDP网络服务                      SendUdp.Close ( ) ;               }

    最新回复(0)