Your terran is randomly generated, you may use a "transition" map like this:
transitions = {Tiles.land: [Tiles.sand] +
[Tiles.tree] * 2 +
[Tiles.land] * 5,
Tiles.sand: [Tiles.water, Tiles.sand],
Tiles.tree: [Tiles.tree, Tiles.land],
Tiles.water: [Tiles.water] * 10
}
It read "When you're near a land, you have a chance to peek sand, two to pick tree, and 5 to pick another land".
Then you may want to code something like this to pick a tile according to the neighbors:
def pick_tile_for(world, x, y):
surrounding = []
if x > 0:
surrounding.append(world[x - 1][y])
if y < len(world) - 1:
surrounding.append(world[x][y + 1])
if y > 0:
surrounding.append(world[x][y - 1])
if x < len(world) - 1:
surrounding.append(world[x + 1][y])
surrounding = [tile for tile in surrounding if tile is not None]
if len(surrounding) == 0:
return None
excluded = set(surrounding[0])
for tile in surrounding:
excluded = excluded - set(transitions[tile])
next = list(chain(*[[t for t in transitions[tile] if t not in excluded]
for tile in surrounding]))
return choice(next)
Finally you may want to use this "not so random" tile picker:
def generate(world_size):
system('cls' if name == 'nt' else 'clear')
world = []
for x in range(world_size):
world.append([None for _ in range(world_size)])
world[choice(range(world_size))][choice(range(world_size))] = Tiles.land
for x in range(world_size):
for y in range(world_size):
if world[x][y] is None:
world[x][y] = pick_tile_for(world, x, y)
for x in range(world_size - 1, -1, -1):
for y in range(world_size - 1, -1, -1):
if world[x][y] is None:
world[x][y] = pick_tile_for(world, x, y)
for line in world:
print ''.join(line)
Note that I'm iterating the world twice, one in reverse, because my "first land" is randomly placed, I do not start from a corner, the world is built around the first randomly placed land.
I think there may be a lot of better ways to do this, but it was fun to do. You also may try to read about "flood fill".
Ceveat: I'll never build rivers with this kind of algorithm.