游戏规则:
三人游戏,每人摸5张牌开始游戏。
只看数字不看花色,轮流出牌,下家出牌必须大于上家,否则要不起,一旦另外两人要不起一个回合结束,回合胜利者获得下回合的优先出牌权。
直到某人先出完牌,游戏结束。
先上运行结果,以飨读者。
发牌
出牌
有点长,截图有点多。
在做的时候考虑了三国杀、斗地主这些卡牌游戏的一些理念,比如牌库、回合牌堆、弃牌堆等,主要设计了Card,Poker,Player三个类。
1. Card 是每张卡牌的具体信息,有花色(suit)、数值(number)和拥有者(player)
suit 有【黑,红,方,梅,“”】五种,“”代表大小王
number 是1,2,3,...,14,15的int数字,代表牌面值的大小
getValue 可以得到牌面值。就是想我们看到的【梅花K】、【大王】这样的牌面
player 是摸牌之后将玩家信息填入,用于记录卡牌使用记录
package pokergame; import java.util.Arrays; public class Card implements Comparable<Card>{ // private final String[] Suits = {"Spade", "Heart", "Diamond", "Club", "Joker"}; private final String[] Suits = {"黑桃", "红桃", "方片", "梅花", ""}; private final String[] Values = {"A","2","3","4","5","6","7","8","9","T","J","Q","K","小 王","大 王"}; private String suit; // 花色 private int number; // 数值 private Player player; // 所属角色 public Card(String suit, String number){ int suitIndex = Arrays.asList(Suits).indexOf(suit); int numIndex = Arrays.asList(Values).indexOf(number); try { this.initCard(suitIndex, numIndex); } catch (Exception e){ e.printStackTrace(); System.out.println(e); } } private void initCard(int s, int n) throws Exception { if (s == -1 || n == -1 || (n != 13 && n != 14 && s == 4) ){ throw new Exception("错误的卡牌格式!"); } else { number = n; suit = Suits[s]; } } // 得到花色 public String getSuit() { return suit; } // 得到牌面值大小(0-12,13,14) public int getValueNumber() { return number; } // 得到牌面值 public String getValue(){ return Values[number]; } // 得到牌名 public String getCardName(){ return suit + Values[number]; } public Player getPlayer() { return player; } public void setPlayer(Player player) { this.player = player; } @Override public int compareTo(Card o) { return this.number - o.number; } }
2. Poker像是【组牌】的概念,List<Card>,就是有card组成的一组牌,可以代表牌库、手牌等
getPokerSize 得到组牌的数量,可用于显示牌库剩余、手牌数目等
sortByValueNumber 由小大大排序
shufflePoker 洗牌
initPoker 初始化一副牌(54张)
getMaxValueNumber得到牌面最大值,用于判断是否有可用卡牌
package pokergame; import java.util.ArrayList; import java.util.Collections; import java.util.List; public class Poker { // private final String[] Suits = {"Spade", "Heart", "Diamond", "Club"}; private final String[] Suits = {"黑桃", "红桃", "方片", "梅花"}; private final String[] Values = {"A","2","3","4","5","6","7","8","9","T","J","Q","K"}; private List<Card> poker; // 组牌(一组牌,多个card组成) public Poker(int pokerSize){ poker = new ArrayList<>(pokerSize); // 初始化大小(手牌上限?) } // 当前扑克牌列表(牌堆,手牌) public List<Card> getPoker() { return poker; } // 打印扑克牌列表 public void printPoker(){ System.out.println("----------"); for (Card c: poker) { System.out.println("|#|" + c.getCardName() + "|#|"); } System.out.println("----------"); } // 打印扑克牌及其使用者列表 public void printPokerAndUser(){ System.out.println("----------"); List<Card> temp = poker; Collections.reverse(temp); // 倒序 for (Card c: temp) { System.out.println( c.getPlayer().getName() + "出牌|#|" + c.getCardName() + "|#|"); } System.out.println("----------"); } // 当前扑克牌数量(牌堆,手牌) public Integer getPokerSize(){ return poker.size(); } // 初始化一组完整的扑克牌(牌堆) public void initPoker(){ // 52张普通花色 for(String s : Suits){ for(String v : Values){ poker.add(new Card(s, v)); } } // 大小王 poker.add(new Card("","小 王")); poker.add(new Card("","大 王")); } // 洗牌 public void shufflePoker(){ Collections.shuffle(poker); } // 按数值由小到大排序 public void sortByValueNumber(){ Collections.sort(poker); } // 返回牌面最大牌 public Card getMaxValueNumber(){ return Collections.max(poker); } // 返回牌面最小牌 public Card getMinValueNumber(){ return Collections.min(poker); } // 选择一张大于输入的最小牌(建议出牌) 必须先排序再使用该方法 public Card getAdviseCard(Card card) throws NoCardUsableException{ for (Card c: poker) { if(c.compareTo(card) > 0 ){ return c; } } throw new NoCardUsableException(); } public void setPoker(List<Card> c){ poker = c; } public void addPoker(Integer index, List<Card> c){ poker.addAll(index, c); } }
3. Player玩家信息,最重要的属性owenedPoker(手牌)
fetchCard 摸牌
useCard 出牌
package pokergame; public class Player { private Integer id; private String name; public Poker ownedPoker; // 拥有的手牌 public Player(int id, String name) { this.id = id; this.name = name; this.ownedPoker = new Poker(0); // 新的poker对象 } // 摸牌 public void fetchCard(Poker pokerHeap) { try { // 从 牌库 移入 手牌 Logic.moveCard(pokerHeap, 0, ownedPoker, 0); // 为牌添加 拥有者 ownedPoker.getPoker().get(0).setPlayer(this); } catch (Exception e) { e.printStackTrace(); System.out.println(e.getMessage()); } } // 出牌 // public void useCard(int position, Poker roundPoker) throws NoCardUsableException { // // 出牌值 大于 回合牌堆顶的值即可 // if (roundPoker.getPokerSize() == 0 || ownedPoker.getPoker().get(position).compareTo(roundPoker.getPoker().get(0)) > 0) { // // 手牌 移入 回合牌堆 // try { // Logic.moveCard(ownedPoker, position, roundPoker, 0); // System.out.println("出牌" + ownedPoker.getPoker().get(position).getCardName()); // } catch (Exception e) { // e.printStackTrace(); // System.out.println(e.getMessage()); // } // } else { // throw new NoCardUsableException(); // } // } public void useCard(Card srcCard, Poker roundPoker) throws NoCardUsableException { // 出牌值 大于 回合牌堆顶的值即可 if (roundPoker.getPokerSize() == 0 || srcCard.compareTo(roundPoker.getPoker().get(0)) > 0) { // 手牌 移入 回合牌堆 try { Logic.moveCard(ownedPoker, srcCard, roundPoker, 0); System.out.println("出牌" + srcCard.getCardName()); } catch (Exception e) { e.printStackTrace(); System.out.println(e.getMessage()); } } else { throw new NoCardUsableException(); } } public Integer getId() { return id; } public String getName() { return name; } }
4. Logic里主要实现了静态方法moveCard,我们把思想转变一下,其实整个游戏过程,摸牌、出牌、弃牌等就是把卡牌挪来挪去,从牌库挪入手牌、从手牌挪到回合牌堆、从回合牌堆挪到弃牌堆等。
package pokergame; public class Logic { public Logic(){} /* 系统逻辑 */ // 移动卡牌(静态方法) public static void moveCard(Poker srcPoker, int srcPosition,Poker destPoker, int destPosition) throws Exception{ if(srcPosition > srcPoker.getPoker().size()) { throw new Exception("源牌堆位置超出!"); } else if (destPosition > destPoker.getPoker().size()){ throw new Exception("目标牌堆位置超出!"); }else{ // 在 源牌堆中找到,然后 加入到 目标牌堆 destPoker.getPoker().add(destPosition ,srcPoker.getPoker().get(srcPosition)); // 从 源牌堆 删除 srcPoker.getPoker().remove(srcPosition); } } public static void moveCard(Poker srcPoker,Card srcCard,Poker destPoker, int destPosition) throws Exception{ if(!srcPoker.getPoker().contains(srcCard)) { throw new Exception("不包含的卡牌!"); } else if (destPosition > destPoker.getPoker().size()){ throw new Exception("目标牌堆位置超出!"); }else{ // 在 源牌堆中找到,然后 加入到 目标牌堆 destPoker.getPoker().add(destPosition , srcCard); // 从 源牌堆 删除 srcPoker.getPoker().remove(srcCard); } } }
5. Round类 实现了回合内的逻辑判断,玩家出牌,挪动卡牌,游戏结算等
package pokergame; import sun.rmi.runtime.Log; import java.util.ArrayList; import java.util.List; public class Round { private Integer roundCount; // 回合数 private List<Player> players; // 参与出牌的角色 private List<Poker> roundHeap; // 回合牌堆(回合内使用) private Poker discardHeap; // 弃牌堆(结算完毕) private Integer passCount; // 要不起的次数,三人游戏两次要不起则回合结束 private Integer playerNum; // 参与出牌的角色数量 private int winnerIndex; // 回合胜利者(即下一回合的初始出牌人)的索引 public Round(List<Player> players, int beginnerIndex) { this.roundCount = 0; this.players = players; this.playerNum = players.size(); this.passCount = 0; this.roundHeap = new ArrayList<>(0); // 主要是为了实现记录出牌顺序的功能 this.discardHeap = new Poker(0); this.winnerIndex = beginnerIndex; } public boolean go() { System.out.println("-----第" + (roundCount + 1) + "回合开始-----"); roundHeap.add(new Poker(0)); // 增加roundHeap passCount = 0; // 清零要不起标记 int turnCount = 0; // 出牌圈数,区别于回合数round do { for (int i = 0; i < 3; i++) { if(turnCount == 0){ i = winnerIndex; // 每回合第一圈的起始出牌人都是上一回合的胜利者 turnCount ++; } if (passCount + 1 < playerNum) { try { players.get(i).ownedPoker.sortByValueNumber(); // 每次都会将手牌由小到大排序 System.out.println(players.get(i).getName() + "的 手牌: "); players.get(i).ownedPoker.printPoker(); players.get(i).useCard(players.get(i).ownedPoker.getMaxValueNumber(), roundHeap.get(roundHeap.size() - 1)); // exception point passCount = 0; winnerIndex = i; // 胜利结算 if(players.get(i).ownedPoker.getPokerSize() == 0) { discardHeap.addPoker(0, roundHeap.get(roundCount).getPoker()); System.out.println("游戏结束,胜利者是:" + (i + 1) + "号玩家" + players.get(i).getName()); System.out.println("卡牌使用记录:"); discardHeap.printPokerAndUser(); // 剩下的如果玩家确认游戏结束了,可以将回合牌堆中的卡牌移入 牌库 重新开始游戏 return true; } } catch (NoCardUsableException e) { passCount++; System.out.println("要不起"); } } } } while (passCount + 1 < playerNum); // 回合结算 System.out.println("当前回合牌堆:"); roundHeap.get(roundCount).printPoker(); System.out.println("本回合胜利者是:" + (winnerIndex + 1) + "号玩家:" + players.get(winnerIndex).getName()); System.out.println("-----第" + (roundCount + 1) + "回合结束-----"); discardHeap.addPoker(0, roundHeap.get(roundCount).getPoker()); // 将回合牌堆卡牌 记录到 弃牌堆(并不是移入,只是记录,卡牌对象实体还是在回合牌堆中) System.out.println("弃牌堆:"); discardHeap.printPoker(); roundCount++; return false; } }
6. NoCardUsableException 是无牌可用异常
package pokergame; public class NoCardUsableException extends Exception { public NoCardUsableException(){ } public NoCardUsableException(String message){ super(message); System.out.println("无可用卡牌"); } }
7. 最后 Client 就是运行主程序。
package pokergame; import java.util.ArrayList; import java.util.List; public class Client { // 参数设置 public static int FETCH_NUM = 5; public static void main(String[] args) { Poker pokerHeap = new Poker(54); // 牌库 pokerHeap.initPoker(); // 牌堆初始化 pokerHeap.shufflePoker(); // 洗牌 // 玩家列表 List<Player> players = new ArrayList<>(3); players.add(new Player(1, "甲")); players.add(new Player(2, "乙")); players.add(new Player(3, "丙")); // 依次摸5张牌 for (int i = 0; i < FETCH_NUM; i++) { for (Player player : players) { player.fetchCard(pokerHeap); } } System.out.println("牌堆剩余:" + pokerHeap.getPokerSize()); // 展示手牌 for (Player player : players) { System.out.println(player.getName() + "手牌:"); player.ownedPoker.printPoker(); } // play System.out.println("开始出牌!"); Round round = new Round(players, 0); Boolean isGameOver = false; // 直到有人出完牌结束比赛 do{ isGameOver = round.go(); }while(!isGameOver); } }
总结: 没有用到抽象类、接口、继承等知识。但对于第三季学到的集合框架、排序、异常都有涉及。没有加入输入互动的部分,程序中出牌都是直接选择自己最大的牌打出,有兴趣的读者可以自己改造。比如我提供一些思路:
开始先选出牌顺序,决定你在玩家列表中的位置,然后每次轮到你出牌,就改成由键盘输入你想打的牌在你手牌中的索引位置。
增加【建议出牌】功能,选出当前手牌中大于上家出牌的最小牌。
由三国杀引发的思路:每个player有自己的技能?在出牌时会触发技能,比如玩家1的红桃牌视为K,玩家2的方片牌可以通吃【JQK】等等
结束,吃饭。
共同学习,写下你的评论
评论加载中...
作者其他优质文章