Delphi开发基于UDP的网络五子棋

Delphi开发基于UDP的网络五子棋

 

五子棋是一种家喻户晓的棋类游戏,它的多变吸引了无数的玩家。下面我来介绍我用Delphi6.0中文企业版制作的“网络五子棋”程序。

一、网络五子棋设计思想

先介绍一下程序原理。下棋需要有棋盘,程序中我们通过在窗体激活时在image1TCanvas对象上画出了五子棋棋盘。有了五子棋棋盘还需要有棋子。我使用了image1TCanvas对象上绘制圆形图案代表棋子。界面中要求用户选择作为主机还是联机,用server变量来保存。

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

0+从机IP

1+下的棋子位置坐标(x,y

2 (联机成功)

3+serverserver代表胜方)

4’(表示要求悔棋)

5+101表示同意,0表示不同意悔棋)

在下棋过程中,为了保存下过的棋子的位子使用了map数组,map数组初值为零,表示此处物棋子。map数组可以有12值,分别代表2种棋子图案,黑子,白子。为了显著表示对方刚下的棋子,这里我们用蓝子代表刚下过的黑子,红子代表刚下过的白子,其横纵坐标位置保存在old_qx,old_qy中。每下一步棋子,首先处理对(old_qx,old_qy)处对方刚下的棋子变色,将本步棋在棋盘上显示,并将本步棋的x,y坐标传出。每当接收到对方的下的棋子位置信息时,根据server判断画红子还是蓝子,并根据位置修改(old_qx,old_qy)。

本程序具有一定智能性,可以判断是否已经连成五子。程序中每下一步棋子,调用Gameover函数判断是否已经连成五子,如果返回非零,则说明已经连成五子,显示输赢结果。无论否已经连成五子,均将本步信息传到对方。Gameover函数中对棋盘的横竖斜方向均判断一遍,来达到检测出输赢。

  图片1

二、窗体界面和具体的代码:

unit Unit1;

 

interface

 

uses

  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

  Dialogs, ExtCtrls, StdCtrls, NMUDP, ComCtrls,

  winsock;

type

  TForm1 = class(TForm)

    Image1: TImage;

    Image2: TImage;

    Label1: TLabel;

    Label2: TLabel;

    GroupBox1: TGroupBox;

    RadioButton1: TRadioButton;

    RadioButton2: TRadioButton;

    GroupBox2: TGroupBox;

    Edit1: TEdit;

    NMUDP1: TNMUDP;

    StatusBar1: TStatusBar;

    Button1: TButton;

    Button2: TButton;

    Button3: TButton;

    function  gameover(a:integer):integer;

    procedure Image1MouseUp(Sender: TObject; Button: TMouseButton;

      Shift: TShiftState; X, Y: Integer);

    procedure RadioButton1Click(Sender: TObject);

    procedure RadioButton2Click(Sender: TObject);

    procedure NMUDP1DataReceived(Sender: TComponent; NumberBytes: Integer;

      FromIP: String; Port: Integer);

    procedure FormCreate(Sender: TObject);

    procedure Button1Click(Sender: TObject);

    procedure Button2Click(Sender: TObject);

    procedure Button3Click(Sender: TObject);

    procedure FormActivate(Sender: TObject);

  private

    { Private declarations }

  public

    { Public declarations }

  end;

 

var

  Form1: TForm1;

  map:array[1..15,1..15] of integer;

  server:integer;

  old_qx,old_qy:integer;

implementation

 

{$R *.dfm}

//uses winsock

//取本机IP地址

function GetIP:String;

var

 WSData: TWSAData;

 Buffer: array[0..63] of Char;

 HostEnt: PHostEnt;

 PPInAddr: ^PInAddr;

 IPString: String;

begin

 IPString:='';

 try

   WSAStartUp($101, WSData);

   GetHostName(Buffer, SizeOf(Buffer));

   HostEnt:=GetHostByName(Buffer);

   if Assigned(HostEnt) then

   begin

     PPInAddr:=@(PInAddr(HostEnt.H_Addr_List^));

     while Assigned(PPInAddr^) do

     begin

       IPString:=StrPas(INet_NToA(PPInAddr^^));

       Inc(PPInAddr);

     end;

   end;

   Result := IPString;

 finally

   try

     WSACleanUp;

   except

   end;

 end;

end;

 

function  TForm1.gameover(a:integer ):integer;//判断输赢

var i,j,s:integer;

begin

s:=0;

For i:= 1 To 11 do

  For j:= 1 To 11 do

  If (map[i,j] = a) and (map[i+1,j+1] = a) And (map[i+2,j+2] = a) And (map[i + 3,j + 3] = a )And (map[i + 4,j + 4] = a )Then  s:= a; // Exit Function

For i:= 5 To 15  do

  For j:= 1 To 11 do

  If (map[i, j] = a) And (map[i - 1, j + 1] = a )And (map[i - 2, j + 2] = a )And (map[i - 3, j + 3] = a) And (map[i - 4, j + 4] = a )Then     s:= a;

 

