局域网象棋游戏(C++实现,使用Socket,界面使用Win32,CodeBlocks+GCC编译)

 目录

  成果

    运行效果图

  过程

    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) {
294             strcat(text,"");
295         } else {
296             strcat(text,"");
297         }
298         TextOut (hdc, 5, 5,text , strlen(text)) ;
299         int M=chess_board->get_M();
300         int N=chess_board->get_N();
301         //画棋盘
302         for(int i=0; i<M; i++) {
303             draw_line(hdc,i,0,i,N-1);
304         }
305         for(int i=0; i<N; i++) {
306             draw_line(hdc,0,i,N/2,i);
307         }
308         for(int i=0; i<N; i++) {
309             draw_line(hdc,N/2+1,i,N,i);
310         }
311         draw_line(hdc,0,3,2,5);
312         draw_line(hdc,0,5,2,3);
313         draw_line(hdc,9,3,7,5);
314         draw_line(hdc,7,3,9,5);
315         draw_line(hdc,4,0,5,0);
316         draw_line(hdc,4,8,5,8);
317         //画棋子
318         for(int i=0; i<M; i++) {
319             for(int j=0; j<N; j++) {
320                 paint_piece(hdc,i,j,chess_board->get_color(i,j),chess_board->get_type(i,j));
321             }
322         }
323         EndPaint(hwnd,&ps);
324     }
325     break;
326     case WM_LBUTTONUP: {
327         if(chess_board==NULL)break;
328         if(!chess_board->is_my_turn())break;//当前没轮到自己下棋
329         GetCursorPos(&mouse);//获取鼠标的屏幕坐标
330         ScreenToClient(hwnd,&mouse);//转换为界面坐标
331         int x,y;
332         pixel_to_xy(mouse.x,mouse.y,&x,&y);//转换为数组坐标
333         if(chess_board->get_color(x,y)==chess_board->get_color()) {//点击的是自己的棋子
334             chess_sx=x;
335             chess_sy=y;
336             break;
337         }
338         if(chess_sx==-1||chess_sy==-1) {//起始坐标未赋值且点击的不是自己的棋子,则break
339             break;
340         }
341         chess_dx=x;
342         chess_dy=y;
343         if(chess_board->my_move_piece(chess_sx,chess_sy,chess_dx,chess_dy)) { //如果移动棋子合法
344             client->send_message("move",chess_sx,chess_sy,chess_dx,chess_dy);   //向对方发送移子信息
345             InvalidateRect(hwnd,NULL,true);
346             if(chess_board->get_is_win()==WIN) {
347                 chess_board->init();//重新初始化棋盘,重下一盘
348                 MessageBox(hwnd,"你赢了","获胜!",NULL);
349                 InvalidateRect(hwnd,NULL,true);
350             }
351         }
352         chess_sx=-1;
353         chess_sy=-1;
354         break;
355     }
356     case WM_DESTROY:
357         if(server!=NULL)server->close();
358         if(client!=NULL)client->close();
359         PostQuitMessage (0);       /* send a WM_QUIT to the message queue */
360         break;
361     default:                      /* for messages that we don't deal with */
362         return DefWindowProc (hwnd, message, wParam, lParam);
363     }
364 
365     return 0;
366 }

