This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
Yarillo's profile picture

[WIP] [Infinity 1.7.0] Procedural building

Started by Yarillo, 11 July 2015 - 04:25 AM
Yarillo #1
Posted 11 July 2015 - 06:25 AM
EDIT: Project is pretty much dead for now. It's almost finished but I went out of town for a week. Now I can't bring myself to finish it. Things left to do:
- An algorithm that scans the city's empty spaces, separates them into rectangles of varying sizes, so that all of the available land is used and with a minimum amount of 1chunk by 1chunk buildings (here a chunk is 13x13 blocks wide)
- A function that "paints" the city. Divides it into neighborhoods. Maybe by creating some dots over the city and for each building, calculating which point is the closest to it. Associate each of those points to a particular colour and you have painted your city.
If anyone wants to finish it, go ahead please

pastebin get dj7hsjKU citybuilder

EDIT: If you want to use it as an API, be sure to set "activateTheExamples" to false at the beginning.


I'm looking for a distraction and I also want to be a little productive during the vacations. So I'm doing this thing and I intend to update it regularly.

If you guys are interested in helping me with it, I made a github since some friends of mine already came forward to tinker the city's generation code.


I just received my optical fiber connection from my french ISP so since it takes me only a couple of minutes to record and upload videos to Youtube, I will probably take advantage of that and upload a video of every big change.
It's not very useful per se but i'm self taught and the time lost will be compensated by the great motivation it gives me to me to be able to show my stuff to my friends and the world. :P
It's just not as fun if nobody sees my stuff.
So for those who enjoy watching progress videos, enjoy ! There's going to be quite a lot of them.


Current features:
  • Push the button and it'll spawn a different building everytime
  • Can work as an API to build structures quickly if cropped a little at the bottom of the code
  • It can build: cubes, floors, walls, rooms, rooms with windows, buildings
  • Smart placement and sizing of windows. It'll most likely make something nice.
  • Randomization of the materials that are used to build stuff
  • Sorting materials with categories ("brown", "gray"…) to make coherent buildings and allow for thematic neighborhoods
  • It will build ghost floors, ghost walls or ghost windows at will if any of the blocks in my list aren't there (aka not having the right mods installed)
  • Road making
  • Almost able to make a full city generation !
PlansPlans:
  • I hope I'll be able to make it build neighborhoods or maybe even cities. I think building the roads first is the smartest way to approach it. It is not !
  • Whenever I feel fancy, I'll try and make it create rooms inside of the buildings. Maybe even with some furnitures.
  • I'll introduce different shapes of buildings later on. Done !
  • Introduce a zombie apocalypse ?
  • Make the buildings pretty ? Done !

Update n°5
[media]http://www.youtube.com/watch?v=MV_GOv9nIM0[/media]

Youtube VideosPlaylist: https://www.youtube....SvQ6Zog4sVudvsW

Update n°1: The basics (That thread wasn't even made back then)
[media]http://www.youtube.com/watch?v=t6kXCcjsvyY[/media]
Update n°2: The random buildings
[media]http://www.youtube.com/watch?v=pHSuypOkPjA[/media]
Update n°3: Making amazing roads
[media]http://www.youtube.com/watch?v=zjen5EzDTAM[/media]
Update n°4: Creating a path (Day 2)
It was actually a pretty bad idea to make this. It really isn't the simplest way to make a city. How do I know where to place my buildings after that ? I scrapped this idea and saved this code as a fun example for the API mode.
[media]http://www.youtube.com/watch?v=P3GSaEJgUIw[/media]
Update n°5: Prettier buildings
[media]http://www.youtube.com/watch?v=MV_GOv9nIM0[/media]
Update n°6: Almost there with the city generation
[media]http://www.youtube.com/watch?v=t22AOA-Zg4Y[/media]

SpoilerIf you are a madman, you can check out the latest of the latest version here:
http://wyvern67.free...citybuilder.lua

It's updated in real time and that's what I use to transfer my code from my computer to the game quickly.
Edited on 16 October 2015 - 02:46 PM
MKlegoman357 #2
Posted 11 July 2015 - 09:53 AM
This is very very nice! I'd really like to see this being able to generate random cities. This kind of thing could be used for adventure maps, where users could replay the same map but with different structures generated every time. Good job!
Yarillo #3
Posted 11 July 2015 - 10:10 AM
Thanks :D/> Glad to see there's some interest in such a thing.
I'll make the neighborhood+cities generator a priority then. I'll make the buildings nicer another time
Edited on 11 July 2015 - 08:11 AM
Yarillo #4
Posted 11 July 2015 - 11:46 AM
Alright, sorry to double post. I'll try not to respond to everyone so I can keep updating the post without double posting :D/>

I finished a couple of functions and it turned out way cooler than expected. It's amazing to watch. It makes roads and intersections easy as hell to make. A starting position, a direction and a length make a road. A position and two directions make an intersection.
It's amazing.


length = 20
x=0
y=0
z=0
road(x,y,z,"north", length)
road(x,y,z,"east", length)
roadIntersection(x,y,z,"north,east")
-- makes an intersection between a road going north and a road going east with walkways and shit


I hooked up a randomizer to the thing to show off a bit. Sorry for the music, I didn't realize I was recording it too.

[media]http://www.youtube.com/watch?v=zjen5EzDTAM[/media]
Edited on 11 July 2015 - 10:16 AM
Bomb Bloke #5
Posted 11 July 2015 - 01:11 PM
Would it be of any interest to you if I were to rig my own building script, WorldPorter, to operate in a silent mode by way of command line arguments? The structures it builds aren't "dynamic" in the way you're going for, but I reckon it'd provide an easy avenue for dotting rather more "abstract" and "unique" buildings around your cities.

I do suggest implementing roads of different widths, and also diagonals, if you're up for it. Certainly vary the textures a bit. A system for implementing slopes would also make your landscapes much more visually interesting.

Good job so far. :)/>
Edited on 11 July 2015 - 11:11 AM
MKlegoman357 #6
Posted 11 July 2015 - 02:11 PM
Alright, sorry to double post. I'll try not to respond to everyone so I can keep updating the post without double posting :D/>

It's perfectly fine to double-post if those two posts are not related. You can also edit a post by clicking on the 'edit' button on the bottom-right of the post.

Anyway, I'm really interested to see what might come out of this, keep up the good work! :)/>

EDIT:
Sorry for the music, I didn't realize I was recording it too.

It starts at the same time the video starts so it's OK, makes the video more interesting to watch :P/>
Edited on 11 July 2015 - 12:13 PM
Yarillo #7
Posted 11 July 2015 - 02:41 PM
It starts at the same time the video starts so it's OK, makes the video more interesting to watch :P/>

It's even kind of synchronized ! It drops when I open the computer and start typing.

I made a new thingy, but it sucks. I thought it would be a good idea to make a city like this but clearly it isn't a good idea.

[media]http://www.youtube.com/watch?v=P3GSaEJgUIw[/media]

Look at the clusterfuck ! Also, you can see a sneak peak of how I transfer the files from my computer to Minecraft. I use an FTP and macros and shit. c:

My new idea is to make a couple of parallel roads with random amounts of space between them. Keep track of where they start and where they end, then, I'd run a script that would search for nearby roads at those points and create perpendicular roads to connect them to each other. And keep track of the point they connect to.

If I were to implement slopes, I think this would become very difficult, as I would have to make everything look good in addition to making it work. And I can foresee how hard it would be to make a nice looking sloped road.
Diagonal roads on the other hand could be cool. Simple as "mx+b". But that's indeed still a challenge. I'd have to make triangular buildings. That's not hard, but I'd have to re-write another function to give them windows. And now that's hard !
To avoid the trouble of making triangular buildings, I could simply fill any triangle with a grassy park though.

EDIT: Oh and I forgot about WorldPorter !
I've seen it and the idea crossed my mind. I like the idea of having a couple of "unique" buildings. But I don't even have the working city building thing yet so that would come later !
But indeed, if you could rig it to operate in silent, I would definetly use it. I would love to have some personnalized trees and stuff. Maybe not the buildings at first because it's hard to implement. But that'll come later maybe
Edited on 11 July 2015 - 01:14 PM
Bomb Bloke #8
Posted 12 July 2015 - 01:23 AM
My new idea is to make a couple of parallel roads with random amounts of space between them. Keep track of where they start and where they end, then, I'd run a script that would search for nearby roads at those points and create perpendicular roads to connect them to each other. And keep track of the point they connect to.

One method would be to define "blocks" of land (which will eventually contain one or more buildings), and then to simply space them apart by whatever your road width is and running said roads along the east-most and south-most sides of each block.

The size of each block would be mostly random, but assuming you were generating them west-to-east, north-to-south, you'd maybe incorporate a flat 25% chance that the width of a new block would line its east end up with the east end of the block to the north of it, and a separate flat 25% chance that its length would line its south end up with the south end of the block to the west of it. This'd hopefully make the layout tend towards having lengthier "streets" while at the same time not looking too boring.
Yarillo #9
Posted 14 July 2015 - 07:42 AM
It was a rather slow progress because of some bugs I have been battling with (:'D) and me celebrating my high school graduation. But it has progressed quite a bite nonetheless !

First of all a friend of mine joined me and helped for a bit with the terrain generator. He was able to make a function that can draw the map of the city (here it is displayed at a scale of 1:12).

(He didn't wake up yet so I won't quote his name before he tells me to)

[media]http://www.youtube.com/watch?v=t22AOA-Zg4Y[/media]

What do you think Bomb Bloke ?

I discovered not long after making that video that some variables responsible for the randomization of the city will only change their value if they are equal to nil. Meaning, if you launch the program twice, the city won't change much. You have to reboot the computer. So that's why all the maps look similar in the video. It's just me using his code the wrong way.


While he did this, I upgraded the building generator (I love doing that :D/> !)

[media]http://www.youtube.com/watch?v=MV_GOv9nIM0[/media]

I added penthouses, spiky buildings that shrink as they build up, terraces…
It's starting to look pretty cool.

EDIT: I also discovered something quite worrying when using FRAPS. My computer really has trouble video capturing AND generating my buildings.



This, gentlemen, is a bunch of spikes representing my CPU getting on its knees when my building generator starts working. Fraps was not capturing when I took the screenshot.
When the CPU usage is around ~10% that's when the building generator is in his waiting phase. 4 seconds of waiting between each building.
This is weird because I have an Intel i7 8 cores 950 @3.07GHz and I thought it was quite good.

Also, we're approaching 1000 lines of code !
Edited on 14 July 2015 - 06:07 AM
Bomb Bloke #10
Posted 14 July 2015 - 09:31 AM
What do you think Bomb Bloke ?

Quite a few gaps, overlaps, and disconnected roads (getting worse as generation moves away from the computer), but even assuming those are unintended and need to be fixed, it captures the basics of the idea. :)/>

EDIT: I also discovered something quite worrying when using FRAPS. My computer really has trouble video capturing AND generating my buildings.

Well, yeah.

Most ComputerCraft scripts don't run code "constantly". They yield often, and do so in order to wait for something - keypresses, timers, rednet messages, whatever. They end up spending more time yielding than executing.

Some scripts, on the other hand, manage to come up with a large workload that doesn't require any pauses. This one simply shovels commands into MineCraft's queue as fast as it can, the intent being to get the whole build done ASAP - and so a core gets maxed out until that happens.

You could, if you wanted, rig up a timer system so that the script only allows a certain amount of blocks to be placed per second. So long as the processor could keep up with that demand, usage would drop off. Frankly, though, I don't see the point - there's not much point in having a decent processor if you don't intend to use it!
Yarillo #11
Posted 14 July 2015 - 09:37 AM
You could, if you wanted, rig up a timer system so that the script only allows a certain amount of blocks to be placed per second. So long as the processor could keep up with that demand, usage would drop off. Frankly, though, I don't see the point - there's not much point in having a decent processor if you don't intend to use it!

I actually did implement that right at the beginning. I added a safeguard inside the commands.execAsync function to avoid "toolongwithoutyielding" errors and the commands queue getting too large.


old.execAsync = commands.execAsync -- Editing the default execAsync command to include a safeguard
local function execAsync(str) -- Too many queries in a single blast will cause problems.
		if execAsyncQueries == nil then
				execAsyncQueries = 0
		end
	  
		timeDiff2 = deltaT("execAsync")
		-- I will measure how long I waited between each queries
		-- As CC time units do not go after the 2d decimal i will wait 0.1s every 850 queries
		-- I call a row of queries a "blast"
		-- (8500 q/s at best)
		if timeDiff2 > restingTime then
				execAsyncQueries = 0 --Blast of queries was cut off. I consider this one finished
		else
				execAsyncQueries = execAsyncQueries+1
		end
		-- console(execAsyncQueries, 7)
		if execAsyncQueries > dangerousAmountOfQueries then
				sleep(restingTime)
				execAsyncQueries = 0 --just to be sure, manually consider the blast as finished
		end
	  
		return old.execAsync(str)
end
commands.execAsync = execAsync
Edited on 14 July 2015 - 07:40 AM
Bomb Bloke #12
Posted 15 July 2015 - 02:26 AM
So all you need to do is reduce "dangerousAmountOfQueries" to taste. :)/>
Forgotten_Boy #13
Posted 15 July 2015 - 05:13 PM
This is really sweet. It would be perfect to implement my dream of playing ye olde game "Railroad Tycoon" in Minecraft by combining Railcraft with this to make villages that actually grow; this could make cities and new industries pop up as you deliver/pickup your goods…
Bomb Bloke #14
Posted 16 July 2015 - 04:59 AM
Egad, you could play a full-on Sim City clone - have players interact via a monitor to zone out areas, then have buildings appear on them over time, maybe spawn the occasional Testificate, and so on…
Yarillo #15
Posted 16 July 2015 - 01:40 PM
Yeah, or even both. You could link the command computer to a screen and it would displays a map of the city. You could add new buildings or make them grow higher and higher by clicking buttons. Each upgrade would cost some blocks and you'd have to use the classic FTB mechanics to move forward in the game.

Some buildings could have a purpose and you could basically invest a shit-load of ressources into industries that would "print" you some hard to craft items.

Possibilities are endless, that's why I'm implementing in the code an "API mode", so that anyone can use it and make awesome things on their own.
Edited on 16 July 2015 - 11:42 AM
Forgotten_Boy #16
Posted 16 July 2015 - 08:34 PM
So… hmmm… couldn't get it to work. Load it up, run "citybuilder", nothing happens. Tried some of your lua/api examples but no deal. How to use?
Yarillo #17
Posted 17 July 2015 - 08:23 AM
By default, the examples are deactivated. It's basically just an API. It launches, registers a lot of functions then stops because no commands have been issued. You have to set activateTheExamples to true at the beginning !
I think I'll activate the examples by default from now on. Those who plan to use it as an API won't mind having to fiddle with one variable.


local activateTheExamples = false -- You won't be able to use it as an API if you activate this but you will have access to cool examples when you launch it

Tried some of your lua/api examples but no deal.

Wut ? Could you be more specific on what happens and how did you achieve that ?
Edited on 17 July 2015 - 07:20 AM
Forgotten_Boy #18
Posted 17 July 2015 - 10:21 PM
I'll try it again with examples activated. I just wanted my kids to be able to build random stuff! I was trying to follow along with the video when I did the API examples so I did something wrong for sure.
Yarillo #19
Posted 18 July 2015 - 06:00 PM
I stumbled upon the weirdest bug.



See the ores everywhere ? That's the bug. I'm only using stone and obsidian here.
Looks like Minecraft tends to add ores everywhere when I generate large amounts of stone blocks. Especially when i'm generating them far away from the computer.

EDIT: Oh damn ! That's because I'm generating stone in undiscovered areas, so when I go there Minecraft loads the area, with my stone in it, and runs his little coroutine to add ores inside it ! Awesome !
I guess using stone in my city will be forbidden ! :D/>
Edited on 18 July 2015 - 04:03 PM
Bomb Bloke #20
Posted 19 July 2015 - 12:55 AM
Huh, and here I thought the chunks would need to be loaded to place blocks into them.

I know that commands.getBlockInfo() can be used to load chunks.
Yarillo #21
Posted 19 July 2015 - 05:50 PM
Sorry Bomb Bloke ! I completly re-did the city generation code to something more modular and generally better.
Your idea just didn't work out as it was and I had to scrap it. It didn't feel "random" enough, it made some holes, you had to check afterwards if there were any useless parallel roads…

So, this morning started from scratch with the generation and I managed to do this:



Completly new approach.

Here's how it's done: You make you city chunk by chunk.
You start by making your first line by simply placing chunks of buildings of random length, and spacing them with a road each time.
3 buildings, then a road, then 2 buildings, then a road, then 1, road, 3, road, 2, road, etc…
After that, you define a set of rules. Here are mine:

- If below me there are two consecutive roads, this chunk will be a building
- If below me there are three consecutive building chunks, start a new road (this prevents making the buildings too large)
- If the chunk below is a road, choose between those two actions:
1) Make a turn (1/3 of the time)
2) Continue the road (2/3 of the time)
- If the chunk below is a building, this chunk will be a building

Then I stopped a little and continued. I added a new step: "make all the intersections and turns look nice"





It was pretty tough. I figured out that not every intersection had to have crosswalks but only those with 3+ openings. The others were technically just "road turns".
So, now that some of my intersections became "turns", some of them have to get little dashes of paint.
Well, that's a problem: how do you know where to start the dashes and where to end them ? You don't want to break the pattern of the road connecting to the turn. It's one dash every 4 blocks and not "one dash every 4 blocks and sometimes when there's a turn it's whatever you want"

I'll spare the explanation because it's not really fun, but it was a challenge to figure that out

Spoiler
    
function tryAndPaintThatBlock(xa,ya,za,direction)
    slab = "chisel:marble_slab"
    asphalt = "chisel:antiBlock"
    paint = "chisel:marble"
    if direction == nil then
        direction = "all"
    end
    if direction == "north" or direction == "south" or direction == "all" then
        if (ya%7) > 3 then
            setBlock(paint,xa,ya,za)
        else
            setBlock(asphalt,xa,ya,za)
        end
    end
    if direction == "east" or direction == "west" or direction == "all" then
        if (xa%7) > 3 then
            setBlock(paint,xa,ya,za)
        else
            setBlock(asphalt,xa,ya,za)
        end
    end
end

    if string.count(openDirections, ",") >= 2 then     -- If this is an intersection
        fill(paint, xa-6, ya-2, za-1, 13, 1, 1) -- crosswalks
        fill(paint, xa-6, ya, za-1, 13, 1, 1)
        fill(paint, xa-6, ya+2, za-1, 13, 1, 1)
        
        fill(paint, xa-2, ya-6, za-1, 1, 13, 1)
        fill(paint, xa, ya-6, za-1, 1, 13, 1)
        fill(paint, xa+2, ya-6, za-1, 1, 13, 1)
        
        fill(asphalt, xa-3, ya-3, za-1, 7, 7, 1) -- asphalt square in the middle
    else -- If this is just a road turn
        fill(asphalt, xa-3, ya-3, za-1, 7, 7, 1) -- asphalt square in the middle
        xMin = 0
        xMax = 0
        yMin = 0
        yMax = 0
        
        if string.find(openDirections, "north") then
            yMax = 6
        end
        if string.find(openDirections, "south") then
            yMin = (-6)
        end
        if string.find(openDirections, "east") then
            xMax = 6
        end
        if string.find(openDirections, "west") then
            xMin = (-6)
        end
        
        for i=xa+xMin,xa+xMax do
            tryAndPaintThatBlock(i,ya,za-1,"east")
        end
        for i=ya+yMin,ya+yMax do
            tryAndPaintThatBlock(xa,i,za-1,"north")
        end
    end
Edited on 19 July 2015 - 04:02 PM
MKlegoman357 #22
Posted 19 July 2015 - 05:55 PM
Nice! But currently it seems like the road is only turning right and not left.
Yarillo #23
Posted 19 July 2015 - 06:06 PM
Yes. That's the flaw :D/>
I knew about it but I didn't think anyone could point it out.
It must be a little bit more obvious than I thought.

It's can be easily fixed, I just didn't bother out of laziness. I've been on it for too long and just wanted to update the devlog and go eat :D/>
The problem is right there:
- If the chunk below is a road, choose between those two actions:
1) Make an east turn either a left or a right turn (1/3 of the time)
2) Continue the road (2/3 of the time)

That's why it's awesome ! You can fix everything very easily :D/>
Edited on 19 July 2015 - 04:08 PM
Bomb Bloke #24
Posted 20 July 2015 - 02:39 AM
Your idea just didn't work out as it was and I had to scrap it. It didn't feel "random" enough, it made some holes, you had to check afterwards if there were any useless parallel roads…

My idea, and what you actually coded, were two different things. I may not've been clear in expressing it.

Your current system should work quite well once you get all the roads to connect together. :)/>
MCplayer #25
Posted 17 August 2016 - 03:22 PM
It does not work it says citybuilder:9: attempt to index ? (a nil value)

pastebin get dj7hsjKU citybuilder works but it does not want to work
Bomb Bloke #26
Posted 18 August 2016 - 03:25 AM
… and what command did you use to start it?