游戏规则:
三人游戏,每人摸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】等等
结束,吃饭。
共同學習,寫下你的評論
評論加載中...
作者其他優質文章



