首页 > 代码库 > 扎金花大小比较算法(Java版)

扎金花大小比较算法(Java版)


注:以下算法说明仅限一副牌(不包含大小王)的情况

1、扎金花规则说明(大家都懂的,这里做简单描述):

1)玩家每人3张牌;

2)牌面大小2、3、4、5、6、7、8、9、10(用T表示),J、Q、K、A,大小依次递增;

3)牌的花色有黑桃(用H表示)、红心(用X表示)、梅花(用M表示)、方块(用F表示),大小依次递减;

4)牌有豹子(3张牌数字大小相同)、同花顺、同花(此种未实现,有兴趣的玩家可以自己加上,或者欢迎和我交流)、顺子、对子、散牌几种类型,大小依次递减;

5)玩家先比牌的类型,如先按照4)中的规则比较,如:当都是豹子时,则比较豹子的牌面值大小(如H9X9M9>F8X8M8;类型相同时,按照2)中的牌面值比较,如:H9X9M2>F8X8HA,M9H9M2<F9X9HA等,详情请自行搜索或者查看代码注释或者看测试用例说明;

2、实现算法特点:

1)采用面向对象方式实现,分别构造牌面值的对象(枚举)、牌的花色对象(枚举)、玩家三张牌的类型(枚举,如豹子、同花顺等)、一张扑克牌对应的对象(一张牌有一个牌面值属性、一个花色属性)、玩家对象(玩家有3张扑克牌,牌的类型属性)

2)主要是通过Java Comparable 接口的compareTo实现比较功能,很方便对玩家手中的牌进行排序(调用Collections.sort方法实现),同事避免了很多if else 比较;

3、上代码:

1)牌面值枚举

/*
 * <pre>
 * 文 件 名:  PorkActor.java
 * 描    述:  <描述>
 * 修改时间:  2014-6-15
 * </pre> 
 */
package com.dobuy.zhajinhua;

/**
 * <pre>
 * 每张扑克牌的牌面数字
 * 
 * </pre>
 */
public enum PorkActor
{
    TWO('2'), THREE('3'), FOUR('4'), FIVE('5'), SIX('6'), SEVEN('7'), EIGHT('8'), NIME('9'), TEN('T'), J('J'), Q('Q'), K(
        'K'), A('A');
    
    private char num;
    
    private PorkActor(char num)
    {
        this.num = num;
    }
    
    /**
     * 获取 num
     * 
     * @return 返回 num
     */
    private char getNum()
    {
        return num;
    }
    
    /**
     * <pre>
     * 根据牌面数字找到扑克牌对应的牌面枚举对象
     * 
     * @param num
     * @return
     * </pre>
     */
    public static PorkActor getPorkActor(char num)
    {
        for (PorkActor porkActor : PorkActor.values())
        {
            if (porkActor.getNum() == num)
            {
                return porkActor;
            }
        }
        return null;
    }
}

2)牌的花色枚举

/*
 * <pre>
 * 文 件 名:  PorkColor.java
 * 描    述:  <描述>
 * 修改时间:  2014-6-15
 * </pre> 
 */
package com.dobuy.zhajinhua;

/**
 * <pre>
 * 扑克牌花色
 * 
 * </pre>
 */
public enum PorkColor
{
    F('F'), M('M'), X('X'), H('H');
    
    /**
     * 牌的花色
     */
    private char color;
    
    private PorkColor(char color)
    {
        this.color = color;
    }
    
    /**
     * <pre>
     * 根据花色字符查找扑克牌的花色枚举对象
     * 
     * @param color
     * @return
     * </pre>
     */
    public static PorkColor getPorkColor(char color)
    {
        for (PorkColor porkColor : PorkColor.values())
        {
            if (porkColor.color == color)
            {
                return porkColor;
            }
        }
        return null;
    }
}

3)一张扑克牌

/*
 * <pre>
 * 文 件 名:  Pork.java
 * 描    述:  <描述>
 * 修改时间:  2014-6-15
 * </pre> 
 */
package com.dobuy.zhajinhua;

/**
 * <pre>
 * 一张扑克牌对象
 * 1.实现Comparable接口,通过compareTo方法进行比较大小
 * 2.比较规则:
 * 1)先看牌面数字,数字大的就大;
 * 2)牌面数字相同时,花色大的就大;
 * 
 * </pre>
 */
