鸿 网 互 联 www.68idc.cn

当前位置 : 服务器租用 > 编程语言开发 > erlang > >

基于CSock的远程五子棋对弈程序

来源:互联网 作者:佚名 时间:2016-07-17 21:12
一、程序说明 基于CSock的远程五子棋对弈程序设计 (1)按Socket异步网络通信方式设计具有C/S模式的数据传输模块; (2)在服务器与客户端各设计一个五子棋棋盘,用于博弈。 (3)博弈开始双方选定黑白棋子,按规则下子,两端同时显示棋局状态,并计时, 超

一、程序说明
基于CSock的远程五子棋对弈程序设计
(1)按Socket异步网络通信方式设计具有C/S模式的数据传输模块;
(2)在服务器与客户端各设计一个五子棋棋盘,用于博弈。
(3)博弈开始双方选定黑白棋子,按规则下子,两端同时显示棋局状态,并计时,
超时者判负。
(4)当任何方向上某一色棋子连成五个,判定该方赢棋。

二、程序界面
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

三、 程序分析
1.设计思想
(1)五子棋软件即可作为服务器端也可以作为客户端,需要在程序中同时顾及软件作
为服务器或客户端的两种情况。
(2)当五子棋作为服务器开启时,就开始在某个端口(本系统采用了1234端口)下监听客户端的连接请求,再开启另一个五子棋软件,输入服务器的IP地址并请求连接,当服务器响应客户端请求时,就可以开始游戏了。
(3)程序进行CSocket来进行数据交流,传递落子坐标。
(4)设计各种自定义算法,如棋盘棋子绘制、落子距离计算、胜负判断、状态变化等。

2.系统设计步骤
(1)程序基于Visual Studio 2010开发,以“MFC应用程序”作为程序开发模板。利用Windwos平台的消息驱动机制,设定当用户执行了某些操作(如点击按钮)时则触发特定函数。
(2)系统的使用需要同时具备服务器与客户端两个角色,服务器要先于客户端开启。
(3)开启五子棋软件,将其设为服务器。服务器端建立一个CMySocket继承于CSocket,增添自定义变量:关联窗口指针(CWnd *pWnd;),自定义函数:窗口绑定函数(void AttachCWnd(CWnd *pW);),以适应本系统实际情况。Create一个CMySocket对象在某个指定端口下(如本系统使用的1234端口)监听(listen)是否有客户端程序请求连接,套接字类型采用流式套接字(SOCK_STREAM)。调用accept阻塞,等待客户端连接。在这时如果有另一个五子棋软件作为客户端初始化一个Socket请求连接(connect)服务器,服务器对该请求进行应答。如果连接成功,则客户端与服务器端的连接就建立了。
(4)当两个玩家之间(服务器与客户端之间)的连接建立起来后,就可以开始游戏了。程序通过m_status来表示当前软件是作为服务器或客户端而存在。自定义结构体Point来存储玩家的落子坐标,然后利用SOCKET来传递给另一位玩家。此外,因为当玩家落子后,需要等到另一位玩家传递来落子坐标后才可以再次落子,通过canPlayChess来控制这种变化。

//用来标识类型,服务器、未指定、客户端
enum status{SERVER=-1,INITIAL=0,CLIENT=1};
//标明当前状态是客户端还是服务器端,服务器=-1,客户端=1,未指定=0
enum status m_status;
//落子坐标
    struct Point{
        int x;
        int y;
    };
//标识当前是否能落子
BOOL canPlayChess;

(5)自定义CSokcet类的以下虚函数,用来处理当“客户端请求连接,玩家关闭游戏、接收消息”等动作。

virtual void OnAccept(int nErrorCode);
virtual void OnClose(int nErrorCode);
virtual void OnReceive(int nErrorCode);

例如当有一方玩家关闭游戏或连接断开时,则
void CMySocket::OnClose(int nErrorCode)
函数将被触发,另一方玩家将弹出对话框进行提示。

四、 系统的具体实现
1. 本系统在Windows8.1系统下基于Visual Studio 2010开发,系统既可以作为服务器与也可以充当客户端。
2. 界面绘制模块:
界面的绘制主要包括窗口界面设置、棋盘绘制、棋子绘制等。主要运用的是CDC。

绘制棋盘代码:

CDC *cdc=GetDC();
CPen di;
di.CreatePen(PS_SOLID,1.3,RGB(0,0,0));
cdc->SelectObject(&di);
//画棋盘横线
for(int i=0;i<ROW;i++)
{   
    cdc->MoveTo(WIDTH,WIDTH+WIDTH*i);
    cdc->LineTo(WIDTH*ROW,WIDTH+WIDTH*i);
}
//画棋盘竖线
for(int j=0;j<ROW;j++)
{
    cdc->MoveTo(WIDTH+WIDTH*j,WIDTH);
    cdc->LineTo(WIDTH+WIDTH*j,WIDTH*ROW);
}

添加黄色背景代码:

    CPaintDC dc(this);
    CDC dcMem;
    dcMem.CreateCompatibleDC(&dc);
    CBitmap bmpBackground;
    bmpBackground.LoadBitmap(IDB_BACK);
    BITMAP bitmap;
    bmpBackground.GetBitmap(&bitmap);
    CBitmap* pbmpPri = dcMem.SelectObject(&bmpBackground);
    dc.StretchBlt(WIDTH/2,WIDTH/2,WIDTH*(ROW-1)+WIDTH, WIDTH*(ROW-1)+WIDTH, &dcMem,0,0,bitmap.bmWidth, bitmap.bmHeight, SRCCOPY);

棋子绘制代码:

