~ 如何用C++自制一个日麻游戏 ~(一)大体框架构建 § 2 数据结构

导入

有了整个游戏的流程构想,就需要把它写进类里,但要清楚类的互相包含关系

正题

以下树状图写出了互相包含的关系:

  1. Maj_system 是整的一局游戏,从洗牌发牌,到荣和算点,包含了四家(Opponent)、136 张牌山(shan)、牌山计数(shan_count)、场风(wind)和现在轮到操作的玩家(turn)
  2. Opponent 是四家的数据,每家都有 14 张手牌(Tile),副露区(Fulu),还有舍张区(牌河)(she)
  3. 每个 Fulu 类型包含了四张牌(Tile),副露来源方(jing),副露类型(lu)
  4. Wind 类型记录风,只有 0 1 2 3(东南西北)四个值,-fulu-(下划线)是副露的枚举类型,以后会介绍在这里插入图片描述

以上,先写 Maj_system 类,除了包含以上的一些数据,还有上一篇所提到的那些函数,都要写进去:

typedef signed __int16 _int_;		//定义类型别名,方便有需要时候修改
typedef unsigned long long _ull_;		//下标转化
using namespace std;

class Maj_system
{
private:
	Wind turn;		//出牌家 
	array<Opponent, 4> _op_;		//四家
	Wind wind;		//场风 
	Round_info shan_count;		//牌山计数
	array<Tile*, 136> shan;		//牌山 注意包括头文件<array>
public:
	explicit Maj_system(_wind_ _wind) :wind{ _wind }		//(场风)开局
	{return;}
	//以上System()为开始构造函数
	void _mo()		//摸牌 
	{return;}
	void _da()		//打牌 
	{return;}
	//以上_mo(),_da()为一般性过程 
	_fulu_ _jiaangang()		//加杠、暗杠判断
	{return 0;}
	_int_ _penggang()		//碰、大明杠判断
	{return 0;}
	bool _chi()		//吃判断
	{return 0;}
	bool _lizhi()		//立直判断 
	{return 0;}
	//以上_jiaangang(),_penggang(),_chi(),_lizhi()为特殊性过程(鸣牌) 
	bool _zimo()			//自摸判断
	{return 0;}
	bool _ronghe(char mode)		//荣和判断 
	{return 0;}
	//以上_zimo(),_ronghe()为总结性过程(特殊鸣牌)
	~Maj_system() //结算
	{return;}
	//以上jiesuan()为结束析构函数 
};