public class Pork implements Comparable<Pork>
{
    /**
     * 扑克牌的牌面数字
     */
    private PorkActor porkActor;
    
    /**
     * 扑克牌的花色
     */
    private PorkColor porkColor;
    
    /**
     * 长度为2的字符串,接收扑克牌的数字和花色:第0位为数字,第1位为花色 <默认构造函数>
     */
    public Pork(String porkAttr)
    {
        char porkActor = porkAttr.charAt(1);
        char porkColor = porkAttr.charAt(0);
        
        setPorkActor(PorkActor.getPorkActor(porkActor));
        setPorkColor(PorkColor.getPorkColor(porkColor));
    }
    
    /**
     * 获取 porkActor
     * 
     * @return 返回 porkActor
     */
    public PorkActor getPorkActor()
    {
        return porkActor;
    }
    
    /**
     * 设置 porkActor
     * 
     * @param 对porkActor进行赋值
     */
    public void setPorkActor(PorkActor porkActor)
    {
        this.porkActor = porkActor;
    }
    
    /**
     * 获取 porkColor
     * 
     * @return 返回 porkColor
     */
    public PorkColor getPorkColor()
    {
        return porkColor;
    }
    
    /**
     * 设置 porkColor
     * 
     * @param 对porkColor进行赋值
     */
    public void setPorkColor(PorkColor porkColor)
    {
        this.porkColor = porkColor;
    }
    
    /**
     * 重载方法
     * 
     * @return
     */
    @Override
    public int hashCode()
    {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((porkActor == null) ? 0 : porkActor.hashCode());
        result = prime * result + ((porkColor == null) ? 0 : porkColor.hashCode());
        return result;
    }
    
    /**
     * 重载方法
     * 
     * @param obj
     * @return
     */
    @Override
    public boolean equals(Object obj)
    {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Pork other = (Pork)obj;
        if (porkActor != other.porkActor)
            return false;
        if (porkColor != other.porkColor)
            return false;
        return true;
    }
    
    /**
     * 重载方法
     * 
     * @param o
     * @return
     */
    @Override
    public int compareTo(Pork o)
    {
        // 先去比较牌面大小
        int compare = getPorkActor().compareTo(o.getPorkActor());
        
        // 牌面相同时
        if (compare == 0)
        {
            // 比较花色
            return getPorkColor().compareTo(o.getPorkColor());
        }
        return compare;
    }
    
    /**
     * 重载方法
     * 
     * @return
     */
    @Override
    public String toString()
    {
        return "Pork [porkActor=" + porkActor + ", porkColor=" + porkColor + "]";
    }
}

4)玩家

/*
 * <pre>
 * 文 件 名:  PorkPlayer.java
 * 描    述:  <描述>
 * 修改时间:  2014-6-15
 * </pre> 
 */
package com.dobuy.zhajinhua;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * <pre>
 * 扑克牌玩家
 * 
 * </pre>
 */
public class PorkPlayer implements Comparable<PorkPlayer>
{
    private String allPorks;
    
    /**
     * 玩家有三张扑克牌
     */
    private List<Pork> porks;
    
    /**
     * 三张扑克牌的类型:豹子、同花顺等
     */
    private PorkType porkType;
    
    /**
     * 每个玩家默认有3张扑克牌 <默认构造函数>
     */
    public PorkPlayer(String porksStr)
    {
        this.allPorks = porksStr;
        init(porksStr);
    }
    
    /**
     * 获取 allPorks
     * 
     * @return 返回 allPorks
     */
    public String getAllPorks()
    {
        return allPorks;
    }
    
    /**
     * <pre>
     * 根据发的3张牌计算出玩家的牌的类型
     * 
     * @param porksStr
     * </pre>
     */
    private void init(String porksStr)
    {
        porks = new ArrayList<Pork>(3);
        int index = 0;
        int size = porksStr.length();
        
        Pork pork = null;
        while (index < size)
        {
            pork = new Pork(porksStr.substring(index, index + 2));
            porks.add(pork);
            index += 2;
        }
        
        // 对三张牌从小到大排序
        Collections.sort(porks);
        
        // 确定三张牌的类型
        if (isBaozi())
        {
            porkType = PorkType.BAOZI;
        }
        else if (isTonghuashun())
        {
            porkType = PorkType.TONGHUASHUN;
        }
        else if (isShunzi())
        {
            porkType = PorkType.SHUNZI;
        }
        else if (isDuizi())
        {
            porkType = PorkType.DUIZI;
        }
        else
        {
            porkType = PorkType.SANPAI;
        }
    }
    
