Primer intento en un juego de Blackjack en Java

Acabo de completar mi primer programa de clases múltiples, Blackjack, ¡y funciona! Permite al usuario jugar al Blackjack contra un solo crupier, sin otros jugadores en la mesa. Me preguntaba, ya que es mi primer programa de clases múltiples, si me podrían ayudar a optimizarlo y / o darme algún consejo. Quiero implementar el seguro y la división, por lo que cualquier consejo para ayudar a preparar el código para eventualmente agregar esas características sería realmente útil. Finalmente, mi método principal es bastante largo. Me preguntaba si esto es típico de los programas Java y, si no, cómo puedo solucionarlo.

Card

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); } 

Concesionario

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(); } 

Comentarios

Respuesta

Estructura de clases

  • Una clase Hand puede resultar útil. Puede calcular y almacenar el valor de la mano. Esto también evitaría la duplicación que tiene actualmente (calcHandValue y Hit).
  • Su contiene una gran cantidad de código que no colocaría allí. Contiene la IA del crupier (¿cuándo acierta el crupier?), Verificación de la condición de ganar / perder, imprimir y contar. Con una clase Hand, ya separaría algunas de ellas. También eliminaría todas las impresiones (dificultan la reutilización del código y conducen a una estructura de código incorrecta) y separaría la lógica de la IA en su propia clase (esto facilitaría el cambio de las reglas, porque están todas en una lugar).
  • Su Blackjack clase también hace demasiado. Imprime, lee la entrada, maneja la entrada, verifica la condición de ganar / perder (de nuevo, una duplicación, ver el siguiente punto), etc. Es el jugador, así como el juego, lo que viola el principio de responsabilidad única.
  • Siempre que copie / pegue código, intente pensar en una alternativa mejor. En este caso, su Dealer y su clase Blackjack contienen muchas duplicaciones. Principalmente porque ambas representan a un jugador de blackjack (el crupier y el jugador). Una clase Player genérica podría ser útil, desde la cual se extienden Dealer y HumanPlayer .

Entonces, para resumir: agregaría como mínimo un Hand, Player y HumanPlayer clase. Posiblemente también una interfaz Input / Output y ConsoleInput / ConsoleOutput clase (que facilitaría la adición de una GUI). Hay más clases que podrías crear, pero este sería un buen comienzo.

Misc

  • toda su shuffle función puede ser reemplazada por Collections.shuffle(deck);.
  • ¿Por qué su Dealer ¿La clase tiene hand y aHand? Esto parece innecesario y confuso.
  • Tiene algunos problemas de estilo (posición de las llaves, sangría, espacios, etc.). Parece principalmente coherente internamente (esa es la parte importante), pero en realidad no coincide con lo que la mayoría de los programadores de Java están acostumbrados. La mayoría de los IDE que admiten Java (por ejemplo, Netbeans o Eclipse) formatean el código por defecto de una manera que la mayoría de los programadores de Java reconocen .
  • Los nombres de las variables y los métodos deben comenzar en minúsculas, para que no se confundan con las clases.
  • if (cond) return true else return false se puede escribir como return cond.

Comentarios

  • bien, intentaré algunas de estas cosas. Muchas gracias . No ‘ entiendo muy bien por qué agregaría una interfaz de entrada / salida y qué haría que hiciera. ¿Te importaría explicar un poco más sobre ese punto?
  • también, ¿en qué clase me recomendarías que ejecute el juego?
  • @Jared Input solo recopilaría la acción (que podría ser una enumeración; hit, split, etc.), y la salida daría como resultado todo. La razón principal es que separa esas cosas del resto. Hace Es su código más legible, y especialmente más reutilizable. Puedes ejecutar tu juego en una clase BlackJackGame, que debería mantener el ciclo del juego (que llamaría a otras clases para repartir una mano, obtener información, verificar la condición de finalización y aplicar resultados, después de lo cual reparte la siguiente mano). Todo lo demás debería ocurrir idealmente en otro lugar.
  • Si no es ‘ t demasiado problema, ¿tiene algún código de ejemplo o enlaces al código de ejemplo de un juego o algo que considere bien estructurado?
  • Muy bien hecho, ¡victoria bien merecida! +1

Responder

Hay muchas cosas que mejorar en esto. Aquí hay un par de consejos para comenzar.

Comentarios excesivos

¿Estos comentarios agregan algo nuevo que no esté claro ya?

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 

No lo hacen. De hecho, la mayoría de los otros comentarios en el código tampoco agregan valor. El mejor código no necesita comentarios. Revise todos los comentarios en su código, si no son necesarios, luego elimínelos, si son necesarios, luego intente cambiar el código de una manera que no necesite comentarios.

Hacer Card inmutable

¿Tendrá sentido para rank, suit y value para cambiar durante la vida útil de una Card instancia? Probablemente no. Así que haga estos campos final. Hay un método setValue, que tampoco necesitas.

Revisa las otras clases también. Haga que todo final que no necesite cambiar o que no tenga sentido cambiar nunca. Esta práctica puede ayudarlo a detectar algunos errores de diseño.

Consulte los tipos por interfaces

Declara varias listas como esta:

ArrayList<Card> hand; 

Utilice el tipo de interfaz en su lugar:

List<Card> hand; 

Considere enum s

Estas variables son buenas 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"}; 

Así:

enum Suit { Clubs, Diamonds, Hearts, Spades } 

Si desea iterar sobre los posibles palos, puede hacer for (Suit suit : Suit.values()) { ... }

números mágicos

Hay demasiados números mágicos en el código. 17, 10, 11, 4, … Sería mejor ponerlos en public static final variables con nombres descriptivos, para aclarar el propósito de estos valores, téngalos juntos cerca de la parte superior del código para un control más fácil y flexibilidad para jugar.

Formateo

El código no sigue el formato común generado por la opción de formato automático de IDE comunes como Eclipse e IntelliJ . Sugiero reformatear todo, para que el código parezca más familiar y más fácil de leer para la mayoría de los programadores Java. En Eclipse, el atajo de teclado es Control-Shift-f

Comentarios

  • Si utilizo el formato automático, ¿es así como se formatean normalmente los programas?
  • Sí, esa ‘ es la forma común
  • Yo ‘ d agrego el » valor » atributo de Card no es necesario, solo la función getValue (), que solo necesita el rango para funcionar.

Responder

Devuelve el booleano directamente

Lo siguiente:

public static boolean isyesorno(String answer) { if(answer.equals("yes") || answer.equals("no")) { return true; } return false; } 

debería convertirse en:

public static boolean isyesorno(String answer) { return answer.equals("yes") || answer.equals("no")) } 

Lo mismo ocurre con public static boolean hasBlackJack(int handValue) y public static boolean isHitorStand(String hitter) y public static boolean checkBust(int handvalue) para este último, debe quitar la impresión de la función.

Usa ruedas ya existentes

Puedes mezclar el mazo usando el -in:

List<Cards> list = Arrays.asList(deck); Collections.shuffle(list); 

Comentarios

  • barajaré de esta manera mi mazo de ArrayList, o dame una nueva lista llamada lista que se baraja?
  • @Jared en su lugar ..

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *