EIC0020 - Laboratório de Computadores 2009/2010-2S 4 em Linha Turma 4, Grupo 2 Autores: Ana Sara Morais, 070509121, ei07121@fe.up.pt Eduardo Luís Barbosa, 080509100, ei08100@fe.up.pt 27 de Maio de 2010
1. Resumo O projecto desenvolvido teve como objectivo principal o desenvolvimento do tradicional jogo 4 em linha, com intuito de aprofundar o conhecimento sobre a utilização da interface de hardware dos periféricos de um computador, melhorar as capacidades de desenvolver software usando uma linguagem de baixo nível, assim como ganhar prática com as ferramentas de desenvolvimento do mesmo. Quanto ao hardware, foi utilizada a placa gráfica, usada em modo gráfico como interface do jogo; o teclado, usado através de interrupções com recurso a rotinas escritas em Assembly, de forma a fazer movimentar os elementos do jogo, permitindo usufruir da jogabilidade; e o altifalante, que permite a inclusão de som no jogo. A nível de software, foi utilizado o editor de texto usado nas aulas práticas, notepad++, para editar código, para compilar e testar o jogo, e devido aos poucos recursos existentes, recorremos à consola e ao software disponível nos laboratórios; foi criada a makefile como ferramenta de gestão de dependências entre vários módulos, usado SVN para a gestão das versões do projecto e Doxygen para gerar a documentação. Todo o projecto foi desenvolvido no sistema operativo Microsoft Windows 98. 2
2. Descrição do programa A aplicação desenvolvida, intitulada de 4 em Linha, segue as regras do jogo original, sendo um jogo para dois jogadores, sendo o objectivo principal colocar quatro peças da mesma cor, ou seja do mesmo jogador, em linha e impedir que o adversário consiga tal feito. O jogo termina quando todas as casas do tabuleiro estiverem preenchidas e ganha o jogador com maior pontuação, isto é, aquele que mais conseguir colocar quatro peças em linha. O jogo inicia com o menu que apresenta duas opções: Novo Jogo e Sair. Ao escolher a primeira opção é apresentado no ecrã um tabuleiro 6x6. As peças, em forma de estrela, são distinguidas por duas cores, uma para cada jogador. Ao longo do jogo é apresentado no ecrã a pontuação de casa jogador. 3
3. Implementação O diagrama UML apresentado ilustra, a relação entre os módulos criadas para a concepção e o desenvolvimento projecto. 4
3.1 Arquitectura do Programa Segue-se a apresentação dos módulos implementados e uma breve descrição de cada um deles: Funcs módulo onde foram desenvolvidas as funções que permitem a jogabilidade: mostrar menu, tabuleiro e permitir a jogada. QueueG equivalente ao módulo Queue, mas com a capacidade de generalização, isto é, aceita elementos de qualquer tipo; Music permite a emissão de efeitos sonoros, dispondo de toda a informação necessária para tocar música; Pixmap contém todos os sprites utilizados no decorrer do programa; Video-graphics - contém as funções necessárias ao uso da placa gráfica do computador em modo gráfico. Incluí as funções de entrada/saída do modo gráfico, de desenho e obtenção de propriedades de pixéis, em suma, este módulo contém todas as funções responsáveis pela parte gráfica da aplicação Ints está encarregue pelas interrupções, suporta todas as funções que permitem habilitar e inibir as mesmas, instalar e desinstalar os handlers respectivos. Kbc - KeyboardController, permite ao utilizador controlar o teclado. Vesa auxilia as funções relativas ao modo gráfico do jogo; Sprite compreende todas as funções relativas aos sprites utilizados durante o jogo, particularmente a função de leitura do pixmap, as funções para criação das sprites e do desenho dos mesmos no ecrã, encontrando-se ainda neste módulo a função de animação das sprites. 3.2 Funcionalidades Funcionalidades implementadas: Das funcionalidades planeadas para o desenvolvimento do projecto, foram implementadas funções para criar e mostrar tabuleiro, bem como colocar peças no mesmo. 5
Tal como planeado, foram, também, implementadas funções que permitem a criação de sprites e visualização das mesmas. Assim como uma função para mover a seta que permite a escolha da coluna onde vai ser colocada a peça. Funcionalidades não implementadas: Não foram implementados a utilização de porto série, a contagem do tempo limite para jogada nem a determinação da pontuação dos jogadores. 3.4 Instruções de compilação e utilização Para compilar o projecto basta fazer download do código-fonte, colocá-lo numa directoria à escolha, navegar até essa directoria, executar o comando make, caso pretenda limpar os ficheiros anteriormente criados (ficheiros com extensão.o, executáveis e bibliotecas) basta executar make clean. Target s principais do makefile são: main.o funcs.o ints.o video-graphics.o sprite.o kbd_isr.o music.o ints_asm.o timer.o kbc.o queue.o Ao compilar o programa geram-se as dependências necessárias para correr o jogo. Quando a compilação termina é gerado um ficheiro main.exe que pode então ser executado para jogar. Funcionamento do programa: O programa começa por exibir um menu com duas opções (Jogar e Sair). Para se movimentar nas opções do menu, o jogador introduz o número 1 ou 0, a selecção da opção escolhida é feita através do clique na tecla ENTER. A opção Jogar leva jogador até ao ambiente de jogo. Teclas Especiais: 6
direita; seta para a direita, possibilita movimentar a seta, indicadora da coluna, para a seta para a esquerda, possibilita movimentar a seta, indicadora da coluna, para a esquerda; ENTER permite colocar a peça na coluna escolhida, ou seja, efectuar a jogada; ESC terminar o programa; 7
4. Conclusões Inicialmente foi feito um planeamento do trabalho, que não foi cumprido devido a dificuldades técnicas, agravadas por falta de recursos para o desenvolvimento do projecto. Para uma melhor interface gráfica, decidimos utilizar sprites mas a visualização do tabuleiro e das peças do jogo, no entanto tivemos alguns problemas na utilização destes o que atrasou imenso o nosso trabalho. O resultado final é um jogo bastante incompleto, que apenas mostra um tabuleiro, 6 por 6 e que permite a colocação de peças. Se recomeçássemos o projecto do início, antes de começar a implementação, apostaríamos num desenho da solução mais cuidado e num melhor planeamento. Na hipótese de um limite de tempo mais alargado, conseguiríamos implementar a verificação de vitória de um jogador e o cálculo de pontuações. 8
Anexos 9
Anexo A - Documentação do Código Módulo: Funcs Código-fonte: Func.h e Funcs.c Mostra o Menu Inicial void displaymenu(); Cria o Tabuleiro Board createboard(int lin, int col); Mostra Tabuleiro void displayboard(board tab, char *testc); Inicia Jogo void startgame(int opc); Mostra jogada void play(board tab, char *testc); Movimenta a seta para a esquerda e para a direita void movearrow(sprite *seta, Board tab, char* base); Módulo: Ints Código-fonte: Ints.h e Ints.c Instala a função escrita em asm, irq_func int install_asm_irq_handler(int irq, void (*irq_func)(void), go32_dpmi_seginf *old_irq); Instala a função escrita em C, irq_func int install_c_irq_handler(int irq, void (*irq_func)(void),_go32_dpmi_seginfo *old_irq); Reinstala a função irq_func (asm), especificada em 'old_irq' void reinstall_asm_irq_handler(int irq, _go32_dpmi_seginfo *old_irq); Reinstala a função irq_func (C), especificada em 'old_irq' void reinstall_c_irq_handler(int irq, _go32_dpmi_seginfo *old_irq); Desactiva irq int disable_irq(int irq); 10
Activa irq void enable_irq(int irq); Módulo: Queue Código-fonte: Queue.h e Queue.c Inicializa a queue void queueinit(queue *q); Põe o char c na queue Bool queueput(queue *q, Byte c); Devolve o próximo elemento da queue int queueget(queue *q); Devolve true se a queue está vazia Bool queueempty(queue *q); Devolve true se a queue está cheia Bool queuefull(queue *q); Módulo: KBC Código-fonte: KBC.h e KBC.c Inicializa KBC int kbc_init(int debug); Escreve data no command register int write_kbc_cmd(unsigned data); int write_kbc_cmd_arg(unsigned data, unsigned arg); Escreve data no data register int write_kbc_data(unsigned data); int write_kbc_data_arg(unsigned data, unsigned arg); Devolve data read int read_kbc(void); 11
Escreve data IO addresse adr int write_kbc(unsigned adr, unsigned data); Módulo: Sprite Código-fonte: Sprite.h e Sprite.c Lê as sprites de pixmap.h char *read_xpm(char *map[], int *width, int *height); Cria Sprite Sprite* create_sprite(char *pic[]); Sprite* create_unbuffered_sprite(char *pic[]); Desenha sprite void draw_sprite(sprite *sprt, char *base); void draw_sprite_scaled(sprite *sprt, char *base, int width, int height); Replica sprite de modo a criar o tabuleiro void draw_board(sprite *sprt, char *base, int x, int y); Remove sprite void delete_sprite(sprite *sprt, char *base); void delete_sprite_scaled(sprite *sprt, char *base, int width, int height); void animate_sprite(sprite *sprt, char *base); Move sprite void move_sprite(sprite *sprt, int x, int y, char *base); Destrói sprite void destroy_sprite(sprite *sprt); Move na memória cujo endereço é 'base', o cursor "sprt, um Sprite standard, partir de sua posição atual para uma nova posição 'Xstep' e 'ystep de distância. void flip_buffer(char* dest, char* src); void flip_buffer_partial(char* dest, char* src, int xi, int yi, int width, int height); Módulo: Video_Graphics Código-fonte: Video_Graphics.h e Video_Graphics.c Entra no modo gráfico char * enter_graphics(int mode, dpmi_meminfo *map); 12
Sai do modo gráfico void leave_graphics( dpmi_meminfo map); Limpa ecrã void clear_screen(char color, char *base); 13
Anexo C Histórico do desenvolvimento 14
Anexo D - Makefile all: main.exe main.exe: main.o funcs.o ints.o video-graphics.o sprite.o kbd_isr.o music.o ints_asm.o timer.o kbc.o queue.o gcc main.o funcs.o ints.o video-graphics.o sprite.o kbd_isr.o music.o ints_asm.o timer.o kbc.o queue.o -o main.exe main.o: main.c gcc -Wall -c main.c -o main.o funcs.o: funcs.c funcs.h gcc -Wall -c funcs.c -o funcs.o ints.o: ints.c ints.h gcc -Wall -c ints.c -o ints.o video-graphics.o: video-graphics.c video-graphics.h gcc -Wall -c video-graphics.c -o video-graphics.o sprite.o: sprite.c sprite.h gcc -Wall -c sprite.c -o sprite.o kbd_isr.o: kbd_isr.asm nasm -o0 -t -f coff kbd_isr.asm -o kbd_isr.o music.o: music.c music.h gcc -Wall -c music.c -o music.o ints_asm.o: ints_asm.asm nasm -t -f coff ints_asm.asm -o ints_asm.o timer.o: timer.h timer.c gcc -Wall -c timer.c -o timer.o kbc.o: kbc.h kbc.c gcc -Wall -c kbc.c -o kbc.o queue.o: queue.h queue.c gcc -Wall -c queue.c -o queue.o clean: rm -rf *.o *.exe rebuild: clean all 15
Anexo E Proposta inicial Proposta de Projecto para Laboratório de Computadores 2009/2010 Nomes: Ana Sara Morais ei07121, Eduardo Luís Barbosa ei08100 Turma: 4 Grupo: 2 Título do Projecto: Quatro em Linha Tipo: Jogo de tabuleiro Baseado em: Jogo com o mesmo nome Periféricos a usar: Placa gráfica no modo gráfico, Timer e Altifalante, Teclado, RTC Interrupções: Teclado, RTC Funções Assembly: read_col, get_time Descrição: O nosso projecto basear-se-á no jogo do quatro em linha, cujo objectivo é que um jogador introduza fichas num tabuleiro de modo a que quatro destas formem uma linha na horizontal, vertical, ou na diagonal. O objectivo passa também por impedir que um segundo jogador possa fazer o mesmo. O jogo começará por mostrar um splash screen com música, e daí os jogadores poderão começar a jogar. É apresentado um tabuleiro com o formato de uma tabela, o qual vai sendo preenchido com as fichas dos jogadores, alternando entre os dois, até que um deles ganhe. Para um jogador fazer uma jogada, terá que especificar a coluna do tabuleiro onde quer colocar a sua ficha. Um efeito sonoro será tocado quando for feita uma jogada. No nosso projecto, cada jogador terá um limite de tempo, em segundos, para jogar. Caso não o respeite, o computador seleccionará uma coluna aleatória para jogar. 16
1ª semana Três objectivos: a) mostrar menu do jogo; b) criar e mostrar tabuleiro; c) colocar peças no tabuleiro através da implementação de interrupções do teclado; void displaymenu(); void createboard(int lin, int col); void displayboard(board); void play(int col); 2ª semana Três objectivos: a)criar sprites; b)animar peças; c)detectar quando as colunas do tabuleiro estão parcialmente ou totalmente preenchidas; Sprite *createsprite(char *pic[], char *base); void *drawsprite(char *vmap[], Sprite *sprt); int animate_pecas(sprite *peca, char *base); bool is_full(int col); 3ª semana Quatro objectivos: a) contar tempo limite da jogada através do RTC; b) determinar pontuações dos jogadores; c) fazer com que as jogadas alternem entre dois jogadores; d) verificar as condições de vitória. 4ª semana Quatro objectivos: a) tocar música durante o jogo usando interrupções do RTC; b) tocar efeitos sonoros; c) implementar jogo usando porto série; d) criar splash screen. 17