    /**
     * <pre>
     * 判断是否是豹子(豹子要求3张牌面大小相同)
     * @return
     * </pre>
     */
    private boolean isBaozi()
    {
        Pork pork = porks.get(0);
        for (int i = 1, size = porks.size(); i < size; i++)
        {
            if (pork.getPorkActor() != porks.get(i).getPorkActor())
            {
                return false;
            }
        }
        return true;
    }
    
    /**
     * <pre>
     * 判断是否是顺子
     * @return
     * </pre>
     */
    private boolean isShunzi()
    {
        for (int i = 1, size = porks.size(); i < size; i++)
        {
            if (porks.get(i - 1).getPorkActor().compareTo(porks.get(i).getPorkActor()) != -1)
            {
                return false;
            }
        }
        return true;
    }
    
    /**
     * <pre>
     * 判断是否是同花顺
     * 
     * @return
     * </pre>
     */
    private boolean isTonghuashun()
    {
        if (!isShunzi())
        {
            return false;
        }
        
        Pork pork = porks.get(0);
        for (int i = 1, size = porks.size(); i < size; i++)
        {
            if (pork.getPorkColor() != porks.get(i).getPorkColor())
            {
                return false;
            }
        }
        return true;
    }
    
    /**
     * <pre>
     * 是否是对子
     * 
     * @return
     * </pre>
     */
    private boolean isDuizi()
    {
        for (int i = 1, size = porks.size(); i < size; i++)
        {
            if (porks.get(i - 1).getPorkActor().compareTo(porks.get(i).getPorkActor()) == 0)
            {
                return true;
            }
        }
        return false;
    }
    
    /**
     * <pre>
     * 获取扑克玩家手中的对子对应的扑克牌(不区分花色)
     * 
     * @return
     * </pre>
     */
    private Pork getDuiziPork()
    {
        for (int i = 1, size = porks.size(); i < size; i++)
        {
            if (porks.get(i - 1).getPorkActor().compareTo(porks.get(i).getPorkActor()) == 0)
            {
                return porks.get(i);
            }
        }
        return null;
    }
    
    /**
     * <pre>
     * 获取玩家手中非成对的那张牌
     * @return
     * </pre>
     */
    private Pork getNoDuiziPork()
    {
        // 玩家只有3张牌,且是对子,而牌又是经过排序的,前2张相等,则最后一张是不成对的,否则后2张成对,第0张不同
        if (porks.get(0).compareTo(porks.get(1)) == 0)
        {
            return porks.get(2);
        }
        else
        {
            return porks.get(0);
        }
    }
    
    /**
     * 获取 porkType
     * 
     * @return 返回 porkType
     */
    public PorkType getPorkType()
    {
        return porkType;
    }
    
    /**
     * 重载方法
     * 
     * @return
     */
    @Override
    public int hashCode()
    {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((porkType == null) ? 0 : porkType.hashCode());
        result = prime * result + ((porks == null) ? 0 : porks.hashCode());
        return result;
    }
    
    /**
     * 重载方法
     * 
     * @param obj
     * @return
     */
    @Override
    public boolean equals(Object obj)
    {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        PorkPlayer other = (PorkPlayer)obj;
        if (porkType != other.porkType)
            return false;
        if (porks == null)
        {
            if (other.porks != null)
                return false;
        }
        else if (!porks.equals(other.porks))
            return false;
        return true;
    }
    
    /**
     * 重载方法
     * 
     * @return
     */
    @Override
    public String toString()
    {
        return "PorkPlayer [porks=" + porks + ", porkType=" + porkType + "]";
    }
    
    /**
     * 重载方法
     * 
     * @param o
     * @return
     */
    @Override
    public int compareTo(PorkPlayer o)
    {
        int compare = getPorkType().compareTo(o.getPorkType());
        // TODO
        if (compare == 0)
        {
            switch (getPorkType())
            {
                /**
                 * 豹子、同花顺、顺子直接比较最大牌(最大牌会先比大小,再比花色)
                 */
                case BAOZI:
                case TONGHUASHUN:
                case SHUNZI:
                {
                    return porks.get(2).compareTo(o.porks.get(2));
                }
                case DUIZI:
                {
                    /**
                     * 对子比较
                     */
                    Pork duizi1 = getDuiziPork();
                    Pork duizi2 = o.getDuiziPork();
                    // 先比较对子大小,对子大小相同时,比较散牌大小
                    if (duizi1.getPorkActor() == duizi2.getPorkActor())
                    {
                        compare = getNoDuiziPork().getPorkActor().compareTo(o.getNoDuiziPork().getPorkActor());
                        // 散牌大小相同时,比较对子中最大牌的花色
                        if (compare == 0)
                        {
                            return duizi1.getPorkColor().compareTo(duizi2.getPorkColor());
                        }
                        return compare;
                    }
                    else
                    {
                        // 对子大小不同时,直接比较对子大小
                        return duizi1.getPorkActor().compareTo(duizi2.getPorkActor());
                    }
                }
                case SANPAI:
                {
                    // 散牌依次从最大数开始比较,只比较牌面值大小,如果相同,则从第二大值开始比较,直到不同或者全部比较完毕为止
                    for (int size = porks.size(), i = size - 1; i >= 0; i--)
                    {
                        compare = porks.get(i).getPorkActor().compareTo(o.porks.get(i).getPorkActor());
                        if (compare != 0)
                        {
                            return compare;
                        }
                    }
                    // 说明三张牌的牌面值全部相同,则比较最大牌的花色
                    return porks.get(2).getPorkColor().compareTo(o.porks.get(2).getPorkColor());
                }
            }
        }
        
        return compare;
    }
}

5)单张扑克牌单元测试类

/*
 * <pre>
 * 文 件 名:  PorkTest.java
 * 描    述:  <描述>
 * 修改时间:  2014-6-15
 * </pre> 
 */
package com.dobuy.zhajinhua;

import static org.junit.Assert.assertTrue;

import org.junit.Test;

/**
 * <pre>
 * <一句话功能简述>
 * 
 * </pre>
 */
public class PorkTest
{
    /**
     * <pre>
     * case 1:牌面大小不同时,比较牌面的大小
     * </pre>
     */
    @Test
    public void test1()
    {
        
        Pork pork1 = new Pork("H5");
        Pork pork2 = new Pork("X6");
        
        assertTrue(pork1.compareTo(pork2) < 0);
    }
    
    /**
     * <pre>
     * case 1:牌面大小相同时,比较牌的花色
     * </pre>
     */
    @Test
    public void test2()
    {
        Pork pork1 = new Pork("H5");
        Pork pork2 = new Pork("X5");
        
        assertTrue(pork1.compareTo(pork2) > 0);
    }
}

6)玩家单元测试类

/*
 * <pre>
 * 文 件 名:  PorkPlayerTest.java
 * 描    述:  <描述>
 * 修改时间:  2014-6-16
 * </pre> 
 */
package com.dobuy.zhajinhua;

import static org.junit.Assert.assertTrue;

import org.junit.Test;

/**
 * <pre>
 * <一句话功能简述>
 * 
 * </pre>
 */
public class PorkPlayerTest
{
    @Test
    public void test1()
    {
        PorkPlayer player1 = new PorkPlayer("H2F2X2");
        assertTrue(player1.getPorkType() == PorkType.BAOZI);
        
        PorkPlayer player2 = new PorkPlayer("H2H3H4");
        assertTrue(player2.getPorkType() == PorkType.TONGHUASHUN);
        
        PorkPlayer player3 = new PorkPlayer("H2F3H4");
        assertTrue(player3.getPorkType() == PorkType.SHUNZI);
        
        PorkPlayer player4 = new PorkPlayer("H3F3H4");
        assertTrue(player4.getPorkType() == PorkType.DUIZI);
        
        PorkPlayer player5 = new PorkPlayer("H3F6H4");
        assertTrue(player5.getPorkType() == PorkType.SANPAI);
    }
    
