Need help with random map/city generator

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
User avatar
Jasoco
Inner party member
Posts: 3725
Joined: Mon Jun 22, 2009 9:35 am
Location: Pennsylvania, USA
Contact:

Re: Need help with random map/city generator

Post by Jasoco »

It shows up now. But the others don't show up for anyone but you because you've already seen them and they're in your browser cache. When you use image hosts, make sure to either use the image URL they supply you or the one that has UBB IMG tags around it. (On that host you used before, you need to click the "Show codes" link to see them. I suggest just using Imgur since they don't hide that stuff from people. It's stupid to hide that stuff. It's the whole point of an image host.)

Alternatively, just use the forum's file attachment system when posting.

Rickton wrote:Do you have any code uploaded anywhere of your method of using that kind of generation? It seems really interesting, but he talks about "sliding" the new room around until it fits, and doing that for every single new room. Is there some kind of algorithm or something for that, or a way of doing it intelligently? Because it seems like it'd be pretty inefficient and slow to do that dozens of times for a full level.
My code is a mess that would make you pull your hair out. Instead code it yourself following the description in the article. It's not that easy though. It's quite complex. And only one of many methods.
User avatar
Imaculata
Party member
Posts: 102
Joined: Mon Aug 10, 2015 2:51 pm

Re: Need help with random map/city generator

Post by Imaculata »

Thanks, will do.

I just added a messy bit of code that is supposed to carve out doorways in between the rooms. But it doesn't seem to be working quite right. Occasionally it only carves out half the doorway, and then stops at the next room. I probably messed something up. Maybe its not reliably picking the correct tiles to replace with floor tiles. Or maybe something is getting overwritten.

Once I get it to work, I'll start adding the doors themselves between the rooms.
louie999
Prole
Posts: 46
Joined: Fri Mar 06, 2015 9:01 am

Re: Need help with random map/city generator

Post by louie999 »

Ok, so I tried making a map generator, I also put the t.console in conf.lua to true because I laid down alot of print() functions around the map generator code so I can really make sure that it's working properly, but my problem is some time after the map generation begins then my game will suddenly freeze and not respond so I have to close it :( is it because there's too many loops? code:

Code: Select all

-- Variables
local generating = false
local gen_default = false
local TILES = wazmb.ALLTILES
local done = false
local lmt = love.math
wazmb.MAP = {}
wazmb.MAP.X = 0
wazmb.MAP.Y = 0
-----
local function newBuilding(x, y)
	TILES[x][y] = {tiletype = 3, tilesetn = 1}
	TILES[x+1][y] = {tiletype = 3, tilesetn = 7}
	TILES[x+2][y] = {tiletype = 3, tilesetn = 2}
	TILES[x][y+1] = {tiletype = 3, tilesetn = 3}
	TILES[x+1][y+1] = {tiletype = 3, tilesetn = 4}
	TILES[x+2][y+1] = {tiletype = 3, tilesetn = 9}
	TILES[x][y+2] = {tiletype = 3, tilesetn = 5}
	TILES[x+1][y+2] = {tiletype = 3, tilesetn = 6}
	TILES[x+1][y+2] = {tiletype = 3, tilesetn = 8}
end

local function tile_occupied(x, y)
	if TILES[x][y].tilesetn ~= 0 then
		return true
	end
	return false
end

local function GENERATE_DEFAULT_TILES(maxx, maxy)
	local numx, numy = 0, 0
	while gen_default do
		local randx, randy = lmt.random(1, maxx), lmt.random(1, maxy)
		if numx >= maxx and maxy >= maxy then gen_default = false end
		if not tile_occupied(randx, randy) then
			TILES[randx][randy] = {tiletype = 1, tilesetn = 1}
			numx = numx + 1
			numx = numy + 1
			print("Placed default tile in "..randx..", "..randy)
		end
	end
	wazmb.ALLTILES = TILES
	done = true
	print("Map generation done")
end

local function GENERATE_BUILDINGS(maxx, maxy, maxb)
	print("Generating buildings...")
	local cb = 0
	print("1")
	while generating do
		local rand1 = math.random(1, 100)
		local rand2 = math.random(1, 100)
		local randx, randy = lmt.random(1, maxx), lmt.random(1, maxy)
		if cb >= maxb then generating = false; gen_default = true end
		if rand1 - rand2 >= 35 then
			newBuilding(randx, randy)
			print("Building generated at - "..randx..", "..randy)
			cb = cb + 1
		end
	end
	if gen_default then GENERATE_DEFAULT_TILES(maxx, maxy) end
end

