Cocos2d-x制作《单机斗地主》源码解剖6:对电脑玩家手中的牌进行分拆
在电脑玩家进行跟牌和出牌操作之前,需要按照游戏规则对其手中的牌进行拆分。具体拆分顺序依据文档需求(见“斗地主规则”),依次为:炸弹、飞机、连对、连牌、三带、对子和单张。以下是详细的实现过程和代码分析。
1. 牌型初步分析与拆分
首先,我们要分析出电脑玩家手中牌的基本类型,如四张(炸弹)、三张、两张和一张。以下是实现该功能的代码:
void GameScene::FenChaiNpcPai(Player* npc) {
/************************************************************************/
/* 1.首先分析出来牌的类型(如:四张,三张,两张,一张) */
/************************************************************************/
std::vector<PaiXing> vec;
PaiXing xing;
CCArray* arr = CCArray::create(); // 临时数组
arr->addObjectsFromArray(npc->getArrPk());
// 提取双鬼
Poker* pk = (Poker *)arr->objectAtIndex(0);
Poker* pk1 = (Poker *)arr->objectAtIndex(1);
if (pk->getHuaSe() == Gui && pk1->getHuaSe() == Gui) {
xing.type = BOMB_CARD;
xing.vec.push_back(pk);
xing.vec.push_back(pk1);
arr->removeObject(pk);
arr->removeObject(pk1);
vec.push_back(xing);
}
// 分析牌型
for (int i = 0; i < arr->count();) {
pk = (Poker*)arr->objectAtIndex(i);
xing.vec.clear(); // 清除数组
// 找出与pk相同的牌
for (int j = i; j < arr->count(); ++j) {
pk1 = (Poker*)arr->objectAtIndex(j);
if (pk->getNum() == pk1->getNum()) {
++i;
xing.vec.push_back(pk1);
} else {
break;
}
}
if (xing.vec.size() == 4)
xing.type = BOMB_CARD;
if (xing.vec.size() == 3)
xing.type = THREE_CARD;
if (xing.vec.size() == 2)
xing.type = DOUBLE_CARD;
if (xing.vec.size() == 1)
xing.type = SINGLE_CARD;
vec.push_back(xing);
}
/************************************************************************/
/* 2.按优先级(先分析炸弹---飞机---连对---连牌--三带,对子,单张)提取牌型并保存用于出牌或跟牌 */
/************************************************************************/
// 提取炸弹
for (std::vector<PaiXing>::iterator iter = vec.begin(); iter != vec.end();) {
if (iter->type == BOMB_CARD) {
xing.type = BOMB_CARD;
xing.vec.clear();
xing.vec = iter->vec;
npc->m_vecPX.push_back(xing); // 把牌型保存到用户数组中
iter = vec.erase(iter);
} else {
++iter;
}
}
// 提取飞机
TiQuFeiJi(npc, THREE_CARD, vec);
// 提取连对
TiQuLianDui(npc, vec);
// 提取连牌
TiQuLianPai(npc, vec);
// 剩余的是三带,对子,单张 全部加入npc牌型中
for (std::vector<PaiXing>::iterator iter = vec.begin(); iter != vec.end();) {
npc->m_vecPX.push_back(*iter);
iter = vec.erase(iter);
}
// 排序
stable_sort(npc->m_vecPX.begin(), npc->m_vecPX.end(), isShorter1);
}
上述代码的主要步骤如下:
- 初始化临时数组:创建一个临时的
CCArray数组arr,并将电脑玩家手中的牌复制到该数组中。 - 提取双鬼:检查数组的前两张牌是否为双鬼,如果是,则将其标记为炸弹类型,并从临时数组中移除。
- 分析牌型:遍历临时数组,找出相同数字的牌,并根据牌的数量确定其类型(炸弹、三张、两张或一张)。
- 按优先级提取牌型:依次提取炸弹、飞机、连对和连牌,并将其保存到
npc->m_vecPX数组中。 - 处理剩余牌型:将剩余的三带、对子和单张牌加入到
npc->m_vecPX数组中。 - 排序:对
npc->m_vecPX数组中的牌型按值从小到大进行排序。
2. 提取飞机牌型
以下是提取飞机牌型的代码:
void GameScene::TiQuFeiJi(Player* npc, CARD_TYPE type, std::vector<PaiXing> &vec) {
Poker * pk = NULL;
PaiXing xing;
for (std::vector<PaiXing>::iterator iter = vec.begin(); iter != vec.end();) {
if (pk == NULL && iter + 1 == vec.end())
break;
if (pk == NULL && iter->type == type && (iter + 1)->type == type) {
Poker* pk1 = iter->vec.front();
Poker* pk2 = (iter + 1)->vec.front();
if (pk1->getNum() - 1 == pk2->getNum()) {
pk = pk2;
xing.type = AIRCRAFT_CARD;
xing.vec.clear();
xing.vec = iter->vec;
iter = vec.erase(iter);
xing.vec.insert(xing.vec.end(), iter->vec.begin(), iter->vec.end());
iter = vec.erase(iter);
}
}
if (pk != NULL) {
if (iter == vec.end()) {
npc->m_vecPX.push_back(xing);
break;
}
Poker* pk1 = iter->vec.front();
if (iter->type == type && pk->getNum() - 1 == pk1->getNum()) {
pk = pk1;
xing.vec.insert(xing.vec.end(), iter->vec.begin(), iter->vec.end());
iter = vec.erase(iter);
if (iter == vec.end()) {
npc->m_vecPX.push_back(xing);
break;
}
} else {
npc->m_vecPX.push_back(xing);
pk = NULL;
}
} else {
++iter;
}
}
}
该函数的主要逻辑是遍历 vec 数组,查找相邻的三张牌(类型为 type),如果它们的数字连续,则将其组合成飞机牌型,并从 vec 数组中移除。
3. 提取连对牌型
提取连对牌型的代码如下:
void GameScene::TiQuLianDui(Player* npc, std::vector<PaiXing> &vec) {
std::vector<PaiXing> vecTem; // 临时数组
std::vector<PaiXing> vecFan; // 存放要重新返还vec里的牌
Poker* pk = NULL;
for (std::vector<PaiXing>::iterator iter = vec.begin(); iter != vec.end();) {
// 将相连的牌加入临时数组中
Poker* pk1 = iter->vec.front();
if ((iter->type == THREE_CARD || iter->type == DOUBLE_CARD) && (pk == NULL || (pk->getNum() - 1 == pk1->getNum() && pk->getNum() < Er))) {
pk = pk1;
vecTem.push_back(*iter);
iter = vec.erase(iter);
} else {
if (pk == NULL)
++iter;
pk = NULL;
if (vecTem.size() >= 3) {
PaiXing xing;
xing.type = COMPANY_CARD;
for (int i = 0; i < vecTem.size(); ++i) {
if (vecTem[i].type == THREE_CARD) {
// 将多余的一张保存返回数组vecFan中
PaiXing xing1;
xing1.type = SINGLE_CARD;
xing1.vec.push_back(vecTem[i].vec.back());
vecTem[i].vec.pop_back();
vecFan.push_back(xing1);
// 将剩余两张保存xing中
xing.vec.insert(xing.vec.end(), vecTem[i].vec.begin(), vecTem[i].vec.end());
}
if (vecTem[i].type == DOUBLE_CARD) {
xing.vec.insert(xing.vec.end(), vecTem[i].vec.begin(), vecTem[i].vec.end());
}
}
vecTem.clear();
npc->m_vecPX.push_back(xing);
} else if (!vecTem.empty()) {
vecFan.insert(vecFan.end(), vecTem.begin(), vecTem.end());
vecTem.clear();
}
}
}
if (!vecTem.empty()) {
if (vecTem.size() >= 3) {
PaiXing xing;
xing.type = COMPANY_CARD;
for (int i = 0; i < vecTem.size(); ++i) {
if (vecTem[i].type == THREE_CARD) {
// 将多余的一张保存返回数组vecFan中
PaiXing xing1;
xing1.type = SINGLE_CARD;
xing1.vec.push_back(vecTem[i].vec.back());
vecTem[i].vec.pop_back();
vecFan.push_back(xing1);
// 将剩余两张保存xing中
xing.vec.insert(xing.vec.end(), vecTem[i].vec.begin(), vecTem[i].vec.end());
}
if (vecTem[i].type == DOUBLE_CARD) {
xing.vec.insert(xing.vec.end(), vecTem[i].vec.begin(), vecTem[i].vec.end());
}
}
vecTem.clear();
npc->m_vecPX.push_back(xing);
} else if (!vecTem.empty()) {
vecFan.insert(vecFan.end(), vecTem.begin(), vecTem.end());
vecTem.clear();
}
}
// 将vecFan返回到vec数组中并从大到小排序
if (!vecFan.empty()) {
vec.insert(vec.end(), vecFan.begin(), vecFan.end());
stable_sort(vec.begin(), vec.end(), isDaDaoXiao);
}
}
该函数的主要步骤如下:
- 创建临时数组:创建两个临时数组
vecTem和vecFan,分别用于存储相连的牌和需要返还到vec数组中的牌。 - 查找相连的牌:遍历
vec数组,将相邻的三张或两张牌加入到vecTem数组中。 - 处理连对牌型:如果
vecTem数组中的牌数量不少于 3 组,则将其组合成连对牌型,并将多余的牌保存到vecFan数组中。 - 处理剩余牌:如果
vecTem数组中的牌数量不足 3 组,则将其全部加入到vecFan数组中。 - 返还并排序:将
vecFan数组中的牌返回到vec数组中,并按从大到小的顺序进行排序。
4. 提取连牌牌型
以下是提取连牌牌型的代码:
void GameScene::TiQuLianPai(Player* npc, std::vector<PaiXing> &vec) {
std::vector<PaiXing> vecTem; // 临时数组
std::vector<PaiXing> vecFan; // 存放要重新返还vec里的牌
Poker* pk = NULL;
for (std::vector<PaiXing>::iterator iter = vec.begin(); iter != vec.end();) {
// 将相连的牌加入临时数组中
Poker* pk1 = iter->vec.front();
if ((iter->type == THREE_CARD || iter->type == DOUBLE_CARD || iter->type == SINGLE_CARD) && (pk == NULL || (pk->getNum() - 1 == pk1->getNum() && pk->getNum() < Er))) {
pk = pk1;
vecTem.push_back(*iter);
iter = vec.erase(iter);
} else {
if (pk == NULL)
++iter;
pk = NULL;
if (vecTem.size() >= 5) {
PaiXing xing;
xing.type = CONNECT_CARD;
for (int i = 0; i < vecTem.size(); ++i) {
if (vecTem[i].type == THREE_CARD) {
// 将多余的两张保存返回数组vecFan中
PaiXing xing1;
xing1.type = DOUBLE_CARD;
xing1.vec.push_back(vecTem[i].vec.back());
vecTem[i].vec.pop_back();
xing1.vec.push_back(vecTem[i].vec.back());
vecTem[i].vec.pop_back();
vecFan.push_back(xing1);
// 将剩余一张保存xing中
xing.vec.insert(xing.vec.end(), vecTem[i].vec.begin(), vecTem[i].vec.end());
}
if (vecTem[i].type == DOUBLE_CARD) {
// 将多余的一张保存返回数组vecFan中
PaiXing xing1;
xing1.type = SINGLE_CARD;
xing1.vec.push_back(vecTem[i].vec.back());
vecTem[i].vec.pop_back();
vecFan.push_back(xing1);
// 将剩余一张保存xing中
xing.vec.insert(xing.vec.end(), vecTem[i].vec.begin(), vecTem[i].vec.end());
}
if (vecTem[i].type == SINGLE_CARD)
xing.vec.push_back(vecTem[i].vec.front());
}
vecTem.clear();
npc->m_vecPX.push_back(xing);
} else if (!vecTem.empty()) {
vecFan.insert(vecFan.end(), vecTem.begin(), vecTem.end());
vecTem.clear();
}
}
}
if (!vecTem.empty()) {
if (vecTem.size() >= 5) {
PaiXing xing;
xing.type = CONNECT_CARD;
for (int i = 0; i < vecTem.size(); ++i) {
if (vecTem[i].type == THREE_CARD) {
// 将多余的两张保存返回数组vecFan中
PaiXing xing1;
xing1.type = DOUBLE_CARD;
xing1.vec.push_back(vecTem[i].vec.back());
vecTem[i].vec.pop_back();
xing1.vec.push_back(vecTem[i].vec.back());
vecTem[i].vec.pop_back();
vecFan.push_back(xing1);
// 将剩余一张保存xing中
xing.vec.insert(xing.vec.end(), vecTem[i].vec.begin(), vecTem[i].vec.end());
}
if (vecTem[i].type == DOUBLE_CARD) {
// 将多余的一张保存返回数组vecFan中
PaiXing xing1;
xing1.type = SINGLE_CARD;
xing1.vec.push_back(vecTem[i].vec.back());
vecTem[i].vec.pop_back();
vecFan.push_back(xing1);
// 将剩余一张保存xing中
xing.vec.insert(xing.vec.end(), vecTem[i].vec.begin(), vecTem[i].vec.end());
}
if (vecTem[i].type == SINGLE_CARD)
xing.vec.push_back(vecTem[i].vec.front());
}
vecTem.clear();
npc->m_vecPX.push_back(xing);
} else if (!vecTem.empty()) {
vecFan.insert(vecFan.end(), vecTem.begin(), vecTem.end());
vecTem.clear();
}
}
// 将vecFan返回到vec数组中并从大到小排序
if (!vecFan.empty()) {
vec.insert(vec.end(), vecFan.begin(), vecFan.end());
stable_sort(vec.begin(), vec.end(), isShorter1);
}
}
该函数的实现逻辑与提取连对牌型的函数类似,主要区别在于可以处理单张牌,并且连牌的数量要求不少于 5 张。
通过以上步骤,我们完成了对电脑玩家手中牌的分拆和排序,为后续的跟牌和出牌操作做好了准备。