For i:= 1 To 15 do

  For j:= 5 To 15 do

  If (map[i, j] = a) And( map[i, j - 1] = a )And( map[i, j - 2] = a) And (map[i, j - 3] = a) And( map[i, j - 4] = a )Then   s:= a;

 

For i:= 5 To 11 do

  For j:= 1 To 15 do

  If (map[i, j] = a) And( map[i + 1, j] = a )And (map[i + 2, j] = a) And( map[i + 3, j] = a) And( map[i + 4, j] = a )Then   s:= a;

if s<>0 then

  Result:=s

else

  Result:=0;

End;

 

procedure TForm1.Image1MouseUp(Sender: TObject; Button: TMouseButton;

  Shift: TShiftState; X, Y: Integer);

var qx,qy:integer;

    buf:array[1..20] of char;

begin

    // label1.Caption :=inttostr(x-12)+'  '+inttostr(y-12);

     button3.Enabled :=true;

     x:=x-13;

     y:=y-13;

     qx:=trunc(x/21)+1;

     qy:=trunc(y/20)+1;

    // label2.Caption :=inttostr(qx)+'  '+inttostr(qy);

     map[qx,qy]:=server;

       //处理上一次的对方走的棋子颜色改变

      if server=1 then  //主机 黑方

       begin

        image1.Canvas.Brush.Color:=clWHITE;

        image1.Canvas.pen.Color:=clWHITE;

        image1.Canvas.Ellipse(old_qx*21-9,old_qy*20-9+1,old_qx*21+9,old_qy*20+9+1);

        old_qx:=qx;old_qy:=qy;

       end

       else

       begin

        image1.Canvas.Brush.Color:=clBlack;

        image1.Canvas.pen.Color:=clBlack;

        image1.Canvas.Ellipse(old_qx*21-9,old_qy*20-9+1,old_qx*21+9,old_qy*20+9+1);

        old_qx:=qx;old_qy:=qy;

       end;

     //处理本次的对方走的棋子

     if  server=1 then

     begin

     image1.Canvas.Brush.Color:=clBlack;

     image1.Canvas.pen.Color:=clBlack;

     image1.Canvas.Ellipse(qx*21-9,qy*20-9+1,qx*21+9,qy*20+9+1);

     end

     else

     begin

     image1.Canvas.Brush.Color:=clWHITE;

     image1.Canvas.pen.Color:=clWHITE;

     image1.Canvas.Ellipse(qx*21-9,qy*20-9+1,qx*21+9,qy*20+9+1);

     end;

 

     //继续传棋子信息

     begin

        buf[1]:='1';buf[2]:=CHR(qx); buf[3]:=CHR(qy);

        NMUDP1.SendBuffer(buf,20);

     end;

     image1.Enabled:=false;//下过一步后,禁止再走棋

 

     //判断输赢

     if gameover(server)=server then

     begin

        label1.caption:='你赢了';

        image1.Enabled:=false;

        buf[1]:='3';buf[2]:=CHR(server);

        NMUDP1.SendBuffer(buf,20);

        button1.Enabled:=true;

        button3.Enabled:=false; //悔棋按钮禁用

        image1.Enabled:=false;

        statusbar1.SimpleText:='你赢了,下棋结束,请重新开局';

     end;

 

end;

 

procedure TForm1.RadioButton1Click(Sender: TObject);

begin

    groupbox2.Visible:=false;

    NMUDP1.LocalPort:=5555;

    NMUDP1.RemotePort:=6666;

    server:=1;

    statusbar1.SimpleText:=getip+'正在等待联机';

end;

 

procedure TForm1.RadioButton2Click(Sender: TObject);

begin

     groupbox2.Visible:=true;

end;

 

procedure TForm1.NMUDP1DataReceived(Sender: TComponent;

  NumberBytes: Integer; FromIP: String; Port: Integer);

var qx,qy,i:integer;

    buf:array[1..20] of char; str:string;

begin

    button3.Enabled:=false; //悔棋按钮禁用

    image1.Enabled:=true;

    NMUDP1.ReadBuffer(buf,NumberBytes);

    case buf[1] of

    '0': //获取对方从机IP

    begin

      i:=2;

      while(buf[i]<>#0)do

      begin

      str:=str+buf[i];i:=i+1;end;

      NMUDP1.RemoteHost:=str;

      buf[1]:='2';

      NMUDP1.SendBuffer(buf,20);

      statusbar1.SimpleText:='连接成功,请先走棋';

    end;

    '1':  //对方走棋信息

    begin

       qx:=ord(buf[2]);

       qy:=ord(buf[3]);

       old_qx:=qx;old_qy:=qy;

     //处理本次的对方走的棋子

      if  server=1 then

       begin

        image1.Canvas.Brush.Color:=clBLUE;

        image1.Canvas.pen.Color:=clBLUE;

        image1.Canvas.Ellipse(qx*21-9,qy*20-9+1,qx*21+9,qy*20+9+1);

       end

      else

       begin

        image1.Canvas.Brush.Color:=clRED;

        image1.Canvas.pen.Color:=clRED;

        image1.Canvas.Ellipse(qx*21-9,qy*20-9+1,qx*21+9,qy*20+9+1);

       end;

    statusbar1.SimpleText:='正在下棋中';

    end;

    '2':  //主机发给从机的连接成功信息

     begin

         statusbar1.SimpleText:='连接主机成功,等待对方走棋';

     end;

    '3': //结束信息

     begin

       image1.Enabled:=false;

        button1.Enabled:=true;

        if  ord(buf[2])=server then

         label1.caption:='你赢了'

        else

         label1.caption:='你输了';

     statusbar1.SimpleText:='下棋结束,请重新开局';

     end;

     '4'://悔棋处理

     begin

       if MessageDlg('对方要求悔棋,同意吗?',mtConfirmation, [mbYes, mbNo], 0) = mrYes then

        begin  //同意

           image1.Canvas.Brush.Color:=image1.Canvas.Pixels[2,2]; //取棋盘颜色

           image1.Canvas.pen.Color:=image1.Canvas.Pixels[2,2];

           image1.Canvas.Ellipse(old_qx*21-9,old_qy*20-9+1,old_qx*21+9,old_qy*20+9+1);

          buf[1]:='5';

          buf[2]:='1';

          NMUDP1.SendBuffer(buf,20);

 

        end

      else

        begin  //不同意

          buf[1]:='5';

          buf[2]:='0';

          NMUDP1.SendBuffer(buf,20);

        end

     end;

     '5':

     begin

        if  buf[2]='1' then //同意

          begin

           image1.Canvas.Brush.Color:=image1.Canvas.Pixels[2,2]; //取棋盘颜色

           image1.Canvas.pen.Color:=image1.Canvas.Pixels[2,2];

           image1.Canvas.Ellipse(old_qx*21-9,old_qy*20-9+1,old_qx*21+9,old_qy*20+9+1);

           old_qx:=-1;old_qy:=-1;

          end

        else

           statusbar1.SimpleText:='对方不同意悔棋';

     end

   end; //case end

 

end;

 

procedure TForm1.FormCreate(Sender: TObject);

begin

     old_qx:=-1;//上一次对方棋子位置

     old_qy:=-1;

end;

 

procedure TForm1.Button1Click(Sender: TObject); //重新开局

var i,j:integer;

begin

 

    For i:= 1 To 14 do

     For j:= 1 To 14 do

     begin

        image1.Canvas.Brush.Color:=image1.Canvas.Pixels[2,2]; //取棋盘颜色

        image1.Canvas.pen.Color:=image1.Canvas.Pixels[2,2];

        image1.Canvas.Ellipse(i*21-9,j*20-9+1,i*21+9,j*20+9+1);

        map[i,j]:=0;

     end;

    // label1.caption:=ColorToString(image1.Canvas.Pixels[2,2]); //TColor;

     RadioButton1.Checked:=false;

     RadioButton2.Checked:=false;

     old_qx:=-1;//上一次对方棋子位置

     old_qy:=-1;

     statusbar1.SimpleText:='重新开局,选择主机或从机';

     label1.Caption:='新局'

end;

 

procedure TForm1.Button2Click(Sender: TObject); //连接主机

var    buf:array[0..20] of char;  //1..20 error

       str1:string;

begin

     NMUDP1.LocalPort:=6666;

     NMUDP1.RemotePort:=5555;

     NMUDP1.RemoteHost:=edit1.text; //主机的IP

     server:=2;

     str1:='0'+getip;

     StrPCopy(buf,str1);

     NMUDP1.SendBuffer(buf,20); //  send 本机的IP

     GroupBox2.Visible:=false;

    // image1.Enabled:=true;

 

end;

 

procedure TForm1.Button3Click(Sender: TObject);

var buf:array[1..20] of char;

begin

    buf[1]:='4';

    NMUDP1.SendBuffer(buf,20);

    statusbar1.SimpleText:='你要求悔棋了';

    Button3.Enabled:=false;

end;

 

procedure TForm1.FormActivate(Sender: TObject);

var i:integer;

begin

     image1.Canvas.Brush.Color:=$0078DDEF; //棋盘颜色

     image1.Canvas.Rectangle(0,0,14*21+20,14*20+20);

     for i:=0 to 14 do

     begin     //横向

     image1.Canvas.Brush.Color:=clBLACK; //棋盘颜色

     image1.Canvas.pen.Color:=clBLACK;

     image1.Canvas.moveto(10,10+20*i);

     image1.Canvas.lineto(14*21+10,10+20*i);

     image1.Canvas.Brush.Color:=clBLACK; //纵向

     image1.Canvas.pen.Color:=clBLACK;

     image1.Canvas.moveto(10+21*i,10);

     image1.Canvas.lineto(10+21*i,14*20+10);

     end;

end;

end.

以上代码在Delphi6+XP通过调试。如果你只有一台电脑,可以把这个项目编译成EXE文件,并且运行两个实例,一个“作为主机”,另一个作为“加入连机棋局”,便可以和自己对弈了。如果检测出输赢,此后就必须“重新开局”进入下一盘。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值