美文网首页编程学习安卓开发相关Android/Java学习日记
炸金花绝对大小计算、比较及排序算法(Java),包含花色参与和不

炸金花绝对大小计算、比较及排序算法(Java),包含花色参与和不

作者: LeonXtp | 来源:发表于2016-12-20 23:48 被阅读1474次

    昨日忽生兴趣,想起同事正在玩的一个炸金花游戏,见他们讨论略有激烈,想来蛮有趣,于是自己也写来玩玩。
    因有要一次产生很多副牌的需求(可能上1000),要对所有的玩家进行一个排序,因此考虑一个能得到每幅牌的绝对大小的统一算法。

    我有王炸

    牌大小计算算法

    花色不参与大小比较:
    • 首先对到手的牌按照牌数字按照由大到小排序,比如拿到3K6,排序为K63
    • 牌大小按照牌型分级。即普通牌、对子、顺子、同花顺、炸弹
    • 对于普通牌型,每张牌视为16进制的一个数,A对应14(e),2对应2,以此类推。牌值即为这幅16进制牌的大小
      比如最大的普通牌为AKJ,其16进制数值为edc,转为10进制则AKJ牌值=14x16x16+13x16+11=3803
    • 对于对子,先将对子放在牌的前两位,则在最大普通牌大小的基础上,加上对子牌的本身大小。 对子的本身大小计算方法:比如最大的对子为AAK,则AAK牌值=14x16+13=237,加上最大的普通牌值3803,即为4040
    • 对于顺子,取最小的那个数,加上最大的对子牌值,比如最大的顺子AKQ牌值=12+4040=4052。最小的顺子A32,A取1,值4041
    • 对于同花,先按照普通牌型计算大小,再加上最大的对子牌值。
      比如最大的同花AKJ牌值=3803+4052=7855
    • 对于同花顺,取最小的那个数,加上最大的同花牌值,比如:
      AKQ牌值=12+7855=7867,最小的同花顺A32,A取1,A32牌值=1+7855=7856
    • 对于炸弹,取第一个数,加上最大的同花顺牌值。
      比如AAA牌值=14+7867=7881
    花色参与大小比较:
    • 比较规则:在牌数字完全一样的情况下,从最大的牌开始比较,黑桃>红桃>梅花>方片,遇到一个较大的,则结束比较。如:红桃A+红桃Q+方片3>梅花A+黑桃Q+黑桃3。如遇顺子时,数字3最大,从3开始比较花色。
    • 花色值设定:黑桃=3红桃=2梅花=1方片=0。
    • 牌值计算原理:在上面花色不参与大小比较算法的基础上,增加对每副计算出来的牌值乘以64再加上对三张牌花色按照4进制进行花色值计算作为附加值。比如:不考虑花色时,红桃6+方片4+方片2的值为6x256+4x16+2=1602,考虑花色时,红桃+方片+方片对应的4进制就是200,其10进制值为32,然后这副牌的牌值即为:1602x64+32=102560。为什么乘以64?因为三个花色4进制值的范围为63~0。乘以64,就是把原来每组牌值大小相邻的牌型拉开63个的间隔,以便于让花色值有发挥的空间哈哈,用来区别数字完全相同但花色不同的牌型。
    • 如果是炸弹,先将炸弹按花色从大到小排序,保证比如黑桃A红桃A方片A会>红桃A梅花A方片A
    如此,就可得对所有的牌值进行了统一的大小计算

    代码实现

    主要分为四个模块

    • entity 实体
    • player provider 发牌器
    • typevalue 牌型识别
    • calculator 计算器,提供不同方式的计算方法
    实体类

    Card 单张牌

    /**
     * 单张牌
     * 
     * @author Leon
     *
     */
    public class Card {
    
        public static final int FLOWER_SPADE = 3;// 黑桃
        public static final int FLOWER_HEART = 2;// 红桃
        public static final int FLOWER_CLUB = 1;// 梅花
        public static final int FLOWER_DIAMOND = 0;// 方片
    
        public static final int NUM_A = 14;
        public static final int NUM_K = 13;
        public static final int NUM_Q = 12;
        public static final int NUM_J = 11;
        public static final int NUM_10 = 10;
        public static final int NUM_9 = 9;
        public static final int NUM_8 = 8;
        public static final int NUM_7 = 7;
        public static final int NUM_6 = 6;
        public static final int NUM_5 = 5;
        public static final int NUM_4 = 4;
        public static final int NUM_3 = 3;
        public static final int NUM_2 = 2;
    
        // 单张牌大小
        private int number;
        // 花色
        private int flower;
    
        public Card() { }
    
        public Card(int flower, int number) {
            this.flower = flower;
            this.number = number;
        }
    
        public int getNumber() {
            return number;
        }
    
        public void setNumber(int number) {
            this.number = number;
        }
    
        public int getFlower() {
            return flower;
        }
    
        public void setFlower(int flower) {
            this.flower = flower;
        }
    
    }
    

    玩家

    /**
     * 玩家,对应一副牌
     * 
     * @author Leon
     *
     */
    public class Player {
    
        public Card[] cards = new Card[3];
        // 牌类型
        private int type;
        // 是否为特殊牌
        private boolean isSpecial = false;
        // A32也是顺子,比花色时,从3开始比较
        private boolean isA32 = false;
        // 牌绝对值大小
        private int value;
    
        public Player() {
        }
    
        public Player(Card card0, Card card1, Card card2) {
            this.cards[0] = card0;
            this.cards[1] = card1;
            this.cards[2] = card2;
        }
    
        public Card[] getCards() {
            return cards;
        }
    
        public void setCards(Card[] cards) {
            this.cards = cards;
        }
    
        public int getType() {
            return type;
        }
    
        public void setType(int type) {
            this.type = type;
        }
    
        public int getValue() {
            return value;
        }
    
        public void setValue(int value) {
            this.value = value;
        }
    
        public boolean isSpecial() {
            return isSpecial;
        }
    
        public void setSpecial(boolean isSpecial) {
            this.isSpecial = isSpecial;
        }
    
        public boolean isA32() {
            return isA32;
        }
    
        public void setA32(boolean isA32) {
            this.isA32 = isA32;
        }
    
    }
    
    
    PlayerProvider

    发牌器接口,负责发牌、洗牌

    /**
     * 发牌器
     * 
     * @author Leon
     *
     */
    public interface PlayerProvider {
    
        // 产生单副牌
        Player getSinglePlayer();
    
        // 产生多副牌
        List<Player> getPlayers(int number);
    
        // 发一张牌
        Card getCard();
    
        // 洗牌
        void shuffle();
    }
    

    其具体实现有

    • LimitedPlayerProvider 有限制的发牌器,从一幅牌中发牌
    • UnlimitedPlayerProvider 无玩家人数限制的发牌器,随机产生牌,不考虑玩家牌完全相同的情况,但一个玩家手中不会出现完全相同的牌。
    TypeValueSetter

    负责对一副牌进行牌型鉴定,并根据牌型使用指定的计算器计算牌值

    /**
     * 牌型识别器,负责鉴定牌型,并按照指定的模式计算牌大小
     * 
     * @author Leon
     *
     */
    public class TypeValueSetter {
    
        private ValueCalculator calculator;
    
        public TypeValueSetter(ValueCalculator calculator) {
            this.calculator = calculator;
        }
    
        // 判断牌型、计算牌型绝对值大小
        public Player regPlayerType(Player player) {
            if (isFlush(player)) {
                if (isStraight(player)) {// 同花顺
                    player.setType(PlayerType.STRAIGHT_FLUSH);
                    player.setValue(calculator.getStraightFlushValue(player));
                } else {// 同花
                    player.setType(PlayerType.FLUSH);
                    player.setValue(calculator.getFlushValue(player));
                }
            } else if (isStraight(player)) {// 顺子
                player.setType(PlayerType.STRAIGHT);
                player.setValue(calculator.getStraightValue(player));
            } else if (isDouble(player)) {
                if (isBmob(player)) {// 炸弹
                    player.setType(PlayerType.BOMB);
                    player.setValue(calculator.getBombValue(player));
                } else {// 对子
                    player.setType(PlayerType.DOUBLE);
                    // 将对子放到玩家牌的前两张的位置,以便于之后的牌值计算
                    PlayerUtil.moveDouble2Front(player);
                    player.setValue(calculator.getDoubleValue(player));
                }
            } else {// 普通牌
                player.setType(PlayerType.NORMAL);
                player.setValue(calculator.getNormalValue(player));
                if (isSpecial(player)) {// 对于特殊牌,本算法不提供特殊大小计算,外部调用者自行判断是否有炸弹玩家产生
                    player.setSpecial(true);
                }
            }
            return player;
        }
    
        // 是否同花
        private boolean isFlush(Player player) {
            return player.cards[0].getFlower() == player.cards[1].getFlower()
                    && player.cards[1].getFlower() == player.cards[2].getFlower();
        }
    
        // 是否顺子,A32也是同花顺,是最小的同花顺(参考自百度百科)
        // 花色参与比较的时候,黑桃A红桃3黑桃2<方片A黑桃3方片2
        private boolean isStraight(Player player) {
            boolean isNomalStraight = player.cards[0].getNumber() == player.cards[1].getNumber() + 1
                    && player.cards[1].getNumber() == player.cards[2].getNumber() + 1;
            boolean isA32 = player.cards[0].getNumber() == 14 && player.cards[1].getNumber() == 3
                    && player.cards[2].getNumber() == 2;
            if (isA32) {
                player.setA32(true);
            }
            return isNomalStraight || isA32;
        }
    
        // 是否炸弹
        private boolean isBmob(Player player) {
            return player.cards[0].getNumber() == player.cards[1].getNumber()
                    && player.cards[1].getNumber() == player.cards[2].getNumber();
        }
    
        // 是否对子
        private boolean isDouble(Player player) {
            return player.cards[0].getNumber() == player.cards[1].getNumber()
                    || player.cards[1].getNumber() == player.cards[2].getNumber();
        }
    
        // 是否特殊牌
        private boolean isSpecial(Player player) {
            return player.cards[0].getNumber() == 5 && player.cards[1].getNumber() == 3 && player.cards[2].getNumber() == 2;
        }
    }
    
    ValueCalculator

    牌值计算器接口,负责提供对一副牌的计算方法,

    /**
     * 牌值计算器
     * 
     * @author Leon
     *
     */
    public interface ValueCalculator {
        // 获取炸弹牌值绝对大小
        int getBombValue(Player player);
    
        // 获取同花顺牌值绝对大小
        int getStraightFlushValue(Player player);
    
        // 获取同花牌值绝对大小
        int getFlushValue(Player player);
    
        // 获取顺子牌值绝对大小
        int getStraightValue(Player player);
    
        // 获取对子牌值绝对大小
        // 在判断牌型时,如果是对子,则将对子放在数组前面两位
        int getDoubleValue(Player player);
    
        // 获取普通牌值绝对大小
        int getNormalValue(Player player);
    
    }
    

    具体实现有:
    NonFlowerValueCalculator
    花色不参与大小比较的计算器,牌越大,牌值越大

    /**
    * 花色不参与牌大小比较的计算器
    * 牌值越大,牌越大
    * @author Leon
    *
    */
    public class NonFlowerValueCalculator implements ValueCalculator {
    
       // 获取炸弹牌值绝对大小
       public int getBombValue(Player player) {
           return player.cards[0].getNumber() + PlayerType.STRAIGHT_FLUSH_MAX_VALUE;
       }
    
       // 获取同花顺牌值绝对大小,A32也是同花顺,是最小的同花顺(参考自百度百科)
       public int getStraightFlushValue(Player player) {
           if (player.isA32()) {
               //此时A就等于是1
               return 1 + PlayerType.FLUSH_MAX_VALUE;
           }
           return player.cards[2].getNumber() + PlayerType.FLUSH_MAX_VALUE;
       }
    
       // 获取同花牌值绝对大小
       public int getFlushValue(Player player) {
           return player.cards[0].getNumber() * 256 + player.cards[1].getNumber() * 16 + player.cards[2].getNumber()
                   + PlayerType.STRAIGHT_MAX_VALUE;
       }
    
       // 获取顺子牌值绝对大小
       public int getStraightValue(Player player) {
           if (player.isA32()) {
               //此时A就等于是1
               return 1 + PlayerType.DOUBLE_MAX_VALUE;
           }
           return player.cards[2].getNumber() + PlayerType.DOUBLE_MAX_VALUE;
       }
    
       // 获取对子牌值绝对大小
       // 在判断牌型时,如果是对子,则将对子放在数组前面两位
       public int getDoubleValue(Player player) {
           return player.cards[1].getNumber() * 16 + player.cards[2].getNumber() + PlayerType.NORMAL_MAX_VALUE;
       }
    
       // 获取普通牌值绝对大小
       public int getNormalValue(Player player) {
           return player.cards[0].getNumber() * 256 + player.cards[1].getNumber() * 16 + player.cards[2].getNumber();
       }
    
    }
    

    FlowerValueCalculator 花色参与大小比较的计算器,牌越大,牌值越大

    /**
     * 花色参与牌值大小比较的计算器,牌值越大,牌越大
     *
     * @author Leon
     *
     */
    public class FlowerValueCalculator implements ValueCalculator {
    
        private int getFlowerValue(Player player) {
            return player.cards[0].getFlower() * 16 + player.cards[1].getFlower() * 4 + player.cards[2].getFlower();
        }
        
        private int getA32FlowerValue(Player player){
            return player.cards[1].getFlower() * 16 + player.cards[2].getFlower() * 4 + player.cards[0].getFlower();
        }
    
        // 获取炸弹牌值绝对大小
        public int getBombValue(Player player) {
            // 炸弹需要先对牌按花色大小排序,保证比如黑桃A红桃A方片A会>红桃A梅花A方片A
            PlayerUtil.sortPlayerByFlower(player);
            return (player.cards[0].getNumber() + PlayerType.STRAIGHT_FLUSH_MAX_VALUE) * 64 + getFlowerValue(player);
        }
    
        // 获取同花顺牌值绝对大小,A32也是同花顺,是最小的同花顺(参考自百度百科)
        public int getStraightFlushValue(Player player) {
            if (player.isA32()) {
                return (1 + PlayerType.FLUSH_MAX_VALUE) * 64 + getA32FlowerValue(player);
            }
            return (player.cards[2].getNumber() + PlayerType.FLUSH_MAX_VALUE) * 64 + getFlowerValue(player);
        }
    
        // 获取同花牌值绝对大小
        public int getFlushValue(Player player) {
            return (player.cards[0].getNumber() * 256 + player.cards[1].getNumber() * 16 + player.cards[2].getNumber()
                    + PlayerType.STRAIGHT_MAX_VALUE) * 64 + getFlowerValue(player);
        }
    
        // 获取顺子牌值绝对大小
        public int getStraightValue(Player player) {
            if (player.isA32()) {
                return (1 + PlayerType.DOUBLE_MAX_VALUE) * 64 + getA32FlowerValue(player);
            }
            return (player.cards[2].getNumber() + PlayerType.DOUBLE_MAX_VALUE) * 64 + getFlowerValue(player);
        }
    
        // 获取对子牌值绝对大小
        // 在判断牌型时,如果是对子,则将对子放在数组前面两位
        public int getDoubleValue(Player player) {
            // 在花色参与计算大小时,将对子中的花色大的换到前面
            PlayerUtil.exchangeSortedDoubleFlower(player);
            return (player.cards[1].getNumber() * 16 + player.cards[2].getNumber() + PlayerType.NORMAL_MAX_VALUE) * 64
                    + getFlowerValue(player);
        }
    
        // 获取普通牌值绝对大小
        public int getNormalValue(Player player) {
            return (player.cards[0].getNumber() * 256 + player.cards[1].getNumber() * 16 + player.cards[2].getNumber()) * 64
                    + getFlowerValue(player);
        }
    }
    
    

    PlayerType类

    /**
     * 对牌型分类,并提供牌大小值的算法,和已经计算好的牌型最大值 
     * 
     * @author Leon
     *
     */
    public class PlayerType {
        // 炸弹
        public static final int BOMB = 5;
        // 最大值AAA=14,加上同花顺6867=7881
        public static final int BOMB_MAX_VALUE = 7881;
    
        // 同花顺,A32也是顺子,是最小的同花顺(参考自百度百科)
        public static final int STRAIGHT_FLUSH = 4;
        // 最大值AKQ=12,加上同花7855=7867
        public static final int STRAIGHT_FLUSH_MAX_VALUE = 7867;
    
        // 同花
        public static final int FLUSH = 3;
        // 最大值AKJ,14*16*16+13*16+11=3803,加上顺子4052=7855
        public static final int FLUSH_MAX_VALUE = 7855;
    
        // 顺子,A32也是顺子,是最小的同花顺(参考自百度百科)
        public static final int STRAIGHT = 2;
        // 最大值AKQ=12,加上对子的最大值基数4040=4052
        public static final int STRAIGHT_MAX_VALUE = 4052;
    
        // 对子
        public static final int DOUBLE = 1;
        // 最大值AAK=14*16+13=237,加上普通牌的基数3803=4040
        public static final int DOUBLE_MAX_VALUE = 4040;
    
        // 普通牌,里面包含一种特殊牌532不同花色
        // 对于特殊牌,本算法不提供特殊大小计算,但会将这副牌标记为特殊牌
        // 外部调用者自行判断是否有炸弹玩家产生
        public static final int NORMAL = 0;
        // 最大值AKJ=14*16*16+13*16+11=3803
        public static final int NORMAL_MAX_VALUE = 3803;
    
    }
    

    还有两个计算器就不贴代码了:

    Low2HeighCalculator
    花色不参与大小比较的计算器,牌越大,牌值越小
    FlowerLow2HeighCalculator
    花色参与大小比较的计算器,牌越大,牌值越小

    PlayerComparator 总指挥

    负责对牌初始化、牌型识别、牌值计算以及排序。你给我一副或者一组凌乱的牌,我让你整洁有序。

    /**
     * 牌型判断比较器,负责对所有玩家的牌大小进行计算和排序
     * 
     * @author Leon
     *
     */
    public class PlayerComparator implements Comparator<Player> {
    
        private TypeValueSetter recognizer;
    
        public PlayerComparator(ValueCalculator calculator) {
            this.recognizer = new TypeValueSetter(calculator);
        }
    
        /**
         * 对玩家牌型进行牌型识别和牌值计算 这副牌三张都已经按照数字从大到小排好序
         * 
         * @param player
         *            一副牌
         */
        public void setupRegularPlayer(Player player) {
            recognizer.regPlayerType(player);
        }
    
        /**
         * 对玩家牌型进行牌型识别和牌值计算 这副牌没有按照数字从大到小排好序
         * 
         * @param player
         *            一副牌
         */
        public void setupUnRegularPlayer(Player player) {
            PlayerUtil.sortPlayerByNumber(player);
            recognizer.regPlayerType(player);
        }
    
        /**
         * 对玩家列表进行牌型判断、值获取及排序 每副牌的三张牌都已经按照数字从大到小排序
         * 
         * @param playersInput
         *            一组牌
         */
        public void sortRegularPlayers(List<Player> playersInput) {
            for (Player player : playersInput) {
                recognizer.regPlayerType(player);
            }
            Collections.sort(playersInput, this);
        }
    
        /**
         * 对玩家列表进行牌型判断、值获取及排序 每副牌的三张牌没有按照从大到小排序
         * 
         * @param playersInput
         *            一组牌
         */
        public void sortUnRegularPlayers(List<Player> playersInput) {
            for (Player player : playersInput) {
                PlayerUtil.sortPlayerByNumber(player);
                recognizer.regPlayerType(player);
            }
            Collections.sort(playersInput, this);
        }
    
        @Override
        public int compare(Player player1, Player player2) {
            return Integer.valueOf(player1.getValue()).compareTo(Integer.valueOf(player2.getValue()));
        }
    }
    

    完整代码在Github上

    相关文章

      网友评论

        本文标题:炸金花绝对大小计算、比较及排序算法(Java),包含花色参与和不

        本文链接:https://www.haomeiwen.com/subject/jkgfvttx.html