Simulação de lançamento de dados em Java

Oct 30 2020

Para um de meus laboratórios, preciso escrever um programa Java completo que simule um jogo de apostas "muito simples". As especificações são as seguintes: simular o lançamento de 3 dados. Se você lançar três seis, então você ganha$20; if you throw three of any other value then you win $10; se você jogar dois dados com o mesmo valor, você ganha$5. If none of the conditions above are met, then you would lose $1

Exemplo de execuções: lançamento 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

O código resultante que escrevi para resolver esse problema é o seguinte:

/**
 * 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++;
        }
    }
}

O problema que estou encontrando com essa abordagem é que, embora o código funcione como deve, gostaria de ter uma maneira mais simples de escrever os dois-de-um-tipo em vez de ter três instruções "if". Meu objetivo nisso é criar uma declaração "ou" de três vias em vez de comparar o dado1 com o dado 2 e 3 e o dado 2 com o dado 1 e 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"); 

Se eu fizesse isso, como poderia imprimir o valor do par que identifiquei?

Respostas

7 Marc Oct 30 2020 at 14:37

Bem-vindo ao CodeReview. Em relação ao seu problema, uma solução rápida é:

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

O operador ternário torna o código mais compacto, mas neste caso acho que é um pouco menos legível do que uma cadeia de if-else.

Algumas outras sugestões:

  • O loop while pode ser substituído por um loop for mais conveniente. A partir de:
    int round = 1;
    while(round<=3){
        //...
        round++;
    }
    
    Para:
    for(int round = 1; round <= 3; round++) {
        //...
    }
    
  • Declarar várias variáveis ​​em uma linha int dice1, dice2, dice3;não é considerado uma boa prática em Java.
  • Em vez de gerar um aleatório floate depois lançá-lo em um int, gere diretamente um intcom ThreadLocalRandom.current().nextInt(6). Docs .
  • Você pode imprimir a primeira linha com System.out.format. A partir de:
    System.out.println();
    System.out.println("Dice 1 : " + dice1 + "    Dice 2 : " + dice2 + "    Dice 3 : " + dice3);
    
    Para:
    System.out.format("%nDice 1 :  %d    Dice 2 : %d    Dice 3 : %n", dice1, dice2, dice3)
    
  • Os nomes das classes devem ser PascalCase . Em vez de Lab6_part3você pode nomeá-lo Lab6Part3.
  • Declare uma constante para o número de rodadas, para facilitar a alteração. Por exemplo:
    public class Lab6Part3 {
    
        private static final int ROUNDS = 3;
    
        public static void main(String[] args) {
            for(int round = 1; round <= ROUNDS; round++) {
                //...
            }
    }
    

Outra abordagem é gerar três números Random#intse calcular as frequências:

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

Você não precisa ter 3 instruções else_if. Existem 3 cenários em que você está em um caso "dois-por-um-tipo". dice1 == dice2, dice1 == dice3, dice2 == dice3. Seu primeiro bloco if pega 2 desses cenários. A única outra opção é se dice2 == dice3.

Se você pensar sobre isso, seu terceiro bloco else_If sempre criaria uma avaliação verdadeira em um dos primeiros dois blocos, então seu código nunca o alcançará.

// 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, não faça isso. Se você quiser usar o meio afago else, escreva sempre em duas linhas consecutivas.

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

Existem duas razões.

  1. Se eu quiser saber se a estrutura está pronta, posso ver dessa forma apenas olhando uma linha após o }. Com o seu original, eu teria que olhar um número arbitrário de linhas para a próxima declaração. Isso fica especialmente ruim à medida que os blocos de comentários ficam mais longos. É perfeitamente possível escrever um comentário mais alto do que uma única tela. O que significa que, em vez de simplesmente rolar um pouco mais, eu precisaria passar a página do comentário para ver se há um elsebloco.
  2. Se alguém usar algo que reformata o código para colocar o elsena mesma linha do }, terá que mover o comentário de qualquer maneira. Então, mesmo que formatem de volta, o comentário será movido. Isso causa confusão no controle de origem. Então, coloque da maneira que o reformatador faria desde o início.

Linguagens de estilo C em geral têm manipulação de blocos confusa. Porque eles usam }para finalizar a estrutura e apenas para finalizar o bloco em uma estrutura contínua. Ou seja, eles não têm como dizer que a ifestrutura está terminando versus continuando com elsealgum tipo. Para compensar, temos que usar convenções de codificação. E uma das convenções mais simples, que transcende o estilo, é nunca colocar uma chave ( }ou {) mais de uma linha de distância da palavra-chave associada, se houver.