Advertisement
theindigamer

Cheap numbers in Piet (Ocaml)

Jul 13th, 2017
120
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
OCaml 4.53 KB | None | 0 0
  1. (* Made for https://math.stackexchange.com/questions/2355111/making-numbers-cheaply *)
  2. (* Inspired by https://pastebin.com/9kiLL3x5 *)
  3. (* Compile using `ocamlbuild -use-ocamlfind -package batteries climb.native` *)
  4.  
  5. open Batteries
  6.  
  7. module MakeCheap = struct
  8.   module H = BatHashtbl
  9.   module V = BatVect
  10.  
  11.   type seq = int list
  12.   let int_max = BatInt.max_num
  13.   type binary_op = PAdd | PSub | PMul | PDiv | PMod
  14.   type elem = Number of int | PDup | Binary of binary_op
  15.   type path = elem list
  16.  
  17.   let string_of_elem = function
  18.     | Number x -> string_of_int x
  19.     | PDup -> "@"
  20.     | Binary PAdd -> "+"
  21.     | Binary PSub -> "-"
  22.     | Binary PMul -> "*"
  23.     | Binary PDiv -> "/"
  24.     | Binary PMod -> "%"
  25.  
  26.   type full_history = {
  27.     maxc : int ref;
  28.     best : (int, int * elem list) H.t; (* number -> (cost, path) *)
  29.     hist : (seq, int) H.t;             (* sequence -> length of best path *)
  30.     lim  : int;
  31.     def  : elem list;
  32.   }
  33.  
  34.   type path_history = {
  35.     cost : int;
  36.     path : path;
  37.     st_l : seq list;
  38.   }
  39.  
  40.   let rec branch path_h full_h =
  41.     if path_h.cost >= !(full_h.maxc) then full_h else
  42.       let make_kids acc = function
  43.         | PDup -> (match path_h.st_l with
  44.             | (h :: t) :: _ -> (PDup, path_h.cost + 1, h :: h :: t) :: acc
  45.             | _ -> acc)
  46.         | Binary x
  47.           -> (match path_h.st_l with
  48.               | (a :: b :: t) :: _ ->
  49.                 if (b < 0 && x = PMod) ||
  50.                    (a = 0 && (x = PDiv || x = PMod)) then acc
  51.                 else let ab_op = (match x with
  52.                     | PAdd -> b + a
  53.                     | PSub -> b - a
  54.                     | PMul -> b * a
  55.                     | PDiv -> b / a
  56.                     | PMod -> b mod a) in
  57.                   (Binary x, path_h.cost + 1, ab_op :: t) :: acc
  58.               | _ -> acc)
  59.         | Number x -> (Number x, path_h.cost + x,
  60.                        (match path_h.st_l with
  61.                         | [] -> [x]
  62.                         | h :: _ -> x :: h)) :: acc in
  63.       let traverse path_h fh (el, cost, seq) =
  64.         if cost > !(fh.maxc) then fh
  65.         else
  66.           let old_c = match H.find_option fh.hist seq with
  67.             | Some x -> x
  68.             | None -> int_max in
  69.           if cost >= old_c then fh
  70.           else
  71.             let _ =
  72.               H.replace fh.hist seq cost;
  73.               if List.length seq = 1 then
  74.                 let x = List.hd seq in
  75.                 if x < 0 then () else
  76.                   let old_c = match H.find_option fh.best x with
  77.                     | Some (y, _) -> y
  78.                     | None -> int_max in
  79.                   if cost < old_c then begin
  80.                     H.replace fh.best x (cost, el :: path_h.path);
  81.                     if old_c = !(fh.maxc) then
  82.                       fh.maxc :=
  83.                         List.range 1 `To fh.lim
  84.                         |> List.map (fst % H.find fh.best)
  85.                         |> List.reduce BatInt.max
  86.                   end
  87.             in
  88.             branch {cost = cost; path = el :: path_h.path;
  89.                     st_l = seq :: path_h.st_l} fh
  90.       in
  91.       full_h.def
  92.       |> List.fold_left make_kids []
  93.       |> List.fold_left (traverse path_h) full_h
  94.  
  95.   let find_all_cheaply max_num goal =
  96.     let start_path_h = {cost = 0; path = []; st_l = [];} in
  97.     let start_full_h guess = {
  98.       best = (List.range 1 `To goal)
  99.              |> List.map (fun x -> (x, (guess, [])))
  100.              |> H.of_list;
  101.       maxc = ref guess;
  102.       hist = H.create 1024;
  103.       def = [PDup;]
  104.             |> List.append
  105.                % List.map (fun x -> Binary x) @@ [PAdd; PSub; PMul; PDiv; PMod;]
  106.             |> List.append
  107.                % List.map (fun x -> Number x) @@ (List.range 1 `To max_num);
  108.       lim = goal;
  109.     } in
  110.  
  111.     let rec run_branch guess full_h =
  112.       let fh = branch start_path_h full_h in
  113.       if List.range 1 `To goal
  114.          |> List.map ((=) guess % fst % H.find fh.best)
  115.          |> List.reduce (||) then
  116.         run_branch (guess + 1) (start_full_h (guess + 1))
  117.       else fh
  118.     in
  119.  
  120.     let full_h = run_branch 16 (start_full_h 16) in
  121.     List.range 1 `To goal
  122.     |> List.map (fun a -> (a, H.find full_h.best a))
  123.     |> List.map (fun (a, (l,s)) -> (a,l, List.map (string_of_elem) @@ List.rev s))
  124.     |> List.sort (fun (a1,_,_) (a2,_,_) -> compare a1 a2)
  125.     |> List.map
  126.       (fun (a,l,s) -> let _ = print_endline @@ Printf.sprintf "%3d (%2d) %s" a l
  127.                         @@ List.fold_left (^) "" s in (a,l,s))
  128.  
  129. end
  130.  
  131. let _ = MakeCheap.find_all_cheaply 5 256
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement