Cocos2d-x制作《单机斗地主》源码解剖6:对电脑玩家手中的牌进行分拆

2015年03月24日 08:54 0 点赞 0 评论 更新于 2025-11-21 18:21

在电脑玩家进行跟牌和出牌操作之前,需要按照游戏规则对其手中的牌进行拆分。具体拆分顺序依据文档需求(见“斗地主规则”),依次为:炸弹、飞机、连对、连牌、三带、对子和单张。以下是详细的实现过程和代码分析。

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);
}

上述代码的主要步骤如下:

  1. 初始化临时数组:创建一个临时的 CCArray 数组 arr,并将电脑玩家手中的牌复制到该数组中。
  2. 提取双鬼:检查数组的前两张牌是否为双鬼,如果是,则将其标记为炸弹类型,并从临时数组中移除。
  3. 分析牌型:遍历临时数组,找出相同数字的牌,并根据牌的数量确定其类型(炸弹、三张、两张或一张)。
  4. 按优先级提取牌型:依次提取炸弹、飞机、连对和连牌,并将其保存到 npc->m_vecPX 数组中。
  5. 处理剩余牌型:将剩余的三带、对子和单张牌加入到 npc->m_vecPX 数组中。
  6. 排序:对 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);
}
}

该函数的主要步骤如下:

  1. 创建临时数组:创建两个临时数组 vecTemvecFan,分别用于存储相连的牌和需要返还到 vec 数组中的牌。
  2. 查找相连的牌:遍历 vec 数组,将相邻的三张或两张牌加入到 vecTem 数组中。
  3. 处理连对牌型:如果 vecTem 数组中的牌数量不少于 3 组,则将其组合成连对牌型,并将多余的牌保存到 vecFan 数组中。
  4. 处理剩余牌:如果 vecTem 数组中的牌数量不足 3 组,则将其全部加入到 vecFan 数组中。
  5. 返还并排序:将 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 张。

通过以上步骤,我们完成了对电脑玩家手中牌的分拆和排序,为后续的跟牌和出牌操作做好了准备。

作者信息

boke

boke

共发布了 3994 篇文章