14.5 Player类
前面定义的类可用于任何扑克牌游戏,即我们没有在这些类中实现任何 Crazy Eights 特有的规则。这可能是件好事,因为以后要开发另一款游戏时,可轻松地重用这些类。
接下来该实现这些规则了。为此,我们将使用两个类:Player 和 Eights,前者封装了玩家策略,后者创建并维护游戏的状态。下面列出了 Player 类定义的开头部分:
public class Player {private String name;private Hand hand;public Player(String name) {this.name = name;this.hand = new Hand(name);}
Player 有两个私有属性:name 和 hand。构造函数接受玩家的名字,并将其存储到实例变量 name 中。在这里,我们必须用 this 将同名的实例变量和形参区分开来。
Player 提供的主要方法是 play;每当轮到玩家出牌时,都用它来决定出哪张牌:
public Card play(Eights eights, Card prev) {Card card = searchForMatch(prev);if (card == null) {card = drawForMatch(eights, prev);}return card;}
第一个形参是一个引用,指向封装了游戏状态的 Eights 对象。需要取牌时要用到这个对象。第二个形参 prev 指的是弃牌堆最上面的扑克牌。
我们采用自上而下的开发,让 play 调用两个辅助方法——searchForMatch 和 drawForMatch。searchForMatch 在当前玩家手中查找与前一个玩家所出的牌匹配的牌:
public Card searchForMatch(Card prev) {for (int i = 0; i < hand.size(); i++) {Card card = hand.getCard(i);if (cardMatches(card, prev)) {return hand.popCard(i);}}return null;}
这里的策略非常简单:for 循环找到并返回第一张匹配的。如果没有匹配的牌,就返回 null。在这种情况下,就必须不断取牌,直到获得匹配的牌:
public Card drawForMatch(Eights eights, Card prev) {while (true) {Card card = eights.draw();System.out.println(name + " draws " + card);if (cardMatches(card, prev)) {return card;}hand.addCard(card);}}
其中的 while 循环将不断运行,直到取到匹配的牌(这里暂时假设终将取到匹配的牌)。它用 Eights 对象来取一张牌。如果这张牌是匹配的,就返回该牌;否则就将其放到玩家手里,并继续取牌。
searchForMatch 和 drawForMatch 都使用了 cardMatches。cardMatches 是一个静态方法,也是在 Player 类中定义的,其以简单的方式实现了这个游戏的规则:
public static boolean cardMatches(Card card1, Card card2) {if (card1.getSuit() == card2.getSuit()) {return true;}if (card1.getRank() == card2.getRank()) {return true;}if (card1.getRank() == 8) {return true;}return false;}
最后,Player 还提供了 score,它在游戏结束时根据玩家手里余下的牌计算罚分:
public int score() {int sum = 0;for (int i = 0; i < hand.size(); i++) {Card card = hand.getCard(i);int rank = card.getRank();if (rank == 8) {sum -= 20;} else if (rank > 10) {sum -= 10;} else {sum -= rank;}}return sum;}