function wazmb.GENERATOR_INIT(maxx, maxy, maxb)
	print("Generating random map...")
	if maxx ~= nil then
		if maxx < 200 then maxx = 200 end 
	end
	if maxy ~= nil then
		if maxy < 200 then maxy = 200 end 
	end
	local MAP_MAX_X = maxx or lmt.random(200, 500)
	local MAP_MAX_Y = maxy or lmt.random(200, 500)
	local MAXBUILD = maxb or lmt.random(20, 50)
	wazmb.MAINCAM = wazmb.gamera.new(0, 0, MAP_MAX_X, MAP_MAX_Y)
	for X = 1, MAP_MAX_X do
		TILES[X] = {}
		for Y = 1, MAP_MAX_Y do
			print("Tile "..X..", "..Y)
			TILES[X][Y] = {tiletype = 0, tilesetn = 0}
		end
	end
	generating = true
	local ok, err = pcall(GENERATE_BUILDINGS, MAP_MAX_X, MAP_MAX_Y, MAXBUILD)
	if err then print(err) end
end

function wazmb.DRAW_MAP()
	print("Drawing map...")
	for X, map in ipairs(wazmb.ALLTILES) do
		for Y, t in ipairs(map) do
			local tiletype = wazmb._TILE._TILESET[t.tilesetn].q
			local x, y = (X - 1) * wazmb._TILE._TILESIZE, (Y - 1) * wazmb._TILE._TILESIZE
			lg.draw(wazmb._TILE._TILE_SET_, tiletype, x, y)
		end
	end
end

function wazmb.Map_Control()
	if done then
		local isDown = wazmb.lk.isDown
		local cam = wazmb.MAINCAM
		local t = {
			["up"] = {x = 0, y = 10},
			["down"] = {x = 0, y = -10},
			["right"] = {x = 10, y = 0},
			["left"] = {x = -10, y = 0}
		}
		if t[isDown] ~= nil then
			wazmb.MAP.X = wazmb.MAP.X + t[isDown].x
			wazmb.MAP.Y = wazmb.MAP.Y + t[isDown].y
		end
		cam:setPosition(wazmb.MAP.X, wazmb.MAP.Y)
	end
end

Code: Select all

fun = true
school = true

function isItFun()
    if school then
       fun = false
    end
    if not fun then 
       me:explode()
    end
end
User avatar
Imaculata
Party member
Posts: 102
Joined: Mon Aug 10, 2015 2:51 pm

Re: Need help with random map/city generator

Post by Imaculata »

You have less loops than I do, and my code finishes in less than a second. So I'm going to guess that the problem is most not related to the number of loops, but more likely caused by your loops getting stuck. I'm not sure if you are using the same method for creating random dungeons as I am. But what I did, was make sure that the crawlers were limited to a maximum length (in rooms), and a maximum number of attempts to find a suitable location to expand the maze to.

There's a high chance for error since my method of making a dungeon is very dumb. The crawler can easily corner itself without realizing it, so I made sure that after a certain number of tries, it simply gives up. Did you insert fail safes?
User avatar
Jasoco
Inner party member
Posts: 3725
Joined: Mon Jun 22, 2009 9:35 am
Location: Pennsylvania, USA
Contact:

Re: Need help with random map/city generator

Post by Jasoco »

You need to set a limit to the amount of loops that can run and use break to exit out of the loop if it gets to be too much. Also you can use love.timer.sleep(0.001) every few thousand loops to give some control back to the system if the loop is going to take a notably long time.
User avatar
Nixola
Inner party member
Posts: 1949
Joined: Tue Dec 06, 2011 7:11 pm
Location: Italy

Re: Need help with random map/city generator

Post by Nixola »

You should call love.event.pump every once in a while during said loops too, so that the OS doesn't think it froze.
lf = love.filesystem
ls = love.sound
la = love.audio
lp = love.physics
lt = love.thread
li = love.image
lg = love.graphics
User avatar
Jasoco
Inner party member
Posts: 3725
Joined: Mon Jun 22, 2009 9:35 am
Location: Pennsylvania, USA
Contact:

Re: Need help with random map/city generator

Post by Jasoco »

Basically in your while loop increment a counter and if it hits an arbitrary number of loops, pump the events and sleep the timer for 0.001 seconds and if you want you can also call love.graphics.clear(), draw some stuff and then love.graphics.present() to draw a "Generating..." message or even a progress bar with some work if you know a definite amount of time it should take.

Some pseudocode:

Code: Select all

local loops = 0
while whatever_you_are_doing do
	-- Your code that takes a while goes here

	if loops == 0 then
		love.graphics.clear() -- Sets the window background to the background color
		love.graphics.setColor(255,255,255) -- A color that is visible against the background color
		love.graphics.print("Generating...", 10, 10)
		love.graphics.present()
		-- You could draw anything you want here really. Don't draw too much.
		-- All you need is something to tell the user that the game is doing stuff.

		love.timer.sleep(0.001)
		love.event.pump()
	end
	loops = loops + 1
	if loops > 10000 then loops = 0 end
