I'm currently working on a Survival type game that I've wanted to build for years and years now; ever since Lost upset me by not focussing on the intricacies of forming a new civilization.
Anyway, the entire map is completely randomly generated and so far I'm pretty happy with it. However, I'm not sure how to go about detecting land-locked water. Natural 'ponds' form very often, and I want to be able to detect them and change their attributes from Sea-like to fresh water.
Could anyone help suggest some cheap algorithms for detecting any water not connected to the largest body/sea? I've had some ideas but they all seem very expensive computationally.
Thank you for helping to get the ball rolling. Firstly, I've found that "range()" works as a great replacement for view()/oview().
That's definitely a viable option, but I have a couple niggles with it. By basing it on oview() I'm going to lose a lot of the interesting coast line. Also, by looping through -all- water it's going to take a while and do a lot of unnecessary processing: I didn't explain before but the player view is huge (50x50) and from each coast there's water as far as the eye can see (by design). That said, I'm still not sure how I'd do it differently. |
Marching squares will help you find the perimeter of your landmass, while allowing you to begin processing the interiors only.
http://devblog.phillipspiess.com/2010/02/23/ better-know-an-algorithm-1-marching-squares/ Albeit, it might be easier to do a four-direction floodfill. |
In response to Ease
|
|
Sorry man I totally messed that up. I think I pulled 3 or less out of my ass. What I 'meant..' to say was 2 or less and only from tiles which are at NORTH SOUTH EAST WEST.
If you make the water into dirt when you encounter 2 or less water in NORTH SOUTH EAST WEST. Then I believe you effectively solve these cases: (And these cases will fill in most ponds and general gappyness without inflating your coasts tooo much.) |
Just a minor pet peeve, but could you not stretch the art? It makes it hard to determine what I'm looking at.
|
Got it working like a champ. The finding the boundary bit, at least. Now I've got a list of every turf the top right square of the 2x2 grid touches, and if needed I can add each turf every square in the grid touches, but I'm not exactly sure how to use this list to get a list of all the turfs within the boundary, if you know what I mean? I'm finding it hard to explain. I've got some rudimentary (i.e., stupid and clumsy) ideas for how to do it, but suggestions from greater minds would be appreciated ^_^
|
I'm not exactly an expert at this, but as far as finding the internal tiles of the boundaries, wouldn't a breadth-first search be ideal?
Store all of the boundary tiles in a list, then choose a tile inside the boundaries - start from that tile and expand outwards until you can't expand anymore (Stop when you hit a boundary tile). |
In response to Albro1
|
|
Albro1 wrote:
I'm not exactly an expert at this, but as far as finding the internal tiles of the boundaries, wouldn't a breadth-first search be ideal? Indeed, once you have found the boundary, you then have to do something of a reverse floodfill FROM the boundary to locate the ocean tiles. To make it more efficient, you could store the maxx,maxy, coordinates of the border, that way you are only performing the floodfill out to the maximum boundary of the land. Then you can get your square within a block(minx,miny,z,maxx,maxy,z), subtract all ocean tiles that you floodfilled, and you just found yourself a list of all interior tiles. To make this more efficient, you may wish to store the land you have generated in a list already, and use binary list operators to speed up the list manipulation. Just subtract all land and external ocean after the floodfill, and you have a list of all interior water. Run through the list and replace with fresh water. |
I decided to take a whack at that marching squares algorithm. It's not as easy as I thought it was going to be. This is what I have so far: (Note I haven't applied my cleanup procedure to the small pools in the island)
Here's my rather messy code for this: proc I just call get_perimeter(z) once everything is generated. One of my main problems is when the generation makes the land go to the edge of the map, the marching squares bug up. |
Either you need to account for the edge of your map, or set your cellular automata to weight probability of spreading to zero as it approaches the map edge.
|
Got it a little better now. I also applied the cleanup to this because the single pools were causing issues.
|
Haha, your code is lot prettier than mine, Albro! My method is finally complete, and working in full with only one small bug. That said, my floodfill method seems really inelegant to me, and I suspect it's 10-100 fold more computationally intensive than it needs to be.
My bug is in finding the start point of the boundary. With tiny little islands surrounding my main island I can't do the "from x=1 y=1 inwards" approach, as I could easily hit one of these islands, so at the moment I start from the centre of the island (which is always 100,100,1 in my program) and go North until I hit a turf of Sea that has more than 4 other Sea turfs within its oview(2). This isn't perfect, but it's working 19 times out of 20 for me. I'm thinking of making it continue North until it hits a turf of Sea which has every turf in its oview(2) as Sea as well, and once that is found march back South until the coast is found. Here's my current code: mob As you can see there's a lot of room for improvement. |
If you use my method of finding the start point(going from x=1 to world.maxx, then y+1), you should be able to not only find the main island but also all of the smaller ones. Just change the starting y to the y value of the topmost turfs (you can do this by storing all of your perimeter turfs in a list and looping to find the one with the highest y value) and setting the x value to the rightmost turf (same concept, highest x value) and starting the search again. This way you won't hit the same island again.
Sorry for not providing any sort of code examples but I'm on my phone. |
I believe it has a complexity of r * w except when you decide what you want r to be, which is around 3ish then you get 3 * w which is a complexity of O(n) which is reasonably low.
# Notes #
Turf's don't have an oview() function. You have to make one, I use for(get_step(listOfDirections[NORTH,SOUTH,EAST,WEST])).
Accuracy is there because sometimes by deleting one pool you create another. Therefore you can get closer and closer to taking out all the pools by running the main loop more times.