0

Pedra Papel e Tesoura - Java

#Java
Leandro Paixão
Leandro Paixão

A proposta aqui é simples recriarmos o velho jogo Pedra Papel e Tesoura de uma forma um pouco diferente. Sempre que eu fazia a proposta deste exercício para meus alunos, as entregas eram das mais variadas possíveis, desde Cases a If's encadeados.

Para este jogo, quero tratar dois itens:

  1. Validar a vitória e/ou derrota com um cálculo matemático
  2. Criar o jogo para dois idiomas: Português e Francês.


Item 1

O jogador irá jogar contra a máquina, então utilizarei um número randômico para que isso ocorra.

A lógica que vou utilizar para encontrar o resultado é a seguinte:

  • Pedra equivale a 1
  • Papel equivale a 2
  • Tesoura equivale a 3

Com isso podemos montar então os seguintes cálculos:

  • Pedra + Papel = 3
  • Pedra + Tesoura = 4
  • Papel + Tesoura = 5

Então se eu escolher Papel e a soma for 3, então eu ganhei, se eu escolher Pedra e a soma for 4 eu ganhei, se eu escolher Tesoura e a soma for 5 então eu ganhei. Qualquer resultado diferente disso então eu perdi!


Ah mas o empate como fica? Pedra + Pedra = 2, Papel + Papel = 4 e Tesoura + Tesoura = 10. Bom nessa lógica ele não consigo fazer uma comparação, mas e se fizermos ao contrário, diminuindo um valor do outro: em todos os casos obtemos 0!


Item 2

Para questão de idiomas, todo o jogo será traduzido, existem várias formas de fazer isso, mas vou adotar o conceito de Interface para este jogo.

Inicialmente o jogo iniciará em Francês e terá uma opção para trocar automaticamente o idioma para Português, que também terá a opção para trocar para Francês e assim por diante.


Desenvolvimento

Para o desenvolvimento deste jogo estou usando a IDE do IntelliJ, mas você pode usar qualquer IDE de sua preferência. Criaremos uma nova aplicação de console.

Primeiramente começaremos trabalhando no item 2, criando assim a Interface para os idiomas, então criaremos uma interface com o seguinte nome ILangue. A nossa interface deverá conter alguns métodos e ficará da seguinte forma:


public interface ILangue {

    public String menu();
    public String optionIncorrect();
    public String victoire();
    public String defaite();
    public String cravate();
    public String joueur();
    public String machine();
    public String sortir();
    public String erreur();
}


Ao implementarmos os métodos iremos entender o que cada um significa, pois estão em francês.

Agora criaremos uma nova classe que implementa a nossa interface criada, o nome da classe será Portugues, ficando da seguinte forma:


public class Portugues implements ILangue{
    @Override
    public String menu() {
        String str = "==== Bem-vindo ao jogo JanKenPon ====\n"
        .concat("Por favor, escolha uma opção:\n")
        .concat("1 - Pedra (JAN)\n")
        .concat("2 - Papel (KEN)\n")
        .concat("3 - Tesoura (PON)\n")
        .concat("4 - Sair\n")
        .concat("5 - Mudar o idioma (PT-BR -> FR-CA)\n")
        .concat("Sua opção: ");

        return str;
    }

    @Override
    public String optionIncorrect() {
        return "Opção incorreta";
    }

    @Override
    public String victoire() {
        return "Parabéns! Você ganhou.";
    }

    @Override
    public String defaite() {
        return "Ohhhh! Infelizmente, você não ganhou.";
    }

    @Override
    public String cravate() {
        return "Ohhhh! Um empate!";
    }

    @Override
    public String joueur() {
        return "Opção do jogador ";
    }

    @Override
    public String machine() {
        return "Opção da máquina ";
    }

    @Override
    public String sortir() {
        return "Até mais!";
    }

    @Override
    public String erreur(){
        return "Erro na aplicação, ela será fechada.";
    }
}


Com as implementações podemos ter uma idéia agora de como se comportará cada método.

No primeiro método, menu, estou utilizando o método concat para concatenar os textos do menu.


