C++实现麻将基本听牌胡牌的算法

原创 2017年08月03日 14:03:10
#include <iostream>
#include <vector>
#include <algorithm>
#include <chrono>

enum MajiangType
{
	emMJType_Wan	= 1, //万
	emMJType_Tiao	= 2, //条
	emMJType_Tong	= 3, //筒
	emMJType_Zi	= 4, //字
	emMJType_Hua	= 5  //花
};

constexpr char MJ(char m, char n) {
	return m << 5 | n;
}

inline MajiangType Majiang_Type(char m) {
	return MajiangType(m >> 5);
}

inline char Majiang_Value(char m) {
	return m & 0x1F;
}

enum emMJ:char
{
	emMJ_1Wan = MJ(emMJType_Wan, 1),
	emMJ_2Wan = MJ(emMJType_Wan, 2),
	emMJ_3Wan = MJ(emMJType_Wan, 3),
	emMJ_4Wan = MJ(emMJType_Wan, 4),
	emMJ_5Wan = MJ(emMJType_Wan, 5),
	emMJ_6Wan = MJ(emMJType_Wan, 6),
	emMJ_7Wan = MJ(emMJType_Wan, 7),
	emMJ_8Wan = MJ(emMJType_Wan, 8),
	emMJ_9Wan = MJ(emMJType_Wan, 9),

	emMJ_1Tiao = MJ(emMJType_Tiao, 1),
	emMJ_2Tiao = MJ(emMJType_Tiao, 2),
	emMJ_3Tiao = MJ(emMJType_Tiao, 3),
	emMJ_4Tiao = MJ(emMJType_Tiao, 4),
	emMJ_5Tiao = MJ(emMJType_Tiao, 5),
	emMJ_6Tiao = MJ(emMJType_Tiao, 6),
	emMJ_7Tiao = MJ(emMJType_Tiao, 7),
	emMJ_8Tiao = MJ(emMJType_Tiao, 8),
	emMJ_9Tiao = MJ(emMJType_Tiao, 9),

	emMJ_1Tong = MJ(emMJType_Tong, 1),
	emMJ_2Tong = MJ(emMJType_Tong, 2),
	emMJ_3Tong = MJ(emMJType_Tong, 3),
	emMJ_4Tong = MJ(emMJType_Tong, 4),
	emMJ_5Tong = MJ(emMJType_Tong, 5),
	emMJ_6Tong = MJ(emMJType_Tong, 6),
	emMJ_7Tong = MJ(emMJType_Tong, 7),
	emMJ_8Tong = MJ(emMJType_Tong, 8),
	emMJ_9Tong = MJ(emMJType_Tong, 9),

	//字牌不能形成顺子,所以编码不能连续,而且有的地方有买码规则,字牌也根据除以4的余数被分配到了4个方位
	emMJ_DongFeng =		MJ(4, 1),//东 1 % 4 = 1
	emMJ_NanFeng =		MJ(4, 6),//南 6 % 4 = 2
	emMJ_XiFeng =		MJ(4, 11),//西 11 % 4 = 3
	emMJ_BeiFeng =		MJ(4, 16),//北 16 % 4 = 0
	emMJ_HongZhong =	MJ(4, 18),//中 18 % 4 = 2
	emMJ_FaCai =		MJ(4, 23),//发 23 % 4 = 3
	emMJ_BaiBan =		MJ(4, 28),//白 28 % 4 = 0

	//一副中花牌各只有一张
	emMJ_Mei =	MJ(5, 1),//梅
	emMJ_Lan =	MJ(5, 3),//兰
	emMJ_Ju =	MJ(5, 5),//菊
	emMJ_Zhu =	MJ(5, 7),//竹
	emMJ_Chun = MJ(5, 9),//春
	emMJ_Xia =	MJ(5, 11),//夏
	emMJ_Qiu =	MJ(5, 13),//秋
	emMJ_Dong = MJ(5,15)  //冬
};

