Seemed like a pretty swanky tutorial so I decided to give it a go in Pygame :D Took me about an hour with getting the algorithm small. I also started with using 8 corner neighbors + self when determining whether to become a land or island, but quickly learned that 4 corners (up, down, left, right) + self is a much better alternative.
If you want to play around with the source, try modifying LAND_COUNT (1-15), LAND_RATIO, and SEA_RATIO. LAND_COUNT is the initial how many land blocks will we generate off of, and it randomly chooses to be a sea or land based on neighbors.

And of course full source

import pygame, random
from pygame.locals import *
SCREEN_WIDTH = 512 # For now this must be 512, 1024, 1536, or 2048
SCREEN_HEIGHT = 512 # For now this must be 512, 1024, 1536, or 2048
LAND_COUNT = 5 # how many initial islands can we spawn off of?
LAND_RATIO = 9 # 8/5 chance of having land vs. sea
SEA_RATIO = 5 # we can change this to any ratio
LAND1_RGBA = (132,176,138,255)
LAND2_RGBA = (119,168,126,255)
SEA1_RGBA = (75,162,255,255)
SEA2_RGBA = (62,142,255,255)
BORDER_RGBA = (80,124,87,255)
class PLand:
def __init__(self, landLayer):
self.landLayer = landLayer
self.largeGrid = []
self.mediumGrid = []
self.smallGrid = []
self.tinyGrid = []
for i in xrange(16): # 4*4
self.largeGrid.append(0)
for i in xrange(256): # 16*16
self.mediumGrid.append(0)
for i in xrange(4096): # 64*64
self.smallGrid.append(0)
for i in xrange(65536): # 256*256
self.tinyGrid.append(0)
self.state = 'largeGrid' # always start at large grid for this implementation
self.tileIdx = 0 # which tile idx are we at?
self.neighbors = [(0,-1),(-1,0),(0,0),(1,0),(0,1)] # check 4 neighbors (up,down,left,right) + self
def update(self):
while self.state != 'done':
if self.state == 'largeGrid':
print 'Creating Large Grid'
randGrid = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]
width = SCREEN_WIDTH/4
height = SCREEN_HEIGHT/4
for i in xrange(LAND_COUNT): # fill in with defined land count
r = random.randint(0, len(randGrid)-1)
idx = randGrid.pop(r)
self.largeGrid[idx] = 1
for i in xrange(len(randGrid)): # fill the other half with water
idx = randGrid.pop(0)
self.largeGrid[idx] = 2
print 'Moving to Medium Grid'
self.state = 'mediumGrid'
elif self.state == 'mediumGrid':
if self.updateGrid(256, 16, 4, self.mediumGrid, self.largeGrid) == True:
print 'Moving to Small Grid'
self.state = 'smallGrid'
elif self.state == 'smallGrid':
if self.updateGrid(4096, 64, 16, self.smallGrid, self.mediumGrid) == True:
print 'Moving to Tiny Grid'
self.state = 'tinyGrid'
elif self.state == 'tinyGrid':
if self.updateGrid(65536, 256, 64, self.tinyGrid, self.smallGrid) == True:
print 'Drawing Grid'
self.state = 'drawGrid'
elif self.state == 'drawGrid':
width = SCREEN_WIDTH/256
height = SCREEN_HEIGHT/256
for idx in xrange(65536):
x = idx % 256
y = idx / 256
if self.tinyGrid[idx] == 1: # Is it land?
isBorder = False
for nx,ny in self.neighbors:
nx += int(x) # our current difference in grids (64/16 = 4, 16/4 = 4)
ny += int(y)
if nx >= 0 and ny >= 0 and nx < 256 and ny < 256:
if self.tinyGrid[ny*256+nx] == 2: # draw this as darker and break
pygame.draw.rect(self.landLayer, BORDER_RGBA, Rect(x*width,y*height,width,height))
isBorder = True
break
if isBorder == False:
if ( random.randint(0,1) == 1 ): #50% chance of coloring different
pygame.draw.rect(self.landLayer, LAND1_RGBA, Rect(x*width,y*height,width,height))
else:
pygame.draw.rect(self.landLayer, LAND2_RGBA, Rect(x*width,y*height,width,height))
else: # is it sea?
if ( random.randint(0,1) == 1 ): #50% chance of coloring different
pygame.draw.rect(self.landLayer, SEA1_RGBA, Rect(x*width,y*height,width,height))
else:
pygame.draw.rect(self.landLayer, SEA2_RGBA, Rect(x*width,y*height,width,height))
print 'Done!'
self.state = 'done'
def updateGrid(self, maxSize, tileSize, oldSize, newGrid, oldGrid):
if self.tileIdx < maxSize:
x = self.tileIdx % tileSize
y = self.tileIdx / tileSize
neighborList = [] # Check our neighbors
for nx,ny in self.neighbors:
nx += int(x/4) # our current difference in grids (256/4 = 64, 64/4 = 16, 16/4 = 4)
ny += int(y/4)
if nx >= 0 and ny >= 0 and nx < oldSize and ny < oldSize:
neighborList.append(oldGrid[ny*oldSize+nx])
waterChance = 0
landChance = 0
for neighbor in neighborList:
if neighbor == 1: # land!
landChance += LAND_RATIO
elif neighbor == 2: # water!
waterChance += SEA_RATIO
r = random.randint(0, landChance+waterChance-1)
width = SCREEN_WIDTH/tileSize
height = SCREEN_HEIGHT/tileSize
if r < landChance:
newGrid[self.tileIdx] = 1 # land
else:
newGrid[self.tileIdx] = 2 # water
self.tileIdx += 1
return False
else:
self.tileIdx = 0
return True
def draw(self,screen):
screen.blit(self.landLayer, (0,0))
def main():
"""Procedural Generation - Luke Arntson '09
"""
pygame.init()
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption('Land Generation')
pygame.mouse.set_visible(0)
background = pygame.Surface(screen.get_size())
background = background.convert()
background.fill((0, 0, 0))
landLayer = pygame.Surface(screen.get_size())
landLayer = landLayer.convert_alpha()
landLayer.fill((0, 0, 0, 0))
mLand = PLand(landLayer)
screen.blit(background, (0, 0))
pygame.display.flip()
print '-- Creating Map(%i,%i) with %i Land Initializer(s), and a Water-to-Sea average of %i/%i --\n'%(SCREEN_WIDTH,SCREEN_HEIGHT,LAND_COUNT,LAND_RATIO,SEA_RATIO)
clock = pygame.time.Clock()
while 1:
clock.tick(60)
for event in pygame.event.get():
if event.type == QUIT:
return
elif event.type == KEYDOWN and event.key == K_ESCAPE:
return
mLand.update()
screen.blit(background, (0, 0))
mLand.draw(screen)
pygame.display.flip()
if __name__ == '__main__': main()