From c3768aca2ef37b9966424c45e0ecbdba106360a7 Mon Sep 17 00:00:00 2001
From: Starbeamrainbowlabs <sbrl@starbeamrainbowlabs.com>
Date: Wed, 29 Apr 2020 19:57:18 +0100
Subject: [PATCH] Create CLI ....I forgot to commit, didn't I?

---
 .gitignore        |   4 +-
 main.lua          | 118 ++++++++++++++++++++++++++++++++++++++++++++++
 maze.lua          |  79 +++++++++----------------------
 maze3d.lua        |  84 +++++++--------------------------
 openscad.lua      |  28 +++++++++++
 utils/print_r.lua |  42 +++++++++++++++++
 utils/strings.lua |  11 +++++
 7 files changed, 241 insertions(+), 125 deletions(-)
 create mode 100644 main.lua
 create mode 100644 openscad.lua
 create mode 100644 utils/print_r.lua
 create mode 100644 utils/strings.lua

diff --git a/.gitignore b/.gitignore
index 8e0c230..5eba201 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,6 @@
-
+*.png
+*.scad
+*.blend1
 # Created by https://www.gitignore.io/api/lua,git
 # Edit at https://www.gitignore.io/?templates=lua,git
 
diff --git a/main.lua b/main.lua
new file mode 100644
index 0000000..189308a
--- /dev/null
+++ b/main.lua
@@ -0,0 +1,118 @@
+local strings = require("./utils/strings")
+local debug = require("./utils/print_r")
+local maze2d = require("./maze")
+local maze3d = require("./maze3d")
+local openscad = require("./openscad")
+
+local settings = {
+	mode = "help",
+	extras = {},
+	
+	width = 11,
+	height = 11,
+	depth = 7,
+	
+	filename = "maze.scad"
+}
+
+for i=1,#arg do
+	if not strings.starts_with(arg[i], "-") then
+		table.insert(settings.extras, arg[i])
+	elseif string.match(arg[i], "--width") or string.match(arg[i], "-w") then
+		i = i + 1
+		settings.width = tonumber(arg[i])
+	elseif string.match(arg[i], "--height") or string.match(arg[i], "-h") then
+		i = i + 1
+		settings.height = tonumber(arg[i])
+	elseif string.match(arg[i], "--depth") or string.match(arg[i], "-d") then
+		i = i + 1
+		settings.depth = tonumber(arg[i])
+	elseif string.match(arg[i], "--seed") or string.match(arg[i], "-s") then
+		i = i + 1
+		settings.seed = tonumber(arg[i])
+	elseif string.match(arg[i], "--filename") or string.match(arg[i], "-f") then
+		i = i + 1
+		settings.filename = arg[i]
+	end
+end
+
+if #settings.extras > 0 then
+	settings.mode = table.remove(settings.extras, 1)
+end
+
+local function help()
+	print([[
+multimaze: Multidimensional maze generator
+	By Starbeamrainbowlabs
+
+Usage:
+	lua main.lua {mode} [{options}]
+
+Modes:
+	help				Displays this message
+	maze				Generates a 2d maze
+	maze3d				Generates a 3d maze
+	maze3d_openscad		Generates a 3d maze as an openscad file
+
+Options:
+	-w	--width			Sets the width of the maze
+	-h	--height		Sets the height of the maze
+	-d	--depth			Sets the depth of the maze (only applicable to 3d mazes)
+	-s	--seed			Sets the seed
+	-f	--filename		Sets the output filename
+]])
+end
+
+local function maze_text()
+	local world, time = maze2d.generate_maze(
+		os.time(),
+		settings.width,
+		settings.height
+	)
+	maze2d.printspace(world, settings.width, settings.height)
+	print("Generation completed in " .. time .. "ms.")
+end
+
+local function maze3d_text()
+	local world, time = maze3d.generate_maze(
+		os.time(),
+		settings.width,
+		settings.height,
+		settings.depth
+	)
+	
+	maze3d.printspace(world, settings.width, settings.height, settings.depth)
+	
+	print("Generation completed in " .. time .. "ms.")
+end
+
+local function maze3d_openscad()
+	local world, time = maze3d.generate_maze(
+		os.time(),
+		settings.width,
+		settings.height,
+		settings.depth
+	)
+	
+	openscad.write3d(
+		world,
+		settings.width, settings.height, settings.depth,
+		settings.filename
+	)
+	print("Maze written to "..settings.filename)
+end
+
+local mode_functions = {
+	["help"] = help,
+	["maze"] = maze_text,
+	["maze3d"] = maze3d_text,
+	["maze3d_openscad"] = maze3d_openscad
+}
+
+local selection = mode_functions[settings.mode]
+if not selection then
+	print("Error: Unrecognised mode '"..settings.mode.."'")
+	selection = mode_functions["help"]
+end
+
+selection()
diff --git a/maze.lua b/maze.lua
index ba90022..87a17c2 100644
--- a/maze.lua
+++ b/maze.lua
@@ -3,60 +3,25 @@
 -------------------------------------
 -- A test by @Starbeamrainbowlabs
 
