Created
May 9, 2013 21:00
-
-
Save pbsds/5550589 to your computer and use it in GitHub Desktop.
A simple random map generator using seeding and growth to produce it's landmasses.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Image, random, math | |
#Made by pbsds/Peder Bergebakken Sundt | |
#Requires PIL to work | |
Palette = (0x0045A0,#0 - Dark water | |
0x0059BC,#1 - Water | |
0x0078F9,#2 - Shore water | |
0xFFF65E,#3 - Sand | |
0x2BE81F,#4 - Grass | |
0x26B719,#5 - Dark Grass | |
0xA8712F,#6 - Mountain foot | |
0x7F3300,#7 - Mountain side | |
0xA8865C,#8 - Mountain top | |
0xCEA471,#9 - Mountain peak | |
0x006B0A,#10- Tree | |
0xFF0000)#11- City | |
def Random(i1, i2): | |
if i1 == i2: | |
return i1 | |
return int(random.randrange(i1,i2)) | |
def TryInt(i):#tries to convert to int, if possible | |
try: | |
return int(i) | |
except: | |
return i | |
def CircleOffsets(Radius): | |
ret = [] | |
for y in xrange(-Radius-1,Radius+1): | |
for x in xrange(-Radius-1,Radius+1): | |
if math.sqrt(x**2 + y**2) <= Radius: | |
ret.append((x, y)) | |
return ret | |
def DecAsc(dec,pad = -1):#Converts a decimal into an ascii string with the specified padding(Big Endian) | |
ret = "" | |
while dec <> 0: | |
ret = chr(dec&0xFF) + ret | |
dec >>= 8 | |
if pad <> -1: | |
if len(ret) > pad: | |
ret = ret[:pad] | |
if len(ret) < pad: | |
ret = "\0"*(pad-len(ret)) + ret | |
return ret | |
def SaveImage(Map, OutputPath = "out.png"): | |
global Palette | |
w = len(Map) | |
h = len(Map[0]) | |
data = [] | |
for y in range(h): | |
for x in range(w): | |
data.append(DecAsc(Palette[Map[x][y]],3)) | |
#data[x+y*w] = DecAsc(Map[x][y],3) | |
data = "".join(data) | |
img = Image.fromstring("RGB", (w, h), data) | |
filetype = OutputPath[OutputPath.rfind(".")+1:] | |
img.save(OutputPath, filetype) | |
print " - pbsds's map generator -" | |
print "The values inside the parentheses are my personal preferences during testing." | |
random.seed(TryInt(raw_input("Insert psuedo-random seed(1337): "))) | |
Chance = input("Insert land-spread chance percentage(99): ") | |
Seeds = input("Insert number of inital land-seeds(20-30): ") | |
Frame = input("Insert border padding size for thhe land-seeds(0): ") | |
MapWidth = input("Insert output image width(400-500): ") | |
MapHeight = input("Insert output image height(400-500): ") | |
print "Generating the land masses..." | |
Check = []#the pixels to work with | |
Map = [[None for y in xrange(MapHeight)] for x in xrange(MapWidth)]#None = Unset, or the indexes from Palette | |
WidthRange = range(MapWidth) | |
HeightRange = range(MapHeight) | |
#inital land-roots: | |
for i in xrange(Seeds): | |
x = Random(Frame, MapWidth-1-Frame) | |
y = Random(Frame, MapHeight-1-Frame) | |
Map[x][y] = 5#dark grass | |
Check.append((x,y)) | |
#Create the landmasses: | |
while Check: | |
x, y = Check.pop(Random(0,len(Check)-1)) | |
for x2, y2 in ((x-1, y-1), (x, y-1), (x+1, y-1), (x+1, y), (x+1, y+1), (x, y+1), (x-1, y+1), (x-1, y)):#For each neightbor: | |
if not (x2 < 0 or x2 >= MapWidth or y2 < 0 or y2 >= MapHeight):#If within the map: | |
if Map[x2][y2] == None:#If neighboring pixel isn't set yet: | |
#if current pixel is water, set neightboring pixel to water. | |
#if it's land, set the neightboring pixel to either land or water chosen randomly | |
if Map[x][y] == 0: | |
Map[x2][y2] = 0#water | |
else: | |
if Random(0,100) < Chance: | |
Map[x2][y2] = 5#grass | |
else: | |
Map[x2][y2] = 0#water | |
Check.append((x2,y2)) | |
print "Removing 1x1 sized lakes..." | |
for y in HeightRange: | |
for x in WidthRange: | |
if Map[x][y] == 0: | |
Remove = True | |
for x2, y2 in ((x+1,y), (x-1,y), (x,y+1), (x,y-1)): | |
if 0 <= x2 < MapWidth and 0 <= y2 < MapHeight: | |
if Map[x2][y2] == 0: | |
Remove = False | |
break | |
if Remove: | |
Map[x][y] = 5 | |
#Colour the map: | |
print "Colouring the map..." | |
Radius1 = CircleOffsets(4) | |
Radius2 = CircleOffsets(13) | |
for i in Radius1: | |
Radius2.remove(i) | |
Radius1.remove((0,0)) | |
for y in HeightRange: | |
for x in WidthRange: | |
if Map[x][y] in (4, 5): | |
#Check if by the water: | |
for x2, y2 in ((x+1,y), (x-1,y), (x,y+1), (x,y-1)): | |
if 0 <= x2 < MapWidth and 0 <= y2 < MapHeight: | |
if Map[x2][y2] in (2,1,0): | |
#Add the sand shore: | |
Map[x][y] = 3#sand | |
#Add Bright water along the shore: | |
for x2, y2 in Radius1: | |
if not (x+x2 < 0 or x+x2 >= MapWidth or y+y2 < 0 or y+y2 >= MapHeight): | |
if Map[x+x2][y+y2] in (0,1): | |
Map[x+x2][y+y2] = 2#Shore water | |
#add deeper water further out along the shore and lighter grass: | |
for x2, y2 in Radius2: | |
if not (x+x2 < 0 or x+x2 >= MapWidth or y+y2 < 0 or y+y2 >= MapHeight): | |
if Map[x+x2][y+y2] == 0: | |
Map[x+x2][y+y2] = 1#normal water | |
elif Map[x+x2][y+y2] == 5: | |
Map[x+x2][y+y2] = 4#light grass | |
#Finishing the work: | |
print "Saving as png...", | |
SaveImage(Map) | |
print "Done!" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is my take on http://www.cartania.com/alexander/generation.html