Exercício: Capturando Pokémon com Herança

Objetivo

Implementar a funcionalidade de captura de Pokémon no jogo. A lógica de movimentação no mapa já está funcionando, e as classes Pokemon e GeradorDePokemon estão disponíveis. O ponto de partida é a tag v0.2.1 do repositório. Sua tarefa é implementar as classes relacionadas às Pokébolas, que serão usadas pelo treinador para tentar capturar os Pokémon encontrados. Para isso, utilizaremos Herança, já que existem diversos tipos de Pokébolas.

Descrição

Neste exercício você irá criar as classes Batalha, Pokebola, GreatBall, UltraBall e MasterBall. Além disso, será necessário alterar as classes Mapa, Treinador, Jogo e JogoConsole.

No diagrama abaixo, # indica um membro protected, ~ indica um membro visível apenas no pacote (sem modificador de acesso) e membros sublinhados são static. As classes Direcao, Posicao, Pokemon e GeradorDePokemon já existem e não precisam ser alteradas.

classDiagram
    class Mapa {
        - double PROBABILIDADE_ENCONTRO$
        - char[][] mapa
        - int largura
        - int altura
        - Random random
        + Mapa(int largura, int altura)
        + Mapa(int largura, int altura, Random random)
        - inicializarMapa(Random r) void
        + renderizar(Posicao posicaoTreinador) String
        + ePosicaoValida(Posicao posicao) boolean
        + encontrouPokemon(Posicao posicao) boolean
    }

    class Posicao {
        - int x
        - int y
        + Posicao(int x, int y)
        + getX() int
        + getY() int
        + deslocar(int dx, int dy) Posicao
    }

    class Direcao {
        <<enumeration>>
        CIMA
        BAIXO
        ESQ
        DIR
        + aplicarEm(Posicao origem) Posicao
    }

    class Treinador {
        - int MAX_POKEMONS$
        - int MAX_POKEBOLAS$
        - String nome
        - Posicao posicao
        - Pokemon[] pokemons
        - int qtdPokemons
        - Pokebola[] pokebolas
        - int qtdPokebolas
        + Treinador(String nome)
        + getNome() String
        + getPosicao() Posicao
        + moverPara(Posicao destino) void
        + getPokemons() List~Pokemon~
        + adicionarPokemon(Pokemon pokemon) boolean
        + equipeCheia() boolean
        + adicionarPokebola(Pokebola pokebola) boolean
        + arremessarPokebola() Pokebola
        + temPokebola() boolean
    }

    class Jogo {
        - Treinador treinador
        - Mapa mapa
        - GeradorDePokemon gerador
        + Jogo(String nome)
        + getTreinador() Treinador
        + renderizarMapa() String
        + mover(Direcao direcao) void
        + encontrouPokemon() boolean
        + iniciarBatalha() Batalha
    }

    class JogoConsole {
        - Jogo jogo
        + JogoConsole(Jogo jogo)
        + iniciar() void
    }

    class GeradorDePokemon {
        - String[] NOMES$
        - int NIVEL_MIN_SELVAGEM$
        - int NIVEL_MAX_SELVAGEM$
        - Random random
        + GeradorDePokemon()
        + GeradorDePokemon(Random random)
        + gerar() Pokemon
    }

    class Batalha {
        - Treinador treinador
        - Pokemon pokemon
        - boolean encerrada
        + Batalha(Treinador treinador, Pokemon pokemon)
        + getPokemon() Pokemon
        + tentarCaptura() boolean
        + fugir() void
        + terminou() boolean
    }

    class Pokemon {
        + int NIVEL_MAXIMO$
        - String nome
        - int nivel
        - int hpMax
        - int hp
        + Pokemon(String nome)
        + Pokemon(String nome, int nivel)
        + getNome() String
        + getNivel() int
        + getHp() int
        + setHp(int hp) void
    }

    class Pokebola {
        - double CHANCE_MINIMA$
        - String nome
        - double taxaBase
        - Random random
        + Pokebola()
        ~ Pokebola(Random random)
        # Pokebola(String nome, double taxaBase, Random random)
        + getNome() String
        # getTaxaDeCaptura(Pokemon pokemon) double
        + capturar(Pokemon pokemon) boolean
    }

    class GreatBall {
        + GreatBall()
        ~ GreatBall(Random random)
    }

    class UltraBall {
        + UltraBall()
        ~ UltraBall(Random random)
    }

    class MasterBall {
        + MasterBall()
        ~ MasterBall(Random random)
        # getTaxaDeCaptura(Pokemon pokemon) double
    }


    %% Relações
    JogoConsole "1" --> "1" Jogo : usa
    JogoConsole ..> Batalha : conduz
    Jogo "1" --> "1" Mapa : usa
    Jogo "1" --> "1" Treinador : possui
    Jogo ..> Batalha : cria
    Jogo "1" --> "1" GeradorDePokemon : usa
    GeradorDePokemon ..> Pokemon : cria
    Treinador "1" --> "1" Posicao : está em
    Mapa ..> Posicao : valida
    Direcao ..> Posicao : desloca
    Treinador "1" --> "0..6" Pokemon : treina
    Treinador "1" --> "0..*" Pokebola : possui
    Batalha "1" --> "1" Treinador : envolve
    Batalha "1" --> "1" Pokemon : envolve
    Pokebola ..> Pokemon : captura
    Pokebola <|-- GreatBall
    Pokebola <|-- UltraBall
    Pokebola <|-- MasterBall

Mapa:

  • Atributos:
    • random: o Random recebido no construtor passa a ser guardado, para também sortear os encontros. Assim, os testes continuam podendo fixar uma semente.
  • Métodos:
    • encontrouPokemon: indica se o treinador encontrou um Pokémon na posição informada.
      • A cada movimento, há 50% de chance (PROBABILIDADE_ENCONTRO) de encontrar um Pokémon.
      • No entanto, um Pokémon só pode ser encontrado em posições com grama ('w').
      • O método recebe uma Posicao, e não um Treinador: o pacote mapa não deve depender do pacote pokemon.

Treinador:

  • Atributos:
    • pokebolas: armazena as pokébolas do treinador, até MAX_POKEBOLAS (6).
  • Métodos:
    • adicionarPokemon (já existe): guarda um Pokémon na equipe, se houver espaço. Retorna false caso contrário.
    • equipeCheia: indica se a equipe já tem MAX_POKEMONS Pokémon.
    • adicionarPokebola: guarda uma pokébola, se houver espaço. Retorna false caso contrário.
    • arremessarPokebola: remove e devolve a pokébola que será utilizada na captura.
      • Uma pokébola só pode ser utilizada uma única vez.
      • Retorna null se o treinador não tiver pokébolas.
    • temPokebola: indica se o treinador ainda tem pokébolas.

Jogo:

  • Atributos:
    • gerador: o GeradorDePokemon usado para criar os Pokémon selvagens.
  • No construtor, crie o gerador e entregue ao treinador uma pokébola de cada tipo (Pokebola, GreatBall, UltraBall e MasterBall).
  • Métodos:
    • encontrouPokemon: pergunta ao mapa se há um Pokémon na posição atual do treinador.
    • iniciarBatalha: usa o gerador para criar um Pokémon selvagem e devolve uma nova Batalha entre ele e o treinador.

GeradorDePokemon (já existe):

  • Cria os Pokémon selvagens: sorteia um nome de uma lista fixa e um nível entre 1 e 30.
  • A faixa de níveis é limitada a 30 para que a diferença entre as pokébolas apareça no jogo: com níveis próximos de Pokemon.NIVEL_MAXIMO, a chance de captura de qualquer pokébola comum cai para perto de 5%.
  • Assim como Mapa, o gerador pode receber um Random. Com uma semente fixa, os Pokémon gerados são sempre os mesmos, o que permite escrever testes (veja TesteGeradorDePokemon).

