--HG--
branch : stargen
This commit is contained in:
cecilkorik 2010-11-27 22:46:01 +00:00
parent 3066899c07
commit f0c3c10b6d
2 changed files with 361 additions and 150 deletions

19
rnd.py
View file

@ -4,7 +4,7 @@ import math
def random():
return random_lib.random()
def gaussian(mean=0.0, stddev=1.0):
def gaussian(mean=0.0, stddev=1.0, skew=0.0):
while True:
x1 = (2.0 * random()) - 1.0
x2 = (2.0 * random()) - 1.0
@ -14,8 +14,25 @@ def gaussian(mean=0.0, stddev=1.0):
w = math.sqrt((-2.0 * math.log(w)) / w)
if skew == 0.0:
return mean + (x1 * w * stddev)
else:
sigma = skew / math.sqrt(1 + skew**2)
v1 = x1 * w
v2 = x2 * w
m = 1.0
if v1 < 0.0:
m = -1.0
vr = sigma * v1 + math.sqrt(1.0 - sigma**2) * v2
return (vr * m * stddev) + mean
def randsign():
if random() >= 0.5:
return 1.0
else:
return -1.0
def randrange(min, max):

View file

@ -1,10 +1,103 @@
from rnd import *
import math
import os, sys
def color(str, col, bright=0, reset=1):
ansi = {'R': '31', 'X': '30', 'G': '32', 'Y': '33', 'B': '34', 'M': '35', 'C': '36', 'W': '37'}
b = '1' if bright else '0'
a = '\x1b[%s;%sm' % (b, ansi[col.upper()])
r = ''
if reset:
r = '\x1b[0;37m'
return '%s%s%s' % (a, str, r)
class Star(object):
def __init__(self, lumin, mass, spect, radius, x, y, z):
self.lumin, self.mass, self.spect, self.radius, self.x, self.y, self.z = \
lumin, mass, spect, radius, x, y, z
def dist(self):
return dist(self.x, self.y, self.z) / 149589000.0 # return value in Astronomical Units
def dists(self):
return [x / 149589000.0 for x in (self.x, self.y, self.z)] # return value in Astronomical Units
def info(self):
return "%s-class star at %.3f (%.3f, %.3f, %.3f), mass %.3f, luminosity %.3f, radius %.3f" % \
tuple([self.spect, self.dist()] + self.dists() + [self.mass, self.lumin, self.radius])
class Planet(object):
def __init__(self, x, y, z, mass, radius, surfarea,
areainsol, gas_giant, density, atmo,
albedo, min_temp, max_temp, avg_temp):
self.x, self.y, self.z, self.mass, self.radius, self.surfarea = x, y, z, mass, radius, surfarea
self.areainsol, self.gas_giant, self.density, self.atmo = areainsol, gas_giant, density, atmo
self.albedo, self.min_temp, self.max_temp, self.avg_temp = albedo, min_temp, max_temp, avg_temp
def dist(self):
return dist(self.x, self.y, self.z) / 149589000.0 # return value in Astronomical Units
def dists(self):
return [x / 149589000.0 for x in (self.x, self.y, self.z)] # return value in Astronomical Units
def info(self):
return "%s%s planet at %.3f (%.3f, %.3f, %.3f) mass %.3e kg, radius %.1f km, density %.3f, atmo %.3e, avg temp %.1f range (%.1f : %.1f), albedo %s" % \
tuple([self.habitability(), self.type(), self.dist()] + self.dists() + [self.mass, self.radius, self.density, self.atmo,
self.avg_temp, self.min_temp, self.max_temp, self.albedo])
def type(self):
if self.gas_giant:
return "gas-giant"
elif self.avg_temp < 60.0:
return "ice"
elif self.max_temp >= 700.0:
return "lava"
else:
return "rocky"
def habitability(self):
if not self.habitable():
return color("Uninhabitable ", 'x', 1)
if self.max_temp < 273.0:
return color("FREEZING ", 'c', 1)
elif self.max_temp < 290.0 and self.avg_temp > 273.0:
return color("COOL ", 'g', 0)
elif self.min_temp > 290.0 and self.avg_temp > 325.0:
return color("SCORCHING ", 'y', 1)
elif self.min_temp > 290.0:
return color("WARM ", 'g', 0)
elif self.avg_temp < 273.0:
return color("COLD ", 'b', 1)
elif self.avg_temp > 300.0:
return color("HOT ", 'r', 1)
elif (self.max_temp - self.min_temp) > 150.0:
return color("EXTREME ", 'm', 1)
elif (self.max_temp - self.min_temp) > 60.0:
return color("EARTHLIKE ", 'g', 0)
elif (self.max_temp - self.min_temp) > 30.0:
return color("COMFORTABLE ", 'g', 1)
else:
return color("GAIA ", 'g', 1)
def habitable(self):
if not self.gas_giant:
if self.atmo > 1e-6 and (245.0 < self.min_temp < 325.0 or 245.0 < self.max_temp < 325.0):
return True
return False
systemsize = 6e9
stars = (1.0, 0.5)
planets = (8.0, 4.0)
class Asteroid_Belt(Planet):
def __init__(self, radius, mass):
self.radius, self.mass = radius, mass
self.num_smashed = 2
def dist(self):
return self.radius
def info(self):
return "-- Asteroid belt at %.3f, mass %.3e --" % (self.radius, self.mass)
def habitable(self):
return False
systemsize = 1e8
stars_randcount = (1.0, 0.5)
planets_randcount = (8.0, 4.0)
star_spectra = ['O', 'B', 'A', 'F', 'G', 'K', 'M']
star_lumin = [(500000.0, 30000.0), (30000.0, 25.0), (25.0, 5.0), (5.0, 1.5), (1.5, 0.6), (0.6, 0.008), (0.08, 0.0008)]
@ -12,20 +105,28 @@ star_masses = [(40.0, 16.0), (16.0, 2.1), (2.1, 1.4), (1.4, 1.04), (1.04, 0.8),
star_radii = [(22.0, 8.0), (7.0, 1.8), (1.8, 1.4), (1.4, 1.15), (1.15, 0.96), (0.96, 0.7), (0.7, 0.25)]
star_prob = [0.0, 0.05, 0.35, 0.6, 0.8, 0.97, 1.0]
num_stars = round(gaussian(*stars))
if num_stars < 1.0:
def dist(x, y, z):
return math.sqrt(x**2 + y**2 + z**2)
def gen_system(force_habitable=False):
single = True
system_habitable = False
while single or (force_habitable and not system_habitable):
single = False
num_stars = round(gaussian(*stars_randcount))
if num_stars < 1.0:
num_stars = 1.0
num_stars = int(num_stars)
num_stars = int(num_stars)
num_planets = round(gaussian(*planets))
if num_planets < 0.0:
num_planets = round(gaussian(*planets_randcount))
if num_planets < 0.0:
num_planets = 0.0
num_planets = int(num_planets)
num_planets = int(num_planets)
stars = []
x = y = z = 0.0
stars = []
x = y = z = 0.0
while len(stars) < num_stars:
while len(stars) < num_stars:
r = random()
for i, e in enumerate(star_prob):
if r <= e:
@ -37,9 +138,8 @@ while len(stars) < num_stars:
smass = (star_masses[si][1] * 0.8) + (star_masses[si][0] * 1.2 * r)
r2 = r + (random() * 0.3) - 0.15
sradius = (star_radii[si][1] * 0.8) + (star_radii[si][0] * 1.2 * r)
print "Star lumin %s mass %s class %s" % (slumin, smass, star_spectra[si])
stars += [(slumin, smass, star_spectra[si], sradius, x, y, z)]
stars += [Star(slumin, smass, star_spectra[si], sradius, x, y, z)]
# first star is always at 0.0, 0.0, 0.0, next star gets random coords
x = gaussian(0.0, systemsize)
@ -47,25 +147,30 @@ while len(stars) < num_stars:
if len(stars) > 1:
z = gaussian(0.0, systemsize / 10e3)
4
planets = []
sys_size = math.sqrt(sys_lumin)
while len(planets) < num_planets:
def dist(x, y, z):
return math.sqrt(x**2 + y**2 + z**2)
planets = []
sys_lumin = sum([x.lumin for x in stars])
sys_size = math.sqrt(sys_lumin / 2.0)
x = gaussian(0.0, systemsize * sys_size)
y = gaussian(0.0, systemsize * sys_size)
z = gaussian(0.0, systemsize * sys_size / 10e3)
while len(planets) < num_planets:
if random() >= 0.5:
loc = 2e7
loczone = 20.0
else:
loc = 2e7
loczone = 1.0
x = gaussian(loc * randsign(), systemsize * sys_size * loczone)
y = gaussian(loc * randsign(), systemsize * sys_size * loczone)
z = gaussian(0, systemsize * sys_size * loczone/ 10e0)
reject = False
for star in stars:
slumin, smass, sclass, sradius, sx, sy, sz
d = dist(x-sx,y-sy,z-sz)
d = dist(x-star.x,y-star.y,z-star.z)
deathzone = sradius + (sradius * 0.25 * math.sqrt(slumin))
deathzone = star.radius + (star.radius * 0.25 * math.sqrt(star.lumin))
if d < deathzone:
reject = True
@ -114,10 +219,9 @@ while len(planets) < num_planets:
surfarea = circsurfarea * 4.0
insol = 0.0
for star in stars:
slumin, smass, sclass, sradius, sx, sy, sz
d = dist(x-sx,y-sy,z-sz)
d = dist(x-star.x,y-star.y,z-star.z)
spower = 3.8e26 * slumin
spower = 3.8e26 * star.lumin
distsphere = 4.0 * math.pi * d**2
insol += (circsurfarea / distsphere) * spower
@ -128,29 +232,119 @@ while len(planets) < num_planets:
atmo = None
while atmo == None or atmo > density:
if gas_giant:
atmo_exp = randrange(-3, 0.5)
atmo_exp = randrange(2.0, 12.0)
else:
atmo_exp = randrange(-9.2, -0.9)
atmo = 10 ** atmo_exp
atmo_exp = randrange(-9, 2.0)
atmo = 1.3 ** atmo_exp
albedo = -1.0
while albedo < 0.0 or albedo > 1.0:
albedo = gaussian(0.35, 0.05)
while albedo <= 0.01 or albedo >= 0.99:
albedo = gaussian(0.35, 0.25)
emissivity = 1.0 - albedo
emissivity = randrange(0.00001, 1.0)
#emissivity = randrange(0.00001, 1.0)
emiss_diff = (((10.0 - pow(atmo, 1.0/2.0)) / 10.0) ** 100.0) / 2.0
emiss_diff = (((10.0 - pow(atmo, 1.0/4.0)) / 10.0) ** 100.0) / 2.0
emissivity_min = emissivity - (emissivity * emiss_diff)
emissivity_max = emissivity + (emissivity * emiss_diff)
avg_temp = pow(((1.0 - albedo) * areainsol) / (4.0 * emissivity * 5.67e-8), 1.0/4.0)
min_temp = pow(((1.0 - albedo) * areainsol) / (4.0 * emissivity_min * 5.67e-8), 1.0/4.0)
max_temp = pow(((1.0 - albedo) * areainsol) / (4.0 * emissivity_max * 5.67e-8), 1.0/4.0)
min_temp = pow(((1.0 - albedo) * areainsol) / (4.0 * emissivity_max * 5.67e-8), 1.0/4.0)
max_temp = pow(((1.0 - albedo) * areainsol) / (4.0 * emissivity_min * 5.67e-8), 1.0/4.0)
planets.append(Planet(x, y, z, mass, radius, surfarea,
areainsol, gas_giant, density, atmo,
albedo, min_temp, max_temp, avg_temp))
planets.sort(cmp=lambda a, b: cmp(dist(a.x, a.y, a.z), dist(b.x, b.y, b.z)))
dels = []
inserts = []
lpl = None
for i in range(0, len(planets)-1):
pl = planets[i]
npl = planets[i+1]
if (npl.dist() - pl.dist()) < math.sqrt((npl.mass + pl.mass) / 1e22) * 0.001:
"BOOM. asteroids"
if i in dels:
# ooh, three or more planets smashing together! exciting!
# this one has already been deleted by the previous one, so we only need to add the next delete
# also update the old asteroid belt to include this planet
dels += [i+1]
belt = inserts[-1][1]
belt.mass += npl.mass
belt.radius = ((belt.radius * float(belt.num_smashed)) + npl.dist()) / (belt.num_smashed + 1)
belt.num_smashed += 1
else:
inserts.append((i - len(dels), Asteroid_Belt((npl.dist() + pl.dist()) / 2.0, npl.mass + pl.mass)))
dels += [i, i+1]
dels.reverse()
for dd in dels:
del planets[dd]
inserts.reverse()
for ii in inserts:
planets.insert(ii[0], ii[1])
system_habitable = any([x.habitable() for x in planets])
return (stars, planets)
def print_system(syst, show_hab=True):
stars, planets = syst
print "\nStars:\n"
for star in stars:
print star.info()
print "\nPlanets:\n"
hab = False
for i, pl in enumerate(planets):
if pl.habitable():
hab = True
print "#%i: %s" % (i+1, pl.info())
if show_hab:
print ""
if hab:
print "System is habitable!"
else:
print "System UNINHABITABLE!"
def main():
if len(sys.argv) > 1:
runs = int(sys.argv[1])
else:
runs = 1
fl = False
systems = []
dead_systems = []
for i in range(runs):
syst = gen_system(fl)
fl = True
if any([x.habitable() for x in syst[1]]):
systems.append(syst)
else:
dead_systems.append(syst)
print color("\n ***************** INHABITABLE SYSTEMS (%d) *****************\n", 'm', 1) % (len(systems),)
for syst in systems:
print_system(syst, False)
print color("\n ***************** UNINHABITABLE SYSTEMS (%d) *****************\x1b[0;37m\n", 'm', 1) % (len(dead_systems),)
for syst in dead_systems:
print_system(syst, False)
if __name__ == '__main__':
main()