void CgameDlg::drawChessPiece(double a,double b,int m_status)
{
        int x=(int)a+1;
        int y=(int)b+1;
        double width=WIDTH/2-2;
        CDC *cdc=GetDC();
        CPen di;
        di.CreatePen(PS_SOLID,1,RGB(0,0,0));
        cdc->SelectObject(&di);//定义画笔
        CBrush brush;
       //如果当前状态是服务器,则画黑子
    if(m_status==SERVER)
        {
        brush.CreateSolidBrush(RGB(0,0,0));
    }else{
        //当前状态是客户端,则画白子
        brush.CreateSolidBrush(RGB(255,255,255));
    }
    cdc->SelectObject(&brush);  
    cdc->Ellipse(x*WIDTH-width,y*WIDTH-width,x*WIDTH+width,y*WIDTH+width;
    Points[(int)a][(int)b]=m_status;
}

3.网络设置模块:
下图为网络设置界面。当用户选中“作为服务器”按钮并点击确定后,将Create一个CMySocket对象在1234端口下监听(listen)是否有客户端程序请求连接,套接字类型采用流式套接字(SOCK_STREAM)。调用accept阻塞,等待客户端连接。
这时如果再开启另一个五子棋软件,并选中“作为客户端”按钮,输入服务器的IP地址,点击确定,客户端将初始化一个Socket请求连接(connect)服务器,服务器对该请求进行应答。如果连接成功,则客户端与服务器端的连接就建立了。

void CgameDlg::OnBnClickedSet()
{
    // TODO: 在此添加控件通知处理程序代码
    CSet cset;
    //如果没有按确认键则退出程序
    if(cset.DoModal() != IDOK)      
    {
        return;
    }
    //作为服务器
    if(!cset.isServer)
    {
         if(IDOK==AfxMessageBox("确定开始侦听客户端的连接请求吗",MB_OKCANCEL))
          {
                m_socket.AttachCWnd(this);
                //端口1234,流式套接字
        BOOL isSuccess=m_socket.Create(1234,SOCK_STREAM);
                if(isSuccess){
                    m_socket.Listen();
                    MessageBox("已开始侦听");
                    SetWindowText("五子棋——正在等待客户端连接");
                    return;
                }
                WORD error=m_socket.GetLastError();
                CString s;
                s.Format("服务器开启失败\r\n错误代代码:%d",error);
                MessageBox(s);
            }   
    }else{
        //作为客户端
        if(IDOK==AfxMessageBox("确定连接到服务器吗",MB_OKCANCEL))
        {
                SetWindowText("五子棋——正在连接服务器");
                CString msg;
                DWORD error;
                m_socket.AttachCWnd(this);
                if(!m_socket.Create())
               {
                    error = GetLastError();
                    msg.Format("创建Socket失败\r\n错误代号:%d",error);
                    goto msgbox;
            }
            //连接服务器
            if(!m_socket.Connect(cset.m_ip,1234))
               {
                    error = GetLastError();
                       msg.Format("连接服务器失败\r\n错误代号:%d",error);
msgbox:
                      MessageBox(msg);
                      return;
            }
            m_status=CLIENT;
            MessageBox("已连接好友,可以开始游戏啦!");
            SetWindowText("五子棋(白子)——等待对方下子");
            canPlayChess=false;
            //开启定时任务
            SetTimer(1,1000,NULL);
            m_hint="对方剩余思考时间:";
        }
    }
}

4.网络通讯模块:
当用户鼠标点击时,将触发OnLButtonDown(UINT nFlags, CPoint point)函数,由参数point可以得到鼠标点击的X轴和Y轴坐标值。然后再来根据judge(double &x,double &y)函数来获取该坐标值该对应棋盘中的哪一个点,在该点绘制棋子,然后再向另一位玩家发送该坐标信息,或者是判定该点击无效。

void CgameDlg::OnLButtonDown(UINT nFlags, CPoint point)
{
    // TODO: 在此添加消息处理程序代码和/或调用默认值
    if(!canPlayChess || m_status==INITIAL)
         {
                return;
    }
         long x=point.x;
         long y=point.y;
         if(x>=WIDTH && x<=WIDTH*ROW && y>=WIDTH && y<=WIDTH*ROW)
         {
                double a=x/(WIDTH*1.0)-1;
                  double b=y/(WIDTH*1.0)-1;
                  if(judge(a,b))
        {   
                        if(Points[(int)a][(int)b]!=INITIAL)
                           {
                                   return;
                            }
                           drawChessPiece(a,b,m_status);
                           point.x=a;
                            point.y=b;
                            if(m_status==SERVER)
                            {
                                  if(!sc->Send((char *)&point,sizeof(point)))
                                  {
                                              MessageBox("发送数据失败");
                                              return;
                                    }
                            }else if(m_status==CLIENT){
                                    if(!m_socket.Send((char *)&point,sizeof(point)))
                                     {
                                            MessageBox("发送数据失败");
                                            return;
                                    }
                              }
                            if(!isWin(m_status))
                              {
                                 if(m_status==SERVER)
                {
                        SetWindowText("五子棋(黑子)——等待对方下子");
                }else{
                         SetWindowText("五子棋(白子)——等待对方下子");
                }
                canPlayChess=false;
                m_hint="对方剩余思考时间:";
                THINK_TIME=31;
            }
        }
    }
}

5.倒计时模块
用于设置一个定时任务,动态改变一个静态文本的内容,从而实现倒计时的效果。在需要的时候可以调用KillTimer(1);取消定时任务。

void CgameDlg::OnTimer(UINT_PTR nIDEvent)
{
    // TODO: 在此添加消息处理程序代码和/或调用默认值
    if(nIDEvent==1)
       {
            THINK_TIME--;
            if(THINK_TIME==0)
            { 
                 if(m_status==SERVER)
                 {
                        if(canPlayChess)
                        {
                             SetWindowText("五子棋(黑子)——思考时间超时,对方获胜");
                                }else{
                              SetWindowText("五子棋(黑子)——对方思考时间超时,你已获胜");
                            }
                   }else{
                        if(canPlayChess)
                            {
                                  SetWindowText("五子棋(白子)——思考时间超时,对方获胜");
                            }else{
                             SetWindowText("五子棋(白子)——对方思考时间超时,你已获胜");
                            }
                    }
                    canPlayChess=false;
                    KillTimer(1);
        }
            m_thinkTime.Format("%d",THINK_TIME);
              UpdateData(FALSE);
    }   
  DialogEx::OnTimer(nIDEvent);
}

代码下载地址,包括实验报告:基于CSock的远程五子棋对弈程序

网友评论
<