zoukankan      html  css  js  c++  java
  • 斗地主算法的设计与实现(三)--如何比较两手牌的大小

    本篇主要讲解斗地主中如何比较两手牌的大小。

    友情提示:本篇是接着以下两篇文章就讲解的,建议先看看下面这2篇。

    斗地主算法的设计与实现--如何判断一手牌的类型(单,对子,三不带,三带一,四代二等)

    斗地主算法的设计与实现--项目介绍&如何定义和构造一张牌

    牌型比较
      火箭最大;炸弹次之;再次是一般牌型(单牌、对牌、三张牌、三带一、单顺、双顺、三顺、飞机带翅膀、四带二)
      一般牌型:只有牌型且张数相同的牌才可按牌点数比较大小。
      其中三带一、飞机带翅膀、四带二组合牌型,比较其相同张数最多的牌点数大小。

    1.比较我的牌和上家的牌的大小,决定是否可以出牌

    /**
         * 比较我的牌和上家的牌的大小,决定是否可以出牌
         *
         * @param myCards
         *            我想出的牌
         *
         * @param myCardType
         *            我的牌的类型
         * @param prevCards
         *            上家的牌
         * @param prevCardType
         *            上家的牌型
         * @return 可以出牌,返回true;否则,返回false。
         */
        public static boolean isOvercomePrev(List<Card> myCards,
                CardType myCardType, List<Card> prevCards, CardType prevCardType) {
            // 我的牌和上家的牌都不能为null
            if (myCards == null || prevCards == null) {
                return false;
            }
    
            if (myCardType == null || prevCardType == null) {
                logger.info("上家出的牌不合法,所以不能出。");
                return false;
            }
    
            // 上一首牌的个数
            int prevSize = prevCards.size();
            int mySize = myCards.size();
    
            // 我先出牌,上家没有牌
            if (prevSize == 0 && mySize != 0) {
                return true;
            }
    
            // 集中判断是否王炸,免得多次判断王炸
            if (prevCardType == CardType.WANG_ZHA) {
                logger.info("上家王炸,肯定不能出。");
                return false;
            } else if (myCardType == CardType.WANG_ZHA) {
                logger.info("我王炸,肯定能出。");
                return true;
            }
    
            // 集中判断对方不是炸弹,我出炸弹的情况
            if (prevCardType != CardType.ZHA_DAN && myCardType == CardType.ZHA_DAN) {
                return true;
            }
    
            // 默认情况:上家和自己想出的牌都符合规则
            CardUtil.sortCards(myCards);// 对牌排序
            CardUtil.sortCards(prevCards);// 对牌排序
    
            int myGrade = myCards.get(0).grade;
            int prevGrade = prevCards.get(0).grade;
    
            // 比较2家的牌,主要有2种情况,1.我出和上家一种类型的牌,即对子管对子;
            // 2.我出炸弹,此时,和上家的牌的类型可能不同
            // 王炸的情况已经排除
    
            // 单
            if (prevCardType == CardType.DAN && myCardType == CardType.DAN) {
                // 一张牌可以大过上家的牌
                return compareGrade(myGrade, prevGrade);
            }
            // 对子
            else if (prevCardType == CardType.DUI_ZI
                    && myCardType == CardType.DUI_ZI) {
                // 2张牌可以大过上家的牌
                return compareGrade(myGrade, prevGrade);
    
            }
            // 3不带
            else if (prevCardType == CardType.SAN_BU_DAI
                    && myCardType == CardType.SAN_BU_DAI) {
                // 3张牌可以大过上家的牌
                return compareGrade(myGrade, prevGrade);
            }
            // 炸弹
            else if (prevCardType == CardType.ZHA_DAN
                    && myCardType == CardType.ZHA_DAN) {
                // 4张牌可以大过上家的牌
                return compareGrade(myGrade, prevGrade);
    
            }
            // 3带1
            else if (prevCardType == CardType.SAN_DAI_YI
                    && myCardType == CardType.SAN_DAI_YI) {
    
                // 3带1只需比较第2张牌的大小
                myGrade = myCards.get(1).grade;
                prevGrade = prevCards.get(1).grade;
                return compareGrade(myGrade, prevGrade);
    
            }
            // 4带2
            else if (prevCardType == CardType.SI_DAI_ER
                    && myCardType == CardType.SI_DAI_ER) {
    
                // 4带2只需比较第3张牌的大小
                myGrade = myCards.get(2).grade;
                prevGrade = prevCards.get(2).grade;
    
            }
            // 顺子
            else if (prevCardType == CardType.SHUN_ZI
                    && myCardType == CardType.SHUN_ZI) {
                if (mySize != prevSize) {
                    return false;
                } else {
                    // 顺子只需比较最大的1张牌的大小
                    myGrade = myCards.get(mySize - 1).grade;
                    prevGrade = prevCards.get(prevSize - 1).grade;
                    return compareGrade(myGrade, prevGrade);
                }
    
            }
            // 连对
            else if (prevCardType == CardType.LIAN_DUI
                    && myCardType == CardType.LIAN_DUI) {
                if (mySize != prevSize) {
                    return false;
                } else {
                    // 顺子只需比较最大的1张牌的大小
                    myGrade = myCards.get(mySize - 1).grade;
                    prevGrade = prevCards.get(prevSize - 1).grade;
                    return compareGrade(myGrade, prevGrade);
                }
    
            }
            // 飞机
            else if (prevCardType == CardType.FEI_JI
                    && myCardType == CardType.FEI_JI) {
                if (mySize != prevSize) {
                    return false;
                } else {
                    // 顺子只需比较第5张牌的大小(特殊情况333444555666没有考虑,即12张的飞机,可以有2种出法)
                    myGrade = myCards.get(4).grade;
                    prevGrade = prevCards.get(4).grade;
                    return compareGrade(myGrade, prevGrade);
                }
            }
    
            // 默认不能出牌
            return false;
        }
    
    


    2.判断我所有的牌中,是否存在能够管住上家的牌,决定出牌按钮是否显示

      

     /**
         * 判断我所有的牌中,是否存在能够管住上家的牌,决定出牌按钮是否显示
         *
         * @param myCards
         *            我所有的牌 *
         * @param prevCards
         *            上家的牌
         * @param prevCardType
         *            上家牌的类型
         * @return 可以出牌,返回true;否则,返回false。
         */
        public static boolean isOvercomePrev(List<Card> myCards,
                List<Card> prevCards, CardType prevCardType) {
            // 我的牌和上家的牌都不能为null
            if (myCards == null || prevCards == null) {
                return false;
            }
    
            if (prevCardType == null) {
                System.out.println("上家出的牌不合法,所以不能出。");
                return false;
            }
    
            // 默认情况:上家和自己想出的牌都符合规则
            CardUtil.sortCards(myCards);// 对牌排序
            CardUtil.sortCards(prevCards);// 对牌排序
    
            // 上一首牌的个数
            int prevSize = prevCards.size();
            int mySize = myCards.size();
    
            // 我先出牌,上家没有牌
            if (prevSize == 0 && mySize != 0) {
                return true;
            }
    
            // 集中判断是否王炸,免得多次判断王炸
            if (prevCardType == CardType.WANG_ZHA) {
                System.out.println("上家王炸,肯定不能出。");
                return false;
            }
    
            if (mySize >= 2) {
                List<Card> cards = new ArrayList<Card>();
                cards.add(new Card(myCards.get(mySize - 1).id));
                cards.add(new Card(myCards.get(mySize - 2).id));
                if (isDuiWang(cards)) {
                    return true;
                }
            }
    
            // 集中判断对方不是炸弹,我出炸弹的情况
            if (prevCardType != CardType.ZHA_DAN) {
                if (mySize < 4) {
                    return false;
                } else {
                    for (int i = 0; i < mySize - 3; i++) {
                        int grade0 = myCards.get(i).grade;
                        int grade1 = myCards.get(i + 1).grade;
                        int grade2 = myCards.get(i + 2).grade;
                        int grade3 = myCards.get(i + 3).grade;
    
                        if (grade1 == grade0 && grade2 == grade0
                                && grade3 == grade0) {
                            return true;
                        }
                    }
                }
    
            }
    
            int prevGrade = prevCards.get(0).grade;
    
            // 比较2家的牌,主要有2种情况,1.我出和上家一种类型的牌,即对子管对子;
            // 2.我出炸弹,此时,和上家的牌的类型可能不同
            // 王炸的情况已经排除
    
            // 上家出单
            if (prevCardType == CardType.DAN) {
                // 一张牌可以大过上家的牌
                for (int i = mySize - 1; i >= 0; i--) {
                    int grade = myCards.get(i).grade;
                    if (grade > prevGrade) {
                        // 只要有1张牌可以大过上家,则返回true
                        return true;
                    }
                }
    
            }
            // 上家出对子
            else if (prevCardType == CardType.DUI_ZI) {
                // 2张牌可以大过上家的牌
                for (int i = mySize - 1; i >= 1; i--) {
                    int grade0 = myCards.get(i).grade;
                    int grade1 = myCards.get(i - 1).grade;
    
                    if (grade0 == grade1) {
                        if (grade0 > prevGrade) {
                            // 只要有1对牌可以大过上家,则返回true
                            return true;
                        }
                    }
                }
    
            }
            // 上家出3不带
            else if (prevCardType == CardType.SAN_BU_DAI) {
                // 3张牌可以大过上家的牌
                for (int i = mySize - 1; i >= 2; i--) {
                    int grade0 = myCards.get(i).grade;
                    int grade1 = myCards.get(i - 1).grade;
                    int grade2 = myCards.get(i - 2).grade;
    
                    if (grade0 == grade1 && grade0 == grade2) {
                        if (grade0 > prevGrade) {
                            // 只要3张牌可以大过上家,则返回true
                            return true;
                        }
                    }
                }
    
            }
            // 上家出3带1
            else if (prevCardType == CardType.SAN_DAI_YI) {
                // 3带1 3不带 比较只多了一个判断条件
                if (mySize < 4) {
                    return false;
                }
    
                // 3张牌可以大过上家的牌
                for (int i = mySize - 1; i >= 2; i--) {
                    int grade0 = myCards.get(i).grade;
                    int grade1 = myCards.get(i - 1).grade;
                    int grade2 = myCards.get(i - 2).grade;
    
                    if (grade0 == grade1 && grade0 == grade2) {
                        if (grade0 > prevGrade) {
                            // 只要3张牌可以大过上家,则返回true
                            return true;
                        }
                    }
                }
    
            }
            // 上家出炸弹
            else if (prevCardType == CardType.ZHA_DAN) {
                // 4张牌可以大过上家的牌
                for (int i = mySize - 1; i >= 3; i--) {
                    int grade0 = myCards.get(i).grade;
                    int grade1 = myCards.get(i - 1).grade;
                    int grade2 = myCards.get(i - 2).grade;
                    int grade3 = myCards.get(i - 3).grade;
    
                    if (grade0 == grade1 && grade0 == grade2 && grade0 == grade3) {
                        if (grade0 > prevGrade) {
                            // 只要有4张牌可以大过上家,则返回true
                            return true;
                        }
                    }
                }
    
            }
            // 上家出4带2
            else if (prevCardType == CardType.SI_DAI_ER) {
                // 4张牌可以大过上家的牌
                for (int i = mySize - 1; i >= 3; i--) {
                    int grade0 = myCards.get(i).grade;
                    int grade1 = myCards.get(i - 1).grade;
                    int grade2 = myCards.get(i - 2).grade;
                    int grade3 = myCards.get(i - 3).grade;
    
                    if (grade0 == grade1 && grade0 == grade2 && grade0 == grade3) {
                        // 只要有炸弹,则返回true
                        return true;
                    }
                }
            }
            // 上家出顺子
            else if (prevCardType == CardType.SHUN_ZI) {
                if (mySize < prevSize) {
                    return false;
                } else {
                    for (int i = mySize - 1; i >= prevSize - 1; i--) {
                        List<Card> cards = new ArrayList<Card>();
                        for (int j = 0; j < prevSize; j++) {
                            cards.add(new Card(myCards.get(i - j).grade));
                        }
    
                        CardType myCardType = getCardType(cards);
                        if (myCardType == CardType.SHUN_ZI) {
                            int myGrade2 = cards.get(cards.size() - 1).grade;// 最大的牌在最后
                            int prevGrade2 = prevCards.get(prevSize - 1).grade;// 最大的牌在最后
    
                            if (myGrade2 > prevGrade2) {
                                return true;
                            }
                        }
                    }
                }
    
            }
            // 上家出连对
            else if (prevCardType == CardType.LIAN_DUI) {
                if (mySize < prevSize) {
                    return false;
                } else {
                    for (int i = mySize - 1; i >= prevSize - 1; i--) {
                        List<Card> cards = new ArrayList<Card>();
                        for (int j = 0; j < prevSize; j++) {
                            cards.add(new Card(myCards.get(i - j).grade));
                        }
    
                        CardType myCardType = getCardType(cards);
                        if (myCardType == CardType.LIAN_DUI) {
                            int myGrade2 = cards.get(cards.size() - 1).grade;// 最大的牌在最后,getCardType会对列表排序
                            int prevGrade2 = prevCards.get(prevSize - 1).grade;// 最大的牌在最后
    
                            if (myGrade2 > prevGrade2) {
                                return true;
                            }
                        }
                    }
                }
    
            }
            // 上家出飞机
            else if (prevCardType == CardType.FEI_JI) {
                if (mySize < prevSize) {
                    return false;
                } else {
                    for (int i = mySize - 1; i >= prevSize - 1; i--) {
                        List<Card> cards = new ArrayList<Card>();
                        for (int j = 0; j < prevSize; j++) {
                            cards.add(new Card(myCards.get(i - j).grade));
                        }
    
                        CardType myCardType = getCardType(cards);
                        if (myCardType == CardType.FEI_JI) {
                            int myGrade4 = cards.get(4).grade;//
                            int prevGrade4 = prevCards.get(4).grade;//
    
                            if (myGrade4 > prevGrade4) {
                                return true;
                            }
                        }
                    }
                }
            }
    
            // 默认不能出牌
            return false;
        }
    
        
    

    3.比较2个grade的大小

    /**
         * 比较2个grade的大小
         *
         * @param grade1
         * @param grade2
         * @return
         */
        private static boolean compareGrade(int grade1, int grade2) {
            return grade1 > grade2;
        }
    
        
    

    4.检测牌的类型

        /**
         * 检测牌的类型
         *
         * @param myCards
         *            我出的牌
         * @return 如果遵守规则,返回牌的类型,否则,返回null。
         */
        public static CardType getCardType(List<Card> myCards) {
            CardType cardType = null;
            if (myCards != null) {
                // 大概率事件放前边,提高命中率
                if (isDan(myCards)) {
                    cardType = CardType.DAN;
                } else if (isDuiWang(myCards)) {
                    cardType = CardType.WANG_ZHA;
                } else if (isDuiZi(myCards)) {
                    cardType = CardType.DUI_ZI;
                } else if (isZhaDan(myCards)) {
                    cardType = CardType.ZHA_DAN;
                } else if (isSanDaiYi(myCards) != -1) {
                    cardType = CardType.SAN_DAI_YI;
                } else if (isSanBuDai(myCards)) {
                    cardType = CardType.SAN_BU_DAI;
                } else if (isShunZi(myCards)) {
                    cardType = CardType.SHUN_ZI;
                } else if (isLianDui(myCards)) {
                    cardType = CardType.LIAN_DUI;
                } else if (isSiDaiEr(myCards)) {
                    cardType = CardType.SI_DAI_ER;
                } else if (isFeiJi(myCards)) {
                    cardType = CardType.FEI_JI;
                }
            }
    
            return cardType;
    
        }
    
    

    未来计划

    接下来2篇将讲述 如何对牌进行排序,如何构造一副牌、洗牌、发牌。

    本周日 2013年10月13日上传所有源码。

     

    相关阅读

    斗地主算法的设计与实现

    面向对象实现斗地主程序的核心算法,包括洗牌、发牌、判断牌型、比较牌的大小、游戏规则等。


    原文参见:http://FansUnion.cn/articles/2729

  • 相关阅读:
    poj 2187 Beauty Contest(旋转卡壳)
    poj 2540 Hotter Colder(极角计算半平面交)
    poj 1279 Art Gallery(利用极角计算半平面交)
    poj 3384 Feng Shui(半平面交的联机算法)
    poj 1151 Atlantis(矩形面积并)
    zoj 1659 Mobile Phone Coverage(矩形面积并)
    uva 10213 How Many Pieces of Land (欧拉公式计算多面体)
    uva 190 Circle Through Three Points(三点求外心)
    zoj 1280 Intersecting Lines(两直线交点)
    poj 1041 John's trip(欧拉回路)
  • 原文地址:https://www.cnblogs.com/qitian1/p/6463553.html
Copyright © 2011-2022 走看看