欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

用C++和Socket开发的本地网络象棋游戏(基于Win32界面,采用CodeBlocks与GCC编译器实现)

最编程 2024-07-19 20:25:52
...

 目录

成果

运行效果图

过程

1. 首先的问题是下棋的两端应该是什么样的?

  2. 接下来的问题是怎么表示,怎么存储?

3. 然后应该怎么通信呢?

代码

main.cpp

chinese_chess.h

Server.h

Client.h

    END


 

 

成果

运行效果图

左边是在虚拟机里运行的,右边是在Host机上运行的。

最新更改后的界面:

 

过程

记不起自己为什么要写这个象棋游戏的,大概是因为刚学了点儿Socket ,所以想用用,于是就写个局域网对战的象棋吧。。。

1. 首先的问题是下棋的两端应该是什么样的?

 

我希望下棋的两个人使用相同的程序。所以就不能像FTP一样,一个客户端,一个服务器端,而只能每个程序都是一样的,既是客户端(Client),又是服务器端(Server)。在通信时,己方的Client 向对方的Server 发送信息,对方的Client 向己方的Server 发送信息。两端都存储棋盘信息,通过通信保持棋盘信息的一致。

然后呢,应该是一端点击界面移子之后,应该能通知对方进行相同的移动。

综合以上两点,运行过程应该是这样的:

当在界面上点击棋子时,先判断当前是否轮到自己落子,如果是,则进行移动,更新界面,并通过Client 向对方Server 发送移动信息。对方Server 收到后,进行同样的移动,更新界面。

这里要求Server能随时接到对方发来的消息,所以Server的监听应该是一个额外的线程。

2. 接下来的问题是怎么表示,怎么存储?

棋盘,应该用二维数组存储比较好,数组坐标(以下所说的"数组坐标 "是指该二维数组中的一个(x,y)数对)对应棋盘坐标。那么数组里存储什么呢,一共有車、马、象、士、将、砲、卒七种棋子,那么设置一个棋子类作为基类,然后设置七个类继承棋子类?基类有一个move函数,每个子类重写该函数?但是移动似乎只是我程序的一小部分,这样似乎没必要。

那么存储整型数值?不同的数值代表不同的棋子?似乎可以。

那么就用7个数代替七种棋子,但是棋子有黑白色,要用一个数表示棋子类型(即是車、马或其他)和棋子颜色两个信息,那就用BLANK =8代表空子,黑方的車、马、象、士、将、砲、卒分别为1到7,白方的車、马、相、士、帅、炮、兵分别为9到15。

这样判断某数组坐标上棋子的颜色,就把其值与BLANK 比较,大于BLANK为白色,否则为黑色。

判断某数组坐标上棋子的类型,则将其值模BLANK 

另外,因为下棋双方的视角是相反的,所以,棋盘在存储时应该是相反的,移动时的坐标也应该进行转换。

3. 然后应该怎么通信呢?

我希望这个程序打开后,就能找到对方,并确定谁是黑色,谁是白色。

也许可以让Client 在运行之后就对局域网进行端口扫描,然后给出正在运行此程序的IP 地址列表,让用户选择要连接到哪个,如果对方已经有了连接,则对方会拒绝此连接,如果对方没有连接,则对方程序会向对方用户提示该连接请求,如果,对方用户同意,则连接建立,否则依然是拒绝此连接。

但是,我没有采用以上所述方法(因为太复杂,我还是先做好主体工作吧=_=)。

所以在程序开始运行后,会让用户输入对方的IP 地址,然后Server 开始监听。之后Client 开始向对方发出连接请求。

Server 监听时,如果收到连接请求,就看对方的IP 地址是否是用户输入的IP 地址,如果不是,说明连接请求不是用户所希望的对方发送的,那就继续监听。

Client 请求连接时,如果对方同意了,就要开始确定自己的颜色了。

确定颜色这里困扰了我很久,最后采用的解决方法是这样的:

核心思想就是谁先发出连接请求,谁就是黑色。

也就是在Client 连接上对方之后,要判断Server 是不是已经连接了对方,如果Server 已连接,就说明是对方先发出的连接请求,那么对方就是黑色,自己就设为白色。如果Server 没有连接,就说明自己先连接上了对方,也就是自己是黑色。

 

