项目——网络对战五子棋(web-gobang)(代码片段)

It‘ssosimple It‘ssosimple     2022-12-04     295

关键词:

目录


项目源码:https://github.com/mxzw/Project/tree/main/webgobang-project/src

1. 项目开发背景

小时候,闲来无事最喜欢和别人玩的游戏之一就是五子棋了,它规则简单,但却玩法多变,直到现在我还时不时的在手机上和别人在线对战。最近自己学习Linux方面的相关知识,学习了多线程和Socket编程,并对网络这块的数据流向有了基本的了解,加之刚好找到了一份纯前端的单机五子棋的源码,于是就想尝试能否在该前端的基础上,加上后端,使其变成联机版的网络在线对战五子棋。

由于本人对前端学习并不深入,所有前端的代码都是基于原来现有的前端代码进行的修改。

项目运行截图

用户登录:

用户注册:

下棋页面:

胜利页面:

2.项目需求 & 开发环境

项目需求

  • 支持用户登录、用户注册功能
  • 支持在线匹配对战功能
  • 支持点击重开按钮重新进行匹配功能
  • 支持对局信息持久化功能

开发环境

gcc/g++:7.3.1版本,使用C++语言进行开发,并采用MySQL数据库进行相应数据的存储。

项目所用到的相关知识点

多线程,互斥锁和条件变量,httplib,JSON,AJAX,MySQL数据库

3.项目设计 & 项目难点

项目设计

首先,本项目大体的数据流向是前端通过AJAX方法将JSON格式的数据传送到后端,后端通过HttpLib库中的方法获取到前端当中的JSON数据,然后对该数据进行相应逻辑的处理后,将结果返回给前端,前端根据结果显示相应的内容。

项目架构图如下:

项目难点:

  • 难点1: 当用户点击开始匹配按钮时,创建房间并为用户分配相应的对手。
  • 难点2:在对战过程中,用户只能下一种颜色的棋子(黑棋/白棋),并且只能在属于自己的回合进行下棋。
  • 难点3:在对战过程中,对手下的棋子的位置能够及时的同步在自己的棋盘上。

4.功能实现 & 难点解决

由项目设计,我们可以得到本项目大概的框架

  • HTTP模块:httplib.h
  • 用户管理模块房间管理模块:room_player.hpp
  • 会话模块:Session.hpp
  • 数据库管理模块:ManagerDB.hpp
  • 工具模块:tools.hpp
  • 项目驱动模块:webgobang.hpp、webgobang.cpp

4.1. 用户登录 / 注册

当用户在网页上点击登录按钮的时候,前端会对用户输入的邮箱和密码进行获取,将数据转为JSON格式(序列化),并通过AJAX,采用POST的http请求方法将该数据发送到后端去,后端校验该数据之后,将其持久化到数据库中,并且根据输入的邮箱和密码生成相应的MD5码作为当前用户的cookie信息返回回去。

登录页面后端代码如下:

http_svr_.Post("/login", [=](const Request& req, Response& res) 
	//1.校验用户(登录页面)所提交的邮箱和密码(与数据库进行比较)
	cout << req.body << endl;
	Json::Reader r;
	Json::Value v;   //存储string :string 的键值对 
	r.parse(req.body, v); // 用于将请求正文中的内容反序列化(从连续的二进制转为对应键值对)
						 //cout <<"v[\\"email\\"]" <<v["email"] << endl; 
						 //cout <<"v[\\"password\\"]" <<v["password"] << endl; 
						 //
   // 1.1 同数据库进行校验并得到对应user_id
	int user_id = this->db_svr_->CheckUserExist(v);

	//1.2 为了防止用户不经过登录,直接访问主页面,我们需要在这里加上一个session会话
	//      设置MD5码,作为会话id 进行http响应

	string tmp = "";
	if (user_id > 0)
	
		Session sess(v, user_id);

		string session_id = sess.GetSessionID();
		//回复的会话格式为JSESSIONID = xxxxx
		tmp += "JSESSIONID=" + session_id;

		all_sess_->SetSessionInfo(session_id, sess);

		//将该用户放到用户管理类中进行管理
		this->pm_->insertPlayer2Map(user_id);

	

	//2.组织http响应格式使用校验的结果进行响应(使用Json对其进行响应,因为ajax中接收的数据类型为json)
	Json::Value res_value;
	// 在前端ajax请求中,要求接收的是一个Json对象,并且该对象中含有status的键值对
	res_value["status"] = user_id >= 0 ? true : false;


	//将该Json-Value对象序列化 并进行响应
	res.body = Serializa(res_value);
	res.set_header("Set-Cookie", tmp.c_str());
	res.set_header("content-Type", "application/json");
	);

生成MD5码

在头文件#include<openssl/md5.h>中包含着生成MD5码的方法。

/*
* SumMd5 : 生成对应的MD5码(初始化session_id_)
*  即将real_str_中的内容调用MD5_xxx函数生成对应的md5码,存入session_id中
*  成功:true
*  失败:false
*  int MD5_Init(MD5_CTX *c);初始化MD5码
*  int MD5_Update(MD5_CTX *c, const void *data, unsigned long len);
* 					 通过传入的字符串更新MD5码
*  int MD5_Final(unsigned char *md, MD5_CTX *c); 获取生成的MD5码
*/
bool SumMd5()

    MD5_CTX c;
    //1.初始化MD5码
    MD5_Init(&c);

    //2.更新和获取MD5码
    if (MD5_Update(&c, real_str_.c_str(), real_str_.size()) != 1)
    
        return false;
    

    unsigned char md5[16] =  0 ;
    if (MD5_Final(md5, &c) != 1)
    
        return false;
    

    //3.将生成的MD5码按照16进制的格式存入会话id中
    char tmp[3] =  0 ;
    char buf[32] =  0 ;
    for (int i = 0; i < 16; ++i)
    
        snprintf(tmp, sizeof(tmp) - 1, "%02x", md5[i]);
        //cout << tmp;
        strncat(buf, tmp, 2);
    

    session_id_ = buf;
    return true;

用户注册的大体逻辑也一样,这里则不再进行过多的解释。

4.2. 项目难点解决

4.2.1 难点一:当用户点击开始匹配按钮时,创建房间并为用户分配相应的对手。

解决:

这里采用vector作为匹配池,创建一个线程用来从匹配池中获取匹配玩家进行匹配,并且引入玩家状态(ONLINE、MATCHING、PLAYING)作为区分,当玩家点击开始匹配按钮时,玩家状态变为MATCHING,并且将其加入匹配池当中,唤醒匹配线程进行匹配,如果匹配线程发现当前匹配玩家数量 < 2,则不进行匹配,如果发送当前匹配玩家数量为奇数,则匹配池中最后一个玩家不进行匹配,等待下一次匹配,其余玩家两两配对。

匹配线程所做的事情:当从匹配池中获取到两个玩家信息后,则为这两名玩家创建一个房间,并将这两名玩家的状态变为PALYING状态,为其分配创建出的房间号和对应所执的黑棋/白棋。

匹配线程代码如下:

// 匹配线程入口函数
static void* MatchServer(void* arg)

	pthread_detach(pthread_self());
	WebGoBang* wg = (WebGoBang*)arg;

	while (1)
	
		pthread_mutex_lock(&wg->vec_lock_);
		//从匹配池中进行匹配
		//要操作的是vector,所以首先要加锁,
		//然后还要判断当前正在匹配玩家的数量,如果小于2,则放入等待队列中 
		while (wg->match_pool_num_ < 2)
		
			pthread_cond_wait(&wg->vec_cond_, &wg->vec_lock_);
		

		//匹配池中的人数一定大于等于2
		// 奇数:一定有一个玩家要轮空
		// 偶数:两两进行匹配

		vector<int>& iv = wg->match_pool_;
		size_t size = iv.size();
		int last_id = -1;
		if (size % 2)
		
			//人数是奇数
			//默认让最后一个人轮空
			last_id = iv[size - 1];
			size -= 1;
		
		for (int i = size - 1;i >= 0; i -= 2)
		
			int player_one = iv[i];
			int player_two = iv[i - 1];

			//假设匹配成功
			int room_id = wg->rm_->CreateRoom(player_one, player_two);
			//然后再让当前这两个用户的状态变为PALYING
			wg->pm_->SetUserStartStatus(player_one, PLAYING);
			wg->pm_->SetUserStartStatus(player_two, PLAYING);

			//为该用户分配房间号
			wg->pm_->SetRoomID(player_one, room_id);
			wg->pm_->SetRoomID(player_two, room_id);

			//为用户分配黑棋/白棋
			wg->pm_->SetUserChessName(player_one, "黑棋");
			wg->pm_->SetUserChessName(player_two, "白棋");


			cout << "player1:" << player_one << ", palyer2:" 
			<< player_two << ",roomId:" << room_id << endl;

		
		if (last_id < -1)
			wg->PushPlayer2MatchPool(last_id);

		wg->MatchPoolClear();
		pthread_mutex_unlock(&wg->vec_lock_);
	
	return NULL;

