局域网象棋游戏(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
- #if defined(UNICODE) && !defined(_UNICODE)
- #define _UNICODE
- #elif defined(_UNICODE) && !defined(UNICODE)
- #define UNICODE
- #endif
- #include <tchar.h>
- #include <windows.h>
- #include <pthread.h>
- #include <windowsx.h>
- #include "chinese_chess.h"
- #include "Server.h"
- #include "Client.h"
- #define WIDTH 600 //界面宽度
- #define HEIGHT 600 //界面高度
- #define ZERO_X 70 //棋盘左边界
- #define ZERO_Y 70 //棋盘上边界
- #define PIECE_BKCOLOR RGB(195,163,109) //棋子背景色
- #define PIECE_WH 45 //棋盘每个格子的宽度和高度
- HWND hwnd; /* This is the handle for our window */
- char* ots_ip; //存储对方IP地址的字符串
- int port;
- bool is_connect_alive=false; //是否连接到对方
- Board * chess_board; //棋盘
- Server *server;
- Client *client;
- int chess_sx=-; //移动起始位置的数组坐标
- int chess_sy=-;
- int chess_dx=-; //移动目标位置的数组坐标
- int chess_dy=-;
- /* Declare Windows procedure */
- LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);
- /* Make the class name into a global variable */
- TCHAR szClassName[ ] = _T("Chinese Chess");
- int WINAPI WinMain (HINSTANCE hThisInstance,
- HINSTANCE hPrevInstance,
- LPSTR lpszArgument,
- int nCmdShow) {
- MSG messages; /* Here messages to the application are saved */
- WNDCLASSEX wincl; /* Data structure for the windowclass */
- /* The Window structure */
- wincl.hInstance = hThisInstance;
- wincl.lpszClassName = szClassName;
- wincl.lpfnWndProc = WindowProcedure; /* This function is called by windows */
- wincl.style = CS_DBLCLKS; /* Catch double-clicks */
- wincl.cbSize = sizeof (WNDCLASSEX);
- /* Use default icon and mouse-pointer */
- wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
- wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
- wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
- wincl.lpszMenuName = NULL; /* No menu */
- wincl.cbClsExtra = ; /* No extra bytes after the window class */
- wincl.cbWndExtra = ; /* structure or the window instance */
- /* Use Windows's default colour as the background of the window */
- wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;
- /* Register the window class, and if it fails quit the program */
- if (!RegisterClassEx (&wincl))
- return ;
- /* The class is registered, let's create the program*/
- hwnd = CreateWindowEx (
- , /* Extended possibilites for variation */
- szClassName, /* Classname */
- _T("Chinese Chess"), /* Title Text */
- WS_OVERLAPPEDWINDOW, /* default window */
- CW_USEDEFAULT, /* Windows decides the position */
- CW_USEDEFAULT, /* where the window ends up on the screen */
- WIDTH, /* The programs width */
- HEIGHT, /* and height in pixels */
- HWND_DESKTOP, /* The window is a child-window to desktop */
- NULL, /* No menu */
- hThisInstance, /* Program Instance handler */
- NULL /* No Window Creation data */
- );
- /* Make the window visible on the screen */
- ShowWindow (hwnd, nCmdShow);
- /* Run the message loop. It will run until GetMessage() returns 0 */
- while (GetMessage (&messages, NULL, , )) {
- /* Translate virtual-key messages into character messages */
- TranslateMessage(&messages);
- /* Send message to WindowProcedure */
- DispatchMessage(&messages);
- }
- /* The program return-value is 0 - The value that PostQuitMessage() gave */
- return messages.wParam;
- }
- //把数组坐标转换为界面坐标
- void xy_to_pixel(int x,int y,int*pixelx,int *pixely) {
- *pixely=x*PIECE_WH+ZERO_Y;
- *pixelx=y*PIECE_WH+ZERO_X;
- }
- //把界面坐标转换为数组坐标
- void pixel_to_xy(int pixelx,int pixely,int*x,int *y) {
- int r=PIECE_WH/;
- *y=(pixelx-(ZERO_X-r))/PIECE_WH;
- *x=(pixely-(ZERO_Y-r))/PIECE_WH;
- }
- //以数组坐标画线
- void draw_line(HDC hdc,int sx,int sy,int dx,int dy) {
- int psx,psy,pdx,pdy;
- xy_to_pixel(sx,sy,&psx,&psy);
- xy_to_pixel(dx,dy,&pdx,&pdy);
- MoveToEx (hdc, psx,psy, NULL) ;
- LineTo (hdc, pdx, pdy) ;
- }
- //以数组坐标画棋子
- void paint_piece(HDC hdc,int x,int y,int color,int type) {
- static HBRUSH piece_brush =CreateSolidBrush (PIECE_BKCOLOR); //棋子的背景色
- if(type==||color==BLANK)return ;
- int px,py;
- xy_to_pixel(x,y,&px,&py);
- int r=PIECE_WH/;
- SelectObject (hdc,piece_brush ) ;
- SelectObject (hdc, GetStockObject (NULL_PEN)) ;
- Ellipse(hdc,px-r,py-r,px+r,py+r);
- char *text=new char[];
- switch(type) {
- case JU:
- strcpy(text,"車");
- break;
- case MA:
- strcpy(text,"马");
- break;
- case XIANG:
- if(color==BLACK)strcpy(text,"象");
- else strcpy(text,"相");
- break;
- case SHI:
- strcpy(text,"士");
- break;
- case JIANG:
- if(color==BLACK)strcpy(text,"将");
- else strcpy(text,"帅");
- break;
- case PAO:
- if(color==BLACK)strcpy(text,"砲");
- else
- strcpy(text,"炮");
- break;
- case ZU:
- if(color==BLACK)strcpy(text,"卒");
- else
- strcpy(text,"兵");
- break;
- default:
- strcpy(text,"");
- }
- SetBkColor(hdc,PIECE_BKCOLOR);//设置文字背景色
- if(color==BLACK) {
- SetTextColor(hdc,RGB(,,)); //设置文字颜色
- } else {
- SetTextColor(hdc,RGB(,,));
- }
- TextOut (hdc, px-r/, py-r/,text , strlen("马")) ;
- delete text;
- }
- void* main_listen(void *) {
- server->listen_message();
- return ;
- }
- //创建线程,使server开始监听
- bool start_listen() {
- pthread_t listen_p;
- int ret;
- ret= pthread_create( &listen_p, NULL, main_listen,NULL ); //
- if( ret != ) { //创建线程成功返回0
- //printf("pthread_create error:error_code=%d\n",ret );
- handle_error(THREAD_ERROR,true,true);
- return false;
- }
- return true;
- }
- void* chess_connect(void *) {
- client->connect_to_ots(); //client开始连接对方server,连接成功后返回
- InvalidateRect(hwnd,NULL,true);
- }
- void init() {
- server=new Server();//创建Server对象
- client=new Client(); //创建Client对象,
- start_listen(); //创建线程,server开始监听
- Sleep();
- pthread_t connect_p;
- int ret;
- ret= pthread_create( &connect_p, NULL, chess_connect,NULL); //
- if( ret != ) { //创建线程成功返回0
- //printf("pthread_create error:error_code=%d\n",ret );
- handle_error(THREAD_ERROR,true,true);
- return ;
- }
- }
- /* This function is called by the Windows function DispatchMessage() */
- LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
- static POINT mouse;
- static HDC hdc;
- static PAINTSTRUCT ps ;
- static int iofip=; //index of ots_ip
- switch (message) { /* handle the messages */
- case WM_CREATE: {
- port=;
- ots_ip=new char[];
- strcpy(ots_ip,"");
- }
- break;
- case WM_KEYDOWN://识别按键,显示输入的内容(对方IP地址)
- if(server!=NULL)break;
- if(wParam==) {//如果是ENTER,则初始化server、client,并开始连接,连接成功后初始化board
- init();
- Sleep();
- InvalidateRect(hwnd,NULL,true);
- }
- if(wParam==VK_BACK) {//删除键
- if(iofip==)return ;
- iofip--;
- ots_ip[iofip]='\0';
- }
- if(wParam<&&wParam>) {//小键盘数字键
- wParam-=;
- }
- if(wParam<&&wParam>) {//主键盘数字键
- ots_ip[iofip]=''-+wParam;
- iofip++;
- ots_ip[iofip]='\0';
- }
- if(wParam==||wParam==) {//小数点键,小键盘110,主键盘229
- ots_ip[iofip]='.';
- iofip++;
- ots_ip[iofip]='\0';
- }
- InvalidateRect(hwnd,NULL,true);
- break;
- case WM_PAINT: {
- static HBRUSH bk_brush =CreateSolidBrush (RGB(,,)); //棋子的背景色
- hdc=BeginPaint (hwnd,&ps) ;
- static HFONT hFont;
- LOGFONT lf;
- lf.lfHeight=PIECE_WH/;
- lf.lfWidth=;
- lf.lfEscapement=;
- lf.lfOrientation= ;
- lf.lfWeight=;
- lf.lfItalic= ;
- lf.lfUnderline= ;
- lf.lfStrikeOut= ;
- lf.lfCharSet=DEFAULT_CHARSET ;
- lf.lfOutPrecision= ;
- lf.lfClipPrecision= ;
- lf.lfQuality= ;
- lf.lfPitchAndFamily= ;
- lstrcpy (lf.lfFaceName, _T("楷体") );
- hFont = CreateFontIndirect (&lf) ;
- SelectFont(hdc,hFont);
- SelectObject(hdc,bk_brush);
- Rectangle(hdc,,,WIDTH,HEIGHT);
- SetBkColor(hdc,RGB(,,));
- if(chess_board==NULL) {//显示输入的IP地址
- char tip[]="请输入对方IP地址:";
- Rectangle(hdc,WIDTH/,HEIGHT/-,WIDTH/*,HEIGHT/+);
- TextOut(hdc,WIDTH/,HEIGHT/-,tip,strlen(tip));
- SetBkColor(hdc,RGB(,,));
- TextOut(hdc,WIDTH/+,HEIGHT/,ots_ip,strlen(ots_ip));
- if(server!=NULL) { //board==NULL而server!=NULL表示正在连接过程中
- char tip[]="正在连接......";
- SetBkColor(hdc,RGB(,,));
- TextOut(hdc,WIDTH/,HEIGHT/+,tip,strlen(tip));
- }
- EndPaint(hwnd,&ps);
- break;
- }
- char text[]="你的颜色:";
- if(chess_board->get_color()==BLACK) {
- strcat(text," 黑");
- } else {
- strcat(text," 白");
- }
- TextOut (hdc, , ,text , strlen(text)) ;
- int M=chess_board->get_M();
- int N=chess_board->get_N();
- //画棋盘
- for(int i=; i<M; i++) {
- draw_line(hdc,i,,i,N-);
- }
- for(int i=; i<N; i++) {
- draw_line(hdc,,i,N/,i);
- }
- for(int i=; i<N; i++) {
- draw_line(hdc,N/+,i,N,i);
- }
- draw_line(hdc,,,,);
- draw_line(hdc,,,,);
- draw_line(hdc,,,,);
- draw_line(hdc,,,,);
- draw_line(hdc,,,,);
- draw_line(hdc,,,,);
- //画棋子
- for(int i=; i<M; i++) {
- for(int j=; j<N; j++) {
- paint_piece(hdc,i,j,chess_board->get_color(i,j),chess_board->get_type(i,j));
- }
- }
- EndPaint(hwnd,&ps);
- }
- break;
- case WM_LBUTTONUP: {
- if(chess_board==NULL)break;
- if(!chess_board->is_my_turn())break;//当前没轮到自己下棋
- GetCursorPos(&mouse);//获取鼠标的屏幕坐标
- ScreenToClient(hwnd,&mouse);//转换为界面坐标
- int x,y;
- pixel_to_xy(mouse.x,mouse.y,&x,&y);//转换为数组坐标
- if(chess_board->get_color(x,y)==chess_board->get_color()) {//点击的是自己的棋子
- chess_sx=x;
- chess_sy=y;
- break;
- }
- if(chess_sx==-||chess_sy==-) {//起始坐标未赋值且点击的不是自己的棋子,则break
- break;
- }
- chess_dx=x;
- chess_dy=y;
- if(chess_board->my_move_piece(chess_sx,chess_sy,chess_dx,chess_dy)) { //如果移动棋子合法
- client->send_message("move",chess_sx,chess_sy,chess_dx,chess_dy); //向对方发送移子信息
- InvalidateRect(hwnd,NULL,true);
- if(chess_board->get_is_win()==WIN) {
- chess_board->init();//重新初始化棋盘,重下一盘
- MessageBox(hwnd,"你赢了","获胜!",NULL);
- InvalidateRect(hwnd,NULL,true);
- }
- }
- chess_sx=-;
- chess_sy=-;
- break;
- }
- case WM_DESTROY:
- if(server!=NULL)server->close();
- if(client!=NULL)client->close();
- PostQuitMessage (); /* send a WM_QUIT to the message queue */
- break;
- default: /* for messages that we don't deal with */
- return DefWindowProc (hwnd, message, wParam, lParam);
- }
- return ;
- }
chinese_chess.h
- #ifndef CHINESE_CHESS_H_INCLUDED
- #define CHINESE_CHESS_H_INCLUDED
- #define JU 1
- #define MA 2
- #define XIANG 3
- #define SHI 4
- #define JIANG 5
- #define PAO 6
- #define ZU 7
- #define BLANK 8 //空子
- #define BLACK -1
- #define WHITE 1
- #define WIN 1
- #define LOSE -1
- class Board {
- private:
- bool turn; //是否轮到自己下棋
- int color; //自己的颜色
- int M,N; //棋盘行数、列数
- int **b; //二维数组
- int is_win; //是否胜利
- bool is_out(int x,int y) {//坐标是否出界
- return x>M||y>N||x<||y<;
- }
- bool is_same_color(int sx,int sy,int dx,int dy) {//源坐标与目的坐标是否是同一颜色
- return get_color(sx,sy)==get_color(dx,dy);
- }
- void swap_num(int & num1,int& num2) {//交换两个数
- num1+=num2;
- num2=num1-num2;
- num1=num1-num2;
- }
- int get_abs(int num) {//取得绝对值
- return num>=?num:-num;
- }
- int num_of_not_blank_betweenn(int sx,int sy,int dx,int dy) {//返回在起始坐标和目的坐标之间棋子的个数
- if(!(sx==dx||sy==dy))return -;
- int num=;
- if(sy>dy)swap_num(sy,dy);
- if(sx>dx)swap_num(sx,dx);
- if(sx==dx) {
- for(int i=sy+; i<dy; i++) {
- if(b[sx][i]!=BLANK)num++;
- }
- }
- if(sy==dy) {
- for(int i=sx+; i<dx; i++) {
- if(b[i][sy]!=BLANK)num++;
- }
- }
- return num;
- }
- bool is_correct_move_JU(int sx,int sy,int dx,int dy) {
- return num_of_not_blank_betweenn(sx,sy,dx,dy)==;
- }
- bool is_correct_move_MA(int sx,int sy,int dx,int dy) {
- int x=dx-sx,y=dy-sy;
- if(get_abs(x)==&&get_abs(y)==) {
- if(get_color(sx+x/,sy)==BLANK)return true;//硌马蹄检测
- }
- if(get_abs(x)==&&get_abs(y)==) {
- if(get_color(sx,sy+y/)==BLANK)return true;//硌马蹄检测
- }
- return false;
- }
- bool is_correct_move_XIANG(int sx,int sy,int dx,int dy) {
- int x=dx-sx,y=dy-sy;
- if(!(get_abs(x)==&&get_abs(y)==)) return false;
- if(get_color(sx+x/,sy+y/)==BLANK)return true;//硌象蹄检测
- return false;
- }
- bool is_correct_move_SHI(int sx,int sy,int dx,int dy) {
- int x=dx-sx,y=dy-sy;
- if(!(get_abs(x)==&&get_abs(y)==)) return false;
- if(dx<)return false;
- if(dy<||dy>)return false;
- return true;
- }
- bool is_correct_move_JIANG(int sx,int sy,int dx,int dy) {
- int x=dx-sx,y=dy-sy;
- if(!((get_abs(x)==&&get_abs(y)==)||(get_abs(x)==&&get_abs(y)==))) return false;
- if(dx<)return false;
- if(dy<||dy>)return false;
- for(int i=; i<; i++) {//明将检测
- if(get_type(i,dy)==JIANG) {
- if(num_of_not_blank_betweenn(dx,dy,i,dy)==) return false;
- return true;
- }
- }
- return true;
- }
- bool is_correct_move_PAO(int sx,int sy,int dx,int dy) {
- int n=get_color(dx,dy)==BLANK?:;
- return num_of_not_blank_betweenn(sx,sy,dx,dy)==n;
- }
- bool is_correct_move_ZU(int sx,int sy,int dx,int dy) {
- if(dx>sx)return false;
- int x=dx-sx,y=dy-sy;
- if(get_abs(x)+get_abs(y)!=)return false;
- if(sx>&&get_abs(x)!=)return false;//过河前只能向前走
- return true;
- }
- bool is_correct_move(int sx,int sy,int dx,int dy) {
- if(sx==dx&&sy==dy) {
- return false;
- }
- if(is_out(sx,sy)||is_out(dx,dy)) {
- return false;
- }
- if(get_color(sx,sy)!=color) {
- return false;
- }
- if(is_same_color(sx,sy,dx,dy)) {
- return false;
- }
- switch(get_type(sx,sy)) {
- case JU:
- return is_correct_move_JU(sx,sy,dx,dy);
- case MA:
- return is_correct_move_MA(sx,sy,dx,dy);
- case XIANG:
- return is_correct_move_XIANG(sx,sy,dx,dy);
- case SHI:
- return is_correct_move_SHI(sx,sy,dx,dy);
- case JIANG:
- return is_correct_move_JIANG(sx,sy,dx,dy);
- case PAO:
- return is_correct_move_PAO(sx,sy,dx,dy);
- case ZU:
- return is_correct_move_ZU(sx,sy,dx,dy);
- default:
- return false;
- }
- }
- void move_s_to_d(int sx,int sy,int dx,int dy) { //移动操作
- if(get_type(dx,dy)==JIANG) { //如果目的棋子是将
- if(get_color(dx,dy)==color)set_win(LOSE);//如果是自己的将,则输
- else set_win(WIN);//如果是对方的将,则赢
- }
- b[dx][dy]=b[sx][sy];
- b[sx][sy]=BLANK;
- change_turn();
- }
- void init_pieces() {
- for(int i=; i<M; i+=M-) {//第一行和最后一行(即车马象士将士象马车)
- for(int index=; index<N; index++) {
- if(index<N/+)b[i][index]=index+;
- else b[i][index]=N-index;
- }
- }
- //卒所在的行
- for(int index=; index<N; index+=) {
- b[][index]=ZU;
- }
- for(int index=; index<N; index+=) {
- b[][index]=ZU;
- }
- b[][]=PAO;
- b[M--][]=PAO;
- b[][N--]=PAO;
- b[M--][N--]=PAO;
- int s,d;//存储起始行和终点行
- if(color==BLACK) {
- s=;//从0行到M/2行,即棋盘上半部分
- d=M/;
- } else {
- s=M/;//棋盘下半部分
- d=M;
- }
- //从s行到d行,把非BLANK的值加BLANK,使小于BLANK的代表黑色棋,大于BLANK的代表白色棋
- for(int index=s; index<d; index++) {
- for(int j=; j<N; j++) {
- if(b[index][j]!=BLANK) {
- b[index][j]+=BLANK;
- }
- }
- }
- }
- public:
- Board(int c) {
- color=c;
- M=;
- N=;
- b=new int*[M];
- for(int i=; i<M; i++) {
- b[i]=new int[N];
- }
- init();
- }
- void init() {//棋盘初始化
- is_win=;
- turn=color==BLACK?true:false;
- for(int i=; i<M; i++) {
- for(int j=; j<N; j++) {
- b[i][j]=BLANK;
- }
- }
- init_pieces();
- }
- int get_M() {
- return M;
- }
- int get_N() {
- return N;
- }
- int get_color() {//获取己方的颜色
- return color;
- }
- int get_color(int x,int y) {//获取棋盘某一坐标上棋子的颜色
- return b[x][y]>BLANK?WHITE:b[x][y]<BLANK?BLACK:BLANK;
- }
- int get_type(int x,int y) {//获取棋子类型(空、车、马、象、士、将、炮、卒)
- return b[x][y]!=BLANK?b[x][y]%BLANK:BLANK;
- }
- void set_win(int is) {
- is_win=is;
- }
- int get_is_win() {
- return is_win;
- }
- void change_turn() {
- turn=turn==true?false:true;
- }
- bool is_my_turn() {
- return turn;
- }
- void othside_move_piece(int sx,int sy,int dx,int dy) {//对方移子
- sx=M--sx;//先进行坐标转换,因对方视角与己方相反
- sy=N--sy;
- dx=M--dx;
- dy=N--dy;
- move_s_to_d(sx,sy,dx,dy);
- }
- bool my_move_piece(int sx,int sy,int dx,int dy) { //己方主动移子
- if(!is_correct_move(sx,sy,dx,dy))return false;
- move_s_to_d(sx,sy,dx,dy);
- return true;
- }
- };
- #endif // CHINESE_CHESS_H_INCLUDED
Server.h
- #ifndef SERVER_H_INCLUDED
- #define SERVER_H_INCLUDED
- #include<stdio.h>
- #include <stdlib.h>
- #include <errno.h>
- #include <winsock2.h>
- #include"chinese_chess.h"
- #define INIT_ERROR 1
- #define BIND_ERROR 2
- #define LISTEN_ERROR 3
- #define CONNECT_ERROR 4
- #define SEND_ERROR 5
- #define ACCEPT_ERROR 6
- #define ALIVE_ERROR 7
- #define THREAD_ERROR 8
- int error_num;
- extern HWND hwnd;
- extern Board* chess_board;
- extern char* ots_ip;
- extern int port;
- extern bool is_connect_alive;
- //线程参数结构体
- typedef struct server_args {
- SOCKET* Com_Sock;
- char * rebuf;
- } server_args;
- //校验检测,与客户端的添加校验是相反操作
- //最后一位之前的所有字符相加取模后,如果等于最后一个字符,则校验通过
- bool server_check(char * r) {
- int len=strlen(r);
- len--;
- int s=;
- for(int i=; i<len; i++) {
- s+=r[i];
- }
- if(r[len]==(s%+'')) {
- r[len]='\0';
- return true;
- }
- return false;
- }
- //错误处理,is_tell控制是否显示错误信息,is_exit控制是否退出程序
- int handle_error(int err,bool is_tell,bool is_exit) {
- error_num=err;
- if(!is_tell)return error_num;
- char error[]="";
- switch(error_num) {
- case INIT_ERROR:
- strcpy(error,"初始化错误");
- break;
- case BIND_ERROR:
- strcpy(error,"绑定端口错误");
- break;
- case LISTEN_ERROR:
- strcpy(error,"监听错误");
- break;
- case ACCEPT_ERROR:
- strcpy(error,"接受连接错误");
- break;
- case CONNECT_ERROR:
- strcpy(error,"无法连接");
- break;
- case ALIVE_ERROR:
- strcpy(error,"连接已断开");
- break;
- case THREAD_ERROR:
- strcpy(error,"线程无法创建");
- break;
- case SEND_ERROR:
- strcpy(error,"发送错误");
- }
- char error_message[];
- strcpy(error_message,"错误:");
- strcat(error_message,error);
- if(is_exit)strcat(error_message,"\n程序将退出。");
- MessageBox(hwnd,error_message,"错误",MB_OK);
- if(is_exit)exit();
- return error_num;
- }
- void* handle_message(void*ar) {
- server_args * serarg=(server_args * )ar;
- char *recv=serarg->rebuf;
- SOCKET* CommandSock=serarg->Com_Sock;
- if(server_check(recv)) {//校验通过发送okok(OK),不通过发送noto(NOTOK)
- send(*CommandSock,"okok",,);
- } else {
- send(*CommandSock,"noto",,);
- return ar;
- }
- if(strncmp(recv,"move",)==) {
- char * pch;
- //将recvBuf以逗号拆分
- pch = strtok (recv,",");
- pch = strtok (NULL,",");
- int xys[];
- int index=;
- while (pch != NULL) {
- xys[index]=atoi(pch);//char* 转换为int
- index++;
- pch = strtok (NULL, ",");
- }
- chess_board->othside_move_piece(xys[],xys[],xys[],xys[]);
- if(chess_board->get_is_win()==LOSE) {
- chess_board->init();//如果输了,则重新初始化棋盘,再下一盘
- MessageBox(hwnd,"你输了","失败!",NULL);
- }
- InvalidateRect(hwnd,NULL,true);
- }
- delete recv;
- }
- class Server {
- private:
- SOCKET Server_Sock;
- SOCKADDR_IN server_addr;
- SOCKADDR_IN client_addr;
- char recvBuf[];
- public:
- Server() {
- WSADATA wsa;
- /*初始化socket资源*/
- if (WSAStartup(MAKEWORD(,),&wsa) != ) {
- handle_error(INIT_ERROR,true,true);
- return;
- }
- if((Server_Sock = socket(AF_INET, SOCK_STREAM, ))==-) {
- handle_error(INIT_ERROR,true,true);
- return;
- }
- ZeroMemory((char *)&server_addr,sizeof(server_addr));
- server_addr.sin_family = AF_INET;
- server_addr.sin_port = htons(port); /*本地监听端口*/
- server_addr.sin_addr.s_addr = htonl(INADDR_ANY); /*有IP*/
- if(bind(Server_Sock,(struct sockaddr *)&server_addr,sizeof(server_addr))==-) {
- handle_error(BIND_ERROR,true,true);
- return;
- }
- if(listen(Server_Sock,)==-) { //其中第二个参数代表能够接收的最多的连接数
- handle_error(LISTEN_ERROR,true,true);
- return;
- }
- strcpy(recvBuf,"");
- }
- void listen_message() {
- int len=sizeof(SOCKADDR);
- while(true) {
- SOCKET Command_Sock = accept(Server_Sock, (SOCKADDR*)&client_addr,&len);
- if(Command_Sock == INVALID_SOCKET) {
- closesocket(Command_Sock);
- handle_error(ACCEPT_ERROR,false,false);
- continue;
- }
- if(client_addr.sin_addr.s_addr!=inet_addr(ots_ip)) {//如果接收的socket不是预期的对方的,则发送wron,继续等待
- send(Command_Sock,"wron",,);
- closesocket(Command_Sock);
- continue;
- }
- send(Command_Sock,"righ",,);
- is_connect_alive=true;
- while(true) {
- if(recv(Command_Sock,recvBuf,,)<=) {//recv返回小于等于0的值,则连接已断开
- handle_error(ALIVE_ERROR,true,true);
- closesocket(Command_Sock);
- close();
- return ;
- }
- char *rbuf=new char[];
- strcpy(rbuf,recvBuf);
- server_args serarg;
- serarg.Com_Sock=&Command_Sock;
- serarg.rebuf=rbuf;
- pthread_t handle_m;
- int ret;
- ret= pthread_create( &handle_m, NULL, handle_message,&serarg); //
- if( ret != ) { //创建线程成功返回0
- // printf("pthread_create error:error_code=%d\n",ret );
- handle_error(THREAD_ERROR,true,true);
- return ;
- }
- strcpy(recvBuf,"");
- }
- closesocket(Command_Sock);
- }
- }
- void close() {
- closesocket(Server_Sock);
- WSACleanup();
- }
- };
- #endif // SERVER_H_INCLUDED
Client.h
- #ifndef CLIENT_H_INCLUDED
- #define CLIENT_H_INCLUDED
- #include <stdio.h>
- #include <winsock2.h>
- #include"chinese_chess.h"
- //为字符串添加校验信息,对所有字符求和,模5之后转化为字符放在字符串最后
- void client_check(char* r) {
- int len=strlen(r);
- int s=;
- for(int i=; i<len; i++) {
- s+=r[i];
- }
- r[len]=s%+'';
- r[len+]='\0';
- }
- class Client {
- private:
- SOCKET Client_Sock;
- SOCKADDR_IN server_addr;
- char sendBuf[];
- public:
- Client() {
- WSADATA wsa;
- /*初始化socket资源*/
- if (WSAStartup(MAKEWORD(,),&wsa) != ) {
- handle_error(INIT_ERROR,true,true);
- return; //代表失败
- }
- if((Client_Sock = socket(AF_INET, SOCK_STREAM, ))==-) {
- handle_error(INIT_ERROR,true,true);
- return; //代表失败
- }
- server_addr.sin_addr.S_un.S_addr=inet_addr(ots_ip);
- server_addr.sin_family=AF_INET;
- server_addr.sin_port=htons(port);
- strcpy(sendBuf,"");
- }
- void connect_to_ots() {
- while(connect(Client_Sock,(SOCKADDR*)&server_addr,sizeof(SOCKADDR)) ==-) {
- handle_error(CONNECT_ERROR,false,false);
- Sleep();
- //printf( "%d ", WSAGetLastError());
- }
- char rec[];
- recv(Client_Sock,rec,,);
- if(strncmp(rec,"wron",)==) { //收到wrong,说明对方所输入的IP不是己方IP
- MessageBox(hwnd,"对方输入的IP不是你\n程序将退出","错误",NULL);
- exit(-);
- }
- //谁先连接谁是黑色
- //如果server已经收到连接,则说明是对方先连接自己,则自己应为白色,否则自己是黑色
- if(is_connect_alive) {
- chess_board=new Board(WHITE);
- } else {
- chess_board=new Board(BLACK);
- }
- }
- void close() {
- closesocket(Client_Sock);
- WSACleanup();
- }
- int send_message(char * message) {
- strcpy(sendBuf,message);
- client_check(sendBuf);
- int len;
- int try_time=;
- while(true) {
- len=send(Client_Sock,sendBuf,strlen(sendBuf)+,);
- if(len!=(strlen(sendBuf)+)) {
- handle_error(SEND_ERROR,false,false);
- //printf( "%d ", WSAGetLastError());
- }
- char rec[];
- recv(Client_Sock,rec,,);
- if(strncmp(rec,"okok",)==) {//收到OK说明数据已经正确被对方收到
- break;
- }
- if(try_time>) { //尝试20次,数据仍无法正确送达,则退出
- handle_error(SEND_ERROR,true,true);
- }
- try_time++;
- }
- return len;
- }
- int send_message(const char * message,int sx,int sy,int dx,int dy) {
- char* message_temp=new char[];
- sprintf(message_temp,"%s,%d,%d,%d,%d,",message,sx,sy,dx,dy);
- int len=send_message(message_temp);
- delete message_temp;
- return len;
- }
- };
- #endif // CLIENT_H_INCLUDED
该程序从2016.3.15晚开始,用了四天的空闲时间。
END
局域网象棋游戏(C++实现,使用Socket,界面使用Win32,CodeBlocks+GCC编译)的更多相关文章
- GLine游戏(Win32GUI实现,CodeBlocks+GCC编译)
游戏规则: 在10X10的棋盘上有五种颜色的棋子. 点击一个棋子,再点击一个空格子,如果两者之间有一条路径的话,棋子会移动到空格子内. 每移动一次,棋盘上会增加三个棋子,其位置和颜色都是随机的. 当横 ...
- Socket服务端和客户端(C++,CodeBlocks+GCC编译)
//main.cpp 1 #include "j_socket.h" #include <stdio.h> #include <pthread.h> ; j ...
- C#中国象棋+游戏大厅 服务器 + 客户端源码
来源:www.ajerp.com/bbs C#中国象棋+游戏大厅 服务器 + 客户端源码 源码开源 C#版中国象棋(附游戏大厅) 基于前人大虾的修改版 主要用委托实现 服务器支持在线人数,大厅桌数的设 ...
- [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 ...
- 基于HTML5实现的中国象棋游戏
棋类游戏在桌面游戏中已经非常成熟,中国象棋的版本也非常多.今天这款基于HTML5技术的中国象棋游戏非常有特色,我们不仅可以选择中国象棋的游戏难度,而且可以切换棋盘的样式.程序写累了,喝上一杯咖啡,和电 ...
- 中国象棋游戏Chess(3) - 实现走棋规则
棋盘的绘制和走棋参看博文:中国象棋游戏Chess(1) - 棋盘绘制以及棋子的绘制,中国象棋游戏Chess(2) - 走棋 现在重新整理之前写的代码,并且对于每个棋子的走棋规则都进行了限制,不像之前那 ...
- 中国象棋游戏Chess(2) - 走棋
之前的文章请看:中国象棋游戏Chess(1) - 棋盘绘制以及棋子的绘制 现在实现走棋的功能. 首先需要获取点击到的棋子,用QWidget中的函数 mouseReleaseEvent 实现函数: vo ...
- C/C++编程笔记:C语言打造中国象棋游戏,项目源代码分享!
中国象棋是起源于中国的一种棋,属于二人对抗性游戏的一种,在中国有着悠久的历史.由于用具简单,趣味性强,成为流行极为广泛的棋艺活动. 它是中国棋文化,也是中华民族的文化瑰宝,它源远流长,趣味浓厚,基本规 ...
- u3d局域网游戏网络(c# socket select 模型)——续
原文:http://www.cnblogs.com/saucerman/p/5555793.html 因为项目要加语音.语音数据都非常大.所以顺带就把之前写的网络模块一起测试了. 然后发现了一些bug ...
随机推荐
- Python模拟登陆新浪微博
上篇介绍了新浪微博的登陆过程,这节使用Python编写一个模拟登陆的程序.讲解与程序如下: 1.主函数(WeiboMain.py): import urllib2 import cookielib i ...
- C站投稿189网盘视频源(UP主篇)
C站投稿189网盘视频源(UP主篇) 现在C站(吐槽弹幕网)的视频来源基本靠的都是189网盘,比如番剧区的每个视频基本来源于此,不像AB两站,拥有自己的资源服务器,为啥呢?没钱啊.都是外来的视频.本站 ...
- CSS3属性 box-shadow 向框添加一个或多个阴影
CSS3属性 利用box-shadow制作网页页眉背景 box-shadow 浏览器支持 IE9+.Firefox 4.Chrome.Opera 以及 Safari 5.1.1 支持 box-shad ...
- [Spring]支持注解的Spring调度器
概述 如果想在Spring中使用任务调度功能,除了集成调度框架Quartz这种方式,也可以使用Spring自己的调度任务框架. 使用Spring的调度框架,优点是:支持注解(@Scheduler),可 ...
- 来,一起让我们越来越懒,面向CSS、JS未来编程。(9.28已更新)
2016.10.29更新 本文存在大量的错误,仅供参考. 不知不觉在前端领域马上一个年头就要过去了,然而再看看自己的代码,果然够烂,那么为什么代码一直没有用面向对象的思维去写CSS呢?首先有两点:一点 ...
- WriteLog
public class WriteLog { /// <summary> /// 创建日志文件 /// </summary& ...
- 深度|作为C端应用的代表,成功的陌生社交应用是什么样子的?
作 为C端应用的代表,成功的陌生社交应用是什么样子的?活跃用户数?收益回报率?在实际社交产品设计中,我们一直为这些所谓的KPI左右,具体到设计行为 上:摆弄相应的界面元素,优化一下文案.页面流,但却很 ...
- 来玩Play框架04 表单
作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 表单(form)是最常见的从客户往服务器传递数据的方式.Play框架提供了一些工具 ...
- 《连载 | 物联网框架ServerSuperIO教程》- 3.设备驱动介绍
1.C#跨平台物联网通讯框架ServerSuperIO(SSIO)介绍 <连载 | 物联网框架ServerSuperIO教程>1.4种通讯模式机制. <连载 | 物联网框架Serve ...
- Atitit.数据采集器 dataspider
Atitit.数据采集器 dataspider /atiplat_cms/src/com/attilax/WebInfoX.java @dep http://cl.cmcher.com/thread ...