    @Test
    public void test2()
    {
        /**
         * case 1:都是豹子时,比较大小
         */
        PorkPlayer player11 = new PorkPlayer("H2F2X2");
        PorkPlayer player12 = new PorkPlayer("HAFAXA");
        assertTrue(player11.compareTo(player12) < 0);
        
        /**
         * case 2:豹子大于同花顺
         */
        PorkPlayer player21 = new PorkPlayer("H2F2X2");
        PorkPlayer player22 = new PorkPlayer("H3H5H4");
        assertTrue(player21.compareTo(player22) > 0);
        
        /**
         * case 3:都是同花顺时,比较大小
         */
        PorkPlayer player31 = new PorkPlayer("H3H5H4");
        PorkPlayer player32 = new PorkPlayer("F5F6F7");
        assertTrue(player31.compareTo(player32) < 0);
        
        /**
         * case 4:同花顺大于顺子
         */
        PorkPlayer player41 = new PorkPlayer("H3H5H4");
        PorkPlayer player42 = new PorkPlayer("F5H6F7");
        assertTrue(player41.compareTo(player42) > 0);
        
        /**
         * case 5:都是顺子时,比较最大牌(先比较最大牌的牌面值,相同则比较花色)
         */
        PorkPlayer player51 = new PorkPlayer("H3X5H4");
        PorkPlayer player52 = new PorkPlayer("F5H6F7");
        assertTrue(player51.compareTo(player52) < 0);
        
        /**
         * case 6:顺子大于对子
         */
        PorkPlayer player61 = new PorkPlayer("H3X5H4");
        PorkPlayer player62 = new PorkPlayer("F5H7F7");
        assertTrue(player61.compareTo(player62) > 0);
        
        /**
         * case 7.1:都是对子时,比较对子大小
         */
        PorkPlayer player71 = new PorkPlayer("H3F5H5");
        PorkPlayer player72 = new PorkPlayer("XAH6FA");
        assertTrue(player71.compareTo(player72) < 0);
        
        /**
         * case 7.2:都是对子时,对子大小相同,比较散牌的大小
         */
        PorkPlayer player73 = new PorkPlayer("HAF5MA");
        PorkPlayer player74 = new PorkPlayer("XAH6FA");
        assertTrue(player73.compareTo(player74) < 0);
        
        /**
         * case 7.3:都是对子时,比较大小(对子大小相同,比较散牌的大小)
         */
        PorkPlayer player75 = new PorkPlayer("HAF5MA");
        PorkPlayer player76 = new PorkPlayer("XAH6FA");
        assertTrue(player75.compareTo(player76) < 0);
        
        /**
         * case 7.4:都是对子时,三张牌牌面值相同,比较对子中最大牌的花色
         */
        PorkPlayer player77 = new PorkPlayer("HAX5MA");
        PorkPlayer player78 = new PorkPlayer("XAM5FA");
        assertTrue(player77.compareTo(player78) > 0);
        
        /**
         * case 8:对子大于散牌
         */
        PorkPlayer player81 = new PorkPlayer("H3F5X3");
        PorkPlayer player82 = new PorkPlayer("FQH9F7");
        assertTrue(player81.compareTo(player82) > 0);
        
        /**
         * case 9.1:都是散牌时,比较最大牌的牌面值大小
         */
        PorkPlayer player91 = new PorkPlayer("H3F5HJ");
        PorkPlayer player92 = new PorkPlayer("X4H6FT");
        assertTrue(player91.compareTo(player92) > 0);
        
        /**
         * case 9.2:都是散牌时,最大牌面值相同时,比较第2大牌的牌面值大小
         */
        PorkPlayer player93 = new PorkPlayer("H3X5HJ");
        PorkPlayer player94 = new PorkPlayer("X4M2FJ");
        assertTrue(player93.compareTo(player94) > 0);
        
        /**
         * case 9.3:都是散牌时,最大、第2大牌面值相同时,比较最小牌的牌面值大小
         */
        PorkPlayer player95 = new PorkPlayer("H3X5HJ");
        PorkPlayer player96 = new PorkPlayer("X4M5FJ");
        assertTrue(player95.compareTo(player96) < 0);
        
        /**
         * case 9.4:都是散牌时,三张牌的牌面值大小全部相同,则比较最大牌的花色
         */
        PorkPlayer player97 = new PorkPlayer("H3X5HJ");
        PorkPlayer player98 = new PorkPlayer("X3M5FJ");
        assertTrue(player97.compareTo(player98) > 0);
    }
}