-1

I have a quadTree type defined by that :

type 'a quadtree = 
| Empty 
| Leaf of 'a 
| Node of 'a quadtree * 'a quadtree * 'a quadtree * 'a quadtree;;

Rooms defined by

type room = {
n : bool;
e : bool;
s : bool;
w : bool;
ps : coord;
exit : bool
}

Coordinates defined by

type coord = {
x : int;
y : int;
}

So TLDR of all that, I have a Quadtree of rooms that have or don't have exits up, down, left and right.

The objective now is to create a function that will find a way (if it exists) from one room to another (from its coordinates), the problem is that I don't see how to do it in OCaml... Anyway, thanks for your time, have a good day.

Edit : To clarify, I am the one defining the types and can alter them if needed. Also, I tried implementing Dijkstra's algorithm (from Wikipedia's pseudo code), but being quite unfamiliar with both graphs, and OCaml's arrays and lists. To be precise, my problem -I think- comes from the fact that I'm not able to modify variables in a function, so for instance in Wikipedia's pseudo code, in this line:

u ← Q.extract_min()                    // Remove and return best vertex

I see how to remove the best vertex, and I see how to return it, but not both at the same time. Or, here:

for each neighbor v of u:           // where v is still in Q.
    alt ← dist[u] + length(u, v)
    if alt < dist[v]:               // A shorter path to v has been found
        dist[v] ← alt 
        prev[v] ← u

How do I modify dist and prev outside of the 'for' loop? Can I use a for loop or is it simpler / better to use a recursive function?

Also I should make clear that the maze is "directional", meaning that being able to go from room A to room B does not mean you'll be able to go from room B to room A.

Edit 2 : I should have clarified this in the beginning, sorry : The quadtree follows this rule :

| Node of North West * North East * South West * South East

Edit 3 : Okay change of plan, turns out I was doing things very stupidly. I don't need to find the way to a certain room, just to an exit. So I tried this :

let rec contains_exit = function
    | [] -> false
    | e::l' when (getCell e.x e.y maze).exit -> true
    | e::l' when (getCell e.x e.y maze).exit = false -> contains_exit l'
;;

let rec find_exit start way =
    if is_exit start then 
        way
    else
    (let a = find_exit (northp start) way@[start] in
    if contains_exit a then
        way
        else
        (
        let b = find_exit (eastp start) way@[start] in
        if contains_exit b then
            way
            else 
            (
        let c = find_exit (southp start) way@[start] in
        if contains_exit c then
            way
                else
                (
            let d = find_exit (westp start) way@[start] in
            if contains_exit d then
                way
                    else
                        way
                )
            )
        )
    )
;;

But it gives me a stack overflow. After a bit of research, it seems that the line "contains_exit a" is never true, so the way is never returned and it loops !

Any idea why that is ? Is the problem my contains_exit function ?

Edit 4 : Ended up doing this function :

let rec find_exit start way =
    sleep 50000000;
    let r = (Random.int 180) in
    set_color (rgb r r r);
    fill_rect (start.x * sizeCell + doorWidth * 2) (start.y * sizeCell + doorWidth * 2) (sizeCell - 4 * doorWidth) (sizeCell - 4 * doorWidth);
    if is_exit start then 
            way@[start]
    else
    (let a = if (getCell start.x start.y maze).n && ((mem (northp start) way) = false) then find_exit (northp start) way@[start] else [] in
    if a != [] then
        a
        else
        (
        let b = if (getCell start.x start.y maze).e && ((mem (eastp start) way) = false) then find_exit (eastp start) way@[start] else [] in
        if b != [] then
            b
            else 
            (
        let c = if (getCell start.x start.y maze).w && ((mem (westp start) way) = false) then find_exit (westp start) way@[start] else [] in
        if c != [] then
            c
                else
                (
            let d = if (getCell start.x start.y maze).s && ((mem (southp start) way) = false) then find_exit (southp start) way@[start] else [] in
            if d != [] then
                d
                    else
                        []
                )
            )
        )
    )
;;

it sometimes works... But other times it blocks and it goes from one room to the one below then up again then down again... I don't understand why !?

