课设五子棋(联机版)

发布于 2018-07-06  7.37k 次阅读


服务端代码

dataNode.h

#pragma once
#include 

using std::string;

struct dataNode { // 数据包结构
	int x, y; // 坐标
	int per; // 前一个棋子编号
	int turn; // 当前棋子编号
	int winner; // 记录胜利者编号
	bool isChess; // 判断该点是否是一个棋子
	bool isRestart;
	dataNode() { // 初始化
		winner = -1;
		isChess = false;
		isRestart = false;
	}
};

server.cpp

#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include "dataNode.h"
#include 
#pragma comment (lib,"ws2_32.lib") 
#include 
#include 
#include 
#define PORT 4567
using namespace std;

SOCKET li[10]; // 保存客户端信息
int cnt = 1;

void  CPlay(void *p)
{
	//const char *str = "hello client";
	SOCKET *pSock = (SOCKET*)p;
	SOCKET socka = *pSock;
	
	// 返回客户端id
	dataNode data;
	data.x = -10;
	data.y = -10;
	data.turn = cnt - 1;
	data.winner = -1;
	send(socka, (char *)&data, sizeof(dataNode), 0);

	// 先给第一个客户端发送数据包
	if (cnt == 2) {
		data.per = cnt;
		data.turn = cnt - 1;
		data.winner = -1;
		int i = send(socka, (char *)&data, sizeof(dataNode), 0);
	}

	sockaddr_in sa = { AF_INET };
	int count = 0;
	while (true) // 持续接收和发送数据包
	{
		data.winner = -1;
		char recvData[256];
		memset(recvData, 0, 256);
		int i = recv(socka, recvData, sizeof(recvData), 0);
		count++;
		if (i <= 0)
		{
			if (GetLastError() == 10054) { // 判断客户端是否退出
				cout << htons(sa.sin_port) << "客户端退出了:" << socka << endl;
				cnt--;
			}
			break;
		}
		// char*和结构体转换
		memset(&data, 0, sizeof(dataNode));
		memcpy(&data, recvData, sizeof(dataNode));
		// 计算数据包要发给谁
		int tmp = data.turn;
		data.turn %= cnt;
		if (data.turn == 0)
			data.turn = 1;
		if (data.turn == 1)
			data.per = 2;
		else
			data.per = 1;
		cout << count << "---" << data.x << "---" << data.y << "---" << data.per << "---" << data.turn << "--*" << data.winner << endl;
		for (int i = 1; i < 9; i++) { // 数据包不发给自己
			if(tmp-1 != i)
				send(li[i], (char *)&data, sizeof(dataNode), 0);
		}
	}
}
int main()
{
	WSAData wd;
	WSAStartup(0x0202, &wd);
	SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
	sockaddr_in sa = { AF_INET };
	sa.sin_port = htons(PORT);
	bind(sock, (sockaddr*)&sa, sizeof(sa));
	listen(sock, 5);
	sockaddr_in from = { AF_INET };
	int nLen = sizeof(from);
	while (true) {
		SOCKET socka = accept(sock, (sockaddr*)&from, &nLen);
		cout << inet_ntoa(sa.sin_addr) << htons(from.sin_port) << "登录了" << endl;
		li[cnt++] = socka;
		_beginthread(CPlay, 0, &socka); //开启一个线程 是以CPlay 命名的函数 传输的数据为socka
	}
	return 0;
}

客户端代码

dataNode.h

#pragma once
#include 

using std::string;

struct dataNode { // 数据包结构
	int x, y; // 坐标
	int per; // 前一个棋子编号
	int turn; // 当前棋子编号
	int winner; // 记录胜利者编号
	bool isChess; // 判断该点是否是一个棋子
	bool isRestart;
	dataNode() { // 初始化
		winner = -1;
		isChess = false;
		isRestart = false;
	}
};

initSock.h

#pragma once
#pragma once
#include    
#include     
#include     
#include     

#pragma comment(lib, "WS2_32")  // 链接到WS2_32.lib   

class CInitSock
{
public:
	CInitSock(BYTE minorVer = 2, BYTE majorVer = 2)
	{
		// 初始化WS2_32.dll   
		WSADATA wsaData;
		WORD sockVersion = MAKEWORD(minorVer, majorVer);
		if (::WSAStartup(sockVersion, &wsaData) != 0)
		{
			exit(0);
		}
	}
	~CInitSock()
	{
		::WSACleanup();
	}
};

client.h

#pragma once
#include "initSock.h"
#include "dataNode.h"

class client {
public:
	void initClient(); // 初始化客户端
	void connectServer(const char *ipAddress); // 连接到指定服务器
	void sendData(dataNode data); // 发送数据包
	dataNode revcData(); // 接收数据包
	void closeConnect(); // 关闭连接

	CInitSock initSock;
	SOCKET s;
	sockaddr_in servAddr;
};

void client::initClient() { // 初始化客户端
	s = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (s == INVALID_SOCKET)
	{
		printf(" Failed socket() \n");
		return;
	}
}

void client::connectServer(const char *ipAddress) { // 连接到指定服务器
	servAddr.sin_family = AF_INET;
	servAddr.sin_port = htons(4567);
	// ipAddress是服务器程序(TCPServer程序)所在机器的IP地址   
	servAddr.sin_addr.S_un.S_addr = inet_addr(ipAddress);
	if (::connect(s, (sockaddr*)&servAddr, sizeof(servAddr)) == -1)
	{
		printf(" Failed connect() \n");
		return;
	}
}

void client::sendData(dataNode data) {  // 发送数据包
	send(s, (char *)&data, sizeof(dataNode), 0);
}

dataNode client::revcData() { // 接收数据包
	dataNode data;
	data.x = -1;
	data.y = -1;
	char recvData[256];
	memset(recvData, 0, 256);
	int i = recv(s, recvData, sizeof(recvData), 0);
	if (i <= 0)
		return data;
	// 清空缓存
	memset(&data, 0, sizeof(dataNode));
	memcpy(&data, recvData, sizeof(dataNode));
	return data;
}

void client::closeConnect() { // 关闭连接  
	::closesocket(s);
}

grid.h

#pragma once
#include 
#include 
#include "dataNode.h"
#define mapWidth 512
#define mapHeight 512
#define size 32
dataNode map[800][800];

class grid {
public:
	void initGrid(); // 初始化棋盘
	void drawGrid(); // 打印棋盘
	void drawTips(dataNode data, int step);
	void drawBlcak(int x, int y); // 在指定位置打印黑色棋子
	void drawWhite(int x, int y); // 在指定位置打印白色棋子
	void drawGameOver(int x, int y); // 在指定位置打印结束提示图
	int check(int x, int y); // 判断当前棋子下完后游戏是否结束
	bool isWin(dataNode data); // 判断是否胜利
private:
	IMAGE blackChessRc, blackChess[3]; // 黑色棋子贴图
	IMAGE whiteChessRc, whiteChess[3]; // 白色棋子贴图
	IMAGE gameOver; // 结束提示图
};

void grid::initGrid() { // 初始化棋盘
	// 初始化窗口
	initgraph(700, mapHeight + size * 2);
	// 加载图片
	loadimage(&gameOver, L"rec\\gameOver.jpg");
	SetWorkingImage(NULL);
	loadimage(&blackChessRc, L"rec\\blackChess.jpg", 62, 31);
	SetWorkingImage(&blackChessRc);
	for (int i = 0; i < 2; i++)
		getimage(&blackChess[i], i * 31, 0, 31, 31);
	SetWorkingImage(NULL);
	loadimage(&whiteChessRc, L"rec\\whiteChess.jpg", 62, 31);
	SetWorkingImage(&whiteChessRc);
	for (int i = 0; i < 2; i++)
		getimage(&whiteChess[i], i * 31, 0, 31, 31);
	SetWorkingImage(NULL);
	settextcolor(BLACK);
}

int grid::check(int x, int y) { // 判断当前棋子下完后游戏是否结束 
	int tmp = map[x][y].turn;
	int cnt = 0;
	// 上
	for (int i = 0; i < 5; i++) {
		if (map[x][y - i * size].isChess && map[x][y - i * size].turn == tmp) {
			cnt++;
			if (cnt >= 6) {
				return tmp;
			}
		}
		else
			break;
	}
	// 下
	for (int i = 0; i < 5; i++) {
		if (map[x][y + i * size].isChess && map[x][y + i * size].turn == tmp) {
			cnt++;
			if (cnt >= 6) {
				return tmp;
			}
		}
		else
			break;
	}
	// 左
	cnt = 0;
	for (int i = 0; i < 5; i++) {
		if (map[x - i * size][y].isChess && map[x - i * size][y].turn == tmp) {
			cnt++;
			if (cnt >= 6) {
				return tmp;
			}
		}
		else
			break;
	}
	// 右
	for (int i = 0; i < 5; i++) {
		if (map[x + i * size][y].isChess && map[x + i * size][y].turn == tmp) {
			cnt++;
			if (cnt >= 6) {
				return tmp;
			}
		}
		else
			break;
	}
	// 左上
	cnt = 0;
	for (int i = 0; i < 5; i++) {
		if (map[x - i * size][y - i * size].isChess && map[x - i * size][y - i * size].turn == tmp) {
			cnt++;
			if (cnt >= 6) {
				return tmp;
			}
		}
		else
			break;
	}
	// 左下
	for (int i = 0; i < 5; i++) {
		if (map[x + i * size][y + i * size].isChess && map[x + i * size][y + i * size].turn == tmp) {
			cnt++;
			if (cnt >= 6) {
				return tmp;
			}
		}
		else
			break;
	}
	// 右上
	cnt = 0;
	for (int i = 0; i < 5; i++) {
		if (map[x + i * size][y - i * size].isChess && map[x + i * size][y - i * size].turn == tmp) {
			cnt++;
			if (cnt >= 6) {
				return tmp;
			}
		}
		else
			break;
	}
	// 右下
	for (int i = 0; i < 5; i++) {
		if (map[x - i * size][y + i * size].isChess && map[x - i * size][y + i * size].turn == tmp) {
			cnt++;
			if (cnt >= 6) {
				return tmp;
			}
		}
		else
			break;
	}
	return -1;
}

bool grid::isWin(dataNode data) { // 判断是否胜利
	if (data.winner != -1) {
		settextcolor(BLACK);
		outtextxy(300, 542, L"胜利");
		if (data.winner == 1)
			drawBlcak(280, 550);
		if (data.winner == 2)
			drawWhite(280, 550);
		return true;
	}
	return false;
}

void grid::drawGrid() { // 打印棋盘
	// 初始化网格
	setbkcolor(RGB(220, 180, 60));
	cleardevice();
	for (int i = 0; i <= mapWidth / size; i++) {
		for (int j = 0; j <= mapHeight / size; j++) {
			map[i][j].x = i * size;
			map[i][j].y = j * size;
		}
	}
	for (int i = 0; i <= mapWidth; i++) {
		for (int j = 0; j <= mapHeight; j++) {
			map[i][j].isChess = false;
		}
	}
	// 打印网格
	setcolor(BLACK);
	for (int i = 0; i < mapWidth / size; i++) {
		for (int j = 0; j < mapHeight / size; j++) {
			line(map[i][j].x + size / 2, map[i][j].y + size / 2, map[i + 1][j].x + size / 2, map[i + 1][j].y + size / 2);
			line(map[i][j].x + size / 2, map[i][j].y + size / 2, map[i][j + 1].x + size / 2, map[i][j + 1].y + size / 2);
		}
	}
	line(size / 2, mapHeight + size / 2, mapWidth + size / 2, mapHeight + size / 2);
	line(mapWidth + size / 2, size / 2, mapWidth + size / 2, mapHeight + size / 2);
	// 打印棋盘上的5个点
	setfillcolor(BLACK);
	fillcircle(mapWidth / 2 + size / 2, mapHeight / 2 + size / 2, 3);
	setfillcolor(BLACK);
	fillcircle(3 * size + 16, 3 * size + 16, 3);
	setfillcolor(BLACK);
	fillcircle(3 * size + 16, 13 * size + 16, 3);
	setfillcolor(BLACK);
	fillcircle(13 * size + 16, 3 * size + 16, 3);
	setfillcolor(BLACK);
	fillcircle(13 * size + 16, 13 * size + 16, 3);
}

void grid::drawTips(dataNode data, int step) {
	settextcolor(BLACK);
	outtextxy(540, 20, L"现在是:");
	if (data.per == 1)
		drawBlcak(614, 20 + 8);
	if (data.per == 2)
		drawWhite(614, 20 + 8);
	outtextxy(630, 20, L"的回合");
	outtextxy(540, 50, L"当前步数:");
	TCHAR s[5];
	_stprintf(s, _T("%d"), step);
	outtextxy(620, 50, s);
	outtextxy(540, 80, L"(1)对局双方各执一");
	outtextxy(540, 110, L"   色棋子。");
	outtextxy(540, 140, L"(2)空棋盘开局。");
	outtextxy(540, 170, L"(3)黑先、白后交替");
	outtextxy(540, 200, L"   下子,每次只能");
	outtextxy(540, 230, L"   下一子。");
	outtextxy(540, 260, L"(4)棋子下在棋盘的");
	outtextxy(540, 290, L"   空白点上,棋子");
	outtextxy(540, 320, L"   下定后,不得向");
	outtextxy(540, 350, L"   其它点移动,不");
	outtextxy(540, 380, L"   得从棋盘上拿掉");
	outtextxy(540, 410, L"   或拿起落别处。");
	outtextxy(540, 440, L"(5)黑方的第一枚棋");
	outtextxy(540, 470, L"   子可下在棋盘任");
	outtextxy(540, 500, L"   意交叉点上。");
	/*
	(1)对局双方各执一色棋子。
	(2)空棋盘开局。
	(3)黑先、白后,交替下子,每次只能下一子。
	(4)棋子下在棋盘的空白点上,棋子下定后,不得向其它点移动,不得从棋盘上拿掉或拿起另落别处。
	(5)黑方的第一枚棋子可下在棋盘任意交叉点上。
	*/
}

