Acabei de concluir meu primeiro programa com várias classes, Blackjack, e funciona! Ele permite ao usuário jogar Blackjack contra um único dealer, sem outros jogadores na mesa. Eu queria saber, visto que é meu primeiro programa de várias classes, se você poderia me ajudar a otimizá-lo e / ou me dar algum conselho. Eu quero implementar seguro e divisão, então qualquer conselho para ajudar a preparar o código para eventualmente adicionar esses recursos seria realmente útil! Finalmente, meu método principal é muito longo – eu queria saber se isso é típico de programas Java e, se não, como posso consertar isso.
Cartão
package Blackjack; class Card { /* * Creates a playing card. */ private int rank;//represents the rank of a card private int suit;//represents the suit of a card private int value;//represents the value of a card private static String[] ranks = {"Joker","Ace","Two","Three","Four","Five","Six","Seven","Eight","Nine","Ten","Jack","Queen","King"}; private static String[] suits = {"Clubs","Diamonds","Hearts","Spades"}; /* * Created with an integer that represents a spot in the String array ranks and the String array suits. This represents * the rank and suit of an individual card. */ Card(int suit, int values) { this.rank=values; this.suit=suit; } /* * Returns the string version of a card. */ public String toString() { return ranks[rank]+" of "+suits[suit]; } /* * Returns the rank of a card. */ public int getRank() { return rank; } /* * Returns the suit of a card. */ public int getSuit() { return suit; } /* * Returns the value of a card. If a jack, queen, or king the value is ten. Aces are 11 for now. */ public int getValue() { if(rank>10) { value=10; } else if(rank==1) { value=11; } else { value=rank; } return value; } /* * Sets the value of a card. */ public void setValue(int set) { value = set; }
Deck
package Blackjack; import java.util.ArrayList; import java.util.Random; /* * Creates and shuffles a deck of 52 playing cards. */ class Deck { private ArrayList<Card> deck;//represents a deck of cards Deck() { deck = new ArrayList<Card>(); for(int i=0; i<4; i++) { for(int j=1; j<=13; j++) { deck.add(new Card(i,j)); } } } /* * Shuffles the deck by changing the indexes of 200 random pairs of cards in the deck. */ public void shuffle() { Random random = new Random(); Card temp; for(int i=0; i<200; i++) { int index1 = random.nextInt(deck.size()-1); int index2 = random.nextInt(deck.size()-1); temp = deck.get(index2); deck.set(index2, deck.get(index1)); deck.set(index1, temp); } } /* * Draws a card from the deck. */ public Card drawCard() { return deck.remove(0); }
Revendedor
package Blackjack; import java.util.ArrayList; import java.util.Arrays; /* * Creates a dealer that the user plays against. */ class Dealer { ArrayList<Card> hand;//represents the dealer"s hand private int handvalue=0;//value of the dealer"s hand (starts at 0) private Card[] aHand;//used to convert the dealer"s hand to an array private int AceCounter;//counts the aces in the dealer"s hand Dealer(Deck deck) { hand = new ArrayList<>(); aHand = new Card[]{}; int AceCounter=0; for(int i=0; i<2; i++) { hand.add(deck.drawCard()); } aHand = hand.toArray(aHand); for(int i=0; i<aHand.length; i++) { handvalue += aHand[i].getValue(); if(aHand[i].getValue()==11) { AceCounter++; } while(AceCounter>0 && handvalue>21) { handvalue-=10; AceCounter--; } } } /* * Prints the dealer"s first card (the card face up at the beginning of a blackjack game). */ public void showFirstCard() { Card[] firstCard = new Card[]{}; firstCard = hand.toArray(firstCard); System.out.println("["+firstCard[0]+"]"); } /* * Gives the dealer another card and updates the value of his hand. Takes into account the value of aces. */ public void Hit(Deck deck) { hand.add(deck.drawCard()); aHand = hand.toArray(aHand); handvalue = 0; for(int i=0; i<aHand.length; i++) { handvalue += aHand[i].getValue(); if(aHand[i].getValue()==11) { AceCounter++; } while(AceCounter>0 && handvalue>21) { handvalue-=10; AceCounter--; } } } /* * Determines if the dealer wants to hit according to classic Blackjack rules. */ public boolean wantsToHit() { if(handvalue<17) { return true; } return false; } /* * Returns true if the dealer has blackjack. */ public boolean hasBlackJack() { if(hand.size()==2 && handvalue==21) { System.out.println("The dealer has blackjack!"); return true; } return false; } /* * Prints the dealer"s hand. */ public void showHand() { System.out.println(hand); } /* * Returns the value of the dealer"s hand. */ public int getHandValue() { return handvalue; } /* * Determines if a dealer has busted. */ public boolean busted(int handvalue) { if(handvalue>21) { System.out.println("The dealer busted!"); return true; } return false; } /* * Takes the turn for the dealer and returns the value of his hand. */ public int takeTurn(Deck deck) { while(wantsToHit()) { System.out.println("The dealer hits"); Hit(deck); if(busted(handvalue)) { break; } } if(handvalue<=21) { System.out.print("The dealer stands."); } return handvalue; }
Principal
package Blackjack; import java.util.*; public class Blackjack { private static int cash;//cash the user bets with private static int bet;//how much the user wants to bet private static int AceCounter;//how many aces are in the user"s hand private static ArrayList<Card> hand;//represents the user"s hand private static int handvalue;//the value of the user"s hand private static String name;//name of the user public static void main(String[] args){ System.out.println("Hi! What is your name?"); Scanner scan = new Scanner(System.in); name = scan.nextLine(); System.out.println("Hello, "+name+", lets play some BlackJack!"); System.out.println("How much cash do you want to start with?"); Scanner money = new Scanner(System.in); cash = money.nextInt(); System.out.println("You start with cash: "+cash); while(cash>0){ Deck deck = new Deck();//initialize deck, dealer, hands, and set the bet. deck.shuffle(); AceCounter=0; Dealer dealer = new Dealer(deck); List<Card> hand = new ArrayList<>(); hand.add(deck.drawCard()); hand.add(deck.drawCard()); System.out.println("How much would you like to bet?"); bet=Bet(cash); System.out.println("Cash:"+(cash-bet)); System.out.println("Money on the table:"+bet); System.out.println("Here is your hand: "); System.out.println(hand); int handvalue = calcHandValue(hand); System.out.println("The dealer is showing: "); dealer.showFirstCard(); if(hasBlackJack(handvalue) && dealer.hasBlackJack())//check if both the user and dealer have blackjack. { Push(); } else if(hasBlackJack(handvalue))//check if the user has blackjack. { System.out.println("You have BlackJack!"); System.out.println("You win 2x your money back!"); cash=cash+bet; Win(); } else if(dealer.hasBlackJack())//check if the dealer has blackjack. { System.out.println("Here is the dealer"s hand:"); dealer.showHand(); Lose(); } else { if(2*bet<cash)//check if the user can double down. { System.out.println("Would you like to double down?");//allows the user to double down. Scanner doubledown = new Scanner(System.in); String doubled = doubledown.nextLine(); while(!isyesorno(doubled)) { System.out.println("Please enter yes or no."); doubled = doubledown.nextLine(); } if(doubled.equals("yes")) { System.out.println("You have opted to double down!"); bet=2*bet; System.out.println("Cash:"+(cash-bet)); System.out.println("Money on the table:"+bet); } } System.out.println("Would you like to hit or stand?");//ask if the user will hit or stand Scanner hitorstand = new Scanner(System.in); String hitter = hitorstand.nextLine(); while(!isHitorStand(hitter)) { System.out.println("Please enter "hit" or "stand"."); hitter = hitorstand.nextLine(); } while(hitter.equals("hit"))//hits the user as many times as he or she pleases. { Hit(deck, hand); System.out.println("Your hand is now:"); System.out.println(hand); handvalue = calcHandValue(hand); if(checkBust(handvalue))//checks if the user busted { Lose(); break; } if(handvalue<=21 && hand.size()==5)//checks for a five card trick. { fivecardtrick(); break; } System.out.println("Would you like to hit or stand?"); hitter = hitorstand.nextLine(); } if(hitter.equals("stand"))//lets the user stand. { int dealerhand = dealer.takeTurn(deck);//takes the turn for the dealer. System.out.println(""); System.out.println("Here is the dealer"s hand:"); dealer.showHand(); if(dealerhand>21)//if the dealer busted, user wins. { Win(); } else { int you = 21-handvalue;//check who is closer to 21 and determine winner int deal = 21-dealerhand; if(you==deal) { Push(); } if(you<deal) { Win(); } if(deal<you) { Lose(); } } } } System.out.println("Would you like to play again?");//ask if the user wants to keep going Scanner yesorno = new Scanner(System.in); String answer = yesorno.nextLine(); while(!isyesorno(answer)) { System.out.println("Please answer yes or no."); answer = yesorno.nextLine(); } if(answer.equals("no")) { break; } } System.out.println("Your cash is: "+cash);//if user doesn"t want to play or runs out of cash, either congratulates them on their winnings or lets them know if(cash==0) { System.out.println("You ran out of cash!"); } else { System.out.println("Enjoy your winnings, "+name+"!"); } } /* * Checks if the user has blackjack. */ public static boolean hasBlackJack(int handValue) { if(handValue==21) { return true; } return false; } /* * Calculates the value of a player"s hand. */ public static int calcHandValue(List<Card> hand) { Card[] aHand = new Card[]{}; aHand = hand.toArray(aHand); int handvalue=0; for(int i=0; i<aHand.length; i++) { handvalue += aHand[i].getValue(); if(aHand[i].getValue()==11) { AceCounter++; } while(AceCounter>0 && handvalue>21) { handvalue-=10; AceCounter--; } } return handvalue; } /* * Asks the user how much he or she would like to bet. */ public static int Bet(int cash) { Scanner sc=new Scanner(System.in); int bet=sc.nextInt(); while(bet>cash) { System.out.println("You cannot bet more cash than you have!"); System.out.println("How much would you like to bet?"); bet=sc.nextInt(); } return bet; } /* * Called if the user wins. */ public static void Win() { System.out.println("Congratulations, you win!"); cash=cash+bet; System.out.println("Cash: "+cash); } /* * Called if the user loses. */ public static void Lose() { System.out.println("Sorry, you lose!"); cash=cash-bet; System.out.println("Cash: "+cash); } /* * Called if the user pushes */ public static void Push() { System.out.println("It"s a push!"); System.out.println("You get your money back."); System.out.println("Cash: "+cash); } /* * Adds a card to user"s hand and calculates the value of that hand. Aces are taken into account. */ public static void Hit(Deck deck, List<Card> hand) { hand.add(deck.drawCard()); Card[] aHand = new Card[]{}; aHand = hand.toArray(aHand); handvalue = 0; for(int i=0; i<aHand.length; i++) { handvalue += aHand[i].getValue(); if(aHand[i].getValue()==11) { AceCounter++; } while(AceCounter>0 && handvalue>21) { handvalue-=10; AceCounter--; } } } /* * Determines if a user has input hit or stand. */ public static boolean isHitorStand(String hitter) { if(hitter.equals("hit") || hitter.equals("stand")) { return true; } return false; } /* * Determines if a user has busted. */ public static boolean checkBust(int handvalue) { if(handvalue>21) { System.out.println("You have busted!"); return true; } return false; } /* * Determines if a user has input yes or no. */ public static boolean isyesorno(String answer) { if(answer.equals("yes") || answer.equals("no")) { return true; } return false; } /* * Called if the user has a five card trick. */ public static void fivecardtrick() { System.out.println("You have achieved a five card trick!"); Win(); }
Comentários
- Pergunta de acompanhamento
Resposta
Estrutura da classe
- Uma classe
Hand
pode ser útil. Ele pode calcular e armazenar o valor da mão. Isso também evitaria a duplicação que você tem atualmente (calcHandValue
eHit
). - Seu
Dealer
classe contém muitos códigos que eu não colocaria lá. Ele contém o dealer AI (quando o dealer bate?), Verificação de condição de vitória / derrota, impressão e contagem. Com umaHand
classe, você já separaria parte dela. Eu também removeria todas as impressões (elas tornam a reutilização do código difícil e levam a uma estrutura de código ruim), e separar a lógica de IA para sua própria classe (isso tornaria mais fácil mudar as regras, porque elas estão todas em uma local). - Sua classe
Blackjack
também faz muito. Ela imprime, lê a entrada, lida com a entrada, verifica a condição de ganhar / perder (de novo, uma duplicação, veja o próximo ponto), etc. É o jogador, assim como o jogo, que viola o princípio da responsabilidade única. - Sempre que você copiar / colar código, tente pensar em uma alternativa melhor. neste caso, sua
Dealer
e suaBlackjack
classe contêm muitas duplicações. Principalmente porque ambos representam um jogador de blackjack (o dealer e o player). Uma classePlayer
genérica pode ser útil, da qualDealer
eHumanPlayer
estendem .
Então, para resumir: eu adicionaria no mínimo um Hand
, Player
e HumanPlayer
classe. Possivelmente também uma Input
/ Output
interface e ConsoleInput
/ ConsoleOutput
classe (o que tornaria mais fácil adicionar uma GUI). Existem mais classes que você pode criar, mas este seria um bom começo.
Misc
- toda a sua função
shuffle
pode ser substituída porCollections.shuffle(deck);
. - Por que sua
Dealer
classe temhand
eaHand
? Isso parece desnecessário e confuso. - você tem alguns problemas de estilo (posição das chaves, recuo, espaços, etc). Parece mais consistente internamente (essa é a parte importante), mas realmente não corresponde ao que a maioria dos programadores Java está acostumada. A maioria dos IDEs que suportam Java (por exemplo, Netbeans ou Eclipse) formata o código por padrão de uma maneira que a maioria dos programadores Java reconhece .
- nomes de variáveis e métodos devem começar em minúsculas, para que não sejam confundidos com classes.
-
if (cond) return true else return false
pode ser escrito comoreturn cond
.
Comentários
- ok, vou tentar algumas dessas coisas. Muito obrigado . Não ‘ não entendo muito bem por que adicionaria uma interface de entrada / saída e o que faria com que ela fizesse. Você se importa em explicar um pouco mais sobre esse ponto?
- Além disso, em qual classe você recomendaria para executar o jogo?
- @Jared Input iria apenas reunir a ação (que poderia ser um enum; hit, split, etc), e a saída seria a saída tudo. O principal motivo é que separa essas coisas do resto. es seu código mais legível e especialmente mais reutilizável. Você pode executar seu jogo em uma classe
BlackJackGame
, que deve manter o loop do jogo (que chamaria outras classes para dar uma mão, obter dados, verificar a condição final e aplicar os resultados, após o que trata a próxima mão). Todo o resto deve acontecer em outro lugar. - se não ‘ t muito problema, você tem algum código de exemplo ou links para código de exemplo de um jogo ou algo que consideraria bem estruturado?
- Muito bem feito, vitória bem merecida! +1
Resposta
Há muito o que melhorar. Aqui estão algumas dicas para começar.
Comentários excessivos
Esses comentários adicionam algo novo que ainda não está claro?
private int rank;//represents the rank of a card private int suit;//represents the suit of a card private int value;//represents the value of a card
Eles não. Na verdade, a maioria dos outros comentários no código também não agregam valor. O melhor código não precisa de comentários. Examine todos os comentários em seu código, se não forem necessários, remova-os, se forem necessários, e tente alterar o código de forma que não precise de comentários.
Tornando Card
imutável
Isso fará sentido para rank
, suit
e value
para mudar no tempo de vida de uma Card
instância? Provavelmente não. Portanto, crie esses campos final
. Existe um método setValue
, que você também não precisa.
Revise as outras classes também. Faça tudo final
que não precisa mudar ou que não faz sentido mudar. Esta prática pode ajudá-lo a identificar alguns bugs de design.
Consulte os tipos por interfaces
Você declara várias listas como esta:
ArrayList<Card> hand;
Use o tipo de interface:
List<Card> hand;
Considere enum
s
Essas variáveis são boas candidatas para enum
s:
private static String[] ranks = {"Joker","Ace","Two","Three","Four","Five","Six","Seven","Eight","Nine","Ten","Jack","Queen","King"}; private static String[] suits = {"Clubs","Diamonds","Hearts","Spades"};
Assim:
enum Suit { Clubs, Diamonds, Hearts, Spades }
Se quiser iterar os naipes possíveis, você pode fazer for (Suit suit : Suit.values()) { ... }
Números mágicos
Existem muitos números mágicos no código. 17, 10, 11, 4, … Seria melhor colocá-los em public static final
variáveis com nomes descritivos, para esclarecer a finalidade desses valores, junte-os perto do topo do código para um controle mais fácil e flexibilidade para brincar.
Formatação
O código não segue a formatação comum gerada pela opção de formatação automática de IDEs comuns como Eclipse e IntelliJ . Eu sugiro reformatar tudo, para fazer o código parecer mais familiar e fácil de ler para a maioria dos programadores Java. No Eclipse, o atalho de teclado é Control-Shift-f
Comentários
- Se eu usar o formato automático, é assim que os programas são normalmente formatados?
- Sim, ‘ é o maneira comum
- I ‘ d adicionar todo o ” valor ” de Card não é necessário, apenas a função getValue (), que só precisa da classificação para funcionar.
Resposta
Retorne o booleano diretamente
O seguinte:
public static boolean isyesorno(String answer) { if(answer.equals("yes") || answer.equals("no")) { return true; } return false; }
deve se tornar:
public static boolean isyesorno(String answer) { return answer.equals("yes") || answer.equals("no")) }
O mesmo vale para public static boolean hasBlackJack(int handValue)
e public static boolean isHitorStand(String hitter)
e public static boolean checkBust(int handvalue)
para o último, você deve retirar a impressão da função.
Use rodas já existentes
Você pode embaralhar o baralho usando o -in:
List<Cards> list = Arrays.asList(deck); Collections.shuffle(list);
Comentários
- embaralhar desta forma embaralhará meu baralho ArrayList ou me dará uma nova lista chamada list que é embaralhada?
- @Jared in place ..