十三张/十三水

规则介绍: 
https://baike.baidu.com/item/%E5%8D%81%E4%B8%89%E6%B0%B4/81938

十三水规则:
    二到四人对局,,一副牌,除去大王,小王,剩下总共52张牌;游戏中每人获得13张牌;
游戏规则
    每位玩家需将手上的13张牌分成头、中、尾三墩牌,头墩三张,中尾墩各五张。

普通牌型(3、5张牌)
    同花顺:同门花色中之五张连续牌。若别家也有同花顺时,比顺子大小如牌型全部相同,则平局。 在底墩,按5道计;在中墩,则记10道;
    铁支(四条):4张相同的牌。在底墩,按4道计;在中墩,则记8道;
    葫芦:由三条加一对组成。若别家也有此牌型,则比三张数字大小。 放中墩,则计3道;
    同花:同门花色中之五张杂牌。若遇相同则先比这副牌中最大的一支,如又相同再比第二支、依此类推。如牌型全部相同则平局;
    顺子:五张牌都是连续的组合,2、3、4、5、6为最小牌。 10、J、Q、K、A为最大牌,若巧遇别家也有则平局。道:第二大的牌不是9、10、J、Q、K(此为第三大)而是A、2、3、4、5;
    三条:三张相同的牌。若遇相同,则比三条部分的数字大小。 在头墩,以三尖刀计;
    三尖刀:头墩为三条。 按3道计。
    二对:牌型中五张牌由两组两张相同数字的牌所组成。若遇相同则先比这副牌中最大的一对,如又相同再比第二对、如又相同再比单张牌大小。如牌型全部相同则平局。
    对子:由两张相同数字的牌组成。若别家也相同则比剩下牌的数字大小,如牌型全部相同则平局。 第一墩是两张带一张,后面是两张带三张;
    乌龙(散牌):组不出任何牌型。只比数字大小,其比法与同花相同。若两家从大到小都一样,则平局;

后台逻辑实现思路: 
一手牌是13张,要组成头墩、中墩、尾墩,每墩可能分别是同花顺、铁枝……对子、乌龙等,要保证尾墩 >= 中墩 > 头墩。可以通过遍历所有类型,获取指定的尾墩组合后,对剩余的牌再进行组合,优先使用递归。 
伪代码:

// CCombinateCards 组合类
// ST_ONE_HAND_COMBINATION 一手牌组合
// ST_CARD_SENTENCE 一墩牌,三张或五张
typedef std::vector<ST_ONE_HAND_COMBINATION> COneHandArray;

bool CCombinateCards::CombinateSpecifiedStyle(COneHandArray &arrTraverseArray,BYTE byType)
{
    CCombinateCards clsGroup = *this;   
    if (clsGroup.m_nCount == 0)
    {
        return true;
    }

    int iStcTypeCount = 0;
    int iSentence = 0;
    int iPos = clsGroup.m_nCount / MAX_SENTENCE_LENGTH;                 // 头、中、尾
    int iLength = iPos == EN_LOCALTION_HEAD? MIN_SENTENCE_LENGTH : MAX_SENTENCE_LENGTH;
    bool bTail = clsGroup.m_nCount == MAX_CARDS_EVERYONE;

    std::vector<ST_CARD_SENTENCE> arrSentence;
    if (byType >= EN_ST_TONGHUASHUN && m_nCount >= MAX_SENTENCE_LENGTH)
    {
        int iTonghuashunCount = NormalTonghuashun(arrSentence);
        if (byType == EN_ST_TONGHUASHUN && bTail && iTonghuashunCount == 0)
        {
            return false;
        }
        for (;iSentence < iTonghuashunCount;++iSentence)    // 如果有同花顺,则将该顺子从手牌中删除,拼凑头和中
        {
            CCombinateCards clsGroupTmp = clsGroup;
            if (!clsGroupTmp.DeleteSentence(arrSentence[iSentence],iLength))        // 删除该句子,获得剩余的牌
            {
                return false;
            }

            if (bTail)                      // 如果是尾墩,应该新建一个 ST_ONE_HAND_CARD ,如果是中或头,则往 arrTraverseArray 的最后一个 ST_ONE_HAND_CARD 中添加
            {
                ST_ONE_HAND_COMBINATION stOneHand;
                stOneHand.SetLocationCard(EN_LOCALTION_TAIL,arrSentence[iSentence]);
                arrTraverseArray.push_back(stOneHand);
            }
            else
            {
                // 如果是取中墩,且已经有一个中墩了,再新建一个 ST_ONE_HAND_COMBINATION ,尾墩不需要新建。如果是尾墩则直接往 arrTraverseArray 里面设置
                if (iPos == EN_LOCALTION_MID && arrTraverseArray[arrTraverseArray.size() - 1].m_astSentence[EN_LOCALTION_MID].IsValidSentence())
                {
                    ST_ONE_HAND_COMBINATION stOneHand;
                    stOneHand.m_astSentence[EN_LOCALTION_TAIL] = arrTraverseArray[arrTraverseArray.size() - 1].m_astSentence[EN_LOCALTION_TAIL];
                    arrTraverseArray.push_back(stOneHand);
                }
                arrTraverseArray[arrTraverseArray.size() - 1].SetLocationCard(iPos,arrSentence[iSentence]);                 
            }

            clsGroupTmp.CombinateSpecifiedStyle(arrTraverseArray,EN_ST_TONGHUASHUN);                                        // 此处获得的是以同花顺为尾墩的组合
        }

        if (iTonghuashunCount > 0 && bTail)
        {
            return true;
        }
    }


    if (byType >= EN_ST_TIEZHI)
    {
        int iTiezhiCount = NormalTiezhi(arrSentence);                                           // 到这里肯定没有同花顺了,此时从剩余的单牌中找一个放入 arrSentence 中
        if (byType == EN_ST_TIEZHI && bTail && iTiezhiCount == 0)
        {
            return false;
        }

        for (iSentence = 0;iSentence < iTiezhiCount;++iSentence)                                // 如果有铁枝,则将该铁枝从手牌中删除,拼凑头和中
        {
            CCombinateCards clsGroupTmp = clsGroup;
            if (!clsGroupTmp.DeleteSentence(arrSentence[iSentence],arrSentence[iSentence].m_iSentenceLen))      // 删除该句子,获得剩余的牌
            {
                return false;
            }

            if (bTail)                      // 如果是尾墩,应该新建一个 ST_ONE_HAND_CARD ,如果是中或头,则往 arrTraverseArray 的最后一个 ST_ONE_HAND_CARD 中添加
            {
                ST_ONE_HAND_COMBINATION stOneHand;
                stOneHand.SetLocationCard(EN_LOCALTION_TAIL,arrSentence[iSentence]);
                arrTraverseArray.push_back(stOneHand);
            }
            else
            {
                // 如果是取中墩,且已经有一个中墩了,再新建一个 ST_ONE_HAND_COMBINATION ,尾墩不需要新建。如果是尾墩则直接往 arrTraverseArray 里面设置
                if (iPos == EN_LOCALTION_MID && arrTraverseArray[arrTraverseArray.size() - 1].m_astSentence[EN_LOCALTION_MID].IsValidSentence())
                {
                    ST_ONE_HAND_COMBINATION stOneHand;
                    stOneHand.m_astSentence[EN_LOCALTION_TAIL] = arrTraverseArray[arrTraverseArray.size() - 1].m_astSentence[EN_LOCALTION_TAIL];
                    arrTraverseArray.push_back(stOneHand);
                }
                arrTraverseArray[arrTraverseArray.size() - 1].SetLocationCard(iPos,arrSentence[iSentence]);
            }
            clsGroupTmp.CombinateSpecifiedStyle(arrTraverseArray,EN_ST_TIEZHI);                                         // 此处是以铁枝为尾墩的组合
        }
        if (iTiezhiCount > 0  && bTail)
        {
            return true;
        }
    }

        // 中间其他类型省略 ...

    int iWulongCount = NormalWulong(arrSentence);
    //if (byType == EN_ST_WULONG/* && bTail */&& iWulongCount == 0)                             // 乌龙不会出现在尾墩
    //{
    //  return false;
    //}
    for (iSentence = 0;iSentence < iWulongCount;++iSentence)                                    // 如果有葫芦,则将该葫芦从手牌中删除,拼凑头和中
    {
        CCombinateCards clsGroupTmp = clsGroup;
        if (!clsGroupTmp.DeleteSentence(arrSentence[iSentence],iLength))        // 删除该句子,获得剩余的牌
        {
            return false;
        }

        if (bTail)                      // 如果是尾墩,应该新建一个 ST_ONE_HAND_CARD ,如果是中或头,则往 arrTraverseArray 的最后一个 ST_ONE_HAND_CARD 中添加
        {
            ST_ONE_HAND_COMBINATION stOneHand;
            stOneHand.SetLocationCard(EN_LOCALTION_TAIL,arrSentence[iSentence]);
            arrTraverseArray.push_back(stOneHand);
        }
        else
        {
            // 如果是取中墩,且已经有一个中墩了,再新建一个 ST_ONE_HAND_COMBINATION ,尾墩不需要新建。如果是尾墩则直接往 arrTraverseArray 里面设置
            if (iPos == EN_LOCALTION_MID && arrTraverseArray[arrTraverseArray.size() - 1].m_astSentence[EN_LOCALTION_MID].IsValidSentence())
            {
                ST_ONE_HAND_COMBINATION stOneHand;
                stOneHand.m_astSentence[EN_LOCALTION_TAIL] = arrTraverseArray[arrTraverseArray.size() - 1].m_astSentence[EN_LOCALTION_TAIL];
                arrTraverseArray.push_back(stOneHand);
            }
            arrTraverseArray[arrTraverseArray.size() - 1].SetLocationCard(iPos,arrSentence[iSentence]);
        }
        clsGroupTmp.CombinateSpecifiedStyle(arrTraverseArray,EN_ST_WULONG);                                         // 
    }

    return true;
}
### 十三自动配牌算法实现 以下是基于十三规则设计的一个简单版本的自动配牌算法。该算法会尝试找到最优的分牌方式,使得玩家的手牌能够最大化得分。 #### 算法思路 1. **输入处理**: 接收玩家手中的 13 扑克牌作为输入。 2. **枚举所有可能的分牌方案**: - 将 13 牌分为头墩(3),中墩(5),尾墩(5)的所有可能性。 3. **评估每种分牌方案**: - 使用定义好的牌型评分函数计算每一墩的分数。 - 综合考虑三墩之间的关系(如前墩不能大于后墩)来筛选合法的分牌方案。 4. **选择最佳方案**: - 找到总分最高的合法分牌方案并返回结果。 下面是具体的 Python 实现代码: ```python from itertools import combinations, permutations def card_value(card): """获取单牌的数值""" values = {'2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9, 'T': 10, 'J': 11, 'Q': 12, 'K': 13, 'A': 1} return values[card[:-1]] def is_straight(hand): """判断是否为顺子""" sorted_hand = sorted([card_value(c) for c in hand]) start = min(sorted_hand) end = max(sorted_hand) return all(x in sorted_hand for x in range(start, end + 1)) and len(set(sorted_hand)) == 5 def is_flush(hand): """判断是否为同花""" suits = set([c[-1] for c in hand]) return len(suits) == 1 def rank_hand(hand): """给定一组牌,返回其牌型和对应的权重""" counts = {} for c in hand: value = card_value(c) counts[value] = counts.get(value, 0) + 1 # 判断各种牌型 if is_straight(hand) and is_flush(hand): return ("Straight Flush", sum([card_value(c) for c in hand])) elif any(counts.values() >= 4): return ("Four of a Kind", max([k for k, v in counts.items() if v == 4])) elif len([v for v in counts.values() if v == 3]) == 1 and \ len([v for v in counts.values() if v == 2]) == 1: return ("Full House", max([k for k, v in counts.items() if v == 3])) elif is_flush(hand): return ("Flush", sum([card_value(c) for c in hand])) elif is_straight(hand): return ("Straight", sum([card_value(c) for c in hand])) elif len([v for v in counts.values() if v == 3]) == 1: return ("Three of a Kind", max([k for k, v in counts.items() if v == 3])) elif len([v for v in counts.values() if v == 2]) == 2: return ("Two Pair", sum([k for k, v in counts.items() if v == 2])) elif len([v for v in counts.values() if v == 2]) == 1: return ("One Pair", max([k for k, v in counts.items() if v == 2])) else: return ("High Card", max([card_value(c) for c in hand])) def evaluate_combination(head, mid, tail): """评价一种分牌组合的有效性和得分""" head_rank, _ = rank_hand(head) mid_rank, _ = rank_head(mid) tail_rank, _ = rank_hand(tail) # 前墩不能超过后墩 if rank_order.index(head_rank) > rank_order.index(mid_rank) or \ rank_order.index(mid_rank) > rank_order.index(tail_rank): return None total_score = score_mapping[head_rank][0] * weight_head + \ score_mapping[mid_rank][1] * weight_mid + \ score_mapping[tail_rank][2] * weight_tail return total_score def find_best_split(cards): """寻找最优的分牌策略""" best_score = float('-inf') best_split = None # 枚举所有的分牌组合 for head in combinations(cards, 3): remaining_cards = list(set(cards) - set(head)) for mid in combinations(remaining_cards, 5): tail = list(set(remaining_cards) - set(mid)) current_score = evaluate_combination(list(head), list(mid), tail) if current_score and current_score > best_score: best_score = current_score best_split = (list(head), list(mid), tail) return best_split # 定义全局变量 rank_order = ["High Card", "One Pair", "Two Pair", "Three of a Kind", "Straight", "Flush", "Full House", "Four of a Kind", "Straight Flush"] score_mapping = { "High Card": [1, 2, 3], "One Pair": [2, 4, 6], "Two Pair": [3, 6, 9], "Three of a Kind": [4, 8, 12], "Straight": [5, 10, 15], "Flush": [6, 12, 18], "Full House": [7, 14, 21], "Four of a Kind": [8, 16, 24], "Straight Flush": [9, 18, 27] } weight_head, weight_mid, weight_tail = 1, 2, 3 # 测试数据 cards = ['2H', '3D', '4C', '5S', '6H', '7D', '8C', '9S', 'TH', 'JD', 'QC', 'KS', 'AH'] best_split = find_best_split(cards) print(f"Best Split: Head={best_split[0]}, Mid={best_split[1]}, Tail={best_split[2]}") ``` 以上代码实现了基本的十三分牌逻辑,并通过暴力枚举的方式找到了最优解[^1]。 --- ###
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值