注:以下算法说明仅限一副牌(不包含大小王)的情况
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); } }