Guest User

Untitled

a guest
Jul 4th, 2018
108
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
OCaml 5.51 KB | None | 0 0
  1. module MazeParse = struct
  2.   let split separator s =
  3.     let list = ref [] in
  4.     let start = ref 0 in
  5.     let () = try
  6.         while true do
  7.           let index = String.index_from s !start separator in
  8.           list := (String.sub s !start (index - !start)) :: !list;
  9.           start := index + 1
  10.         done
  11.       with Not_found -> list := (String.sub s !start ((String.length s) - !start)) :: !list
  12.     in
  13.     List.rev !list
  14.  
  15.   let from_file filename =
  16.     let channel = open_in_bin filename in
  17.     let size = in_channel_length channel in
  18.     let buffer = Buffer.create size in
  19.     Buffer.add_channel buffer channel size;
  20.     let s = Buffer.contents buffer in
  21.     let lines = split '\n' s in
  22.     let height, width = List.length lines, String.length (List.hd lines) in
  23.     let maze = Array.make_matrix width height ' ' in
  24.     for x = 0 to width -1 do
  25.       for y = 0 to height -1 do
  26.         maze.(x).(y) <- (List.nth lines y).[x]
  27.       done
  28.     done;
  29.     maze
  30. end
  31.  
  32. module Maze = struct
  33.   let width maze = Array.length maze
  34.   let height maze = Array.length maze.(0)
  35.   let get maze (x, y) = maze.(x).(y)
  36.   let set maze (x, y) value = maze.(x).(y) <- value
  37.  
  38.   let walk maze path =
  39.     let walk_once coords = set maze coords '.' in
  40.     List.iter walk_once path
  41.  
  42.   let draw maze =
  43.     for y = 0 to (height maze) -1 do
  44.       for x = 0 to (width maze) -1 do
  45.         print_char (get maze (x, y))
  46.       done;
  47.       print_newline ()
  48.     done
  49.  
  50.   let is_passable maze coord = get maze coord <> '#'
  51.  
  52.   let distance (x1, y1) (x2, y2) =
  53.     let square x = x * x in
  54.     let sqrt_int x = int_of_float (sqrt (float_of_int x)) in
  55.     sqrt_int (square (x1 - x2) + square (y1 - y2))
  56.  
  57.   let neighbor_nodes maze (x, y) =
  58.     let nodes = [] in
  59.     let nodes = if x > 0 then (x -1, y) :: nodes else nodes in
  60.     let nodes = if x < (width maze) - 1 then (x +1, y) :: nodes else nodes in
  61.     let nodes = if y > 0 then (x, y -1) :: nodes else nodes in
  62.     let nodes = if y < (height maze) - 1 then (x, y +1) :: nodes else nodes in
  63.     nodes  
  64. end
  65.  
  66. exception No_path_found
  67.  
  68. type cost = { steps: int; estimation: int; camefrom: int * int }
  69.  
  70. type node =
  71.   | Unknown
  72.   | Closed
  73.   | Exploring of cost
  74.   | Explored of cost
  75.  
  76. let rec reconstruct_path matrix node path =
  77.   let camefrom = match matrix.(fst node).(snd node) with
  78.     | Explored cost -> cost.camefrom
  79.     | Exploring cost -> cost.camefrom
  80.     | _ -> node
  81.   in
  82.   if camefrom = node then node :: path
  83.   else reconstruct_path matrix camefrom (node :: path)
  84.  
  85. let find_path maze start goal =
  86.   let heuristic = Maze.distance in
  87.   let matrix = Array.make_matrix (Maze.width maze) (Maze.height maze) Unknown in
  88.  
  89.   let module CoordSet = Set.Make(
  90.     struct
  91.       type t = int * int
  92.       let estimate node = match node with
  93.         | Exploring cost -> cost.estimation
  94.         | Explored cost -> cost.estimation
  95.         | Unknown -> max_int
  96.         | Closed -> max_int
  97.      
  98.       let compare a b =
  99.         let score_a = estimate matrix.(fst a).(snd a) in
  100.         let score_b = estimate matrix.(fst b).(snd b) in
  101.         let diff = score_a - score_b in
  102.         if diff = 0 then Pervasives.compare a b else diff
  103.     end) in
  104.  
  105.   let openset = ref (CoordSet.singleton start) in
  106.   let select_best set = CoordSet.min_elt set in
  107.   let is_explorable maze node =
  108.     if not (Maze.is_passable maze node) then false
  109.     else match matrix.(fst node).(snd node) with
  110.       | Closed -> false
  111.       | Unknown -> true
  112.       | Exploring _ -> true
  113.       | Explored _ -> false
  114.   in
  115.   let get_steps node = match matrix.(fst node).(snd node) with
  116.     | Exploring cost -> cost.steps
  117.     | Explored cost -> cost.steps
  118.     | Closed -> max_int
  119.     | Unknown -> failwith "should not be called"
  120.   in
  121.   let set_exploring node steps camefrom =
  122.     matrix.(fst node).(snd node) <- Exploring { steps = steps; estimation = steps + (heuristic node goal); camefrom = camefrom };
  123.   in
  124.   let explored node = match matrix.(fst node).(snd node) with
  125.     | Exploring cost -> Explored cost
  126.     | _ -> failwith ("not really explored: " ^ string_of_int (fst node) ^ ", " ^ string_of_int (snd node))
  127.   in
  128.   set_exploring start 0 start;
  129.   let rec loop () =
  130.     if CoordSet.is_empty !openset then raise No_path_found
  131.     else
  132.       let current = select_best !openset in
  133.       if current = goal then List.rev (reconstruct_path matrix goal [])
  134.       else begin
  135.         openset := CoordSet.remove current !openset;
  136.         matrix.(fst current).(snd current) <- explored current;
  137.        
  138.         let neighbors = Maze.neighbor_nodes maze current in
  139.         let explore node =
  140.           if is_explorable maze node then
  141.             let tentative_steps = (get_steps current) + 1 in
  142.             if not (CoordSet.mem node !openset) then begin
  143.               set_exploring node tentative_steps current;
  144.               openset := CoordSet.add node !openset;
  145.             end
  146.             else if tentative_steps < (get_steps node) then set_exploring node tentative_steps current
  147.         in
  148.         List.iter explore neighbors;
  149.         loop ()
  150.       end
  151.   in
  152.   loop ()
  153.  
  154. let () =
  155.   let maze = MazeParse.from_file "maze.txt" in
  156.   let before = Unix.gettimeofday () in
  157.   let path = find_path maze (0, 0) (78, 29) in
  158.  
  159.   for i = 0 to 1000 do
  160.     ignore (find_path maze (0, 0) (78, 29))
  161.   done;
  162.  
  163.   let after = Unix.gettimeofday () in
  164.   Maze.walk maze path;
  165.   Maze.draw maze;
  166.   Printf.printf "%d steps\n" (List.length path);
  167.   let time = (after -. before) *. 1000. in
  168.   Printf.printf "%f ms\n" time
Add Comment
Please, Sign In to add comment