Hi all,

I am new to functional programming and thought this might be
interesting to others to see my approach to this kind a recursion
problem. I found this problem hard to think through, does anyone have
any suggestions for simpler solution to this type of problem?

I needed a recursive backtracking algorithm that did not consume the
stack so to test out how I might go about writing it I wrote a
trampolined version of maze solver that I had to write at uni. The
maze solver algorithm is something like what can be seen here
http://www.cs.bu.edu/teaching/alg/maze/ it is basically a fairly
unintelligent brute force solution but very simple to implement with
four self recursive calls (one for each direction) but not easily
using tail recursion.

The version I have implemented here is not as simple but it is
trampolined so that it doesn't consume the stack and it is functional
without mutation. It brakes the problem down into two mutually
recursive functions recur-forward and backtrack.

Note: This test does not use a large enough maze to worry about
blowing the stack (you would need a very large maze) but the real
algorithm I will need to write will be much more deeply recursive than
this.

Here is the code, the solve-maze function looks large but it is really
two functions wrapped up in a function using letfn so the caller does
not have to worry about using trampoline.

(def test-maze [[:X :X :X :X :X :X :X :X :X :X]
                          [:X :o :X :o :o :o :o :o :o :X]
                          [:X :o :X :o :o :o :X :o :o :X]
                          [:X :o :X :o :o :X :X :o :o :X]
                          [:X :o :X :X :o :o :X :o :o :X]
                          [:X :o :o :X :X :o :X :X :o :X]
                          [:X :o :o :X :F :o :o :X :o :X]
                          [:X :o :o :o :X :X :X :X :o :X]
                          [:X :o :X :o :o :o :o :o :o :X]
                          [:X :X :X :X :X :X :X :X :X :X]])

(defn tile-in-bounds? [[x y] maze]
  (let [h (count maze)]
    (if (and (>= y 0) (>= x 0) (< y h))
      (if (< x (count (maze y)))
        true))))

(defn get-tile [[x y] maze]
  (if (tile-in-bounds? [x y] maze)
    ((maze y) x)))

(defn update-tile [[x y] maze value]
  (if (tile-in-bounds? [x y] maze)
    (assoc maze y (assoc (maze y) x value))
    maze))

(defn valid-tile? [[x y] maze]
  (= (get-tile [x y] maze) :o))

(defn print-maze [maze]
  (if (coll? maze)
    (print (map println-str maze))
    (println maze)))

(defn filter-neighbours [f [x y] maze]
  (for [dir (list [0 -1] [1 0] [0 1] [-1 0])
        :when (f (map + [x y] dir) maze)]
    (map + [x y] dir)))

(defn valid-neighbours [[x y] maze]
  (filter-neighbours #(valid-tile? %1 %2) [x y] maze))

(defn find-neighbours [match [x y] maze]
  (filter-neighbours #(= (get-tile %1 %2) match) [x y] maze))

(defn solve-maze [[init-x init-y] init-maze]
  (letfn [(recur-forward [[x y] track maze]
            (if-not (empty? (find-neighbours :F [x y] maze))
              #(backtrack true (conj track [x y]) maze)
              (let [neighbours (valid-neighbours [x y] maze)
                    next-maze (update-tile [x y] maze :?)
                    next-track (conj track [x y])]
                (if-not (empty? neighbours)
                  #(recur-forward (first neighbours) next-track next-
maze)
                  #(backtrack false track next-maze)))))
          (backtrack [found track maze]
            (let [curr-track (pop track)
                  solved-maze (update-tile (first track) maze :*)]
              (if found
                (if (empty? curr-track)
                  maze
                  #(backtrack found curr-track solved-maze))
                (let [curr-coord (first curr-track)
                      neighbours (valid-neighbours curr-coord maze)
                      next-maze (update-tile curr-coord maze :*)]
                  (if-not (empty? neighbours)
                    #(recur-forward curr-coord (rest track) maze)
                    #(backtrack found curr-track maze))))))]
    (let [start [init-x init-y]]
      (trampoline recur-forward start (list start) init-maze))))

(print-maze (solve-maze [1 1] test-maze))

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to