chinese_chess.h

  1 #ifndef CHINESE_CHESS_H_INCLUDED
  2 #define CHINESE_CHESS_H_INCLUDED
  3 
  4 #define JU 1
  5 #define MA 2
  6 #define XIANG 3
  7 #define SHI 4
  8 #define JIANG 5
  9 #define PAO 6
 10 #define ZU 7
 11 #define BLANK 8 //空子
 12 
 13 #define BLACK -1
 14 #define WHITE 1
 15 
 16 #define WIN 1
 17 #define LOSE -1
 18 class Board {
 19 private:
 20     bool turn;   //是否轮到自己下棋
 21     int color;  //自己的颜色
 22     int M,N;        //棋盘行数、列数
 23     int **b;    //二维数组
 24     int is_win;     //是否胜利
 25 
 26     bool is_out(int x,int y) {//坐标是否出界
 27         return x>M||y>N||x<0||y<0;
 28     }
 29 
 30     bool is_same_color(int sx,int sy,int dx,int dy) {//源坐标与目的坐标是否是同一颜色
 31         return get_color(sx,sy)==get_color(dx,dy);
 32     }
 33     void swap_num(int & num1,int& num2) {//交换两个数
 34         num1+=num2;
 35         num2=num1-num2;
 36         num1=num1-num2;
 37     }
 38     int get_abs(int num) {//取得绝对值
 39         return num>=0?num:-num;
 40     }
 41     int num_of_not_blank_betweenn(int sx,int sy,int dx,int dy) {//返回在起始坐标和目的坐标之间棋子的个数
 42         if(!(sx==dx||sy==dy))return -1;
 43         int num=0;
 44         if(sy>dy)swap_num(sy,dy);
 45         if(sx>dx)swap_num(sx,dx);
 46         if(sx==dx) {
 47             for(int i=sy+1; i<dy; i++) {
 48                 if(b[sx][i]!=BLANK)num++;
 49             }
 50         }
 51         if(sy==dy) {
 52             for(int i=sx+1; i<dx; i++) {
 53                 if(b[i][sy]!=BLANK)num++;
 54             }
 55         }
 56         return num;
 57     }
 58     bool is_correct_move_JU(int sx,int sy,int dx,int dy) {
 59         return num_of_not_blank_betweenn(sx,sy,dx,dy)==0;
 60     }
 61     bool is_correct_move_MA(int sx,int sy,int dx,int dy) {
 62         int x=dx-sx,y=dy-sy;
 63         if(get_abs(x)==2&&get_abs(y)==1) {
 64             if(get_color(sx+x/2,sy)==BLANK)return true;//硌马蹄检测
 65         }
 66         if(get_abs(x)==1&&get_abs(y)==2) {
 67             if(get_color(sx,sy+y/2)==BLANK)return true;//硌马蹄检测
 68         }
 69         return false;
 70     }
 71     bool is_correct_move_XIANG(int sx,int sy,int dx,int dy) {
 72         int x=dx-sx,y=dy-sy;
 73         if(!(get_abs(x)==2&&get_abs(y)==2)) return false;
 74         if(get_color(sx+x/2,sy+y/2)==BLANK)return true;//硌象蹄检测
 75         return false;
 76     }
 77     bool is_correct_move_SHI(int sx,int sy,int dx,int dy) {
 78         int x=dx-sx,y=dy-sy;
 79         if(!(get_abs(x)==1&&get_abs(y)==1)) return false;
 80         if(dx<7)return false;
 81         if(dy<3||dy>5)return false;
 82         return true;
 83     }
 84     bool is_correct_move_JIANG(int sx,int sy,int dx,int dy) {
 85         int x=dx-sx,y=dy-sy;
 86         if(!((get_abs(x)==1&&get_abs(y)==0)||(get_abs(x)==0&&get_abs(y)==1))) return false;
 87         if(dx<7)return false;
 88         if(dy<3||dy>5)return false;
 89         for(int i=0; i<3; i++) {//明将检测
 90             if(get_type(i,dy)==JIANG) {
 91                 if(num_of_not_blank_betweenn(dx,dy,i,dy)==0) return false;
 92                 return true;
 93             }
 94         }
 95         return true;
 96     }
 97     bool is_correct_move_PAO(int sx,int sy,int dx,int dy) {
 98         int n=get_color(dx,dy)==BLANK?0:1;
 99         return num_of_not_blank_betweenn(sx,sy,dx,dy)==n;
100     }
101     bool is_correct_move_ZU(int sx,int sy,int dx,int dy) {
102         if(dx>sx)return false;
103         int x=dx-sx,y=dy-sy;
104         if(get_abs(x)+get_abs(y)!=1)return false;
105         if(sx>4&&get_abs(x)!=1)return false;//过河前只能向前走
106         return true;
107     }
108 
109     bool is_correct_move(int sx,int sy,int dx,int dy) {
110         if(sx==dx&&sy==dy) {
111             return false;
112         }
113         if(is_out(sx,sy)||is_out(dx,dy)) {
114             return false;
115         }
116         if(get_color(sx,sy)!=color) {
117             return false;
118         }
119         if(is_same_color(sx,sy,dx,dy)) {
120             return false;
121         }
122         switch(get_type(sx,sy)) {
123         case JU:
124             return is_correct_move_JU(sx,sy,dx,dy);
125         case MA:
126             return is_correct_move_MA(sx,sy,dx,dy);
127         case XIANG:
128             return is_correct_move_XIANG(sx,sy,dx,dy);
129         case SHI:
130             return is_correct_move_SHI(sx,sy,dx,dy);
131         case JIANG:
132             return is_correct_move_JIANG(sx,sy,dx,dy);
133         case PAO:
134             return is_correct_move_PAO(sx,sy,dx,dy);
135         case ZU:
136             return is_correct_move_ZU(sx,sy,dx,dy);
137         default:
138             return false;
139         }
140     }
141 
142     void move_s_to_d(int sx,int sy,int dx,int dy) { //移动操作
143         if(get_type(dx,dy)==JIANG) {    //如果目的棋子是将
144             if(get_color(dx,dy)==color)set_win(LOSE);//如果是自己的将,则输
145             else set_win(WIN);//如果是对方的将,则赢
146         }
147         b[dx][dy]=b[sx][sy];
148         b[sx][sy]=BLANK;
149         change_turn();
150     }
151 
152     void init_pieces() {
153         for(int i=0; i<M; i+=M-1) {//第一行和最后一行(即车马象士将士象马车)
154             for(int index=0; index<N; index++) {
155                 if(index<N/2+1)b[i][index]=index+1;
156                 else b[i][index]=N-index;
157             }
158         }
159         //卒所在的行
160         for(int index=0; index<N; index+=2) {
161             b[3][index]=ZU;
162         }
163         for(int index=0; index<N; index+=2) {
164             b[6][index]=ZU;
165         }
166         b[2][1]=PAO;
167         b[M-1-2][1]=PAO;
168         b[2][N-1-1]=PAO;
169         b[M-1-2][N-1-1]=PAO;
170         int s,d;//存储起始行和终点行
171         if(color==BLACK) {
172             s=0;//从0行到M/2行,即棋盘上半部分
173             d=M/2;
174         } else {
175             s=M/2;//棋盘下半部分
176             d=M;
177         }
178         //从s行到d行,把非BLANK的值加BLANK,使小于BLANK的代表黑色棋,大于BLANK的代表白色棋
179         for(int index=s; index<d; index++) {
180             for(int j=0; j<N; j++) {
181                 if(b[index][j]!=BLANK) {
182                     b[index][j]+=BLANK;
183                 }
184             }
185         }
186     }
187 
188 public:
189     Board(int c) {
190         color=c;
191         M=10;
192         N=9;
193         b=new int*[M];
194         for(int i=0; i<M; i++) {
195             b[i]=new int[N];
196         }
197         init();
198     }
199     void init() {//棋盘初始化
200         is_win=0;
201         turn=color==BLACK?true:false;
202         for(int i=0; i<M; i++) {
203             for(int j=0; j<N; j++) {
204                 b[i][j]=BLANK;
205             }
206         }
207         init_pieces();
208     }
209     int get_M() {
210         return M;
211     }
212     int get_N() {
213         return N;
214     }
215     int get_color() {//获取己方的颜色
216         return color;
217     }
218     int get_color(int x,int y) {//获取棋盘某一坐标上棋子的颜色
219         return b[x][y]>BLANK?WHITE:b[x][y]<BLANK?BLACK:BLANK;
220     }
221     int get_type(int x,int y) {//获取棋子类型(空、车、马、象、士、将、炮、卒)
222         return b[x][y]!=BLANK?b[x][y]%BLANK:BLANK;
223     }
224     void set_win(int is) {
225         is_win=is;
226     }
227     int get_is_win() {
228         return is_win;
229     }
230     void change_turn() {
231         turn=turn==true?false:true;
232     }
233     bool is_my_turn() {
234         return turn;
235     }
236     void othside_move_piece(int sx,int sy,int dx,int dy) {//对方移子
237         sx=M-1-sx;//先进行坐标转换,因对方视角与己方相反
238         sy=N-1-sy;
239         dx=M-1-dx;
240         dy=N-1-dy;
241         move_s_to_d(sx,sy,dx,dy);
242     }
243     bool my_move_piece(int sx,int sy,int dx,int dy) {       //己方主动移子
244         if(!is_correct_move(sx,sy,dx,dy))return false;
245         move_s_to_d(sx,sy,dx,dy);
246         return true;
247     }
248 };
249 
250 #endif // CHINESE_CHESS_H_INCLUDED

Server.h

  1 #ifndef SERVER_H_INCLUDED
  2 #define SERVER_H_INCLUDED
  3 #include<stdio.h>
  4 #include <stdlib.h>
  5 #include <errno.h>
  6 #include <winsock2.h>
  7 #include"chinese_chess.h"
  8 
  9 #define INIT_ERROR 1
 10 #define BIND_ERROR 2
 11 #define LISTEN_ERROR 3
 12 #define CONNECT_ERROR 4
 13 #define SEND_ERROR 5
 14 #define ACCEPT_ERROR 6
 15 #define ALIVE_ERROR 7
 16 #define THREAD_ERROR 8
 17 int error_num;
 18 extern HWND hwnd;
 19 extern Board* chess_board;
 20 extern char* ots_ip;
 21 extern int port;
 22 extern bool is_connect_alive;
 23 
 24 //线程参数结构体
 25 typedef struct server_args {
 26     SOCKET* Com_Sock;
 27     char * rebuf;
 28 } server_args;
 29 //校验检测,与客户端的添加校验是相反操作
 30 //最后一位之前的所有字符相加取模后,如果等于最后一个字符,则校验通过
 31 bool server_check(char * r) {
 32     int len=strlen(r);
 33     len--;
 34     int s=0;
 35     for(int i=0; i<len; i++) {
 36         s+=r[i];
 37     }
 38     if(r[len]==(s%5+'0')) {
 39         r[len]='\0';
 40         return true;
 41     }
 42     return false;
 43 }
 44 //错误处理,is_tell控制是否显示错误信息,is_exit控制是否退出程序
 45 int handle_error(int err,bool is_tell,bool is_exit) {
 46     error_num=err;
 47     if(!is_tell)return error_num;
 48     char error[30]="";
 49     switch(error_num) {
 50     case INIT_ERROR:
 51         strcpy(error,"初始化错误");
 52         break;
 53     case BIND_ERROR:
 54         strcpy(error,"绑定端口错误");
 55         break;
 56     case LISTEN_ERROR:
 57         strcpy(error,"监听错误");
 58         break;
 59     case ACCEPT_ERROR:
 60         strcpy(error,"接受连接错误");
 61         break;
 62     case CONNECT_ERROR:
 63         strcpy(error,"无法连接");
 64         break;
 65     case ALIVE_ERROR:
 66         strcpy(error,"连接已断开");
 67         break;
 68     case THREAD_ERROR:
 69         strcpy(error,"线程无法创建");
 70         break;
 71     case SEND_ERROR:
 72         strcpy(error,"发送错误");
 73     }
 74     char error_message[50];
 75     strcpy(error_message,"错误:");
 76     strcat(error_message,error);
 77     if(is_exit)strcat(error_message,"\n程序将退出。");
 78     MessageBox(hwnd,error_message,"错误",MB_OK);
 79     if(is_exit)exit(0);
 80     return error_num;
 81 }
 82 void* handle_message(void*ar) {
 83     server_args * serarg=(server_args * )ar;
 84     char *recv=serarg->rebuf;
 85     SOCKET* CommandSock=serarg->Com_Sock;
 86     if(server_check(recv)) {//校验通过发送okok(OK),不通过发送noto(NOTOK)
 87         send(*CommandSock,"okok",4,0);
 88     } else {
 89         send(*CommandSock,"noto",4,0);
 90         return ar;
 91     }
 92     if(strncmp(recv,"move",4)==0) {
 93         char * pch;
 94         //将recvBuf以逗号拆分
 95         pch = strtok (recv,",");
 96         pch = strtok (NULL,",");
 97         int xys[4];
 98         int index=0;
 99         while (pch != NULL) {
100             xys[index]=atoi(pch);//char* 转换为int
101             index++;
102             pch = strtok (NULL, ",");
103         }
104         chess_board->othside_move_piece(xys[0],xys[1],xys[2],xys[3]);
105         if(chess_board->get_is_win()==LOSE) {
106             chess_board->init();//如果输了,则重新初始化棋盘,再下一盘
107             MessageBox(hwnd,"你输了","失败!",NULL);
108         }
109         InvalidateRect(hwnd,NULL,true);
110     }
111     delete recv;
112 }
113 class Server {
114 private:
115     SOCKET Server_Sock;
116     SOCKADDR_IN server_addr;
117     SOCKADDR_IN client_addr;
118     char recvBuf[20];
119 
120 public:
121     Server() {
122         WSADATA wsa;
123         /*初始化socket资源*/
124         if (WSAStartup(MAKEWORD(1,1),&wsa) != 0) {
125             handle_error(INIT_ERROR,true,true);
126             return;
127         }
128 
129         if((Server_Sock = socket(AF_INET, SOCK_STREAM, 0))==-1) {
130             handle_error(INIT_ERROR,true,true);
131             return;
132         }
133         ZeroMemory((char *)&server_addr,sizeof(server_addr));
134         server_addr.sin_family = AF_INET;
135         server_addr.sin_port = htons(port);           /*本地监听端口*/
136         server_addr.sin_addr.s_addr = htonl(INADDR_ANY); /*有IP*/
137 
138         if(bind(Server_Sock,(struct sockaddr *)&server_addr,sizeof(server_addr))==-1) {
139             handle_error(BIND_ERROR,true,true);
140             return;
141         }
142         if(listen(Server_Sock,5)==-1) { //其中第二个参数代表能够接收的最多的连接数
143             handle_error(LISTEN_ERROR,true,true);
144             return;
145         }
146         strcpy(recvBuf,"");
147     }
148 
149     void listen_message() {
150         int len=sizeof(SOCKADDR);
151         while(true) {
152             SOCKET Command_Sock  = accept(Server_Sock, (SOCKADDR*)&client_addr,&len);
153             if(Command_Sock == INVALID_SOCKET) {
154                 closesocket(Command_Sock);
155                 handle_error(ACCEPT_ERROR,false,false);
156                 continue;
157             }
158             if(client_addr.sin_addr.s_addr!=inet_addr(ots_ip)) {//如果接收的socket不是预期的对方的,则发送wron,继续等待
159                 send(Command_Sock,"wron",4,0);
160                 closesocket(Command_Sock);
161                 continue;
162             }
163             send(Command_Sock,"righ",4,0);
164             is_connect_alive=true;
165             while(true) {
166                 if(recv(Command_Sock,recvBuf,20,0)<=0) {//recv返回小于等于0的值,则连接已断开
167                     handle_error(ALIVE_ERROR,true,true);
168                     closesocket(Command_Sock);
169                     close();
170                     return ;
171                 }
172                 char *rbuf=new char[20];
173                 strcpy(rbuf,recvBuf);
174                 server_args serarg;
175                 serarg.Com_Sock=&Command_Sock;
176                 serarg.rebuf=rbuf;
177                 pthread_t handle_m;
178                 int ret;
179                 ret= pthread_create( &handle_m,  NULL, handle_message,&serarg); //
180                 if( ret != 0 ) { //创建线程成功返回0
181                     // printf("pthread_create error:error_code=%d\n",ret );
182                     handle_error(THREAD_ERROR,true,true);
183                     return ;
184                 }
185                 strcpy(recvBuf,"");
186             }
187             closesocket(Command_Sock);
188         }
189     }
190     void close() {
191         closesocket(Server_Sock);
192         WSACleanup();
193     }
194 };
195 
196 
197 #endif // SERVER_H_INCLUDED

Client.h

 1 #ifndef CLIENT_H_INCLUDED
 2 #define CLIENT_H_INCLUDED
 3 
 4 #include <stdio.h>
 5 #include <winsock2.h>
 6 
 7 #include"chinese_chess.h"
 8 
 9 //为字符串添加校验信息,对所有字符求和,模5之后转化为字符放在字符串最后
10 void client_check(char* r) {
11     int len=strlen(r);
12     int s=0;
13     for(int i=0; i<len; i++) {
14         s+=r[i];
15     }
16     r[len]=s%5+'0';
17     r[len+1]='\0';
18 }
19 class Client {
20 private:
21     SOCKET Client_Sock;
22     SOCKADDR_IN server_addr;
23     char sendBuf[20];
24 public:
25     Client() {
26         WSADATA wsa;
27         /*初始化socket资源*/
28         if (WSAStartup(MAKEWORD(1,1),&wsa) != 0) {
29             handle_error(INIT_ERROR,true,true);
30             return;   //代表失败
31         }
32         if((Client_Sock = socket(AF_INET, SOCK_STREAM, 0))==-1) {
33             handle_error(INIT_ERROR,true,true);
34             return;   //代表失败
35         }
36         server_addr.sin_addr.S_un.S_addr=inet_addr(ots_ip);
37         server_addr.sin_family=AF_INET;
38         server_addr.sin_port=htons(port);
39         strcpy(sendBuf,"");
40     }
41     void connect_to_ots() {
42         while(connect(Client_Sock,(SOCKADDR*)&server_addr,sizeof(SOCKADDR)) ==-1) {
43             handle_error(CONNECT_ERROR,false,false);
44             Sleep(100);
45             //printf( "%d ", WSAGetLastError());
46         }
47         char rec[5];
48         recv(Client_Sock,rec,4,0);
49         if(strncmp(rec,"wron",4)==0) {  //收到wrong,说明对方所输入的IP不是己方IP
50             MessageBox(hwnd,"对方输入的IP不是你\n程序将退出","错误",NULL);
51             exit(-1);
52         }
53         //谁先连接谁是黑色
54         //如果server已经收到连接,则说明是对方先连接自己,则自己应为白色,否则自己是黑色
55         if(is_connect_alive) {
56             chess_board=new Board(WHITE);
57         } else {
58             chess_board=new Board(BLACK);
59         }
60     }
61     void close() {
62         closesocket(Client_Sock);
63         WSACleanup();
64     }
65 
66     int send_message(char * message) {
67         strcpy(sendBuf,message);
68         client_check(sendBuf);
69         int len;
70         int try_time=0;
71         while(true) {
72             len=send(Client_Sock,sendBuf,strlen(sendBuf)+1,0);
73             if(len!=(strlen(sendBuf)+1)) {
74                 handle_error(SEND_ERROR,false,false);
75                 //printf( "%d ", WSAGetLastError());
76             }
77             char rec[5];
78             recv(Client_Sock,rec,4,0);
79             if(strncmp(rec,"okok",4)==0) {//收到OK说明数据已经正确被对方收到
80                 break;
81             }
82             if(try_time>20) {   //尝试20次,数据仍无法正确送达,则退出
83                 handle_error(SEND_ERROR,true,true);
84             }
85             try_time++;
86         }
87         return len;
88     }
89     int send_message(const char * message,int sx,int sy,int dx,int dy) {
90         char* message_temp=new char[20];
91         sprintf(message_temp,"%s,%d,%d,%d,%d,",message,sx,sy,dx,dy);
92         int len=send_message(message_temp);
93         delete message_temp;
94         return len;
95     }
96 };
97 
98 
99 #endif // CLIENT_H_INCLUDED

 

 

该程序从2016.3.15晚开始,用了四天的空闲时间。

END

原文地址:https://www.cnblogs.com/maxuewei2/p/5297263.html