以上这些我放在了 main.hpp 中,和 main 函数分开
至此 Maj_system 类大体框架完成,以后不会有很大的变动了

  1. enum 类型相当于把一堆宏定义(#define)(或常量表达式(constexpr))打包,成为一个集合,可以美化代码
  2. 数组(array<类型名, 长度> 对象名)是一个长度一定的数组,较一般的数组功能更多,数据更安全
  3. 容器(vector<类型名> 对象名(长度, 初值))(括号内可只有长度,也可以没有括号(空容器))是一个可变长度的数组,功能强大,也更安全;但是类成员 vector 不能直接初始化,要在构造函数内进行 resize(n)1 操作
  4. 构造函数后用":",也是赋初值的意思,可以将成员函数在这里赋值,他会在构造函数执行前赋值
  5. 大括号赋初值是 C++11 的新标准,它统一了所有赋初值的方法(如普通的等于号赋值,数组的大括号赋值,对象的构造函数赋值等):
//旧赋值法
int a = 1;
int b[3] = {1,2,3};
T c(3);		// T 是一个类,构造函数接受一个整数参数
//新赋值法
int a{ 1 };
int b[3]{ 1,2,3 };		//注意!这里没有等于号
T c{ 3 };		//同上
  1. explicit 是针对于构造函数的关键字,加上后就会禁止隐式调用构造函数2,在最新版VS上,相当于禁止了用等号赋初值(因为等号是将等号后的字符先实现成对象,再赋值)(大、小括号的赋初值仍然允许)
enum _wind_ { EAST, SOUTH, WEST, NORTH };		//风
enum _fulu_ {		//副露 0b指的是二进制数,用处会在以后解释
	NULL_FULU	= 0b1,				//空
	CHI			= 0b10,				//吃
	PENG		= 0b100,			//碰
	JIA			= 0b1000,			//加杠
	DAMING		= 0b10000,			//大明杠
	AN			= 0b100000,			//暗杠
	LIZHI		= 0b1000000,		//立直
	LIUJV		= 0b10000000,		//流局
	RONGHE		= 0b100000000,		//荣和
	ZIMO		= 0b1000000000,		//自摸
	DA			= 0b10000000000,	//打牌 打、摸这两个只是为了传递信息
	MO			= 0b100000000000	//摸牌
};
  1. 这里有个优化:%4相当于&3,2的正整数次幂有这种性质3
class Wind
{
public:
	_wind_ wind{ EAST };		//值范围0-3
	explicit Wind(_int_ _wind = EAST) : wind{ (_wind_)(_wind & 3) } {}		//对负数失效
	operator _int_() const { return wind; }
	void operator=(_int_ _wind) { wind = (_wind_)_wind; }
	_wind_& operator++() { wind = (_wind_)((wind + 1) & 3); return wind; }		//&3相当于%4
	_wind_& operator--()
	{
		wind = wind == EAST ? NORTH : (_wind_)(wind - 1);
		return wind;
	}
};

汇编

class Fulu
{
public:
	vector<Tile*> tile;
	/*vector<Tile*> tile(4, nullptr);*/		//(E0079)应输入类型说明符
	_fulu_ lu{ NULL_FULU };		//记录副露类型
	Wind jing;		//记录牌来源方
	explicit Fulu(){tile.reserve(4);}		//分配空间
};
class Opponent
{
public:
	vector<Tile*> tile;		//手牌区 注意包括头文件<vector>
	vector<Fulu> fulu;		//副露区 
	vector<Tile*> she;		//牌河(舍张)
	explicit Opponent(){ tile.reserve(14);tile.resize(14); }		//分配空间
};

这里的 Round_info 为了更好地贴近面麻,加入了壁、幢等说法,如果要简化,这个可以只用一个整型变量记录

class Round_info
{
private:
	_int_ dice1{ 0 };		//记录骰子点数(每点表示1幢)
	_int_ dice2{ 0 };
	Wind bi;		//壁牌计数(0-3)
	_int_ zhuang{ 0 };		//幢计数(0-16)
	bool height{ 0 };		//是某幢上面(0)还是下面(1)的牌
	_int_ count{ 0 };		//牌山计数
public:
	void rand_dice()		//随机生成骰子
	{
		dice1 = rand() % 6 + 1;
		dice2 = rand() % 6 + 1;
		zhuang = dice1 + dice2;
	}
	constexpr operator _int_() const { return count; }
	_int_& operator++()
	{
		if (height == 0)
			height = 1;
		else if (zhuang < 16)
		{
			height = 0;
			++zhuang;
		}
		else
		{
			height = 0;
			zhuang = 0;
			++bi;
		}
		++count;
		return count;
	}
	constexpr _int_ remains() const { return 122 - count - gang; }
	//count 是下一张牌的序号,但下一张牌的序号正好是现在已取走的牌数,所以剩余数=136-14-count-gang
	//gang 是杠牌的计数,以后会引入
};

规则参考

[1] 日麻百科
[2] 资深麻友
[3] 雀魂麻将
[4] 雀姬麻将

原创文章,转载请标明出处,如有谬误欢迎各位指正
欢迎交流麻将算法,QQ:2639914082

  1. C++ vector的reserve和resize详解 ↩︎

  2. C++ explicit关键字详解 ↩︎

  3. 使用与运算符代替求余运算符的技巧 ↩︎

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值