----------------------------------------------
--- Intelligent table printing function
----------------------------------------------
--- From http://coronalabs.com/blog/2014/09/02/tutorial-printing-table-contents/
+local M = {}
 
-function print_r ( t )
-    local print_r_cache={}
-    local function sub_print_r(t,indent)
-        if (print_r_cache[tostring(t)]) then
-            print(indent.."*"..tostring(t))
-        else
-            print_r_cache[tostring(t)]=true
-            if (type(t)=="table") then
-                for pos,val in pairs(t) do
-                    if (type(val)=="table") then
-                        print(indent.."["..pos.."] => "..tostring(t).." {")
-                        sub_print_r(val,indent..string.rep(" ",string.len(pos)+8))
-                        print(indent..string.rep(" ",string.len(pos)+6).."}")
-                    elseif (type(val)=="string") then
-                        print(indent.."["..pos..'] => "'..val..'"')
-                    else
-                        print(indent.."["..pos.."] => "..tostring(val))
-                    end
-                end
-            else
-                print(indent..tostring(t))
-            end
-        end
-    end
-    if (type(t)=="table") then
-        print(tostring(t).." {")
-        sub_print_r(t,"  ")
-        print("}")
-    else
-        sub_print_r(t,"  ")
-    end
-    print()
-end
+-- Intelligent table printing function: http://coronalabs.com/blog/2014/09/02/tutorial-printing-table-contents/
 
-if arg[1] ~= nil then
-        width = tonumber(arg[1])
-else
-        width = 35
-end
-if arg[2] ~= nil then
-        height = tonumber(arg[2])
-else
-        height = 15
-end
+-- if arg[1] ~= nil then
+--         width = tonumber(arg[1])
+-- else
+--         width = 35
+-- end
+-- if arg[2] ~= nil then
+--         height = tonumber(arg[2])
+-- else
+--         height = 15
+-- end
 
 ----------------------------------
 -- function to print out the world
 ----------------------------------
-function printspace(space, w, h)
+function M.printspace(space, w, h)
         for y = 0, h - 1, 1 do
                 local line = ""
                 for x = 0, w - 1, 1 do
@@ -67,9 +32,9 @@ function printspace(space, w, h)
 end
 
 -- Initialise the world
-start_time = os.clock()
 
-function generate_maze(seed, width, height)
+function M.generate_maze(seed, width, height)
+    start_time = os.clock()
 	-- minetest.log("action", "width: "..width..", height: "..height)
 	math.randomseed(seed) -- seed the random number generator with the system clock
 
@@ -150,12 +115,14 @@ function generate_maze(seed, width, height)
 		end
 		--printspace(world, width, height)
 	end
-
-	return world
+    
+    end_time = os.clock()
+    
+	return world, (end_time - start_time) * 1000
 end
 
-local world = generate_maze(os.time(), width, height)
+-- local world, time   = generate_maze(os.time(), width, height)
+-- printspace(world, width, height)
+-- print("Generation completed in " .. time .. "s.")
 
-printspace(world, width, height)
-end_time = os.clock()
-print("Generation completed in " .. (end_time - start_time) .. "s.")
+return M
diff --git a/maze3d.lua b/maze3d.lua
index 0d73410..896a79c 100644
--- a/maze3d.lua
+++ b/maze3d.lua
@@ -2,45 +2,7 @@
 -- 3D Maze generation script
 -------------------------------------
 -- A test by @Starbeamrainbowlabs