以上就是编码前及编码时的大致想法。

 

代码

注: 用 CodeBlocks 编译时若出现类似" undefined reference to `send@16' " 的错误,在Settings->Complier->Global Complier Settings->Linker Settings 中添加 C:\Program Files (x86)\CodeBlocks\MinGW\lib\libwsock32.a

main.cpp

 

  1 #if defined(UNICODE) && !defined(_UNICODE)
  2 #define _UNICODE
  3 #elif defined(_UNICODE) && !defined(UNICODE)
  4 #define UNICODE
  5 #endif
  6 
  7 #include <tchar.h>
  8 #include <windows.h>
  9 #include <pthread.h>
 10 #include <windowsx.h>
 11 #include "chinese_chess.h"
 12 #include "Server.h"
 13 #include "Client.h"
 14 
 15 
 16 #define WIDTH 600       //界面宽度
 17 #define HEIGHT 600      //界面高度
 18 #define ZERO_X 70       //棋盘左边界
 19 #define ZERO_Y 70       //棋盘上边界
 20 #define PIECE_BKCOLOR    RGB(195,163,109)   //棋子背景色
 21 #define PIECE_WH 45        //棋盘每个格子的宽度和高度
 22 
 23 HWND hwnd;               /* This is the handle for our window */
 24 char* ots_ip;           //存储对方IP地址的字符串
 25 int port;
 26 bool is_connect_alive=false;        //是否连接到对方
 27 Board * chess_board;          //棋盘
 28 Server *server;
 29 Client *client;
 30 int chess_sx=-1;        //移动起始位置的数组坐标
 31 int chess_sy=-1;
 32 int chess_dx=-1;        //移动目标位置的数组坐标
 33 int chess_dy=-1;
 34 
 35 
 36 
 37 
 38 /*  Declare Windows procedure  */
 39 LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);
 40 
 41 /*  Make the class name into a global variable  */
 42 TCHAR szClassName[ ] = _T("Chinese Chess");
 43 
 44 int WINAPI WinMain (HINSTANCE hThisInstance,
 45                     HINSTANCE hPrevInstance,
 46                     LPSTR lpszArgument,
 47                     int nCmdShow) {
 48     MSG messages;            /* Here messages to the application are saved */
 49     WNDCLASSEX wincl;        /* Data structure for the windowclass */
 50 
 51     /* The Window structure */
 52     wincl.hInstance = hThisInstance;
 53     wincl.lpszClassName = szClassName;
 54     wincl.lpfnWndProc = WindowProcedure;      /* This function is called by windows */
 55     wincl.style = CS_DBLCLKS;                 /* Catch double-clicks */
 56     wincl.cbSize = sizeof (WNDCLASSEX);
 57 
 58     /* Use default icon and mouse-pointer */
 59     wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
 60     wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
 61     wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
 62     wincl.lpszMenuName = NULL;                 /* No menu */
 63     wincl.cbClsExtra = 0;                      /* No extra bytes after the window class */
 64     wincl.cbWndExtra = 0;                      /* structure or the window instance */
 65     /* Use Windows's default colour as the background of the window */
 66     wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;
 67 
 68     /* Register the window class, and if it fails quit the program */
 69     if (!RegisterClassEx (&wincl))
 70         return 0;
 71 
 72     /* The class is registered, let's create the program*/
 73     hwnd = CreateWindowEx (
 74                0,                   /* Extended possibilites for variation */
 75                szClassName,         /* Classname */
 76                _T("Chinese Chess"),       /* Title Text */
 77                WS_OVERLAPPEDWINDOW, /* default window */
 78                CW_USEDEFAULT,       /* Windows decides the position */
 79                CW_USEDEFAULT,       /* where the window ends up on the screen */
 80                WIDTH,                 /* The programs width */
 81                HEIGHT,                 /* and height in pixels */
 82                HWND_DESKTOP,        /* The window is a child-window to desktop */
 83                NULL,                /* No menu */
 84                hThisInstance,       /* Program Instance handler */
 85                NULL                 /* No Window Creation data */
 86            );
 87 
 88     /* Make the window visible on the screen */
 89     ShowWindow (hwnd, nCmdShow);
 90 
 91     /* Run the message loop. It will run until GetMessage() returns 0 */
 92     while (GetMessage (&messages, NULL, 0, 0)) {
 93         /* Translate virtual-key messages into character messages */
 94         TranslateMessage(&messages);
 95         /* Send message to WindowProcedure */
 96         DispatchMessage(&messages);
 97     }
 98 
 99     /* The program return-value is 0 - The value that PostQuitMessage() gave */
100     return messages.wParam;
101 }
102 //把数组坐标转换为界面坐标
103 void xy_to_pixel(int x,int y,int*pixelx,int *pixely) {
104     *pixely=x*PIECE_WH+ZERO_Y;
105     *pixelx=y*PIECE_WH+ZERO_X;
106 }
107 //把界面坐标转换为数组坐标
108 void pixel_to_xy(int pixelx,int pixely,int*x,int *y) {
109     int r=PIECE_WH/2;
110     *y=(pixelx-(ZERO_X-r))/PIECE_WH;
111     *x=(pixely-(ZERO_Y-r))/PIECE_WH;
112 }
113 //以数组坐标画线
114 void draw_line(HDC hdc,int sx,int sy,int dx,int dy) {
115     int psx,psy,pdx,pdy;
116     xy_to_pixel(sx,sy,&psx,&psy);
117     xy_to_pixel(dx,dy,&pdx,&pdy);
118     MoveToEx (hdc, psx,psy, NULL) ;
119     LineTo (hdc, pdx, pdy) ;
120 }
121 //以数组坐标画棋子
122 void paint_piece(HDC hdc,int x,int y,int color,int type) {
123     static HBRUSH  piece_brush =CreateSolidBrush (PIECE_BKCOLOR);    //棋子的背景色
124     if(type==0||color==BLANK)return ;
125     int px,py;
126     xy_to_pixel(x,y,&px,&py);
127     int r=PIECE_WH/2;
128     SelectObject (hdc,piece_brush ) ;
129     SelectObject (hdc, GetStockObject (NULL_PEN)) ;
130     Ellipse(hdc,px-r,py-r,px+r,py+r);
131     char *text=new char[5];
132     switch(type) {
133     case JU:
134         strcpy(text,"");
135         break;
136     case MA:
137         strcpy(text,"");
138         break;
139     case XIANG:
140         if(color==BLACK)strcpy(text,"");
141         else strcpy(text,"");
142         break;
143     case SHI:
144         strcpy(text,"");
145         break;
146     case JIANG:
147         if(color==BLACK)strcpy(text,"");
148         else strcpy(text,"");
149         break;
150     case PAO:
151         if(color==BLACK)strcpy(text,"");
152         else
153             strcpy(text,"");
154         break;
155     case ZU:
156         if(color==BLACK)strcpy(text,"");
157         else
158             strcpy(text,"");
159         break;
160     default:
161         strcpy(text,"");
162     }
163     SetBkColor(hdc,PIECE_BKCOLOR);//设置文字背景色
164     if(color==BLACK) {
165         SetTextColor(hdc,RGB(0,0,0));       //设置文字颜色
166     } else {
167         SetTextColor(hdc,RGB(255,255,255));
168     }
169     TextOut (hdc, px-r/2, py-r/2,text , strlen("")) ;
170     delete text;
171 }
172 
173 void* main_listen(void *) {
174     server->listen_message();
175     return 0;
176 }
177 //创建线程,使server开始监听
178 bool  start_listen() {
179     pthread_t listen_p;
180     int ret;
181     ret= pthread_create( &listen_p,  NULL, main_listen,NULL ); //
182     if( ret != 0 ) { //创建线程成功返回0
183         //printf("pthread_create error:error_code=%d\n",ret );
184         handle_error(THREAD_ERROR,true,true);
185         return false;
186     }
187     return true;
188 }
189 
190 
191 void* chess_connect(void *) {
192     client->connect_to_ots();  //client开始连接对方server,连接成功后返回
193     InvalidateRect(hwnd,NULL,true);
194 }
195 
196 void init() {
197     server=new Server();//创建Server对象
198     client=new Client(); //创建Client对象,
199     start_listen();             //创建线程,server开始监听
200     Sleep(1000);
201     pthread_t connect_p;
202     int ret;
203     ret= pthread_create( &connect_p,  NULL, chess_connect,NULL); //
204     if( ret != 0 ) { //创建线程成功返回0
205         //printf("pthread_create error:error_code=%d\n",ret );
206         handle_error(THREAD_ERROR,true,true);
207         return ;
208     }
209 }
210 
211 
212 
213 /*  This function is called by the Windows function DispatchMessage()  */
214 
215 LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
216     static POINT mouse;
217     static HDC hdc;
218     static PAINTSTRUCT ps ;
219     static int iofip=0;     //index of ots_ip
220     switch (message) {                /* handle the messages */
221     case WM_CREATE: {
222         port=35536;
223         ots_ip=new char[20];
224         strcpy(ots_ip,"");
225     }
226     break;
227     case WM_KEYDOWN://识别按键,显示输入的内容(对方IP地址)
228         if(server!=NULL)break;
229         if(wParam==13) {//如果是ENTER,则初始化server、client,并开始连接,连接成功后初始化board
230             init();
231             Sleep(100);
232             InvalidateRect(hwnd,NULL,true);
233         }
234         if(wParam==VK_BACK) {//删除键
235             if(iofip==0)return 0;
236             iofip--;
237             ots_ip[iofip]='\0';
238         }
239         if(wParam<106&&wParam>95) {//小键盘数字键
240             wParam-=48;
241         }
242         if(wParam<58&&wParam>47) {//主键盘数字键
243             ots_ip[iofip]='0'-48+wParam;
244             iofip++;
245             ots_ip[iofip]='\0';
246         }
247         if(wParam==110||wParam==229) {//小数点键,小键盘110,主键盘229
248             ots_ip[iofip]='.';
249             iofip++;
250             ots_ip[iofip]='\0';
251         }
252         InvalidateRect(hwnd,NULL,true);
253         break;
254     case WM_PAINT: {
255         static HBRUSH  bk_brush =CreateSolidBrush (RGB(240,240,240));    //棋子的背景色
256         hdc=BeginPaint (hwnd,&ps) ;
257         static HFONT hFont;
258         LOGFONT lf;
259         lf.lfHeight=PIECE_WH/2;
260         lf.lfWidth=0;
261         lf.lfEscapement=0;
262         lf.lfOrientation=0 ;
263         lf.lfWeight=5;
264         lf.lfItalic=0 ;
265         lf.lfUnderline=0 ;
266         lf.lfStrikeOut=0 ;
267         lf.lfCharSet=DEFAULT_CHARSET ;
268         lf.lfOutPrecision=0 ;
269         lf.lfClipPrecision=0 ;
270         lf.lfQuality=0 ;
271         lf.lfPitchAndFamily=0 ;
272         lstrcpy (lf.lfFaceName, _T("楷体") );
273         hFont = CreateFontIndirect (&lf) ;
274         SelectFont(hdc,hFont);
275         SelectObject(hdc,bk_brush);
276         Rectangle(hdc,0,0,WIDTH,HEIGHT);
277         SetBkColor(hdc,RGB(240,240,240));
278         if(chess_board==NULL) {//显示输入的IP地址
279             char tip[20]="请输入对方IP地址:";
280             Rectangle(hdc,WIDTH/5,HEIGHT/2-10,WIDTH/5*4,HEIGHT/2+30);
281             TextOut(hdc,WIDTH/5,HEIGHT/2-50,tip,strlen(tip));
282             SetBkColor(hdc,RGB(240,240,240));
283             TextOut(hdc,WIDTH/5+5,HEIGHT/2,ots_ip,strlen(ots_ip));
284             if(server!=NULL) {             //board==NULL而server!=NULL表示正在连接过程中
285                 char tip[20]="正在连接......";
286                 SetBkColor(hdc,RGB(240,240,240));
287                 TextOut(hdc,WIDTH/5,HEIGHT/2+50,tip,strlen(tip));
288             }
289             EndPaint(hwnd,&ps);
290             break;
291         }
292         char text[10]="你的颜色:";
293         if(chess_board->get_color()==BLACK) {