C++带赖子的麻将听牌检测算法实现
2017-09-07 09:51
531 查看
#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 = 1 emMJ_NanFeng = MJ(4, 2),//南 2 % 4 = 2 emMJ_XiFeng = MJ(4, 3),//西 3 % 4 = 3 emMJ_BeiFeng = MJ(4, 4),//北 4 % 4 = 0 emMJ_HongZhong = MJ(4, 5),//中 5 % 4 = 1 emMJ_FaCai = MJ(4, 6),//发 6 % 4 = 2 emMJ_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++和OpenCv的SIFT_图像局部特征检测算法代码的实现
- C++实现麻将基本听牌胡牌的算法
- C++实现图算法(二)
- 二分查找算法的C/C++实现
- DSP实现快速Hough变换圆检测算法
- Canny边缘检测算法原理及其VC实现详解(二)
- 求两个整数的最小公倍数和最大公约数的算法及其C++实现
- 基于LRU算法的连接检测实现
- Atitit 图像清晰度 模糊度 检测 识别 评价算法 源码实现attilax总结
- KMP模式匹配算法 C++实现
- [C/C++] 构造最优二叉树-赫夫曼(哈夫曼、Huffman)树算法实现
- C/C++:各种基本算法实现小结(四)—— 图及其遍历
- 算法入门->冒泡排序->C/C++ 语言实现
- smo算法的c++实现
- Canny边缘检测原理与C++实现(2)实现部分
- 用c++实现DES基本算法
- 边界检测算法分析与实现
- U盘小偷——C++实现U盘插入检测和文件扫描拷贝
- 算法设计之,堆,堆排序,基于最大堆的最大优先队列的实现(C++实现)
- 算法-对分查找(二分查找)C++实现