一、平胡简介
麻将平胡牌(无财神):N(0、1、2、3、4)个三牌和1个对子,手牌总数为 N * 3 + 2。
二、平胡计算
1、先检测手牌数量是否符合要求。
2、获得可能的三牌集(三连[1, 2, 3]、三同[1, 1, 1])和对子集(对子[1, 1])。
3、使用组合函数,获得所有可能的三牌组合集,每个组合包括N个三牌。
4、若无三牌组合,则是单调平胡,则直接遍历对子集,若有对子在手牌中数量足够,则可胡牌。
5、若有三牌组合,遍历三牌组合集,若三牌组合集中所有的三牌都在手牌中数量足够,再遍历对子集,若有对子在手牌中数量足够,则可胡牌。
三、测试函数
测试函数中的arr的下标为麻将牌唯一标志,值为该麻将牌数量。
#define LEN 34 // 万(0 - 8)、条(9 - 17)、筒(18 - 26)、东南西北中发白(27 - 33)
enum GROUP_TYPE
{
GROUP_GANG,
GROUP_LIAN,
GROUP_PENG,
GROUP_PAIR
};
struct CardGroup
{
int card;
GROUP_TYPE type;
};
int main(int argc, char *argv[])
{
int arr[LEN] = {0};
arr[0] = 2;
finishCard(arr, 0);
arr[0] = 2;
arr[1] = 3;
finishCard(arr, 1);
arr[0] = 2;
arr[1] = 3;
arr[9] = 1;
arr[10] = 1;
arr[11] = 1;
finishCard(arr, 2);
arr[0] = 2;
arr[1] = 3;
arr[8] = 3;
arr[9] = 1;
arr[10] = 1;
arr[11] = 1;
finishCard(arr, 3);
arr[0] = 2;
arr[1] = 3;
arr[8] = 3;
arr[9] = 1;
arr[10] = 1;
arr[11] = 1;
arr[30] = 3;
finishCard(arr, 4);
return 0;
}
四、胡牌牌数检测
bool checkCardCount(int cardData[LEN], int groupSize)
{
// 三牌组合最多只有4种
if (groupSize < 0 || groupSize > 4) return false;
int cardCount = 0;
for (int i = 0; i < LEN; i++)
{
// 每一种牌最多只能有4张
if (cardData[i] < 0 || cardData[i] > 4) return false;
cardCount += cardData[i];
}
// 牌总数检测
if (cardCount != (groupSize * 3) + 2) return false;
return true;
}
五、获得可能的三牌集和对子集
void getCardGroupsAndPairs(int cardData[LEN], vector<CardGroup> &cardGroups, vector<CardGroup> &cardPairs)
{
for (int i = 0; i < LEN; i++)
{
if (cardData[i] <= 0) continue;
CardGroup cardGroup;
cardGroup.card = i;
// 三连
if ((i >= 0 && i <= 6) || // 一万 - 七万
(i >= 9 && i <= 15) || // 一条 - 七条
(i >= 18 && i <= 24)) // 一筒 - 七筒
{
if (cardData[i] > 0 && cardData[i + 1] > 0 && cardData[i + 2] > 0)
{
cardGroup.type = GROUP_LIAN;
cardGroups.push_back(cardGroup);
}
}
// 三同
if (cardData[i] >= 3)
{
cardGroup.type = GROUP_PENG;
cardGroups.push_back(cardGroup);
}
// 对子
if (cardData[i] >= 2)
{
cardGroup.type = GROUP_PAIR;
cardPairs.push_back(cardGroup);
}
}
}
六、组合函数
七、胡牌分析
最后打印出所有胡牌的牌型
void finishCard(int cardData[LEN], int groupSize)
{
// 胡牌牌数检测
if (!checkCardCount(cardData, groupSize)) return;
// 获取所有的三牌、对子
vector<CardGroup> cardGroups; // 三牌(三连、三同)
vector<CardGroup> cardPairs; // 对子
getCardGroupsAndPairs(cardData, cardGroups, cardPairs);
// 获得组合集 groups (使用组合函数),每个组合元素个数为 groupSize
vector<vector<int>> groups;
combination(cardGroups.size(), groupSize, groups);
// 获得可以胡牌的组合集 groupsOk(三牌和对子都符合要求)
vector<vector<int>> groupsOk;
int t[LEN] = {0};
// 若无三牌,对子是否符合要求
if (groups.size() <= 0)
{
vector<int> group;
memcpy(t, cardData, sizeof(t));
for (int i = 0; i < cardPairs.size(); i++)
{
CardGroup &cg = cardPairs[i];
if (t[cg.card] >= 2)
{
group.push_back(i);
groupsOk.push_back(group);
group.pop_back();
}
}
}
// 若有三牌,三牌和对子是否符合要求
for (int i = 0; i < groups.size(); i++)
{
bool isOk = true;
vector<int> &group = groups[i];
memcpy(t, cardData, sizeof(t));
// 三牌是否符合要求
for (int j = 0; j < group.size(); j++)
{
int cardGroupIndex = group[j];
CardGroup &cg = cardGroups[cardGroupIndex];
if (cg.type == GROUP_PENG) // 三同
{
t[cg.card] -= 3;
if (t[cg.card] < 0) isOk = false;
}
else if (cg.type == GROUP_LIAN) // 三连
{
if (--t[cg.card] < 0 || --t[cg.card + 1] < 0 || --t[cg.card + 2] < 0) isOk = false;
}
else
{
isOk = false;
}
if (!isOk) break;
}
// 对子是否符合要求
if (isOk)
{
for (int i = 0; i < cardPairs.size(); i++)
{
CardGroup &cg = cardPairs[i];
if (t[cg.card] >= 2)
{
group.push_back(i);
groupsOk.push_back(group);
group.pop_back();
}
}
}
}
// 打印胡牌结果
cout << "success size: " << groupsOk.size() << endl;
for (int i = 0; i < groupsOk.size(); i++)
{
vector<int> &groupOk = groupsOk[i];
for (int j = 0; j < groupOk.size(); j++)
{
CardGroup &cg = (j == groupOk.size() - 1) ? cardPairs[groupOk[j]] : cardGroups[groupOk[j]];
if (cg.type == GROUP_LIAN)
{
cout << "(" << cg.card << ", " << cg.card + 1 << ", " << cg.card + 2 << ") ";
}
else if (cg.type == GROUP_PENG)
{
cout << "(" << cg.card << ", " << cg.card << ", " << cg.card << ") ";
}
else if (cg.type == GROUP_PAIR)
{
cout << "(" << cg.card << ", " << cg.card << ") ";
}
}
cout << "\n-----------------------------" << endl;
}
}