Algoritmos e Estruturas de Dados [LEEC] (2023/2024)

Enunciado do Trabalho Prático

[Calendário] [Changelog] [Introdução] [Descrição] [Interface] [Avaliação] [Mooshak] [FAQ] [Observações]


Calendário


Changelog


Introdução

Este enunciado descreve o trabalho prático da disciplina de Algoritmos e Estruturas de Dados do 1º ano da Licenciatura em Engenharia Eletrotécnica e Computadores. Os alunos podem e devem tirar todas as dúvidas com os docentes da disciplina.

O trabalho é para ser realizado por grupos de 2 alunos do mesmo turno. Qualquer exceção a esta regra, se porventura vier a existir, terá de ter por base uma autorização por escrito (por ex. por email) dos docentes da disciplina.

O trabalho tem 2 fases de entrega. Serão aqui anunciados os detalhes nos próximos dias, incluindo as duas datas de entrega.

Vão ser realizadas discussões do projeto.

O programa deve ser escrito em ANSI-C (a norma oficial do C), e deve ser desenvolvido usando a metodologia de programação da disciplina. Se quiser, pode usar este código como ponto de partida: drones.zip [basta clicar para fazer download].

Este código oferecido respeita a metodologia de programação que foi apresentada na aula teórica 9. Mas o código tem algumas imperfeições nos detalhes: (1) faltam os comentários nos ficheiros ".h", incluindo as tão importantes precondições; (2) há um erro ao escrever-se "eh" em vez de "e'", (3) o comando T não está a validar o argumento (que não pode ser negativo); talvez haja mais casos... São sinais de que este código é para ser olhado criticamente e poderá precisar de modificações.


Programação nas Alturas: Uma Aventura com Drones!

Objetivo

O objetivo deste trabalho é desenvolver um programa que efetue a gestão duma frota de drones que entregam encomendas feitas num site de comércio eletrónico. Os drones estão estacionados numa base de drones onde também existe um grande armazém que guarda os produtos a enviar.

Drones

Os drones voam, transportando pacotes da base até ao endereço de clientes e depois regressam à base. As coordenadas da base são conhecidas e os drones servem apenas os clientes situados na zona geográfica à volta da base. Vamos considerar que a latitude e a longitude são dois simples números inteiros, que exprimem distâncias em km.

Para além da gestão dos drones e das encomendas, a aplicação efetua simulação da passagem do tempo. Por exemplo, num dado momento é possível saber em que ponto da sua viagem se encontra um drone. A resolução do relógio é 1 hora, o que significa que iremos trabalhar sempre com tempos que são múltiplos de 1 hora. Quando a aplicação começa, o relógio começa com o valor zero.

Cada drone possui um identificador único (um inteiro que é atribuído automaticamente no momento da criação, sequencialmente a começar em 1), uma distância máxima de voo disponível antes de precisar de manutenção, e um limite de carga transportável. Poderá guardar ainda mais informação, dependendo da forma como forem implementadas as funcionalidades do programa.

Os drones voam a velocidade constante de 10 km/hora. A distância de ida é sempre arredondada para cima, para o primeiro múltiplo de 10, para garantir que o tempo de voo é um múltiplo de 1 hora. Note também que, quando um drone faz uma viagem, ele percorre a distância entre a base e a localização do cliente, mas depois precisa de regressar à base, o que duplica a distância percorrida. Por exemplo, se o endereço do cliente se encontrar a 24 km, isso significa que se considera que a viagem de ida é de 30 km e que vai durar três horas. A viagem de regresso também é de 30 km e dura 3 horas.

A razão de ser de todos estes arredondamentos é evitar a inexatidão dos números reais, que poderia ser um problema no Mooshak.

Encomendas

Cada encomenda possui um identificador único (um inteiro que é atribuído automaticamente no momento da criação a começar em 1001), um peso, as coordenadas de destino e ainda três valores temporais relevantes que são registados quando estiverem disponíveis: o momento em que a encomenda foi feita, o momento em que saiu da base, e o momento em que chegou ao destino. Poderá guardar ainda mais informação, dependendo da forma como forem implementadas as funcionalidades do programa.

Manutenção

Sempre que um drone básico regressa duma viagem, vai automaticamente para manutenção, o que envolve carregar a bateria e substituir algumas peças de desgaste rápido. O tempo de manutenção é constante e igual para todos os drones básicos: são 3 horas. Terminada a manutenção, o drone fica novamente disponível para transportes futuros. Evidentemente que, durante o período de manutenção, um drone não está disponível para efetuar transportes.

Drones básicos e drones coletivos

É possível recorrer à ação conjunta dum coletivo de drones para conseguir transportar as encomendas mais pesadas. Um coletivo comporta-se, em muitos aspetos, como um drone maior.

Vamos então trabalhar com os conceitos de drone básico e de drone coletivo. A partir de agora, a palavra drone será usada para referir quaisquer elementos destas duas categorias.

Drones coletivos

Um drone coletivo é constituído por uma coleção de drones básicos (é proibido um drone coletivo fazer parte de outro drone coletivo). Um drone básico que faça parte dum coletivo, só efetua transportes integrado no coletivo. Está disponível uma operação para criar drones coletivos e outra para destruir drones coletivos, deixando livres os drones básicos constituintes. Para criar um novo drone coletivo, só se podem usar drones básicos que estejam livres, ou seja, estacionados à espera de trabalho e que não façam já parte de algum drone coletivo.

Um drone coletivo combina a capacidade dos seus drones básicos, adicionando as capacidades de transporte desses drones. Já o alcance máximo de voo dum drone coletivo é limitado pelo alcance mais baixo de qualquer um dos seus componentes.

Não é possível fazer a manutenção direta dum drone coletivo. É preciso primeiro destruir o coletivo. Quando se destrói um drone coletivo, os seus elementos retomam a sua individualidade e são imediatamente alvo de manutenção. Normalmente, o utilizador do sistema cria um drone coletivo especificamente para fazer o envio duma encomenda pesada, e depois destrói esse drone coletivo logo após o seu regresso (mas não é obrigado a fazer isso).


Interface com o utilizador - Comandos

O programa funciona através de comandos. É o utilizador que regista as encomendas, cria os drones, envia as encomendas, manda efetuar manutenções, etc.

Nesta secção, após alguma informação geral sobre os comandos, iremos apresentar todos os comandos que o sistema deve ser capaz de executar.

Formatos

Os comandos são pedidos ao utilizador usando o prompt '> '. Cada comando é identificado por um caráter que, no caso de ser uma letra, pode ser dada em maiúsculas ou minúsculas. Por exemplo, o comando de criação dum drone básico pode ser introduzido pelo utilizador usando a forma 'b' ou 'B'.

O formato do output dos comandos é rígido, para poder avaliado pelo Mooshak.

Nos exemplos deste documento, todo o texto introduzido pelo utilizador aparece em negrito. Já texto escrito pelo programa aparece de forma normal. Em diversos contextos explicitamos as mudanças de linha usando o símbolo '↩'.

Comandos com erros

Se o utilizador introduzir um comando desconhecido, o programa escreve a mensagem "Comando desconhecido.↩". Por exemplo:

Muitos dos comandos possuem argumentos, que no nosso caso serão sempre números inteiros, geralmente não negativos. Por favor, assuma que o utilizador nunca comete o erro de introduzir uma string ou um número real em vez dum número inteiro. O utilizador poderá é enganar-se na quantidade de argumentos e também poderá introduzir valores negativos por engano (só são permitidos inteiros negativos na especificação de coordenadas). Estes dois últimos aspetos precisam de ser validados.

Se o utilizador introduzir um comando com um erro destes, o programa escreve a mensagem "Dados invalidos.↩". Por exemplo:

Qualquer outra situação em que o utilizador tente fazer algo de impossível, por exemplo tentar fazer voar um drone que não existe ou que está em manutenção, o programa escreve escrever a mensagem "Nao pode fazer isso.↩". Há dezenas de possibilidades relativamente a pedidos impossíveis de concretizar, mas a mensagem de erro é sempre a mesma. Por exemplo, assumindo que o drone 3 está em manutenção:

Comando '.' - Sair

Não tem argumentos. Termina a execução do programa. Escreve a mensagem "Obrigado. Volte sempre!".

Comando '?' - Ajuda

Não tem argumentos. Mostra os comandos disponíveis. Repare que cada comando é identificado por uma letra maiúscula que aparece algures no respetivo texto. Por exemplo, 'B' diz respeito à palavra "Basico", que ocorre no meio do texto.

Comando 'B' - cria drone Básico