void grid::drawWhite(int x, int y)  {// 在指定位置打印白色棋子
	putimage(x - (size / 2), y - (size / 2), &whiteChess[1], SRCAND);
	putimage(x - (size / 2), y - (size / 2), &whiteChess[0], SRCINVERT);
}

void grid::drawBlcak(int x, int y) { // 在指定位置打印黑色棋子
	putimage(x - (size / 2), y - (size / 2), &blackChess[1], SRCAND);
	putimage(x - (size / 2), y - (size / 2), &blackChess[0], SRCINVERT);
}

void grid::drawGameOver(int x, int y) {
	putimage(x, y, &gameOver);
}


client.cpp

#include "client.h"
#include 
#include "grid.h"

using namespace std;

int main() {
	// 初始化客户端
	string ipAddress;
	client test;
	cout << "请输入服务器IP(127.0.0.1):" << endl;
	cin >> ipAddress;
	test.initClient();
	test.connectServer(ipAddress.c_str());

	// 用来记录是第几号客户端
	dataNode id;
	id = test.revcData();

	// 初始化窗口
	grid grid;
	grid.initGrid();
	grid.drawGrid();
	int step = 0;
	while (1) {
		dataNode data;// 用来保存接收和发送的数据 
		// 打印提示信息
		grid.drawTips(data, step);
		data = test.revcData(); // 发送数据包
		// 打印对方的棋子
		if (data.x != -10 && data.y != -10){
			if (data.per == 1)
				grid.drawBlcak(data.x, data.y);
			if (data.per == 2)
				grid.drawWhite(data.x, data.y);
			map[data.x][data.y].isChess = true;
			map[data.x][data.y].turn = data.per;
		}
		// 判断是否胜利
		if (grid.isWin(data)) {
			data = test.revcData();
			if (data.isRestart) { // 判断对方是否要继续游戏
				grid.initGrid();
				grid.drawGrid();
				data.winner = -1;
				data.isRestart = false;
				step = 0;
			}
			else {
				_getch();
				test.closeConnect();
				return 0;
			}
		}
		// 如果到自己的回合了
		settextcolor(BLACK);
		outtextxy(540, 20, L"现在是");
		if (id.turn == 1)
			grid.drawBlcak(614, 20 + 8);
		if (id.turn == 2)
			grid.drawWhite(614, 20 + 8);
		outtextxy(630, 20, L"的回合");
		while (data.turn == id.turn) {
			// 获取鼠标点击信息,先清空鼠标缓存
			FlushMouseMsgBuffer();
			MOUSEMSG m;
			m = GetMouseMsg();
			if (m.mkLButton && !map[(m.x / 32) * 32 + 16][(m.y / 32) * 32 + 16].isChess) {
				step++;
				// 让棋子在格子交点出现
				data.x = (m.x / 32) * 32 + 16;
				data.y = (m.y / 32) * 32 + 16;
				// 判断棋子颜色
				if (id.turn == 1)
					grid.drawBlcak(data.x, data.y);
				if (id.turn == 2)
					grid.drawWhite(data.x, data.y);
				// 发送数据包
				map[data.x][data.y].isChess = true;
				map[data.x][data.y].turn = id.turn;
				data.winner = grid.check(data.x, data.y);
				data.turn = id.turn + 1;
				test.sendData(data);
				break;
			}
		}
		// 判断是否胜利
		if (grid.isWin(data)) {
			grid.drawGameOver(250, 242);
			char isRestart = getch();
			if (isRestart == 'y' || isRestart == 'Y') { // 判断是否要继续游戏
				grid.initGrid();
				grid.drawGrid();
				data.winner = -1;
				data.isRestart = true;
				test.sendData(data);
				data.isRestart = false;
				step = 0;
			}
			else {
				_getch();
				test.closeConnect();
				return 0;
			}
		}
	}
	_getch();
	test.closeConnect();
	return 0;
}