Skip to content

Instantly share code, notes, and snippets.

@skairunner
Last active August 29, 2015 13:59
Show Gist options
  • Save skairunner/10523846 to your computer and use it in GitHub Desktop.
Save skairunner/10523846 to your computer and use it in GitHub Desktop.
gen.py and mcworldgen
import math
from enum import Enum
class Block(Enum):
upperShell = 1
lowerShell = 2
upperInterior = 3
lowerInterior = 4
upperInnerShell = 5
lowerInnerShell = 6
#Von Neumann in 3D
neighbors = [ ( 1, 0, 0), (-1, 0, 0), (0 , 1, 0), ( 0,-1, 0), ( 0, 0, 1), ( 0, 0,-1) ]
def add(t1, t2):
if len(t1) != len(t2):
raise ArithmeticError("Length of tuples do not match")
result = [a + b for a, b in zip(t1, t2)]
return result
def sphereDistanceSquared(x, y, z, c):
return (x-c)**2 + (y-c)**2 + (z-c)**2
class BlockCircle:
def __init__(self, r, depth = 1):
self.r = r
self.side = r + 1 # The actual cube space is 2 larger on every side
self.depth = depth
self.blueprint = list()
self.drawBlueprint()
def getBlock(self, coord):
if (0 <= coord[0] < 2 * self.side+1 and 0 <= coord[1] < 2 * self.side+1 and 0 <= coord[2] < 2 * self.side+1):
return self.blueprint[coord[0]][coord[1]][coord[2]]
else:
raise KeyError("Coords (%d,%d,%d) out of bounds" % (coord[0],coord[1],coord[2]))
def setBlock(self, coord, value):
self.blueprint[coord[0]][coord[1]][coord[2]] = value
def drawBlueprint(self):
print "Starting sphere " + str(self.side)
# Dimensions of the cube circumscribing the sphere is (2s+1)*(2s+1)*(2s+1)
self.blueprint = [[[0 for x in xrange(2 * self.side + 1)] for y in xrange(2 * self.side + 1)] for z in xrange(2 * self.side + 1)]
# If the block selected is not in the sphere (whose center is (r+2,r+2,r+2)), set it as 0. Else, set it as 1.
for z in xrange(2 * self.side + 1):
for y in xrange(2 * self.side + 1):
for x in xrange(2 * self.side + 1):
if sphereDistanceSquared(x,y,z,self.side + 1) <= self.r**2 :
if z > self.side:
self.blueprint[z][y][x] = Block.upperInterior
else:
self.blueprint[z][y][x] = Block.lowerInterior
print "\tFloodfilling..."
# Next, set all boundary blocks as 2.
for z in xrange(2 * self.side + 1):
for y in xrange(2 * self.side + 1):
for x in xrange(2 * self.side + 1):
# For starters, the top layer and the bottom layer is always a boundary
# Same for x or y boundaries
if (z == 0 or z == 2 * self.side or y == 0 or y == 2 * self.side or x == 0 or x == 2 * self.side) and self.blueprint[z][y][x] == 1: # The selected space must also be part of the sphere
if z > self.side:
self.blueprint[z][y][x] = Block.upperShell
else:
self.blueprint[z][y][x] = Block.lowerShell
continue
if self.blueprint[z][y][x] == 0: # If an Air tile can reach a stone tile, it is a boundary.
current = (z, y, x)
for deltaCoord in neighbors:
neighbor = add(current, deltaCoord)
try:
if self.getBlock(neighbor) == Block.upperInterior or self.getBlock(neighbor) == Block.lowerInterior:
if neighbor[0] > self.side:
self.setBlock(neighbor, Block.upperShell)
else:
self.setBlock(neighbor, Block.lowerShell)
except KeyError:
"" # Do nothing, just move on to the next item in the work queue
except:
raise
# Flood fill for inner boundaries
for z in xrange(2 * self.side + 1):
for y in xrange(2 * self.side + 1):
for x in xrange(2 * self.side + 1):
if self.blueprint[z][y][x] == Block.upperShell or self.blueprint[z][y][x] == Block.lowerShell: # If a Boundary tile can reach a stone tile, it is an inner boundary. (3)
current = (z, y, x)
for deltaCoord in neighbors:
neighbor = add(current, deltaCoord)
try:
if self.getBlock(neighbor) == Block.upperInterior or self.getBlock(neighbor) == Block.lowerInterior: #
if neighbor[0] > self.side:
self.setBlock(neighbor, Block.upperInnerShell)
else:
self.setBlock(neighbor, Block.lowerInnerShell)
except KeyError:
"" # Do nothing, just move on to the next item in the work queue
except:
raise
from planetoid import Planetoid
import pickle
import json
import random
import sys
noOutput = False
noPrompts = False
if "-s" in sys.argv:
noOutput = True
if "-n" in sys.argv:
noPrompts = True
blueprints = dict()
planetoids = list()
def exportLeveldat():
level = {}
level["dataType"] = "level"
level["allowCommands"] = "true"
level["hardcore"] = "false"
level["MapFeatures"] = "false"
level["GameType"] = 1
level["SpawnX"] = 0
level["SpawnY"] = 6
level["SpawnZ"] = 0
level["rainAllowed"] = "true"
level["thunderAllowed"] = "true"
level["RandomSeed"] = "12345"
level["generatorName"] = "default"
level["generatorOptions"] = ""
level["LevelName"] = "Test"
gamerules = {}
gamerules["commandBlockOutput"] = "true"
gamerules["doDaylightCycle"] = "true"
gamerules["doFireTick"] = "true"
gamerules["doMobLoot"] = "true"
gamerules["doMobSpawning"] = "true"
gamerules["doTileDrops"] = "true"
gamerules["keepInventory"] = "false"
gamerules["mobGriefing"] = "true"
gamerules["naturalRegeneration"] ="true"
level["GameRules"] = gamerules
if not noOutput:
sys.stdout.write(json.dumps(level))
def exportBedrock(planets, mapwidth, bedrock=True, water=True):
minx, miny, maxx, maxy = mapwidth, mapwidth, -1, -1
# figure out the boundaries
for p in planets:
if p.center[1]-p.r-1 < miny:
miny = p.center[1]-p.r-1
if p.center[1]+p.r+1 > maxy:
maxy = p.center[1]+p.r+1
if p.center[2]-p.r-1 < minx:
minx = p.center[2]-p.r-1
if p.center[2]+p.r+1 > maxx:
maxx = p.center[2]+p.r+1
blocksData = {}
blocksData["dataType"] = "blocks"
blocksData["dimension"] = 0
blocksData["xOffset"] = 0
blocksData["yOffset"] = 0
blocksData["zOffset"] = 0
blocksData["biomes"] = []
height = 0
if not bedrock and not water:
return
if bedrock and not water:
height = 1
if not bedrock and water:
return
if bedrock and water:
height = 3
blocks = [[[0 for x in xrange(maxx-minx)] for y in xrange(maxy-miny)] for z in xrange(height)]
for y in xrange(maxy - miny):
for x in xrange(maxx - minx):
#if bedrock: # by this point it's assured that bedrock is true
blocks[0][y][x] = 7 # bedrock!
if water:
blocks[1][y][x] = 8
blocks[2][y][x] = 8
blocksData["blocks"] = blocks
sys.stdout.write(json.dumps(blocksData))
def exportCube(coord, width, planetoid):
blocksData = dict()
blocksData["dataType"] = "blocks"
blocksData["dimension"] = 0
blocksData["biomes"] = []
blocksData["xOffset"] = coord[2]
blocksData["yOffset"] = coord[1]
blocksData["zOffset"] = coord[0]
blocksData["blocks"] = [[[-1 for x in xrange(width)] for y in xrange(width)] for z in xrange(width)]
blocksData["decorate"] = True
planetoid.rasterize(blocksData["blocks"], blueprints, True)
if not noOutput:
sys.stdout.write(json.dumps(blocksData)) # No whitespace
numSpheres = 10
mapwidth = 256
# Spawn the planetoids
for x in xrange(numSpheres):
r = random.randint(10, 20)
x = random.randint(r, mapwidth - r - 2)
y = random.randint(r, mapwidth - r - 2)
z = random.randint(r, 256 - r - 1)
planetoid = Planetoid(r, (z, y, x))
chance = random.randint(0,1)
if chance==0:
planetoid.shell = "test"
planetoid.innershell = "test"
planetoid.contents = "ore"
elif chance==1:
planetoid.shell = "leaves"
planetoid.innershell = "leaves"
planetoid.contents = "wood"
planetoids.append(planetoid)
### New JSON commands method of output
for p in planetoids:
exportCube(p.center, 2*(p.r+1)+1, p)
exportBedrock(planetoids, mapwidth)
sys.stdout.write(json.dumps({"dataType":"exportAllRegions"}))
#sys.stdout.write(json.dumps({"dataType":"thanks"}))
"""# Place the actual blocks
blockmap = Map(mapwidth)
for planetoid in planetoids:
planetoid.rasterize(blockmap, blueprints)"""
"""# Output into json
output = dict()
dimension = dict()
dimension["biomes"] = []
dimension["decorate"] = False
dimension["blocks"] = blockmap.map
output["dimensions"] = dict()
output["dimensions"]["0"] = dimension
output["level"] = level
if noOutput:
if not noPrompts:
print 'Done'
else:
print json.dumps(output)"""
from random import uniform, randint
from enum import Enum
import pickle
from blockcircle import BlockCircle
### Load resource files
equalChanceDict = dict() # name: id
uneqChanceDict = dict() # name: (id, prob)
lotterySums = dict()
eqClasses = ["wood", "leaves", "stone", "earth"]
uneqClasses = ["ores", "liquid"]
# Get a random block of blocktype from uneqChanceDict
def getUnequalChanceBlock(blocktype):
num = uniform(0, lotterySums[blocktype])
result = None
counter = 0
keys = uneqChanceDict["ores"].keys()
while num > 0:
result = uneqChanceDict["ores"][keys[counter]]
num -= result[1]
counter += 1
return result[0]
# Same, but for equal chance blocks like wood type
def getEqualChanceBlock(blocktype):
lim = len(lotterySums[blocktype])
num = randint(0, lim-1)
return equalChanceDict[blocktype][equalChanceDict[blocktype].keys()[num]]
def getBlock(blocktype):
if blocktype == "air":
return 0
if blocktype in eqClasses:
return getEqualChanceBlock(blocktype)
elif blocktype in uneqClasses:
return getUnequalChanceBlock(blocktype)
else:
raise TypeError("Could not find classification \"" + blocktype + "\"")
for c in eqClasses:
with open("resources/" + c, "r") as f:
equalChanceDict[c] = dict()
first = True
for line in f:
if first:
first = False
continue
line = line[:-1]
words = line.split("\t")
if len(words[1].split(":")) > 1: #ie, it is a id:damage pair
split = words[1].split(":")
id_damage = [int(split[0]), int(split[1])]
equalChanceDict[c][words[0]] = id_damage
else:
equalChanceDict[c][words[0]] = int(words[1])
# Ores, etc, are not equal probability.
for c in uneqClasses:
with open("resources/" + c, "r") as f:
uneqChanceDict[c] = dict()
lotterySums[c] = 0
first = True
for line in f:
if first:
first = False
continue
line = line[:-1]
words = line.split("\t")
uneqChanceDict[c][words[0]] = (int(words[1]), float(words[2]))
lotterySums[c] += float(words[2])
class SchemaKey(Enum):
schema= 0
upperShell = 1
lowerShell = 2
upperInterior = 3
lowerInterior = 4
upperInnerShell = 5
lowerInnerShell = 6
satellite = 7
huge = 8
description = 9
schemata = dict()
# load schemata
with open("resources/schemata", "r") as f:
for line in f:
line = line[:-1]
things = line.split("\t")
schemata[things[0]] = things
class Planetoid:
def __init__(self, r, coord, schema="none"):
self.r = r
self.schema = schema
self.center = coord
self.attributes = dict()
# Retrieve values from tables.
# If the upper and lower things are the same classification, they get the same block type
self.attributes["upperShell"] = getBlock(schemata[schema][SchemaKey.upperShell])
if schemata[schema][SchemaKey.lowerShell] == schemata[schema][SchemaKey.upperShell]:
self.attributes["lowerShell"] = self.attributes["upperShell"]
else:
self.attributes["lowerShell"] = getBlock(schemata[schema][SchemaKey.lowerShell])
self.attributes["upperInnerShell"] = getBlock(schemata[schema][SchemaKey.upperInnerShell])
if schemata[schema][SchemaKey.upperInnerShell] == schemata[schema][SchemaKey.lowerInnerShell]:
self.attributes["lowerInnerShell"] = self.attributes["upperInnerShell"]
else:
self.attributes["lowerInnerShell"] = getBlock(schemata[schema][SchemaKey.lowerInnerShell])
self.attributes["upperInterior"] = schemata[schema][SchemaKey.upperInterior]
if schemata[schema][SchemaKey.upperInterior] == schemata[schema][SchemaKey.lowerInterior]:
self.attributes["lowerInterior"] = self.attributes["upperInterior"]
else:
self.attributes["lowerInterior"] = getBlock(schemata[schema][SchemaKey.upperInterior])
self.attributes["satellite"] = schemata[schema][SchemaKey.satellite]
self.attributes["huge"] = schemata[schema][SchemaKey.huge]
self.oreprob = .5 # Hardcoded for now.
def getOreBlock(self, blockType):
num = uniform(0.0, 1.0)
if num < self.oreprob:
return self.attributes[SchemaKey(blockType)]
return 1 # stone
def getBlock(self, blockType):
if blockType == 0:
return 0
elif blockType == None:
raise TypeError("blockType is not defined.")
blockClass = schemata[self.schema][blockType]
if blockClass == "ores": # get a random block if ores
return getOreBlock(self)
else:
return self.attributes[SchemaKey(blockType)]
# if relative=True, don't translate the center of the cube
def rasterize(self, blockmap, blueprints, relative = False):
if not (self.r in blueprints):
with open("resources/sphere" + str(self.r) + ".form", "r") as f:
blueprints[self.r] = pickle.load(f)
circle = blueprints[self.r]
cz = self.center[0]
cy = self.center[1]
cx = self.center[2]
if relative:
for z in xrange(2*(self.r+1) +1):
for y in xrange(2*(self.r+1) +1):
for x in xrange (2*(self.r+1) +1):
blockType = circle.blueprint[z][y][x]
if not blockType == 0:
blockmap[z][y][x] = self.getBlock(blockType)
else:
for z in xrange(2*(self.r+1) +1):
for y in xrange(2*(self.r+1) +1):
for x in xrange (2*(self.r+1) +1):
blockType = circle.blueprint[z][y][x]
blockmap.map[cz + z - circle.side][cy + y - circle.side][cx + x - circle.side] = self.getBlock(blockType)
import math
from blockcircle import BlockCircle
import sys
import pickle
if len(sys.argv) < 3:
print "Usage:\npython spheregen.py [lower limit, >= 3] [upper limit]"
quit()
if sys.argv[1] < 3:
print "Lower limit must be >= 3"
lower = int(sys.argv[1])
upper = int(sys.argv[2])
for r in xrange(lower, upper+1):
circle = BlockCircle(r)
file = open("resources/sphere" + str(circle.r) + ".form", "w")
print "\tWriting..."
pickle.dump(circle, file)
file.close()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment