局域网象棋游戏(C++实现,使用Socket,界面使用Win32,CodeBlocks+GCC编译)
目录
成果
运行效果图
左边是在虚拟机里运行的,右边是在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