网络联机中国象棋
程序截图
环境配置
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)编程,我先给出两个案例,先别点进去看。
用到的库是<WinSocket2.h>这个库函数,这个库需要预加载,要开始套接字编程时需要先调用 WSAStartup,结束套接字编程时需要调用 WSACleanup。
也就是说模板是:
#include<WinSock2.h>
#pragma comment(lib, "Ws2_32.lib")
int main()
{
WSADATA wsData; // WSADATA windows 异步套接字数据
// MAKEWORD(a, b)是一个宏,等效于 (a|(b<<8)),MAKEWORD(2, 2) 是版本号 2.2 的意思,写成 short 类型就是 0x0202
// 注意 WSAStartup 的第二个数据不能传空指针,不然会报错
WSAStartup(MAKEWORD(2, 2), &wsData); // 这个函数用于初始化 WSADATA,第一个参数为版本号,是 short 类型,第二个参数是 WSADATA 的首地址,用于赋值
// ...这里就是编程的内容
WSACleanup(); // 这个函数用于释放系统资源
return 0;
}
如果完全没接触过网络编程,或者对 C++ 套接字编程不熟悉的,那现在就可以点开那两个链接看了,我觉得我是做到的没有多余代码的,只要看函数说明应该能意会那些函数要怎么应用。
最后所需要的拓展知识是多线程的知识,只学一种。因为接受消息是一定会不停等待消息发送过来的,如果你不开一个线程来等待对面发消息过来,那么光是接受消息的功能就能令程序干不了任何事情,所以需要多线程。
具体用三个函数 CreateThread;WaitForSingleObject;CloseHandle;
要用多线程时引用一下 Windows.h 头文件,同时注意一下 WinSocket.h 这个头文件要放在 Windows.h 头文件之前,不然会有一些报错。
最简单的案例:
#include<Windows.h>
#include<strsafe.h>
struct ThreadFuncParam
{
bool* isexit;
int* num;
};
DWORD WINAPI ThreadFunc(LPVOID lParam)// 线程具体执行的代码
{
ThreadFuncParam* param = (ThreadFuncParam*)lParam;
while (!*param->isexit)
{
Sleep(1000);
(*param->num)++;
}
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<TheSendMessage> 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<WinSock2.h>
#include<Windows.h>
#include<vector>
#include<strsafe.h>
#include<conio.h>
#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<TheSendMessage>* 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<TheSendMessage> (*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<TheSendMessage> 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<string>
#include<functional>
#include<graphics.h>
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<bool()> 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 << 24) - 1);
setlinecolor(col);
setlinestyle(PS_SOLID, 1);
settextcolor(col);
DrawButton(num);
}
void DrawButton_GetTo(MGB::Button num)
{
COLORREF col = getbkcolor();
col = col ^ ((1 << 24) - 1);
setlinecolor(col);
setlinestyle(PS_SOLID, 5);
settextcolor(col);
DrawButton(num);
}
void DrawButton_Response(MGB::Button num)
{
COLORREF col = getbkcolor();
col = col ^ ((1 << 24) - 1);
setlinecolor(col);
setlinestyle(PS_SOLID, 5);
settextcolor(col);
DrawButton(num, { 0.05, 0.05 });
}
MGB::Button CreateButton(MGB::Vec2 pos, MGB::Vec2 anchor, MGB::Vec2 Size, std::string name, std::function<bool()> CallbackFunc)
{
MGB::Button result;
result.pos = pos;
result.anchor = anchor;
result.Size = Size;
result.name = name;
result.CallbackFunc = CallbackFunc;
return result;
}
MGB::List_Node_Button* CreateList_Node_Button(MGB::Button button)
{
MGB::List_Node_Button* result = new MGB::List_Node_Button;
result->button = button;
result->next = nullptr;
return result;
}
void clearList_Node_Button(MGB::List_Node_Button* (&head))
{
MGB::List_Node_Button* current = head;
while (current != nullptr)
{
MGB::List_Node_Button* temp = current;
current = current->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<list>
#include<math.h>
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<bool(double progress)> Act; // 行动时的函数,根据 process 确定进行到什么程度
std::function<bool()>End_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<bool(double progress)> Act)
{
this->Act = Act;
}
void SetEnd_Act(std::function<bool()> 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<Piece*> 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<Piece*>::iterator ite = alivePool.begin();
if (result != nullptr)
{
for (std::list<Piece*>::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<Piece*>::iterator ite = alivePool.begin();
if (result != nullptr)
{
for (std::list<Piece*>::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<Vec2>& 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<Piece*>::iterator ite = alivePool.begin();
for (std::list<Piece*>::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<Vec2> 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 <= 5)
OperationalArea.push_back({ place.x + 1, place.y + 1 });
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 <= 5 && place.y - 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 <= 5 && place.y + 1 <= 2)
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 <= 2)
OperationalArea.push_back({ place.x - 1, place.y + 1 });
if (operateBoard.isCanThrough({ place.x + 1, place.y - 1 }, Kni_Camp) &&
place.x + 1 <= 5)
OperationalArea.push_back({ place.x + 1, place.y - 1 });
if (operateBoard.isCanThrough({ place.x - 1, place.y - 1 }, Kni_Camp) &&
place.x - 1 >= 3)
OperationalArea.push_back({ place.x - 1, place.y - 1 });
}
}
// 设置将帅的可移动区域
void SetKing(Vec2 place, COLORREF Soldier_Camp)
{
COLORREF Camp = operateBoard.GetCamp();
if (Soldier_Camp == Camp)
{
if (operateBoard.isCanThrough({ place.x, place.y - 1 }, Soldier_Camp) &&
place.y - 1 >= 7)
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 <= 5)
OperationalArea.push_back({ place.x + 1, place.y });
if (operateBoard.isCanThrough({ place.x, place.y + 1 }, Soldier_Camp))
OperationalArea.push_back({ place.x, place.y + 1 });
char i = 1;
while (operateBoard.isInMap({ place.x, place.y - i }) &&
operateBoard.GetMapPiece({ place.x, place.y - i }) == nullptr)i++;
if (operateBoard.GetMapPiece({ place.x, place.y - i })->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 <= 5)
OperationalArea.push_back({ place.x + 1, place.y });
if (operateBoard.isCanThrough({ place.x, place.y + 1 }, Soldier_Camp) &&
place.y + 1 <= 2)
OperationalArea.push_back({ place.x, place.y + 1 });
char i = 1;
while (operateBoard.isInMap({ place.x, place.y + i }) &&
operateBoard.GetMapPiece({ place.x, place.y + i }) == nullptr)i++;
if (operateBoard.isInMap({ place.x, place.y + i }) &&
(operateBoard.GetMapPiece({ place.x, place.y + i })->getpiece_Name() == "帅" ||
operateBoard.GetMapPiece({ place.x, place.y + i })->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<strsafe.h>
#include<WinSock2.h>// 这东西一定要在 Windows.h 头文件上面
#include<Windows.h>
#include<iostream>
#include<conio.h>
#include<time.h>
#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<len)
{
result *= 10;
switch (arr[i++])
{
case L'0':result += 0; break;
case L'1':result += 1; break;
case L'2':result += 2; break;
case L'3':result += 3; break;
case L'4':result += 4; break;
case L'5':result += 5; break;
case L'6':result += 6; break;
case L'7':result += 7; break;
case L'8':result += 8; break;
case L'9':result += 9; break;
default:
break;
}
}
return result;
}
unsigned short TransferCharToUSHORT(const char* arr, unsigned int len)
{
unsigned short result = 0;
int i = 0;
while (i < len)
{
result *= 10;
result += arr[i] - '0';
i++;
}
return result;
}
int main()
{
HWND hwnd = initgraph(WIDTH, HEIGHT);
BeginBatchDraw();
setbkcolor(RGB(200, 191, 231));
bool isexit = false;
MGB::List_Button manager = Create_List_Button();
addChild_List_Button(manager, CreateList_Node_Button(
CreateButton({ WIDTH * (0.5), HEIGHT * (0.25) }, { 0.5, 0.5 }, { WIDTH * (0.2), HEIGHT * (0.2) }, "单机",
[&]()
{
SinglePlayerScene();
return true;
})));
addChild_List_Button(manager, CreateList_Node_Button(
CreateButton({ WIDTH * (0.5), HEIGHT * (0.5) }, { 0.5, 0.5 }, { WIDTH * (0.2), HEIGHT * (0.2) }, "联机",
[&]()
{
wchar_t IpAddress[21];
InputBox(IpAddress, 21, 0, L"请输入 ipv4:port", L"127.0.0.1:6000");
// wchar_t PortNum[6];
// InputBox(PortNum, 6, 0, L"请输入端口号", L"6000");
size_t t_len = WideCharToMultiByte(CP_UTF8, 0, IpAddress, -1, NULL, 0, NULL, FALSE);
char* t_IAN = new char[t_len];
WideCharToMultiByte(CP_UTF8, 0, IpAddress, -1, t_IAN, t_len, NULL, FALSE);
char* t_child = strstr(t_IAN, ":");
*t_child = '\0';
unsigned long ipaddressnum = inet_addr(t_IAN);
// unsigned long ipaddressnum = inet_addr(t_IAN);
// StringCchLength(PortNum, 6, &t_len);
// unsigned short t_num = TransferWideCharToUSHORT(PortNum, t_len);
// unsigned short portnum = htons(t_num);
HallScene(hwnd, ipaddressnum, htons(TransferCharToUSHORT(t_child + 1, strlen(t_child + 1))));
delete[] t_IAN;
return true;
})));
addChild_List_Button(manager, CreateList_Node_Button(
CreateButton({ WIDTH * (0.5), HEIGHT * (0.75) }, { 0.5, 0.5 }, { WIDTH * (0.2), HEIGHT * (0.2) }, "退出",
[&]()
{
isexit = true;
return true;
})));
ExMessage msg;
while (true)
{
while (peekmessage(&msg, EM_MOUSE));
cleardevice();
Trigger_Button(&msg, manager);
FlushBatchDraw();
if (isexit)break;
}
Clear_List_Button(manager);
EndBatchDraw();
closegraph();
return 0;
}
服务器端在服务器上运行,客户端的 ip 地址连接到服务器端的 ip 地址就行,客户端的项目要包含 My_Gui_Button.h 和 Chess.h 两个头文件。
由于我租了一个服务器,需要的可以输入 ipv4 地址为 119.29.157.70,端口号为 6000 来连上。