Simulación de lanzamiento de dados en Java

Oct 30 2020

Para uno de mis laboratorios, necesito escribir un programa Java completo que simule un juego de apuestas de dados "muy simple". Las especificaciones son las siguientes: simula el lanzamiento de 3 dados. Si lanzas tres seises, ganas$20; if you throw three of any other value then you win $10; si tiras dos dados que tienen el mismo valor, entonces ganas$5. If none of the conditions above are met, then you would lose $1.

Ejecuciones de ejemplo: lanzamiento de dados

Dice 1 : 2     Dice 2 : 1    Dice 3 : 2
Congratulations : You threw TWO - 2s
You win $5 Dice 1 : 2 Dice 2 : 2 Dice 3 : 2 Congratulations : You threw THREE - 2s You win $10

Dice 1 : 4    Dice 2 : 6    Dice 3 : 3
Unfortunately : You did not throw anything of value
You lose $1

El código resultante que escribí para resolver este problema es el siguiente:

/**
 * SRN: 507-147-9
 */
public class Lab6_part3 {

    public static void main(String[] args) {

        // define vars
        int round = 1;
        int dice1, dice2, dice3;

        while (round <= 3) {

            dice1 = 1 + (int)(Math.random() * 6);
            dice2 = 1 + (int)(Math.random() * 6);
            dice3 = 1 + (int)(Math.random() * 6);

            System.out.println();
            System.out.println("Dice 1 : " + dice1 + "    Dice 2 : " + dice2 + "    Dice 3 : " + dice3);

            // Three of a kind
            if ((dice1 == dice2) && (dice1 == dice3)) { // 3-of-a-kind (D1)                              

                // Rolls three sixes
                if (dice1 == 6) {
                    System.out.println("Congratulations : You threw THREE - 6s");
                    System.out.println("You win $20");
                } else {
                    // Rolls three of anything else
                    System.out.println("Congratulations : You threw THREE - " + dice1 + "s");
                    System.out.println("You win $10"); } } // Two of a kind (PRINT "dice1") else if (dice1 == dice2 || dice1 == dice3) { System.out.println("Congratulations : You threw TWO - " + dice1 + "s"); System.out.println("You win $5");
            } 
            
            // Two of a kind (PRINT "dice2")
            else if (dice2 == dice1 || dice2 == dice3) {                              
                System.out.println("Congratulations : You threw TWO - " + dice2 + "s");
                System.out.println("You win $5"); } // Two of a kind (PRINT "dice3") else if (dice3 == dice1 || dice3 == dice2) { System.out.println("Congratulations : You threw TWO - " + dice3 + "s"); System.out.println("You win $5");
            } 
            // Did not throw anything of value
            else {
                System.out.println("Unfortunately : You did not throw anything of value");
                System.out.println("You lose $1");
            }
            round++;
        }
    }
}

El problema con el que me encuentro con este enfoque es que, aunque el código funciona como se supone que debe hacerlo, me gustaría tener una forma más sencilla de escribir los dos en su tipo en lugar de tener tres declaraciones "si". Mi objetivo en esto es crear una declaración de tres vías "o" en lugar de comparar el dado1 con el dado 2 y 3, y el dado 2 con el dado 1 y 3, etc.

