首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >王者饮酒游戏

王者饮酒游戏
EN

Code Review用户
提问于 2013-10-05 13:19:38
回答 1查看 1.1K关注 0票数 3

这个审查请求现在通过自定义UI和其他更改演变为本审查请求

我写了一个程序来模拟喝纸牌游戏国王。这是我的第三个Java项目。我不太明白如何创建类CardDeck。我很难创建它们,所以我继续使用了一些在教程中编写它们的代码。但我想我现在明白了。

我计划在PSVM中的while-true loop下添加一个主菜单,其结构与playGame()中的结构相似。

我想回顾一下我的代码,以及一些关于如何使代码更高效、更干净、更容易阅读或其他任何通用指针的提示。

游戏的解释在下面的代码注释框中:

代码语言:javascript
运行
复制
 /**
 * @author              :KyleMHB
 * Project Number       :0003
 * Project Name         :Kings
 * IDE                  :NETBEANS
 * Goal of Project      - 
 * Kings is a rule based drinking game using cards for 4+ players.
 * The Rules are read in from a rules.txt so that one can easily change the rules.
 * How the game works:
 * Players shuffle a deck of cards, place a glass between them and circle the
 * cards around the base of the glass.
 * The players then take turns picking cards, each card has its own rule associated to it.
 * Most importantly, there are 4 Kings, each time a King is picked, 
 * the player who picked it can pour as much of his/her drink into the glass between
 * them as they wish.
 * The game ends when the fourth and final King is picked.
 * The player to pick the final King must down the glass in the center of table.
 */

卡类:

代码语言:javascript
运行
复制
public static class Card {
        private int rank, suit;

        private  String[] suits = { "Hearts", "Spades", "Diamonds", "Clubs" };
        private  String[] ranks  = { "Ace", "2", "3", "4", "5", "6", "7", "8",
                                     "9", "10", "Jack", "Queen", "King" };

        Card(int suit, int rank){
                this.rank=rank;
                this.suit=suit;
        }
        public @Override String toString(){
                  return ranks[rank] + " of " + suits[suit];
        }
        public int getRank() {
                 return rank;
        }
        public int getSuit() {
                return suit;
        }
    }

甲板类

代码语言:javascript
运行
复制
public static class Deck {
    private static ArrayList<Card> cards;
     Deck() {
         cards=new ArrayList<Card>();
            for (int a=0; a<=3; a++){
                    for (int b=0; b<=12; b++){
                       cards.add( new Card(a,b) );
                     }
            }
            Collections.shuffle(cards, new Random());
            Collections.shuffle(cards, new Random(System.nanoTime()));
            //double shuffle for randomness
     }
     public Card drawFromDeck(){       
            return cards.remove( 0 );
     }
     public int getTotalCards(){
            return cards.size();
     }
}

PSVM()rules声明:

代码语言:javascript
运行
复制
private static List<String> rules;

public static void main(String[] args) throws FileNotFoundException, IOException {     
    setGameRules(new File("rules.txt"));
    playGame(getNum("How many people are going to play","Number of Players"));
}

setRules()方法:

代码语言:javascript
运行
复制
/**
* Rules are not hard-coded because people often have different ones,
* Therefore I made an easily editable rules.txt file.
* Also my rule file has formatting in using the \n,
* However when the file is read it is read as \ and n
* Hence why I used the replaceAll( "\\\\n","\n");
*/
private static void setRules(File f) throws FileNotFoundException, IOException {
    rules = Files.readAllLines(f.toPath(), Charset.defaultCharset());
    for(int i=0; i!=rules.size(); i++){
        rules.set(i, rules.get(i).replaceAll( "\\\\n","\n"));
    }
}

getNum()方法:

代码语言:javascript
运行
复制
//This method was left as getNum because I will use it later for a Main Menu
private static int getNum(String prompt,String title) {
    return Integer.parseInt(JOptionPane.showInputDialog(null,prompt,title,3));
}

playGame()方法:

代码语言:javascript
运行
复制
private static void playGame(int players) {
    int playerTurn;
    int choice;
    int kings=0;
    Card cardDrawn;
    Deck deck=new Deck();
    while(true){//loop to run the game till the 4th king is drawn
        playerTurn=0;
        while (playerTurn!=players){//used to give each player a turn
            choice=getChoice("Player "+(playerTurn+1),
                             "Would you like to skip or draw?","Draw","Skip","Exit");

            if (choice==0){
                cardDrawn=deck.drawFromDeck();
                System.out.println(cardDrawn);
                kings+=showCard(cardDrawn,kings,playerTurn+1);     
                playerTurn++;
            }                   
            else if(choice==1)
                playerTurn++;              
            else
                System.exit(0);                                    
         }//Turn reset loop
    }//continuous loop
}

getChoice()方法:

代码语言:javascript
运行
复制
//this method is used so that I can reuse it later in the main menu.
private static int getChoice(String title, String prompt,
                             String a, String b, String c) {
    Object[] options = { a, b, c};
    return JOptionPane.showOptionDialog(null, prompt, title,0,2,
                                        null,options,options[0]);
    }