这个时候,前端的逻辑就是当监听到用户点击开始匹配按钮之后,就向后端发送一个AJAX请求,用来进行通知后端进行匹配,当后端将当前用户放入匹配池中之后,将相应的结果返回给前端,前端在获得这个结果之后,则就意味着当前用户正在处于匹配状态(MATCHING),为了获知什么时候才会匹配上,则前端需要不停的向后端发送AJAX请求,获知当前匹配的结果,直到当前用户匹配上为止。这里的循环我们采用的是setInterval函数,在该函数内部不停调用AJAX请求,并且每隔1秒发送一次。

js如下:

SetMatch: function() 
if (game.match_flag == = false)
return;
console.log("SetMatch Func , 开始匹配...");
$.ajax(
	url: "/SetMatch",
	type : "Get",
	dataType : "JSON",
	//async : false ==> 禁止异步请求   chrome 有可能会禁止 ajax 的同步请求
	async : false,
	success : function(data) 
		if (data.status == = 0) 
			console.log("SetMatch Func , 开始匹配状态设置成功...");
			game.GetMatchResult()
		
else 
 alert("匹配失败, 重新匹配...");

,
	);
,

GetMatchResult: function() 
	//循环的获取匹配结果
	//向后台循环发送获取匹配结果, 直到匹配成功 -- 调用setInterval函数即可  
	let s = 0;
	let _time = setInterval(() = > 
		console.log("GetMatchResult, " + s++);
		$.ajax(
			url: "/Match",
			type : "Get",
			dataType : "JSON",
			async : false,//同步
			success : function(data) 
				game.match_status = data.status;
				if (data.status == = 1) 
					/*
					*   data = 
					*      status : 匹配状态
					*      room_id : xxx,
					*      chess_name: xxxx
					*   
					* */
					console.log("匹配成功,room_id is:" + data.room_id);
					alert("匹配成功, 您执 " + data.chess_name 
											+ ", 黑棋先走");
					game.room_id = data.room_id;
					game.my_chess_name = data.chess_name;

					/*
					 * 当匹配成功之后,持白棋的人,
					 * 需要循环去获取执黑色棋子的落子位置
					 */
					if (data.chess_name == = "白棋")
						game.GetPeerStep();

					game.match_flag = false;

					//将颜色变为灰色
					game.start_match.style.background = '#d0cdcd';
					game.start_match.style.color = '#505050';

				
				else if (data.status == = 0 && s >= 30)
				
					//当没有匹配上,一直到循环结束,才弹框提示匹配人数太少
					alert("当前匹配人数太少,请重新匹配...");
				
				else if (data.status == = -1)
				
					alert("当前会话失效,请重新登录....");
					window.location.href = "index.html";
				
			,
			);
		if (s >= 30 || game.match_status == = 1 
					|| game.match_status == = -1) 
			//当调用次数超过30次时。就不再循环调用
			// 或者当匹配上了
			// 会着匹配失败
			clearInterval(_time);
		
	, 1000); // 1000 -> 每隔1秒调用一次
,

这里也不是无限制的在进行调用,而是当调用次数超过30次,或者已经匹配上、匹配失败的情况下,都会停止对该函数的调用。

4.2.2 难点二:在对战过程中,用户只能下一种颜色的棋子(黑棋/白棋),并且只能在属于自己的回合进行下棋。

解决:

首先,我们要明确的一点就是当前用户能够下棋的条件是一定是已经匹配上了,并且当前处于自己的回合,才可以进行下棋。当两个用户匹配上之后,在创建房间的时候,已经规定了每个用户只能使用一种棋子的颜色(用户1:黑色,用户2:白色),并且决定了当前该哪个用户下棋(初始请求是第一个用户先下棋),因此,我们只需要判断当前房间中的是否该自己下棋即可。


因此,每次只需判断当前用户的user_id_和whose_turn_是否相同即可,相同则代表着是自己的回合,不同则代表着是对方的回合,不能进行下棋

此时前端的逻辑就是当用户点击棋盘上的某一点的时候,就向后端发送一个AJAX请求,判断当前是否是自己的回合,如果是则允许落子,否则,不允许落子。如果当前是允许落子的,那么在棋盘上绘制对应棋子之前,会再发送一个AJAX请求到后端,后端会对当前房间的 whose_turn_ 进行修改,并且保存当前所走棋子的位置信息在vector中

4.2.3 难点三:在对战过程中,对手下的棋子的位置能够及时同步在自己的棋盘上。

解决:

假设当前是用户1的回合,当用户1点击棋盘上的某个位置的时候,前端会给后端发送一个AJAX请求,后端在接收到该请求后,会对当前房间的 whose_turn_ 进行修改,并且保存当前所走棋子的位置信息在一个vector中,然后返回相应的结果,前端在接收到该结果之后,就去循环获取对方棋盘最近一次下棋的棋子位置,同理,调用setInterval函数进行循环获取,即向后端发送AJAX请求,后端将保存在vector中的最后位置的棋子位置信息进行返回,前端拿到该棋子位置信息之后,将该棋子绘制到当前用户的棋盘上,这样就实现了两个用户对战棋盘之间的同步。


这里还需注意的是初始情况下,即两个用户刚刚匹配上的时候,这个时候由于是黑棋先走,因此,黑棋用户在刚开始时是不需要获取对方棋盘上棋子的结果的,而相应的白棋是第二步走的,因此,白棋用户需要在刚开始时循环获取对方棋子的下棋结果

项目——网络对战五子棋(web-gobang)(代码片段)

目录1.项目开发背景2.项目需求&开发环境3.项目设计&项目难点4.功能实现&难点解决4.1.用户登录/注册4.2.项目难点解决4.2.1难点一:当用户点击开始匹配按钮时,创建房间并为用户分配相应的对手。4.2.2难点二:... 查看详情

java课程设计——五子棋

一、团队名称、成员介绍团队名称:啦啦啦队团队成员:?何炎玲【组长】:201821123007网络1811负责模块:人机对战、难度等级、界面设计、撰写团队博客?林莹:201821123034网络1812负责模块:人人对战、胜负判定、计时器、代码规... 查看详情

linux项目实战——五子棋(单机人人对战版)(代码片段)

Linux操作系统项目实战——五子棋GIF: 目录       Linux操纵系统项目——五子棋一、问题导引:二、实现要求:三、五子棋原理:1.落子数据信息保存载体:2.落子思路:3.判断“五子连珠”四、项目实... 查看详情

qtvs2017《五子棋》人机对战项目(代码片段)

...年了。一年前,自己学习qt语言时,顺便写了一个五子棋的小项目,个人感觉,这个五子棋项目还算挺大型的,整个项目代码量加起来可能有1w行,现在分享出来给有需要的朋友,参考借鉴。项目包括实... 查看详情

结对-结对编项目作业名称-需求分析

目标要求本次结对编程设计网络休闲益智类游戏——五子棋。这个软件应该达到以下目标:制定合法规则,能够判断出非法操作,以便公正地进行并分出胜负;支持人人对战方便操作和使用。基本规则1、双人对战,由五子棋的... 查看详情

结对-结对编项目作业名称-需求分析

【目标要求】本次结对编程设计网络休闲益智类游戏——五子棋。这个软件应该达到以下目标:制定合法规则,能够判断出非法操作,以便公正地进行并分出胜负;支持人人对战方便用户的操作和使用。【基本规则】1、双人对... 查看详情

双人五子棋对战(需要easyx图像库)

...运行一下玩玩~(虽然==没LOL好玩2333333)设计题目:双人五子棋对战单机游戏使用C语言及VC绘图库,在VC环境下,设计一个双人五子棋对战单机游戏,双方各执黑白一子,黑白双方轮流落子,直到某一 查看详情

五子棋项目的实现人机对战类的具体设计

在之前描述了博弈树算法的思想,现在则是关键类的设计实现。在具体的过程中我们先要设计一个遍历棋型算法,来遍历整个棋盘中的各种棋型 通过最后返回值的不同,来确定不同的棋型当中有评估函数对当前的棋型进行打... 查看详情

结对编程项目五子棋-需求分析

简单介绍五子棋游戏的制作过程与需求支持多个平台的游戏运行,有无网络都可运行玩乐的休闲益智游戏制作:初步设置一个游戏窗口,调节窗口大小画出游戏需要的画面,绘画出需要的落子设置鼠标按键,退出游戏的按键设置... 查看详情

从0开始的go+websocket构建五子棋对战系统(代码片段)

基本框架直接照搬,不多解释。dao为数据库处理层po为实体类middleware为中间件,cors处理跨域app对request请求进行封装router处理路由service为服务层,进行数据处理逻辑网络框架使用gin作为整体的网络框架,文档在这里https://github.com... 查看详情

c#五子棋小游戏源码(人机对战)

点击查看:C#五子棋小游戏源码(人机对战)文件大小:1.3M操作系统:Windows10旗舰版开发工具:VS2019开发语言:.cs 查看详情

结对-结对编项目作业名称-需求分析

HTML版五子棋游戏需求分析  一、建立坐标点,在HTML页面内进行棋盘的绘画  二、对PVE/PVP对战进行分析    1、先进行输赢条件的分析,五子棋可以获胜的条件(横着/竖着/倾斜的五颗颜色相同的棋子连接中无对方的棋子... 查看详情

结对-结对编项目作业名称-设计文档

设计项目:五子棋游戏设计人员:孙政凯游戏设计平台:pygame安装下载python,pygame复习巩固知识点,设计窗口,设计落子,设计游戏规则,设计关闭窗口按键,检查程序。游戏窗口:480,480游戏模式:人机模式,人人对战游戏双... 查看详情

linux实现五子棋(人人对战)(代码片段)

分步解析对于game函数的解析playermove——用户落子isover——判定四种情况chessout——求特定方向的连珠数使用全局变量xy的原因showboard——数组内容可视化完整代码1.game.c#include"game.h"intx=0;inty=0;voidmenu()printf("****************... 查看详情

五子棋人机对战(代码片段)

1.?项目流程UI界面部分利用Java的图形界面工具swing和awt来绘制棋盘的框架,绘制了19X19的网格。然后在画布上增加监听器来监听鼠标点击的部分,然后在邻近的网格交点处绘制棋子,这样就实现了下棋的效果。逻辑处理部分19X19的... 查看详情

c语言实现五子棋三子棋人机对战,包含电脑人工智能对战(可攻可守)(非标题党)(代码片段)

C语言——五子棋、井字棋人机对“战”针对C语言学习过程中的五子棋、三子棋实现记录五子棋人机对战C语言——五子棋、井字棋人机对“战”实际效果一、头文件(game.h)二、测试文件(test.c)三、游戏程序文... 查看详情

五子棋程序设计实现技术文档(代码片段)

五子棋程序设计实现文档文章目录五子棋程序设计实现文档前言一、运行截图二、基本思路1.实现过程2.落子3.悔棋4.人机对战的实现1.机器人落子逻辑**2.改进胜负判断方法3.计算目标点的权值(白棋ai使用)4.计算目标点的权值(黑棋a... 查看详情

五子棋程序设计实现技术文档(代码片段)

五子棋程序设计实现文档文章目录五子棋程序设计实现文档前言一、运行截图二、基本思路1.实现过程2.落子3.悔棋4.人机对战的实现1.机器人落子逻辑**2.改进胜负判断方法3.计算目标点的权值(白棋ai使用)4.计算目标点的权值(黑棋a... 查看详情