目录

  成果

    运行效果图

  过程

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

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<||y<;
  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>=?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 -;
  43. int num=;
  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+; i<dy; i++) {
  48. if(b[sx][i]!=BLANK)num++;
  49. }
  50. }
  51. if(sy==dy) {
  52. for(int i=sx+; 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)==;
  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)==&&get_abs(y)==) {
  64. if(get_color(sx+x/,sy)==BLANK)return true;//硌马蹄检测
  65. }
  66. if(get_abs(x)==&&get_abs(y)==) {
  67. if(get_color(sx,sy+y/)==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)==&&get_abs(y)==)) return false;
  74. if(get_color(sx+x/,sy+y/)==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)==&&get_abs(y)==)) return false;
  80. if(dx<)return false;
  81. if(dy<||dy>)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)==&&get_abs(y)==)||(get_abs(x)==&&get_abs(y)==))) return false;
  87. if(dx<)return false;
  88. if(dy<||dy>)return false;
  89. for(int i=; i<; i++) {//明将检测
  90. if(get_type(i,dy)==JIANG) {
  91. if(num_of_not_blank_betweenn(dx,dy,i,dy)==) 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?:;
  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)!=)return false;
  105. if(sx>&&get_abs(x)!=)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=; i<M; i+=M-) {//第一行和最后一行(即车马象士将士象马车)
  154. for(int index=; index<N; index++) {
  155. if(index<N/+)b[i][index]=index+;
  156. else b[i][index]=N-index;
  157. }
  158. }
  159. //卒所在的行
  160. for(int index=; index<N; index+=) {
  161. b[][index]=ZU;
  162. }
  163. for(int index=; index<N; index+=) {
  164. b[][index]=ZU;
  165. }
  166. b[][]=PAO;
  167. b[M--][]=PAO;
  168. b[][N--]=PAO;
  169. b[M--][N--]=PAO;
  170. int s,d;//存储起始行和终点行
  171. if(color==BLACK) {
  172. s=;//从0行到M/2行,即棋盘上半部分
  173. d=M/;
  174. } else {
  175. s=M/;//棋盘下半部分
  176. d=M;
  177. }
  178. //从s行到d行,把非BLANK的值加BLANK,使小于BLANK的代表黑色棋,大于BLANK的代表白色棋
  179. for(int index=s; index<d; index++) {
  180. for(int j=; 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=;
  192. N=;
  193. b=new int*[M];
  194. for(int i=; i<M; i++) {
  195. b[i]=new int[N];
  196. }
  197. init();
  198. }
  199. void init() {//棋盘初始化
  200. is_win=;
  201. turn=color==BLACK?true:false;
  202. for(int i=; i<M; i++) {
  203. for(int j=; 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--sx;//先进行坐标转换,因对方视角与己方相反
  238. sy=N--sy;
  239. dx=M--dx;
  240. dy=N--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=;
  35. for(int i=; i<len; i++) {
  36. s+=r[i];
  37. }
  38. if(r[len]==(s%+'')) {
  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[]="";
  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[];
  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();
  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",,);
  88. } else {
  89. send(*CommandSock,"noto",,);
  90. return ar;
  91. }
  92. if(strncmp(recv,"move",)==) {
  93. char * pch;
  94. //将recvBuf以逗号拆分
  95. pch = strtok (recv,",");
  96. pch = strtok (NULL,",");
  97. int xys[];
  98. int index=;
  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[],xys[],xys[],xys[]);
  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[];
  119.  
  120. public:
  121. Server() {
  122. WSADATA wsa;
  123. /*初始化socket资源*/
  124. if (WSAStartup(MAKEWORD(,),&wsa) != ) {
  125. handle_error(INIT_ERROR,true,true);
  126. return;
  127. }
  128.  
  129. if((Server_Sock = socket(AF_INET, SOCK_STREAM, ))==-) {
  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))==-) {
  139. handle_error(BIND_ERROR,true,true);
  140. return;
  141. }
  142. if(listen(Server_Sock,)==-) { //其中第二个参数代表能够接收的最多的连接数
  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",,);
  160. closesocket(Command_Sock);
  161. continue;
  162. }
  163. send(Command_Sock,"righ",,);
  164. is_connect_alive=true;
  165. while(true) {
  166. if(recv(Command_Sock,recvBuf,,)<=) {//recv返回小于等于0的值,则连接已断开
  167. handle_error(ALIVE_ERROR,true,true);
  168. closesocket(Command_Sock);
  169. close();
  170. return ;
  171. }
  172. char *rbuf=new char[];
  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
  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. #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=;
  13. for(int i=; i<len; i++) {
  14. s+=r[i];
  15. }
  16. r[len]=s%+'';
  17. r[len+]='\0';
  18. }
  19. class Client {
  20. private:
  21. SOCKET Client_Sock;
  22. SOCKADDR_IN server_addr;
  23. char sendBuf[];
  24. public:
  25. Client() {
  26. WSADATA wsa;
  27. /*初始化socket资源*/
  28. if (WSAStartup(MAKEWORD(,),&wsa) != ) {
  29. handle_error(INIT_ERROR,true,true);
  30. return; //代表失败
  31. }
  32. if((Client_Sock = socket(AF_INET, SOCK_STREAM, ))==-) {
  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)) ==-) {
  43. handle_error(CONNECT_ERROR,false,false);
  44. Sleep();
  45. //printf( "%d ", WSAGetLastError());
  46. }
  47. char rec[];
  48. recv(Client_Sock,rec,,);
  49. if(strncmp(rec,"wron",)==) { //收到wrong,说明对方所输入的IP不是己方IP
  50. MessageBox(hwnd,"对方输入的IP不是你\n程序将退出","错误",NULL);
  51. exit(-);
  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=;
  71. while(true) {
  72. len=send(Client_Sock,sendBuf,strlen(sendBuf)+,);
  73. if(len!=(strlen(sendBuf)+)) {
  74. handle_error(SEND_ERROR,false,false);
  75. //printf( "%d ", WSAGetLastError());
  76. }
  77. char rec[];
  78. recv(Client_Sock,rec,,);
  79. if(strncmp(rec,"okok",)==) {//收到OK说明数据已经正确被对方收到
  80. break;
  81. }
  82. if(try_time>) { //尝试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[];
  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. #endif // CLIENT_H_INCLUDED

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

END

局域网象棋游戏(C++实现,使用Socket,界面使用Win32,CodeBlocks+GCC编译)的更多相关文章

  1. GLine游戏(Win32GUI实现,CodeBlocks+GCC编译)

    游戏规则: 在10X10的棋盘上有五种颜色的棋子. 点击一个棋子,再点击一个空格子,如果两者之间有一条路径的话,棋子会移动到空格子内. 每移动一次,棋盘上会增加三个棋子,其位置和颜色都是随机的. 当横 ...

  2. Socket服务端和客户端(C++,CodeBlocks+GCC编译)

    //main.cpp 1 #include "j_socket.h" #include <stdio.h> #include <pthread.h> ; j ...

  3. C#中国象棋+游戏大厅 服务器 + 客户端源码

    来源:www.ajerp.com/bbs C#中国象棋+游戏大厅 服务器 + 客户端源码 源码开源 C#版中国象棋(附游戏大厅) 基于前人大虾的修改版 主要用委托实现 服务器支持在线人数,大厅桌数的设 ...

  4. [CareerCup] 12.3 Test Move Method in a Chess Game 测试象棋游戏中的移动方法

    12.3 We have the following method used in a chess game: boolean canMoveTo( int x, int y). This metho ...

  5. 基于HTML5实现的中国象棋游戏

    棋类游戏在桌面游戏中已经非常成熟,中国象棋的版本也非常多.今天这款基于HTML5技术的中国象棋游戏非常有特色,我们不仅可以选择中国象棋的游戏难度,而且可以切换棋盘的样式.程序写累了,喝上一杯咖啡,和电 ...

  6. 中国象棋游戏Chess(3) - 实现走棋规则

    棋盘的绘制和走棋参看博文:中国象棋游戏Chess(1) - 棋盘绘制以及棋子的绘制,中国象棋游戏Chess(2) - 走棋 现在重新整理之前写的代码,并且对于每个棋子的走棋规则都进行了限制,不像之前那 ...

  7. 中国象棋游戏Chess(2) - 走棋

    之前的文章请看:中国象棋游戏Chess(1) - 棋盘绘制以及棋子的绘制 现在实现走棋的功能. 首先需要获取点击到的棋子,用QWidget中的函数 mouseReleaseEvent 实现函数: vo ...

  8. C/C++编程笔记:C语言打造中国象棋游戏,项目源代码分享!

    中国象棋是起源于中国的一种棋,属于二人对抗性游戏的一种,在中国有着悠久的历史.由于用具简单,趣味性强,成为流行极为广泛的棋艺活动. 它是中国棋文化,也是中华民族的文化瑰宝,它源远流长,趣味浓厚,基本规 ...

  9. u3d局域网游戏网络(c# socket select 模型)——续

    原文:http://www.cnblogs.com/saucerman/p/5555793.html 因为项目要加语音.语音数据都非常大.所以顺带就把之前写的网络模块一起测试了. 然后发现了一些bug ...

随机推荐

  1. Python模拟登陆新浪微博

    上篇介绍了新浪微博的登陆过程,这节使用Python编写一个模拟登陆的程序.讲解与程序如下: 1.主函数(WeiboMain.py): import urllib2 import cookielib i ...

  2. C站投稿189网盘视频源(UP主篇)

    C站投稿189网盘视频源(UP主篇) 现在C站(吐槽弹幕网)的视频来源基本靠的都是189网盘,比如番剧区的每个视频基本来源于此,不像AB两站,拥有自己的资源服务器,为啥呢?没钱啊.都是外来的视频.本站 ...

  3. CSS3属性 box-shadow 向框添加一个或多个阴影

    CSS3属性 利用box-shadow制作网页页眉背景 box-shadow 浏览器支持 IE9+.Firefox 4.Chrome.Opera 以及 Safari 5.1.1 支持 box-shad ...

  4. [Spring]支持注解的Spring调度器

    概述 如果想在Spring中使用任务调度功能,除了集成调度框架Quartz这种方式,也可以使用Spring自己的调度任务框架. 使用Spring的调度框架,优点是:支持注解(@Scheduler),可 ...

  5. 来,一起让我们越来越懒,面向CSS、JS未来编程。(9.28已更新)

    2016.10.29更新 本文存在大量的错误,仅供参考. 不知不觉在前端领域马上一个年头就要过去了,然而再看看自己的代码,果然够烂,那么为什么代码一直没有用面向对象的思维去写CSS呢?首先有两点:一点 ...

  6. WriteLog

    public class WriteLog     {         /// <summary>         /// 创建日志文件         /// </summary& ...

  7. 深度|作为C端应用的代表,成功的陌生社交应用是什么样子的?

    作 为C端应用的代表,成功的陌生社交应用是什么样子的?活跃用户数?收益回报率?在实际社交产品设计中,我们一直为这些所谓的KPI左右,具体到设计行为 上:摆弄相应的界面元素,优化一下文案.页面流,但却很 ...

  8. 来玩Play框架04 表单

    作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 表单(form)是最常见的从客户往服务器传递数据的方式.Play框架提供了一些工具 ...

  9. 《连载 | 物联网框架ServerSuperIO教程》- 3.设备驱动介绍

    1.C#跨平台物联网通讯框架ServerSuperIO(SSIO)介绍 <连载 | 物联网框架ServerSuperIO教程>1.4种通讯模式机制. <连载 | 物联网框架Serve ...

  10. Atitit.数据采集器 dataspider

    Atitit.数据采集器 dataspider /atiplat_cms/src/com/attilax/WebInfoX.java  @dep http://cl.cmcher.com/thread ...