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:
- Validar a vitória e/ou derrota com um cálculo matemático
- 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.
Carlos Silva
28/03/2021 06:01