麻将Ai的出牌逻辑

来源:互联网 发布:借贷软件排行 编辑:程序博客网 时间:2024/06/11 22:13

写了一个测试版的麻将游戏,涉及机器人出牌和胡牌这些东西,想做个笔记,记录一下。

  • 机器人出牌
  • 分析牌型
  • 主要逻辑源码
  • 附图

机器人出牌

机器人的基础AI有三个基础准则:

  1. 有杠先杠
  2. 有碰就碰
  3. 有番打番

梳理下大概的出牌逻辑框架

一共分为两大步骤

  1. 分析牌型

    这里主要做的是,把同花色中不满足条件的挑选出来,放入一个数组里。

  2. 出牌分析

    根据已有的可打的数组牌,根据场上的情况,做一些调整,然后打出相对比较好的牌。

分析牌型

  1. 隔两张的单牌 (112 5 8899)

  2. 四连顺 - 孤 (0 2345 0)

  3. 隔一张的单排 (123 5 78)

  4. 顺子的余牌 (1123,5667)

  5. (112,334,566)

  6. 返回所有非刻非顺的牌

主要逻辑源码

do {        // 有杠先杠        {            for (unsigned char i = 0, s = 0; i < MAX_INDEX; ++i) {                s = pmj->index(i);                if (s == 4) {                    // 检测若在听牌的情况下,如果杠了 是否还能听牌, 杠了是不是牌型会变的更差                    int i = 0;                    *out_card = SRAnalysis::switchToCardData(s);                    *out_card_count = 4;                    return WIK_GANG;                }            }        }        // 有单独的风牌则打风牌        {            std::vector<int> vec_index;            for (int idx = 0; (idx = pmj->getFanPaiOne(idx)) != -1; ++idx) {                vec_index.push_back(idx);            }            if (!vec_index.empty()) {                int i = rand() % vec_index.size();                int temp = vec_index.at(i);                // 快速切牌判断                for (int i = 0; i < 4; ++i) {                    if (mahjong_[i] == nullptr && i == (int)direction_)                        continue;                    // 风有好几张,先打别人能碰的,以达到快速切牌的效果                    for (auto idx : vec_index) {                        if (2 <= mahjong_[i]->have(SRAnalysis::switchToCardData(idx))) {                            temp = idx;                        }                    }                }                *out_card = SRAnalysis::switchToCardData(temp);                *out_card_count = 1;                break;            }        }        // 分析花牌        {            // 获得牌型            unsigned char card_index[MAX_INDEX] = {};            for (int i = 0; i < MAX_INDEX; ++i)                card_index[i] = pmj->index(i);            // 开始分析            {                std::vector<int> vec_index;                // 牌型解析                {                    int huapai_weight = 0;                    for (int i = 0; i < 3; ++i) {                        // 存放下标的数组                        unsigned char temp_card_index[MAX_COUNT] = {};                        char temp_count = 0;                        // 分析当前区间的花牌                        int temp_weight = SRAnalysis::analysisHuaPai(&card_index[i * 9], &card_index[i * 9 + 9],                            temp_card_index, &temp_count);                        // 先分析最高出牌权重的麻将                        if (temp_weight > huapai_weight) {                            huapai_weight = temp_weight;                            vec_index.clear();                        }                        // 将花牌的下标存入容器                        for (int n = 0; n < temp_count; ++n)                            vec_index.push_back((temp_card_index[n] + i * 9));                    }                }                // 分析最佳出牌                {                    if (!vec_index.empty()) {                        int temp_i = rand() % vec_index.size();                        int temp_index = vec_index.at(temp_i);                        // 转移连牌 如 12  34 此类                        std::vector<int> vec_shun_index;                        {                            for (int i = 0; i < vec_index.size(); ++i) {                                // 若下标[i+1] 与 下标 [i] 相邻,则转移                                if (i != 0 &&                                     vec_index.at(i - 1) + 1 == vec_index.at(i)                                    && (vec_index.at(i - 1) % 9 > vec_index.at(i) % 9)) {                                    vec_shun_index.push_back(vec_index.at(i));                                    vec_shun_index.push_back(vec_index.at(i - 1));                                    auto iter = vec_index.begin();                                    iter += (i - 1);                                    iter = vec_index.erase(iter);                                    vec_index.erase(iter);                                }                            }                            // 默认出 12  34 顺牌                            if (!vec_shun_index.empty()) {                                temp_index = vec_shun_index.at(rand() % vec_shun_index.size());                            }                        }                        // 转移将牌                        std::vector<int> vec_double_index;                        {                            for (auto iter = vec_index.begin();                                iter != vec_index.end();) {                                if (2 <= pmj->index(*iter)) {                                    vec_double_index.push_back(*iter);                                    iter = vec_index.erase(iter);                                }                                else {                                    ++iter;                                }                            }                            // 若将牌不是唯一且,没有散顺可出,则默认出将牌                            if (vec_double_index.size() > 1 && vec_shun_index.empty())                                temp_index = vec_double_index.at(                                    rand() % vec_double_index.size());                        }                        // 如果还有单牌,则默认出单牌                        if (!vec_index.empty()) {                            temp_index = vec_index.at(                                rand() % vec_index.size());                        }                        // 判断快速切牌                        if (roundNumber_ <= 6 && roundNumber_ >= 0) {                            // 快速切牌判断                            int sign_index = 0;                            for (int i = (int)enDirection::South;                                i <= (int)enDirection::East; ++i) {                                if (mahjong_[i] == nullptr || i == (int)direction_)                                    continue;                                // 查看玩家拥有的牌型                                for (auto idx : vec_index) {                                    if (2 == mahjong_[i]->have(                                    SRAnalysis::switchToCardData(idx))) {                                        sign_index = idx;                                    }                                }                            }                            // 判断是否有附和切牌标准的牌型                            if (sign_index != 0 && temp_index != sign_index) {                                (*out_card) = SRAnalysis::switchToCardData(sign_index);                                (*out_card_count) = 1;                                break;                            }                        } // end 快速切牌判断结束                          // 遍历检查幺九牌型                        for (auto x : vec_index) {                            const int& temp = x % 9;                            if (temp <= 1 || temp >= 7) {                                temp_index = x;                                break;                            }                        }                        (*out_card) = SRAnalysis::switchToCardData(temp_index);                        (*out_card_count) = 1;                        break;                    }                }            }        }        return -1;    } while (false);

附图

这里写图片描述

原创粉丝点击