网络联机中国象棋

您所在的位置:网站首页 中国象棋可以联机 网络联机中国象棋

网络联机中国象棋

2024-02-12 19:04| 来源: 网络整理| 查看: 265

程序截图

环境配置

visual studio 2019,EasyX_20211109,字符集配置为 Unicode 字符集。

网络联机说明

网络编程需要一个服务器,这个服务器可以是自己的电脑,但最好是一台在公网上有固定 ip 的电脑,云服务器就是让你可以远程的这类电脑,至于自己的电脑作为服务器,那就得自己在 cmd 里输入 ipconfig/all 来查看“服务器 ip 地址”也就是自己电脑的 IP 地址。

IP 是因特网互联协议(Internet Protocol)的缩写,而 IP 地址是一个具体的地址,IP 这种协议是发送数据时要解码的一段数据,而 IP 地址是 IP 这种协议里面要包含的内容。

这个程序用的 ip 地址是 ipv4 地址,也就是用 4 字节的数据表示一个 IP 地址,也就是一个 unsigned long 来表示 ip 地址。

127.0.0.1 表示本主机,也就是 localhost,你可以不知道你电脑的 ip 地址,但只要在这台电脑上连到 127.0.0.1 就会连到自己。

然后用到 TCP 协议,TCP 就是传输控制协议(Transmission Control Protocol)的缩写,TCP 协议跟 IP 协议一样是一段发送出去的数据,有特定的编码方式,没什么好讲的,调一下别人封装好的库就能用了。

然后是端口号(port),这个是告诉电脑这段数据是给哪个程序的,也就是找到目标应用程序,是一个 16 位无符号整数,也就是一个 unsigned short 数据类型,不过在能取的所有值即 0~65535 的整数中,0~1024 的整数是给操作系统用的,应用程序的端口号一般大于 1024。

需要事先知道的概念就这些,当然网络编程还有很多概念,但是都没用上。

具体到编程语言的实现就是套接字(Socket)编程,我先给出两个案例,先别点进去看。

1:网络编程 helloworld

2:select 模式介绍

用到的库是这个库函数,这个库需要预加载,要开始套接字编程时需要先调用 WSAStartup,结束套接字编程时需要调用 WSACleanup。

也就是说模板是:

#include #pragma comment(lib, "Ws2_32.lib") int main() { WSADATA wsData; // WSADATA windows 异步套接字数据 // MAKEWORD(a, b)是一个宏,等效于 (a|(bnum)++; } return 0; } int main() { HANDLE thread; bool isexit = false; int num = 0; ThreadFuncParam threadfuncparam; threadfuncparam.isexit = &isexit; threadfuncparam.num = # thread = CreateThread(NULL, 0, ThreadFunc, &threadfuncparam, 0, NULL); // 创建线程,0、NULL 照抄,ThreadFunc、&threadfuncparam 是必须得传的 WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), L"回车使线程结束\n", 8, NULL, NULL); // 8 是字符个数,\0 不计在内 getchar(); isexit = true; WaitForSingleObject(thread, INFINITE); // 等待线程结束,INFINITE 是永远等待的意思 CloseHandle(thread); // 关闭线程 wchar_t t_Buf[128]; size_t len = 0; StringCchPrintf(t_Buf, 128, L"线程运行秒数:%d\n", num); // 这个相当于 sprintf 函数 StringCchLength(t_Buf, 128, &len); // 这个相当于 strlen 函数 WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), t_Buf, len, NULL, NULL); // 这个相当于 printf 函数 getchar(); return 0; }

如果你已经看到这里了,其实后面的已经可以不用看了,完全可以独自写出来一个联机的程序,我这个项目就是证明。

个人实现

接下来就是个人实现这个项目的方法。

首先我实现了字符集转换,具体看字符集转换。字符集转换方便接受消息和显示消息。

服务器端用的是 select 模式,这种轮询的方式有信息进来不能立刻处理,得问问对面准备好了没有,准备好了再发送信息过去,所以需要一个缓冲区来将信息的处理先存进里面去,能发送消息的时候一次性取出来发过去,同时 select 模式下的 fd_set 的规则很简单,在 vs 里看一下源代码就知道了,只是一个动态数组的逻辑,而且最大接入数是固定的 FD_SETSIZE,直接用数组下标来区分是哪个客户端的消息处理即可,所以这个缓冲区我的写法是

std::vector allmessage[FD_SETSIZE];

TheSendMessage 是发送消息的某种编码规则,长这样:

struct TheSendMessage { char code; size_t len; char message[SENDMESSAGEMAXLEN]; };

SENDMESSAGEMAXLEN 是一条消息最长能是多长。

code 是消息码,方便接受到消息后进行解码,比如客户端告诉服务器端我进入了某个房间,如果是先发"I'll go into the room"让服务器判断一下,再发过去一个整型的数据,效率肯定不如用一个字节的消息码直接告诉服务器我要进入房间。

所有状态码的取值都用宏定义来固定

#define ISERRORMESSAGE 0 // 错误信息 #define ISSENDMESSAGE 1 // 发送消息 #define ISCONNECTNUMCHANGE 2// 在线人数改变 #define ISENTERROOM 3 // 进入房间 #define ISEXITROOM 4 // 退出房间 #define ISCONNECTMAXNUM 5 // 最大连接人数 #define ISHALLMESSAGE 6 // 大厅中房间有无人的数据 #define ISCHESSMOVE 7 // 棋子移动的数据 #define ISUNDO 8 // 嗯。。。 #define ISGIVEUP 9 // 认输 #define ISGETREADY 10 // 准备

len 是接收到的数据的有效长度,接受到一个信息接收到什么时候结束就通过这个数据来控制,不然接受着接受着把下一条信息的内容都给接受了就不好了。

接收到信息,解完码,再处理接收到的信息,流程就是这样。

接受信息的操作是用多线程来操作的,使线程退出的方法用的是一个 bool* isexit; 和在主线程里连接到服务器端来控制。用指针可以不用定义全局变量也能让两个线程共享内存,这样我在主线程让 isexit 变成 true 线程里的 isexit 也会变成 true,况且我也只开一个线程,不用考虑脏读的问题。服务器端连接到服务器端是为了避免无数据接入时的 select 阻塞住导致线程无法退出。这些待会看服务器端主函数代码的时候就能看到。

客户端就要先实现象棋的逻辑,这些我写在了 Chess.h 头文件里,这个反而是整个项目里最简单的事情。

最后以客户端为第一视角介绍一下这个项目。

首先打开程序会进入这个初始界面,这时没有联机,服务器还不知道你的存在。

点击联机,服务器知道了你的存在,它给你发来最大在线人数,和在线人数,并且你开辟出一块内存作为房间表,服务器告诉你每个房间里有没有人,你一一记录下来,与此同时你终于看到了大厅的模样。

一种情况

另一种情况

有人进入房间时你看到的会是这样。这时候服务器端挨个找了每一个在线的人,告诉他们有人进入某个房间了,要你们修改一下房间表,当你进入房间时,你会告诉服务器你进入了某个房间,服务器同时继续挨个告诉。

进入房间后双方都准备好。

提子

落子

落子那一瞬间你告诉服务器我要从 6,9 的位置下到 4,7 的位置,服务器告诉你对面的人你的下法,你对面的人同时将你的下法转换了下坐标系,也就是 8-6,9-9、8-4,9-7,最后对面那位得到的值是 2,0 到 4,2 的位置。

客户端和服务器端的位数要相同,因为代码里用到了 size_t,size_t 在 x32 下是 unsigned int 在 x64 下是 unsigned long long,我这里用的是 x64,接受消息时解码 size_t 的内容用的是 ntohll,发送消息时编码用的是 htonll,如果改为 32 位的程序就把所有 ntohll htonll 改一下。vs 下设置为 x64 编译器的方法为:

这个项目上传到 gitee 上了,不想配置直接下载就行:中国象棋联机

服务器端 server:

#define _WINSOCK_DEPRECATED_NO_WARNINGS #define FD_SETSIZE 64 #define HALLNUM (FD_SETSIZE>>1) #include #include #include #include #include #pragma comment(lib, "Ws2_32.lib") #define SENDMESSAGEMAXLEN 300 #define ISERRORMESSAGE 0 #define ISSENDMESSAGE 1 #define ISCONNECTNUMCHANGE 2 #define ISENTERROOM 3 #define ISEXITROOM 4 #define ISCONNECTMAXNUM 5 #define ISHALLMESSAGE 6 #define ISCHESSMOVE 7 #define ISUNDO 8 #define ISGIVEUP 9 #define ISGETREADY 10 struct People { char roomnumber; bool isOne; }; struct PeopleList { People arr[FD_SETSIZE]; size_t len; }; struct Room { int people1, people2; bool ispeople1, ispeople2; }; void InitPeopleList(PeopleList* peoplelist) { for (int i = 0; i < FD_SETSIZE; i++) { peoplelist->arr[i].roomnumber = -1; } peoplelist->len = 1; } size_t TransformCharToWideChar(const char* target, wchar_t** output) { size_t len = MultiByteToWideChar(CP_ACP, 0, target, -1, NULL, 0); TCHAR* arr = new TCHAR[len]; MultiByteToWideChar(CP_ACP, 0, target, -1, arr, len); *output = arr; return len; } struct TheSendMessage { char code; size_t len; char message[SENDMESSAGEMAXLEN]; }; bool AddTSM(std::vector* array, char code, size_t len, const char* message) { // if (array->len >= MAXMESSAGE)return false; TheSendMessage temp; temp.code = code; temp.len = len; for (int i = 0; i < len; i++)temp.message[i] = message[i]; array->push_back(temp); return true; } struct DealMessageParam { SOCKET serveSocket; fd_set* SocketArray; Room (*hall)[HALLNUM]; PeopleList *peoplelist; std::vector (*allmessage)[FD_SETSIZE]; bool isExit; }; void My_Strcpy(char* target, const char* source, size_t len) { for (int i = 0; i < len; i++) { target[i] = source[i]; } } bool My_Strcmp(const char* target, const char* source, size_t len) { for (size_t i = 0; i < len; i++) if (target[i] != source[i])return false; return true; } void WINAPI My_Printf(const char* arr) { wchar_t* tarr; size_t szlen = TransformCharToWideChar(arr, &tarr); WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), tarr, szlen - 1, NULL, NULL); // \0 被计算进去 delete[] tarr; } void WINAPI My_Printf(const char* arr, const char* another) { wchar_t* tarr; wchar_t* tanother; size_t szlen = TransformCharToWideChar(arr, &tarr); szlen += TransformCharToWideChar(another, &tanother); wchar_t* OutMsg = new wchar_t[szlen]; StringCchPrintf(OutMsg, szlen, tarr, tanother); WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), OutMsg, szlen - 4, NULL, NULL); // % s \0 \0 这四个被计算进去 delete[] tarr; delete[] tanother; delete[] OutMsg; } DWORD WINAPI DealMessage(LPVOID lparam) { fd_set AllWrite; FD_ZERO(&AllWrite); DealMessageParam* param = (DealMessageParam*)lparam; while (!param->isExit) { fd_set fdRead = *param->SocketArray; fd_set fdWrite = AllWrite; int iRet = select(0, &fdRead, &fdWrite, NULL, NULL); if (iRet > 0) { for (size_t i = 0; i < param->SocketArray->fd_count; i++) { if (FD_ISSET(param->SocketArray->fd_array[i], &fdWrite)&& param->SocketArray->fd_array[i]!=param->serveSocket)// 如果在可写集合里不为主机 { for (int j = 0; j < (*param->allmessage)[i].size(); j++)// 所有种类的信息看看那条能发送 { if ((*param->allmessage)[i][j].code != -1) { send(param->SocketArray->fd_array[i], &(*param->allmessage)[i][j].code, sizeof(char), 0); size_t messagelen = htonll((*param->allmessage)[i][j].len); send(param->SocketArray->fd_array[i], (char*)&messagelen, sizeof(messagelen), 0); send(param->SocketArray->fd_array[i], (char*)(*param->allmessage)[i][j].message, sizeof(char)* (*param->allmessage)[i][j].len, 0); (*param->allmessage)[i][j].code = -1; } } (*param->allmessage)[i].clear(); FD_CLR(param->SocketArray->fd_array[i], &AllWrite); // My_Printf("完事了"); } if (FD_ISSET(param->SocketArray->fd_array[i], &fdRead))// 如果连到服务器的套接字在可读的区域里 { if (param->SocketArray->fd_array[i] == param->serveSocket)// 如果可读的是自己 { if (param->SocketArray->fd_count < FD_SETSIZE) { sockaddr_in clientAddress; int clientlen = sizeof(clientAddress); SOCKET clientSocket = accept(param->serveSocket, (sockaddr*)&clientAddress, &clientlen); // 接受 FD_SET(clientSocket, param->SocketArray); for (int index = 1; index < param->SocketArray->fd_count; index++)// 告诉所有现存的人新人来了 { FD_SET(param->SocketArray->fd_array[index], &AllWrite); if (param->SocketArray->fd_array[index] == clientSocket)// 如果是新加入的那个人 { unsigned int maxconnect = htonl(FD_SETSIZE); AddTSM(&(*param->allmessage)[index], ISCONNECTMAXNUM, sizeof(unsigned int), (char*)&maxconnect); char t_sendmessage[FD_SETSIZE]; for (int t_index = 0; t_index < HALLNUM; t_index++) { t_sendmessage[2 * t_index] = (*param->hall)[t_index].ispeople1; t_sendmessage[2 * t_index + 1] = (*param->hall)[t_index].ispeople2; } AddTSM(&(*param->allmessage)[index], ISHALLMESSAGE, FD_SETSIZE, (char*)t_sendmessage); } unsigned int connectnum = htonl(param->SocketArray->fd_count - 1); AddTSM(&(*param->allmessage)[index], ISCONNECTNUMCHANGE, sizeof(unsigned int), (char*)&connectnum); } param->peoplelist->len++; My_Printf("接受到连接:%s\n", inet_ntoa(clientAddress.sin_addr)); } else { My_Printf("连接太多\n"); } } else { char szText[SENDMESSAGEMAXLEN + sizeof(char)+sizeof(size_t)]; int iRecv = recv(param->SocketArray->fd_array[i], szText, SENDMESSAGEMAXLEN + sizeof(char) + sizeof(size_t), 0); // 接受到消息 if (iRecv > 0) { switch (szText[0]) { case ISSENDMESSAGE: { if (param->peoplelist->arr[i].roomnumber != -1) { unsigned int t_roomnumber = param->peoplelist->arr[i].roomnumber; if (param->peoplelist->arr[i].isOne && (*param->hall)[t_roomnumber].ispeople2) { int theIndex = (*param->hall)[t_roomnumber].people2; FD_SET(param->SocketArray->fd_array[theIndex], &AllWrite); size_t messagelen = *(size_t*)(szText + sizeof(char)); messagelen = ntohll(messagelen); AddTSM(&(*param->allmessage)[theIndex], ISSENDMESSAGE, messagelen, (char*)&szText[sizeof(char) + sizeof(size_t)]); } else if (!param->peoplelist->arr[i].isOne && (*param->hall)[t_roomnumber].ispeople1) { int theIndex = (*param->hall)[t_roomnumber].people1; FD_SET(param->SocketArray->fd_array[theIndex], &AllWrite); size_t messagelen = *(size_t*)(szText + sizeof(char)); messagelen = ntohll(messagelen); AddTSM(&(*param->allmessage)[theIndex], ISSENDMESSAGE, messagelen, (char*)&szText[sizeof(char) + sizeof(size_t)]); } } } break; case ISENTERROOM: { size_t messagelen = *(size_t*)(szText + sizeof(char)); messagelen = ntohll(messagelen); unsigned int roomnumber = ntohl(*(unsigned int*)(szText + sizeof(char) + sizeof(size_t))); if (roomnumber >= HALLNUM) { FD_SET(param->SocketArray->fd_array[i], &AllWrite); AddTSM(&(*param->allmessage)[i], ISERRORMESSAGE, 12, "no this room"); } else if (param->peoplelist->arr[i].roomnumber != -1) { FD_SET(param->SocketArray->fd_array[i], &AllWrite); AddTSM(&(*param->allmessage)[i], ISERRORMESSAGE, 15, "you are in room"); } else { int theroomnumber = -1; if (!(*param->hall)[roomnumber].ispeople1) { (*param->hall)[roomnumber].ispeople1 = true; (*param->hall)[roomnumber].people1 = i; param->peoplelist->arr[i].isOne = true; param->peoplelist->arr[i].roomnumber = roomnumber; theroomnumber = 2 * roomnumber; } else if (!(*param->hall)[roomnumber].ispeople2) { (*param->hall)[roomnumber].ispeople2 = true; (*param->hall)[roomnumber].people2 = i; param->peoplelist->arr[i].isOne = false; param->peoplelist->arr[i].roomnumber = roomnumber; theroomnumber = 2 * roomnumber + 1; } else { FD_SET(param->SocketArray->fd_array[i], &AllWrite); AddTSM(&(*param->allmessage)[i], ISERRORMESSAGE, 16, "the room is full"); } if (theroomnumber != -1) { for (int index = 1; index < param->SocketArray->fd_count; index++) { FD_SET(param->SocketArray->fd_array[index], &AllWrite); unsigned int temp = htonl(theroomnumber); AddTSM(&(*param->allmessage)[index], ISENTERROOM, sizeof(unsigned int), (char*)&temp); } } } } break; case ISEXITROOM: { if (param->peoplelist->arr[i].roomnumber == -1) { FD_SET(param->SocketArray->fd_array[i], &AllWrite); AddTSM(&(*param->allmessage)[i], ISERRORMESSAGE, 15, "you are in hall"); } else { unsigned int theroomnumber = 0; if (param->peoplelist->arr[i].isOne) { (*param->hall)[param->peoplelist->arr[i].roomnumber].ispeople1 = false; (*param->hall)[param->peoplelist->arr[i].roomnumber].people1 = -1; theroomnumber = 2 * param->peoplelist->arr[i].roomnumber; } else { (*param->hall)[param->peoplelist->arr[i].roomnumber].ispeople2 = false; (*param->hall)[param->peoplelist->arr[i].roomnumber].people2 = -1; theroomnumber = 2 * param->peoplelist->arr[i].roomnumber + 1; } param->peoplelist->arr[i].roomnumber = -1; param->peoplelist->arr[i].isOne = false; for (int index = 1; index < param->SocketArray->fd_count; index++) { FD_SET(param->SocketArray->fd_array[index], &AllWrite); unsigned int temp = htonl(theroomnumber); AddTSM(&(*param->allmessage)[index], ISEXITROOM, sizeof(unsigned int), (char*)&temp); } } } break; case ISCHESSMOVE: { unsigned int t_roomnumber = param->peoplelist->arr[i].roomnumber; int t_index=-1; if (param->peoplelist->arr[i].isOne) { if(param->hall[t_roomnumber]->ispeople2) t_index = param->hall[t_roomnumber]->people2; } else { if (param->hall[t_roomnumber]->ispeople1) t_index = param->hall[t_roomnumber]->people1; } if (t_index != -1) { size_t messagelen = *(size_t*)(szText + sizeof(char)); messagelen = ntohll(messagelen); FD_SET(param->SocketArray->fd_array[t_index], &AllWrite); AddTSM(&(*param->allmessage)[t_index], ISCHESSMOVE, messagelen, (char*)&(szText[sizeof(char) + sizeof(size_t)])); } else { FD_SET(param->SocketArray->fd_array[i], &AllWrite); AddTSM(&(*param->allmessage)[i], ISERRORMESSAGE, 21, "no people in opposite"); } } break; case ISGETREADY: { unsigned int t_roomnumber = param->peoplelist->arr[i].roomnumber; int t_index = -1; if (param->peoplelist->arr[i].isOne) { if (param->hall[t_roomnumber]->ispeople2) t_index = param->hall[t_roomnumber]->people2; } else { if (param->hall[t_roomnumber]->ispeople1) t_index = param->hall[t_roomnumber]->people1; } if (t_index != -1) { char t_code = ISGETREADY; // 填充的数据 FD_SET(param->SocketArray->fd_array[t_index], &AllWrite); AddTSM(&(*param->allmessage)[t_index], ISGETREADY, sizeof(char), &t_code); } else { FD_SET(param->SocketArray->fd_array[i], &AllWrite); AddTSM(&(*param->allmessage)[i], ISERRORMESSAGE, 21, "no people in opposite"); } } break; case ISGIVEUP: { unsigned int t_roomnumber = param->peoplelist->arr[i].roomnumber; int t_index = -1; if (param->peoplelist->arr[i].isOne) { if (param->hall[t_roomnumber]->ispeople2) t_index = param->hall[t_roomnumber]->people2; } else { if (param->hall[t_roomnumber]->ispeople1) t_index = param->hall[t_roomnumber]->people1; } if (t_index != -1) { char t_code = ISGIVEUP; // 填充的数据 FD_SET(param->SocketArray->fd_array[t_index], &AllWrite); AddTSM(&(*param->allmessage)[t_index], ISGIVEUP, sizeof(char), &t_code); } else { FD_SET(param->SocketArray->fd_array[i], &AllWrite); AddTSM(&(*param->allmessage)[i], ISERRORMESSAGE, 21, "no people in opposite"); } } break; default: break; } } // 断开连接 else { closesocket(param->SocketArray->fd_array[i]); int theroomnumber = -1; if (param->peoplelist->arr[i].roomnumber != -1) { if (param->peoplelist->arr[i].isOne) { (*param->hall)[param->peoplelist->arr[i].roomnumber].ispeople1 = false; (*param->hall)[param->peoplelist->arr[i].roomnumber].people1 = -1; theroomnumber = param->peoplelist->arr[i].roomnumber * 2; } else { (*param->hall)[param->peoplelist->arr[i].roomnumber].ispeople2 = false; (*param->hall)[param->peoplelist->arr[i].roomnumber].people2 = -1; theroomnumber = param->peoplelist->arr[i].roomnumber * 2 + 1; } param->peoplelist->arr[i].roomnumber = -1; param->peoplelist->arr[i].isOne = false; } for (int index = i + 1; index < param->peoplelist->len; index++) { if (param->peoplelist->arr[index].roomnumber != -1) { if (param->peoplelist->arr[index].isOne)(*param->hall)[param->peoplelist->arr[index].roomnumber].people1--; else (*param->hall)[param->peoplelist->arr[index].roomnumber].people2--; } param->peoplelist->arr[index - 1] = param->peoplelist->arr[index]; } param->peoplelist->len--; FD_CLR(param->SocketArray->fd_array[i], param->SocketArray); for (int index = 1; index < param->SocketArray->fd_count; index++)// 告诉所有现存的人有人走了 { FD_SET(param->SocketArray->fd_array[index], &AllWrite); unsigned int connectnum = htonl(param->SocketArray->fd_count - 1); AddTSM(&(*param->allmessage)[index], ISCONNECTNUMCHANGE, sizeof(unsigned int), (char*)&connectnum); if (theroomnumber != -1) { unsigned int temp = htonl(theroomnumber); AddTSM(&(*param->allmessage)[index], ISEXITROOM, sizeof(unsigned int), (char*)&temp); } } i--; } } } } } else { My_Printf("故障了\n"); closesocket(param->serveSocket); FD_CLR(param->serveSocket, param->SocketArray); break; } } return 0; } int main() { WSAData wsa; WSAStartup(MAKEWORD(2, 2), &wsa); SOCKET serveSocket = socket(AF_INET, SOCK_STREAM, 0); sockaddr_in serveAddress; serveAddress.sin_addr.S_un.S_addr = htonl(ADDR_ANY); // ip 地址 serveAddress.sin_family = AF_INET; // ipv4 serveAddress.sin_port = htons(6000); // 端口号 bind(serveSocket, (sockaddr*)&serveAddress, sizeof(serveAddress)); // 绑定 listen(serveSocket, 5); // 监听 fd_set fdSocket; FD_ZERO(&fdSocket); FD_SET(serveSocket, &fdSocket); Room hall[HALLNUM]; PeopleList peoplelist; std::vector allmessage[FD_SETSIZE]; InitPeopleList((PeopleList*)&peoplelist); for (int i = 0; i < HALLNUM; i++) { hall[i].ispeople1 = false; hall[i].ispeople2 = false; hall[i].people1 = -1; hall[i].people2 = -1; } HANDLE DealMes; DealMessageParam dealmessageparam; dealmessageparam.isExit = false; dealmessageparam.SocketArray = &fdSocket; dealmessageparam.serveSocket = serveSocket; dealmessageparam.allmessage = &allmessage; dealmessageparam.peoplelist = &peoplelist; dealmessageparam.hall = &hall; DealMes = CreateThread(NULL, 0, DealMessage, &dealmessageparam, 0, NULL); getch(); dealmessageparam.isExit = true; SOCKET clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); serveAddress.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); serveAddress.sin_port = htons(6000); serveAddress.sin_family = AF_INET; connect(clientSocket, (sockaddr*)&serveAddress, sizeof(serveAddress)); // 连过去避免 select 阻塞住,可控一些 WaitForSingleObject(DealMes, INFINITE); CloseHandle(DealMes); shutdown(serveSocket, SD_RECEIVE); for (int i = 0; i < fdSocket.fd_count; i++) { closesocket(fdSocket.fd_array[i]); } WSACleanup(); return 0; }

My_Gui_Button.h:

#pragma once #include #include #include TCHAR* TransformCharToWideChar(const char* target) { int len = MultiByteToWideChar(CP_ACP, 0, target, -1, NULL, 0); TCHAR* arr = new TCHAR[len]; MultiByteToWideChar(CP_ACP, 0, target, -1, arr, len); return arr; } namespace MGB { struct Vec2 { double x, y; }; struct Button { Vec2 pos; Vec2 anchor; Vec2 Size; std::function CallbackFunc; std::string name; void operator=(Button num) { this->anchor = num.anchor; this->CallbackFunc = num.CallbackFunc; this->name = num.name; this->pos = num.pos; this->Size = num.Size; } }; struct List_Node_Button { Button button; List_Node_Button* next; }; struct List_Button { List_Node_Button* head; unsigned long long len; List_Node_Button* operatButton; bool ispress; }; } bool isInButton(double x, double y, MGB::Button num) { double l_x = num.pos.x - num.Size.x * num.anchor.x; double l_y = num.pos.y - num.Size.y * num.anchor.y; double r_x = num.pos.x + num.Size.x * (1 - num.anchor.x); double r_y = num.pos.y + num.Size.y * (1 - num.anchor.y); if (x > l_x && x < r_x && y > l_y && y < r_y)return true; return false; } void drawRoundedRectangle(double l_x, double l_y, double r_x, double r_y) { double Size_x = r_x - l_x; double Size_y = r_y - l_y; roundrect(l_x, l_y, r_x, r_y, Size_x / 3.0, Size_y / 3.0); } void DrawButton(MGB::Button num, MGB::Vec2 offset = { 0, 0 }) { double l_x = num.pos.x - num.Size.x * num.anchor.x; double l_y = num.pos.y - num.Size.y * num.anchor.y; double r_x = num.pos.x + num.Size.x * (1 - num.anchor.x); double r_y = num.pos.y + num.Size.y * (1 - num.anchor.y); drawRoundedRectangle(l_x, l_y, r_x, r_y); l_x += num.Size.x / 6.0; l_y += num.Size.y / 6.0; r_x -= num.Size.x / 6.0; r_y -= num.Size.y / 6.0; double Size_x = r_x - l_x; double Size_y = r_y - l_y; if (num.name.size() != 0) { settextstyle(Size_y, Size_x / (double)num.name.size(), _T("Consolas")); TCHAR* arr = TransformCharToWideChar(num.name.c_str()); outtextxy(l_x + offset.x * Size_x, l_y + offset.y * Size_y, arr); delete[] arr; } } void DrawButton_Restore(MGB::Button num) { COLORREF col = getbkcolor(); col = col ^ ((1 next; delete temp; } head = nullptr; } void addChild_List_Node_Button(MGB::List_Node_Button* parent, MGB::List_Node_Button* child) { if (parent->next != nullptr)clearList_Node_Button(parent->next); parent->next = child; } void addToChild_List_Node_Button(MGB::List_Node_Button* head, MGB::List_Node_Button* child) { MGB::List_Node_Button* current = head; while (current->next != nullptr)current = current->next; current->next = child; } MGB::List_Button Create_List_Button() { MGB::List_Button result; result.head = nullptr; result.len = 0; result.operatButton = nullptr; result.ispress = false; return result; } void addChild_List_Button(MGB::List_Button& num, MGB::List_Node_Button* child) { if (num.head == nullptr)num.head = child; else addToChild_List_Node_Button(num.head, child); num.len++; } void Clear_List_Button(MGB::List_Button& num) { num.len = 0; clearList_Node_Button(num.head); if (num.operatButton != nullptr)delete num.operatButton; num.operatButton = nullptr; } MGB::List_Node_Button* GetOpeartButton(MGB::List_Button& num, double x, double y) { MGB::List_Node_Button* last = nullptr; MGB::List_Node_Button* current = num.head; while (current != nullptr && !isInButton(x, y, current->button)) { last = current; current = current->next; } if (current != nullptr) { if (last != nullptr) last->next = current->next; else num.head = current->next; current->next = nullptr; num.len--; } return current; } void addToHead_List_Button(MGB::List_Button& num, MGB::List_Node_Button* (&head)) { head->next = num.head; num.head = head; head = nullptr; num.len++; } void DrawList_Button(MGB::List_Button num) { MGB::List_Node_Button* current = num.head; while (current != nullptr) { DrawButton_Restore(current->button); current = current->next; } } void Trigger_Button(ExMessage* msg, MGB::List_Button& List_Button) { if (!List_Button.ispress && msg->lbutton) { List_Button.ispress = true; if (List_Button.operatButton != nullptr)DrawButton_Response(List_Button.operatButton->button); DrawList_Button(List_Button); } else if (!List_Button.ispress && !msg->lbutton) { if (List_Button.operatButton == nullptr) List_Button.operatButton = GetOpeartButton(List_Button, msg->x, msg->y); else if (!isInButton(msg->x, msg->y, List_Button.operatButton->button)) { addToHead_List_Button(List_Button, List_Button.operatButton); List_Button.operatButton = GetOpeartButton(List_Button, msg->x, msg->y); } if (List_Button.operatButton != nullptr)DrawButton_GetTo(List_Button.operatButton->button); DrawList_Button(List_Button); } else if (List_Button.ispress && msg->lbutton) { if (List_Button.operatButton != nullptr)DrawButton_Response(List_Button.operatButton->button); DrawList_Button(List_Button); } else if (List_Button.ispress && !msg->lbutton) { List_Button.ispress = false; DrawList_Button(List_Button); if (List_Button.operatButton != nullptr) { if (isInButton(msg->x, msg->y, List_Button.operatButton->button)) { List_Button.operatButton->button.CallbackFunc(); DrawButton_GetTo(List_Button.operatButton->button); } else DrawButton_Restore(List_Button.operatButton->button); addToHead_List_Button(List_Button, List_Button.operatButton); } } }

Chess.h

#pragma once #ifndef PI #define PI 3.1415926536 #endif // !PI 3.1415926536 #include"My_Gui_Button.h" #include #include COLORREF BOARDCOL = RGB(212, 165, 121); // 棋盘颜色 COLORREF PIECECOL = RGB(254, 201, 139); // 棋子颜色 COLORREF DIFFERENTCOL = RGB(128, 128, 128); // 不同颜色,用于填充 // 二维向量,棋盘数组的坐标 struct Vec2 { char x, y; }; // 动作类,实现动画效果 class Action { double delay; // 延迟时间 double FPS; // 帧率 double progress; // 进度 std::function Act; // 行动时的函数,根据 process 确定进行到什么程度 std::functionEnd_Act; // 行动结束时的函数 MGB::Vec2 Start; // 进行移动的行动时起始位置的标记 MGB::Vec2 End; // 进行移动的行动时终止位置的标记 std::string Sign; // 字符串标记,用于保存行动中需要的字符信息 public: Action() :delay(0), FPS(0), progress(0) { Start = { 0, 0 }; End = { 0, 0 }; } Action(double delay, double FPS, double progress) :delay(delay), FPS(FPS), progress(progress) { Start = { 0, 0 }; End = { 0, 0 }; } void SetAct(std::function Act) { this->Act = Act; } void SetEnd_Act(std::function End_Act) { this->End_Act = End_Act; } void SetBasicValue(double delay, double FPS, double progress) { this->delay = delay; this->FPS = FPS; this->progress = progress; } // 运行每一帧 bool RunEveryFrameAction() { if (progress > 1.0)return false; Act(progress); Sleep(1000 * FPS); progress += FPS / delay; if (progress > 1.0) { End_Act(); return false; } return true; } // 设置移动路径 void SetMoveAct_Route(MGB::Vec2 Start, MGB::Vec2 End) { this->Start = Start; this->End = End; } // 得到移动到现在的位置,这里可以改动轨迹方程 MGB::Vec2 GetMoveAct_Current() { return { Start.x + (End.x - Start.x) * progress, Start.y + (End.y - Start.y) * progress }; } MGB::Vec2 GetEnd() { return End; } MGB::Vec2 GetStart() { return Start; } void SetSign(std::string Sign) { this->Sign = Sign; } std::string GetSign() { return this->Sign; } }; // 棋子类,实现画棋子功能,同时也能保存棋子在棋盘中的坐标 class Piece { Vec2 pos; // 位置 std::string piece_Name; // 棋子名字 COLORREF Camp; // 阵营 public: Piece() { pos = { 0, 0 }; piece_Name = ""; Camp = BOARDCOL; } Piece(Vec2 pos, std::string piece_Name, COLORREF Camp) :piece_Name(piece_Name), Camp(Camp) { this->pos = pos; } ~Piece() {} void setPos(const Vec2 pos) { this->pos = pos; } void setpiece_Name(const std::string piece_Name) { this->piece_Name = piece_Name; } void setCamp(const COLORREF Camp) { this->Camp = Camp; } Vec2 getPos() { return pos; } std::string getpiece_Name() { return piece_Name; } COLORREF getCamp() { return Camp; } void DrawPiece_Ori(MGB::Vec2 place, MGB::Vec2 size) { double zoomOut = 0.8; setlinecolor(DIFFERENTCOL); ellipse(place.x - size.x * zoomOut / 2.0, place.y - size.y * zoomOut / 2.0, place.x + size.x * zoomOut / 2.0, place.y + size.y * zoomOut / 2.0); setfillcolor(PIECECOL); floodfill(place.x, place.y, DIFFERENTCOL); setlinecolor(PIECECOL); ellipse(place.x - size.x * zoomOut / 2.0, place.y - size.y * zoomOut / 2.0, place.x + size.x * zoomOut / 2.0, place.y + size.y * zoomOut / 2.0); setlinecolor(Camp); double Ratio = 0.7; double skewing = zoomOut * (1 - Ratio) / 4.0; ellipse(place.x - size.x * zoomOut * Ratio / 2.0, place.y - size.y * zoomOut * Ratio / 2.0 - size.y * skewing, place.x + size.x * zoomOut * Ratio / 2.0, place.y + size.y * zoomOut * Ratio / 2.0 - size.y * skewing); double a = size.x * zoomOut * Ratio / 2.0; double b = size.y * zoomOut * Ratio / 2.0; double text_size = 2 * sqrt(a * a * b * b / (a * a + b * b)); settextstyle((int)text_size, (int)(text_size / 2.0), L"Consolas"); settextcolor(Camp); setbkmode(TRANSPARENT); TCHAR* arr = TransformCharToWideChar(piece_Name.c_str()); outtextxy(place.x - text_size / 2.0, place.y - text_size / 2.0 - size.y * skewing, arr); delete[] arr; } void DrawPiece_Pick(MGB::Vec2 place, MGB::Vec2 size) { double zoomOut = 1; setlinecolor(DIFFERENTCOL); ellipse(place.x - size.x * zoomOut / 2.0, place.y - size.y * zoomOut / 2.0, place.x + size.x * zoomOut / 2.0, place.y + size.y * zoomOut / 2.0); setfillcolor(PIECECOL); floodfill(place.x, place.y, DIFFERENTCOL); setlinecolor(PIECECOL); ellipse(place.x - size.x * zoomOut / 2.0, place.y - size.y * zoomOut / 2.0, place.x + size.x * zoomOut / 2.0, place.y + size.y * zoomOut / 2.0); setlinecolor(Camp); double Ratio = 0.7; double skewing = zoomOut * (1 - Ratio) / 4.0; ellipse(place.x - size.x * zoomOut * Ratio / 2.0, place.y - size.y * zoomOut * Ratio / 2.0 - size.y * skewing, place.x + size.x * zoomOut * Ratio / 2.0, place.y + size.y * zoomOut * Ratio / 2.0 - size.y * skewing); double a = size.x * zoomOut * Ratio / 2.0; double b = size.y * zoomOut * Ratio / 2.0; double text_size = 2 * sqrt(a * a * b * b / (a * a + b * b)); settextstyle(text_size, text_size / 2.0, L"Consolas"); settextcolor(Camp); setbkmode(TRANSPARENT); TCHAR* arr = TransformCharToWideChar(piece_Name.c_str()); outtextxy(place.x - text_size / 2.0, place.y - text_size / 2.0 - size.y * skewing, arr); delete[]arr; } }; // 棋盘类,画棋盘和棋子,根据数组下标寻找该位置的棋子 class ChessBoard { Piece* map[10][9] = { nullptr }; // 地图数组 std::list alivePool; // 生存池,可以理解为静态链表 MGB::Vec2 pos; // 位置 MGB::Vec2 size; // 尺寸 COLORREF Camp; // 第一人称阵营 public: ChessBoard() { pos = { 0, 0 }; size = { 0, 0 }; Camp = BLACK; } ChessBoard(MGB::Vec2 pos, MGB::Vec2 size, COLORREF Camp) :Camp(Camp) { this->pos = pos; this->size = size; } ~ChessBoard() { ClearMap(); } MGB::Vec2 getGrid() { return { size.x / 9.0, size.y / 10.0 }; } MGB::Vec2 getStart() { return { pos.x - size.x / 2.0 + getGrid().x / 2.0, pos.y - size.y / 2.0 + getGrid().y / 2.0 }; } void initPiece(Vec2 pos, std::string name, COLORREF Camp, bool as = true, bool isonly = false) { Piece* temp; temp = new Piece(pos, name, Camp); alivePool.push_back(temp); map[pos.y][pos.x] = temp; if (isonly)return; temp = new Piece({ 8 - pos.x, pos.y }, name, Camp); alivePool.push_back(temp); map[pos.y][8 - pos.x] = temp; if (!as)return; COLORREF anoCamp = ((Camp == RED) ? BLACK : RED); temp = new Piece({ pos.x, 9 - pos.y }, name, anoCamp); alivePool.push_back(temp); map[9 - pos.y][pos.x] = temp; temp = new Piece({ 8 - pos.x, 9 - pos.y }, name, anoCamp); alivePool.push_back(temp); map[9 - pos.y][8 - pos.x] = temp; } void InitMap() { initPiece({ 0, 9 }, "車", Camp); initPiece({ 1, 9 }, "马", Camp); if (Camp == BLACK) { initPiece({ 2, 0 }, "相", RED, false); initPiece({ 2, 9 }, "象", BLACK, false); initPiece({ 0, 3 }, "兵", RED, false); initPiece({ 0, 6 }, "卒", BLACK, false); initPiece({ 2, 3 }, "兵", RED, false); initPiece({ 2, 6 }, "卒", BLACK, false); initPiece({ 4, 0 }, "帅", RED, false, true); initPiece({ 4, 9 }, "将", BLACK, false, true); initPiece({ 4, 3 }, "兵", RED, false, true); initPiece({ 4, 6 }, "卒", BLACK, false, true); } else { initPiece({ 2, 0 }, "象", BLACK, false); initPiece({ 2, 9 }, "相", RED, false); initPiece({ 0, 6 }, "兵", RED, false); initPiece({ 0, 3 }, "卒", BLACK, false); initPiece({ 2, 6 }, "兵", RED, false); initPiece({ 2, 3 }, "卒", BLACK, false); initPiece({ 4, 9 }, "帅", RED, false, true); initPiece({ 4, 0 }, "将", BLACK, false, true); initPiece({ 4, 6 }, "兵", RED, false, true); initPiece({ 4, 3 }, "卒", BLACK, false, true); } initPiece({ 3, 9 }, "士", Camp); initPiece({ 1, 7 }, "炮", Camp); } void ClearMap() { for (Piece* i : alivePool)delete i; alivePool.clear(); for (int i = 0; i < 10; i++) for (int j = 0; j < 9; j++) map[i][j] = nullptr; } void DrawArcPoint(MGB::Vec2 pos, bool isLeft = true, bool isRight = true) { MGB::Vec2 grid = { size.x / 9.0, size.y / 10.0 }; if (isLeft) { arc(pos.x - grid.x / 2.0, pos.y - grid.y / 2.0, pos.x - grid.x / 6.0, pos.y - grid.y / 6.0, PI * 3 / 2.0, PI * 2); arc(pos.x - grid.x / 2.0, pos.y + grid.y / 6.0, pos.x - grid.x / 6.0, pos.y + grid.y / 2.0, 0, PI / 2.0); } if (isRight) { arc(pos.x + grid.x / 6.0, pos.y - grid.y / 2.0, pos.x + grid.x / 2.0, pos.y - grid.y / 6.0, PI, PI * 3 / 2.0); arc(pos.x + grid.x / 6.0, pos.y + grid.y / 6.0, pos.x + grid.x / 2.0, pos.y + grid.y / 2.0, PI / 2.0, PI); } } void Draw_Board() { setlinecolor(DIFFERENTCOL); rectangle(pos.x - size.x / 2.0, pos.y - size.y / 2.0, pos.x + size.x / 2.0, pos.y + size.y / 2.0); setfillcolor(BOARDCOL); floodfill(pos.x, pos.y, DIFFERENTCOL); setlinecolor(BOARDCOL); rectangle(pos.x - size.x / 2.0, pos.y - size.y / 2.0, pos.x + size.x / 2.0, pos.y + size.y / 2.0); MGB::Vec2 grid = getGrid(); MGB::Vec2 start = getStart(); setlinecolor(RGB(62, 23, 0)); for (int i = 0; i < 4; i++) { for (int j = 0; j < 8; j++) { rectangle(grid.x * j + start.x, grid.y * i + start.y, grid.x * (j + 1) + start.x, grid.y * (i + 1) + start.y); rectangle(grid.x * j + start.x, grid.y * (i + 5) + start.y, grid.x * (j + 1) + start.x, grid.y * (i + 6) + start.y); } } line(grid.x * 3 + start.x, grid.y * 0 + start.y, grid.x * 5 + start.x, grid.y * 2 + start.y); line(grid.x * 3 + start.x, grid.y * 2 + start.y, grid.x * 5 + start.x, grid.y * 0 + start.y); line(grid.x * 3 + start.x, grid.y * 9 + start.y, grid.x * 5 + start.x, grid.y * 7 + start.y); line(grid.x * 3 + start.x, grid.y * 7 + start.y, grid.x * 5 + start.x, grid.y * 9 + start.y); // 画炮台,兵,卒,炮一开始放的地方 for (int i = 1; i < 4; i++) { DrawArcPoint({ start.x + grid.x * i * 2, start.y + grid.y * 3 }); DrawArcPoint({ start.x + grid.x * i * 2, start.y + grid.y * 6 }); } DrawArcPoint({ start.x + grid.x, start.y + grid.y * 2 }); DrawArcPoint({ start.x + grid.x, start.y + grid.y * 7 }); DrawArcPoint({ start.x + grid.x * 7, start.y + grid.y * 2 }); DrawArcPoint({ start.x + grid.x * 7, start.y + grid.y * 7 }); DrawArcPoint({ start.x, start.y + grid.y * 3 }, false, true); DrawArcPoint({ start.x, start.y + grid.y * 6 }, false, true); DrawArcPoint({ start.x + grid.x * 8, start.y + grid.y * 3 }, true, false); DrawArcPoint({ start.x + grid.x * 8, start.y + grid.y * 6 }, true, false); double zoomOut = 0.8; MGB::Vec2 offset = { (1 - zoomOut) * grid.x / 2.0, (1 - zoomOut) * grid.y / 2.0 }; settextstyle(grid.y * zoomOut, (grid.x / 2.0) * zoomOut, L"Consolas"); settextcolor(BLACK); setbkmode(TRANSPARENT); outtextxy(start.x + grid.x * 1 + offset.x, start.y + grid.y * 4 + offset.y, L"楚"); outtextxy(start.x + grid.x * 2 + offset.x, start.y + grid.y * 4 + offset.y, L"河"); outtextxy(start.x + grid.x * 5 + offset.x, start.y + grid.y * 4 + offset.y, L"汉"); outtextxy(start.x + grid.x * 6 + offset.x, start.y + grid.y * 4 + offset.y, L"界"); } void PutPiece() { MGB::Vec2 grid = { size.x / 9.0, size.y / 10.0 }; MGB::Vec2 start = { pos.x - size.x / 2.0 + grid.x / 2.0, pos.y - size.y / 2.0 + grid.y / 2.0 }; for (Piece* i : alivePool) i->DrawPiece_Ori({ start.x + grid.x * i->getPos().x, start.y + grid.y * i->getPos().y }, grid); } MGB::Vec2 GetPlace() { return pos; } MGB::Vec2 GetSize() { return size; } COLORREF GetCamp() { return Camp; } void setPos(MGB::Vec2 pos) { this->pos = pos; } void setSize(MGB::Vec2 size) { this->size = size; } void CampOpposite() { if (Camp == RED)Camp = BLACK; else Camp = RED; } Vec2 GetMapVec2(double x, double y) { MGB::Vec2 grid = { size.x / 9.0, size.y / 10.0 }; return { (char)(x / grid.x), (char)(y / grid.y) }; } Piece* GetOperatePiece(double x, double y) { MGB::Vec2 grid = { size.x / 9.0, size.y / 10.0 }; if (x > 9 * grid.x || x < 0 || y>10 * grid.y || y < 0)return nullptr; Piece* result = map[(int)(y / grid.y)][(int)(x / grid.x)]; map[(int)(y / grid.y)][(int)(x / grid.x)] = nullptr; std::list::iterator ite = alivePool.begin(); if (result != nullptr) { for (std::list::iterator ite = alivePool.begin(); ite != alivePool.end(); ite++) { if (*ite == result) { alivePool.erase(ite); break; } } } return result; } Piece* GetOperatePiece(Vec2 place) { Piece* result = map[place.y][place.x]; map[place.y][place.x] = nullptr; std::list::iterator ite = alivePool.begin(); if (result != nullptr) { for (std::list::iterator ite = alivePool.begin(); ite != alivePool.end(); ite++) { if (*ite == result) { alivePool.erase(ite); break; } } } return result; } void TakePiece(Piece& num) { MGB::Vec2 grid = { size.x / 9.0, size.y / 10.0 }; MGB::Vec2 start = { pos.x - size.x / 2.0 + grid.x / 2.0, pos.y - size.y / 2.0 + grid.y / 2.0 }; num.DrawPiece_Pick({ start.x + grid.x * num.getPos().x, start.y + grid.y * num.getPos().y }, grid); } bool isInMap(Vec2 place) { return !(place.x > 8 || place.x < 0 || place.y>9 || place.y < 0); } bool isCanThrough(Vec2 place, COLORREF Camp) { if (!isInMap(place))return false; if (map[place.y][place.x] != nullptr && map[place.y][place.x]->getCamp() == Camp)return false; return true; } Piece* GetMapPiece(Vec2 place) { return map[place.y][place.x]; } void DrawOperationalArea(std::list& list) { MGB::Vec2 grid = { size.x / 9.0, size.y / 10.0 }; MGB::Vec2 start = { pos.x - size.x / 2.0 + grid.x / 2.0, pos.y - size.y / 2.0 + grid.y / 2.0 }; setfillcolor(GREEN); for (Vec2 i : list) solidcircle(i.x * grid.x + start.x, i.y * grid.y + start.y, min(grid.x, grid.y) / 5.0); } bool PutPieceInBoard(Piece* piece) { if (piece == nullptr)return false; Piece* ori = map[piece->getPos().y][piece->getPos().x]; map[piece->getPos().y][piece->getPos().x] = piece; alivePool.push_back(piece); if (ori != nullptr) { std::list::iterator ite = alivePool.begin(); for (std::list::iterator ite = alivePool.begin(); ite != alivePool.end(); ite++) { if (*ite == ori) { alivePool.erase(ite); break; } } delete ori; return false; } return true; } bool isFailure() { for (Piece* i : alivePool) if ((i->getpiece_Name() == "帅" && i->getCamp() == Camp) || (i->getpiece_Name() == "将" && i->getCamp() == Camp))return false; return true; } }; // 管理者,管理游戏中的所有规则及处理鼠标信息 class Manager_Chess { ChessBoard operateBoard; // 用于操作的棋盘 Piece* operatePiece; // 正在操作的棋子 std::list OperationalArea; // 正在操作棋子的可移动区域 bool ispress; // 是否按下 bool isRedTurn; // 是否是红色一方的回合 bool isBlocking; // 是否阻塞,不处理鼠标信息 bool isConnectWeb; // 是否联网 Vec2 beginplace; Vec2 endplace; SOCKET clientSocket; Action Act; // 正在进行的行动 private: // 画一个阴阳鱼 void DrawTaiJi(MGB::Vec2 pericious, double radius, COLORREF Camp) { setlinecolor(Camp); arc(pericious.x - radius / 2.0, pericious.y - radius, pericious.x + radius / 2.0, pericious.y, PI / 2.0, PI * 3 / 2.0); arc(pericious.x - radius / 2.0, pericious.y, pericious.x + radius / 2.0, pericious.y + radius, -PI / 2.0, PI / 2.0); if (Camp == RED)arc(pericious.x - radius, pericious.y - radius, pericious.x + radius, pericious.y + radius, -PI / 2.0, PI / 2.0); else arc(pericious.x - radius, pericious.y - radius, pericious.x + radius, pericious.y + radius, PI / 2.0, PI * 3 / 2.0); } // 设置兵的可移动区域 void SetSoldier(Vec2 place, COLORREF Soldier_Camp) { COLORREF Camp = operateBoard.GetCamp(); if (Soldier_Camp == Camp) { if (operateBoard.isCanThrough({ place.x, place.y - 1 }, Soldier_Camp)) OperationalArea.push_back({ place.x, place.y - 1 }); if (place.y < 5) { if (operateBoard.isCanThrough({ place.x - 1, place.y }, Soldier_Camp)) OperationalArea.push_back({ place.x - 1, place.y }); if (operateBoard.isCanThrough({ place.x + 1, place.y }, Soldier_Camp)) OperationalArea.push_back({ place.x + 1, place.y }); } } else { if (operateBoard.isCanThrough({ place.x, place.y + 1 }, Soldier_Camp)) OperationalArea.push_back({ place.x, place.y + 1 }); if (place.y >= 5) { if (operateBoard.isCanThrough({ place.x - 1, place.y }, Soldier_Camp)) OperationalArea.push_back({ place.x - 1, place.y }); if (operateBoard.isCanThrough({ place.x + 1, place.y }, Soldier_Camp)) OperationalArea.push_back({ place.x + 1, place.y }); } } } // 设置马的可移动区域 void SetHorse(Vec2 place, COLORREF Horse_Camp) { if (operateBoard.isInMap({ place.x, place.y + 1 }) && operateBoard.GetMapPiece({ place.x, place.y + 1 }) == nullptr) { if (operateBoard.isCanThrough({ place.x - 1, place.y + 2 }, Horse_Camp)) OperationalArea.push_back({ place.x - 1, place.y + 2 }); if (operateBoard.isCanThrough({ place.x + 1, place.y + 2 }, Horse_Camp)) OperationalArea.push_back({ place.x + 1, place.y + 2 }); } if (operateBoard.isInMap({ place.x, place.y - 1 }) && operateBoard.GetMapPiece({ place.x, place.y - 1 }) == nullptr) { if (operateBoard.isCanThrough({ place.x - 1, place.y - 2 }, Horse_Camp)) OperationalArea.push_back({ place.x - 1, place.y - 2 }); if (operateBoard.isCanThrough({ place.x + 1, place.y - 2 }, Horse_Camp)) OperationalArea.push_back({ place.x + 1, place.y - 2 }); } if (operateBoard.isInMap({ place.x + 1, place.y }) && operateBoard.GetMapPiece({ place.x + 1, place.y }) == nullptr) { if (operateBoard.isCanThrough({ place.x + 2, place.y + 1 }, Horse_Camp)) OperationalArea.push_back({ place.x + 2, place.y + 1 }); if (operateBoard.isCanThrough({ place.x + 2, place.y - 1 }, Horse_Camp)) OperationalArea.push_back({ place.x + 2, place.y - 1 }); } if (operateBoard.isInMap({ place.x - 1, place.y }) && operateBoard.GetMapPiece({ place.x - 1, place.y }) == nullptr) { if (operateBoard.isCanThrough({ place.x - 2, place.y + 1 }, Horse_Camp)) OperationalArea.push_back({ place.x - 2, place.y + 1 }); if (operateBoard.isCanThrough({ place.x - 2, place.y - 1 }, Horse_Camp)) OperationalArea.push_back({ place.x - 2, place.y - 1 }); } } // 设置炮或车的可移动区域 void SetCarOrGun(Vec2 place, COLORREF Camp, bool isGun) { char i = 1; while (operateBoard.isInMap({ place.x, place.y + i }) && operateBoard.GetMapPiece({ place.x, place.y + i }) == nullptr) { OperationalArea.push_back({ place.x, place.y + i }); i++; } if (isGun) { i++; while (operateBoard.isInMap({ place.x, place.y + i }) && operateBoard.GetMapPiece({ place.x, place.y + i }) == nullptr)i++; } if (operateBoard.isCanThrough({ place.x, place.y + i }, Camp)) OperationalArea.push_back({ place.x, place.y + i }); i = 1; while (operateBoard.isInMap({ place.x, place.y - i }) && operateBoard.GetMapPiece({ place.x, place.y - i }) == nullptr) { OperationalArea.push_back({ place.x, place.y - i }); i++; } if (isGun) { i++; while (operateBoard.isInMap({ place.x, place.y - i }) && operateBoard.GetMapPiece({ place.x, place.y - i }) == nullptr)i++; } if (operateBoard.isCanThrough({ place.x, place.y - i }, Camp)) OperationalArea.push_back({ place.x, place.y - i }); i = 1; while (operateBoard.isInMap({ place.x - i, place.y }) && operateBoard.GetMapPiece({ place.x - i, place.y }) == nullptr) { OperationalArea.push_back({ place.x - i, place.y }); i++; } if (isGun) { i++; while (operateBoard.isInMap({ place.x - i, place.y }) && operateBoard.GetMapPiece({ place.x - i, place.y }) == nullptr)i++; } if (operateBoard.isCanThrough({ place.x - i, place.y }, Camp)) OperationalArea.push_back({ place.x - i, place.y }); i = 1; while (operateBoard.isInMap({ place.x + i, place.y }) && operateBoard.GetMapPiece({ place.x + i, place.y }) == nullptr) { OperationalArea.push_back({ place.x + i, place.y }); i++; } if (isGun) { i++; while (operateBoard.isInMap({ place.x + i, place.y }) && operateBoard.GetMapPiece({ place.x + i, place.y }) == nullptr)i++; } if (operateBoard.isCanThrough({ place.x + i, place.y }, Camp)) OperationalArea.push_back({ place.x + i, place.y }); } // 设置象的可移动区域 void SetElephant(Vec2 place, COLORREF Ele_Camp) { COLORREF Camp = operateBoard.GetCamp(); if (Ele_Camp == Camp) { if (operateBoard.isInMap({ place.x + 1, place.y + 1 }) && operateBoard.GetMapPiece({ place.x + 1, place.y + 1 }) == nullptr && operateBoard.isCanThrough({ place.x + 2, place.y + 2 }, Ele_Camp)) OperationalArea.push_back({ place.x + 2, place.y + 2 }); if (operateBoard.isInMap({ place.x + 1, place.y - 1 }) && operateBoard.GetMapPiece({ place.x + 1, place.y - 1 }) == nullptr && operateBoard.isCanThrough({ place.x + 2, place.y - 2 }, Ele_Camp) && place.y - 2 >= 5) OperationalArea.push_back({ place.x + 2, place.y - 2 }); if (operateBoard.isInMap({ place.x - 1, place.y + 1 }) && operateBoard.GetMapPiece({ place.x - 1, place.y + 1 }) == nullptr && operateBoard.isCanThrough({ place.x - 2, place.y + 2 }, Ele_Camp)) OperationalArea.push_back({ place.x - 2, place.y + 2 }); if (operateBoard.isInMap({ place.x - 1, place.y - 1 }) && operateBoard.GetMapPiece({ place.x - 1, place.y - 1 }) == nullptr && operateBoard.isCanThrough({ place.x - 2, place.y - 2 }, Ele_Camp) && place.y - 2 >= 5) OperationalArea.push_back({ place.x - 2, place.y - 2 }); } else { if (operateBoard.isInMap({ place.x + 1, place.y + 1 }) && operateBoard.GetMapPiece({ place.x + 1, place.y + 1 }) == nullptr && operateBoard.isCanThrough({ place.x + 2, place.y + 2 }, Ele_Camp) && place.y + 2 < 5) OperationalArea.push_back({ place.x + 2, place.y + 2 }); if (operateBoard.isInMap({ place.x + 1, place.y - 1 }) && operateBoard.GetMapPiece({ place.x + 1, place.y - 1 }) == nullptr && operateBoard.isCanThrough({ place.x + 2, place.y - 2 }, Ele_Camp)) OperationalArea.push_back({ place.x + 2, place.y - 2 }); if (operateBoard.isInMap({ place.x - 1, place.y + 1 }) && operateBoard.GetMapPiece({ place.x - 1, place.y + 1 }) == nullptr && operateBoard.isCanThrough({ place.x - 2, place.y + 2 }, Ele_Camp) && place.y + 2 < 5) OperationalArea.push_back({ place.x - 2, place.y + 2 }); if (operateBoard.isInMap({ place.x - 1, place.y - 1 }) && operateBoard.GetMapPiece({ place.x - 1, place.y - 1 }) == nullptr && operateBoard.isCanThrough({ place.x - 2, place.y - 2 }, Ele_Camp)) OperationalArea.push_back({ place.x - 2, place.y - 2 }); } } // 设置士的可移动区域 void SetKnight(Vec2 place, COLORREF Kni_Camp) { COLORREF Camp = operateBoard.GetCamp(); if (Kni_Camp == Camp) { if (operateBoard.isCanThrough({ place.x + 1, place.y + 1 }, Kni_Camp) && place.x + 1 = 3) OperationalArea.push_back({ place.x - 1, place.y + 1 }); if (operateBoard.isCanThrough({ place.x + 1, place.y - 1 }, Kni_Camp) && place.x + 1 = 7) OperationalArea.push_back({ place.x + 1, place.y - 1 }); if (operateBoard.isCanThrough({ place.x - 1, place.y - 1 }, Kni_Camp) && place.x - 1 >= 3 && place.y - 1 >= 7) OperationalArea.push_back({ place.x - 1, place.y - 1 }); } else { if (operateBoard.isCanThrough({ place.x + 1, place.y + 1 }, Kni_Camp) && place.x + 1 = 3) OperationalArea.push_back({ place.x - 1, place.y }); if (operateBoard.isCanThrough({ place.x + 1, place.y }, Soldier_Camp) && place.x + 1 getpiece_Name() == "帅" || operateBoard.GetMapPiece({ place.x, place.y - i })->getpiece_Name() == "将") OperationalArea.push_back({ place.x, place.y - i }); } else { if (operateBoard.isCanThrough({ place.x, place.y - 1 }, Soldier_Camp)) OperationalArea.push_back({ place.x, place.y - 1 }); if (operateBoard.isCanThrough({ place.x - 1, place.y }, Soldier_Camp) && place.x - 1 >= 3) OperationalArea.push_back({ place.x - 1, place.y }); if (operateBoard.isCanThrough({ place.x + 1, place.y }, Soldier_Camp) && place.x + 1 getpiece_Name() == "将")) OperationalArea.push_back({ place.x, place.y + i }); } } // 设置特殊的动画效果 void AnimationSpecial(std::string name, double delay, double FPS) { Act.SetBasicValue(delay, FPS, 0); Act.SetSign(name); Act.SetAct([&](double progress) { double Size = (2 - progress) * min(operateBoard.GetSize().x, operateBoard.GetSize().y) * 0.1; DrawTaiJi(operateBoard.GetPlace(), Size, operatePiece->getCamp()); settextstyle(Size, Size / 2.0, L"Consolas"); settextcolor(operatePiece->getCamp()); setbkmode(TRANSPARENT); TCHAR* arr = TransformCharToWideChar(Act.GetSign().c_str()); outtextxy(operateBoard.GetPlace().x - Size / 2.0, operateBoard.GetPlace().y - Size / 2.0, arr); delete[]arr; return true; }); Act.SetEnd_Act([&]() { isBlocking = false; operatePiece = nullptr; return true; }); } void ConnectAnimationSpecial(std::string name, double delay, double FPS) { Act.SetBasicValue(delay, FPS, 0); Act.SetSign(name); Act.SetAct([&](double progress) { double Size = (2 - progress) * min(operateBoard.GetSize().x, operateBoard.GetSize().y) * 0.1; DrawTaiJi(operateBoard.GetPlace(), Size, operatePiece->getCamp()); settextstyle(Size, Size / 2.0, L"Consolas"); settextcolor(operatePiece->getCamp()); setbkmode(TRANSPARENT); TCHAR* arr = TransformCharToWideChar(Act.GetSign().c_str()); outtextxy(operateBoard.GetPlace().x - Size / 2.0, operateBoard.GetPlace().y - Size / 2.0, arr); delete[]arr; return true; }); Act.SetEnd_Act([&]() { isBlocking = false; operatePiece = nullptr; isRedTurn = !isRedTurn; return true; }); } // 判断是否将军 bool isCheck() { SetOperationalArea(*operatePiece); for (Vec2 i : OperationalArea) if (operateBoard.GetMapPiece(i) && (operateBoard.GetMapPiece(i)->getpiece_Name() == "帅" || operateBoard.GetMapPiece(i)->getpiece_Name() == "将"))return true; return false; } // 设置棋子移动的动画效果 void Animation(Vec2 goal, double delay, double FPS)// FPS 是 1/10.0 之类表示一秒多少帧 { MGB::Vec2 begin = { operatePiece->getPos().x * operateBoard.getGrid().x + operateBoard.getStart().x, operatePiece->getPos().y * operateBoard.getGrid().y + operateBoard.getStart().y }; MGB::Vec2 end = { goal.x * operateBoard.getGrid().x + operateBoard.getStart().x, goal.y * operateBoard.getGrid().y + operateBoard.getStart().y }; Act.SetBasicValue(delay, FPS, 0); Act.SetMoveAct_Route(begin, end); Act.SetAct([&](double progress) { operatePiece->DrawPiece_Pick(Act.GetMoveAct_Current(), operateBoard.getGrid()); return true; }); Act.SetEnd_Act([&]() { Vec2 Goal = operateBoard.GetMapVec2(Act.GetEnd().x - operateBoard.GetPlace().x + operateBoard.GetSize().x / 2.0, Act.GetEnd().y - operateBoard.GetPlace().y + operateBoard.GetSize().y / 2.0); for (Vec2 i : OperationalArea) { if (i.x == Goal.x && i.y == Goal.y) { isRedTurn = !isRedTurn; // 自己下如果在原地踏步就很麻烦 operatePiece->setPos(Goal); break; } } if (isCheck()) { operateBoard.PutPieceInBoard(operatePiece); AnimationSpecial("将", 0.5, 0.05); } else if (!operateBoard.PutPieceInBoard(operatePiece)) AnimationSpecial("吃", 0.5, 0.05); else { isBlocking = false; operatePiece = nullptr; } return true; }); } void ConnectAnimation(Vec2 goal, double delay, double FPS) { MGB::Vec2 begin = { operatePiece->getPos().x * operateBoard.getGrid().x + operateBoard.getStart().x, operatePiece->getPos().y * operateBoard.getGrid().y + operateBoard.getStart().y }; MGB::Vec2 end = { goal.x * operateBoard.getGrid().x + operateBoard.getStart().x, goal.y * operateBoard.getGrid().y + operateBoard.getStart().y }; Act.SetBasicValue(delay, FPS, 0); Act.SetMoveAct_Route(begin, end); Act.SetAct([&](double progress) { operatePiece->DrawPiece_Pick(Act.GetMoveAct_Current(), operateBoard.getGrid()); return true; }); Act.SetEnd_Act([&]() { Vec2 Goal = operateBoard.GetMapVec2(Act.GetEnd().x - operateBoard.GetPlace().x + operateBoard.GetSize().x / 2.0, Act.GetEnd().y - operateBoard.GetPlace().y + operateBoard.GetSize().y / 2.0); operatePiece->setPos(Goal); if (isCheck()) { operateBoard.PutPieceInBoard(operatePiece); ConnectAnimationSpecial("将", 0.5, 0.05); } else if (!operateBoard.PutPieceInBoard(operatePiece)) { ConnectAnimationSpecial("吃", 0.5, 0.05); } else { isRedTurn = !isRedTurn; // 由于可以肯定不会原地踏步,所以可以这么写 isBlocking = false; operatePiece = nullptr; } return true; }); } public: Manager_Chess() :ispress(false), operatePiece(nullptr), operateBoard(), isRedTurn(true), isBlocking(false), Act(), isConnectWeb(false) { this->beginplace = { 0, 0 }; this->endplace = { 0, 0 }; } Manager_Chess(MGB::Vec2 pos, MGB::Vec2 size, COLORREF Camp) :ispress(false), operatePiece(nullptr), operateBoard(pos, size, Camp), isRedTurn(true), isBlocking(false), Act(), isConnectWeb(false) { this->beginplace = { 0, 0 }; this->endplace = { 0, 0 }; operateBoard.InitMap(); } ~Manager_Chess() { if (operatePiece != nullptr)delete operatePiece; OperationalArea.clear(); } Piece* GetOperatePiece(double x, double y) { Vec2 place = operateBoard.GetMapVec2(x - operateBoard.GetPlace().x + operateBoard.GetSize().x / 2.0, y - operateBoard.GetPlace().y + operateBoard.GetSize().y / 2.0); if (!operateBoard.isInMap(place))return nullptr; if (isRedTurn && operateBoard.GetMapPiece(place) != nullptr && operateBoard.GetMapPiece(place)->getCamp() == RED || !isRedTurn && operateBoard.GetMapPiece(place) != nullptr && operateBoard.GetMapPiece(place)->getCamp() == BLACK) return operateBoard.GetOperatePiece(x - operateBoard.GetPlace().x + operateBoard.GetSize().x / 2.0, y - operateBoard.GetPlace().y + operateBoard.GetSize().y / 2.0); return nullptr; } void SetOperationalArea(Piece num) { OperationalArea.clear(); if (num.getpiece_Name() == "兵" || num.getpiece_Name() == "卒")SetSoldier(num.getPos(), num.getCamp()); else if (num.getpiece_Name() == "马")SetHorse(num.getPos(), num.getCamp()); else if (num.getpiece_Name() == "車")SetCarOrGun(num.getPos(), num.getCamp(), false); else if (num.getpiece_Name() == "相" || num.getpiece_Name() == "象")SetElephant(num.getPos(), num.getCamp()); else if (num.getpiece_Name() == "士")SetKnight(num.getPos(), num.getCamp()); else if (num.getpiece_Name() == "炮")SetCarOrGun(num.getPos(), num.getCamp(), true); else if (num.getpiece_Name() == "帅" || num.getpiece_Name() == "将")SetKing(num.getPos(), num.getCamp()); } bool DealMouseMsg(ExMessage* msg) { if (isBlocking)return false; if (!ispress && msg->lbutton) { ispress = true; if (operatePiece == nullptr) { operatePiece = GetOperatePiece(msg->x, msg->y); if (operatePiece != nullptr)SetOperationalArea(*operatePiece); } else { Vec2 finalPlace = operateBoard.GetMapVec2(msg->x - operateBoard.GetPlace().x + operateBoard.GetSize().x / 2.0, msg->y - operateBoard.GetPlace().y + operateBoard.GetSize().y / 2.0); if (operateBoard.isInMap(finalPlace)) { if (!operateBoard.isCanThrough(finalPlace, operatePiece->getCamp())) { operateBoard.PutPieceInBoard(operatePiece); operatePiece = GetOperatePiece(msg->x, msg->y); SetOperationalArea(*operatePiece); } else { isBlocking = true; bool t_result = false; Animation(finalPlace, 0.5, 0.05); if (this->isConnectWeb) { for (Vec2 i : OperationalArea) { if (i.x == finalPlace.x && i.y == finalPlace.y) { this->beginplace = operatePiece->getPos(); this->endplace = finalPlace; t_result = true; break; } } return t_result; } } } } } else if (ispress && !msg->lbutton) { ispress = false; } return false; } void DealMessage(Vec2 begin, Vec2 end) { begin.x = 8 - begin.x; begin.y = 9 - begin.y; end.x = 8 - end.x; end.y = 9 - end.y; operatePiece = operateBoard.GetOperatePiece(begin); isBlocking = true; ConnectAnimation(end, 0.5, 0.05); } void DrawBoard() { operateBoard.Draw_Board(); operateBoard.PutPiece(); if (isBlocking) { Act.RunEveryFrameAction(); } else if (operatePiece != nullptr) { operateBoard.TakePiece(*operatePiece); operateBoard.DrawOperationalArea(OperationalArea); } } void Reopen() { if (isBlocking)return; operateBoard.ClearMap(); operateBoard.CampOpposite(); operateBoard.InitMap(); isRedTurn = true; isBlocking = false; Act.SetBasicValue(0, 0, 0); } void Reset() { operateBoard.ClearMap(); operateBoard.InitMap(); if (operatePiece != nullptr)delete operatePiece; operatePiece = nullptr; isRedTurn = true; isBlocking = false; Act.SetBasicValue(0, 0, 0); } ChessBoard& GetChessBoard() { return this->operateBoard; } bool IsRedTurn() { return this->isRedTurn; } void SetIsConnect(bool isconnect, SOCKET clientSocket) { this->isConnectWeb = isconnect; this->clientSocket = clientSocket; } Vec2 GetBeginPlace() { return this->beginplace; } Vec2 GetEndPlace() { return this->endplace; } bool GetIsBlocking() { return this->isBlocking; } };

客户端 client:

#define SENDMESSAGEMAXLEN 300 #define _WINSOCK_DEPRECATED_NO_WARNINGS #include #include// 这东西一定要在 Windows.h 头文件上面 #include #include #include #include #include"Chess.h" #pragma comment(lib, "Ws2_32.lib") #define WIDTH 640 #define HEIGHT 640 #define ISERRORMESSAGE 0 #define ISSENDMESSAGE 1 #define ISCONNECTNUMCHANGE 2 #define ISENTERROOM 3 #define ISEXITROOM 4 #define ISCONNECTMAXNUM 5 #define ISHALLMESSAGE 6 #define ISCHESSMOVE 7 #define ISUNDO 8 #define ISGIVEUP 9 #define ISGETREADY 10 size_t TransformCharToWideChar(const char* target, wchar_t** output) { size_t len = MultiByteToWideChar(CP_ACP, 0, target, -1, NULL, 0); TCHAR* arr = new TCHAR[len]; MultiByteToWideChar(CP_ACP, 0, target, -1, arr, len); *output = arr; return len; } void WINAPI My_Printf(const char* arr) { wchar_t* tarr; size_t szlen = TransformCharToWideChar(arr, &tarr); WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), tarr, szlen - 1, NULL, NULL); delete[] tarr; } void WINAPI My_Printf(const char* arr, const char* another) { wchar_t* tarr; wchar_t* tanother; size_t szlen = TransformCharToWideChar(arr, &tarr); szlen += TransformCharToWideChar(another, &tanother); wchar_t* OutMsg = new wchar_t[szlen]; StringCchPrintf(OutMsg, szlen, tarr, tanother); WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), OutMsg, szlen - 4, NULL, NULL); delete[] tarr; delete[] tanother; delete[] OutMsg; } void WINAPI My_Printf(const char* arr, int num) { wchar_t* tarr; size_t szlen = TransformCharToWideChar(arr, &tarr); wchar_t* OutMsg = new wchar_t[szlen + 12]; StringCchPrintf(OutMsg, szlen + 12, tarr, num); StringCchLength(OutMsg, szlen + 12, &szlen); WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), OutMsg, szlen, NULL, NULL); delete[] tarr; delete[] OutMsg; } bool WINAPI My_Strcmp(const char* target, const char* source, size_t len) { for (size_t i = 0; i < len;i++) if (target[i] != source[i])return false; return true; } void My_Strcpy(char* target, const char* source, size_t len) { for (int i = 0; i < len; i++) { target[i] = source[i]; } } struct RecvMessageParam { SOCKET clientSocket; bool isExit; unsigned int* connectnum; unsigned int* maxconnectnum; bool** hall; int* roomnumber; HWND hwnd; bool* isMove; char* Place; bool* isoppositeReady; bool* isselfReady; wchar_t* RecvSM; size_t* RecvSMLen; }; struct DealErrorMessageParam { const char* message; size_t len; HWND hwnd; bool* isoppositeReady; bool* isselfReady; }; void DealErrorMessage(DealErrorMessageParam* param) { if (param->len == 12 && My_Strcmp(param->message, "no this room", 12)) { MessageBox(param->hwnd, L"没有这个房间", L"错误", MB_OK); } else if (param->len == 16 && My_Strcmp(param->message, "the room is full", 16)) { MessageBox(param->hwnd, L"房间满了", L"错误", MB_OK); } else if (param->len == 15 && My_Strcmp(param->message, "you are in room", 15)) { MessageBox(param->hwnd, L"你在房间里", L"错误", MB_OK); } else if (param->len == 15 && My_Strcmp(param->message, "you are in hall", 15)) { MessageBox(param->hwnd, L"你在大厅里", L"错误", MB_OK); } else if (param->len == 21 && My_Strcmp(param->message, "no people in opposite", 21)) { *param->isoppositeReady = false; *param->isselfReady = false; MessageBox(param->hwnd, L"对面没人", L"错误", MB_OK); } } DWORD WINAPI RecvMessage(LPVOID lParam) { RecvMessageParam* param = (RecvMessageParam*)lParam; while (!param->isExit) { char code; size_t messagelen = 0; char recvBuf[SENDMESSAGEMAXLEN]; int retVal = 0; while (!param->isExit && recv(param->clientSocket, &code, sizeof(char), 0) == SOCKET_ERROR) { retVal = WSAGetLastError(); if (retVal == WSAEWOULDBLOCK) { Sleep(1000); continue; } } while (!param->isExit && recv(param->clientSocket, (char*)&messagelen, sizeof(size_t), 0) == SOCKET_ERROR) { retVal = WSAGetLastError(); if (retVal == WSAEWOULDBLOCK) { Sleep(100); continue; } } messagelen = ntohll(messagelen); while (!param->isExit && recv(param->clientSocket, recvBuf, messagelen, 0) == SOCKET_ERROR) { retVal = WSAGetLastError(); if (retVal == WSAEWOULDBLOCK) { Sleep(100); continue; } } if (param->isExit)break; switch (code) { case ISERRORMESSAGE: { DealErrorMessageParam t_param; t_param.message = recvBuf; t_param.len = messagelen; t_param.hwnd = param->hwnd; t_param.isoppositeReady = param->isoppositeReady; t_param.isselfReady = param->isselfReady; DealErrorMessage(&t_param); } break; case ISSENDMESSAGE: { *param->RecvSMLen = messagelen >> 1; for (int i = 0; i < messagelen >> 1; i++)*(param->RecvSM + i) = *((wchar_t*)recvBuf + i); } break; case ISCONNECTNUMCHANGE: *(param->connectnum) = ntohl(*(unsigned int*)recvBuf); break; case ISCONNECTMAXNUM: *(param->maxconnectnum) = ntohl(*(unsigned int*)recvBuf); break; case ISHALLMESSAGE: { while (*(param->hall) == nullptr)Sleep(100); for (int i = 0; i < messagelen; i++)*(*(param->hall) + i) = recvBuf[i]; } break; case ISENTERROOM: { unsigned int index = ntohl(*(unsigned int*)recvBuf); *(*(param->hall) + index) = true; } break; case ISEXITROOM: { unsigned int index = ntohl(*(unsigned int*)recvBuf); *(*(param->hall) + index) = false; } break; case ISCHESSMOVE: { *param->isMove = true; for (int i = 0; i < messagelen; i++) *(param->Place + i) = recvBuf[i]; } break; case ISGETREADY: { *param->isoppositeReady = true; } break; case ISGIVEUP: { *param->isoppositeReady = false; *param->isselfReady = false; } break; default: break; } } return 0; } void DrawTaiJi(double width, double height, COLORREF Camp1, COLORREF Camp2) { MGB::Vec2 pericious = { width * 0.5, height * 0.5 }; double radius = min(width, height) * (1 / 3.0); setlinecolor(RGB(185, 122, 87)); circle(pericious.x, pericious.y, radius); arc(pericious.x - radius / 2.0, pericious.y - radius, pericious.x + radius / 2.0, pericious.y, PI / 2.0, PI * 3 / 2.0); arc(pericious.x - radius / 2.0, pericious.y, pericious.x + radius / 2.0, pericious.y + radius, -PI / 2.0, PI / 2.0); setfillcolor(Camp1); floodfill(pericious.x, pericious.y - radius * (0.5), RGB(185, 122, 87)); setfillcolor(Camp2); floodfill(pericious.x, pericious.y + radius * (0.5), RGB(185, 122, 87)); circle(pericious.x, pericious.y - radius * (0.5), radius * (0.25)); circle(pericious.x, pericious.y + radius * (0.5), radius * (0.25)); setfillcolor(Camp1); floodfill(pericious.x, pericious.y + radius * (0.5), RGB(185, 122, 87)); setfillcolor(Camp2); floodfill(pericious.x, pericious.y - radius * (0.5), RGB(185, 122, 87)); } struct RoomSceneParam { SOCKET clientSocket; COLORREF Camp; bool* isMove; char* Place; bool* opposite; bool* isoppositeReady; bool* isselfReady; wchar_t* RecvSM; size_t* RecvSMLen; HWND hwnd; }; void OutTextMessage(double xx, double yy, const wchar_t* message, size_t len) { if (len == 0)return; int rows = 0; settextcolor(BLACK); settextstyle(HEIGHT * (1 / 36.0), WIDTH * (1 / 80.0), _T("Consolas")); while (rows * 20 < len) { wchar_t t_message[21]; int i = 0; for (i = 0; i < 20 && i + rows * 20 < len; i++)t_message[i] = message[i + rows * 20]; t_message[i] = L'\0'; outtextxy(xx, yy + rows * (WIDTH * (1 / 80.0) + 4), t_message); rows++; } } bool GiveUpFunc(SOCKET clientSocket, bool* isoppositeReady, bool* isselfReady) { *isoppositeReady = false; *isselfReady = false; char code = ISGIVEUP; while (true) { int retVal = send(clientSocket, &code, 1, 0); if (SOCKET_ERROR == retVal) { int err = WSAGetLastError(); if (err == WSAEWOULDBLOCK) { Sleep(5); continue; } else { printf("send failed\n"); closesocket(clientSocket); WSACleanup(); return false; } } break; } return true; } bool RoomScene(RoomSceneParam* param) { // 认输,对话,悔棋,准备,移动棋子,在线否 Manager_Chess manager_Chess({ WIDTH * 0.5, HEIGHT * 0.5 }, { WIDTH * 0.5, HEIGHT * 0.5 }, param->Camp); manager_Chess.SetIsConnect(true, param->clientSocket); bool isexit = false; MGB::List_Button manager = Create_List_Button(); wchar_t myselfmessage[SENDMESSAGEMAXLEN >> 1]; size_t MMLen = 0; addChild_List_Button(manager, CreateList_Node_Button( CreateButton({ WIDTH * (7 / 8.0), HEIGHT * (1 / 8.0) }, { 0.5, 0.5 }, { WIDTH * (1 / 6.0), HEIGHT * (1 / 6.0) }, "返回", [&]() { char code = ISEXITROOM; while (true) { int retVal = send(param->clientSocket, &code, 1, 0); if (SOCKET_ERROR == retVal) { int err = WSAGetLastError(); if (err == WSAEWOULDBLOCK) { Sleep(5); continue; } else { printf("send failed\n"); closesocket(param->clientSocket); WSACleanup(); return false; } } break; } isexit = true; *param->isselfReady = false; *param->isoppositeReady = false; return true; }))); addChild_List_Button(manager, CreateList_Node_Button( CreateButton({ WIDTH * (7 / 8.0), HEIGHT * (0.25 + 0.1) }, { 0.5, 0.5 }, { WIDTH * (1 / 8.0), HEIGHT * (1 / 11.0) }, "准备", [&]() { *param->isselfReady = true; char code = ISGETREADY; while (true) { int retVal = send(param->clientSocket, &code, 1, 0); if (SOCKET_ERROR == retVal) { int err = WSAGetLastError(); if (err == WSAEWOULDBLOCK) { Sleep(5); continue; } else { printf("send failed\n"); closesocket(param->clientSocket); WSACleanup(); return false; } } break; } return true; }))); addChild_List_Button(manager, CreateList_Node_Button( CreateButton({ WIDTH * (7 / 8.0), HEIGHT * (0.25 + 0.2) }, { 0.5, 0.5 }, { WIDTH * (1 / 8.0), HEIGHT * (1 / 11.0) }, "悔棋", [&]() { MessageBox(param->hwnd, L"落子无悔大丈夫", L"哲理", MB_OK); return true; }))); addChild_List_Button(manager, CreateList_Node_Button( CreateButton({ WIDTH * (7 / 8.0), HEIGHT * (0.25 + 0.3) }, { 0.5, 0.5 }, { WIDTH * (1 / 8.0), HEIGHT * (1 / 11.0) }, "认输", [&]() { GiveUpFunc(param->clientSocket, param->isoppositeReady, param->isselfReady); return true; }))); addChild_List_Button(manager, CreateList_Node_Button( CreateButton({ WIDTH * (7 / 8.0), HEIGHT * (0.25 + 0.4) }, { 0.5, 0.5 }, { WIDTH * (1 / 8.0), HEIGHT * (1 / 11.0) }, "对话", [&]() { InputBox(myselfmessage, SENDMESSAGEMAXLEN >> 1, 0, L"请输入要发送的信息"); char code = ISGETREADY; StringCchLength(myselfmessage, SENDMESSAGEMAXLEN >> 1, &MMLen); char sendBuf[sizeof(char) + sizeof(size_t) + SENDMESSAGEMAXLEN]; sendBuf[0] = ISSENDMESSAGE; size_t temp = htonll(MMLen * 2); My_Strcpy(&sendBuf[sizeof(char)], (char*)&temp, sizeof(size_t)); My_Strcpy(&sendBuf[sizeof(char) + sizeof(size_t)], (char*)myselfmessage, MMLen * 2); while (true) { int retVal = send(param->clientSocket, sendBuf, sizeof(char) + sizeof(size_t) + MMLen * 2, 0); if (SOCKET_ERROR == retVal) { int err = WSAGetLastError(); if (err == WSAEWOULDBLOCK) { Sleep(5); continue; } else { printf("send failed\n"); closesocket(param->clientSocket); WSACleanup(); return false; } } break; } return true; }))); ExMessage msg; bool haveStarted = false; bool haveJudged = false; while (true) { while (peekmessage(&msg, EM_MOUSE)); cleardevice(); if (*param->isoppositeReady && *param->isselfReady) { if (!haveStarted)haveStarted = true; if (!((manager_Chess.IsRedTurn()) ^ (manager_Chess.GetChessBoard().GetCamp() == RED))) { if (!haveJudged) { haveJudged = true; if (manager_Chess.GetChessBoard().isFailure()) { GiveUpFunc(param->clientSocket, param->isoppositeReady, param->isselfReady); continue; } } if (manager_Chess.DealMouseMsg(&msg)) { char sendBuf[sizeof(char) * 5 + sizeof(size_t)]; sendBuf[0] = ISCHESSMOVE; size_t messagelen = htonll(4); My_Strcpy((char*)&sendBuf[sizeof(char)], (char*)&messagelen, sizeof(size_t)); sendBuf[sizeof(char) + sizeof(size_t) + 0] = manager_Chess.GetBeginPlace().x; sendBuf[sizeof(char) + sizeof(size_t) + 1] = manager_Chess.GetBeginPlace().y; sendBuf[sizeof(char) + sizeof(size_t) + 2] = manager_Chess.GetEndPlace().x; sendBuf[sizeof(char) + sizeof(size_t) + 3] = manager_Chess.GetEndPlace().y; while (true) { int retVal = send(param->clientSocket, sendBuf, sizeof(char) * 5 + sizeof(size_t), 0); if (SOCKET_ERROR == retVal) { int err = WSAGetLastError(); if (err == WSAEWOULDBLOCK) { Sleep(5); continue; } else { printf("send failed\n"); closesocket(param->clientSocket); WSACleanup(); return false; } } break; } } } else if (*(param->isMove)) { haveJudged = false; manager_Chess.DealMessage({ *(param->Place + 0), *(param->Place + 1) }, { *(param->Place + 2), *(param->Place + 3) }); *(param->isMove) = false; } manager_Chess.DrawBoard(); } else { manager_Chess.GetChessBoard().Draw_Board(); if (*param->isselfReady) { settextcolor(manager_Chess.GetChessBoard().GetCamp()); settextstyle(HEIGHT * (0.125), WIDTH * (1 / 24.0), _T("Consolas")); outtextxy(WIDTH * (0.25 + 1 / 8.0), HEIGHT * (0.5 + 1 / 16.0), L"已准备"); } if (*param->isoppositeReady) { settextcolor((manager_Chess.GetChessBoard().GetCamp() == RED ? BLACK : RED)); settextstyle(HEIGHT * (0.125), WIDTH * (1 / 24.0), _T("Consolas")); outtextxy(WIDTH * (0.25 + 1 / 8.0), HEIGHT * (0.25 + 1 / 16.0), L"已准备"); } if (haveStarted) { manager_Chess.Reset(); haveStarted = false; } } setfillcolor(manager_Chess.GetChessBoard().GetCamp()); fillrectangle(WIDTH * (19 / 24.0), HEIGHT * (19 / 24.0), WIDTH * (23 / 24.0), HEIGHT * (23 / 24.0)); if (*param->opposite)setfillcolor((manager_Chess.GetChessBoard().GetCamp() == RED ? BLACK : RED)); else { *param->isoppositeReady = false; *param->isselfReady = false; setfillcolor(WHITE); } fillrectangle(WIDTH * (1 / 24.0), HEIGHT * (1 / 24.0), WIDTH * (5 / 24.0), HEIGHT * (5 / 24.0)); setlinecolor(YELLOW); if (!((manager_Chess.IsRedTurn()) ^ (manager_Chess.GetChessBoard().GetCamp() == RED))) rectangle(WIDTH * (19 / 24.0), HEIGHT * (19 / 24.0), WIDTH * (23 / 24.0), HEIGHT * (23 / 24.0)); else rectangle(WIDTH * (1 / 24.0), HEIGHT * (1 / 24.0), WIDTH * (5 / 24.0), HEIGHT * (5 / 24.0)); // 一行 20 字 setlinecolor(BLACK); rectangle(WIDTH * (0.25), HEIGHT * (1 / 24.0), WIDTH * (0.75), HEIGHT * (5 / 24.0)); rectangle(WIDTH * (0.25), HEIGHT * (19 / 24.0), WIDTH * (0.75), HEIGHT * (23 / 24.0)); OutTextMessage(WIDTH * (0.25), HEIGHT * (19 / 24.0), myselfmessage, MMLen); OutTextMessage(WIDTH * (0.25), HEIGHT * (1 / 24.0), param->RecvSM, *param->RecvSMLen); Trigger_Button(&msg, manager); FlushBatchDraw(); if (isexit)break; } Clear_List_Button(manager); return true; } bool HallScene(HWND hwnd, unsigned long ipaddressnum, unsigned short portnum) { WSAData wsa; WSAStartup(MAKEWORD(2, 2), &wsa); SOCKET clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); sockaddr_in serveAddress; serveAddress.sin_addr.S_un.S_addr = ipaddressnum; // ip 地址,在自己主机上玩就改成 127.0.0.1 serveAddress.sin_port = portnum; // 端口号 serveAddress.sin_family = AF_INET; if (SOCKET_ERROR == connect(clientSocket, (sockaddr*)&serveAddress, sizeof(serveAddress))) { MessageBox(hwnd, L"连接到服务器失败", L"错误", MB_OK); return false; } int iMode = 1; int retVal = ioctlsocket(clientSocket, FIONBIO, (u_long*)&iMode); if (retVal == SOCKET_ERROR) { printf("ioctlsocket failed!"); WSACleanup(); return false; } My_Printf("clinet is running...\n"); while (true) { retVal = connect(clientSocket, (sockaddr*)&serveAddress, sizeof(serveAddress)); if (SOCKET_ERROR == retVal) { int err = WSAGetLastError(); if (err == WSAEWOULDBLOCK || err == WSAEINVAL)// windows 异步系统将要阻塞 { Sleep(1); printf("check connect\n"); continue; } else if (err == WSAEISCONN)break; else { printf("connection failed\n"); closesocket(clientSocket); WSACleanup(); return false; } } } unsigned int connectnum = 0; unsigned int maxconnectnum = 0; int roomnumber = 0; bool isMove = false; bool* hall = nullptr; char* Place = new char[4]; bool isoppositeReady = false; bool isselfReady = false; wchar_t RecvSM[SENDMESSAGEMAXLEN >> 1]; size_t RecvSMLen = 0; RecvMessageParam recvmessageparam; recvmessageparam.clientSocket = clientSocket; recvmessageparam.connectnum = &connectnum; recvmessageparam.isExit = false; recvmessageparam.maxconnectnum = &maxconnectnum; recvmessageparam.hall = &hall; recvmessageparam.roomnumber = &roomnumber; recvmessageparam.isMove = &isMove; recvmessageparam.Place = Place; recvmessageparam.isoppositeReady = &isoppositeReady; recvmessageparam.isselfReady = &isselfReady; recvmessageparam.hwnd = hwnd; recvmessageparam.RecvSM = (wchar_t*)RecvSM; recvmessageparam.RecvSMLen = &RecvSMLen; HANDLE recvMes; recvMes = CreateThread(NULL, 0, RecvMessage, &recvmessageparam, 0, NULL); while (!maxconnectnum)Sleep(100); // 这里会像是程序卡住,姑妄任之 if (maxconnectnum > 1000 || maxconnectnum < 0) { MessageBox(hwnd, L"错误", L"连接异常", MB_OK); shutdown(clientSocket, 0); recvmessageparam.isExit = true; delete[] Place; WaitForSingleObject(recvMes, INFINITE); CloseHandle(recvMes); closesocket(clientSocket); WSACleanup(); return false; } hall = new bool[maxconnectnum]; COLORREF Camp1, Camp2; Camp1 = Camp2 = WHITE; bool isexit = false; MGB::List_Button manager = Create_List_Button(); // 尺寸为 1/9 addChild_List_Button(manager, CreateList_Node_Button( CreateButton({ WIDTH * (11 / 12.0), HEIGHT * (1 / 12.0) }, { 0.5, 0.5 }, { WIDTH * (1 / 9.0), HEIGHT * (1 / 9.0) }, "返回", [&]() { isexit = true; return true; }) )); addChild_List_Button(manager, CreateList_Node_Button( CreateButton({ WIDTH * (0.5), HEIGHT * (11 / 12.0) }, { 0.5, 0.5 }, { WIDTH * (1 / 9.0), HEIGHT * (1 / 9.0) }, "进入", [&]() { if (*(hall + roomnumber * 2) && *(hall + roomnumber * 2 + 1)) { MessageBox(hwnd, L"错误", L"房间人满了", MB_OK); return false; } RoomSceneParam roomsceneparam; roomsceneparam.clientSocket = clientSocket; roomsceneparam.isMove = &isMove; roomsceneparam.opposite = (hall + roomnumber * 2 + !(*(hall + roomnumber * 2))); roomsceneparam.Place = Place; roomsceneparam.Camp = ((*(hall + roomnumber * 2)) ? BLACK : RED); roomsceneparam.isoppositeReady = &isoppositeReady; roomsceneparam.isselfReady = &isselfReady; roomsceneparam.RecvSM = (wchar_t*)RecvSM; roomsceneparam.RecvSMLen = &RecvSMLen; roomsceneparam.hwnd = hwnd; RecvSMLen = 0; char sendBuf[sizeof(unsigned int) + sizeof(char) + sizeof(size_t)]; unsigned int t_roomnumber = htonl(roomnumber); sendBuf[0] = ISENTERROOM; size_t messagelen = sizeof(unsigned int); messagelen = htonll(messagelen); My_Strcpy(sendBuf + sizeof(char), (char*)&messagelen, sizeof(size_t)); My_Strcpy(sendBuf + sizeof(char) + sizeof(size_t), (char*)&t_roomnumber, sizeof(unsigned int)); while (true) { retVal = send(clientSocket, sendBuf, sizeof(unsigned int) + sizeof(char) + sizeof(size_t), 0); if (SOCKET_ERROR == retVal) { int err = WSAGetLastError(); if (err == WSAEWOULDBLOCK) { Sleep(5); continue; } else { printf("send failed\n"); closesocket(clientSocket); WSACleanup(); return false; } } break; } RoomScene(&roomsceneparam); return true; }) )); addChild_List_Button(manager, CreateList_Node_Button( CreateButton({ WIDTH * (1 / 12.0), HEIGHT * (0.5) }, { 0.5, 0.5 }, { WIDTH * (1 / 9.0), HEIGHT * (1 / 9.0) }, "左", [&]() { roomnumber = (roomnumber + (maxconnectnum >> 1) - 1) % (maxconnectnum >> 1); return true; }) )); addChild_List_Button(manager, CreateList_Node_Button( CreateButton({ WIDTH * (11 / 12.0), HEIGHT * (0.5) }, { 0.5, 0.5 }, { WIDTH * (1 / 9.0), HEIGHT * (1 / 9.0) }, "右", [&]() { roomnumber = (roomnumber + 1) % (maxconnectnum >> 1); return true; }) )); ExMessage msg; while (true) { while (peekmessage(&msg, EM_MOUSE)); cleardevice(); if (*(hall + 2 * roomnumber))Camp1 = RED; else Camp1 = WHITE; if (*(hall + 2 * roomnumber + 1))Camp2 = BLACK; else Camp2 = WHITE; Trigger_Button(&msg, manager); DrawTaiJi(WIDTH, HEIGHT, Camp1, Camp2); wchar_t* tarr; size_t szlen = TransformCharToWideChar("在线人数:%d/%d", &tarr); wchar_t* OutMsg = new wchar_t[szlen + 24]; StringCchPrintf(OutMsg, szlen + 24, tarr, connectnum, maxconnectnum); StringCchLength(OutMsg, szlen + 24, &szlen); settextstyle(HEIGHT * (1 / 6), WIDTH * (0.125) * (1 / (double)szlen), _T("Consolas")); outtextxy(0, 0, OutMsg); delete[] tarr; delete[] OutMsg; FlushBatchDraw(); if (isexit)break; } delete[] hall; delete[] Place; Clear_List_Button(manager); shutdown(clientSocket, 0); recvmessageparam.isExit = true; WaitForSingleObject(recvMes, INFINITE); CloseHandle(recvMes); closesocket(clientSocket); WSACleanup(); return true; } bool SinglePlayerScene() { MGB::List_Button manager = Create_List_Button(); bool isexit = false; Manager_Chess manager_Chess({ WIDTH * 0.375, HEIGHT * 0.375 }, { WIDTH * 0.75, HEIGHT * 0.75 }, RED); addChild_List_Button(manager, CreateList_Node_Button( CreateButton({ WIDTH * 0.875, HEIGHT * 0.125 }, { 0.5, 0.5 }, { WIDTH / 5.0, HEIGHT * 0.1875 }, "悔棋", [&]() { manager_Chess.GetChessBoard(). setPos({ manager_Chess.GetChessBoard().GetPlace().x, manager_Chess.GetChessBoard().GetPlace().y + 100 }); return true; }) )); addChild_List_Button(manager, CreateList_Node_Button( CreateButton({ WIDTH * 0.875, HEIGHT * 0.375 }, { 0.5, 0.5 }, { WIDTH / 5.0, HEIGHT * 0.1875 }, "重开", [&]() { manager_Chess.Reopen(); return true; }) )); addChild_List_Button(manager, CreateList_Node_Button( CreateButton({ WIDTH * 0.875, HEIGHT * 0.625 }, { 0.5, 0.5 }, { WIDTH / 5.0, HEIGHT * 0.1875 }, "结束游戏", [&]() { isexit = true; return true; }) )); ExMessage msg; while (true) { while (peekmessage(&msg, EM_MOUSE)); cleardevice(); manager_Chess.DealMouseMsg(&msg); manager_Chess.DrawBoard(); Trigger_Button(&msg, manager); FlushBatchDraw(); if (isexit)break; } Clear_List_Button(manager); return true; } unsigned short TransferWideCharToUSHORT(wchar_t* arr, unsigned int len) { unsigned short result = 0; int i = 0; while (i


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3