-
----------------------------------------------
--- Intelligent table printing function
----------------------------------------------
--- From http://coronalabs.com/blog/2014/09/02/tutorial-printing-table-contents/
-
-function print_r ( t )
-	local print_r_cache = {}
-	local function sub_print_r(t, indent)
-		if (print_r_cache[tostring(t)]) then
-			print(indent.."*"..tostring(t))
-		else
-			print_r_cache[tostring(t)] = true
-			if (type(t) == "table") then
-				for pos, val in pairs(t) do
-					if (type(val) == "table") then
-						print(indent.."["..pos.."] => "..tostring(t).." {")
-						sub_print_r(val, indent..string.rep(" ", string.len(pos) + 8))
-						print(indent..string.rep(" ", string.len(pos) + 6).."}")
-					elseif (type(val) == "string") then
-						print(indent.."["..pos..'] => "'..val..'"')
-					else
-						print(indent.."["..pos.."] => "..tostring(val))
-					end
-				end
-			else
-				print(indent..tostring(t))
-			end
-		end
-	end
-	if (type(t) == "table") then
-		print(tostring(t).." {")
-		sub_print_r(t, "  ")
-		print("}")
-	else
-		sub_print_r(t, "  ")
-	end
-	print()
-end
+local M = {}
 
 if arg[1] ~= nil then
 	width = tonumber(arg[1])
@@ -66,7 +28,7 @@ end
 ----------------------------------
 -- function to print out the world
 ----------------------------------
-function printspace(space, w, h, d)
+function M.printspace(space, w, h, d)
 	for z = 0, d - 1, 1 do
 		for y = 0, h - 1, 1 do
 			local line = ""
@@ -80,9 +42,10 @@ function printspace(space, w, h, d)
 end
 
 -- Initialise the world
-start_time = os.clock()
 
-function generate_maze3d(seed, width, height, depth)
+function M.generate_maze(seed, width, height, depth)
+	start_time = os.clock()
+	
 	print("Generating maze "..width.."x"..height.."x"..depth)
 	math.randomseed(seed) -- seed the random number generator with the system clock
 
@@ -113,7 +76,6 @@ function generate_maze3d(seed, width, height, depth)
 		local directions = "" -- the different directions we can move in
         if cz - 2 > 0 and world[cz - 2][cy][cx] == "#" then
             directions = directions .. "-"
-            -- print("cz: "..cz..", cz - 2: "..(cz-2)..", here: '"..world[cz][cy][cx].."', there: '"..world[cz - 2][cy][cx].."'")
         end
         if cz + 2 < depth and world[cz + 2][cy][cx] == "#" then
             directions = directions .. "+"
@@ -131,17 +93,12 @@ function generate_maze3d(seed, width, height, depth)
 			directions = directions .. "r"
 		end
         