else if (dice1 == dice2 || dice1 == dice3 || dice2 == dice3) {                              
    System.out.println("Congratulations : You threw TWO - " + somethings?? + "s");
    System.out.println("You win $5"); 

Si tuviera que hacer eso, ¿cómo podría imprimir el valor del par que he identificado?

Respuestas

7 Marc Oct 30 2020 at 14:37

Bienvenido a CodeReview. Con respecto a su problema, una solución rápida es:

else if (dice1 == dice2 || dice1 == dice3 || dice2 == dice3) {       
    int doubleNum = dice1 == dice2 ? dice1 : dice1 == dice3 ? dice1 : dice3;
    System.out.println("Congratulations : You threw TWO - " + doubleNum + "s");
    System.out.println("You win $5"); 
}

El operador ternario hace que el código sea más compacto, pero en este caso creo que es un poco menos legible que una cadena de if-else.

Algunas otras sugerencias:

  • El bucle while se puede reemplazar por un bucle for más conveniente. De:
    int round = 1;
    while(round<=3){
        //...
        round++;
    }
    
    A:
    for(int round = 1; round <= 3; round++) {
        //...
    }
    
  • Declarar varias variables en una línea int dice1, dice2, dice3;no se considera una buena práctica en Java.
  • En lugar de generar un aleatorio floaty luego convertirlo en un int, genere directamente un intcon ThreadLocalRandom.current().nextInt(6). Docs .
  • Puede imprimir la primera línea con System.out.format. De:
    System.out.println();
    System.out.println("Dice 1 : " + dice1 + "    Dice 2 : " + dice2 + "    Dice 3 : " + dice3);
    
    A:
    System.out.format("%nDice 1 :  %d    Dice 2 : %d    Dice 3 : %n", dice1, dice2, dice3)
    
  • Los nombres de las clases deben ser PascalCase . En lugar de Lab6_part3puedes nombrarlo Lab6Part3.
  • Declare una constante para el número de rondas, para facilitar el cambio. Por ejemplo:
    public class Lab6Part3 {
    
        private static final int ROUNDS = 3;
    
        public static void main(String[] args) {
            for(int round = 1; round <= ROUNDS; round++) {
                //...
            }
    }
    

Otro enfoque es generar tres números Random#intsy calcular las frecuencias:

Random r = new Random();
// Generate three random numbers from 1 to 6
IntStream diceRolls = r.ints(3, 1, 7);

// Generate map of frequencies
Map<Integer, Long> freq = diceRolls.boxed()
        .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));

boolean winner = false;
for (Entry<Integer, Long> entry : freq.entrySet()) {
    int number = entry.getKey();
    long times = entry.getValue();
    if (times == 3) {
        // Three of a kind ...
        winner = true;
    } else if (times == 2) {
        // Two of a kind...
        System.out.println("Congratulations : You threw TWO - " + number + "s");
        System.out.println("You win $5");
        winner = true;
    }
}
if(!winner) {
    // Did not throw anything of value
}
2 SavvasParastatidis Oct 30 2020 at 21:40

No es necesario tener 3 declaraciones else_if. Hay 3 escenarios en los que se encuentra en un caso de "dos contra uno". dice1 == dice2, dice1 == dice3, dice2 == dice3. Su primer bloque if atrapa 2 de estos escenarios. La única otra opción es si dice2 == dice3.

Si lo piensa, su tercer bloque else_If siempre crearía una evaluación verdadera en uno de los dos primeros bloques, por lo que su código nunca lo alcanzará.

// Two of a kind (PRINT "dice1")
else if (dice1 == dice2 || dice1 == dice3) {
    System.out.println("Congratulations : You threw TWO - " + dice1 + "s");
    System.out.println("You win $5"); } // Two of a kind (PRINT "dice2") else if (dice2 == dice3) { System.out.println("Congratulations : You threw TWO - " + dice2 + "s"); System.out.println("You win $5");
}
2 mdfst13 Oct 31 2020 at 18:20
        }
        
        // Two of a kind (PRINT "dice1")
        else if (dice1 == dice2 || dice1 == dice3) {

Por favor, no hagas esto. Si desea utilizar el medio abrazado else, escríbalo siempre en dos líneas consecutivas.

        }
        else if (dice1 == dice2 || dice1 == dice3) {
          // Two of a kind (PRINT "dice1")

Hay dos razones.

  1. Si quiero saber que la estructura está terminada, de esta manera puedo ver con solo mirar una línea más allá del }. Con su original, tendría que buscar un número arbitrario de líneas en la siguiente declaración. Esto se vuelve especialmente malo a medida que los bloques de comentarios se alargan. Es completamente posible escribir un comentario que sea más alto que una sola pantalla. Lo que significa que en lugar de simplemente desplazarme un poco más, necesitaría retroceder la página más allá del comentario para ver que hay un elsebloque.
  2. Si alguien usa algo que reformatea el código para poner el elseen la misma línea que el }, tendrá que mover el comentario de todos modos. Entonces, incluso si lo vuelven a formatear, el comentario se moverá. Esto causa confusión en el control de fuentes. Así que dígalo de la manera en que lo haría el reformateador desde el principio.

Los lenguajes de estilo C en general tienen un manejo de bloques confuso. Porque usan }ambos para terminar la estructura y solo para terminar el bloque en una estructura continua. Es decir, no tienen forma de decir que la ifestructura está terminando en lugar de continuar con elsealgo de algún tipo. Para compensar, tenemos que usar convenciones de codificación. Y una de las convenciones más simples, que trasciende el estilo, es no poner nunca una llave ( }o {) a más de una línea de la palabra clave asociada, si la hay.