我正在为我的第一个编程类构建一个21点游戏。除了在这门课上的编程经验外,我没有任何编程经验。我们的主要项目是使用java构建和运行21点程序。
我已经建立了我的牌组,洗牌,发了一张牌。我站在原地不动的地方是给被拉的牌赋值。我不知道该如何实施。有人能为我指路吗?如有任何帮助,我们将不胜感激!
以下是我的卡片和甲板类:
public class Card
{
private int suit;
private int rank;
private int value;
private String [] suits = {"Spades", "Hearts", "Clubs", "Diamonds"};
private String [] ranks = {"Ace", "2", "3", "4", "5", "6", "7", "8", "9", "10", "Jack", "Queen", "King"};
public Card (int cardSuit, int cardRank)
{
suit = cardSuit;
rank = cardRank;
}
public int getSuit()
{
return suit;
}
public int getRank()
{
return rank;
}
public String toString()
{
return ranks[rank] + " of " + suits[suit];
}
}
import java.util.ArrayList;
import java.util.*;
public class Deck
{
private ArrayList<Card> deck;
private int suitCounter;
private int rankCounter;
private int randomIndex;
private Card card;
private ArrayList<Card> temp;
private Card removeCard;
public Deck() //building deck with 52 cards
{
deck = new ArrayList<Card>();
for(suitCounter = 0; suitCounter < 4; suitCounter++)
{
for(rankCounter = 0; rankCounter < 13; rankCounter++)
{
deck.add(new Card(suitCounter, rankCounter));
}
}
}
public void shuffle() //shuffling the deck
{
Collections.shuffle(deck);
}
public Card deal() //picking the first card out of the deck
{
card = deck.get(0);
return card;
}
public void remove() //removing the dealt card from the deck and putting it in a new temporary deck
{
temp = new ArrayList<Card>();
removeCard = deck.remove(0);
temp.add(removeCard);
}
public String toString() //display the choosen card
{
return "Card: " + card;
}
}
到目前为止,我还没有一个官方的带有主要方法的Blackjack游戏类。我的希望是建立牌组,发一张牌,获得价值,然后我开始建立主要方法。但以下是我必须帮助知道我是否打印出了有价值的卡片。
public class Game
{
public static void main(String[] args)
{
Deck playingDeck = new Deck(); //creating deck object
playingDeck.shuffle(); //shuffling deck
playingDeck.deal(); //dealing card 1 of player's hand
playingDeck.remove(); //removing card 1 from deck
System.out.println(playingDeck); //printing out card 1
playingDeck.deal(); //dealing card 2 of player's hand
playingDeck.remove(); //removing card 2 from deck
System.out.println(playingDeck); //printing out card 2
}
提前谢谢!
一些问题
尽管您通常走在正确的轨道上,但您的实现有一些设计决策需要重新考虑。这也是为什么你发现很难为所绘制的卡片赋值的部分原因。在你的设计中,一张牌有一个value
属性,但如果你仔细想想,一张卡在21点的价值只取决于它的等级和等级。此外,它不会随着时间的推移而改变,这就是为什么你应该保持值与排名紧密耦合的原因。对于rank和suits,需要考虑的另一件事是,只有一组有限的允许和定义良好的值,远远小于int
或String
可以采用的值的范围,所以我建议您使用枚举来键入这些值。请记住,枚举只是特殊的类,因此它们也可以具有属性。
以下是我将如何实现Rank
和Suit
。
Rank.java
public enum Rank {
// the value is determined only by rank so assign the value here!
TWO(2, "2"),
THREE(3, "3"),
FOUR(4, "4"),
FIVE(5, "5"),
SIX(6, "6"),
SEVEN(7, "7"),
EIGHT(8, "8"),
NINE(9, "9"),
TEN(10, "10"),
JACK(10, "J"),
QUEEN(10, "Q"),
KING(10, "K"),
ACE(11, "A");
private final int value;
private final String display;
/**
* Each rank has a value and a String which determines how the rank is displayed in the console.
* @param value value assigned to a rank
* @param display String displayed to represent this rank
*/
Rank(int value, String display) {
this.value = value;
this.display = display;
}
public int getValue() {
return value;
}
public String getDisplay() {
return display;
}
@Override
public String toString() {
return display;
}
}
Suit.java
public enum Suit {
SPADES("Spades"),
HEARTS("Hearts"),
CLUBS("Clubs"),
DIAMONDS("Diamonds");
// display value in console
private final String display;
/**
* Suit of a card
* @param display display value for a suit
*/
Suit(String display) {
this.display = display;
}
public String getDisplay() {
return display;
}
@Override
public String toString() {
return display;
}
}
这对Card
类有一些好处。一张卡只需要一套套装和一个等级,就这样。有了等级,它现在也会自动分配一个值。实现该卡的问题是,您在每张卡中都有一个包含所有可能的等级和套装值的数组。再想想现实世界。一张牌当然不包含所有可能等级和套装的值,而是只有一个等级和一套套装。您的类Card
是一个简化的真实世界卡模型,因此它应该具有真实世界卡的属性。以这种方式设计它也会帮助你掌握你的程序,因为你的设计会更容易理解。
这里是我实现的卡片。
Card.java
public class Card {
// value of a card is not going to change, so we can make it final
private final Suit suit;
private final Rank rank;
public Card(Suit suit, Rank rank) {
this.suit = suit;
this.rank = rank;
}
public Suit getSuit() {
return suit;
}
public Rank getRank() {
return rank;
}
@Override
public String toString() {
// prints e.g. "2-Hearts"
return rank.toString() + "-" + suit.toString();
}
}
现在,让我们转到Deck
类。这场战争实际上是你精心设计的,因为一副牌只是一组卡片。但是你的方法deal()
有一个问题,它阻碍了你知道抽到了哪张牌。在您的实现中,您只需移除牌组顶部的卡,但不会将其退回,因此您基本上只是从内存中擦除一张卡,它就会丢失,无法在游戏中玩。默认情况下,ArrayList
的remove()
方法会返回删除的值,因此请使用该方法。此外,这可能会让人头疼,但我认为你应该将deal()
重命名为drawTop()
,因为在现实世界中,交易需要先画一张牌,然后交给玩家,然后玩家接受这张牌并将其放在手中。
这是我的Deck
实现。由于我之前所做的更改,需要进行一些更改,但这基本上是你的甲板,上面描述了更改。
Deck.java
import java.util.ArrayList;
import java.util.*;
public class Deck {
private final ArrayList<Card> deck;
/**
* Building deck with 52 cards
*
* Try to use Java Doc syntax to like this to comment on methods.
* These comments will be shown in IDEs and an HTML page can be auto-generated from them.
*/
public Deck(){
// get all possible value from the enums for Suit and Ranks
var allSuits = Suit.values();
var allRanks = Rank.values();
// You can provide an initial capacity for how many card you are going to have
// this will avoid unnecessary resizing of the array but that is not that important here
this.deck = new ArrayList<Card>(allSuits.length * allRanks.length);
// These are for-each loop that loop over all possible suits and ranks
for (var suit : allSuits) {
for (var rank : allRanks) {
// add one card for each suit-rank combination
deck.add(new Card(suit, rank));
}
}
}
/**
* Shuffle the deck.
*/
public void shuffle() //shuffling the deck
{
Collections.shuffle(deck);
}
public Card drawTop() //picking the first card out of the deck
{
// check if Deck is empty or not!
if(this.isEmpty()) return null;
// remove removes the card and returns the removed card
return deck.remove(0);
}
/**
* Checks if deck is empty i. e. no cards left in the deck.
* @return true, if no cards are left in deck, false otherwise
*/
public boolean isEmpty(){
return deck.isEmpty();
}
/**
* Gets number of remaining cards in the deck. This is helpful for debugging purposes.
* @return number of remaining cards in the deck
*/
public int getNoRemainingCars(){
return deck.size();
}
@Override
public String toString() {
return deck.toString();
}
}
您现在可以调用var topCard = new Deck().drawTop()
,它将从牌组顶部移除一张卡,该卡将存储在topCard
中。您可以简单地调用System.out.println(topCard)
或使用topCard.getRank()
和topCard.getSuit()
来查看抽到了哪张牌。记住,有了等级,你也会得到卡片的价值。
请参阅下面的main
方法,了解如何与所有这些类进行交互。
public static void main(String[] args) {
// create the deck with all 52 cards
var deck = new Deck();
// print the deck
System.out.printf("Newly created deck: %sn", deck);
// shuffle the cards
deck.shuffle();
System.out.printf("Shuffled deck: %sn", deck);
// remove the first card
var dealtCard = deck.drawTop();
// print the car that was dealt
System.out.printf("Removed card: %sn", dealtCard);
// print info about that card
System.out.printf("The card that was removed from the deck is the %s of %sn", dealtCard.getRank(), dealtCard.getSuit());
// print deck again to show that the card is no longer in the deck
System.out.printf("The deck after a card was removed: %sn", deck);
}
预期输出:
Newly created deck: [2-Spades, 3-Spades, 4-Spades, 5-Spades, 6-Spades, 7-Spades, 8-Spades, 9-Spades, 10-Spades, J-Spades, Q-Spades, K-Spades, A-Spades, 2-Hearts, 3-Hearts, 4-Hearts, 5-Hearts, 6-Hearts, 7-Hearts, 8-Hearts, 9-Hearts, 10-Hearts, J-Hearts, Q-Hearts, K-Hearts, A-Hearts, 2-Clubs, 3-Clubs, 4-Clubs, 5-Clubs, 6-Clubs, 7-Clubs, 8-Clubs, 9-Clubs, 10-Clubs, J-Clubs, Q-Clubs, K-Clubs, A-Clubs, 2-Diamonds, 3-Diamonds, 4-Diamonds, 5-Diamonds, 6-Diamonds, 7-Diamonds, 8-Diamonds, 9-Diamonds, 10-Diamonds, J-Diamonds, Q-Diamonds, K-Diamonds, A-Diamonds]
Shuffled deck: [6-Diamonds, 9-Hearts, Q-Diamonds, 2-Hearts, 7-Hearts, A-Clubs, J-Clubs, A-Hearts, 3-Clubs, 4-Hearts, 3-Hearts, Q-Clubs, 9-Clubs, 6-Hearts, K-Clubs, 8-Clubs, 2-Diamonds, J-Hearts, J-Spades, 4-Diamonds, A-Spades, 7-Clubs, 3-Diamonds, 4-Clubs, Q-Spades, 5-Spades, 5-Hearts, 10-Clubs, 2-Spades, 5-Clubs, K-Hearts, K-Diamonds, 4-Spades, A-Diamonds, 6-Spades, 9-Spades, 3-Spades, 10-Spades, 7-Diamonds, K-Spades, J-Diamonds, 7-Spades, 6-Clubs, 5-Diamonds, 8-Spades, 10-Diamonds, 9-Diamonds, Q-Hearts, 10-Hearts, 8-Hearts, 2-Clubs, 8-Diamonds]
Removed card: 6-Diamonds
The card that was removed from the deck is the 6 of Diamonds
The deck after a card was removed: [9-Hearts, Q-Diamonds, 2-Hearts, 7-Hearts, A-Clubs, J-Clubs, A-Hearts, 3-Clubs, 4-Hearts, 3-Hearts, Q-Clubs, 9-Clubs, 6-Hearts, K-Clubs, 8-Clubs, 2-Diamonds, J-Hearts, J-Spades, 4-Diamonds, A-Spades, 7-Clubs, 3-Diamonds, 4-Clubs, Q-Spades, 5-Spades, 5-Hearts, 10-Clubs, 2-Spades, 5-Clubs, K-Hearts, K-Diamonds, 4-Spades, A-Diamonds, 6-Spades, 9-Spades, 3-Spades, 10-Spades, 7-Diamonds, K-Spades, J-Diamonds, 7-Spades, 6-Clubs, 5-Diamonds, 8-Spades, 10-Diamonds, 9-Diamonds, Q-Hearts, 10-Hearts, 8-Hearts, 2-Clubs, 8-Diamonds]
接下来的步骤
现在,就你的第一个问题而言。但由于你是一个初学者,你想要一些指导,我决定给你一些下一步该做什么以及如何做的指导:)我知道这是一个很大的挑战,但我相信你会认真考虑的。在接下来的几节中,我将向你展示你真的可以从牌组中抽牌并将其交给玩家。
几句话前我提到
在现实世界中的交易包括首先画一张牌,然后把它交给玩家,然后玩家接受这张牌并把它放在他的手上
这个句子中的名词应该是你的下一个类,动词应该是你下一个方法。那就是:
Hand
- 一组卡片
- 添加和移除手牌的能力
Player
- 获得卡片并将其放入其手中的能力
- 应该具有
Hand
,该CCD_27又是Card
的集合
Croupier
(用于句子中的发牌部分)- 克劳皮尔是一名球员,拥有额外的属性和能力
- 需要一张
Deck
的牌,他/她可以向玩家发放 - 从
Deck
中抽取一张牌并将其发给玩家的能力
手
一只手是一个set of cards
(我在这里使用的是Java数据结构HashSet),所有卡片中都有一个CCD23,所以这应该是属性。此外,Hand
应该允许向其添加和移除卡,这将是我们的方法。添加和移除时,我们需要相应地调整手部的total value
。
Hand.java
mport java.util.HashSet;
import java.util.Set;
public class Hand {
private final Set<Card> cards;
private int totalValue;
public Hand() {
this.cards = new HashSet<>();
this.totalValue = 0;
}
/**
* Add a card to the hand
* @param card card to be added
*/
public void addCard(Card card){
// add card to hand
cards.add(card);
// increase total value by value of the card
totalValue += card.getRank().getValue();
}
public void removeCard(Suit suits, Rank rank){
var searchedCard = new Card(suits, rank);
// call other remove method which does the actual removal
removeCard(searchedCard);
}
public void removeCard(Card card){
// remove card if it is present
cards.remove(card);
// remove card from total value
totalValue -= card.getRank().getValue();
}
@Override
public String toString() {
return "Hand{" +
"cards=" + cards +
", totalValue=" + totalValue +
'}';
}
}
播放器
让我们继续看球员。玩家将拥有一个name
和一个Hand
。此外,他/她(如果你想分配一个btw,也可以选择性别)应该有能力接受一张牌,并在发牌时将其放在手中。玩家也会有其他能力,比如决定是否接下另一张牌和其他东西,但这取决于你如何扩展,我只会关注如何接牌。
Player.java
public class Player {
// protected is a necessary access qualifier so subclasses of Player (Croupier will also have the property name and hand without specifically stating that, see Croupier.java)
protected String name;
protected Hand hand;
public Player(String name) {
this.name = name;
this.hand = new Hand();
System.out.printf("Player %s has joined the game.n", name);
}
/**
* Receive a card and add it to the hand
* @param card card to be received
*/
public void receiveCard(Card card){
// add card to hand
hand.addCard(card);
}
@Override
public String toString() {
return "Player{" +
"name='" + name + ''' +
", hand=" + hand +
'}';
}
public String getName() {
return name;
}
}
Crouper
现在有趣的是,一个赌台管理员是一个拥有玩家所有能力和属性的玩家,但他/她也有更多的能力和属性。为了反映这一点,我们在Java中使用了一个称为继承的特性。所以Croupier
拥有Player
所拥有的一切,更重要的是它拥有extends
和Player
。继承将允许我们简单地定义它,并且我们不必再次为Croupier
重新实现我们为Player
所做的一切。因此,我们只需要关注Croupier
的附加属性和能力,即他/她得到一副他/她可以洗牌并发给玩家的牌。为了洗牌,我们只使用Deck
的shuffle()
方法。为了发牌,我们使用Deck
的drawTop()
方法从牌组中抽一张牌(并将该牌退回),以及我们将抽到的牌传给的Player
的receiveCard()
方法。这样,牌组中最上面的牌将被移除并添加到玩家的手牌中。所有这些都发生在dealCardTo()
方法中。
Croupier.java
/**
* Croupier extends Player as he/she is also a Player and should have the same possibilities as a player, but also has additional functionality.
* Google for Java inheritence for more information
*/
public class Croupier extends Player{
private final Deck deck;
public Croupier(String name, Deck deck) {
// call constructor of superclass player
super(name);
this.deck = deck;
}
public void dealCardTo(Player player){
// get one card of the deck
var cardToDeal = deck.drawTop();
// deal the card to the player
player.receiveCard(cardToDeal);
System.out.printf("%s is dealing a card to %s.n", name, player.getName());
}
// this is just for demo purposes
public String showDeck(){
return deck.toString();
}
public void shuffleCards(){
deck.shuffle();
System.out.printf("Croupier %s has shuffled the cards.n", name);
}
@Override
public String toString() {
return "Croupier{" +
"name='" + name + ''' +
", hand=" + hand +
", deck=" + deck +
'}';
}
}
把它们放在一起
现在我们有了向玩家发牌的所有必要功能,所以这里有一个我们如何做到这一点的快速示例。
Application.java
public class Application {
public static void main(String[] args) {
System.out.println("""
+-------------------------------------------------------------------
| Example: Create players, a deck and deal 1 card to each player
+-------------------------------------------------------------------
""");
// create two players, and the croupier
var john = new Player("John");
System.out.println(john);
var elton = new Player("Elton");
System.out.println(elton);
// create a croupier; a groupie is a player and deals cards, so give him a new deck of cards
var croupierTom = new Croupier("Tom", new Deck());
System.out.println(croupierTom);
// a list of all players
var allPlayers = new ArrayList<Player>();
allPlayers.add(john);
allPlayers.add(elton);
// remember a croupier is also a player because it is a subclass
allPlayers.add(croupierTom);
// let the croupier shuffle the cards
croupierTom.shuffleCards();
// tell croupier to deal one card to each player (including himself)
for (var player : allPlayers) {
croupierTom.dealCardTo(player);
}
// now let's see who got which cards
for (var player : allPlayers) {
System.out.println(player);
}
}
}
预期输出:
+-------------------------------------------------------------------
| Example: Create players, a deck and deal 1 card to each player
+-------------------------------------------------------------------
Player John has joined the game.
Player{name='John', hand=Hand{cards=[], totalValue=0}}
Player Elton has joined the game.
Player{name='Elton', hand=Hand{cards=[], totalValue=0}}
Player Tom has joined the game.
Croupier{name='Tom', hand=Hand{cards=[], totalValue=0}, deck=[2-Spades, 3-Spades, 4-Spades, 5-Spades, 6-Spades, 7-Spades, 8-Spades, 9-Spades, 10-Spades, J-Spades, Q-Spades, K-Spades, A-Spades, 2-Hearts, 3-Hearts, 4-Hearts, 5-Hearts, 6-Hearts, 7-Hearts, 8-Hearts, 9-Hearts, 10-Hearts, J-Hearts, Q-Hearts, K-Hearts, A-Hearts, 2-Clubs, 3-Clubs, 4-Clubs, 5-Clubs, 6-Clubs, 7-Clubs, 8-Clubs, 9-Clubs, 10-Clubs, J-Clubs, Q-Clubs, K-Clubs, A-Clubs, 2-Diamonds, 3-Diamonds, 4-Diamonds, 5-Diamonds, 6-Diamonds, 7-Diamonds, 8-Diamonds, 9-Diamonds, 10-Diamonds, J-Diamonds, Q-Diamonds, K-Diamonds, A-Diamonds]}
Croupier Tom has shuffled the cards.
Tom is dealing a card to John.
Tom is dealing a card to Elton.
Tom is dealing a card to Tom.
Player{name='John', hand=Hand{cards=[7-Spades], totalValue=7}}
Player{name='Elton', hand=Hand{cards=[4-Diamonds], totalValue=4}}
Croupier{name='Tom', hand=Hand{cards=[2-Spades], totalValue=2}, deck=[8-Hearts, 5-Hearts, 9-Hearts, 3-Spades, Q-Hearts, 8-Clubs, 2-Diamonds, 6-Diamonds, 10-Hearts, 2-Hearts, 8-Spades, Q-Clubs, 2-Clubs, Q-Spades, 5-Diamonds, 5-Clubs, 7-Clubs, 4-Hearts, K-Spades, 3-Hearts, 9-Clubs, K-Clubs, Q-Diamonds, A-Clubs, 10-Spades, 4-Clubs, A-Spades, J-Diamonds, 10-Diamonds, 7-Diamonds, J-Hearts, A-Hearts, 5-Spades, A-Diamonds, 4-Spades, 7-Hearts, 6-Hearts, J-Clubs, 9-Diamonds, 6-Spades, J-Spades, 8-Diamonds, 10-Clubs, 6-Clubs, 3-Diamonds, K-Diamonds, 3-Clubs, 9-Spades, K-Hearts]}
请记住,通过洗牌,您的结果可能会略有不同,但每个玩家(包括赌台操作员)手中都应该有一张牌,并且牌组中应该删除所有这些牌。
下一步该怎么办
现在,这将为您提供一个很好的框架来实现游戏的其余部分。还有很多事情要做。下一步可能应该创建一个Game
类,它代表一个BlackJack游戏。再次从现实世界出发,记住你的设计应该是一个模型。总是从一个实例的能力(=方法)和属性(=属性)的角度来思考。一个Game
肯定会有一批玩家,一个赌台和启动游戏的能力。想想比赛开始时应该发生什么:赌台将不得不发牌(我们已经看到了如何发牌),但这不仅仅是一张牌,相关玩家将决定是否要换一张牌。您需要执行21点规则以及获胜条件来确定获胜者。你也可以为玩家实施一些赌注,等等
我希望这能帮助你了解你可以开始什么以及如何开始,下一步该做什么:)快乐编码:)
免责声明:我的设计不是金标准或类似的东西,它只是一种可能的设计,可能还有其他(更好的)设计。但我认为这是一种相对简单但足够强大的方法。