目錄
成果
運行效果圖
左邊是在虛擬機里運行的,右邊是在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