Tem dois argumentos: a capacidade de carga e o alcance do novo drone. O novo drone fica registado na base de drones. Como se vê, é mostrada muita informação sobre o drone:

Comando 'C' - cria drone Coletivo

Tem o máximo de 6 argumentos, que são interpretados como identificadores de drones básicos existentes. Como se vê, as características do novo drone são derivadas dos drones constituintes, usando as regras já explicadas. Também é mostrado um novo campo:

Comando 'D' - Despacha encomenda

Tem dois argumentos: o id da encomenda a enviar e o id do drone a usar no transporte. O drone começa a voar imediatamente, se estiver disponível para o envio.

Um drone pode não estar disponível para um envio. Um drone básico não está disponível para um envio: se já estiver a voar; ou se estiver em manutenção; ou se fizer parte dum drone coletivo; ou se não tiver alcance suficiente para a encomenda; ou se não tiver capacidade de carga suficiente para a encomenda. Um drone básico não está disponível para um envio: se já estiver a voar; ou se não tiver alcance suficiente para a encomenda; ou se não tiver capacidade de carga suficiente para a encomenda.

Claro que uma encomenda que está no ar a ser transportada, ou que já foi enviada no passado, não pode ser enviada outra vez.

Comando 'E' - cria Encomenda

Tem três argumentos: o peso, a latitude e longitude do destino. Cria a encomenda e regista-a na base, para envio futuro. Como se vê, é mostrada muita informação sobre a encomenda:

Comando 'L' - Lista

Não tem argumentos. Lista primeiro todos os drones pela ordem de registo e depois todas as encomendas, pela ordem de registo.

Se não existir registado qualquer drone ou encomenda, o comando escreve "Nao existem drones nem encomendas↩".

Comando 'R' - Remove drone

Tem apenas um argumento: o id dum drone. Em princípio, o drone é removido da base e destruído. Mas um drone básico só pode ser removido se não fizer parte dum coletivo, nem estiver a voar, nem estiver em manutenção. Um drone coletivo só pode ser removido se não estiver a voar. Quando um drone coletivo é removido, os drones básicos constituintes permanecem vivos e entram imediatamente em manutenção. As encomendas nunca são removidas.

Comando 'T' - avança Tempo

Tem apenas um argumento: o número de horas a avançar. O avanço do tempo tem muitas consequências internas no estado do programa, mas elas só podem ser observadas através do comando 'L'.

Avaliação

Eis como a nota será calculada:

A maioria dos testes de avaliação do Mooshak será conhecida previamente. Mas não todos, pois alguns só serão divulgados depois do prazo de entrega do trabalho. Teste o seu programa usando os princípios estudados na teórica 7. Um grupo teste o seu programa de forma mais exaustiva, espera-se que obtenha uma nota um pouco melhor do que um grupo que não o faça.

A apreciação da qualidade, inclui pelo menos estes elementos:

Duas fases

Há duas fases de entrega do trabalho, que aparecem descritas no final dos slides da teórica 9, mas que se repete aqui: Ainda não há a certeza de qual o peso relativo de cada fase na nota, mas neste momento já se pode garantir que a 1ª fase valerá pelo menos 50% da nota.

Discussões

Prova de avaliação escrita

Juntamente com o 2º teste de AED, vai ocorrer uma pequena prova de avaliação escrita sobre o trabalho. Essa prova já se considera parte da discussão do trabalho. Os alunos levam para a prova uma listagem com o código que escreveram - ficheiros ".c" e ficheiros ".h".

A prova de avaliação escrita sobre o trabalho incide sobre a primeira parte do trabalho. A duração desta prova vai ser 20 minutos.

A listagem tem obrigatoriamente de corresponder ao código duma submissão que esteja presente no Mooshak, e cada aluno precisa de indicar o número dessa submissão na folha das respostas. O papel das listagem não se entrega no final da prova. Para corrigir as respostas dos alunos, os professores irão consultar o código das submissões dentro do próprio Mooshak.

Apesar da avaliação escrita incidir sobre a primeira parte do trabalho, será permitido e perfeitamente normal um aluno levar o código duma submissão recente da 2ª fase. As respostas serão corrigidas tendo por referência o código dessa submissão, esquecendo quaisquer outros fatores.

Discussão presencial

