Linguagens e Ambientes de Programação (2022/2023) [Eng.Inf. - DI/FCT/UNL]

Enunciado do 1º Projeto Prático (OCaml)

Artur Miguel Dias


Datas

  • 29/mar (20:00) - Divulgação da versão provisória do enunciado.
  • 31/abr (20:00) - Divulgação da versão final do enunciado (com possíveis correções).
  • 05/abr (20:00) - Data e hora recomendada para entrega.
  • 11/abr (09:00) - Data e hora limite de entrega do 1º projeto.
  • 11/abr (09:01) - Entrega com atraso. Um valor de penalização por cada minuto de atraso.


    Changelog


    Submission rules

    A-maze-ing mazes

    This project is about mazes (labyrinths) where we want to find a way from an entrance to an exit. And if we can find the shortest path, that would be ideal.

    There is the graphical representation of one maze on the right. It contains several rooms, represented by circles, and several oriented passages, represented by arrows. The blue rooms are the entrances and the green rooms the exits.

    This maze does not contain loops. When dealing with maze with loops, the infinite paths become a concern.

    This project is mostly about mazes with no loops. But also includes, at the end, a little taste of mazes with loops.


    Module "Maze"

    The aim of this project is to write a closed OCaml module named "Maze" containing data representations and functions concerning the notion of maze.

    The module interface has already been fully written and you are not allowed to change it: Maze.mli. As you can see, the data representation is public and there is also a small number of public functions declared. All the other entities you might define in the module body will become private. The representation is public to allow Mooshak to check the code of your module.

    Use this file as a starting point to write your module body: Maze.ml.


    Representation of the mazes

    In this project, we will represent the mazes, and associated concepts, using the following OCaml types:

    Each room is represented by a non-negative integer and each passage is represented by a pair of two distinct rooms (the two rooms that passage connects).

    Each maze if characterized by:

    For a maze to be valid, obviously all the rooms referred in "entrances", "exits" and "passages" must be previously declared in "rooms". Another restriction is that a room cannot be at the same time an entrance and an exit.

    Furthermore, all the lists must be in the so called canonical form, that is they must be sorted and do not include any duplicated elements.

    Here is the representation of our main example:


    Inductive method over mazes

    Note that in a maze without loops, the part of the maze starting at some room n is actually a n-ary tree. The room n itself is the root of the said tree and we can get the children of n from the maze using an auxiliary function next: To exemplify, there is a function that determines, in a maze m without loops, the maximum distance from the room n to a leaf room. A leaf room is any room without any way-out passage.

    Note the use of the usual auxiliary function that processes the children.

    It is easy to describe this function. If the room is a leaf room, then the result is 0. Otherwise, the result is 1 plus the maximum depth of the various children.

    In any case, note that some of the requested functions do not need to analyze mazes structurally, and they should be programmed using different styles.

    Another observation. Some of the requested functions that analyze mazes structurally, happen to build and return a list. In most of these cases, it is more compact and legible (so, a better style) to replace the auxiliary function by a direct call to the provided function flatMap. This function flatMap also has the extra advantage of keeping all the lists sorted and without repetition, as required in the description of many of the requested functions.


    The public functions of the module

    There are 14 public functions to implement. Please, write the functions in good functional style, avoiding any kind of code based on imperative reasoning. Anyway, note that within the functional style, there are several possibilities you might want to consider, depending on the problem at hand. The possibilities include: direct use of the inductive method; decomposition into simpler functions; taking advantage of the offered functions over sorted list with no repetitions; a mixture of all these.

    In the descriptions below, the preconditions of each function are clearly stated. Please, do not check the preconditions in your code; simply assume them, as usual.

    The task of each function is to calculate the result according to the specification and also to ensure the stated postconditions.

    It is provided a simple example for each function.

    isValid : maze -> bool
    (* pre: none *)
    (* post: none *)
    let isValid m = ... 
    
    makeLineMaze : room -> room -> maze
    (* pre: a < b *)
    (* post: isValid result *)
    let makeLineMaze a b = ... 
    
    combine : maze -> maze -> maze
    (* pre: isValid m1, isValid m2 *)
    (* post: isValid result *)
    let combine m1 m2 = ... 
    
    next : maze -> room -> rooms
    (* pre: isValid m *)
    (* post: isCanonical result *)
    let next m r = ... 
    
    next2 : next2 : maze -> rooms -> rooms
    (* pre: isValid m *)
    (* post: isCanonical result *)
    let next2 m rs = ... 
    
    prev : maze -> room -> rooms
    (* pre: isValid m *)
    (* post: isCanonical result *)
    let prev m r = ... 
    
    adjacent : maze -> room -> rooms
    (* pre: isValid m *)
    (* post: isCanonical result *)
    let adjacent m r = ... 
    
    reachable : maze -> rooms
    (* pre: isValid m, not (hasLoop m) *)
    (* post: isCanonical result *)
    let reachable m = ... 
    
    solitary : maze -> rooms
    (* pre: isValid m *)
    (* post: isCanonical result *)
    let solitary m = ... 
    
    islands : maze -> island list
    (* pre: isValid m, not (hasLoop m) *)
    (* post: isCanonical result *)
    let islands m = ... 
    
    shortest : maze -> path
    (* pre: isValid m, not (hasLoop m) *)
    (* post: none *)
    let shortest m = ... 
    
    paths : maze -> path list
    (* pre: isValid m, not (hasLoop m) *)
    (* post: none *)
    let paths m = ... 
    
    hasLoop : maze -> bool
    (* pre: isValid m *)
    (* post: none *)
    let hasLoop m = ... 
    
    shortest2 : maze -> path
    (* pre: isValid m *)
    (* post: none *)
    let shortest2 m = ... 
    

    Evaluation and grades

    You will submit the file "Maze.ml" via Mooshak.

    Around 80% of the grade of your group is automatically assigned by Mooshak. The remaining 20% is assigned manually by the teachers, who will analyze the quality of your code.

    A special case: In case of code of extremely bad quality, or code that uses the forbidden imperative mechanisms of OCaml, or code that constantly simulates imperative mechanisms and concepts, a special rule will be used so that the grade will be always below 50%, even if the program works well.

    To develop the project, we strongly recommend you use the OCaml interpreter, probably inside the Eclipse IDE.

    However, you should know that Mooshak will compile your module using the following command:

    After the compilation, Mooshak will test the module in the interpreter like this:

    Please, make a backup of the file "Maze.ml" once a while, because the file can disappear as result of human error or as result of a software/hardware malfunction.


    Main rules


    Remarks


    Final

    Have fun!