Guião 6 de POO - moodle.fct.unl.pt · Neste guião vamos exercitar conceitos como a herança e a...

16
Guião 6 de POO 2008/2009 1 Celebutante 1.0 Programação Orientada pelos Objectos, 2008/2009 Guião 6, versão 1.0 , 7 de Maio de 2009 (principais actualizações a vermelho) [email protected] Notas prévias: Este guião deve ser entregue no mooshak até às 23h55 de 18 de Maio. São fornecidos ficheiros de exemplo que deve usar para verificar a sua resolução com uma ferramenta do tipo diff antes de submeter o seu trabalho ao mooshak. As primeiras cinco submissões de cada tarefa não penalizam a cotação da tarefa. No entanto, por cada submissão subsequente serão descontados 10% da cotação da tarefa. Por favor inclua o seu nome e número de aluno em todos os seus ficheiros, num comentário com a tag @author. Objectivos didácticos deste guião Neste guião vamos exercitar conceitos como a herança e a polimorfia, bem como preparar a utilização de interfaces gráficas e eventos nos nossos projectos. O projecto será construindo de acordo com um padrão arquitectural recorrente na programação: o Model-View-Controller. Neste guião vamo-nos concentrar no modelo (Model). O Model-View-Controller é um padrão especialmente concebido para isolar claramente a parte lógica de um sistema da sua apresentação e interface com o utilizador. Por outras palavras, deve ser possível substituir a camada de interface e apresentação do sistema sem que isso tenha impacto na camada lógica do sistema, e vice-versa. O modelo (Model) representa a informação, ou dados, geridos pelo sistema. A vista (View) representa os elementos da interface visual do sistema com o seu utilizador, e pode incluir áreas de texto, representações gráficas, botões, etc. O controlador (Controller) é responsável pela comunicação entre o modelo e a vista. O guião cobre a primeira de 3 partes distintas: neste guião vamos concentrar-nos no modelo que está por detrás do jogo, que será avaliado com o apoio do mooshak; num segundo guião, vamos construir uma interface visual (a vista + controlador) para a aplicação que desenvolvemos na primeira fase. Como evidência do seu sucesso desse guião, deverá capturar um pequeno filme com a execução da sua aplicação, colocar o filme online e enviar através do moodle o respectivo link para que o docente possa assistir à demonstração do seu trabalho. Além disso, deverá submeter o código completo via moodle, juntamente com o link para o seu filme. Finalmente, num terceiro guião, faremos um upgrade ao motor do jogo, dotando-o de adversários mais inteligentes para o nosso personagem.

Transcript of Guião 6 de POO - moodle.fct.unl.pt · Neste guião vamos exercitar conceitos como a herança e a...

Page 1: Guião 6 de POO - moodle.fct.unl.pt · Neste guião vamos exercitar conceitos como a herança e a polimorfia, ... 20 jogadas, todos os convidados ... Depois de tocar uma música,

Guião 6 de POO 2008/2009

1

Celebutante 1.0

Programação Orientada pelos Objectos, 2008/2009

Guião 6, versão 1.0, 7 de Maio de 2009 (principais actualizações a vermelho)

[email protected]

Notas prévias: Este guião deve ser entregue no mooshak até às 23h55 de 18 de Maio.

São fornecidos ficheiros de exemplo que deve usar para verificar a sua resolução com uma ferramenta

do tipo diff antes de submeter o seu trabalho ao mooshak. As primeiras cinco submissões de cada tarefa

não penalizam a cotação da tarefa. No entanto, por cada submissão subsequente serão descontados

10% da cotação da tarefa. Por favor inclua o seu nome e número de aluno em todos os seus ficheiros,

num comentário com a tag @author.

Objectivos didácticos deste guião Neste guião vamos exercitar conceitos como a herança e a polimorfia, bem como preparar a utilização

de interfaces gráficas e eventos nos nossos projectos. O projecto será construindo de acordo com um

padrão arquitectural recorrente na programação: o Model-View-Controller. Neste guião vamo-nos

concentrar no modelo (Model).

O Model-View-Controller é um padrão especialmente concebido para isolar claramente a parte lógica de

um sistema da sua apresentação e interface com o utilizador. Por outras palavras, deve ser possível

substituir a camada de interface e apresentação do sistema sem que isso tenha impacto na camada

lógica do sistema, e vice-versa. O modelo (Model) representa a informação, ou dados, geridos pelo

sistema. A vista (View) representa os elementos da interface visual do sistema com o seu utilizador, e

pode incluir áreas de texto, representações gráficas, botões, etc. O controlador (Controller) é

responsável pela comunicação entre o modelo e a vista.

O guião cobre a primeira de 3 partes distintas: neste guião vamos concentrar-nos no modelo que está

por detrás do jogo, que será avaliado com o apoio do mooshak; num segundo guião, vamos construir

uma interface visual (a vista + controlador) para a aplicação que desenvolvemos na primeira fase. Como

evidência do seu sucesso desse guião, deverá capturar um pequeno filme com a execução da sua

aplicação, colocar o filme online e enviar através do moodle o respectivo link para que o docente possa

assistir à demonstração do seu trabalho. Além disso, deverá submeter o código completo via moodle,

juntamente com o link para o seu filme. Finalmente, num terceiro guião, faremos um upgrade ao motor

do jogo, dotando-o de adversários mais inteligentes para o nosso personagem.

Page 2: Guião 6 de POO - moodle.fct.unl.pt · Neste guião vamos exercitar conceitos como a herança e a polimorfia, ... 20 jogadas, todos os convidados ... Depois de tocar uma música,

Guião 6 de POO 2008/2009

2

Celebutante 1.0 Confesse lá. Está mortinho para saber o que é um Celebutante, não é? Estupendo! Aqui vai: Celebutante

é a contracção da palavra celebridade com a palavra debutante. Portanto, um celebutante é uma

celebridade debutante. O conceito é algo parecido com o de socialite, mas o celebutante é célebre,

enquanto que o socialite é convidado para tudo o que é festa, mesmo se não for famoso. Um

celebutante digno desse título é famoso por ser famoso. Ou seja, é alguém que é tremendamente

famoso, ninguém sabe muito bem porquê, dado ser absolutamente desprovido de qualquer talento

conhecido. Normalmente, é parente de alguém tremendamente rico. É uma razão como outra qualquer.

E o que faz na vida um Celebutante? hmmmm…errr... nada!

Em rigor, estamos a ser injustos. Um celebutante vai a festas, muitas festas, onde se esforça por

coleccionar o maior número possível de fotografias com todos os outros participantes das festas. Assim,

aparece nas revistas e vai ficando cada vez mais famoso.

O que lhe propomos neste guião é que crie o jogo de computador, o Celebutante, em que temos de

conduzir uma jovem celebutante rumo à glória dos mexericos. O objectivo da nossa heroína é ser

fotografada com todas as pessoas existentes numa festa, sem dar de caras com um dos seus ex-

namorados. Isso seria a morte social da celebutante. Desagradável que só visto. Tá a ver? Super!

Vamos então conhecer as regras do jogo. O jogo tem dois tipos de personagens. Um herói (a

celebutante) e um número indeterminado de vilões (os ex-namorados da celebutante que são uns

monstros a evitar o mais possível, pelo menos na perspectiva dela). Todos estes personagens deslocam-

se na festa. Os movimentos do herói são controlados pelo jogador. Os movimentos dos vilões são

controlados pelo computador.

No início, a festa tem um número indeterminado de convidados, para além dos nossos personagens que

estão espalhados pela casa. Felizmente, é gente muito parada, que não se mexe. Ou seja, têm uma

posição fixa, ao longo de toda a festa. Quando a celebutante passa pelo local onde está um destes

convidados, tira uma fotografia com ele. Para todos os efeitos, o convidado “deixa de existir” para o

jogo, porque a celebutante só tira uma fotografia com cada convidado. O convidado literalmente deixa

de aparecer no écran. A celebutante ganha 10 pontos de celebridade por cada fotografia conseguida. O

jogo termina quando a celebutante tirar fotografias com todos os convidados, ou quando esbarrar com

um ex-namorado (o que quer que aconteça primeiro).

Em determinadas localizações especiais, existem bandas musicais. Quando passa por uma banda, a

celebutante pode pedir, apenas uma vez, para que a banda toque o “Poeira”, da Ivete Sangallo. Durante

20 jogadas, todos os convidados dançam, levantando poeira. No meio de tanto pó, a celebutante tem

uma oportunidade dourada para passar por um ex-namorado e dar-lhe uma bofetada (200 pontos de

celebridade). Só pode dar uma bofetada a cada namorado, durante a música. Ao fim das 20 jogadas, o

pó assenta e os ex-namorados voltam a ser pessoas a evitar pela celebutante. Durante o período da

música, a celebutante continua a tirar fotos com os convivas por quem passa. Depois de tocar uma

música, a banda desaparece, a exemplo dos convidados já fotografados.

Page 3: Guião 6 de POO - moodle.fct.unl.pt · Neste guião vamos exercitar conceitos como a herança e a polimorfia, ... 20 jogadas, todos os convidados ... Depois de tocar uma música,

Guião 6 de POO 2008/2009

3

Os personagens deslocam-se numa de 4 direcções. Cima, baixo, esquerda e direita. Finalmente, e para

terminar esta descrição, a ausência de talento destes personagens é tanta que eles não conseguem

atravessar paredes. Ok? Lembre-se disto, que é um detalhe importante.

Se você não gosta de revistas de mexericos, pode pensar que o objectivo do seu herói é comer todas as

miniaturas de pastéis de nata de uma festa para a qual não foi convidado, enquanto foge aos

seguranças, que os bónus são luzes que se desligam, dando-lhe a possibilidade de dar um tabefe ao

segurança, enquanto a luz não volta. Mesmo esquema de pontos e restantes regras.

Se também não gosta de pastéis de nata, imagine que o seu herói é o Pac-Man, a comer pontinhos

brancos e a fugir aos fantasmas, excepto quando passa pelos locais de bónus, que lhe permitem atacar

os fantasmas durante um tempo limitado. Vai ver que o jogo é sempre o mesmo. Só muda a

apresentação. Pronto. Admitimos. O Celebutante não é mais que um Pacman disfarçado. Ainda não

tinha reparado?

Para lhe aguçar a curiosidade, aqui fica o aspecto dos maravilhosos jogos (Celebutante à esquerda,

Pacman à direita). Nós vamos explicar tudo com celebutantes e ex-namorados. Verá, dentro em breve,

que em vez de um jogo está a implementar pelo menos 3 jogos diferentes, com um mesmo modelo.

Uma nota final na introdução deste guião. Vamos começar por lhe explicar o que pretendemos em cada

uma das tarefas que vai submeter ao mooshak. Após a conclusão das 4 tarefas deste guião, terá

construído o motor destes jogos. ANTES de começar a implementar o que quer que seja, leia o guião

TODO. Depois de descrevermos o que pretendemos, vamos descrever detalhadamente a

implementação de todas as classes necessárias a este guião. A recomendação que lhe deixamos é a

seguinte: comece por criar versões esqueleto de todas as classes do motor do jogo, com a

implementação básica para permitir a compilação com sucesso. À medida que for progredindo nas

submissões ao mooshak, preencherá, progressivamente, a implementação de todas as classes. Não

avance para uma tarefa sem terminar com sucesso a anterior. A funcionalidade das tarefas anteriores é

sempre integrada nas tarefas seguintes.

Page 4: Guião 6 de POO - moodle.fct.unl.pt · Neste guião vamos exercitar conceitos como a herança e a polimorfia, ... 20 jogadas, todos os convidados ... Depois de tocar uma música,

Guião 6 de POO 2008/2009

4

Tarefa A – Leitura do mapa do jogo

Os palacetes em que decorrem as gloriosas festas da nossa celebutante são autênticos labirintos, com

vários corredores, salas que nunca mais acabam, etc.

A sua primeira tarefa no mooshak consiste precisamente na construção de labirintos. Vamos verificar

que a leitura do seu labirinto foi executada com sucesso. Leia da consola a especificação de um labirinto.

A estrutura dos dados na entrada é a seguinte: na primeira linha, temos dois inteiros, com a altura e

largura do labirinto. Seguem-se altura linhas, cada uma com largura carácteres, representando o

labirinto e seu conteúdo. Cada carácter dessa linha tem um significado especial:

„#‟ – Parede do labirinto

„ „ – Posição vazia no labirinto

„.‟ – Convidado mesmo a pedir uma fotografia

„H‟ – Herói, no caso, a nossa celebutante.

„R‟, „G‟, „B‟ – Monstros vermelho (Red), verde (Green), ou azul (Blue)

„O‟ – Ponto de bónus no labirinto

Assuma que, ao ler o estado inicial do labirinto, as posições iniciais do herói e dos monstros estão vazias.

Ou seja, se removêssemos os personagens, nessas posições estaria o carácter „ „. A especificação do

labirinto é seguida de uma sequência de comandos. Será assim, para todas as tarefas. Para já, temos

apenas dois comandos:

„p‟ – imprime o labirinto, no seu estado actual, representando todos os elementos acima

descritos.

„w‟ – imprime o labirinto, representando apenas as paredes.

Pode obter no moodle vários exemplos desta tarefa. Tenha em atenção que é absolutamente essencial

usar o diff para verificar que está realmente a produzir os resultados exactamente como o esperado. Por

exemplo, um dos testes é totalmente desprovido de paredes, mas os espaços em branco têm mesmo de

ser representados tal e qual como especificado. Aqui fica um pequeno exemplo. Este labirinto tem 9

linhas e 27 colunas. Temos um herói (H), quatro pontos de bónus (O), e três vilões, representados por R,

G e B. Repare que os dois comandos mandam escrever, sucessivamente, o labirinto preenchido e o

labirinto por preencher. Neste exemplo, tal como noutros que se seguem, representamos a verde, o

input, a preto, o output.

9 27

###########################

#...........O.............#

#.######.R######.H#######.#

#.#......#...........#....#

#O###....#...........#....#

#.#.....G#...........#....#

#.#.....O.######.....#....#

#.................B....O..#

###########################

p w

Page 5: Guião 6 de POO - moodle.fct.unl.pt · Neste guião vamos exercitar conceitos como a herança e a polimorfia, ... 20 jogadas, todos os convidados ... Depois de tocar uma música,

Guião 6 de POO 2008/2009

5

###########################

#...........O.............#

#.######.R######.H#######.#

#.#......#...........#....#

#O###....#...........#....#

#.#.....G#...........#....#

#.#.....O.######.....#....#

#.................B....O..#

###########################

###########################

# #

# ###### ###### ####### #

# # # # #

# ### # # #

# # # # #

# # ###### # #

# #

###########################

Tarefa B – Movimentos dos personagens

Nesta tarefa vamos aprender a movimentar os personagens. Implementemos na classe AvatarClass

os métodos up(), down(), left() e right() e move(). É natural que necessite de implementar

alguns métodos auxiliares, nesta ou noutras classes. Veja a discussão detalhada das classes e interfaces

a implementar, no fim do guião. Voltemos aos movimentos dos personagens. Lembre-se, ninguém

consegue atravessar paredes. No entanto, e porque esta gente vive num mundo de fantasia, as casas

têm uma propriedade especial. Se em duas paredes exteriores opostas existirem portas (ou seja,

espaços em branco) perfeitamente alinhadas, ao sair por uma das portas o personagem entra pela porta

oposta. Por exemplo, se as duas portas estiverem alinhadas horizontalmente e o personagem sair pela

porta do lado esquerdo, entra directamente pela porta do lado direito. Se sair pela porta do lado direito,

entra pela do lado esquerdo. O raciocínio é semelhante, quando pensamos em parede extremas

opostas, com portas alinhadas verticalmente. Falta saber o que acontece se um personagem tentar sair

por uma porta que não tem uma porta do lado oposto, perfeitamente alinhada. O que acontece é

simples. Não consegue sair. É como se esbarrasse na parede do lado oposto.

Redefina os mesmos métodos na classe HeroClass, de modo a que o nosso herói vá tirando as fotos

(com o efeito secundário de fazer desaparecer os convidados). Repare que, ao contrário do que

acontece com o nosso herói, os vilões não tiram fotos com os convidados, portanto, nada acontece a um

convidado quando o vilão passa por ele. Mas, para já, deixemos os vilões descansados. Vão ficar quietos,

nesta tarefa. Vamos ignorar o que acontece quando o herói passa por um ponto de bónus, ou mesmo

por um vilão. Ou seja, por agora, não acontece nada.

Vamos então ver se tudo isto funciona bem. A tarefa B do mooshak serve precisamente para verificar

que as regras de movimento estão correctas. Como sempre, use os exemplos disponíveis no moodle

para verificar que tudo está bem, com o diff, antes de submeter a sua tarefa ao mooshak. O formato de

Page 6: Guião 6 de POO - moodle.fct.unl.pt · Neste guião vamos exercitar conceitos como a herança e a polimorfia, ... 20 jogadas, todos os convidados ... Depois de tocar uma música,

