Advertisement
Guest User

Untitled

a guest
Mar 23rd, 2017
122
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. alias $std.data as data
  2. alias $std.fun as fun
  3. alias $std.strings as strings
  4. alias $std.math as math
  5.  
  6. alias $std.data.map as map
  7. alias $std.data.reduce as reduce
  8. alias $std.data.reduce_until as reduce_until
  9. alias $std.data.put as put
  10. alias $std.data.put_in as put_in
  11. alias $std.data.update as update
  12. alias $std.data.update_in as update_in
  13. alias $std.data.size as size
  14. alias $std.data.shuffle as shuffle
  15. alias $std.data.values as values
  16. alias $std.data.find as find
  17. alias $std.data.last as last
  18. alias $std.data.delete as delete
  19. alias $std.data.repeat as repeat
  20. alias $std.data.insert as insert
  21. alias $std.data.any? as any?
  22.  
  23. alias $std.core.hash as hash
  24. alias $std.core.inspect as inspect
  25. alias $std.core.nil? as nil?
  26. alias $std.core.present? as present?
  27.  
  28. alias lib.empty? as empty?
  29.  
  30. library matrix_2d {
  31.  
  32.   make: (w, h, f) ->
  33.     if any?([w, h], nil?) then nil
  34.     if w <= 0 then throw "width must be positive"
  35.     if h <= 0 then throw "height must be positive"
  36.     let {
  37.       matrix: data.map(repeat(w), (_) -> repeat(h))
  38.     }
  39.     if f == nil
  40.       matrix
  41.     else
  42.       data.map(matrix,
  43.         (col, x) -> data.map(col,
  44.           (row, y) -> f(x, y)))
  45.  
  46.   # maps over each matrix cell building a new matrix of same shape
  47.   # f receives three arguments: input cell, x coordinate, y coordinate
  48.   mmap: (m, f) ->
  49.     map(m,
  50.       (col, x) -> map(col,
  51.         (row, y) -> f(m[x y], x, y)))
  52.  
  53.   col: (m, x) ->
  54.     m[x]
  55.  
  56.   row: (m, y) ->
  57.     map(m, (col) -> col[y])
  58.  
  59.   width: (m) ->
  60.     if m == nil then nil
  61.     size(row(m, 0))
  62.  
  63.   height: (m) ->
  64.     if m == nil then nil
  65.     size(m[0])
  66.  
  67.   transpose: (m) ->
  68.     if m == nil then nil
  69.     mmap(
  70.       make(height(m), width(m)),
  71.       (_, x, y) -> m[y x]
  72.     )
  73. }
  74.  
  75. library maze {
  76.  
  77.   # simple mapping of directions to their opposites
  78.   opposite: {
  79.     :north :south
  80.     :east  :west
  81.     :south :north
  82.     :west  :east
  83.   }
  84.  
  85.   # returns a cell with all walls intact
  86.   make_cell: (x, y) -> {
  87.     :x x
  88.     :y y
  89.     :walls {
  90.       :north true
  91.       :east  true
  92.       :south true
  93.       :west  true
  94.     }
  95.     :travel []      # neigbor coordinates of the cell put here when building travel paths
  96.     :visited false  # helper during backtracking through maze
  97.   }
  98.  
  99.   # returns a map of coordinates to neighboring cells, if any
  100.   # neighbors_map(gen(5,5), {:x 0, :y 0})
  101.   # {
  102.   #   :south {
  103.   #     :x 0,
  104.   #     :y 1
  105.   #   },
  106.   #   :east {
  107.   #     :x 1,
  108.   #     :y 0
  109.   #   }
  110.   # }
  111.   neighbors_map: (maze, pos) ->
  112.     let {
  113.       x: pos[:x]
  114.       y: pos[:y]
  115.       cells: maze[:cells]
  116.       neighbor_cells: data.filter(
  117.         {
  118.           :north  cells[x   y-1]
  119.           :east   cells[x+1 y  ]
  120.           :south  cells[x   y+1]
  121.           :west   cells[x-1 y  ]
  122.         },
  123.         present?
  124.       )
  125.     }
  126.     # just retain the addresses, not the cell payload
  127.     map(neighbor_cells, (cell) -> cell_pos(cell))
  128.  
  129.   # given a cell, returns only its coordinates in a map
  130.   cell_pos: (cell) ->
  131.     {
  132.       :x cell[:x],
  133.       :y cell[:y]
  134.     }
  135.  
  136.   # just the values of the neighbor_map, losing the direction keys
  137.   neighbors_list: (maze, pos) ->
  138.     values(neighbors_map(maze, pos))
  139.  
  140.   # given two neighboring cell positions
  141.   # returns the wall direction from the first to the second
  142.   # return value is undefined if the cells are not actually neighboring cells
  143.   # > wall_between({:x 0, :y 0}, {:x 0 :y 1})
  144.   # "south"
  145.   # > wall_between({:x 0, :y 1}, {:x 0 :y 0})
  146.   # "north"
  147.   #
  148.   wall_between: (c1, c2) ->
  149.     if c1[:x] == c2[:x]
  150.       if c1[:y] < c2[:y]
  151.         :south
  152.       else
  153.         :north
  154.     else
  155.       if c1[:x] < c2[:x]
  156.         :east
  157.       else
  158.         :west
  159.  
  160.   break_wall_between: (maze, c1, c2) ->
  161.     let {
  162.       wall_c1_to_c2: wall_between(c1, c2)
  163.       wall_c2_to_c1: opposite[wall_c1_to_c2]
  164.     }
  165.     ->> (maze)
  166.         (maze) -> put_in(maze, [:cells c1[:x] c1[:y] :walls wall_c1_to_c2], false)
  167.         (maze) -> put_in(maze, [:cells c2[:x] c2[:y] :walls wall_c2_to_c1], false)
  168.  
  169.   init: (w, h, seed) ->
  170.     {
  171.       :cells matrix_2d.make(w, h, make_cell)
  172.       :size {:w w, :h h}
  173.       :seed seed
  174.       :path []
  175.       :path_done false
  176.       :total_visited 0
  177.       :cell_count w * h
  178.     }
  179.  
  180.   mark_visited: (maze, pos) ->
  181.     ->> (maze)
  182.         (maze) -> update_in(maze, [:total_visited], lib.inc)
  183.         (maze) -> put_in(maze, [:cells, pos[:x] pos[:y], :visited], true)
  184.  
  185.   update_seed: (maze, randomness) ->
  186.     update(maze, :seed, (seed) -> hash([randomness, seed]))
  187.  
  188.   update_cell: (maze, pos, f) ->
  189.     update_in(maze, [:cells pos[:x] pos[:y]], f)
  190.  
  191.   all_coords: (maze) ->
  192.     lib.flatten(
  193.       matrix_2d.mmap(maze[:cells], (_, x, y) -> {:x x, :y y})
  194.     )
  195.  
  196.   build_travel_path: (maze) ->
  197.     reduce(
  198.       all_coords(maze),
  199.       maze,
  200.       (maze, at) ->
  201.         let {
  202.           shuffled_neighbors:   shuffle(neighbors_list(maze, at), maze[:seed])
  203.         }
  204.         ->> (maze)
  205.             (maze) -> update_seed(maze, shuffled_neighbors)
  206.             (maze) -> update_cell(maze, at, (cell) -> {...cell, :travel shuffled_neighbors})
  207.     )
  208.  
  209.   random_cell_pos: (maze) ->
  210.     let {
  211.       x:    math.rand(maze[:seed])
  212.       y:    math.rand(hash([x, maze[:seed]]))
  213.     }
  214.     {
  215.       :x (x * maze[:size :w]) as long,
  216.       :y (y * maze[:size :h]) as long
  217.     }
  218.  
  219.   step: (maze) ->
  220.     # short circuit out if done
  221.     if maze[:path_done] then maze
  222.  
  223.     # not started yet?
  224.     if empty?(maze[:path])
  225.       # pick a random starting point
  226.       let {
  227.         pos: random_cell_pos(maze)
  228.       }
  229.       ->> (maze)
  230.           (maze) -> update_seed(maze, pos)
  231.           (maze) -> mark_visited(maze, pos)
  232.           (maze) -> put(maze, :path, [pos])
  233.  
  234.     else
  235.       # move on from the last item
  236.       let {
  237.         at:       last(maze[:path])
  238.         travel:   maze[:cells at[:x] at[:y] :travel]
  239.         next:     find(travel, (pos) -> !maze[:cells pos[:x] pos[:y] :visited])
  240.       }
  241.       if next
  242.         # there is a cell we can visit from here
  243.         ->> (maze)
  244.             (maze) -> break_wall_between(maze, at, next)
  245.             (maze) -> mark_visited(maze, next)
  246.             (maze) -> update(maze, :path, (path) -> [...path, next])
  247.  
  248.       else
  249.         # there is no more cells we can visit from here
  250.         # already done?
  251.         if maze[:cell_count] == maze[:total_visited]
  252.           {...maze, :path_done true, :path []}
  253.         else
  254.           # need to backtrack on path
  255.           ->> (maze)
  256.               (maze) -> put(maze, :path, data.init(maze[:path]))
  257.               # if backtracked completely, the path is complete
  258.               (maze) ->
  259.                 if empty?(maze[:path])
  260.                   put(maze, :path_done, true)
  261.                 else
  262.                   maze
  263.  
  264.   gen: (w=8, h=8, seed=nil) ->
  265.     let {
  266.       maze: build_travel_path(init(w, h, seed))
  267.     }
  268.     fun.until(step, maze, (m) -> m[:path_done])
  269.  
  270.   str: (maze) ->
  271.  
  272.     let {
  273.  
  274.       cell_to_top_str: (cell) ->
  275.         (if cell[:x] == 0 "+" "") .. (if cell[:walls :north] "---" "   ") .. "+"
  276.  
  277.       cell_to_mid_str: (cell) ->
  278.         (if cell[:x] == 0 (if cell[:walls :west] "|" " ") "") .. "   " .. (if cell[:walls :east] "|" " ")
  279.  
  280.       cell_to_bot_str: (cell) ->
  281.         (if cell[:x] == 0 "+" "") .. (if cell[:walls :south] "---" "   ") .. "+"
  282.  
  283.     }
  284.  
  285.     reduce(
  286.       # transposing inverts rows and columns
  287.       # so the transposed cells are a list of rows vs. the original representation as a list of columns
  288.       matrix_2d.transpose(maze[:cells]),
  289.       "",
  290.       (a, row, y) ->
  291.         let {
  292.           row_top_line: if y==0 then strings.join(map(row, (cell) -> cell_to_top_str(cell))) else nil
  293.           row_mid_line: strings.join(map(row, (cell) -> cell_to_mid_str(cell)))
  294.           row_bot_line: strings.join(map(row, (cell) -> cell_to_bot_str(cell)))
  295.         }
  296.         a .. "\n" ..
  297.           if y==0
  298.             strings.join([row_top_line, row_mid_line, row_bot_line], "\n")
  299.           else
  300.             strings.join([row_mid_line, row_bot_line], "\n")
  301.     )
  302.  
  303. }
  304.  
  305. library lib {
  306.  
  307.   empty?: (xs) ->
  308.     size(xs) == 0
  309.  
  310.   mapcat: (xs, f) ->
  311.     reduce(map(xs, f), [], (a, x) -> if x is list then [...a, ...x] else a)
  312.  
  313.   flatten: (list xs) ->
  314.     reduce(xs, [], (a, x) -> if x is list then [...a, ...x] else [...a, x])
  315.  
  316.   fibl: (list xs) ->
  317.     if !xs then [nil, 0]
  318.     if xs == [nil, 0] then [0,1]
  319.     [xs[1], xs[0]+xs[1]]
  320.  
  321.   fib: (long x) ->
  322.     if (x <= 0) then 0
  323.     fun.iterate(fibl, fibl(), x)[1]
  324.  
  325.   reverse: (list xs) ->
  326.     if (xs == nil) then nil
  327.     reduce(xs, [], (a, x) -> data.prepend(x, a))
  328.  
  329.   inc: (long x) ->
  330.     x+1
  331.  
  332.   zipmap: (list keys, list values) ->
  333.     reduce(keys, {}, (a, k, i) -> put(a, k, values[i]))
  334.  
  335.   interpose: (list xs, s) ->
  336.     if xs == nil then nil
  337.     reduce(xs, [],          # build a new list
  338.       (a, x, i) ->
  339.         if i == 0
  340.           [x]               # first item remains as is
  341.           [...a, s, x]      # follow-up items are preceded with seperator
  342.     )
  343.  
  344. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement