diff --git a/.gitpod.Dockerfile b/.gitpod.Dockerfile new file mode 100644 index 000000000..f8e49f267 --- /dev/null +++ b/.gitpod.Dockerfile @@ -0,0 +1,7 @@ +FROM gitpod/workspace-full + +# Install custom tools, runtimes, etc. +# For example "bastet", a command-line tetris clone: +# RUN brew install bastet +# +# More information: https://www.gitpod.io/docs/config-docker/ diff --git a/.gitpod.yml b/.gitpod.yml new file mode 100644 index 000000000..475c7d9ad --- /dev/null +++ b/.gitpod.yml @@ -0,0 +1,6 @@ +image: + file: .gitpod.Dockerfile + +tasks: + - init: 'echo "TODO: Replace with init/build command"' + command: 'echo "TODO: Replace with command to start project"' diff --git a/README.md b/README.md index c331ec17e..cbb6dfb5e 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,43 @@ +[![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/pythonprobr/pythonbirds) + Python Birds =========== +Essa versão é a mais simples. Ela não contém replay nem reset, de forma que o jogo não pode retroceder. + +Para versão mais complexa, acesse a branch [diversao](https://github.com/pythonprobr/pythonbirds/tree/diversao) + Projeto para Ensino de Programação Orientadas a Objetos em Python. A versão utilizada para desenvolvimento foi Python 3.4 Um vídeo fala mais que 1000 palavras: [Python Birds](https://www.youtube.com/watch?v=b899h0lNd7U&list=PLA05yVJtRWYTm0sIa6n56UpCjCsR5ekla) +# Contribuidores + +* [Giovane Liberato](https://github.com/giovaneliberato) +* [Guido Luz](https://github.com/GuidoBR) +* [Michel Amaral](https://github.com/michelamaral) +* [Renzo Nuccitelli](https://github.com/renzon) + # Abordagem Instalar [Python 3](https://www.python.org/download/). -Baixar a versão contendo apenas a [estrutura do projeto](https://github.com/pythonprobr/pythonbirds/archive/diversao.zip) +Baixar o zip do projeto (botão Download Zip) -Os testes se encontram dentro do pacote "testes" e servem para definir a dinâmica das classes. Para rodar todos testes, execute +Os testes se encontram dentro do pacote "testes" e servem para definir a dinâmica das classes. Para rodar todos testes, execute: python executor_de_testes.py -Explicação detalhada sobre classes e métodos se encontram em [Simplificação do Jogo](#simplifica%C3%A7%C3%A3o-do-jogo) +Explicação detalhada sobre classes e métodos se encontram nos scripts atores.py e fase.py. ## Ordem de desenvolvimento A ordem preferida é começar pelos atores, seguindo a ordem dos testes presentes no script atores_testes.py. Depois passar para a fase_teste.py, onde é implementada uma fase. -Após o desenvolvimento é possível emular um jogo que termina em vitória rodando: +É possível emular um jogo que termina em vitória rodando: python fase_testes.py @@ -51,12 +64,12 @@ Contém lógica para rodar jogo e exibir no console. ## script placa_grafica_tkinter.py -Contém lógica para rodar jogo e em uma janela. +Contém lógica para rodar jogo em uma janela. # Simplificação do Jogo 1. Atores são pontos no plano cartesiano. -2. A velocidade dos pontos e pequena, de tal forma que a cada passo os atores se movam apenas para pontos vizinhos. +2. A velocidade dos pontos é pequena, de tal forma que a cada passo os atores se movam apenas para pontos vizinhos. 3. A colisão entre pontos ocorre quando eles estão em ponto vizinho, de acordo com valor de intervalo. A seguir é apresentada a especificação detalhada do jogo. @@ -70,32 +83,15 @@ Classe base para todos atores do jogo. Método que recebe o tempo (float) como parâmetro e retorna uma tupla com 2 elementos, posição horizontal (x) como primeiro elemento e posição vertical (y) como segundo. -### Método resetar - -Método que ao ser executado, seta o valor de _tempo_de_colisão para None - -### Método status - -O ator possui os status Ativo ou Destruido. Além disso o status deve ser dependente do tempo. Ou seja, se o ator foi -destruido no tempo 1, ele deve possuir status Ativo antes desse tempo, como 0.9, e Destruido após esse tempo, como em tempo 1.1. - ### Método colidir O método colidir executa a lógica de colisão. A colisão só ocorre com atores ativos e que estejam -em pontos vizinhos. -O ator deve guardar o tempo de colisão para calcular corretamente seu status. -Além disso, um intervalo é recebido como parâmetro indicando qual a tolerância para considerar um ponto vizinho. - Por padrão, seu valor é 1. - -## Método caracter - -O método caracter retorna 'A' quando o ator tem status Ativo e '+' caso contrário. Também é depende do tempo. - +em pontos vizinhos. Ao colidir, os atores envolvidos devem ter seus status alterado para DESTRUIDO ## Classe Obstaculo -Classe que representa obstáculos na fase e que podem ser destruidos por pássaros. Herda de ator. Seu caracter de -representação é a letra "O" quando Ativo. +Classe que representa obstáculos na fase e que podem ser destruídos por pássaros. Herda de ator. Seu caracter de +representação é a letra "O", quando ATIVO. ### Status @@ -104,98 +100,93 @@ Assim ele vai "sumir" da tela. ## Classe Porco -Classe que representa porcos na fase e que podem ser destruidos por pássaros. Herda de ator. Seu caracter de +Classe que representa porcos na fase e que podem ser destruídos por pássaros. Herda de ator. Seu caracter de representação é a o caracter "@". +### Status + +Um obstáculo ao ter seu status alterado para DESTRUIDO deve ter seu caracter de apresentação alterado para "+" (sinal de mais). +Assim sua imagem é alterada para a de porco morto. + ## Passaro Classe base de todos os passáros. Cada tipo possui uma velocidade de lançamento (v). No lançamento o jogador escolhe o ângulo (teta), em graus, no qual o passáro deve ser lançado. O lançamento respeita as regras de lançamento oblíquo com -gravidade (G) constante e igual a 10 m/s^2. +gravidade (GRAVIDADE) constante e igual a 10 m/s^2. ### Método lancar -O método lançar recebe o ângulo, em graus, que será feito o lançamento. Cada pássaro deve armazenar esse valor e o tempo -de lançamento para cálculo de sua posíção. Lembrar que o tempo das fórmulas é delta_t = T_final - T_inicial - -### Método de resetar - -O método resetar deve chamar o método resetar de Ator. -Além disso, deve setar como Nome os parầmetros de tempo de lançamento e ângulo de lançamento +O método lançar recebe o ângulo, em graus, que será feito o lançamento. Ele deve ser convertido para radianos. +Cada pássaro deve armazenar esse valor e o tempo +de lançamento para cálculo de sua posíção. Lembrar que o tempo das fórmulas é delta_t = T_final - T_inicial. ### Método de colidir_com_chao Todo pássaro que colidir com o chão (y<=0) deve ser destruído. -### Método foi lançado +### Método foi_lançado Esse método deve retornar verdadadeiro se o pássaro foi lançado (tempo de lançamento é None). -Caso contrário deve retornar falso +Caso contrário deve retornar falso. -### Lançamentomento +### Lançamento -Se o pássaro ainda não foi lançado, ou se o tempo de jogo é menor que o tempo de lançamento, -O pássaro deve permanecer na posição inicial. +Se o pássaro ainda não foi lançado, o pássaro deve permanecer na posição inicial. -Calso tenha sido lançado e seu status esteja ativo, sua posição deve ser calculada de acordo com o lançamento oblíquo. +Caso tenha sido lançado e seu status esteja ATIVO, sua posição deve ser calculada de acordo com o lançamento oblíquo. Nesse caso, delta_t vai ser igual ao tempo do jogo menos o tempo do lançamento. -Caso contrário, ele deve retornar a posição onde colidiu. Para isso, pasta -calcular o ponto em que o pássaro estava no tempo da colisão. +Caso contrário, ele deve retornar a posição onde colidiu. #### Método posicao_horizontal -Fórmula X=X0+v*cos(teta)*delta_t. +Fórmula X=X0+v\*cos(teta)\*delta_t. #### Método posicao_vertical -Fórmula Y=Y0+v*sen(teta)delta_t-(G*delta_t^2)/2. +Fórmula Y=Y0+v\*sen(teta)delta_t-(G\*delta_t^2)/2. ## Classe Passaro Vermelho -Tipo de Pássaro que representa o pássaro vermelho. Possui velocidade de lançamento igual a 20 m/s. Seu caracter é "D". +Tipo de Pássaro que representa o pássaro vermelho. Possui velocidade de lançamento igual a 20 m/s. +Seu caracter quanto ATIVO é "V". Quando DESTRUIDO é "v". ## Classe Passaro Amarelo -Tipo de Pássaro que representa o pássaro amarelo. Possui velocidade de lançamento igual a 30 m/s. Seu caracter é ">". +Tipo de Pássaro que representa o pássaro amarelo. Possui velocidade de lançamento igual a 30 m/s. +Seu caracter quanto DESTRUIDO é "a". ## Classe Fase -Classe responsável por organizar atores e transformarem os dados em pontos a serem representados na tela. +Classe responsável por organizar atores e transformar os dados em pontos a serem representados na tela. ### Método adicionar_obstaculo -Método que adiciona um ou mais obstáculos na fase +Método que adiciona um ou mais obstáculos na fase. ### Método adicionar_porco -Método que adiciona um ou mais porcos na fase +Método que adiciona um ou mais porcos na fase. ### Método adicionar_passaro -Método que adiciona um ou mais pássaros na fase - -### Método acabou +Método que adiciona um ou mais pássaros na fase. -Método que recebe o tempo do jogo e retorna verdadeiro (True) se o jogo acabou e (False) caso contrário. -O jogo pode acabar por duas razôes: - -1. Todos porcos foram destruídos -2. Não há mais pássaros a serem lançados ### Método status -Recebe o tempo como parâmetro e retorna mensagem com status do jogo. +Recebe o tempo como parâmetro e retorna status do jogo. -1. Se o jogo está em andamento, retorna mensagem "Jogo em andamento." -2. Se o jogo acabou e não existem porcos ativos, retorna a mensagem "Jogo em encerrado. Você ganhou!" -3. Se o jogo acabou e existem porcos ativos, retorna a mensagem "Jogo em encerrado. Você perdeu!" +1. Se o jogo está em andamento, retorna status "EM_ANDAMENTO"; +2. Se o jogo acabou e não existem porcos ativos, retorna STATUS "VITORIA"; +3. Se o jogo acabou e existem porcos ativos, retorna status "DERROTA". ### Método lancar -Recebe o ângulo e o tempo do lançamento. Deve delegar o lançamento ao primeiro pássaro ativo da lista de pássaros. +Recebe o ângulo e o tempo do lançamento. Deve delegar o lançamento ao primeiro pássaro ATIVO da lista de pássaros que +ainda não foi lançado. ### Método calcular_pontos @@ -204,8 +195,18 @@ Método que executa a lógica do jogo a cada passo (tempo), retornando pontos a Ele deve: 1. Calcular a posição de cada pássaro, verificando se ele colidiu com algum obstáculo, porco ou chão. -2. Retornar instâncias da classe Ponto, informando x,y e caracter respectivo a cada ator. +2. Retornar instâncias da classe Ponto, informando x, y e caracter respectivo a cada ator. ### Divirta-se!!!! -Powered by [Python Pro](http://adm.python.pro.br) \ No newline at end of file +Powered by [Python Pro](http://adm.python.pro.br) + +# Observação Importante + +Esse projeto usa somente o interpretador padrão do Python. Para fazer jogos com mais interatividade, existe a biblioteca Pygame. Ela não foi utilizada por motivos de simplicidade, para evitar que o aluno iniciante tenha dificuldades na hora de instalar o projeto. + +Contudo o Estevão Fonseca fez um versão com essa biblioteca, confira o projeto: + + + +Ele também colocou esse [vídeo no Youtube](https://www.youtube.com/watch?v=B7G5JtCFepE). diff --git a/atores.py b/atores.py index 1cf2becbb..cfc2ef5ea 100644 --- a/atores.py +++ b/atores.py @@ -1,126 +1,140 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals + import math DESTRUIDO = 'Destruido' ATIVO = 'Ativo' +GRAVIDADE = 10 # m/s^2 class Ator(): + """ + Classe que representa um ator. Ele representa um ponto cartesiano na tela. + """ _caracter_ativo = 'A' _caracter_destruido = ' ' def __init__(self, x=0, y=0): + """ + Método de inicialização da classe. Deve inicializar os parâmetros x, y, caracter e status + + :param x: Posição horizontal inicial do ator + :param y: Posição vertical inicial do ator + """ self.y = y self.x = x - self._tempo_de_colisao = None - - def caracter(self, tempo): - return self._caracter_ativo if self.status(tempo) == ATIVO else self._caracter_destruido + self.status = ATIVO - def resetar(self): - self._tempo_de_colisao = None - - def status(self, tempo): - if self._tempo_de_colisao is None or self._tempo_de_colisao > tempo: - return ATIVO - return DESTRUIDO + def caracter(self): + return self._caracter_ativo if self.status == ATIVO else self._caracter_destruido def calcular_posicao(self, tempo): - return self.arredondar_posicao() + """ + Método que calcula a posição do ator em determinado tempo. + Deve-se imaginar que o tempo começa em 0 e avança de 0,01 segundos + + :param tempo: o tempo do jogo + :return: posição x, y do ator + """ + return 1, 1 - def arredondar_posicao(self): - self.x, self.y = round(self.x), round(self.y) - return self.x, self.y + def colidir(self, outro_ator, intervalo=1): + """ + Método que executa lógica de colisão entre dois atores. + Só deve haver colisão se os dois atores tiverem seus status ativos. + Para colisão, é considerado um quadrado, com lado igual ao parâmetro intervalo, em volta do ponto onde se + encontra o ator. Se os atores estiverem dentro desse mesmo quadrado, seus status devem ser alterados para + destruido, seus caracteres para destruido também. - def colidir(self, outro_ator, tempo, intervalo=1): - if self.status(tempo) == DESTRUIDO or outro_ator.status(tempo) == DESTRUIDO: - return - x1, y1 = self.arredondar_posicao() - x2, y2 = outro_ator.arredondar_posicao() + :param outro_ator: Ator a ser considerado na colisão + :param intervalo: Intervalo a ser considerado + :return: + """ + pass - if x1 - intervalo <= x2 <= x1 + intervalo and y1 - intervalo <= y2 <= y1 + intervalo: - self._tempo_de_colisao = tempo - outro_ator._tempo_de_colisao = tempo class Obstaculo(Ator): - _caracter_ativo = 'O' + pass class Porco(Ator): - _caracter_ativo = '@' - _caracter_destruido = '+' + pass -GRAVIDADE = 10 # m/s^2 +class DuploLancamentoExcecao(Exception): + pass class Passaro(Ator): - velocidade_escalar = None + velocidade_escalar = 10 def __init__(self, x=0, y=0): + """ + Método de inicialização de pássaro. + + Deve chamar a inicialização de ator. Além disso, deve armazenar a posição inicial e incializar o tempo de + lançamento e angulo de lançamento + + :param x: + :param y: + """ super().__init__(x, y) self._x_inicial = x self._y_inicial = y self._tempo_de_lancamento = None self._angulo_de_lancamento = None # radianos - def resetar(self): - super().resetar() - self._tempo_de_lancamento = None - self._angulo_de_lancamento = None + def foi_lancado(self): + """ + Método que retorna verdaeira se o pássaro já foi lançado e falso caso contrário + :return: booleano + """ + return True - def foi_lancado(self): - return self._tempo_de_lancamento is not None + def colidir_com_chao(self): + """ + Método que executa lógica de colisão com o chão. Toda vez que y for menor ou igual a 0, + o status dos Passaro deve ser alterado para destruido, bem como o seu caracter - def colidir_com_chao(self, tempo): - if self.y <= 0: - self._tempo_de_colisao = tempo + """ + pass - def _calcular_posicao_horizontal(self, delta_t): - self.x = self._x_inicial + self.velocidade_escalar * delta_t * math.cos(self._angulo_de_lancamento) + def calcular_posicao(self, tempo): + """ + Método que cálcula a posição do passaro de acordo com o tempo. - def _calcular_posicao_vertical(self, delta_t): - self.y = (self._y_inicial + - self.velocidade_escalar * delta_t * math.sin(self._angulo_de_lancamento) - - (GRAVIDADE / 2) * delta_t ** 2) + Antes do lançamento o pássaro deve retornar o valor de sua posição inicial - def _calcular_posicao(self, tempo): - delta_t = tempo - self._tempo_de_lancamento - self._calcular_posicao_vertical(delta_t) - self._calcular_posicao_horizontal(delta_t) + Depois do lançamento o pássaro deve calcular de acordo com sua posição inicial, velocidade escalar, + ângulo de lancamento, gravidade (constante GRAVIDADE) e o tempo do jogo. - def calcular_posicao(self, tempo): - if self._aguardando_lancamento(tempo): - self.x = self._x_inicial - self.y = self._y_inicial - elif self._ja_colidiu(tempo): - self._calcular_posicao(self._tempo_de_colisao) - else: - self._calcular_posicao(tempo) - return self.arredondar_posicao() + Após a colisão, ou seja, ter seus status destruido, o pássaro deve apenas retornar a última posição calculada. - def lancar(self, angulo, tempo): - self._tempo_de_lancamento = tempo - self._angulo_de_lancamento = math.radians(angulo) + :param tempo: tempo de jogo a ser calculada a posição + :return: posição x, y + """ + return 1, 1 - def _aguardando_lancamento(self, tempo): - return not self.foi_lancado() or tempo < self._tempo_de_lancamento - def _ja_colidiu(self, tempo): - return self.foi_lancado() and self.status(tempo) == DESTRUIDO + def lancar(self, angulo, tempo_de_lancamento): + """ + Lógica que lança o pássaro. Deve armazenar o ângulo e o tempo de lançamento para posteriores cálculo. + O ângulo é passado em graus e deve ser transformado em radianos + + :param angulo: + :param tempo_de_lancamento: + :return: + """ + pass class PassaroAmarelo(Passaro): - velocidade_escalar = 30 # m/s - _caracter_ativo = 'A' - _caracter_destruido = 'a' + pass class PassaroVermelho(Passaro): - velocidade_escalar = 20 # m/s - _caracter_ativo = 'V' - _caracter_destruido = 'v' + pass \ No newline at end of file diff --git a/fase.py b/fase.py index b2bdf8c8f..3385175c6 100644 --- a/fase.py +++ b/fase.py @@ -3,85 +3,105 @@ from atores import ATIVO +VITORIA = 'VITORIA' +DERROTA = 'DERROTA' +EM_ANDAMENTO = 'EM_ANDAMENTO' + + class Ponto(): def __init__(self, x, y, caracter): self.caracter = caracter - self.x = x - self.y = y + self.x = round(x) + self.y = round(y) def __eq__(self, other): return self.x == other.x and self.y == other.y and self.caracter == other.caracter + def __hash__(self): + return hash(self.x) ^ hash(self.y) + def __repr__(self, *args, **kwargs): return "Ponto(%s,%s,'%s')" % (self.x, self.y, self.caracter) class Fase(): def __init__(self, intervalo_de_colisao=1): + """ + Método que inicializa uma fase. + + :param intervalo_de_colisao: + """ self.intervalo_de_colisao = intervalo_de_colisao self._passaros = [] self._porcos = [] self._obstaculos = [] - def _adicionar_ator(self, lista, *atores): - lista.extend(atores) def adicionar_obstaculo(self, *obstaculos): - self._adicionar_ator(self._obstaculos, *obstaculos) + """ + Adiciona obstáculos em uma fase + + :param obstaculos: + """ + pass def adicionar_porco(self, *porcos): - self._adicionar_ator(self._porcos, *porcos) + """ + Adiciona porcos em uma fase + + :param porcos: + """ + pass def adicionar_passaro(self, *passaros): - self._adicionar_ator(self._passaros, *passaros) + """ + Adiciona pássaros em uma fase + + :param passaros: + """ + pass + + def status(self): + """ + Método que indica com mensagem o status do jogo + + Se o jogo está em andamento (ainda tem porco ativo e pássaro ativo), retorna essa mensagem. - def acabou(self, tempo): - return not self._existe_porco_ativo(tempo) or not self._existe_passaro_ativo(tempo) + Se o jogo acabou com derrota (ainda existe porco ativo), retorna essa mensagem - def status(self, tempo): - if not self._existe_porco_ativo(tempo): - return 'Jogo em encerrado. Você ganhou!' - if self._existe_passaro_ativo(tempo): - return 'Jogo em andamento.' - return 'Jogo em encerrado. Você perdeu!' + Se o jogo acabou com vitória (não existe porco ativo), retorna essa mensagem + + :return: + """ + return EM_ANDAMENTO def lancar(self, angulo, tempo): - for passaro in self._passaros: - if not passaro.foi_lancado(): - passaro.lancar(angulo, tempo) - return + """ + Método que executa lógica de lançamento. + + Deve escolher o primeiro pássaro não lançado da lista e chamar seu método lançar + + Se não houver esse tipo de pássaro, não deve fazer nada + + :param angulo: ângulo de lançamento + :param tempo: Tempo de lançamento + """ + pass - def resetar(self): - for ator in chain(self._passaros, self._obstaculos, self._porcos): - ator.resetar() def calcular_pontos(self, tempo): - pontos = [self._calcular_ponto_de_passaro(p, tempo) for p in self._passaros] - obstaculos_e_porcos = chain(self._obstaculos, self._porcos) - pontos.extend([self._transformar_em_ponto(ator, tempo) for ator in obstaculos_e_porcos]) + """ + Lógica que retorna os pontos a serem exibidos na tela. + + Cada ator deve ser transformado em um Ponto. + + :param tempo: tempo para o qual devem ser calculados os pontos + :return: objeto do tipo Ponto + """ + pontos=[self._transformar_em_ponto(a) for a in self._passaros+self._obstaculos+self._porcos] + return pontos - def _transformar_em_ponto(self, ator, tempo): - return Ponto(ator.x, ator.y, ator.caracter(tempo)) - - def _calcular_ponto_de_passaro(self, passaro, tempo, ): - passaro.calcular_posicao(tempo) - for ator in chain(self._obstaculos, self._porcos): - if ATIVO == passaro.status(tempo): - passaro.colidir(ator, tempo, self.intervalo_de_colisao) - passaro.colidir_com_chao(tempo) - else: - break - return self._transformar_em_ponto(passaro, tempo) - - def _existe_porco_ativo(self, tempo): - return self._verificar_se_existe_ator_ativo(self._porcos, tempo) - - def _verificar_se_existe_ator_ativo(self, atores, tempo): - for a in atores: - if a.status(tempo) == ATIVO: - return True - return False - - def _existe_passaro_ativo(self, tempo): - return self._verificar_se_existe_ator_ativo(self._passaros, tempo) + def _transformar_em_ponto(self, ator): + return Ponto(ator.x, ator.y, ator.caracter()) + diff --git a/fases/escudo_espartano.py b/fases/escudo_espartano.py index b5478e16c..9b01135f0 100644 --- a/fases/escudo_espartano.py +++ b/fases/escudo_espartano.py @@ -13,7 +13,7 @@ from random import randint if __name__ == '__main__': - fase = Fase(intervalo_de_colisao=10) + fase = Fase(intervalo_de_colisao=32) # Adicionar Pássaros Amarelos diff --git a/fases/rodar_fase_exemplo.py b/fases/rodar_fase_exemplo.py index 26cbc27f5..0f67e4cf1 100644 --- a/fases/rodar_fase_exemplo.py +++ b/fases/rodar_fase_exemplo.py @@ -11,7 +11,7 @@ from placa_grafica_tkinter import rodar_fase if __name__ == '__main__': - fase = Fase(intervalo_de_colisao=10) + fase = Fase(intervalo_de_colisao=32) # Adicionar Pássaros Vermelhos diff --git a/placa_grafica.py b/placa_grafica.py index 5f8bfe3af..77ae67aef 100644 --- a/placa_grafica.py +++ b/placa_grafica.py @@ -52,7 +52,7 @@ def _animar(delta_t, fase, passo, tempo, msg): def _jogar(delta_t, fase, passo, tempo, msg): - while not fase.acabou(tempo): + while not fase.acabou(): tempo = desenhar_e_esperar(delta_t, fase, passo, tempo, msg) entrada = ouvir_teclado() if entrada: diff --git a/placa_grafica_tkinter.py b/placa_grafica_tkinter.py index 9b8e76f7d..318542e65 100644 --- a/placa_grafica_tkinter.py +++ b/placa_grafica_tkinter.py @@ -6,7 +6,7 @@ from os import path import atores -from fase import Fase +from fase import Fase, EM_ANDAMENTO, VITORIA from atores import PassaroVermelho, PassaroAmarelo, Porco, Obstaculo ALTURA_DA_TELA = 600 # px @@ -43,13 +43,15 @@ def plotar(camada_de_atores, ponto): camada_de_atores.create_image((x, y), image=image, anchor=NW) -def animar(tela, camada_de_atores, fase, passo=0.01, delta_t=0.01): +def animar(tela, camada_de_atores, fase, passo=0.01, delta_t=0.04): tempo = 0 passo = int(1000 * passo) angulo = 0 multiplicador_rebobinar = 20 def _animar(): + tempo_de_inicio_de_animacao=time.time() + nonlocal tempo nonlocal delta_t nonlocal angulo @@ -57,10 +59,10 @@ def _animar(): if tempo <= 0: tempo = 0 delta_t /= -multiplicador_rebobinar - if fase.acabou(tempo): + if fase.status() != EM_ANDAMENTO: camada_de_atores.create_image(162, 55, image=PYTHONBIRDS_LOGO, anchor=NW) camada_de_atores.create_image(54, 540, image=MENU, anchor=NW) - if 'ganhou' in fase.status(tempo): + if fase.status() == VITORIA: img = VOCE_GANHOU else: img = VOCE_PERDEU @@ -73,21 +75,28 @@ def _animar(): camada_de_atores.create_line(52, 493, 52 + tamanho_seta * math.cos(angulo_rad), 493 + tamanho_seta * math.sin(angulo_rad), width=1.5) - camada_de_atores.create_text(35, 493, text=u"%d°" % angulo) + camada_de_atores.create_text(35, 493, text="%d°" % angulo) for ponto in fase.calcular_pontos(tempo): plotar(camada_de_atores, ponto) - tela.after(passo, _animar) + tempo_gasto_com_animacao= round((time.time() - tempo_de_inicio_de_animacao)*1000) # Trans + tempo_proxima_animacao = passo - tempo_gasto_com_animacao if passo>tempo_gasto_com_animacao else 1 + tela.after(tempo_proxima_animacao, _animar) def _ouvir_comandos_lancamento(evento): nonlocal angulo if evento.keysym == 'Up': angulo += 1 + if angulo > 360: + angulo = 1 elif evento.keysym == 'Down': angulo -= 1 + if angulo < 0: + angulo = 359 elif evento.keysym == 'Return' or evento.keysym == 'space': fase.lancar(angulo, tempo) def _replay(event): + return nonlocal tempo nonlocal delta_t if fase.acabou(tempo): @@ -96,6 +105,7 @@ def _replay(event): def _jogar_novamente(event): + return nonlocal tempo nonlocal delta_t if fase.acabou(tempo): @@ -132,7 +142,7 @@ def rodar_fase(fase): if __name__ == '__main__': - fase = Fase(intervalo_de_colisao=10) + fase = Fase(intervalo_de_colisao=32) passaros = [PassaroVermelho(30, 30), PassaroAmarelo(30, 30), PassaroAmarelo(30, 30)] porcos = [Porco(750, 1), Porco(700, 1)] obstaculos = [Obstaculo(310, 100)] diff --git a/testes/atores_testes.py b/testes/atores_testes.py index b0d00ac7a..f4254f29e 100644 --- a/testes/atores_testes.py +++ b/testes/atores_testes.py @@ -1,62 +1,55 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from unittest.case import TestCase -from atores import Ator, DESTRUIDO, ATIVO, Obstaculo, Porco, PassaroAmarelo, PassaroVermelho - +from os import path +import sys -class AtorBaseTest(TestCase): - def assert_ator_caracteres(self, ator, caracter_status_ativo, caracter_status_destruido): - 'Confere status e caracteres ativos e destruidos de um ator' - # Conferencia de caracter de ator ativo - self.assertEqual(ATIVO, ator.status(0)) - self.assertEqual(caracter_status_ativo, ator.caracter(0)) - - # Colidindo ator com ele mesmo para alterar seu status para destruido - ator.colidir(ator, 3.2) - - self.assertEqual(caracter_status_ativo, ator.caracter(3.1), - 'Status ativo, logo o caracter deveria ser %s' % caracter_status_ativo) - self.assertEqual(caracter_status_destruido, ator.caracter(3.2), - 'Status destruido, logo o caracter deveria ser %s' % caracter_status_destruido) - self.assertEqual(caracter_status_destruido, ator.caracter(4), - 'Status destruido, logo o caracter deveria ser %s' % caracter_status_destruido) +project_dir = path.dirname(__file__) +project_dir = path.join('..') +sys.path.append(project_dir) +import unittest +from unittest.case import TestCase +from atores import Ator, DESTRUIDO, ATIVO, Obstaculo, Porco, PassaroAmarelo, PassaroVermelho -class AtorTestes(AtorBaseTest): - def teste_ator_posicao(self): - ator = Ator() - self.assertTupleEqual((0, 0), ator.calcular_posicao(0)) - ator = Ator(0.3, 0.5) - self.assertTupleEqual((0, 0), ator.calcular_posicao(2.3), 'Deveria arredondar para inteiro') - ator = Ator(0.6, 2.1) - self.assertTupleEqual((1, 2), ator.calcular_posicao(3.14), 'Deveria arredondar para inteiro') - def teste_resetar(self): +class AtorTestes(TestCase): + def teste_valores_padrao(self): + 'Testa valores iniciais padrão de um Ator' ator = Ator() - self.assertIsNone(ator._tempo_de_colisao) - ator._tempo_de_colisao = 1 - ator.resetar() - self.assertIsNone(ator._tempo_de_colisao) - + self.assertEqual(0, ator.x) + self.assertEqual(0, ator.y) + self.assertEqual(ATIVO, ator.status) + self.assertEqual('A', ator.caracter()) + + def teste_valores_passados_por_parametro(self): + 'Testa se valores passados no inicializador são armazenados no objeto' + ator = Ator(1, 2) + self.assertEqual(1, ator.x) + self.assertEqual(2, ator.y) + self.assertEqual(ATIVO, ator.status) + self.assertEqual('A', ator.caracter()) - def teste_status(self): + def teste_ator_posicao(self): + 'Teste que verifica que o ator comum não deve se mover independente do tempo do jogo' ator = Ator() - 'Confere status de um ator' - # Conferencia de caracter de ator ativo - self.assertEqual(ATIVO, ator.status(0)) + x, y = ator.calcular_posicao(0) + self.assertEqual(0, x) + self.assertEqual(0, y) - # Colidindo ator com ele mesmo para alterar seu status para destruido - ator._tempo_de_colisao = 3.2 + ator = Ator(0.3, 0.5) + x, y = ator.calcular_posicao(10) + self.assertEqual(0.3, x) + self.assertEqual(0.5, y) - self.assertEqual(ATIVO, ator.status(3.1), - 'Status para tempo 3.1 de jogo deveria mostrar status ativo, já que é menor que o tempo de colisão 3.2') - self.assertEqual(DESTRUIDO, ator.status(3.2), - 'Status para tempo 3.2 de jogo deveria mostrar status destruido, já que é igual ao tempo de colisão 3.2') - self.assertEqual(DESTRUIDO, ator.status(4), - 'Status para tempo 4 de jogo deveria mostrar status destruido, já que é maior que o tempo de colisão 3.2') def teste_colisao_entre_atores_ativos(self): + """ + Teste de colisão entre dois atores + Inicialmente atores possuem status ATIVO. Ao se chocarem, ele muda para DESTRUIDO + A função assert_colisao_atores_ativos testa justamente se dois atore ativos se chocam quando estão em posições + vizinhas. + """ ator = Ator(2, 2) # Ator recém criado deve ter status ativo ator2 = Ator(2, 2) self.assert_colisao_atores_ativos(ator, ator2) @@ -73,7 +66,8 @@ def teste_colisao_entre_atores_ativos_com_intervalo(self): # Com intervalo 2, diferente do padrão 1, essa colisão deveria acontecer self.assert_colisao_atores_ativos(Ator(2, 2), Ator(2, 4), 2) - def teste_nao_colisao_entre_atores_ativos(self): + def teste_nao_colisao_entre_atores_distantes(self): + 'Teste de que não há colisão entre atores distantes' self.assert_nao_colisao(Ator(2, 2), Ator(2, 4)) self.assert_nao_colisao(Ator(2, 2), Ator(3, 4)) self.assert_nao_colisao(Ator(2, 2), Ator(4, 2)) @@ -84,8 +78,9 @@ def teste_nao_colisao_entre_atores_ativos(self): self.assert_nao_colisao(Ator(2, 2), Ator(0, 4)) def teste_colisao_somente_um_ator_destruido(self): + 'Teste de que um ator destruído não pode colidir com nenhum outro, mesmo que estejam próximos' ator = Ator(2, 2) - ator.colidir(ator, 0) # colidingo ator com ele mesmo para alterar seu status para destruido + ator.colidir(ator, 0) # colidindo ator com ele mesmo para alterar seu status para destruido ator2 = Ator(2, 2) self.assert_nao_colisao(ator, ator2) self.assert_nao_colisao(Ator(2, 3), ator) @@ -107,78 +102,117 @@ def teste_colisao_somente_um_ator_destruido(self): self.assert_nao_colisao(Ator(1, 3), ator) def test_caracter(self): + 'Teste de caracter para status ATIVO e DESTRUIDO' ator = Ator() - self.assert_ator_caracteres(ator, 'A', ' ') + self.assertEqual('A', ator.caracter()) + outro_ator_na_mesma_posicao = Ator() + ator.colidir(outro_ator_na_mesma_posicao) + self.assertEqual(' ', ator.caracter()) def assert_colisao_atores_ativos(self, ator, ator2, intervalo=1): - 'Se certifica que há colisão entre atores ativos' - tempo_da_colisao = 2 + """ + Se certifica que há colisão entre atores ativos + Atenção: Esse não é método de teste porque nao se inicia com prefixo "text". + Ele serve apenas para encapsular toda lógica de teste de colisão entre dois atores ativos + """ # Conferindo status dos dois atores antes da colisão - self.assertEqual(ator.status(tempo_da_colisao), ATIVO, 'Status deveria ser ativo antes da colisão') - self.assertEqual(ator2.status(tempo_da_colisao), ATIVO, 'Status deveria ser ativo antes da colisão') - ator.colidir(ator2, tempo_da_colisao, intervalo) + self.assertEqual(ator.status, ATIVO, 'Status deveria ser ativo antes da colisão') + self.assertEqual(ator2.status, ATIVO, 'Status deveria ser ativo antes da colisão') + ator.colidir(ator2, intervalo) # Conferindo status dos dois atores depois da colisão - self.assertEqual(ator2.status(tempo_da_colisao), DESTRUIDO, 'Status deveria ser destruido depois da colisão') - self.assertEqual(ator.status(tempo_da_colisao), DESTRUIDO, 'Status deveria ser destruido depois da colisão') + self.assertEqual(ator2.status, DESTRUIDO, 'Status deveria ser destruido depois da colisão') + self.assertEqual(ator.status, DESTRUIDO, 'Status deveria ser destruido depois da colisão') def assert_nao_colisao(self, ator, ator2): - 'Se certifica que não colisão entre dois atores' - tempo_da_colisao = 2 + """ + Se certifica que não colisão entre dois atores + Atenção: Esse não é método de teste porque nao se inicia com prefixo "text". + Ele apenas encapsula a lógica de não colisão entre dois atores. + So seja, eles deve manter seus respectivos status mesmo depois da chamada do metodo colidir + """ # Armazenando status antes da colisão - status_inicial_ator = ator.status(tempo_da_colisao) - status_inicial_ator_2 = ator2.status(tempo_da_colisao) + status_inicial_ator = ator.status + status_inicial_ator_2 = ator2.status - ator.colidir(ator2, tempo_da_colisao) + ator.colidir(ator2) # Conferindo se status ficaram inalterados - self.assertEqual(status_inicial_ator, ator.status(tempo_da_colisao), 'Status de ator não deveria mudar') - self.assertEqual(status_inicial_ator_2, ator2.status(tempo_da_colisao), 'Status de ator2 não deveria mudar') + self.assertEqual(status_inicial_ator, ator.status, 'Status de ator não deveria mudar') + self.assertEqual(status_inicial_ator_2, ator2.status, 'Status de ator2 não deveria mudar') -class ObstaculoTestes(AtorBaseTest): +class ObstaculoTestes(TestCase): + """ + Esperado '0' como caracter de obstáculo ativo e ' ' como caracter de obstáculo destruído + """ def teste_status(self): obstaculo = Obstaculo() - self.assert_ator_caracteres(obstaculo, 'O', ' ') + self.assertEqual('O', obstaculo.caracter()) + outro_ator_na_mesma_posicao = Ator() + obstaculo.colidir(outro_ator_na_mesma_posicao) + self.assertEqual(' ', obstaculo.caracter()) -class PorcoTestes(AtorBaseTest): +class PorcoTestes(TestCase): + """ + Esperado '@' como caracter de porco ativo e '+' como caracter de porco destruido + """ def teste_status(self): porco = Porco() - self.assert_ator_caracteres(porco, '@', '+') + self.assertEqual('@', porco.caracter()) + outro_ator_na_mesma_posicao = Ator() + porco.colidir(outro_ator_na_mesma_posicao) + self.assertEqual('+', porco.caracter()) -class PassaroBaseTests(AtorBaseTest): +class PassaroBaseTests(TestCase): + """ + Classe base para teste de passaros. + Essa classe não contèm nenhum teste, serve apenas para encapsular a lógica de asserção de posição de passaros + vermelhos e também dos amarelos. + + """ + def assert_passaro_posicao(self, x_esperado, y_esperado, status_esperado, passaro, tempo): + """ + Método que se testa posição do pássaro. + Atenção: Esse não é um método de teste porque não se inicia com prefixo "test". + :param x_esperado: posição x esperada do passaro + :param y_esperado: posição y esperada do passaro + :param status_esperado: status esperado do passaro + :param passaro: passaro alvo do teste + :param tempo: tempo do jogo + """ x_calculado, y_calculado = passaro.calcular_posicao(tempo) - self.assertEqual(x_esperado, x_calculado) - self.assertEqual(y_esperado, y_calculado) - passaro.colidir_com_chao(tempo) - self.assertEqual(status_esperado, passaro.status(tempo)) + self.assertEqual(x_esperado, round(x_calculado), 'valor real de x = %s' % x_calculado) + self.assertEqual(y_esperado, round(y_calculado), 'valor real de y = %s' % y_calculado) + self.assertEqual(status_esperado, passaro.status, '(x = %s, y = %s)' % (x_calculado, y_calculado)) class PassaroVermelhoTests(PassaroBaseTests): + """ + Classe de teste e Passaro Vermelho + """ + def teste_status(self): - passaro_amarelo = PassaroVermelho(1, 1) - self.assert_ator_caracteres(passaro_amarelo, 'V', 'v') + passaro_vermelho = PassaroVermelho(1, 1) + self.assertEqual('V', passaro_vermelho.caracter()) + outro_ator_na_mesma_posicao = Ator() + passaro_vermelho.colidir(outro_ator_na_mesma_posicao) + self.assertEqual('v', passaro_vermelho.caracter()) + def teste_velocidade_escalar(self): self.assertEqual(20, PassaroVermelho.velocidade_escalar) - def teste_resetar(self): - passaro = PassaroVermelho() - self.assertIsNone(passaro._tempo_de_colisao) - self.assertIsNone(passaro._tempo_de_lancamento) - self.assertIsNone(passaro._angulo_de_lancamento) - passaro._tempo_de_colisao = 1 - passaro._tempo_de_lancamento = 2 - passaro._angulo_de_lancamento = 90 - passaro.resetar() - self.assertIsNone(passaro._tempo_de_colisao) - self.assertIsNone(passaro._tempo_de_lancamento) - self.assertIsNone(passaro._angulo_de_lancamento) def teste_foi_lancado(self): + """ + Teste de lançamento. Enquanto o método lançar do passaro não for chamado, o méotodo foi_lancado deve retornar + Falso + :return: + """ passaro_vermelho = PassaroVermelho(1, 1) self.assertFalse(passaro_vermelho.foi_lancado(), 'Se o método lançar ainda não foi executado, deve retornar falso') @@ -186,138 +220,96 @@ def teste_foi_lancado(self): self.assertTrue(passaro_vermelho.foi_lancado(), 'Se o método lançar foi executado, deve retornar verdadeiro') - def teste_posicao_antes_do_lancamento(self): - 'Método que testa que o pássaro fica parado antes do tempo de lançamento' - passaro_vermelho = PassaroVermelho(1, 1) - passaro_vermelho.lancar(90, 2) # passaro lancado a 90 graus no tempo 2 segundos - # - for t in range(20): - t /= 10 - self.assertEqual((1, 1), passaro_vermelho.calcular_posicao(t), - 'Não deveria se mover no tempo %s < 2 segundtos' % t) - - def teste_colisao_com_chao(self): - for i in range(30): - passaro = PassaroVermelho(i, 0) - passaro.colidir_com_chao(3) - self.assertEqual(DESTRUIDO, passaro.status(3), 'Deve colidir com chão sempre que y=0') - - def teste_colisao(self): - passaro_vermelho = PassaroVermelho(1, 1) - passaro_vermelho.lancar(45, 2) # passaro lancado a 45 graus no tempo 2 segundos - - porco = Porco(14, 10) - x_calculado, y_calculado = passaro_vermelho.calcular_posicao(2.89) - self.assertEqual(14, x_calculado) - self.assertEqual(10, y_calculado) - - passaro_vermelho.colidir(porco, 2.89) - self.assertEqual(DESTRUIDO, passaro_vermelho.status(2.89)) - - # Deve ficar parado onde colidiu para qualquer tempo maior ou igual que o de colisão - x_calculado, y_calculado = passaro_vermelho.calcular_posicao(2.89) - self.assertEqual(14, x_calculado) - self.assertEqual(10, y_calculado) - - x_calculado, y_calculado = passaro_vermelho.calcular_posicao(4) - self.assertEqual(14, x_calculado) - self.assertEqual(10, y_calculado) + """ + Testando que o passáro colide quando sua posição y é menor ou igual a 0 + :return: + """ + passaro = PassaroVermelho(0, 0) + passaro.colidir_com_chao() + self.assertEqual(DESTRUIDO, passaro.status, 'Deve colidir com chão sempre que y<=0') + passaro = PassaroVermelho(1, 0) + passaro.colidir_com_chao() + self.assertEqual(DESTRUIDO, passaro.status, 'Deve colidir com chão sempre que y<=0') + passaro = PassaroVermelho(2, 0) + passaro.colidir_com_chao() + self.assertEqual(DESTRUIDO, passaro.status, 'Deve colidir com chão sempre que y<=0') + passaro = PassaroVermelho(2, -0.1) + passaro.colidir_com_chao() + self.assertEqual(DESTRUIDO, passaro.status, 'Deve colidir com chão sempre que y<=0') + passaro = PassaroVermelho(2, -5) + passaro.colidir_com_chao() + self.assertEqual(DESTRUIDO, passaro.status, 'Deve colidir com chão sempre que y<=0') class PassaroAmareloTests(PassaroBaseTests): + """ + Classe de Tests para passaros amarelos + """ + def teste_status(self): passaro_amarelo = PassaroAmarelo(1, 1) - self.assert_ator_caracteres(passaro_amarelo, 'A', 'a') + self.assertEqual('A', passaro_amarelo.caracter()) + outro_ator_na_mesma_posicao = Ator() + passaro_amarelo.colidir(outro_ator_na_mesma_posicao) + self.assertEqual('a', passaro_amarelo.caracter()) def teste_velocidade_escalar(self): self.assertEqual(30, PassaroAmarelo.velocidade_escalar) - def teste_posicao_antes_do_lancamento(self): - passaro_amarelo = PassaroAmarelo(1, 1) - passaro_amarelo.lancar(90, 2) # passaro lancado a 90 graus no tempo 2 segundos - # - for t in range(20): - t /= 10 - self.assertEqual((1, 1), passaro_amarelo.calcular_posicao(t), - 'Não deveria se mover no tempo %s < 2 segundtos' % t) - - - def teste_colisao_com_chao(self): - for i in range(30): - passaro = PassaroAmarelo(i, 0) - passaro.colidir_com_chao(3) - self.assertEqual(DESTRUIDO, passaro.status(3), 'Deve colidir com chão sempre que y=0') - - def teste_colisao(self): - passaro_amarelo = PassaroAmarelo(1, 1) - passaro_amarelo.lancar(45, 2) # passaro lancado a 45 graus no tempo 2 segundos - porco = Porco(4, 4) - x_calculado, y_calculado = passaro_amarelo.calcular_posicao(2.12) - self.assertEqual(4, x_calculado) - self.assertEqual(3, y_calculado) - - passaro_amarelo.colidir(porco, 2.12) - self.assertEqual(DESTRUIDO, passaro_amarelo.status(2.12)) - # Deve ficar parado onde colidiu para qualquer tempo maior ou igual que o de colisão - x_calculado, y_calculado = passaro_amarelo.calcular_posicao(2.12) - self.assertEqual(4, x_calculado) - self.assertEqual(3, y_calculado) - - x_calculado, y_calculado = passaro_amarelo.calcular_posicao(1000) - self.assertEqual(4, x_calculado) - self.assertEqual(3, y_calculado) - def teste_lacamento_vertical(self): + """ + Tests de lançamento vertical. Nele, o passaro só se move verticalmente e sua posição y se matém contanstante + :return: + """ passaro_amarelo = PassaroAmarelo(1, 1) passaro_amarelo.lancar(90, 2) # passaro lancado a 90 graus no tempo 2 segundos - # função auxiliar que mantém x fixo com valor 1, status Ativo, variando apenas o tempo e a posição y - def assert_vertical(y_local, tempo): - self.assert_passaro_posicao(1, y_local, ATIVO, passaro_amarelo, tempo) + # subindo - assert_vertical(1, 2.0) - assert_vertical(1, 2.0) - assert_vertical(1, 2.01) - assert_vertical(2, 2.02) - assert_vertical(2, 2.03) - assert_vertical(2, 2.04) - assert_vertical(2, 2.05) + self.assert_posicao_vertical(1, 2.0, passaro_amarelo) + self.assert_posicao_vertical(1, 2.01, passaro_amarelo) + self.assert_posicao_vertical(2, 2.02, passaro_amarelo) + self.assert_posicao_vertical(2, 2.03, passaro_amarelo) + self.assert_posicao_vertical(2, 2.04, passaro_amarelo) + self.assert_posicao_vertical(2, 2.05, passaro_amarelo) # descendo - assert_vertical(46, 5.26) - assert_vertical(46, 5.27) - assert_vertical(46, 5.279999999999999) - assert_vertical(46, 5.29) - assert_vertical(46, 5.3) - assert_vertical(46, 5.3100000000000005) - assert_vertical(45, 5.32) - assert_vertical(45, 5.33) - assert_vertical(45, 5.34) - assert_vertical(45, 5.35) - assert_vertical(45, 5.359999999999999) - assert_vertical(45, 5.37) - assert_vertical(45, 5.38) - assert_vertical(45, 5.390000000000001) - assert_vertical(45, 5.4) - assert_vertical(45, 5.41) - assert_vertical(45, 5.42) - assert_vertical(45, 5.43) - assert_vertical(45, 5.4399999999999995) - assert_vertical(45, 5.45) - assert_vertical(45, 5.46) - assert_vertical(45, 5.470000000000001) - assert_vertical(45, 5.48) + self.assert_posicao_vertical(46, 5.26, passaro_amarelo) + self.assert_posicao_vertical(46, 5.27, passaro_amarelo) + self.assert_posicao_vertical(46, 5.279999999999999, passaro_amarelo) + self.assert_posicao_vertical(46, 5.29, passaro_amarelo) + self.assert_posicao_vertical(46, 5.3, passaro_amarelo) + self.assert_posicao_vertical(46, 5.3100000000000005, passaro_amarelo) + self.assert_posicao_vertical(45, 5.32, passaro_amarelo) + self.assert_posicao_vertical(45, 5.33, passaro_amarelo) + self.assert_posicao_vertical(45, 5.34, passaro_amarelo) + self.assert_posicao_vertical(45, 5.35, passaro_amarelo) + self.assert_posicao_vertical(45, 5.359999999999999, passaro_amarelo) + self.assert_posicao_vertical(45, 5.37, passaro_amarelo) + self.assert_posicao_vertical(45, 5.38, passaro_amarelo) + self.assert_posicao_vertical(45, 5.390000000000001, passaro_amarelo) + self.assert_posicao_vertical(45, 5.4, passaro_amarelo) + self.assert_posicao_vertical(45, 5.41, passaro_amarelo) + self.assert_posicao_vertical(45, 5.42, passaro_amarelo) + self.assert_posicao_vertical(45, 5.43, passaro_amarelo) + self.assert_posicao_vertical(45, 5.4399999999999995, passaro_amarelo) + self.assert_posicao_vertical(45, 5.45, passaro_amarelo) + self.assert_posicao_vertical(45, 5.46, passaro_amarelo) + self.assert_posicao_vertical(45, 5.470000000000001, passaro_amarelo) + self.assert_posicao_vertical(45, 5.48, passaro_amarelo) # preparando para impacto no chão - assert_vertical(1, 8.0) - assert_vertical(1, 8.01) + self.assert_posicao_vertical(1, 8.0, passaro_amarelo) + self.assert_posicao_vertical(1, 8.01, passaro_amarelo) # colisão - self.assert_passaro_posicao(1, 0, DESTRUIDO, passaro_amarelo, 8.02) + self.assert_posicao_vertical(0, 8.04, passaro_amarelo) + passaro_amarelo.colidir_com_chao() + self.assertEqual(DESTRUIDO, passaro_amarelo.status) def test_lancamento_45_graus(self): passaro_amarelo = PassaroAmarelo(1, 1) @@ -749,11 +741,24 @@ def test_lancamento_45_graus(self): self.assert_passaro_posicao(91, 1, ATIVO, passaro_amarelo, 6.24) self.assert_passaro_posicao(91, 1, ATIVO, passaro_amarelo, 6.25) self.assert_passaro_posicao(91, 1, ATIVO, passaro_amarelo, 6.26) - self.assert_passaro_posicao(92, 0, DESTRUIDO, passaro_amarelo, 6.27) - + self.assert_passaro_posicao(92, 0, ATIVO, passaro_amarelo, 6.29) + passaro_amarelo.colidir_com_chao() + self.assertEqual(DESTRUIDO, passaro_amarelo.status) # Código de geração de testes # for delta_t in range(0, 550): # t = 2 + (delta_t / 100) # x, y = passaro_amarelo.calcular_posicao(t) # print(' self.assert_passaro_posicao(%s, %s, ATIVO, passaro_amarelo, %s)' % (x, y, t)) + + def assert_posicao_vertical(self, y, tempo, passaro): + """ + Método auxiliar que mantém x fixo com valor 1, status Ativo, variando apenas o tempo e a posição y + Atenção, esse não é um teste porque não começa com prefixo "test" + """ + self.assert_passaro_posicao(1, y, ATIVO, passaro, tempo) + + +if __name__=='__main__': + teste= AtorTestes() + teste.teste_colisao_entre_atores_ativos() diff --git a/testes/fase_testes.py b/testes/fase_testes.py index 06072f398..dc0dcfbd3 100644 --- a/testes/fase_testes.py +++ b/testes/fase_testes.py @@ -1,230 +1,212 @@ # -*- coding: utf-8 -*- -from itertools import chain import os -from unittest.case import TestCase -import math import sys +from os import path +from unittest.case import TestCase + +project_dir = path.dirname(__file__) +project_dir = path.join('..') +sys.path.append(project_dir) +from placa_grafica_tkinter import rodar_fase project_dir = os.path.join(os.path.dirname(__file__), '..') project_dir = os.path.normpath(project_dir) sys.path.append(project_dir) -from atores import Obstaculo, Porco, PassaroVermelho, PassaroAmarelo, DESTRUIDO, ATIVO -from fase import Fase, Ponto -import placa_grafica +from atores import (Obstaculo, Porco, PassaroVermelho, PassaroAmarelo, + DESTRUIDO, ATIVO, DuploLancamentoExcecao) +from fase import Fase, Ponto, EM_ANDAMENTO, VITORIA, DERROTA + + +class AtorFake: + def __init__(self, x=0, y=0): + self.y = y + self.x = x + self.status = ATIVO + self.colidir_executado = False + self.calcular_posicao_executado = False + self.intervalo_colisao = None + + def calcular_posicao(self, tempo): + self.calcular_posicao_executado = True + + def colidir(self, outro_ator, intervalo): + self.colidir_executado = outro_ator.colidir_executado = True + self.intervalo_colisao = outro_ator.intervalo_colisao = intervalo + + def caracter(self): + return ' ' + + +class ObstaculoFake(AtorFake): + pass + + +class PorcoFake(AtorFake): + pass + + +class PassaroFake(AtorFake): + def __init__(self, x=0, y=0): + super().__init__(x, y) + self._lancado = False + self.colidir_com_chao_executado = False + + def foi_lancado(self): + return self._lancado + + def lancar(self, angulo, tempo): + if self._lancado: + raise DuploLancamentoExcecao() + self._lancado = True + + def colidir_com_chao(self): + self.colidir_com_chao_executado = True class FaseTestes(TestCase): def teste_adicionar_obstaculo(self): fase = Fase() self.assertListEqual([], fase._obstaculos) - obstaculo = Obstaculo() + obstaculo = ObstaculoFake() fase.adicionar_obstaculo(obstaculo) self.assertListEqual([obstaculo], fase._obstaculos) - obstaculo1, obstaculo2 = Obstaculo(), Obstaculo() + obstaculo1, obstaculo2 = ObstaculoFake(), ObstaculoFake() fase.adicionar_obstaculo(obstaculo1, obstaculo2) - self.assertListEqual([obstaculo, obstaculo1, obstaculo2], fase._obstaculos) + self.assertListEqual([obstaculo, obstaculo1, obstaculo2], + fase._obstaculos) def teste_adicionar_porco(self): fase = Fase() self.assertListEqual([], fase._porcos) - porco = Porco() + porco = PorcoFake() fase.adicionar_porco(porco) self.assertListEqual([porco], fase._porcos) - porco1, porco2 = Porco(), Porco() + porco1, porco2 = PorcoFake(), PorcoFake() fase.adicionar_porco(porco1, porco2) self.assertListEqual([porco, porco1, porco2], fase._porcos) def teste_adicionar_passaro(self): fase = Fase() self.assertListEqual([], fase._passaros) - passaro = PassaroVermelho() + passaro = PassaroFake() fase.adicionar_passaro(passaro) self.assertListEqual([passaro], fase._passaros) - passaro1, passaro2 = PassaroVermelho(), PassaroAmarelo() + passaro1, passaro2 = PassaroFake(), PassaroFake() fase.adicionar_passaro(passaro1, passaro2) self.assertListEqual([passaro, passaro1, passaro2], fase._passaros) - def teste_acabou_sem_porcos(self): fase = Fase() - self.assertTrue(fase.acabou(0)) + self.assertEqual(VITORIA, fase.status()) def teste_acabou_com_porcos_e_passaros(self): fase = Fase() - porcos = [Porco(1, 1) for i in range(2)] # criando 2 porcos - passaros = [PassaroAmarelo(1, 1) for i in range(2)] # criando 2 pássaros + porcos = [PorcoFake(1, 1) for _ in range(2)] # criando 2 porcos + passaros = [PassaroFake(1, 1) for _ in range(2)] # criando 2 pássaros fase.adicionar_porco(*porcos) fase.adicionar_passaro(*passaros) - self.assertFalse(fase.acabou(0)) - self.assertFalse(fase.acabou(2.9)) - self.assertFalse(fase.acabou(3)) - - # colidingo cada passaro com um porco no tempo 3 - for passaro, porco in zip(passaros, porcos): - passaro.colidir(porco, 3) + self.assertEqual(EM_ANDAMENTO, fase.status()) - self.assertFalse(fase.acabou(0)) - self.assertFalse(fase.acabou(2.9)) - self.assertTrue(fase.acabou(3)) + for ator in porcos + passaros: + ator.status = DESTRUIDO + self.assertEqual(VITORIA, fase.status()) fase.adicionar_obstaculo(Obstaculo()) - self.assertTrue(fase.acabou(3), 'Obstáculo não interfere no fim do jogo') + self.assertEqual(VITORIA, fase.status(), + 'Obstáculo não interfere no fim do jogo') - fase.adicionar_porco(Porco()) - self.assertTrue(fase.acabou(3), 'Com Porco ativo e sem pássaro para lançar, o jogo deveria acabar') + fase.adicionar_porco(PorcoFake()) + self.assertEqual(DERROTA, fase.status(), + 'Com Porco ativo e sem pássaro para lançar, o jogo ' + 'deveria acabar') - fase.adicionar_passaro(PassaroAmarelo()) - self.assertFalse(fase.acabou(3), 'Com Porco ativo e com pássaro para lançar, o jogo não deveria acabar') + fase.adicionar_passaro(PassaroFake()) + self.assertEqual(EM_ANDAMENTO, fase.status(), + 'Com Porco ativo e com pássaro para lançar, o jogo ' + 'não deveria acabar') def teste_status(self): fase = Fase() - porcos = [Porco(1, 1) for i in range(2)] - passaros = [PassaroAmarelo(1, 1) for i in range(2)] + porcos = [PorcoFake(1, 1) for _ in range(2)] + passaros = [PassaroFake(1, 1) for _ in range(2)] fase.adicionar_porco(*porcos) fase.adicionar_passaro(*passaros) - self.assertEqual('Jogo em andamento.', fase.status(0)) - self.assertEqual('Jogo em andamento.', fase.status(2.9)) - self.assertEqual('Jogo em andamento.', fase.status(3)) - - for passaro, porco in zip(passaros, porcos): - passaro.colidir(porco, 3) + self.assertEqual(EM_ANDAMENTO, fase.status()) - self.assertEqual('Jogo em andamento.', fase.status(0)) - self.assertEqual('Jogo em andamento.', fase.status(2.9)) - self.assertEqual('Jogo em encerrado. Você ganhou!', fase.status(3), - 'Sem porcos ativos o jogo deveria terminar com vitória') + for ator in porcos + passaros: + ator.status = DESTRUIDO + self.assertEqual(VITORIA, fase.status(), + 'Sem porcos ativos o jogo deveria terminar com ' + 'vitória') - fase.adicionar_obstaculo(Obstaculo()) - self.assertEqual('Jogo em encerrado. Você ganhou!', fase.status(3), + fase.adicionar_obstaculo(ObstaculoFake()) + self.assertEqual(VITORIA, fase.status(), 'Obstáculo não interfere para definir vitória') - porco = Porco() + porco = PorcoFake() fase.adicionar_porco(porco) - self.assertEqual('Jogo em encerrado. Você perdeu!', fase.status(3), - 'Com Porco ativo e sem pássaro para lançar, o jogo deveria acabar em derrota') + self.assertEqual(DERROTA, fase.status(), + 'Com Porco ativo e sem pássaro para lançar, o jogo ' + 'deveria acabar em derrota') - fase.adicionar_passaro(PassaroAmarelo()) - self.assertEqual('Jogo em andamento.', fase.status(3), - 'Com Porco ativo e com pássaro para lançar, o jogo não deveria acabar') + fase.adicionar_passaro(PassaroFake()) + self.assertEqual(EM_ANDAMENTO, fase.status(), + 'Com Porco ativo e com pássaro para lançar, o jogo ' + 'não deveria acabar') - porco.colidir(porco, 3) - self.assertEqual('Jogo em encerrado. Você ganhou!', fase.status(3), + porco.status = DESTRUIDO + self.assertEqual(VITORIA, fase.status(), 'Sem porco ativo, o jogo deveria acabar com vitória') def teste_lancar_passaro_sem_erro_quando_nao_existe_passaro(self): - passaro_vermelho, passaro_amarelo = PassaroVermelho(1, 1), PassaroAmarelo(1, 1) + passaros = [PassaroFake(1, 1) for _ in range(2)] fase = Fase() - fase.adicionar_passaro(passaro_vermelho, passaro_amarelo) - self.assertFalse(passaro_vermelho.foi_lancado()) - self.assertFalse(passaro_amarelo.foi_lancado()) + fase.adicionar_passaro(*passaros) + self.assertFalse(passaros[0].foi_lancado()) + self.assertFalse(passaros[1].foi_lancado()) fase.lancar(90, 1) fase.lancar(45, 3) - fase.lancar(31, 5) # testando que lançar passaros depios de todos lançados não causa erro - - self.assertTrue(passaro_vermelho.foi_lancado()) - self.assertEqual(math.radians(90), passaro_vermelho._angulo_de_lancamento) - self.assertEqual(1, passaro_vermelho._tempo_de_lancamento) + fase.lancar(31, + 5) # testando que lançar passaros depios de todos + # lançados não causa erro - self.assertTrue(passaro_amarelo.foi_lancado()) - self.assertEqual(math.radians(45), passaro_amarelo._angulo_de_lancamento) - self.assertEqual(3, passaro_amarelo._tempo_de_lancamento) + self.assertTrue(passaros[0].foi_lancado()) + self.assertTrue(passaros[1].foi_lancado()) - def teste_intervalo_de_colisao_padrão(self): + def teste_intervalo_de_colisao_padrao(self): ''' - Método que testa se o intervalo de colisão da Fase é repassado aos atores. Padrão de intervalo é 1 + Método que testa se o intervalo de colisão da Fase é repassado aos + atores. Padrão de intervalo é 1 ''' fase = Fase() - passaro = PassaroAmarelo(1, 1) + passaro = PassaroFake(1, 1) fase.adicionar_passaro(passaro) - porco = Porco(2, 2) + porco = PorcoFake(2, 2) fase.adicionar_porco(porco) fase.calcular_pontos(0) - self.assertEqual(DESTRUIDO, passaro.status(0)) - self.assertEqual(DESTRUIDO, porco.status(0)) + self.assertTrue(passaro.colidir_executado) + self.assertTrue(porco.colidir_executado) + self.assertTrue(passaro.calcular_posicao_executado) + self.assertTrue(passaro.colidir_com_chao_executado) + self.assertEqual(1, passaro.intervalo_colisao) + self.assertEqual(1, porco.intervalo_colisao) def teste_intervalo_de_colisao_nao_padrao(self): ''' - Método que testa se o intervalo de colisão da Fase é repassado aos atores. valor testado: 31 + Método que testa se o intervalo de colisão da Fase é repassado aos + atores. valor testado: 31 ''' fase = Fase(30) - passaro = PassaroAmarelo(1, 1) + passaro = PassaroFake(1, 1) fase.adicionar_passaro(passaro) - porco = Porco(31, 31) + porco = PorcoFake(31, 31) fase.adicionar_porco(porco) fase.calcular_pontos(0) - self.assertEqual(DESTRUIDO, passaro.status(0)) - self.assertEqual(DESTRUIDO, porco.status(0)) - - def teste_calcular_pontos(self): - fase_exemplo = criar_fase_exemplo() - expected = [Ponto(3, 3, 'V'), Ponto(3, 3, 'A'), Ponto(3, 3, 'A'), Ponto(31, 10, 'O'), Ponto(78, 1, '@'), - Ponto(70, 1, '@')] - self.assertListEqual(expected, fase_exemplo.calcular_pontos(0)) - - expected = [Ponto(31, 11, 'v'), Ponto(17, 25, 'A'), Ponto(3, 3, 'A'), Ponto(31, 10, ' '), Ponto(78, 1, '@'), - Ponto(70, 1, '@')] - self.assertListEqual(expected, fase_exemplo.calcular_pontos(4)) - - expected = [Ponto(31, 11, 'v'), Ponto(57, 30, 'A'), Ponto(69, 2, 'a'), Ponto(31, 10, ' '), Ponto(78, 1, '@'), - Ponto(70, 1, '+')] - self.assertListEqual(expected, fase_exemplo.calcular_pontos(7)) - - expected = [Ponto(31, 11, 'v'), Ponto(77, 2, 'a'), Ponto(69, 2, 'a'), Ponto(31, 10, ' '), Ponto(78, 1, '+'), - Ponto(70, 1, '+')] - self.assertListEqual(expected, fase_exemplo.calcular_pontos(8.5)) - - self.assertFalse(fase_exemplo.acabou(8.3)) - self.assertTrue(fase_exemplo.acabou(8.5)) - - def teste_resetar(self): - 'Testa se o método resetar de faze chama o método resetar de todos atores' - fase_exemplo = criar_fase_exemplo() - atores = list(chain(fase_exemplo._passaros, fase_exemplo._obstaculos, fase_exemplo._porcos)) - fase_exemplo.calcular_pontos(0) - - fase_exemplo.calcular_pontos(4) - - fase_exemplo.calcular_pontos(7) - fase_exemplo.calcular_pontos(8.5) - - self.assertFalse(fase_exemplo.acabou(8.3)) - self.assertTrue(fase_exemplo.acabou(8.5)) - # certificando que todos atore foram destruidos - for a in atores: - self.assertEqual(DESTRUIDO, a.status(8.5)) - for p in fase_exemplo._passaros: - self.assertTrue(p.foi_lancado(), 'Todos pássaros foram lançados') - fase_exemplo.resetar() - for a in atores: - self.assertEqual(ATIVO, a.status(8.5), 'Após resetar atore devem voltar a ficar ativos') - for p in fase_exemplo._passaros: - self.assertFalse(p.foi_lancado(), 'Nenhum pássaro foi lançado') - - -def criar_fase_exemplo(): - fase_exemplo = Fase() - passaros = [PassaroVermelho(3, 3), PassaroAmarelo(3, 3), PassaroAmarelo(3, 3)] - porcos = [Porco(78, 1), Porco(70, 1)] - obstaculos = [Obstaculo(31, 10)] - - fase_exemplo.adicionar_passaro(*passaros) - fase_exemplo.adicionar_porco(*porcos) - fase_exemplo.adicionar_obstaculo(*obstaculos) - - fase_exemplo.lancar(45, 1) - fase_exemplo.lancar(63, 3) - fase_exemplo.lancar(23, 4) - - for i in range(86): - fase_exemplo.calcular_pontos(i / 10) - return fase_exemplo - - -if __name__ == '__main__': - placa_grafica.animar(criar_fase_exemplo()) + self.assertEqual(30, passaro.intervalo_colisao) + self.assertEqual(30, porco.intervalo_colisao) diff --git a/testes/integracao.py b/testes/integracao.py new file mode 100644 index 000000000..11f2f0178 --- /dev/null +++ b/testes/integracao.py @@ -0,0 +1,188 @@ +# -*- coding: utf-8 -*- +from itertools import chain + +import os +from os import path +from unittest.case import TestCase +import math +import sys + +project_dir = path.dirname(__file__) +project_dir = path.join('..') +sys.path.append(project_dir) +from placa_grafica_tkinter import rodar_fase + +project_dir = os.path.join(os.path.dirname(__file__), '..') +project_dir = os.path.normpath(project_dir) +sys.path.append(project_dir) + +from atores import Obstaculo, Porco, PassaroVermelho, PassaroAmarelo, DESTRUIDO, ATIVO, \ + Ator, Passaro +from fase import Fase, Ponto, EM_ANDAMENTO, VITORIA, DERROTA + +class FaseTestes(TestCase): + def teste_acabou_com_porcos_e_passaros(self): + fase = Fase() + porcos = [Porco(1, 1) for i in range(2)] # criando 2 porcos + passaros = [PassaroAmarelo(1, 1) for i in range(2)] # criando 2 pássaros + fase.adicionar_porco(*porcos) + fase.adicionar_passaro(*passaros) + + self.assertEqual(EM_ANDAMENTO, fase.status()) + + # colidindo cada passaro com um porco no tempo 3 + for passaro, porco in zip(passaros, porcos): + passaro.colidir(porco, 3) + + self.assertEqual(VITORIA, fase.status()) + + fase.adicionar_obstaculo(Obstaculo()) + self.assertEqual(VITORIA, fase.status(), 'Obstáculo não interfere no fim do jogo') + + fase.adicionar_porco(Porco()) + self.assertEqual(DERROTA, fase.status(), 'Com Porco ativo e sem pássaro para lançar, o jogo deveria acabar') + + fase.adicionar_passaro(PassaroAmarelo()) + self.assertEqual(EM_ANDAMENTO, fase.status(), + 'Com Porco ativo e com pássaro para lançar, o jogo não deveria acabar') + + def teste_status(self): + fase = Fase() + porcos = [Porco(1, 1) for i in range(2)] + passaros = [PassaroAmarelo(1, 1) for i in range(2)] + fase.adicionar_porco(*porcos) + fase.adicionar_passaro(*passaros) + self.assertEqual(EM_ANDAMENTO, fase.status()) + + for passaro, porco in zip(passaros, porcos): + passaro.colidir(porco, 3) + + self.assertEqual(VITORIA, fase.status(), + 'Sem porcos ativos o jogo deveria terminar com vitória') + + fase.adicionar_obstaculo(Obstaculo()) + self.assertEqual(VITORIA, fase.status(), + 'Obstáculo não interfere para definir vitória') + + porco = Porco() + fase.adicionar_porco(porco) + self.assertEqual(DERROTA, fase.status(), + 'Com Porco ativo e sem pássaro para lançar, o jogo deveria acabar em derrota') + + fase.adicionar_passaro(PassaroAmarelo()) + self.assertEqual(EM_ANDAMENTO, fase.status(), + 'Com Porco ativo e com pássaro para lançar, o jogo não deveria acabar') + + porco.colidir(porco, 3) + self.assertEqual(VITORIA, fase.status(), + 'Sem porco ativo, o jogo deveria acabar com vitória') + + def teste_lancar_passaro_sem_erro_quando_nao_existe_passaro(self): + passaro_vermelho, passaro_amarelo = PassaroVermelho(1, 1), PassaroAmarelo(1, 1) + fase = Fase() + fase.adicionar_passaro(passaro_vermelho, passaro_amarelo) + self.assertFalse(passaro_vermelho.foi_lancado()) + self.assertFalse(passaro_amarelo.foi_lancado()) + fase.lancar(90, 1) + fase.lancar(45, 3) + fase.lancar(31, 5) # testando que lançar passaros depios de todos lançados não causa erro + + self.assertTrue(passaro_vermelho.foi_lancado()) + self.assertEqual(math.radians(90), passaro_vermelho._angulo_de_lancamento) + self.assertEqual(1, passaro_vermelho._tempo_de_lancamento) + + self.assertTrue(passaro_amarelo.foi_lancado()) + self.assertEqual(math.radians(45), passaro_amarelo._angulo_de_lancamento) + self.assertEqual(3, passaro_amarelo._tempo_de_lancamento) + + def teste_intervalo_de_colisao_padrão(self): + ''' + Método que testa se o intervalo de colisão da Fase é repassado aos atores. Padrão de intervalo é 1 + ''' + fase = Fase() + passaro = PassaroAmarelo(1, 1) + fase.adicionar_passaro(passaro) + porco = Porco(2, 2) + fase.adicionar_porco(porco) + fase.calcular_pontos(0) + self.assertEqual(DESTRUIDO, passaro.status) + self.assertEqual(DESTRUIDO, porco.status) + + def teste_intervalo_de_colisao_nao_padrao(self): + ''' + Método que testa se o intervalo de colisão da Fase é repassado aos atores. valor testado: 31 + ''' + fase = Fase(30) + passaro = PassaroAmarelo(1, 1) + fase.adicionar_passaro(passaro) + porco = Porco(31, 31) + fase.adicionar_porco(porco) + fase.calcular_pontos(0) + self.assertEqual(DESTRUIDO, passaro.status) + self.assertEqual(DESTRUIDO, porco.status) + + def teste_calcular_pontos(self): + fase_exemplo = criar_fase_exemplo() + expected = set([Ponto(3, 3, 'A'), Ponto(3, 3, 'A'), Ponto(31, 10, 'O'), Ponto(78, 1, '@'), + Ponto(70, 1, '@'), Ponto(3, 3, 'V')]) + self.assertSetEqual(expected, set(fase_exemplo.calcular_pontos(0))) + + fase_exemplo.lancar(45, 1) + + # i variando de 1 até 2.9 + for i in range(100, 300, 1): + fase_exemplo.calcular_pontos(i / 100) + + fase_exemplo.lancar(63, 3) + + # i variando de 3 até 3.9 + for i in range(300, 400, 1): + fase_exemplo.calcular_pontos(i / 100) + + fase_exemplo.lancar(23, 4) + + expected = set([Ponto(32, 11, 'v'), Ponto(17, 25, 'A'), Ponto(3, 3, 'A'), Ponto(31, 10, ' '), Ponto(78, 1, '@'), + Ponto(70, 1, '@')]) + + self.assertSetEqual(expected, set(fase_exemplo.calcular_pontos(4))) + + # i variando de 4 até 6.9 + for i in range(400, 700, 1): + fase_exemplo.calcular_pontos(i / 100) + + expected = set( + [Ponto(32, 11, 'v'), Ponto(57, 30, 'A'), Ponto(70, 2, 'a'), Ponto(31, 10, ' '), Ponto(78, 1, '@'), + Ponto(70, 1, '+')]) + + self.assertSetEqual(expected, set(fase_exemplo.calcular_pontos(7))) + + # i variando de 7 até 8.49 + for i in range(700, 849, 1): + fase_exemplo.calcular_pontos(i / 100) + print(fase_exemplo.calcular_pontos(8.5)) + + expected = set([Ponto(32, 11, 'v'), Ponto(77, 0, 'a'), Ponto(70, 2, 'a'), Ponto(31, 10, ' '), Ponto(78, 1, '+'), + Ponto(70, 1, '+')]) + + self.assertSetEqual(expected, set(fase_exemplo.calcular_pontos(8.5))) + + self.assertEqual(VITORIA, fase_exemplo.status()) + + +def criar_fase_exemplo(multiplicador=1): + fase_exemplo = Fase(1 if multiplicador == 1 else 32) + passaros = [PassaroVermelho(3 * multiplicador, 3 * multiplicador), + PassaroAmarelo(3 * multiplicador, 3 * multiplicador), + PassaroAmarelo(3 * multiplicador, 3 * multiplicador)] + porcos = [Porco(78 * multiplicador, multiplicador), Porco(70 * multiplicador, multiplicador)] + obstaculos = [Obstaculo(31 * multiplicador, 10 * multiplicador)] + + fase_exemplo.adicionar_passaro(*passaros) + fase_exemplo.adicionar_porco(*porcos) + fase_exemplo.adicionar_obstaculo(*obstaculos) + + return fase_exemplo + + +if __name__ == '__main__': + rodar_fase(criar_fase_exemplo(10)) diff --git a/testes/testes_placa_grafica.py b/testes/testes_placa_grafica.py index 025c04004..556328f98 100644 --- a/testes/testes_placa_grafica.py +++ b/testes/testes_placa_grafica.py @@ -14,8 +14,6 @@ def teste_inverter_coordenadas(self): self.assertTupleEqual((10, 0), placa_grafica.normalizar_coordenadas(10, placa_grafica.ALTURA - 1)) def teste_desenhar_frame_vazio(self): - self.maxDiff = None - class PontoCartesiano(): def __init__(self, x, y, caracter): self.caracter = caracter