#include <iostream>
#include <vector>
#include <set>
#include <algorithm>enum MajiangType:uint8_t
{emMJType_Wan   = 1, //万emMJType_Tiao  = 2, //条emMJType_Tong  = 3, //筒emMJType_Zi    = 4, //字emMJType_Hua   = 5  //花
};constexpr uint8_t MJ(uint8_t m, uint8_t n) {return m << 4 | (n & 0x0F);
}inline MajiangType Majiang_Type(uint8_t m) {return MajiangType(m >> 4);
}inline uint8_t Majiang_Value(uint8_t m) {return m & 0x0F;
}enum emMJ:uint8_t
{emMJ_Unknown = 0,emMJ_Joker = 0,     //变后的赖子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),emMJ_DongFeng =      MJ(4, 1),//东 1 % 4 = 1emMJ_NanFeng =      MJ(4, 2),//南 2 % 4 = 2emMJ_XiFeng =       MJ(4, 3),//西 3 % 4 = 3emMJ_BeiFeng =      MJ(4, 4),//北 4 % 4 = 0emMJ_HongZhong =    MJ(4, 5),//中 5 % 4 = 1emMJ_FaCai =        MJ(4, 6),//发 6 % 4 = 2emMJ_BaiBan =       MJ(4, 7),//白 7 % 4 = 3//一副中花牌各只有一张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::set<emMJ> all_majiang_types = {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_HongZhong,emMJ_FaCai,emMJ_BaiBan
};//十三幺牌型:13张再加其中任意一张
static const std::set<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 };using MaJiangPai = std::vector<emMJ>;template <typename T, typename V>
static T Find_In_Sorted(T begin, T end, V v) {auto it = begin;while (it != end){if (*it == v){break;}else if (*it > v){it = end;break;}++it;}return it;
}//递归拆分手牌
bool ResolvePai(MaJiangPai pai, uint8_t joker_count)
{if (pai.empty() && joker_count % 3 == 0){return true;}else if (pai.size() + joker_count < 3){return false;}if (pai.size() >= 3 && pai[0] == pai[2]){//找到刻子牌并移除pai.erase(pai.begin(), pai.begin() + 3);if (ResolvePai(pai, joker_count)) {return true;}}else if (pai.size() >= 2 && pai[0] == pai[1] && joker_count >= 1){--joker_count;//找到刻子牌并移除pai.erase(pai.begin(), pai.begin() + 2);if (ResolvePai(pai, joker_count)) {return true;}}else if (pai.size() >= 1 && joker_count >= 2){joker_count -= 2;//找到刻子牌并移除pai.erase(pai.begin(), pai.begin() + 1);if (ResolvePai(pai, joker_count)) {return true;}}if (Majiang_Type(pai[0]) < emMJType_Zi){auto it1 = Find_In_Sorted(pai.begin() + 1, pai.end(), pai[0] + 1);if (it1 != pai.end()){auto it2 = Find_In_Sorted(it1 + 1, pai.end(), pai[0] + 2);if (it2 != pai.end()){//找到顺序牌并移除pai.erase(it2);pai.erase(it1);pai.erase(pai.begin());if (ResolvePai(pai, joker_count))return true;}else if(joker_count >= 1){//找到顺序牌并移除--joker_count;pai.erase(it1);pai.erase(pai.begin());if (ResolvePai(pai, joker_count))return true;}}else if(joker_count >= 1){auto it2 = Find_In_Sorted(pai.begin() + 1, pai.end(), pai[0] + 2);if (it2 != pai.end()){//找到顺序牌并移除--joker_count;pai.erase(it2);pai.erase(pai.begin());if (ResolvePai(pai, joker_count))return true;}else if (joker_count >= 2){joker_count -= 2;pai.erase(pai.begin());if (ResolvePai(pai, joker_count))return true;}}}return false;
}//普通和牌类型
bool IsCommonHu(const MaJiangPai& original_pai)
{//前提:牌已经排好序,不含已碰牌和已杠牌,所以牌数应该是3n+2//过程:先找出一对将牌,然后再寻找刻子牌和顺子牌,直到剩余牌为0才表示可和牌,否则不能和牌//记录将牌位置size_t jiang_location = 0;MaJiangPai pai;while (true){auto i = jiang_location + 1;if (i >= original_pai.size()){return false;}pai = original_pai;if (jiang_location != 0){if (pai[i] == pai[jiang_location]){++i;}}//寻找将牌位置,记录将牌第二个,并擦除该两牌jiang_location = 0;for (; i < pai.size(); ++ i){if (pai[i] == pai[i - 1]){jiang_location = i;pai.erase(pai.begin() + i - 1, pai.begin() + i + 1);break;}else if (pai[i] != emMJ_Joker && pai[0] == emMJ_Joker){jiang_location = i;pai.erase(pai.begin() + i, pai.begin() + i + 1);pai.erase(pai.begin());break;}}if (jiang_location == 0){//没有将牌,不能和牌return false;}//无赖子时可直接循环拆分,有赖子时较复杂一些,需要递归拆分auto joker_end = pai.begin();while (joker_end != pai.end() && *joker_end == emMJ_Joker){++joker_end;}uint8_t joker_count = joker_end - pai.begin();if (joker_count > 0){pai.erase(pai.begin(), joker_end);if (ResolvePai(pai, joker_count)){break;}}else{//剩下的牌数是3的倍数//从左起第1张牌开始,它必须能组成刻子牌或者顺子牌才能和,否则不能和while (pai.size() >= 3){if (pai[0] == pai[2]){//找到刻子牌并移除pai.erase(pai.begin(), pai.begin() + 3);}else if (Majiang_Type(pai[0]) < emMJType_Zi){auto it1 = Find_In_Sorted(pai.begin() + 1, pai.end(), pai[0] + 1);//auto it1 = std::lower_bound(pai.begin() + 1, pai.end(), pai[0] + 1);if (it1 != pai.end()){auto it2 = Find_In_Sorted(it1 + 1, pai.end(), pai[0] + 2);//auto it2 = std::lower_bound(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;}}else{break;}}if (pai.empty()){break;}}}return true;
}std::set<emMJ> Is131Ting(const MaJiangPai& original_pai)
{std::set<emMJ> setTingPai;if (original_pai.size() != pattern131.size()){return setTingPai;}auto pai_begin = original_pai.begin();while (pai_begin != original_pai.end() && *pai_begin == emMJ_Joker){++pai_begin;}uint8_t joker_count = pai_begin - original_pai.begin();//先找将牌auto it_jiang = pai_begin + 1;while (it_jiang != original_pai.end()){if (*it_jiang == *(it_jiang-1)){break;}++it_jiang;}if (it_jiang == original_pai.end()){//没找到将牌,则如果是十三幺就胡13张auto it1 = pai_begin;auto it2 = pattern131.begin();while (it1 != original_pai.end() && it2 != pattern131.end()){if (*it1 != *it2) {if (joker_count == 0){return setTingPai;}--joker_count;++it2;continue;}++it1;++it2;}for (const auto& ting : pattern131){setTingPai.insert(ting);}return setTingPai;}//找到将牌,则如果是十三幺就只能赖子个数加一张auto pai = original_pai;pai.erase(pai.begin() + (it_jiang - original_pai.begin()));auto it1 = pai.cbegin() + joker_count;auto it2 = pattern131.cbegin();while(it1 != pai.cend() && it2 != pattern131.cend()){if (*it1 != *it2){if (setTingPai.size() > joker_count){setTingPai.clear();break;}setTingPai.insert(*it2);++it2;continue;}++it1;++it2;}if (it1 == pai.cend() && it2 != pattern131.cend()){setTingPai.insert(*it2);}return setTingPai;
}std::set<emMJ> Is7pairsTing(const MaJiangPai& original_pai)
{std::set<emMJ> setTingPai;if (original_pai.size() == 13){auto pai_begin = original_pai.begin();while (pai_begin != original_pai.end() && *pai_begin == emMJ_Joker){++pai_begin;}uint8_t joker_count = pai_begin - original_pai.begin();for (; pai_begin != original_pai.end(); ++pai_begin){if (pai_begin + 1 != original_pai.end() && *pai_begin == *(pai_begin + 1)){++pai_begin;}else if(setTingPai.size() > joker_count){//还有没成对的牌时,如果之前没配对的牌数已经超过赖子数,则组不成小七对setTingPai.clear();break;}else{//不相等时,以赖子抵setTingPai.insert(*pai_begin);}}if (pai_begin == original_pai.end() && setTingPai.size() < joker_count){//匹配完成后,如果还有剩余赖子,则可以匹配任何牌,即整幅麻将都可以和setTingPai = all_majiang_types;}}return setTingPai;
}std::set<emMJ> CheckTing(const MaJiangPai& pai)
{std::set<emMJ> ting_pai;if (pai.size() == 13){auto ting_pai = Is131Ting(pai);if (!ting_pai.empty()){//三十幺牌型与其它牌型不兼容,直接返回return ting_pai;}ting_pai = Is7pairsTing(pai);//小七对牌型与普通牌型兼容,即可能和小七对,也可能普通和。}//赖子个数:赖子牌编码最小,在排好序的队列前面auto joker_end = pai.cbegin();while (joker_end != pai.cend() && *joker_end == emMJ_Joker){++joker_end;}uint8_t jocker_count = joker_end - pai.cbegin();for (auto i : all_majiang_types){//没有赖子时才过滤,有赖子的时候不能过滤,因为赖子单调的时候是和所有牌if(jocker_count  == 0){if (pai.front() - i > 1 || i - pai.back() > 1){continue;}if (Majiang_Type(i) >= emMJType_Zi){//字牌必须有相同的才可能和if (!std::binary_search(pai.cbegin(), pai.cend(), i)) {continue;}}else{auto it = std::find_if(pai.cbegin(), pai.cend(), [&i,&jocker_count](const char& c) {//万筒条必须满足牌的数字相邻才有可能和return abs(c - i) <= 1;});if (it == pai.cend()) {continue;}}}auto temp(pai);auto range = std::equal_range(temp.begin(), temp.end(), i);if (std::distance(range.first, range.second) == 4) {//如果已经有四张牌了,不能算听牌continue;}temp.insert(range.second, i);if (IsCommonHu(temp)){ting_pai.insert(i);}}return ting_pai;
}int main()
{MaJiangPai v = {emMJ_Joker,emMJ_1Wan, emMJ_1Wan, emMJ_2Wan, emMJ_2Wan, emMJ_3Wan, emMJ_3Wan,emMJ_4Wan, emMJ_4Wan, emMJ_5Wan, emMJ_5Wan, emMJ_6Wan, emMJ_6Wan};auto ting = CheckTing(v);for(auto i : ting){std::cout<<(int) i <<std::endl;}return 0;
}

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

  1. 带赖子的麻将胡牌算法Java_带赖子的超高效麻将、跑胡子胡牌算法

    文档 github地址 https://github.com/yuanfengyun/qipai/tree/master/doc lua版 https://github.com/yuanfengyun ...

  2. 带赖子的麻将胡牌算法Java_有人讨论下麻将胡牌,出牌算法吗,求思路

    前段时间学会了打麻将,觉得老祖宗的智慧真的博大精深,很好玩,食胡的时候真兴奋啊,于是空余时间就想自己写个麻将游戏出来,模仿欢乐麻将那种,数学差,想了两个礼拜才想出一个胡牌算法,前段时间学会了打麻将. ...

  3. 麻将听牌的算法(java)

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

  4. java判断麻将听牌,和牌看听:麻将听牌种类大全

    原标题:和牌看听:麻将听牌种类大全 听牌(叫胡)的类型 一.听一张(3种) 1.听边张(听尖张) 一二----三 2.听嵌张(坎张) 二四----三 3.单钓将 中----中 二.听两张(5种) 1. ...

  5. ~ 如何用C++自制一个日麻游戏 ~(二)听牌判断算法 § 1 判断听牌(附带C#实现)

    导入 -- 什么环节只要用算法判断一次,就能知道是否听牌立直.还差什么牌就可以荣和自摸? -- 只要在缺一张手牌(如1.4.7.10.13张时)的情况下判断是否听牌.听哪些牌,就可以为上面的复杂判断提 ...

  6. 带赖子的超高效麻将、跑胡子胡牌算法

    腾讯课堂视频讲解:https://ke.qq.com/course/305608?tuin=104cb0e2 文档 github地址 https://github.com/yuanfengyun/qi ...

  7. 分享一个麻将听牌算法(带癞子)(Lua)

    算法已验证,目前没有发现问题,还有待优化,请指正(没有考虑东南西北中发白): M.CardTb = {{0,0,0,0,0,0,0,0,0}, --筒子{0,0,0,0,0,0,0,0,0}, --万 ...

  8. 跑胡子c语言算法,跑胡子胡牌算法Java版(带赖子、基于回溯算法)

    跑胡子规则 跑胡子,小写"一"到"十"各4张共40张,大写"壹"到"拾"各4张共40张. 砌牌:跑胡子为3人同玩,庄家砌 ...

  9. 跑胡子胡牌算法(带赖子、基于回溯算法)

    跑胡子规则 跑胡子,小写"一"到"十"各4张共40张,大写"壹"到"拾"各4张共40张. 砌牌:跑胡子为3人同玩,庄家砌 ...

最新文章

  1. 开发人员改变世界的初心
  2. jfinal集成使用shiro
  3. 最不安全的网络管理员大盘点
  4. Windows 蠕虫首次现身 Mac 电脑:伪装成 Adobe Flash 软件
  5. 字节输入流一次读取多个字节
  6. Linux虚拟文件系统(VFS)
  7. inputstream是否一定要close_汽车加装行李架后,总被交警拦下,类似改装,是否一定要备案...
  8. adobe怎么统计字数_SEO技能:怎么写站内文章对网站排名更好?
  9. 源代码电影涉及的计算机思想,关于电影源代码的结局解释
  10. mysql事件执行记录表_MySQL事件异常记录
  11. git 如何忽略掉文件夹_如何使git忽略某些文件或文件夹
  12. 强者不能恒强,偏向虎山行
  13. 手把手教你免费申请支持通配符的 SSL 证书
  14. PowerDesigner16.5下载
  15. java毕业设计彩妆销售网站Mybatis+系统+数据库+调试部署
  16. 主力吸筹的两种模式以及躺赢股市的三种票
  17. MSDC 4.3 接口规范(15)
  18. 使用Sivarc使PLC程序标准化
  19. 电子合同渐成主流,君子签以区块链技术打造合同签约新生态
  20. 典型2R机械臂结构分析 2R-manipulator Geometric Modeling

热门文章

  1. 2022年全国职业院校技能大赛赛项比赛时间、承办校信息统计表(第二批)
  2. 你眼中的未来,是我们回不去的曾经
  3. 双系统安装Windowslinux
  4. vscode 前端快捷键
  5. 大数据时代互联网创业者的蓝海战略思维
  6. 屠榜目标跟踪!SwinTrack:Transformer跟踪的简单而强大的基线
  7. 山东计算机春考大专学校排名,2020年山东十大专科学校排名 山东比较好
  8. TikTok新规:严禁录播盗播,保护原创内容
  9. Photoshop-avi-gif-合集
  10. quartus II工具In-System Memory Content Editor使用方法