end
Where 10000 above is a number you would figure out yourself based on how long each loop takes. If you set it too low you'll slow down your generation because the drawing and sleep time will add up way too much. If you set it too high the loop will run longer than the OS wants to wait and you might get the spinning cursor or Windows "Application is hanging" dialog. So tweak the value on a per loop basis.
louie999
Prole
Posts: 46
Joined: Fri Mar 06, 2015 9:01 am

Re: Need help with random map/city generator

Post by louie999 »

Thanks guys, the map generator is still not 100% complete but atleast the loop problem is fixed :D

Code: Select all

local function GENERATE_BUILDINGS(maxsize, maxb)
	local cb = 0
	while generating do
		local rand1 = math.random(1, 100)
		local rand2 = math.random(1, 100)
		local randx, randy = lmt.random(1, maxsize), lmt.random(1, maxsize)
		if cb >= maxb then generating = false; gen_default = true end
		if rand1 - rand2 >= 35 then
			newBuilding(randx, randy)
			wazmb.MAP.X = randx
			wazmb.MAP.Y = randy
			cb = cb + 1
		end
	end
	wazmb.ALLTILES = TILES
	wazmb.mapready = true
end

function wazmb.GENERATOR_INIT(maxsize, maxb)
	if maxsize ~= nil then
		if maxsize < 200 then maxsize = 200 end
	end
	local i = 0
	local MAP_MAX_SIZE = maxsize or lmt.random(200, 500)
	local MAXBUILD = maxb or lmt.random(20, 50)
	wazmb.MAINCAM = wazmb.gamera.new(0, 0,  MAP_MAX_SIZE,  MAP_MAX_SIZE)
	for X = 1,  MAP_MAX_SIZE do
		TILES[X] = {}
		for Y = 1,  MAP_MAX_SIZE do
			if i >= 500 then love.event.pump(); love.timer.sleep(0.001); i = 0 end
			TILES[X][Y] = {tiletype = 1, tilesetn = 10}
			i = i + 1
		end
	end
	generating = true
	GENERATE_BUILDINGS(MAP_MAX_SIZE, MAXBUILD)
end

Code: Select all

fun = true
school = true

function isItFun()
    if school then
       fun = false
    end
    if not fun then 
       me:explode()
    end
end
User avatar
Imaculata
Party member
Posts: 102
Joined: Mon Aug 10, 2015 2:51 pm

Re: Need help with random map/city generator

Post by Imaculata »

Excellent work Louie! In the case of my Zelda clone, the crawler can only go in 4 directions. So I have it randomly pick one of the 4 directions, and check if it can build a room there. If it can't find a room after 24 tries, it finishes the process, and declares the chain as done. 24 is a much smaller number than 10,000 of course. But the difference in calculation time is probably merely a fraction of a second. I figured that it wouldn't take that many tries to find a suitable room, if there are only 4 options to randomly choose from.

When I start adding the sub-chains, then the crawlers will need more fail-safes. Because what if I try to create 4 chains, but the third chain has already used up all available empty rooms? I'll need to make sure that the crawler gives up after a while, and continues with the dungeon creation process.
User avatar
ArchAngel075
Party member
Posts: 319
Joined: Mon Jun 24, 2013 5:16 am

Re: Need help with random map/city generator

Post by ArchAngel075 »

I had once used a tiled like method to make a dungeon crawler.

First i would place a root room - it is always a static x by y room.

Next iterate n times where n is the number of rooms id like to add,
for each iteration pick a random room and check if it isn't fully neighbored. That is that it doesn't have a room in each 4 directions.
If it can take a room then for each direction do the following,
Pick a distance to place a corridor, then from the biggest size room possible to the smallest decreasing (width and height test separately) try and fit that room in so that it doesn't over lap other rooms, it can have shared walls with a existing room though.
If a possible fit is found then place the room and then carve a straight corridor between the two rooms.
after each successful wall placement for every room test if there if there is a room in any four directions and a random chance payed (~30% to do this else it ignores), if one is found then it forces them to have a corridor link.

With repeating in all four directions it can give up and mark a room as 'complete' if it has had all 4 directions exhausted (no rooms can fit if it tried on all four)
Repeat this process n times.

Note :
It was brutal making it, especially since i had to make rooms downsize and then center, and a issue when one room was evenly length on a wall and another had a odd sized length/breadth wall and the two wanted to corridor between one another.
It made amazing multi entranced connections and could squeeze the most of a tilemap. almost using up 80% of the tiles, only small bits of air that is around 2x2 in-between because of the attempt at force downsizing a room until it fits.
Post Reply

Who is online

Users browsing this forum: Amazon [Bot] and 50 guests