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
Randomrecebido no construtor passa a ser guardado, para também sortear os encontros. Assim, os testes continuam podendo fixar uma semente.
- random: o
- 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 umTreinador: o pacotemapanão deve depender do pacotepokemon.
- A cada movimento, há 50% de chance (
- encontrouPokemon: indica se o treinador encontrou um Pokémon na posição informada.
Treinador:
- Atributos:
- pokebolas: armazena as pokébolas do treinador, até
MAX_POKEBOLAS(6).
- pokebolas: armazena as pokébolas do treinador, até
- Métodos:
- adicionarPokemon (já existe): guarda um Pokémon na equipe, se houver espaço. Retorna
falsecaso contrário. - equipeCheia: indica se a equipe já tem
MAX_POKEMONSPokémon. - adicionarPokebola: guarda uma pokébola, se houver espaço. Retorna
falsecaso 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
nullse o treinador não tiver pokébolas.
- temPokebola: indica se o treinador ainda tem pokébolas.
- adicionarPokemon (já existe): guarda um Pokémon na equipe, se houver espaço. Retorna
Jogo:
- Atributos:
- gerador: o
GeradorDePokemonusado para criar os Pokémon selvagens.
- gerador: o
- No construtor, crie o gerador e entregue ao treinador uma pokébola de cada tipo (
Pokebola,GreatBall,UltraBalleMasterBall). - 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
Batalhaentre 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 umRandom. Com uma semente fixa, os Pokémon gerados são sempre os mesmos, o que permite escrever testes (vejaTesteGeradorDePokemon).
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,Batalhanã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
truese o Pokémon foi capturado.- Se a equipe do treinador estiver cheia, nenhuma pokébola é arremessada, o método retorna
falsee a batalha continua. - Se o treinador não tiver pokébolas, o método retorna
falsee a batalha termina. - Se o Pokémon escapar e a pokébola arremessada era a última, a batalha termina.
- Se a equipe do treinador estiver cheia, nenhuma pokébola é arremessada, o método retorna
- fugir: encerra a batalha.
- terminou: indica se a batalha terminou.
- 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
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
Randomusado 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 umnew Random().Pokebola(Random random): sem modificador de acesso. Cria uma Pokébola comum com oRandominformado. 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 viasuper(...).- Assim como em
MapaeGeradorDePokemon, um construtor delega ao outro comthis(...).
- 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 constantePokemon.NIVEL_MAXIMO(100).
- Utilize a seguinte fórmula:
- capturar: usa o atributo
randompara sortear um número aleatório entre 0 e 1 (não crie umnew Random()dentro do método). Se ele for menor que a chance de captura, o Pokémon é capturado.- Retorne
truese a captura for bem-sucedida,falsecaso contrário.
- Retorne
- Construtores:
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 oRandom.- 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.