Algoritmos e Estruturas de Dados I Prof. Daniel M. Martin (daniel.martin@ufabc.edu.br) Aula 9 (laboratório)
O Labirinto
Descrição do problema O problema é achar o caminho entre dois pontos de interesse num labirinto Os dois pontos de interesse são o rato e o queijo Você deve ajudar o rato a achar o queijo
Exemplo
Descrição do problema O labirinto é um dado do problema e pode ser visto como uma matriz m x n m = número de linhas; n = número de colunas (no exemplo anterior m = n = 6) Você pode sempre supor que o labirinto é fechado, isto é, que há paredes ao redor do labirinto para impedir o rato de sair O labirinto será dado na entrada padrão codificado como um arquivo texto
Descrição do problema Codificação genérica de um labirinto: m n a 11 a 12 a 13... a 1m a 21 a 22 a 23... a 2m... a n1 a n2 a n3... a nm Cada a ij vale: 0 posição livre 1 posição rato 2 posição queijo 3 parede
Codificação do labirinto do exemplo Salvar o texto abaixo num arquivo teste.txt 6 6 3 3 3 3 3 3 3 1 3 2 0 3 3 0 3 3 0 3 3 0 0 0 0 3 3 0 3 0 3 3 3 3 3 3 3 3
Lendo o labirinto da entrada O arquivo main.c (antigo labirinto.c) contém uma função que lê um labirinto da entrada Os dados lidos são armazenados numa estrutura labirinto que é definida por: struct s_labirinto { int linhas; int colunas; int **M; } typedef struct s_labirinto labirinto;
Já implementado (em main.c) /* le os dados da entrada e devolve variável do tipo labirinto */ labirinto le_labirinto_da_entrada(); /* aloca espaço para uma matriz de inteiros com m linhas e n colunas */ // usada em le_labirinto_da_entrada() int **aloca_matriz(int m, int n); /* libera uma matriz M com m linhas que havia sido alocada dinamicamente */ void libera_matriz(int **M, int m);
Já implementado (em main.c) /* imprime o labirinto na saída padrão */ void imprime_labirinto(labirinto L); /* uma função main de exemplo de uso das funções de labirinto */ int main() { labirinto L = le_labirinto_da_entrada(); imprime_labirinto(l); } libera_matriz(l.m, L.linhas); return 0;
Compilando e rodando o exemplo No linux linux$ gcc main.c pilha.c item.c -o lab linux$ lab < teste.txt 3 3 3 3 3 3 3 1 3 2 0 3 3 0 3 3 0 3 3 0 0 0 0 3 3 0 3 0 3 3 3 3 3 3 3 3 linux$
Compilando e rodando o exemplo No windows Crie um projeto no Dev C++ (ou Code::blocks) Inclua os arquivos main.c, item.c e pilha.c (e também item.h e pilha.h) Compile seu programa (suponha que o executável receba o nome de lab.exe) Coloque o arquivo teste.txt no mesmo diretório do programa executável lab.exe
Compilando e rodando o exemplo No windows Clique no menu iniciar e digite o comando cmd e aperte a tecla ENTER Um prompt de comando se abre Navegue (usando o comando cd) até o diretório do seu executável (o mesmo do teste.txt)
Compilando e rodando o exemplo No windows C:\SeuProjeto> lab.exe < teste.txt 3 3 3 3 3 3 3 1 3 2 0 3 3 0 3 3 0 3 3 0 0 0 0 3 3 0 3 0 3 3 3 3 3 3 3 3 C:\SeuProjeto>
O que você deve fazer? Achar o caminho do rato ao queijo usando a estrutura de dados pilha e o algoritmo que será explicado mais adiante Para isso você deve usar os arquivos pilha.h, pilha.c, item.h e item.c que estão em labirinto.tar.gz Você não pode modificar esses arquivos! (O pessoal das 8:00 pode, mas é preferível que, em vez disso, usem a nova versão no site.) Estude o arquivo main.c (antigo labirinto.c) Você deverá começar o trabalho a partir dele
Usando a estrutura labirinto labirinto L = le_labirinto_da_entrada(); // para acessar as dimensões // do labirinto L você pode fazer int m = L.linhas, n = L.colunas; /* para acessar o código (0, 1, 2 ou 3) da linha i e coluna j do labirinto, usar a matriz L.M */ codigo = L.M[i][j]; Veja, por exemplo, o código da função imprime_labirinto() em labirinto.c
Como explorar o labirinto? Você precisará uma variável P do tipo pilha A pilha armazena objetos do tipo item O tipo item, para nós, será uma posição no labirinto, e será declarado em item.h: struct s_ponto { int i; // num da linha int j; // num da coluna }; typedef struct s_ponto item;
Arquivo: item.h #ifndef ARQUIVO_ITEM_H #define ARQUIVO_ITEM_H struct s_ponto { int i; int j; }; typedef struct s_ponto item; // imprime um item na tela (não pula linha) void imprime_item(item x); #endif
Arquivo: item.c #include "item.h" #include <stdio.h> void imprime_item(item x) { printf(" (%d, %d)", x.i, x.j); }
Primeiros passos Fazer uma função cujo cabeçalho é: item posicao_do_rato(labirinto L); que recebe um labirinto e devolve a posição do rato (devolve um item com o campo i contendo a linha do rato e o campo j contendo a coluna do rato) (Turma das 10:00) Criar aloca_matriz_itens baseada na função aloca_matriz que aloca uma matriz de item em vez de int
Primeiros Passos (Turma das 10:00) Alocar matriz de item D com as mesmas dimensões do labirinto para guardar os predecessores (Turma das 8:00) Alocar matrizes de inteiros Di e Dj com as mesmas dimensões do labirinto para guardar cada coordenada dos predecessores O pessoal das 8:00 também pode fazer o que o pessoal das 10:00 fez. Venha na minha sala se você precisar de ajuda
Como explorar o labirinto? declarar variáveis t e pos_atual do tipo item lê labirinto L da entrada criar pilha vazia P colocar posição inicial do rato em P alocar matriz D (turma das 10) alocar matrizes Di e Dj (turma das 8) enquanto pilha P não está vazia faça pos_atual = pop(p) /* o trecho a seguir tenta explorar o norte */ t.i = pos_atual.i 1; t.j = pos_atual.j; se posição t não é parede (e não foi processada) então push(p, t) por em D (ou Di e Dj) que se chegou em t por pos_atual
Como explorar o labirinto? /* o trecho a seguir tenta explorar o sul */ t.i = pos_atual.i + 1; t.j = pos_atual.j; se posição t não é parede (e não foi processada) então push(p, t) guarda na matriz D que o predecessor de t é pos_atual /* o trecho a seguir tenta explorar o oeste */ t.i = pos_atual.i; t.j = pos_atual.j 1; se posição t não é parede (e não foi processada) então push(p, t) guarda na matriz D que o predecessor de t é pos_atual
Como explorar o labirinto? /* o trecho a seguir tenta explorar o leste */ t.i = pos_atual.i; t.j = pos_atual.j + 1; se posição t não é parede (e não foi processada) então push(p, t) guarda na matriz D que o predecessor de t é pos_atual marca pos_atual como processada (ou como parede) // por exemplo fazendo L.M[pos_atual.i][pos_atual.j] = 3; fim do enquanto
Exemplo de tradução do início do pseudo-código para linguagem C pilha P; labirinto L = le_labirinto_da_entrada(); item t, pos; push(p, posicao_do_rato(l)); while(!pilha_vazia(p)) { pos = pop(p); t.i = pos.i 1; t.j = pos.j; if (L.M[t.i][t.j]!= 3) { push(p, t); D[t.i][t.j] = pos; } etc...
Como usar a matriz D para imprimir o caminho? Pessoal, o mais fácil de fazer (com a matriz D) é imprimir o caminho do queijo até o rato, ou seja o caminho inverso ao desejado. Vamos fazer isso primeiro e depois modificamos a função para imprimir o caminho certo
Como usar a matriz D para imprimir o caminho reverso? IMPRIME_CAMINHO_REV pos pos_queijo enquanto pos!= pos_rato faça imprime pos pos D[ pos.i][pos.j]; imprime pos_rato
Como usar a matriz D para imprimir o caminho do rato até o queijo? IMPRIME_CAMINHO_CERTO cria pilha vazia Q pos pos_queijo push(q, pos); enquanto pos!= pos_rato faça pos D[ pos.i][pos.j]; push(q, pos); enquanto pilha Q não está vazia faça imprime pop(q)
Como usar a matriz D para imprimir o caminho do rato até o queijo? IMPRIME_CAMINHO_CERTO cria pilha vazia Q pos pos_queijo push(q, pos); enquanto pos!= pos_rato faça // A turma das 8 pode fazer t pos pos.i Di[ t.i][t.j]; pos.j Dj[ t.i][t.j]; push(q, pos); enquanto pilha Q não está vazia faça imprime pop(q)
Instruções Entregar somente o seu arquivo main.c (não é para entregar nenhum outro arquivo junto) Não enviar compactado A turma das 8:00 deve adaptar o programa para usar os arquivos pilha.h, pilha.c, item.h e item.c em labirinto.tar.gz (seu programa deveria funcionar automaticamente, exceto se você definiu o tipo ponto que deve ser a mesma coisa que item e se você usou x,y em vez de i,j na struct s_ponto)
Instruções Entregar até meia noite de quarta-feira 14/03 Entrega posterior fica com zero O assunto do e-mail deve ser AED1-EL4