autor: Michał Powała
repositório de origem: simulação de luta de dois heróis
simulação de luta de dois heróis
Este repositório contém soluções para determinada tarefa (abaixo neste arquivo) abordadas de diferentes maneiras. Cada abordagem (finalizada) possui seu próprio branch e tag própria. O aplicativo pode ser executado no formulário CLI.
O objetivo deste repositório é (para mim) praticar código limpo e um pouco de bom design de código, que segue:
- Quatro pilares do OOP (para mim, se você focar no SOLID, eles surgiram sozinhos)
- Abstração
- Herança
- Encapsulamento
- Polimorfismo
- Princípios SÓLIDOS
- Responsabilidade única (cada membro da minha turma tem sua própria tarefa única)
- Aberto/fechado (quando comecei a brincar com abordagens diferentes, a maioria dos meus internos estavam intactos, só precisava adicionar novas funcionalidades)
- Princípio de substituição de Liskov (meu código usa abstração quando necessário e não tem conhecimento da implementação subjacente; veja as classes Unit e Colleague, por exemplo)
- Princípio de segregação de interface (muitas interfaces pequenas em vez de uma grande)
- Princípio de inversão de dependência (o único lugar onde as classes são instanciadas é o script run.php e as fábricas, e veja o Randomizer - ele pode ser ridicularizado! Eu não uso rand() diretamente em nenhuma classe)
- Padrões de projeto
- Decorador (src/Modificador)
- Método de fábrica
- Padrão Observador (ramificação adequada)
- Padrão Mediador (ramificação adequada)
- Padrões táticos DDD
- Objeto de valor (src/Propriedade)
- Agregado (src/Unit - cada pedaço de lógica que poderia ser encapsulado aqui está aqui)
- Serviço de Domínio (TurnService)
- Serviço de aplicativo (GamePlayService)
- Fábricas (fábricas)
- Eventos (src/Event) (sim, eu poderia usar o barramento de eventos)
- Acoplamento fraco (decorre do SOLID)
Registradores de ações/eventos, registradores de erros, impressora (leitora) e lógica central estão todos fracamente acoplados entre si - TDD
Para ser honesto, não é TDD puro onde você primeiro implementa testes que falham e depois o código-fonte após o qual os testes passam (acho que não é prático), mas depois de criar algum nível de abstração ou uma classe eu testei imediatamente e corrigi todos os pequenos bugs se erros necessários ou lógicos que eu não tinha pensado. É por isso que quando implementei o script run.php (que é o ponto de entrada para o aplicativo) e fiz isso como último passo, o código funcionou! Acho que isso é bom o suficiente e o TDD puro não é necessário.
Como correr
- Execute docer:
docker-compose up -d
- execute o aplicativo:
docker-compose exec cli php run.php
- execute testes de unidade
docker-compose exec cli vendor/bin/phpunit tests/
TAREFA
Crie uma simulação de batalha entre Orderus e a fera. Cada vez que a batalha começa, Beast e Orderus são gerados com estatísticas diferentes que seguem as seguintes regras:
- Ordene-nos:
- Saúde: 70 - 100
- Força: 70 - 80
- Defesa: 45 – 55
- Velocidade: 40 – 50
- Sorte: 10% - 30% (0% significa sem sorte, 100% de sorte o tempo todo)
- habilidades adicionais:
- Ataque rápido: Golpeie duas vezes enquanto for sua vez de atacar; há 10% de chance de ele usar essa habilidade toda vez que atacar
- Escudo mágico: sofre apenas metade do dano normal quando um inimigo ataca; há uma mudança de 20%, ele usará essa habilidade toda vez que defender
- Besta:
- Saúde: 60 - 90
- Força: 60 - 90
- Defesa: 40 – 60
- Velocidade: 40 – 60
- Sorte: 25% - 40%
Regras de jogo:
- O primeiro ataque é feito pelo jogador com maior velocidade. Se ambos os jogadores tiverem a mesma velocidade, o ataque será realizado pelo jogador com maior sorte.
- Após um ataque, os jogadores trocam de papéis: o atacante agora defende e o defensor agora ataca.
- O dano causado pelo atacante é calculado com a seguinte fórmula:
Damage = Attacker strength – Defender defence
- O dano é subtraído da saúde do defensor. Um atacante pode errar o golpe e não causar dano se o defensor tiver sorte naquele turno.
- As habilidades de Orderus ocorrem aleatoriamente, com base em suas chances, então leve-as em consideração a cada turno.
- O jogo termina quando um dos jogadores fica sem saúde ou o número de turnos chega a 20.
- O aplicativo deve exibir os resultados a cada turno: o que aconteceu, quais habilidades foram usadas (se houver), o dano causado, a saúde restante do defensor.
- Se tivermos um vencedor antes de atingir o número máximo de rodadas, ele deverá ser declarado.
FILIAIS e ETIQUETAS
- ramo: base; tag: aplicativo de execução base:
Contém solução básica (funcionalidade principal, unidade testada) que ainda não suporta impressão - ramo: padrão observador; tag: padrão de observador de aplicativo em execução:
Contém todo o código do branch base
e o estende com impressão e registro com uso do Observer Pattern - ramo: padrão mediador; tag: padrão de mediador de aplicativo em execução:
Contém todo o código da ramificação base
e o estende com impressão e registro com uso do Mediator Pattern
Mais ramificações e abordagens a serem adicionadas...
Filial atual: base
PALAVRA DE EXPLICAÇÃO
A solução mais fácil e de 'aplicativo web' seria usar algum barramento de eventos externo ou próprio, mas eu só quero brincar com Design Patterns (talvez eu adicione essa solução algum dia).
PENDÊNCIA
- Esqueci de implementar a habilidade de erro...
- Corrigir delegação não existente no decorador MagicShield