Agora vamos criar a nossa próxima classe que conterá a mesma implementação da nossa interface ILangue, mas agora os retornos serão em Francês. A classe se chamará Francais, que ficará no seguinte formato:

public class Francais implements  ILangue{
    @Override
    public String menu() {
        String str = "==== Bienvenue dans le jeu JanKenPon ====\n"
        .concat("S'il vous plaît, choisir une option:\n")
        .concat("1 - Pierre (JAN)\n")
        .concat("2 - Papier (KEN)\n")
        .concat("3 - Ciseaux (PON)\n")
        .concat("4 - Sortir\n")
        .concat("5 - Changer la langue (FR-CA -> PT-BR)\n")
        .concat("Votre option: ");

        return str;
    }

    @Override
    public String optionIncorrect() {
        return "Option incorrect";
    }

    @Override
    public String victoire() {
        return "Félicitations! Vous avez gangé.";
    }

    @Override
    public String defaite() {
        return "Ohhhh! Malheureusement, vous n'avez pas gagné.";
    }

    @Override
    public String cravate() {
        return "Ohhhh! Une cravate!";
    }

    @Override
    public String joueur() {
        return "Option de joueur ";
    }

    @Override
    public String machine() {
        return "Option Machine ";
    }

    @Override
    public String sortir() {
        return "À bientôt!";
    }

    @Override
    public String erreur(){
        return "Erreur d'application, elle sera fermée.";
    }
}


A lógica utilizada aqui é a mesma da classe Portugues e com isso encerramos este item.


Para identificarmos as opções de Pedra, Papel e Tesoura utilizaremos o conceito de Enum, que é utilizado para criar estruturas de dados organizadas. O nome da nossa classe enum será JanKenPon, que é PedraPapelTesoura. Aproveitaremos e adicionaremos mais dois itens na lista, que é a opção Sair e Mudar Idioma:

public enum JanKenPon {
    JAN(1),KEN(2),PON(3), SORTIR(4), LANGUE(5);
}

Associei já um valor para cada item pois precisaremos realizar a soma depois na nossa classe Main.

Como precisaremos realizar a soma, então será necessário adicionar mais dois métodos nesta classe enum, o método construtor e um método para retornar o valor correspondente, no caso retornar 1, 2, 3 e assim por diante:

public enum JanKenPon {
    JAN(1),KEN(2),PON(3), SORTIR(4), LANGUE(5);

    private int valor;
    JanKenPon(int valor) {
        this.valor = valor;
    }

    public int getValor(){
        return this.valor;
    }
}

Nossa classe está quase pronta.

Mas teremos um pequeno problema no futuro, como o usuário irá escolher uma opção para poder jogar, então a nossa classe enum deverá receber um valor inteiro e retornar a opção correspondente, por exemplo se for informado 2, deverá retornar o KEN, certo? Para resolver este problema faremos um overload do método getValor(). Adicionaremos um foreach para percorrer todos os itens da classe enum e retornar o valor quando for encontrado.

No final nossa classe ficará da seguinte forma:

public enum JanKenPon {
    JAN(1),KEN(2),PON(3), SORTIR(4), LANGUE(5);

    private int valor;
    JanKenPon(int valor) {
        this.valor = valor;
    }

    public int getValor(){
        return this.valor;
    }

    public static JanKenPon getValor(int valor){
        for (JanKenPon jkp : values()) {
            if (jkp.getValor() == valor){
                return jkp;
            }
        }
        return null;
    }
}


Agora vamos, juntar tudo criado a adicionar a lógica na nossa classe Main., lembrando que esta classe é criada pela IDE do Intellij quando criamos um projeto console.

Por opção vou criar um método chamado ecrire que receberá um objeto qualquer, que neste projeto será somente texto, e exibira a informação passada na tela, sim é uma "cópia" do método System.out.println(), optei por isso para justamente não ficar tão poluído a nossa tela. É uma opção minha, você pode utilizar a forma de impressão normal se quiser.

static void ecrire(Object obj){
    System.out.println(obj);
}


No início do nosso método Main precisamos declarar algumas variáveis que utilizaremos no decorrer do nosso sistema, sendo um scanner para captar o que foi digitado, um random para um número aleatório que será a escolha da máquina, a opção que foi escolhida pelo usuário (Jan, Ken, Pon) e uma que conterá o resultado da soma.

Scanner sci = new Scanner(System.in);
Random rand = new Random();

JanKenPon option = null;
int resultat = 0;


Agora precisamos instanciar a nossa classe de idioma, que será responsável por exibir as informações necessárias.

ILangue langue = new Francais();

Estou iniciando a nossa variável com o idioma francês, mas você pode utilizar o português também.


A partir deste ponto toda a nossa lógica ficará dentro de um try/except/finally, mas não farei uma validação se a pessoa informar algo que não seja um número.

Outra idéia é que o jogo fique rodando até que o usuário opte por sair do jogo, então utilizarei o laço de repetição do/while, onde a condição do while será a variável option diferente de SORTIR (sair).

try {
    do {

    } while (option != JanKenPon.SORTIR);

    }catch (Exception e) {
        ecrire(langue.erreur());
        e.printStackTrace();
    } finally {
        sci.close();
    }
}


Note que no exception estamos somente informando que houve um erro e exibindo o printStackTrace() do erro. A aplicação será encerrada nesse momento.

No finally, estaremos fechando o scanner, para que não fique pendurado na memória e de algum erro posterior para nós.


Dentro do nosso laço agora vamos exibir o menu e esperar o usuário informar a sua opção. Também já podemos adicionar o "tchauzinho" caso o usuário opte por sair do sistema

try {
    do {
        ecrire(langue.menu());

        option = JanKenPon.getValor(sci.nextInt());


    } while (option != JanKenPon.SORTIR);

    ecrire(langue.sortir());

}catch (Exception e) {
    ecrire(langue.erreur());
    e.printStackTrace();
} finally {
    sci.close();
}

Se retornarmos ao nosso método getValor, na nossa interface lembraremos que se retornar NULO (null) então foi informada uma opção inválida.

try {
    do {
        ecrire(langue.menu());

        option = JanKenPon.getValor(sci.nextInt());

        if (option == null) {
            ecrire(langue.optionIncorrect());
            ecrire("");
        }

    } while (option != JanKenPon.SORTIR);

    ecrire(langue.sortir());

}catch (Exception e) {
    ecrire(langue.erreur());
    e.printStackTrace();
} finally {
    sci.close();
}


Na sequência encadearemos um else if, verificando se a opção escolhida foi de trocar o idioma. Verificaremos se a instancia da variável langue e trocaremos por uma oposta.

try {
    do {
        ecrire(langue.menu());

        option = JanKenPon.getValor(sci.nextInt());

        if (option == null) {
            ecrire(langue.optionIncorrect());
            ecrire("");
        } else if (option == JanKenPon.LANGUE) {
            if (langue.getClass() == Francais.class){
                langue = new Portugues();
            } else {
                langue = new Francais();
            }
        }

    } while (option != JanKenPon.SORTIR);

    ecrire(langue.sortir());

}catch (Exception e) {
    ecrire(langue.erreur());
    e.printStackTrace();
} finally {
    sci.close();
}

Aqui vemos a vantagem de usar uma Interface, pois independente da classe referenciada não precisamos trocar a nossa variável.


Agora iremos encadear mais um if que será onde o jogo em si ocorrerá. Neste ponto o que nos interessa é que a opção escolhida seja menor que 4. Também já podemos tratar a escolha da máquina

try {
    do {
        ecrire(langue.menu());

        option = JanKenPon.getValor(sci.nextInt());

        if (option == null) {
            ecrire(langue.optionIncorrect());
            ecrire("");
        } else if (option == JanKenPon.LANGUE) {
            if (langue.getClass() == Francais.class){
                langue = new Portugues();
            } else {
                langue = new Francais();
            }
        } else if (option.getValor() < 4) {

            int choixRand = rand.nextInt(3);
            choixRand += 1;

            var optionMachine = JanKenPon.getValor(choixRand);
        }

    } while (option != JanKenPon.SORTIR);

    ecrire(langue.sortir());

}catch (Exception e) {
    ecrire(langue.erreur());
    e.printStackTrace();
} finally {
    sci.close();
}