Guião 6 de POO 2008/2009

6

entrada de dados é semelhante ao que existia para a task A, mas agora temos novos comandos a

especificar tentativas de movimentos. Algumas tentativas têm sucesso, outras nem por isso, quando os

personagens esbarram em paredes. Caso seja possível, o personagem desloca-se uma posição no

sentido solicitado. Caso isso não seja possível, pela existência de uma parede, o personagem fica

parado. Os comandos disponíveis são os seguintes:

„u‟ – Tenta deslocar o celebutante para cima.

„d‟ – Tenta deslocar o celebutante para baixo.

„l‟ – Tenta deslocar o celebutante para a esquerda.

„r‟ – Tenta deslocar o celebutante para a direita.

„m‟ – Tenta deslocar o celebutante para a última direcção por ele tomada.

A estes comandos juntam-se os anteriores, que nos permitem verificar o estado do labirinto, após

algumas jogadas. Nada como um pequeno exemplo, para ilustrar como tudo funciona. Voltemos ao

nosso conhecido labirinto, agora com duas pequenas alterações: Abrimos um par de portas no labirinto

anterior, alinhadas verticalmente. Lembre-se, é importante que elas estejam alinhadas.

A celebutante começa por tentar mexer-se, mas não consegue, por estar parada e sem direcção prévia.

Mandamos imprimir o mapa. Tudo conforme esperado? Avancemos. Ela tenta ir para a direita e esbarra

na parede. Depois, desloca-se quatro posições para cima. Imprimimos o labirinto de novo, e ei-la cá em

baixo. Saiu pela porta de cima, entrou pela de baixo. Repare o que aconteceu aos pontos que estavam

pelo trajecto. Desapareceram, tendo sido substituídos por espaços em branco. Vamos andar mais um

bocado. Três casas para a direita (a primeira, com „r‟, as restantes duas com „m‟, uma tentativa

falhada de descer que esbarra na parede, e uma casa para a esquerda. Ao longo deste processo a

celebutante passou por um ex-namorado, mas ignorou-o. Entretanto, tirou fotos por onde passou.

9 27

################# #########

#...........O.............#

#.######.R######.H#######.#

#.#......#...........#....#

#O###....#...........#....#

#.#.....G#...........#....#

#.#.....O.######.....#....#

#.................B....O..#

################# #########

m p r u u u u p r m m d l p

################# #########

#...........O.............#

#.######.R######.H#######.#

#.#......#...........#....#

#O###....#...........#....#

#.#.....G#...........#....#

#.#.....O.######.....#....#

#.................B....O..#

################# #########

Page 7: Guião 6 de POO - moodle.fct.unl.pt · Neste guião vamos exercitar conceitos como a herança e a polimorfia, ... 20 jogadas, todos os convidados ... Depois de tocar uma música,

Guião 6 de POO 2008/2009

7

################# #########

#...........O.... ........#

#.######.R######. #######.#

#.#......#...........#....#

#O###....#...........#....#

#.#.....G#...........#....#

#.#.....O.######.....#....#

#................HB ...O..#

################# #########

################# #########

#...........O.... ........#

#.######.R######. #######.#

#.#......#...........#....#

#O###....#...........#....#

#.#.....G#...........#....#

#.#.....O.######.....#....#

#................ BH ..O..#

################# #########

Tarefa C – Pontuações, bónus, fim de jogo

Nesta tarefa, os vilões continuam paralizados. Todo o resto do jogo deve funcionar. Os comandos são

exactamente os mesmos que vimos até agora, mas o programa faz mais que na tarefa anterior. Além do

tabuleiro, imprime uma linha indicando o total de pontos obtidos até ao momento, o tempo de

actividade que resta ao bónus (quando se passa por um bónus, é criado um contador inicializado a 20 e

que é decrementado, 1 por jogada, até chegar a 0; enquanto este contador for maior que 0, os vilões

estão vulneráveis e são representados com uma minúscula; os comandos p e w não decrementam este

contador. Depois de terminar o jogo, o único comando que funciona é o ‘P’. Ignoramos os outros.

Vamos a um exemplo, no mesmo labirinto. Começamos por ir a correr até ao bónus mais próximo, indo

primeiro para cima e depois para a esquerda. Imprimimos imediatamente antes e depois de consumir o

bónus. Seguimos para o vilão R, para lhe dar uma bela bofetada. Depois, como bons celebutantes,

fazemos algo estúpido. Vamos para cima, descemos de novo para ver que isso não dá pontos, voltamos

a subir e damos várias cabeçadas na parede. Quando o vilão acorda, perdemos o jogo sem glória,

atirando-nos nos seus braços e provocando assim um escândalo. Não esperava por esta, pois não? A

celebutante reconcilia-se com o desamado, perdendo assim o jogo. Uma triste comédia romântica.

9 27

################# #########

#...........O.............#

#.######.R######.H#######.#

#.#......#...........#....#

#O###....#...........#....#

#.#.....G#...........#....#

#.#.....O.######.....#....#

#.................B....O..#

################# #########

u l m m m p m p m m m d p u d u u u u u u u u u u u u u u u u d p

Page 8: Guião 6 de POO - moodle.fct.unl.pt · Neste guião vamos exercitar conceitos como a herança e a polimorfia, ... 20 jogadas, todos os convidados ... Depois de tocar uma música,

Guião 6 de POO 2008/2009

8

################# #########

#...........OH ........#

#.######.R######. #######.#

#.#......#...........#....#

#O###....#...........#....#

#.#.....G#...........#....#

#.#.....O.######.....#....#

#.................B....O..#

################# #########

Points: 50 Timer: 0 Game over: false

################# #########

#...........H ........#

#.######.r######. #######.#

#.#......#...........#....#

#O###....#...........#....#

#.#.....g#...........#....#

#.#.....O.######.....#....#

#.................b....O..#

################# #########

Points: 50 Timer: 20 Game over: false

################# #########

#........ ........#

#.######.H######. #######.#

#.#......#...........#....#

#O###....#...........#....#

#.#.....g#...........#....#

#.#.....O.######.....#....#

#.................b....O..#

################# #########

Points: 280 Timer: 16 Game over: false

################# #########

#........ ........#

#.######.H######. #######.#

#.#......#...........#....#

#O###....#...........#....#

#.#.....G#...........#....#

#.#.....O.######.....#....#

#.................B....O..#

################# #########

Points: 280 Timer: 0 Game over: true

Tarefa D – Dotar os vilões de “inteligência artificial”

Talvez seja um exagero falar em “inteligência” dos nossos vilões. Afinal, estamos a falar de pessoas que

são conhecidas por serem ex-namorados de celebutantes. Não se pode pedir demais. Ainda assim, os

nossos vilões têm o mérito de já não estar nas boas graças da nossa heroína, o que demonstra alguma

inteligência. Como é que esses dois neurónios dos nossos vilões se manifestam no jogo? Na prática,

vamos apenas estabelecer, por ordem de prioridades, o sentido em que os nossos vilões se tentam

Page 9: Guião 6 de POO - moodle.fct.unl.pt · Neste guião vamos exercitar conceitos como a herança e a polimorfia, ... 20 jogadas, todos os convidados ... Depois de tocar uma música,

Guião 6 de POO 2008/2009

9

deslocar em cada momento. A regra é simples: vamos criar uma lista circular de prioridades para cada

um deles. Em cada momento, o vilão vai tentar seguir o sentido que estiver seleccionado na sua lista de

prioridades. Se não conseguir, passa ao sentido seguinte. E assim sucessivamente. Temos três tipos de

vilões estúpidos neste jogo. Um vermelho, um verde e um azul. Cada um deles tem a sua lista de

prioridades. Se houver num jogo dois vermelhos, eles têm listas de prioridades iguais. Mas cada um tem

a sua, portanto, consoante a sua localização no labirinto, podem estar em estados diferentes.

Aqui ficam as 3 listas de prioridades, uma por cor. Por omissão, os vilões começam sempre na posição 0,

depois passam para a 1, a seguir para a 2, depois para a 3, mais tarde voltam à 0 e assim

sucessivamente.

Vilão 0 1 2 3

Vermelho UP DOWN LEFT RIGHT

Verde DOWN UP LEFT RIGHT

Azul RIGHT LEFT UP DOWN

Pensemos no caso do vilão vermelho. Enquanto puder, ele vai-se deslocar para cima. Mais cedo ou mais

tarde, é natural que encontre uma parede. Nessa altura, passa a deslocar-se para baixo e assim faz, até

encontrar uma parede. A partir dessa altura, desloca-se para a esquerda, até encontrar uma parede.

Finalmente, desloca-se para a direita, até encontrar uma parede. E o que acontece quando encontra

essa parede? Simples, volta ao princípio do vector de prioridades, ou seja, desloca-se para cima. Não é

um comportamento lá muito inteligente, pois não? Lá está. Nós começamos por avisar que eles eram

pouco espertos.

Mas não se preocupe. Nós temos um plano.

Num dos próximos guiões, você vai conhecer o futuro ex-marido da celebutante, que é, no fundo, o boss

deste jogo. Eles estão separados, mas são bons amigos, dizem as revistas da especialidade. Na verdade,

fontes anónimas próximas da mãe da celebutante dizem que essa amizade está para terminar, assim

que o ex-marido conseguir que a celebutante assine os papéis do divórcio. Dizem as más línguas que

este vilão implacável persegue a celebutante em busca do desejado autógrafo que lhe permitirá dar o

golpe do baú. E você vai ajudá-lo, a troco de uma simbólica percentagem paga em géneros (leia-se, em

pontos do respectivo guião).

Vá, mãos à obra. Este guião é bastante maior que o código que vai ter de escrever, mas mesmo assim

tem muito por onde se entreter. Idealmente, ao fim de uma semana deverá ter isto praticamente

pronto, para então tratar da bonecada.

Passe à página seguinte e veja como pretendemos que implemente este motor de jogo. É importante

que siga estas indicações, para que depois seja fácil acrescentar a interface gráfica e o ex-marido da

celebutante, claro.

Page 10: Guião 6 de POO - moodle.fct.unl.pt · Neste guião vamos exercitar conceitos como a herança e a polimorfia, ... 20 jogadas, todos os convidados ... Depois de tocar uma música,

Guião 6 de POO 2008/2009

10

Como implementar este guião? Ponto prévio. Como sempre, vamos implementar tudo isto dentro de um package chamado poo.

Além de uma classe Main, para ir fazendo os testes que lhe vamos solicitando, deverá ir criando, para

cada tarefa a entregar no mooshak, a seguinte estrutura de classes e interfaces, que deve usar como

referência para todo o guião. Use este diagrama como fonte de consulta para saber exactamente que

métodos são esperados em cada classe e interface. Siga estas indicações à risca, por favor. A explicação

para cada um destes elementos será dada já a seguir.

Observe o diagrama de classes em UML. Se não está bem recordado da notação, aqui vai um guia ultra

curto. Reveja a matéria, para mais detalhes.

No diagrama, o círculo no canto superior direito dos rectângulos indica que estamos a representar uma

interface, e não uma classe. Temos 3 interfaces, como pode observar. A primeira coisa a fazer neste

guião será mesmo declarar essas interfaces no seu projecto. Cada uma destas interfaces é

implementada por uma ou mais classes. A relação de implementação representa-se com uma linha

tracejada, com um triângulo junto à interface. Por exemplo, a classe MazeClass implementa a interface

Maze.

Os métodos e atributos marcados com + são públicos, com – privados, e com # protegidos (protected).

{readOnly} indica uma constante. Sublinhado indica que o atributo ou método é de classe (static).

Itálico significa abstracto (abstract).

O diamante na associação entre MazeClass e MazeItem significa que existe na classe MazeClass uma

variável (privada, denominada myMaze) que contém uma colecção de objectos do tipo MazeItem.

Sugerimos que implemente essa colecção usando uma tabela (private MazeItem[][] myMaze;). O

outro diamante, que aparece na associação entre MazeClass e Monster indica que MazeClass tem uma

colecção de objectos do tipo Monster. Embora o diagrama UML não especifique como devemos

implementar essa colecção, a sugestão que lhe damos é que a implemente como uma ArrayList

(private ArrayList<Monster> monsters;), dado que vai necessitar de uma para criar os iteradores de

monstros.

A seta de MazeClass para Hero significa que existe uma variável de instância privada, denominada hero,

declarada na classe MazeClass (ou seja, declarada como private Hero hero;). De igual modo, a seta

de AvatarClass para Maze significa que em AvatarClass deve declarar uma variável privada chamada

myMaze.

É evidente que você seria capaz de criar uma estrutura de classes por si só para resolver este problema.

Mas estamos a dar-lhe estas indicações de modo a deixar tudo pronto para que seja mais fácil construir

uma interface gráfica em cima deste motor de jogo, por um lado, e acrescentar o boss do jogo, por

outro.

Se necessitar de métodos privados ou protegidos adicionais, acrescente-os. Mas mantenha a interface

pública das classes, por favor. Reporte alguma falha importante que detecte ao seu docente.

Page 11: Guião 6 de POO - moodle.fct.unl.pt · Neste guião vamos exercitar conceitos como a herança e a polimorfia, ... 20 jogadas, todos os convidados ... Depois de tocar uma música,

Guião 6 de POO 2008/2009

11

A interface Maze

Pois bem, vamos criar uma interface java Maze para representar estes labirintos. Consulte o diagrama

para saber quais as operações a declarar na interface Maze. E, por favor, não confunda o conceito de

interface Java com as interfaces gráficas do guião que virá mais tarde, para implementar a interface

visual do jogo.

A operação getRows() devolve o número total de linhas no labirinto.

A operação getColumns() devolve o número total de colunas do labirinto.

A operação toString() devolve a representação em String do labirinto. Esta representação

deve incluir não só as paredes, mas também todos os elementos que se encontram no labirinto,

tais como as personagens e outros objectos que lá se encontrem.

Page 12: Guião 6 de POO - moodle.fct.unl.pt · Neste guião vamos exercitar conceitos como a herança e a polimorfia, ... 20 jogadas, todos os convidados ... Depois de tocar uma música,

Guião 6 de POO 2008/2009

12

A operação printWalls é semelhante a toString, mas apenas representa o labirinto,

ignorando os objectos e personagens que nele se encontram.

A operação elementAt(int r, int c) devolve o elemento na posição (r,c), em que r

representa as linhas (rows) e c representa as colunas (columns). Se não existir nenhum

elemento na posição (r,c) a operação devolve null.

A operação characterAt(int r, int c) devolve a personagem que se encontra na posição

(r,c), se lá estiver alguma personagem, ou null, se não existir nenhuma personagem nessa

posição. No caso de existir mais do que um personagem na posição (r,c), algo que pode

acontecer, por exemplo, se uma celebutante esbarrar com um ex-namorado. A operação

devolve, por esta prioridade, a celebutante, ou os ex-namorados, por ordem de criação no jogo.

Repare que esta ordem dos ex-namorados é a ditada no momento da criação do jogo. De cima

para baixo, da esquerda para a direita, tal e qual como quando lemos um texto em Português.

A operação getHero() devolve a nossa celebutante.

getMonsterIterator() devolve um iterador com todos os ex-namorados, que são iterados pela

sua ordem de criação.

A operação moveAll() faz movimentar todos os personagens do jogo, começando pela

celebutante, e continuando, por ordem, por cada um dos ex-namorados.

A operação moveMonsters() movimenta apenas os ex-namorados. Para que queremos duas

operações? Suponha que a celebutante está parada. Queremos poder mandar mover os

monstros, mesmo quando a celebutante está a descansar. Para evitar duplicação desnecessária

de código, sugerimos que use moveMonsters() como uma operação auxiliar de moveAll().

A operação consume(int r, int c) consome o recurso na posição (r, c). Esta operação

permite ao jogador ir ganhando pontos. Tantos quantos o recurso na posição (r, c) valer. Se

nessa posição existir um objecto do tipo Collectable, isso vale 10 pontos. Se existir um ex-

namorado assustado que ainda não levou uma bofetada desde que o período de bónus

começou, isso vale 200 pontos.

A operação isGameOver() devolve true se o jogo já terminou, false caso contrário. O jogo

termina quando não há mais objectos do tipo Consumable disponíveis, ou na sequência de um

escândalo.

A operação getCurrentPoints() devolve o número de pontos atingidos até ao momento neste

jogo. Como deve imaginar, estes são precisamente os pontos que foram sendo acumulados de

cada vez que se chamou a operação consume(int r, int c).

A operação getRemainingTime() indica quantas jogadas faltam para terminar o período de

bónus, iniciado quando a celebutante consome um bónus.

A operação updateMorale() actualiza a moral dos ex-namorados, consoante o valor devolvido

por getRemainingTime(). Se este último for 0, os ex-namorados estão no seu estado normal e

são perigosos para a celebutante. Se o tempo for maior que 0, os ex-namorados passam ao

estado assustado, em que podem ser esbofeteados pela celebutante. Adiante veremos como se

gere a moral dos ex-namorados.

A operação addPoints(int points) é uma operação auxiliar que permite adicionar pontos ao

jogo (por exemplo, na sequência de uma foto tirada).

Page 13: Guião 6 de POO - moodle.fct.unl.pt · Neste guião vamos exercitar conceitos como a herança e a polimorfia, ... 20 jogadas, todos os convidados ... Depois de tocar uma música,

Guião 6 de POO 2008/2009

13

A operação gameOver() termina o jogo.

A operação rowUp(int r) indica a linha imediatamente acima de r. Devolve r-1 sempre,

excepto quando r = 0. Nesse caso, devolve a última linha do labirinto.

A operação rowDown(int r) é a dual de rowUp(int r). Como calcula, devolve 0 quando r

corresponde à última linha do labirinto.

A operação colLeft(int c) devolve c-1, excepto quando c = 0. Nesse caso, devolve a última

coluna do labirinto.

A operação colRight(int c) devolve a coluna à direita de c, se existir, ou 0, caso contrário.

A classe MazeClass

Programe uma classe MazeClass que implementa a interface Maze. Para além das operações definidas

na interface, a sua classe deverá ter ainda um construtor que recebe a altura (r), largura (c), um

ArrayList<String> (m) em que cada String representa uma linha do labirinto. Repare que este

construtor não só cria a representação interna do labirinto, mas também cria todos os objectos dentro

do labirinto, bem como os personagens que por ele vão passear. Como pode observar no diagrama de

classes, pedimos-lhe que declare uma série de variáveis nesta classe:

maxRows representa o número de linhas do labirinto.

maxCols representa o número de colunas do labirinto. remainingCollectables representa o número de pessoas que ainda não foram fotografadas

no jogo.

remainingBonuses representa o número de bónus por consumir no jogo.

currentPoints representa o total de pontos obtidos no jogo.

scareTimer representa o tempo que falta até que os ex-namorados voltem ao seu estado normal, na sequência da activação de um bónus.

scandal indica se ocorreu um escândalo (se a celebutante foi encontrada por um ex-namorado.

myMaze representa uma tabela com todos os items guardados no labirinto (paredes, consumíveis, bónus e espaços livres).

monsters representa a lista de ex-namorados da celebutante.

hero representa a celebutante.

A interface MazeItem

Esta interface define duas operações partilhadas por todos os items que podem estar no labirinto:

toString() devolve a String correspondente ao item.

getPoints() devolve o número de pontos que o item vale.

A classe Empty

As instâncias desta classe representam posições que não têm nenhum item no labirinto.

toString() devolve a String “ “. Repare que há mesmo um espaço em branco na String.

getPoints() devolve 0.

Page 14: Guião 6 de POO - moodle.fct.unl.pt · Neste guião vamos exercitar conceitos como a herança e a polimorfia, ... 20 jogadas, todos os convidados ... Depois de tocar uma música,

Guião 6 de POO 2008/2009

14

A classe Bonus

As instâncias desta classe representam posições do labirinto em que se encontram bónus. Sempre que a

celebutante passar por um ponto de bónus, este é consumido e substituído por um objecto da classe

Empty. A classe Bonus implementa os seguintes métodos:

toString() devolve a string “O” (de bOnus).

getPoints() devolve 0 pontos.

A classe Collectable

As instâncias desta classe representam posições do labirinto em que se encontram objectos colectáveis.

Neste caso, convidados junto dos quais a celebutante pode tirar fotografias. Uma vez tirada a fotografia,

a instância de Collectable deixa de ter qualquer interesse para a celebutante. Amealhados os pontos,

ela é descartada e substituída por uma instância de Empty, no labirinto. A classe Collectable

implementa os seguintes métodos:

toString() devolve a String “.”.

getPoints() devolve 10 pontos.

A classe Wall

As instâncias desta classe representam as posições do labirinto ocupadas por paredes. As paredes são

intransponíveis para os personagens deste jogo. A classe Wall implementa os seguintes métodos:

toString() devolve a String “#”.

getPoints() devolve 0 pontos.

A interface Avatar

Esta interface é genérica para todas as personagens do nosso jogo. Como tal, vai conter as operações

necessárias à movimentação dessas personagens, obtenção das suas coordenadas, etc.

toString() devolve uma String com o carácter correspondente à personagem que implementa esta interface.

up() tenta deslocar o personagem para cima. Pode conseguir, ou não, dependendo da existência de uma parede a bloquear a progressão.

down() tenta deslocar a personagem para baixo. Pode conseguir, ou não.

left() tenta deslocar a personagem para a esquerda. Pode conseguir, ou não.

right() tenta deslocar a personagem para a direita. Pode conseguir, ou não.

getRow() devolve a linha em que a personagem se encontra.

getColumn() devolve a coluna em que a personagem se encontra.

setMaze(Maze aMaze) passa a referência do labirinto para a personagem.

isMoving() devolve true se a personagem tiver algum sentido de deslocação (por exemplo, para a esquerda), ou false, caso contrário.

move() ordena à personagem para se mover no sentido em que se tentou deslocar da última vez. Por exemplo, se anteriormente uma personagem se tentou deslocar para a direita, move() vai ordenar que se desloque para a direita. Se a última tentativa foi para baixo, move() vai tentar que se desloque para baixo. E assim sucessivamente.

Page 15: Guião 6 de POO - moodle.fct.unl.pt · Neste guião vamos exercitar conceitos como a herança e a polimorfia, ... 20 jogadas, todos os convidados ... Depois de tocar uma música,

Guião 6 de POO 2008/2009

15

A classe AvatarClass

Programe uma classe abstracta AvatarClass que implementa a interface Avatar. Numa primeira fase,

limite-se a implementar os getters e setters relativos às coordenadas das personagens deste jogo. Os

restantes métodos ficam para mais tarde. Ou seja, deixe a sua implementação vazia, mas implemente-

os, para que o seu programa possa compilar. Apesar de ser uma classe abstracta, vamos definir nela

algumas constantes que apresentamos de seguida. UP, DOWN, LEFT e RIGHT são constantes inteiras,

usadas apenas como forma de enumerar as possíveis direcções de forma consistente no programa (por

exemplo, quando queremos passar uma direcção por argumento, ou guardar a última direcção usada).

São declaradas com o modificador protected, para que possam ser acedidas nas sub-classes, mas não

noutras classes. Os valores atribuídos a estas constantes não são particularmente importantes, desde

que sejam inteiros e todos diferentes uns dos outros:

UP = 1

DOWN = 2;

LEFT = 3;

RIGHT = 4;

Além destas constantes, vamos também definir uma série de variáveis, todas declaradas com o

modificador private, que passamos a descrever:

isMoving vale true se a personagem tiver alguma direcção de movimentação, false caso contrário. Por exemplo, quando começa, a celebutante está parada.

myMaze guarda uma referência para o labirinto em que a personagem se movimenta.

row guarda a linha do labirinto em que a personagem se encontra.

col guarda a coluna do labirinto em que a personagem se encontra.

moveDirection guarda a direcção em que a personagem se tentou deslocar pela última vez.

A classe Hero

Programe uma classe HeroClass, subclasse de AvatarClass, que implementará o nosso herói. Numa

primeira fase, basta implementar o construtor e o método toString(), que deve devolver “H”.

Posteriormente, quando implementar os métodos para tentar deslocar a celebutante em cada uma das

direcções, sugerimos também que implemente o método privado updateMaze(), que consome os

recursos (com o método consume, declarado em Maze) que possam existir na posição de destino após a

tentativa de movimentação da celebutante.

A classe Monster

Programe uma classe Monster, subclasse de AvatarClass, que representa os vilões deste nosso jogo.

Esta classe é abstracta. Neste guião, apenas vamos criar uma sub-classe de Monster, denominada

StupidMonster. Como você imagina, estamos a criar esta classe abstracta a pensar num futuro guião,

em que implementaremos novos vilões. Mas, para já, concentremo-nos nesta classe abstracta. Vamos

criar algumas variáveis, que passamos a descrever:

isScared é uma variável de classe, ou seja, o seu valor é partilhado por todos os (ex-namorados) da nossa celebutante. O seu valor fica a true sempre que a celebutante passa por

Page 16: Guião 6 de POO - moodle.fct.unl.pt · Neste guião vamos exercitar conceitos como a herança e a polimorfia, ... 20 jogadas, todos os convidados ... Depois de tocar uma música,

Guião 6 de POO 2008/2009

16

um ponto de bónus. Durante o período activo do bónus, isScared mantem-se a true. Assim que o período do bónus termina, isScared volta ao estado false.

isHit é uma variável de instância que por omissão tem o valor false. No entanto, durante o período em que o bónus é activado, a celebutante pode dar uma bofetada num ex-namorado, e é nesse momento que a variável assume o valor de true. Este valor é mantido até ao final do período de bónus. Quando termina o bónus, o valor volta a false. Esta variável tem apenas um propósito. Durante um período de bónus, a celebutante só ganha pontos da primeira vez em que der uma bofetada num ex-namorado. Uma vez dada esta bofetada, a consulta a esta variável permite que não se continuem a contabilizar pontos por contactos posteriores, dentro do mesmo período de bónus. Naturalmente, se houver mais tarde um outro período de bónus, a celebutante pode, novamente, descarregar a sua fúria de novo no mesmo ex-namorado.

points guarda o número de pontos que este ex-namorado vale, quando leva uma bofetada. Além destas variáveis, necessitamos de algumas operações adicionais:

scare() é usada para assustar os monstros (ou seja, para colocar a variável isScared a true). Além disso, coloca isHit a false. Esta função deve ser chamada quando o bónus é activado.

encourage() coloca isScared a false, tal como isHit.

isScared() devolve true se o monstro está assustado, false caso contrário.

updateMaze() actualiza o estado do jogo após o movimento de um monstro. Repare, se o monstro se movimentar e chocar com a celebutante, isso significa que apanha um estalo, se estiver assustado, ou faz a celebutante perder o jogo, se não estiver.

getPoints() devolve o número de pontos que este monstro vale neste momento.

A classe StupidMonster

Programe a classe StupidMonster. Esta classe implementa todos os vilões, neste guião. Tal como o

nome indica, estes vilões não serão particularmente inteligentes, mas guardamos o verdadeiro boss

deste jogo para outro guião.

Como certamente se recorda, dissemos na explicação da tarefa D que pretendemos que implemente um

vector circular para tratar da escolha da direcção a tomar pelo monstro, em cada momento. Como

apenas existem 4 direcções possíveis, use um vector com 4 posições, em que coloca cada uma das

direcções possíveis, por ordem de prioridades iniciais. Use uma variável auxiliar para saber qual a

direcção escolhida pelo seu vilão num dado momento. Quando esse sentido estiver temporariamente

indisponível (porque o monstro esbarraria numa parede), passe ao sentido seguinte. Se chegar ao final

do vector, volte ao princípio. Garanta apenas que se o seu monstro estiver fechado em 4 paredes o seu

programa não entra num ciclo infinito. Seria a morte social, não tanto do monstro, mas pelo menos da

sua submissão ao mooshak.

Esta classe vai ter 3 variáveis de instância:

priorities – Vector de inteiros com 4 elementos, representando as prioridades do monstro.

currentPriorityIndex – Índice inteiro que indica qual a direcção a tomar em cada

momento. Por razões óbvias, este valor varia entre 0 e 3.

color – um carácter que pode assumir os valores „R‟, „G‟, e „B‟, consoante a cor do

monstro.