-		print("Currently at ("..cx..", "..cy..", "..cz..") - directions: "..directions)
-        
 		--print("radar output: '" .. directions .. "' (length: " .. #directions .. "), curnode: " .. curnode)
 		if #directions > 0 then
 			-- we still have somewhere that we can go
-			--print("This node is not a dead end yet.")
 			local curdirnum = math.random(1, #directions)
 			local curdir = string.sub(directions, curdirnum, curdirnum)
             
-            print("Picked direction '"..curdir.."' (index "..curdirnum..")")
-            
             if curdir == "+" then
                 world[cz + 1][cy][cx] = " "
                 world[cz + 2][cy][cx] = " "
@@ -168,37 +125,28 @@ function generate_maze3d(seed, width, height, depth)
 				cx = cx + 2
 			end
             
-            
-    		print("Now at ("..cx..", "..cy..", "..cz..") ")
-            
 			table.insert(nodes, { x = cx, y = cy, z = cz })
 		else
-			-- print("The node at " .. curnode .. " is a dead end.")
 			table.remove(nodes, curnode)
 			if #nodes > 0 then
-				--print("performing teleport.");
 				curnode = math.random(1, #nodes)
-				--print("New node: " .. curnode)
-				-- print("Nodes table: ")
-				-- print_r(nodes)
+				
 				cx = nodes[curnode]["x"]
 				cy = nodes[curnode]["y"]
                 cz = nodes[curnode]["z"]
-			else
-				--print("Maze generation complete, no teleportation necessary.")
 			end
 		end
-		-- io.read("*l")
-		-- print("\n\n\n\n\n\n\n\n\n")
-		-- printspace(world, width + 1, height + 1, depth + 1)
-		
 	end
-
-	return world
+	
+	end_time = os.clock()
+	
+	return world, (end_time - start_time) * 1000
 end
 
-local world = generate_maze3d(seed, width, height, depth)
+return M
 
-printspace(world, width, height, depth)
-end_time = os.clock()
-print("Generation completed in " .. (end_time - start_time) .. "s.")
+-- local world = M.generate_maze3d(seed, width, height, depth)
+-- 
+-- M.printspace(world, width, height, depth)
+-- end_time = os.clock()
+-- print("Generation completed in " .. (end_time - start_time) .. "s.")
diff --git a/openscad.lua b/openscad.lua
new file mode 100644
index 0000000..fe2089a
--- /dev/null
+++ b/openscad.lua
@@ -0,0 +1,28 @@
+local M = {}
+
+function M.write3d(maze, width, height, depth, filename)
+	local scale = 5
+	local inverted = true
+	local out = io.open(filename, "w")
+	out:write("// Generated by multimaze (lua)\n")
+	out:write("// Hit the Render button (F6) to make the preview look better")
+	
+	out:write("// Uncomment to make the preview look transclucent")
+	out:write("// color(\"red\", 0.25)\n");
+	if inverted then out:write("difference() {\n\tcube(size=["..scale*width..", "..scale*height..", "..scale*depth.."])")
+	else out:write("union() {") end
+	out:write("\n")
+	for z = 0, depth - 1, 1 do
+		for y = 0, height - 1, 1 do
+			for x = 0, width - 1, 1 do
+				if maze[z][y][x] == "#" then
+					out:write("\ttranslate(["..x*scale..", "..y*scale..", "..z*scale.."]) cube(size = "..scale..");\n")
+				end
+			end
+		end
+	end
+	out:write("}\n")
+	out:close()
+end
+
+return M
diff --git a/utils/print_r.lua b/utils/print_r.lua
new file mode 100644
index 0000000..3fb5f56
--- /dev/null
+++ b/utils/print_r.lua
@@ -0,0 +1,42 @@
+local M = {}
+
+---------------------------------------------
+-- Intelligent table printing function
+---------------------------------------------
+-- From http://coronalabs.com/blog/2014/09/02/tutorial-printing-table-contents/
+
+function M.print_r ( t )
+	local print_r_cache = {}
+	local function sub_print_r(t, indent)
+		if (print_r_cache[tostring(t)]) then
+			print(indent.."*"..tostring(t))
+		else
+			print_r_cache[tostring(t)] = true
+			if (type(t) == "table") then
+				for pos, val in pairs(t) do
+					if (type(val) == "table") then
+						print(indent.."["..pos.."] => "..tostring(t).." {")
+						sub_print_r(val, indent..string.rep(" ", string.len(pos) + 8))
+						print(indent..string.rep(" ", string.len(pos) + 6).."}")
+					elseif (type(val) == "string") then
+						print(indent.."["..pos..'] => "'..val..'"')
+					else
+						print(indent.."["..pos.."] => "..tostring(val))
+					end
+				end
+			else
+				print(indent..tostring(t))
+			end
+		end
+	end
+	if (type(t) == "table") then
+		print(tostring(t).." {")
+		sub_print_r(t, "  ")
+		print("}")
+	else
+		sub_print_r(t, "  ")
+	end
+	print()
+end
+
+return M;
diff --git a/utils/strings.lua b/utils/strings.lua
new file mode 100644
index 0000000..8f63755
--- /dev/null
+++ b/utils/strings.lua
@@ -0,0 +1,11 @@
+local M = {}
+
+function M.starts_with(str, start)
+   return str:sub(1, #start) == start
+end
+
+function M.ends_with(str, ending)
+   return ending == "" or str:sub(-#ending) == ending
+end
+
+return M