showCard()方法:

代码语言:javascript
运行
复制
//The method name was originally checkIfKing(), I think this is better?
private static int showCard(Card a, int kings, int player) {
    if(a.rank==12)//checks if the card is a King
        if(kings==3){//checks if the card is the final King
            JOptionPane.showMessageDialog(null,
                    "Player "+player+" has Drawn the Final King\n\n"
                    + "Restart the Program to Play again",a.toString(),1);
            System.exit(0);
            return 0;
        }
        else{
            JOptionPane.showMessageDialog(null,"Player "+player+
                    " has drawn the "+a.toString()+"\n"
                    + "Which is King "+(kings+1)+"/4\n\n"
                    +rules.get(a.rank),a.toString(),1);
            return 1;
        }
    else{
        JOptionPane.showMessageDialog(null,"Player "+player+
                " has drawn the "+a.toString()+"\n\n"
                + rules.get(a.rank),a.toString(),1);
        return 0;
    }
}
EN

回答 1

Code Review用户

回答已采纳

发布于 2013-10-05 14:50:40

您的代码在业务代码(游戏的实际实现)和用户界面代码之间有着非常紧密的耦合。这意味着我不能轻易地重用您的代码,例如,在命令行上播放它,而不是您已经实现的GUI。

您可以通过为所有UI操作定义一个接口来实现这种分离。在您的main中,您可以决定要使用哪个UI实现。

代码语言:javascript
运行
复制
interface UserInterface {
  public int     numberOfPlayers();
  public boolean playerWantsToDrawCard(int player);  // if false: skip
  public void    showCard(Card card, int player, boolean last);
}

然后,将现有的方法重构为GraphicalUserInterface implements UserInterface。请注意,这是一个与您的不同的抽象-您已经展示的实现抽象了所显示的内容,但硬编码了这是如何做到的。我宁愿反过来做这件事。

使用这个新接口,您的playGame将更改为:

代码语言:javascript
运行
复制
private static void playGame(UserInterface ui) {
    int players = ui.numberOfPlayers();
    int kings = 0
    Deck deck = new Deck();

    while (!deck.isEmpty()) {
        for (int player = 0; player < players; player++) {
            // players do not have to draw a card
            if (!ui.playerWantsToDrawCard(player)) {
                continue;
            }

            Card drawnCard = deck.draw();

            if (drawnCard.getRank() == Rank.KING) {
                kings++;
            }
            ui.showCard(drawnCard, player, kings == 4 || deck.isEmpty());

            // exit if we've seen all kings
            if (kings == 4) {
               return;
            }
        }
    }
}

虽然这假定了一些进一步的更改(例如,对Deck API),但主要的一点是业务逻辑不需要直接处理UI。其他重要的变化是:

  • 我使用一个for循环来迭代所有玩家,而不是通过另一个while循环混淆这一点。
  • 我在尽可能紧的范围内声明我的变量。在内部循环之外不需要drawnCard,所以我在那里声明它。根据经验,在不直接初始化变量的情况下,不应该声明变量。

有一件事不是很明显,但我让ui.showCard方法知道这张卡是否是国王,而这是否是最后一个国王。我要提供的唯一提示是这是否是游戏中的最后一张牌。

有一件事我还没有解释,那就是Rank.KING。在原始代码中,您有硬编码的12,它不会向代码的读者解释任何东西。目前,您使用整数指定每张卡的匹配项和级别。这可以说是错误的,您应该使用枚举:

代码语言:javascript
运行
复制
enum Rank {
  ACE   ("Ace"),
  TWO   ("2"),
  THREE ("3"),
  FOUR  ("4"),
  FIVE  ("5"),
  SIX   ("6"),
  SEVEN ("7"),
  EIGHT ("8"),
  NINE  ("9"),
  TEN   ("10"),
  JACK  ("Jack"),
  QUEEN ("Queen"),
  KING  ("King");

  String name;

  Rank(String name) {
    this.name = name;
  }
}

Suit也是。现在我们的Card看起来就像

代码语言:javascript
运行
复制
class Card {
  private Rank rank;
  private Suit suit;

  public String toString() {
    return rank.toString() + " of " + suit.toString();
  }

  ...
}

其他方法已经更新了它们的类型。使用枚举可以提供更多的类型安全性,以及所有可能的值的可比较性和可迭代视图等自动添加。这样我们就可以建造类似的甲板

代码语言:javascript
运行
复制
cards = new ArrayList<Card>();
for (Suit suit : Suit.values()) {
  for (Rank rank : Rank.values()) {
    cards.add(new Card(suit, rank));
  }
}
Collections.shuffle(cards, new Random());

顺便说一下,洗牌一次是“随机的”,而额外的洗牌并不会增加随机性。如果你需要一个真正的密码学级熵源,new Random()给你的内部伪随机数生成器是不够的。但是为了这个游戏的目的,它的使用是相当好的。

票数 5
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/32281

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档