game/stars.py

161 lines
4.2 KiB
Python

# -*- coding: utf-8 -*-
#
# Copyright © 2024 Simon Forman
#
# This file is part of game
#
# game is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# game is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with game. If not see <http://www.gnu.org/licenses/>.
#
'''
A field of stars. Each has planets. Each planet has...?
Keep it simple.
Stars have what qualities?
- Cosmetic
- color
- brightness
- size
- Game stats
- age?
- ?
🌑 🌒 🌓 🌔 🌕 🌖 🌗 🌘
♈ ♉ ♊ ♋ ♌ ♍ ♎ ♏ ♐ ♑ ♒ ♓
'''
from math import log10
from random import (
expovariate,
gauss,
lognormvariate,
randint,
random,
seed,
)
from poisson import poisson
from wordlist import generate_name
ROMAN_NUMBERS = [
None, # no zero, but this aligns entries and their indicies
'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX', 'X',
'XI', 'XII', 'XIII', 'XIV', 'XV', 'XVI', 'XVII', 'XVIII', 'XIX', 'XX',
'XXI', 'XXII', 'XXIII', 'XXIV', 'XXV', 'XXVI', 'XXVII', 'XXVIII', 'XXIX',
]
seed(23)
MINIMUM_DISTANCE_BETWEEN_STARS = 160
# The old standard 4:3 ratio screen of 1024 x 768 pixels,
# let's have 10 x 10 of those.
WIDTH, HEIGHT = 10240, 7680
# Database stuff. SQL, etc.
def init_db(conn):
print('Generating star data.')
c = conn.cursor()
for values in generate_stars(WIDTH, HEIGHT, MINIMUM_DISTANCE_BETWEEN_STARS):
c.execute('insert into stars(x, y, radius, name) values (?, ?, ?, ?)', values)
generate_planets_for_star(c)
c.close()
conn.commit()
def iter_stars(conn):
c = conn.cursor()
c.execute('select x, y, radius, name from stars')
yield from c.fetchall()
c.close()
def get_planets_for_star_named(conn, name):
c = conn.cursor()
c.execute(
'''\
select
planets.ordo,
planets.bio_capacity,
planets.industrial_capacity
from planets join stars
on stars.id = planets.star
and stars.name = ?
order by planets.ordo
limit 29
''', # Limit because we only have 29 Roman numbers.
(name,)
)
# TODO: move the display-related code to ui module?
for ordo, bio_capacity, industrial_capacity in c.fetchall():
assert bio_capacity >= 0
assert industrial_capacity >= 0
b = round(log10(bio_capacity), 1) if bio_capacity else 0
i = round(log10(industrial_capacity), 1) if industrial_capacity else 0
yield f'{name}-{ROMAN_NUMBERS[ordo]}', b, i
c.close()
# Procedural Generation of solar system data
def generate_stars(width, height, minimum_distance_between_stars):
unique_names = set()
for x, y in poisson(width, height, minimum_distance_between_stars):
name = generate_name()
while name in unique_names:
name = generate_name()
# I did get a name collision early on!
unique_names.add(name)
yield x, y, round(1 + expovariate(1)), name
def how_many_planets():
'''
Return a non-negative integer, the number of planets a star should have.
'''
n = round(gauss(5, 5))
while n < 0:
n = round(gauss(5, 5))
return n
def has_life():
# Ten percent chance of life
return random() < 0.1
def generate_planets_for_star(c):
# Only call this just after creating a star.
star_id = c.lastrowid
for ordo in range(1, how_many_planets() + 1):
bio_capacity = round(lognormvariate(10, 5))
bio = bio_capacity if has_life() else 0
c.execute(
'insert into planets'
'(ordo, star, bio_capacity, bio, industrial_capacity)'
' values (?, ?, ?, ?, ?)',
(
ordo,
star_id,
bio_capacity,
bio,
round(lognormvariate(10, 5)),
),
)