Batalha:

  • Essa classe controla a interação entre Treinador e Pokemon, simulando uma batalha.
  • Na versão atual, existem duas opções: tentar capturar o Pokémon ou fugir.
  • Assim como Jogo, Batalha não lê do teclado nem imprime nada: quem mostra as opções e lê a escolha do usuário é JogoConsole.
  • Atributos:
    • treinador: o treinador em batalha.
    • pokemon: o Pokémon selvagem em batalha.
    • encerrada: indica se a batalha terminou.
  • Métodos:
    • tentarCaptura: arremessa uma pokébola do treinador no Pokémon. Se a captura der certo, o Pokémon passa a fazer parte da equipe do treinador. Retorna true se o Pokémon foi capturado.
      • Se a equipe do treinador estiver cheia, nenhuma pokébola é arremessada, o método retorna false e a batalha continua.
      • Se o treinador não tiver pokébolas, o método retorna false e a batalha termina.
      • Se o Pokémon escapar e a pokébola arremessada era a última, a batalha termina.
    • fugir: encerra a batalha.
    • terminou: indica se a batalha terminou.

Fluxo de uma batalha:

stateDiagram-v2
    direction LR
    inicio: Pokemon apresentado
    usuario: Esperando decisão
    state decisao <<choice>>
    state capturou <<choice>>
    [*] --> inicio
    inicio --> usuario: Treinador escolhe o que fazer
    usuario --> decisao
    decisao --> [*]: Fugir
    decisao --> capturou: Capturar (arremessa pokébola)
    decisao --> usuario: Capturar com a equipe cheia (nada é arremessado)
    capturou --> [*]: Pokémon escapou e acabaram as pokébolas
    capturou --> usuario: Pokémon escapou, mas o treinador tem mais pokébolas
    capturou --> [*]: Pokémon foi capturado

Pokebola:

  • Atributos:
    • nome: nome da Pokébola.
    • taxaBase: taxa base de captura (entre 0.0 e 1.0).
    • random: o Random usado no sorteio da captura, recebido no construtor.
    • CHANCE_MINIMA: constante com a chance mínima de captura (0.05).
  • Métodos:
    • Construtores:
      • Pokebola(): cria uma Pokébola comum (nome “Pokébola”, taxa base = 0.4), com um new Random().
      • Pokebola(Random random): sem modificador de acesso. Cria uma Pokébola comum com o Random informado. Existe para os testes, que ficam no mesmo pacote e podem fixar uma semente.
      • Pokebola(String nome, double taxaBase, Random random): protected, usado pelas subclasses via super(...).
      • Assim como em Mapa e GeradorDePokemon, um construtor delega ao outro com this(...).
    • getTaxaDeCaptura: para um dado Pokémon, calcula a chance de capturá-lo.
      • Utilize a seguinte fórmula:
        • \[taxaBase - taxaBase \cdot \cfrac{nivelPokemon}{NIVEL\_MAXIMO} + CHANCE\_MINIMA\]
        • NIVEL_MAXIMO é a constante Pokemon.NIVEL_MAXIMO (100).
    • capturar: usa o atributo random para sortear um número aleatório entre 0 e 1 (não crie um new Random() dentro do método). Se ele for menor que a chance de captura, o Pokémon é capturado.
      • Retorne true se a captura for bem-sucedida, false caso contrário.

Classes Derivadas de Pokebola:

  • Crie as seguintes subclasses de Pokebola. Cada uma tem dois construtores, como Pokebola: um público, sem parâmetros, e um sem modificador de acesso, que recebe o Random.
    • Great Ball: taxa base = 0.6.
    • Ultra Ball: taxa base = 0.8.
    • Master Ball: taxa base = 1.0, mas sempre captura. Para isso, sobrescreva getTaxaDeCaptura.

This site uses Just the Docs, a documentation theme for Jekyll.