Java | Blackjack

164 阅读3分钟

2023 面向对象程序设计实践(Java)OJ 作业,遇到这么一个折磨人的东西。debug 过久,遂记录。

题目描述

21点又名黑杰克(Blackjack),起源于法国,已流传到世界各地,有着悠久的历史。现在在世界各地的赌场中都可以看到二十一点。随着互联网的发展,二十一点开始走向网络时代。该游戏由2到6个人玩,使用除大小王之外的52张牌,游戏者的目标是使手中的牌的点数之和不超过21点且尽量大。
大家手中扑克点数的计算规则是:2至9牌,按其原点数计算;K、Q、J和10牌都算作10点;A 牌(ace)既可算作1点也可算作11点,由玩家自己决定(当玩家停牌时,点数一律视为最大而尽量不爆,如A+9为20,A+4+8为13,A+3+A视为15)。

你的任务设计基于指定策略的一个21点游戏的部分功能。

指定策略为:如果手中牌的点数之和小于17点则继续要下一张牌,直到大于等于17点为止。如果手里的牌有A,且A的点数当成11点没有超过21点,则此时A要按11点计算,如果超过21点,则A要按1点计算。

一个参考的设计为:1、设计一个card类,用于保存一张牌;2、设计一个hand类,用于保存一手牌;3、设计一个player类,该类可以基于指定策略完成一次游戏过程。

输入

若干行(至少2行),每行代表一张牌。具体格式见样例。

输出

若干行。

读入前两张牌不输出,从第三张牌开始(如果需要),则每次要牌,要先输出Hit,然后读入下一张牌,并依次输出该牌的花色及点数(A输出1 11,即它有两个点数)。当不再要牌时要先输出Stand,然后在一行内输出这一手牌,牌与牌之间用一个空格分隔。

牌输出的顺序为先看牌面,牌面小的在前(牌面由小到大的顺序为A,2,3....J,Q,K),当牌面相同时看花色,输出顺序从前到后为Spade, Heart, Diamond, Club。

最后一行输出这一手牌的结果,如果总点数超过21点,则输出Bust,如果是Blackjack(一手牌只有两张牌且点数相加和为21点),则输出Blackjack。其他情况则输出一个整数,代表这手牌的点数(尽量大且不爆)。具体格式见样例。

样例输入

Spade 4
Heart A
Heart 3

样例输出

Hit
Heart 3
Stand
HeartA Heart3 Spade4
18

完整代码

package hw2.C;

import java.util.*;

class Card implements Comparable<Card> {
    private String suit;
    private String rank;
    private static final HashMap<String, Integer> suits = new HashMap<>();
    private static final HashMap<String, Integer> ranks = new HashMap<>();

    static {
        suits.put("Spade", 1);suits.put("Heart", 2);suits.put("Diamond", 3);suits.put("Club", 4);
        ranks.put("A", 1);ranks.put("2", 2);ranks.put("3", 3);ranks.put("4", 4);
        ranks.put("5", 5);ranks.put("6", 6);ranks.put("7", 7);ranks.put("8", 8);
        ranks.put("9", 9);ranks.put("10", 10);ranks.put("J", 11);ranks.put("Q", 12);ranks.put("K", 13);
    }

    public Card(String suit, String rank) {
        this.suit = suit;
        this.rank = rank;
    }

    public String getSuit() {
        return suit;
    }

    public String getRank() {
        return rank;
    }

    public int getValue() {
        if (rank.equals("A")) {
            return 11;
        } else if (rank.equals("K") || rank.equals("Q") ||
                rank.equals("J") || rank.equals("10")) {
            return 10;
        } else {
            return Integer.parseInt(rank);
        }
    }

    @Override
    public String toString() {
        return suit + " " + rank;
    }

    @Override
    public int compareTo(Card o) {
        int diffOfRank = ranks.get(this.getRank()) - ranks.get(o.getRank());
        int diffOfSuit = suits.get(this.getSuit()) - suits.get(o.getSuit());
        if (diffOfRank > 0) {
            return 1;
        } else if (diffOfRank < 0) {
            return -1;
        } else {
            if (diffOfSuit > 0) {
                return 1;
            } else if (diffOfSuit < 0) {
                return -1;
            } else {
                return 0;
            }
        }
    }
}

class Hand {
    private PriorityQueue<Card> cards;

    public Hand() {
        this.cards = new PriorityQueue<>();
    }

    public void addCard(Card c) {
        cards.add(c);
    }

    public int getValue() {
        int value = 0;
        int aces = 0;
        for (Card c : cards) {
            value += c.getValue();
            if (c.getRank().equals("A")) {
                aces++;
            }
        }
        while (value > 21 && aces > 0) {
            value -= 10;
            aces--;
        }
        return value;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        PriorityQueue<Card> pq = new PriorityQueue<>(cards);
        while (!pq.isEmpty()) {
            Card c = pq.poll();
            sb.append(c.getSuit()).append(c.getRank()).append(" ");
        }
        return sb.toString().trim();
    }
}

class Player {
    private Hand hand;

    public Player() {
        hand = new Hand();
    }

    public void addCard(Card c) {
        hand.addCard(c);
    }

    public int getValue() {
        return hand.getValue();
    }

    public boolean canHit() {
        return this.getValue() < 17;
    }

    public void hit(Card c) {
        System.out.println("Hit");
        this.hand.addCard(c);
        if (c.getRank().equals("A")) {
            System.out.println(c.getSuit() + " 1 11");
        } else {
            System.out.println(c.getSuit() + " " + c.getValue());
        }
    }

    public void stand() {
        System.out.println("Stand");
        System.out.println(hand.toString());
        int n = this.getValue();
        if (n == 21 && hand.toString().split(" ").length == 2) {
            System.out.println("Blackjack");
        } else if (n > 21) {
            System.out.println("Bust");
        } else {
            System.out.println(n);
        }
    }
}

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        LinkedList<Card> queue = new LinkedList<>();
        while (sc.hasNextLine()) {
            String[] arr = sc.nextLine().split(" ");
            Card c = new Card(arr[0], arr[1]);
            queue.addLast(c);
        }
        Player p = new Player();
        p.addCard(queue.removeFirst());
        p.addCard(queue.removeFirst());
        while (p.canHit() && !queue.isEmpty()) {
            Card c = queue.removeFirst();
            p.hit(c);
        }
        p.stand();
    }
}

最后问题出在这里

image.png

image.png

多亏了 newbing,整个类的设计真的很优雅。我觉得比较关键的是 Card 的成员变量还是只存基本的 suitrank ,其点数值由于会随着摸牌过程发生变化,所以把它写成一个成员方法 getValue() 来实时返回。

设计!设计!还是设计!