Skip to content

Instantly share code, notes, and snippets.

@nimaid
Created August 23, 2023 06:18
Show Gist options
  • Save nimaid/a2cd4d204bd9c677d67e8a8d8de44d06 to your computer and use it in GitHub Desktop.
Save nimaid/a2cd4d204bd9c677d67e8a8d8de44d06 to your computer and use it in GitHub Desktop.
A maze generator for ComputerCraft (Direct Version)
-- pastebin get R8SRcS2S maze.lua
-- A recursive backtracker maze generator for my Create mechanical maze
-- All function parameters are 0-indexed, Because I hate LUA.
-- To install the slave PC software:
--[[
pastebin get 5fGx23UZ rx.lua
edit rx.lua
pastebin get GWSF49zj startup.lua
--]]
-- Networking parameters
rednet.open("bottom") -- Open RedNet on the bottom face
local msg_size = 5 -- Only 5 bits per computer
-- Main user variables
local maze_dim = 8 -- An 8x8 Maze
local button_side = "front" -- The side to listen for button inputs on
-- Function to send out data to an array of rx computers
send_bytes = function(data)
local send_length = #data
if send_length > 0 then
msgs_needed = math.ceil(send_length / msg_size)
for m=0,(msgs_needed-1) do
msg_offset = m * msg_size
msg_str = tostring(m) -- start of string
for i=1,msg_size do
local data_in = ""
if data[msg_offset+i] == true then
data_in = "1"
else
data_in = "0"
end
msg_str = msg_str .. " " .. data_in
end
rednet.broadcast(msg_str)
end
end
end
-- Function to make a table filled with a value
filled_table = function(table_size, value)
local new_table = {}
for i=1, table_size do
new_table[i] = value
end
return new_table
end
-- Function to make a 2d table filled with a value
filled_table_2d = function(table_size_x, table_size_y, value)
local new_table = {}
for x=1, table_size_x do
new_table[x] = {}
for y=1, table_size_y do
new_table[x][y] = value
end
end
return new_table
end
-- A class to represent directions
local Direction = { UP = 0,
RIGHT = 1,
DOWN = 2,
LEFT = 3 }
-- A function to pick a random Direction
random_dir = function()
local n = math.random(4)
if n == 1 then
return Direction.UP
elseif n == 2 then
return Direction.DOWN
elseif n == 3 then
return Direction.LEFT
else
return Direction.RIGHT
end
end
-- Main Function
main = function()
-- Make major variables
local cell_explored = filled_table_2d(maze_dim, maze_dim, false)
local wall_count = maze_dim * (maze_dim + 1) * 2
local wall = filled_table(wall_count, true)
-- A function to get the index of a cell wall in a specified direction
get_wall_index = function(x, y, dir)
local n = x + (y * maze_dim) -- The "cell number", left to right, top to bottom
if dir == Direction.UP then
return n + (maze_dim * (maze_dim + 1))
elseif dir == Direction.DOWN then
return n + (maze_dim * (maze_dim + 2))
elseif dir == Direction.LEFT then
return n + y
elseif dir == Direction.RIGHT then
return n + y + 1
else
error("Invalid direction!")
end
end
-- A function to set a wall state
set_wall = function(x, y, dir, state)
wall[get_wall_index(x, y, dir)+1] = state
end
-- A simplified function to break a wall
break_wall = function(x, y, dir)
set_wall(x, y, dir, false)
end
-- A function to "explore" a given cell
explore_cell = function(x, y)
cell_explored[x+1][y+1] = true
end
-- A function to get the "explored" state of a cell
is_explored = function(x, y)
return cell_explored[x+1][y+1]
end
-- A function to get a list of unexplored neighbor cells and their directions
get_neighbors = function(x, y)
local neighbor_list = {}
if x > 0 then -- If a left cell exists
if not is_explored(x - 1, y) then -- If left cell not explored
table.insert(neighbor_list,
{
dir = Direction.LEFT,
x = x - 1,
y = y
}
)
end
end
if x < (maze_dim-1) then -- If a right cell exists
if not is_explored(x + 1, y) then -- If right cell not explored
table.insert(neighbor_list,
{
dir = Direction.RIGHT,
x = x + 1,
y = y
}
)
end
end
if y > 0 then -- If an up cell exists
if not is_explored(x, y - 1) then -- If up cell not explored
table.insert(neighbor_list,
{
dir = Direction.UP,
x = x,
y = y - 1
}
)
end
end
if y < (maze_dim - 1) then -- If a down cell exists
if not is_explored(x, y + 1) then -- If down cell not explored
table.insert(neighbor_list,
{
dir = Direction.DOWN,
x = x,
y = y + 1
}
)
end
end
return neighbor_list
end
-- Calculate random starting point and direction
local start_point = {dir = random_dir(), x = nil, y = nil}
local start_coord = math.random(maze_dim) - 1
if start_point.dir == Direction.UP then
start_point.x = start_coord
start_point.y = 0
elseif start_point.dir == Direction.DOWN then
start_point.x = start_coord
start_point.y = maze_dim - 1
elseif start_point.dir == Direction.LEFT then
start_point.x = 0
start_point.y = start_coord
elseif start_point.dir == Direction.RIGHT then
start_point.x = maze_dim - 1
start_point.y = start_coord
end
-- Make end point opposite of start point
local end_point = { x = (maze_dim - 1) - start_point.x,
y = (maze_dim - 1) - start_point.y,
dir = nil
}
if start_point.dir == Direction.UP then
end_point.dir = Direction.DOWN
elseif start_point.dir == Direction.DOWN then
end_point.dir = Direction.UP
elseif start_point.dir == Direction.LEFT then
end_point.dir = Direction.RIGHT
elseif start_point.dir == Direction.RIGHT then
end_point.dir = Direction.LEFT
end
-- Punch entrance and exit
break_wall(start_point.x, start_point.y, start_point.dir)
break_wall(end_point.x, end_point.y, end_point.dir)
--[[
-- Recursive method:
--
-- 1. Given a current cell as a parameter
-- 2. Mark the current cell as visited
-- 3. While the current cell has any unvisited neighbour cells
-- a. Choose one of the unvisited neighbours
-- b. Remove the wall between the current cell and the chosen cell
-- c. Invoke the routine recursively for the chosen cell
--]]
recursive_maze = function(x, y) -- (1)
explore_cell(x, y) -- (2)
-- (3)
local neighbours = get_neighbors(x, y)
while(#neighbours > 0) do
local new_cell = neighbours[math.random(#neighbours)] -- (a)
break_wall(x, y, new_cell.dir) -- (b)
recursive_maze(new_cell.x, new_cell.y) -- (c)
neighbours = get_neighbors(x, y) -- Update neighbors list
end
end
recursive_maze(end_point.x, end_point.y) -- Run!
send_bytes(wall) -- Setup maze
end
print("Give a redstone signal to the " .. button_side .. " to make a new maze!\n")
while true do
os.pullEvent("redstone") -- Wait for a "redstone" event
if rs.getInput(button_side) then -- If it's the right side...
print("Making maze...")
main()
print(" Done!")
end
end
-- pastebin get 5fGx23UZ rx.lua
-- edit rx.lua
-- pastebin get GWSF49zj startup.lua
local rx_id = "0"
rednet.open("bottom")
-- A helper table to map bit positions to sides
local side_bit_map = {nil, "left", "back", "right", "front", "top"}
split_text = function(text_in)
local result = {}
for w in text_in:gmatch("%S+") do
table.insert(result, w)
end
return result
end
while true do
id, msg = rednet.receive()
split_msg = split_text(msg)
if #split_msg == #side_bit_map then
rx_dest = split_msg[1]
if rx_dest == rx_id then
print("Data received!")
for idx=2,6 do
if split_msg[idx] == "1" then
rs.setOutput(side_bit_map[idx], true)
else
rs.setOutput(side_bit_map[idx], false)
end
end
print(" Sides updated!")
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment