局域網象棋游戲(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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM