Removing Islands
I've done this sort of thing before in one of my games. To get rid of outer islands, the process was basically:
- First there must be a guarantee the center of the map will always belong to the main land, and each pixel starts out either as "Land" or "Water" (i.e. different colors).
- Then do a four direction flood fill starting from the center the map and spreading throughout any "Land" tiles. Mark every pixel visited by this flood fill as a different type such as "MainLand".
- Finally go over the entire map and convert any remaining "Land" pixel to "Water to get rid of other islands.
Removing Lakes
As for getting rid of the holes (or lakes) inside the island, you do a similar process but starting from the corners of the map and spreading through "Water" tiles instead. This will allow you to distinguish the "Sea" from the other water tiles, and then you can get rid of them just like you got rid of the islands before.
Example
Let me dig up my implementation of the flood fill that I have here somewhere (disclaimer, I didn't care about efficiency, so I'm sure there are many more efficient ways to implement it):
private void GenerateSea()
{
// Initialize visited tiles list
visited.Clear();
// Start generating sea from the four corners
GenerateSeaRecursive(new Point(0, 0));
GenerateSeaRecursive(new Point(size.Width - 1, 0));
GenerateSeaRecursive(new Point(0, size.Height - 1));
GenerateSeaRecursive(new Point(size.Width - 1, size.Height - 1));
}
private void GenerateSeaRecursive(Point point)
{
// End recursion if point is outside bounds
if (!WithinBounds(point)) return;
// End recursion if the current spot is a land
if (tiles[point.X, point.Y].Land) return;
// End recursion if this spot has already been visited
if (visited.Contains(point)) return;
// Add point to visited points list
visited.Add(point);
// Calculate neighboring tiles coordinates
Point right = new Point(point.X + 1, point.Y);
Point left = new Point(point.X - 1, point.Y);
Point up = new Point(point.X, point.Y - 1);
Point down = new Point(point.X, point.Y + 1);
// Mark neighbouring tiles as Sea if they're not Land
if (WithinBounds(right) && tiles[right.X, right.Y].Empty)
tiles[right.X, right.Y].Sea = true;
if (WithinBounds(left) && tiles[left.X, left.Y].Empty)
tiles[left.X, left.Y].Sea = true;
if (WithinBounds(up) && tiles[up.X, up.Y].Empty)
tiles[up.X, up.Y].Sea = true;
if (WithinBounds(down) && tiles[down.X, down.Y].Empty)
tiles[down.X, down.Y].Sea = true;
// Call the function recursively for the neighboring tiles
GenerateSeaRecursive(right);
GenerateSeaRecursive(left);
GenerateSeaRecursive(up);
GenerateSeaRecursive(down);
}
I used this as a first step to get rid of lakes in my game. After calling that, all I'd have to do was something like:
private void RemoveLakes()
{
// Now that sea is generated, any empty tile should be removed
for (int j = 0; j != size.Height; j++)
for (int i = 0; i != size.Width; i++)
if (tiles[i, j].Empty) tiles[i, j].Land = true;
}
Edit
Adding some extra information based on the comments. In case your search space is too big, you might experience a stack overflow when using the recursive version of the algorithm. Here's a link on stackoverflow (pun intended :-)) to a non recursive version of the algorithm, using a Stack<T>
instead (also in C# to match my answer, but should be easy to adapt to other languages, and there are other implementations on that link too).