If you want to try the whole program, here it is : link

  • 2
    "I don't see how to do it in OCaml" is not really a question that fits into the scope of Stackoverflow. What did you try? Do you have you an idea of an algorithm to resolve your issue? If yes, where did OCaml hinder your implementation? – octachron Dec 17 '18 at 13:05
  • As @octachron said, you are not providing enough information. If you wish for simple algorithms: Look ahead using a recursive function to test a path up to the room you wish to go. It is not very effective but works well in OCaml and help for basic understanding of recursions. Else: Find an algorithm then we can help you for the implementation – Titouan Freville Dec 17 '18 at 16:54
  • Also, are you the one defining the types ? Can you alter them ? – Titouan Freville Dec 17 '18 at 17:08
  • Then a small change is to add the root of the Node so you can know witch room you are in. OCaml is a functional language so you should use recursive function as they are cleaner to use and write than loops in OCaml. As you do not use pointers in functional, you have to make sub-function returning what you wish. – Titouan Freville Dec 18 '18 at 09:03

1 Answers1

0

Then you can go for some thing like this:

type 'a quadtree = 
| Empty 
| Leaf of 'a 
| Node of 'a * 'a quadtree * 'a quadtree * 'a quadtree * 'a quadtree;;

type room = {
n : bool;
e : bool;
s : bool;
w : bool;
ps : coord;
exit : bool
};;

type coord = {
x : int;
y : int;
};;

let rec treeForRoom(tree, room) = 
    match tree with
    | Empty -> Empty
    | Leaf l -> if l.ps == room.ps then l else Empty
    | Node (r, n, e, s, w) as node -> 
        if r == room 
        then  node
        else 
            match ((r.ps.x - room.ps.x), (r.ps.y - room.ps.y)) with
            | (0, n) -> if n > 0 then treeForRoom(w) else treeForRoom(e)
            | (n, 0) -> if n > 0 then treeForRoom(s) else treeForRoom(n)

(* Assuming the root of the tree is the room we start from *)
let rec searchPath(tree, r) = 
    match tree with
    | Empty -> (false, 0, [])
    | Leaf l -> if l == r then (true, 0) else (false, 0, [])
    | Node (r, n, e, s, w) as node -> 
       let pn = searchPath(n, r) 
       and pe = searchPath(e, r) 
       and ps = searchPath(s, r)
       and pw = searchPath(w, r)
    in
        find_best_path(p1, p2, p3, p4)

let find_best_path(p1, p2, p3, p4) = 
    match (p1, p2, p3, p4) with
    | ((false,_,_), (false,_,_), (false,_,_), (false,_,_)) -> (false, -1, [])
    | ((true, w, p), (false,_,_), (false,_,_), (false,_,_)) -> (true, w, p)
    | ((false,_,_), (true, w, p)), (false,_,_), (false,_,_)) -> (true, w, p)
    | ((false,_,_), (false,_,_), (true, w, p)), (false,_,_)) -> (true, w, p)
    | ((false,_,_), (false,_,_), (false,_,_),(true, w, p)) -> (true, w, p)
    | ((p1ok, p1w, p1p), (p2ok, p2w, p2p),(p3ok, p3w, p3p),(p4ok, p4w, p4p)) -> 
        if p1ok && p2ok && p3ok && p4ok
        then 
            min_weight([(p1ok, p1w, p1p), (p2ok, p2w, p2p),(p3ok, p3w, p3p),(p4ok, p4w, p4p)])
        else 
        ....

let rec min_weight(l) = 
    match l with
    | [] -> (false, -1, [])
    | [t] -> t 
    | [(to, tw, tp) as t::q] -> let (mo, mw, mp) as minw = min_weight(q) in 
        if tw < mw
        then
            t
        else
            minw

I added the root to the type definition ('a* ...) so I can make a function to find the good tree to go through. I also assume that the tree respect the following rule: (root, north room, east room, south room, west room) for each node (you can make an add function to ensure this property).

Then you go through the tree exploring from the end and getting the minimal weight path for then end to the start point. (It is the same weight as it goes through the same paths under the same conditions (cause you explore the tree from the start but compute the path from then end)).

This code does not take into account the possibility to pass through doors but it is a just a check to add as the way of going through the tree is already correctly oriented.

I let you complete and correct the code.

Hope it will help you.

Titouan Freville
  • 489
  • 4
  • 13