Na escolha da máquina, pegamos o valor no random e adicionamos mais 1, pois o método nextInt(3), retorna 3 valores iniciando pelo 0, então neste caso nos retornaria entre [0,1,2]. O zero para nós é um problema, pois no inicio do artigo consideramos o zero como empate.

Resolvido isso, então vamos para a próxima parte que é a soma das duas escolhas e a validação se usuário ganhou ou não

try {
    do {
        ecrire(langue.menu());

        option = JanKenPon.getValor(sci.nextInt());

        if (option == null) {
            ecrire(langue.optionIncorrect());
            ecrire("");
        } else if (option == JanKenPon.LANGUE) {
            if (langue.getClass() == Francais.class){
                langue = new Portugues();
            } else {
                langue = new Francais();
            }
        } else if (option.getValor() < 4) {

            int choixRand = rand.nextInt(3);
            choixRand += 1;

            var optionMachine = JanKenPon.getValor(choixRand);

            resultat = option.getValor() + optionMachine.getValor();

            if ((option == JanKenPon.JAN && resultat == 4) ||
                (option == JanKenPon.KEN && resultat == 3) ||
                (option == JanKenPon.PON && resultat == 5)) {
                ecrire(langue.victoire());
            } 
        }

    } while (option != JanKenPon.SORTIR);

    ecrire(langue.sortir());

}catch (Exception e) {
    ecrire(langue.erreur());
    e.printStackTrace();
} finally {
    sci.close();
}


Até este momento conseguimos verificar se o usuário ganhou. Agora iremos encadear um else/if que é a validação do empate. Se não passar nesta validação então consideraremos que o usuário perdeu.

Por último iremos exibir as opções escolhida do usuário e da máquina, ficando mais ou menos da seguinte forma:

try {
    do {
        ecrire(langue.menu());

        option = JanKenPon.getValor(sci.nextInt());

        if (option == null) {
            ecrire(langue.optionIncorrect());
            ecrire("");
        } else if (option == JanKenPon.LANGUE) {
            if (langue.getClass() == Francais.class){
                langue = new Portugues();
            } else {
                langue = new Francais();
            }
        } else if (option.getValor() < 4) {

            int choixRand = rand.nextInt(3);
            choixRand += 1;

            var optionMachine = JanKenPon.getValor(choixRand);

            resultat = option.getValor() + optionMachine.getValor();

            if ((option == JanKenPon.JAN && resultat == 4) ||
                (option == JanKenPon.KEN && resultat == 3) ||
                (option == JanKenPon.PON && resultat == 5)) {
                ecrire(langue.victoire());
            } else if ((option.getValor() - optionMachine.getValor()) == 0) {
                ecrire(langue.cravate());
            } else {
                ecrire(langue.defaite());
            }

            ecrire(langue.joueur() + option);
            ecrire(langue.machine() + optionMachine);
            ecrire("");
        }

    } while (option != JanKenPon.SORTIR);

    ecrire(langue.sortir());

}catch (Exception e) {
    ecrire(langue.erreur());
    e.printStackTrace();
} finally {
    sci.close();
}


Por aqui encerramos o nosso jogo, até onde realizei os testes não encontrei erros, mas se você achar algum fique a vontade para corrigir.

Deixe também seus comentários, criticas e sugestões.

O código fonte está disponível no github.

Para nos conectarmos e trocarmos informações Leandro Paixão


Um abraço e até a próxima.


0
5

Comentários (1)

1
Carlos Silva

Carlos Silva

28/03/2021 06:01

Fala, Leandro Paixão,


Muito boa sua didática e estratégia de modelagem do algorítimo, vou tirar um tempinho hoje para replicar esse exemplo com c#.


Parabéns pelo post e disponibilidade me compartilhar conhecimento.


Um abraço,


Carlos Antonio.

None

Brasil