const std::vector<char> all_majiang = {
	emMJ_1Wan,
	emMJ_2Wan,
	emMJ_3Wan,
	emMJ_4Wan,
	emMJ_5Wan,
	emMJ_6Wan,
	emMJ_7Wan,
	emMJ_8Wan,
	emMJ_9Wan,

	emMJ_1Tiao,
	emMJ_2Tiao,
	emMJ_3Tiao,
	emMJ_4Tiao,
	emMJ_5Tiao,
	emMJ_6Tiao,
	emMJ_7Tiao,
	emMJ_8Tiao,
	emMJ_9Tiao,

	emMJ_1Tong,
	emMJ_2Tong,
	emMJ_3Tong,
	emMJ_4Tong,
	emMJ_5Tong,
	emMJ_6Tong,
	emMJ_7Tong,
	emMJ_8Tong,
	emMJ_9Tong,

	emMJ_DongFeng,
	emMJ_NanFeng,
	emMJ_XiFeng,
	emMJ_BeiFeng,
	emMJ_BaiBan,
	emMJ_FaCai,
	emMJ_HongZhong
};
const std::vector<emMJ> pattern131 = { emMJ_1Wan,emMJ_9Wan,emMJ_1Tiao,emMJ_9Tiao,emMJ_1Tong,emMJ_9Tong,
		emMJ_DongFeng,emMJ_NanFeng,emMJ_XiFeng,emMJ_BeiFeng,emMJ_HongZhong,emMJ_FaCai,emMJ_BaiBan};

//基本和牌类型
bool IsCommonHu(const std::vector<char>& original_pai)
{
	//前提:牌已经排好序,不含已碰牌和已杠牌,所以牌数应该是3n+2
	//过程:先找出一对将牌,然后再寻找刻子牌和顺子牌,直到剩余牌为0才表示可和牌,否则不能和牌

	//记录将牌位置
	size_t jiang_location = -1;
	std::vector<char> pai;
	while (true)
	{
		auto i = jiang_location + 1;
		if (i >= original_pai.size())
		{
			return false;
		}

		pai = original_pai;
		if (jiang_location != -1)
		{
			if (pai[i] == pai[jiang_location])
			{
				++i;
			}
		}

		//寻找将牌位置,记录将牌第二个,并擦除该两牌
		jiang_location = -1;
		for (; i < pai.size() - 1; ++ i)
		{
			if (pai[i] == pai[i + 1])
			{
				jiang_location = i + 1;
				pai.erase(pai.begin() + i, pai.begin() + i + 2);
				break;
			}
		}
		if (jiang_location == -1)
		{
			//没有将牌,不能和牌
			return false;
		}

		//剩下的牌数是3的倍数
		//从左起第1张牌开始,它必须能组成刻子牌或者顺子牌才能和,否则不能和
		while (pai.size() >= 3)
		{
			if (pai[0] == pai[1] && pai[0] == pai[2])
			{
				//找到刻子牌并移除
				pai.erase(pai.begin(), pai.begin() + 3);
			}
			else
			{
				auto it1 = std::find(pai.begin() + 1,pai.end(), pai[0] + 1);
				if (it1 != pai.end())
				{
					auto it2 = std::find(it1 + 1, pai.end(), pai[0] + 2);
					if (it2 != pai.end())
					{
						//找到顺子牌并移除
						pai.erase(it2);
						pai.erase(it1);
						pai.erase(pai.begin());
					}
					else
					{
						break;
					}
				}
				else
				{
					break;
				}
			}
		}

		if (pai.empty())
		{
			break;
		}
	}
	
	return true;
}

//是否是十三幺牌型
bool Is131Hu(std::vector<char> original_pai)
{
	if (original_pai.size() != 14)
	{
		return false;
	}
	size_t i = 0;
	while (i < original_pai.size()-1)
	{
		if (original_pai[i] == original_pai[i+1])
		{
			break;
		}
		++i;
	}
	if (i == original_pai.size() - 1)
	{
		return false;
	}

	original_pai.erase(original_pai.begin() + i);
	if (pai.size() != pattern131.size()){
		return false;
	}
	for (size_t i = 0; i < pai.size(); ++i)
	{
		if (original_pai.[i] != pattern131[i]) {
			return false;
		}
	}
	return true;
}

//是否是小七对牌型
bool Is7pairsHu(const std::vector<char>& pai) {
	const int pairs = 7;
	if (pai.size() != 2 * pairs)
	{
		return false;
	}
	int i = 0;
	for (; i < pairs; ++i)
	{
		if (pai[2 * i] != pai[2 * i + 1])
		{
			break;
		}
	}
	return i == pairs;
}

//检查听哪些牌
std::vector<char> Ting(const std::vector<char>& pai)
{
	//遍历所有牌,依次加入,判断是否能和牌
	std::vector<char> hupai;
	for (auto& i : all_majiang)
	{
		auto temp(pai);
		temp.push_back(i);

		std::sort(temp.begin(), temp.end());  //先排序
		if (Is131Hu(temp) || Is7pairsHu(temp) || IsCommonHu(temp))
		{
			hupai.push_back(i);
		}
	}
	return hupai;
}

int main()
{
	std::vector<char> v = { MJ(1,2),MJ(1,2),MJ(2,2),MJ(2,2),MJ(1,3),
		MJ(3,4),MJ(3,4),MJ(1,4),MJ(1,4),MJ(2,5),MJ(2,5),MJ(1,6),MJ(1,6) };

	std::vector<char> tingpai;
	auto start = std::chrono::steady_clock::now();
	tingpai = Ting(v);
	std::chrono::nanoseconds ns = std::chrono::steady_clock::now() - start;
	std::cout << "time:" << ns.count() << std::endl;
	for (auto& i : tingpai)
	{
		std::cout << "Result:" << (int)i << std::endl;
	}

	system("pause");
    	return 0;
}

麻将游戏的听牌算法

测试测试!!~~~~这两周都是在测试各种BUG,没事情的时候自己在网上学学新知识,也为下个月的游戏改版预热。最近呢我也开始了我的shader之旅,估计也是这充满神秘和艰辛的旅途吧,哈哈哈!      ...
  • lj343424531
  • lj343424531
  • 2015年11月04日 11:04
  • 6765

癞子麻将胡牌以及听牌算法实现

最先实现的就是算法的实现。 需求:碰杠胡  ,不能吃 ,不能听 ,只能自摸胡,其中癞子可以做任意牌但是不能碰和杠。 写的时候还不会玩麻将,还是老板教的。^_^ 最麻烦的是胡牌算法。之前搜到的都是不包...
  • skillart
  • skillart
  • 2014年10月24日 11:03
  • 18502

麻将听胡算法

近来正在做麻将游戏,写了个判断听牌的算法(暂且称其为算法),和大家分享一下, 算法还没通过全面的验证,可能会遗漏某些情况。(我不太会打麻将阿) 其中判断和牌算法的程序代码是网上找的,是听牌算法的基...
  • deng5254
  • deng5254
  • 2015年07月22日 02:20
  • 3129

麻将的和牌、听牌以及一向听(即能否打一张牌进行立直)的算法。

感觉网上关于麻将的源码资源很少,一般这种算法都是用递归,把牌堆分解成若干子牌堆然后针对2-3张牌的情形给出一个出口。 和牌算法比较常见,毕竟只要是麻将编程都要用到,后面两种虽然普通的麻将编程用不到,...
  • u010274704
  • u010274704
  • 2016年12月08日 10:46
  • 2114

麻将听牌的算法(java)

   近来正在做麻将游戏,写了个判断听牌的算法(暂且称其为算法),和大家分享一下,算法还没通过全面的验证,可能会遗漏某些情况。(我不太会打麻将阿)其中判断和牌算法的程序代码是网上找的,是听牌算法的基石...
  • caoxiongjun
  • caoxiongjun
  • 2006年09月06日 12:37
  • 9318

麻将算法(上)

麻将算法
  • xiu2016
  • xiu2016
  • 2017年07月28日 20:59
  • 1202

node.js——麻将算法(一)基本判胡

大家好,失踪已久的九日哥回来了    由于前段时间一直专注于开发鉴黄,所以身心都有些不适,故也给了自己很长的放松时间~ 然而回来了之后,九日哥毅然决然的选择了棋牌(dubo)事业~看来我这辈子也就...
  • sm9sun
  • sm9sun
  • 2017年03月23日 20:41
  • 5055

麻将癞子胡牌听牌算法

  • 2017年03月18日 20:47
  • 28KB
  • 下载

麻将查听算法

麻将通常有13张牌,在打的时候随着吃,碰越来越少。总数应该维持在3*n + 1,n=0~4,比如1张,4张,7张或10张。胡牌时,加一张(可能是自摸或吃碰来的一张)组成n个顺子或暗刻,外加一个麻将对...
  • u014261855
  • u014261855
  • 2016年10月30日 16:56
  • 1863

C++带赖子的麻将听牌检测算法实现

c++ 对带有赖子的麻将听牌检测算法的实现
  • sdghchj
  • sdghchj
  • 2017年09月07日 09:51
  • 592
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:C++实现麻将基本听牌胡牌的算法
举报原因:
原因补充:

(最多只允许输入30个字)