Passado o prazo de entrega do trabalho, muitos grupos serão dispensados de ter uma discussão presencial. Será então divulgada a lista dos grupos que vão ter realmente a discussão presencial.


Mooshak

O arquivo zip que o seu grupo submete não precisa de conter a pasta "TADs", e mesmo que a contenha, o Mooshak ignora-a. O Mooshak usa a sua própria pasta "TADs". Uma boa maneira, mas não a única, de criar o zip é através deste comando do linux/wsl:

O Mooshak compila o seu programa usando o seguinte comando:

Na fase 1, o Mooshak valida o seu programa usando 12 testes. Na fase 1 não existem testes secretos. Serão usados apenas os 12 testes divulgados.

Na fase 2, o Mooshak valida o seu programa usando 10 testes públicos, mais 10 testes que serão secretos até ao prazo de entrega final.

Eis os testes públicos: tests1.zip, tests2.zip [basta clicar para fazer download].


FAQ

  1. Se um drone for destruído, o respetivo id fica disponível para posterior reutilização?

    • Não. Esse id deixa de poder ser usado. Os ids são simplesmente atribuídos sequencialmente.

  2. E se uma encomenda for destruída, o tratamento do id é igual?

    • As encomendas não são destruídas. Depois duma entrega ser feita, o registo da entrega é guardado até ao final da execução do programa.

  3. A atribuição do id a um drone é feita onde? No sistema, na base, no próprio TAD do drone?

    • Todas essas hipóteses são aceitáveis se forem programadas duma forma simples e lógica. No entanto, se existir uma forma de atribuir o id diretamente no TAD do drone, sem intervenção de mais ninguém, essa forma será um pouco melhor porque reforça a aplicação do nosso princípio da repartição de responsabilidades.
      [Mas note que se a regra de atribuição de ids fosse mais complicada, por exemplo com reutilização de de ids passados, então a lógica teria de ser programada na base.]

  4. - Vejo que, nos testes, é possível criar um drone coletivo com zero ou um drones básicos. Isso faz sentido?

    • Os testes complementam o enunciado em alguns aspetos de detalhe. Se os testes permitem o que você disse, então você terá de fazer dessa maneira. Se o enunciado dissesse outra coisa, então você faria diferente.
    • Está aqui em causa a filosofia de quem escreveu o enunciado. Quando está em causa uma coleção de valores, a nossa filosofia é permitir os casos particulares de cardinalidade 0 e 1, a não se que exista uma razão muito forte para não o fazer.

  5. Uma maneira prática de ler uma sequência de inteiros com tamanho não fixo...?

    • Se a sequência tiver tamanho limitado, a forma mais simples é usar o sscanf do costume, mas com a ajuda dum vetor. Por exemplo, para ler o máximo de 4 inteiros:

        #define MAX_INT	4
        int ints[MAX_INT], extra;
        int n = sscanf(linha, "%d %d %d %d %d", &ints[0], &ints[1], &ints[2], &ints[3], &extra);
        
      A função sscanf diz quantos inteiros foram lidos, e os inteiros lidos ficam num vetor. Agora podemos navegar nos inteiros lidos usando um índice i, algo que é essencial. Se a resposta for MAX_INT+1, ficamos a saber que a linha tinha pelo menos um inteiro a mais, não sendo uma linha válida por isso.

  6. Devo definir dois TADs diferentes, um para os drones básicos e outro para os drones compostos?

    • Essa ideia parece lógica e poderia ser concretizada. Mas, na na prática cria pelo menos estas três complicações: (1) quase todas as funções dos dois tipos de drones ficariam com o código completamente igual; (2) perdíamos a liberdade de juntar na mesma coleções os dois tipos de drone; (3) acima de tudo, o programador deixava de poder fazer raciocínios para drones em geral, tendo de estar constantemente a distinguir os dois casos.

      O melhor é tentar definir apenas um TAD. Na struct colocar um campo booleano a distinguir o tipo do drone. A maioria dos campos da struct fica partilhada pelos dois tipos de drone; mas poderão existir alguns campos que só são usados por um determinado tipo de drone.

      Se definir apenas um TAD, é boa ideia escrever duas funções de criação diferentes: criaBasicoDrone e criaCompostoDrone. [A palavra "Drone" aparece no final dos nomes para seguir a nossa regra de nomeação das funções dos TADs.]


Observações


Final

Bom trabalho! Esperamos que goste.