├── README.md ├── browse_sector.py ├── build_db_from_csv.py ├── convert_travmap_to_csv.py ├── covers ├── a.jpg ├── b.jpg ├── c.jpg ├── d.jpg ├── e.jpg ├── f.jpg ├── g.jpg ├── h.jpg ├── i.jpg ├── j.jpg ├── k.jpg ├── l.jpg ├── m.jpg ├── n.jpg ├── o.jpg └── p.jpg ├── culture.py ├── export_sector.py ├── far_trader.py ├── first_in_generation.py ├── generate_menu.py ├── images ├── agricultural.png ├── asteroid.png ├── barren.png ├── blank.png ├── cold.png ├── corrosive.png ├── exotic.png ├── garden.png ├── gas giant.PNG ├── heavy.png ├── hipop.png ├── hot.png ├── important.png ├── industrial.png ├── light.png ├── lopop.png ├── mask.png ├── moon.png ├── naval.png ├── non_agricultural.png ├── non_industrial.png ├── ocean.png ├── planet.png ├── prison.png ├── scout.png ├── ship.png ├── tbd.png ├── vacuum.png └── wealthy.png ├── journey_data.py ├── mainworld_calculator.py ├── mainworld_selector.py ├── names.csv ├── non_mw.py ├── routes_short_path.py ├── sector_db ├── example-66.db ├── example-66.db_routes.txt ├── example-66.db_tab.txt └── traveller_map_poster.html ├── splash.jpg ├── splash_browser.jpg ├── sunburst.ico ├── tables ├── Orbital Eccentricity Table.txt ├── Orbital Separation Table.txt ├── Planet Density Table.txt ├── Star Characteristics III.txt ├── Star Characteristics V.txt └── World Type Table.txt ├── test_export_def.py ├── trade_goods.csv ├── traveller_functions.py ├── traveller_map.py ├── traveller_master.py └── travellerization.py /README.md: -------------------------------------------------------------------------------- 1 | # traveller-universe-creator 2 | 3 | ## aka Bartleby's Sector Builder 4 | 5 | **v 1.1.0 - 2024-05-28** 6 | 7 | Full release complete, including install exe. 8 | 9 | **Program Purpose:** 10 | 11 | The program generates an entire sci-fi sector of stars, planets, moons using "realistic" scientific models 12 | 13 | **How to Run:** 14 | 15 | Option 1: From the installer 16 | - Download and run the install_bartleby_sector_builder_v.1.1.0.exe file. 17 | (This can be found in the "Releases" section on the right). 18 | - Once installed: 19 | - run generate_menu.exe to generate a DB 20 | - find generated DBs in /sector_db 21 | - run browse_sector.exe to view a sector 22 | - optionally use a DB browser (like https://sqlitebrowser.org/) to look at every piece of data for each planet/system 23 | - **please note:** the example-66 database in the /sector_db directory MUST be there for browse_sector to work. Do not remove it. 24 | 25 | Option 2: From the code - requires python run time environment 26 | - run generate_menu.exe to generate a DB 27 | - find generated DBs in /sector_db 28 | - run browse_sector.exe to view a sector 29 | - optionally use a DB browser (like https://sqlitebrowser.org/) to look at every piece of data for each planet/system 30 | - **please note:** the example-66 database in the /sector_db directory MUST be there for browse_sector to work. Do not remove it. 31 | 32 | Requirements for Option 2 (non-EXE installs): 33 | 34 | - Programmed using Python 3.12 35 | 36 | - This is the up to date list of all required imports: 37 | 38 | - pandas v1.4.2 or higher 39 | - numpy v1.22.3 or higher 40 | - networkx v2.8.4 or higher 41 | - matplotlib v3.5.2 or higher 42 | - pillow v9.2 or higher 43 | - PyPDF2 v3.0.1 (latest at this time) 44 | - reportlab v4.2.0 (latest at this time) 45 | - FreeSimpleGUI v5.1.0 (latest at this time) 46 | 47 | 48 | 49 | **v 1.1.0 - Upgrades** 50 | 51 | - Changed from PySimpleGUI to FreeSimpleGUI (until I better understand licensing requirements) 52 | - Added browser functionality to use APIs from travellerworlds.com and travellermap.com 53 | - Added "Rings" value to browser 54 | - Updated PBG value at creation to include PBG numbers beyond primary system 55 | - moved text tables to tables directory 56 | 57 | 58 | 59 | **FAQ:** 60 | 61 | Q: What does the program do? 62 | 63 | A: After setting the parameters you want, it produces a traveller/cepheus sector and provides the information in separate files explained above. 64 | 65 | The text files can be used to visualize the sector using https://travellermap.com/ or PyMapGen at https://github.com/ShawnDriscoll/PyMapGen 66 | 67 | The db file houses information for every star and planet in the sector. It includes UWPs for mainworld and non-mainworlds. 68 | You can browse and search this database using the excellent SQLite browser from: https://sqlitebrowser.org/ 69 | 70 | Q: What Traveller rules does it use? 71 | 72 | A: It uses mostly GURPS First In for the science stuff (like stellar details, and planet temperature). 73 | T5 rules for the Traveller stuff (like law level and influence scores). 74 | I have also been using parts of Architect of Worlds (a modern update to First In by the same creator) 75 | 76 | Q: Where do the planet names come from 77 | 78 | A: They come from all over the internet, but most come from: https://simplemaps.com/data/world-cities 79 | 80 | Q: What are these other non-python files? 81 | 82 | A: 83 | 84 | traveller_map_poster.html: a spartan method of using the travellermap API to build sector maps for the sectors you build. 85 | Choose the tab and route file and click submit and an image map of your sector will be created by travellermap. 86 | 87 | names.csv: Used to create planet/moon names and can be modified to add or remove names you prefer or dislike. 88 | Be careful if you remove too many names as the program requires thousands and will crash if it runs out (it does not use the same one twice) 89 | 90 | trade_goods.csv: Copied from T5 and used to show wants and surpluses in each system. Feel free to modify to taste. 91 | 92 | txt files: various First In tables used to create the science stuff. Feel free to modify but be careful - you void the warranty if you mess with this stuff. 93 | 94 | Q: Where can I find more info? 95 | 96 | A: I hang out at COTI (https://www.travellerrpg.com/) from time to time and discuss it there. 97 | 98 | I also made some YouTube vidoes walking through the program: https://www.youtube.com/channel/UCJVDA8TEy3aRHwrGVTsNJBg 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /build_db_from_csv.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Tue May 21 15:16:11 2024 4 | 5 | @author: sean 6 | """ 7 | 8 | import pandas as pd 9 | import sqlite3 10 | from traveller_functions import hex_to_int 11 | from traveller_map import build_travellermap_file 12 | from routes_short_path import create_route_xml 13 | 14 | new_sector_db = 'sector_db/solomani_rim.db' 15 | sector_name = 'Solomani Rim' 16 | 17 | 18 | conn_new = sqlite3.connect(new_sector_db) 19 | conn_old = sqlite3.connect('sector_db/example-66.db') 20 | 21 | df = pd.read_csv('sector_db/solomani_rim.csv', dtype=str) 22 | 23 | new_df = pd.DataFrame() 24 | 25 | new_df['location_orb'] = df['Hex'] 26 | new_df['location'] = df['Hex'] 27 | new_df['system_name'] = df['Name'] 28 | 29 | new_df['starport'] = df['UWP'].str[0] 30 | 31 | new_df['size'] =df['UWP'].str[1].apply(hex_to_int) 32 | new_df['atmosphere'] = df['UWP'].str[2].apply(hex_to_int) 33 | new_df['hydrographics'] = df['UWP'].str[3].apply(hex_to_int) 34 | 35 | new_df['population'] = df['UWP'].str[4].apply(hex_to_int) 36 | new_df['government'] = df['UWP'].str[5].apply(hex_to_int) 37 | new_df['law'] = df['UWP'].str[6].apply(hex_to_int) 38 | 39 | new_df['tech_level'] = df['UWP'].str[8].apply(hex_to_int) 40 | 41 | new_df['main_world'] = 1 42 | 43 | new_df.to_sql('traveller_stats', conn_new, if_exists='replace', index_label = 'id') 44 | 45 | 46 | 47 | system_df = pd.DataFrame() 48 | system_df['id'] = df.index 49 | system_df['location'] = df['Hex'] 50 | system_df['remarks'] = df['Remarks'] 51 | 52 | system_df['ix'] = df['{Ix}'] 53 | system_df['ex'] = df['(Ex)'] 54 | system_df['cx'] = df['[Cx]'] 55 | 56 | system_df['n'] = df['N'] 57 | system_df['bases'] = df['B'] 58 | system_df['zone'] = df['Z'] 59 | 60 | system_df['pbg'] = df['PBG'] 61 | system_df['w'] = df['W'] 62 | system_df['allegiance'] = df['A'] 63 | system_df['stars'] = df['Stellar'] 64 | 65 | system_df['remarks'].fillna('None',inplace=True) 66 | 67 | 68 | system_df.to_sql('system_stats', conn_new, if_exists='replace', index=False) 69 | 70 | 71 | 72 | 73 | sql_statement = 'SELECT * FROM ' 74 | 75 | table_list = ['far_trader', 76 | 'journey_data', 77 | 'orbital_bodies', 78 | 'perceived_culture', 79 | 'stellar_bodies', 80 | 'main_world_eval'] 81 | 82 | for each_table in table_list: 83 | sql_command = sql_statement + each_table 84 | df = pd.read_sql(sql_command, conn_old) 85 | df = pd.DataFrame(columns=df.columns) 86 | df['location'] = new_df['location'] 87 | if each_table in ['journey_data', 'orbital_bodies','main_world_eval']: 88 | df['location_orbit'] = new_df['location'] 89 | if each_table == 'orbital_bodies': 90 | df['gravity'] = 1 91 | df['wtype'] = 'N/A' 92 | df['size_class'] = 'N/A' 93 | df['atmos_composition'] = 'N/A' 94 | df['temperature'] = 300 95 | df['zone'] = 'Middle Zone' 96 | df['body'] = 'Planet' 97 | df['climate'] = 'N/A' 98 | 99 | elif each_table == 'traveller_stats': 100 | df['location_orb'] = new_df['location'] 101 | 102 | elif each_table == 'far_trader': 103 | df['needs'] == 'NA' 104 | df['wants'] == 'NA' 105 | 106 | 107 | if each_table == 'perceived_culture': 108 | df.fillna('N/A', inplace=True) 109 | else: 110 | df.fillna(0, inplace=True) 111 | 112 | 113 | df['id'] = df.index 114 | df.to_sql(each_table, conn_new, if_exists='replace') 115 | 116 | 117 | 118 | build_travellermap_file(new_sector_db, sector_name) 119 | create_route_xml(0,new_sector_db,0) 120 | 121 | 122 | 123 | 124 | conn_old.commit() 125 | conn_new.commit() 126 | conn_new.close() 127 | conn_old.close() 128 | 129 | 130 | 131 | -------------------------------------------------------------------------------- /convert_travmap_to_csv.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Tue May 21 14:36:01 2024 4 | 5 | @author: sean 6 | """ 7 | 8 | import pandas as pd 9 | 10 | df = pd.read_fwf('sector_db/solomani_rim.txt') 11 | df.drop(index=0, inplace=True) 12 | df['Hex'] = df['Hex'].astype(str) 13 | df.to_csv('sector_db/solomani_rim.csv', index=False) -------------------------------------------------------------------------------- /covers/a.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/covers/a.jpg -------------------------------------------------------------------------------- /covers/b.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/covers/b.jpg -------------------------------------------------------------------------------- /covers/c.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/covers/c.jpg -------------------------------------------------------------------------------- /covers/d.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/covers/d.jpg -------------------------------------------------------------------------------- /covers/e.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/covers/e.jpg -------------------------------------------------------------------------------- /covers/f.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/covers/f.jpg -------------------------------------------------------------------------------- /covers/g.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/covers/g.jpg -------------------------------------------------------------------------------- /covers/h.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/covers/h.jpg -------------------------------------------------------------------------------- /covers/i.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/covers/i.jpg -------------------------------------------------------------------------------- /covers/j.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/covers/j.jpg -------------------------------------------------------------------------------- /covers/k.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/covers/k.jpg -------------------------------------------------------------------------------- /covers/l.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/covers/l.jpg -------------------------------------------------------------------------------- /covers/m.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/covers/m.jpg -------------------------------------------------------------------------------- /covers/n.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/covers/n.jpg -------------------------------------------------------------------------------- /covers/o.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/covers/o.jpg -------------------------------------------------------------------------------- /covers/p.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/covers/p.jpg -------------------------------------------------------------------------------- /culture.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed Dec 1 15:44:10 2021 4 | 5 | @author: sean 6 | """ 7 | def create_culture_stats(seed_number,db_name): 8 | 9 | import sqlite3 10 | import random 11 | 12 | random.seed(seed_number) 13 | 14 | def create_culture_table(): 15 | sql_create_tb_far_trader_table = """CREATE TABLE perceived_culture( 16 | id INTEGER PRIMARY KEY AUTOINCREMENT, 17 | location TEXT, 18 | age TEXT, 19 | appearance TEXT, 20 | tendency TEXT, 21 | materialism TEXT, 22 | honesty TEXT, 23 | bravery TEXT, 24 | social_conflict TEXT, 25 | work_ethic TEXT, 26 | consumerism TEXT, 27 | spiritual_outlook TEXT, 28 | status_quo_outlook TEXT, 29 | custom TEXT, 30 | interests TEXT, 31 | common_skills TEXT) 32 | 33 | ;""" 34 | 35 | c.execute('DROP TABLE IF EXISTS perceived_culture') 36 | c.execute(sql_create_tb_far_trader_table) 37 | 38 | # MAIN PROGRAM 39 | 40 | conn = sqlite3.connect(db_name) 41 | c = conn.cursor() 42 | create_culture_table() 43 | 44 | 45 | age_list = ['infant-centric','infants neither seen nor heard','youth-centric','no youth culture', 46 | 'young adult-centric','adult-centric','adult-centric','adult-centric','revere seniors','revere seniors','seniors reviled'] 47 | appearance_list = ['dirty','clean','unkempt','immaculate','casual','formal','average','average','average'] 48 | tendency_list = ['perceptive','violent','vain','selfish','kind','careless','capricious','serious','trusting','suspicious', 49 | 'studious','cruel','loquacious','curious','non-curious','intelligent','anti-intellectual','impulsive','empathetic', 50 | 'cheerful','morose','sensitive','insensitive','humble','haughty','even-tempered','hot-tempered','relaxed', 51 | 'black and white - no gray','pragmatic','sexist','obsessed','peaceful','fashionable','war'] 52 | materialism_list = ['minimal possessions','average','average','average','modest possessions','covet possessions'] 53 | honesty_list = ['scrupulous','honour-bound','truthful','average','average','average','average','deceitful','untrustworthy'] 54 | bravery_list = ['average','average','average','foolhardy','brave','cautious','reject bravery as an ideal'] 55 | work_ethic_list = ['very relaxed','relaxed','average','average','average','driven','beyond driven'] 56 | social_conflict_list = ['average','average','average','conflict adverse','conflict phobic','enjoy conflict','thrive on conflict'] 57 | consumerism_list = ['miserly','conservative spender','average','average','spendthrift','wasteful'] 58 | spiritual_outlook_list = ['martyr-like','devout','reverent','moderate','moderate','questioning','irreverent'] 59 | status_quo_outlook_list = ['radical','progressive','progressive','conservative','conservative','progressive','progressive','conservative', 60 | 'conservative','reactionary'] 61 | custom_role_list = ['everyone','everyone','everyone','everyone','everyone','everyone','everyone','everyone','everyone','everyone', 62 | 'natives','citizens','visitors','certain political groups','certain sex','law enforcement','entertainers', 63 | 'heroes','athletes','certain races','religious figures','military figures','certain occupations','political figures', 64 | 'medical professionals','certain age groups','scientists','academics','low social class','high social class', 65 | 'criminals','socialites','celebrities','workers','off-worlders/travellers'] 66 | custom_list = ['same clothes for all sexes','unusual clothes','unusual headgear','shaved heads','hair never cut','unusual hair color', 67 | 'unusual hair styles','unusual eyebrows','unusual facial alterations','unusual body alterations','unusual fingernails', 68 | 'unusual toe nails','unusual cosmetics','unusual jewelry','unusual accessories','unusual handgear','tattooing on face', 69 | 'tattooing on body','hidden tattooing','unusual foods','unusual beverages','unusual food preparation','segregated at meals', 70 | 'vegetarian','vegan','certain colored food','certain shaped food','certain food sources','eat in special location', 71 | 'eat only in private','eat in special orientation','eat with unusual utensils','eat only at home','eat at unusual times', 72 | 'eat only at certain times','rituals before eating','rituals after eating','one group eats leftovers', 73 | 'cannabalistic','live privately','live in small groups','live in special locations','live at work', 74 | 'live under special conditions','confined to quarters','live under special care','have extravagant quarters', 75 | 'have minimal quarters','have unusual quarters','quarters must be visited','live with extended families', 76 | 'live in communal housing','live only in certain terrain','must move around','unsual media','unusual starport', 77 | 'unusual lifecycle','unusual social standings','unusual trade','unusual nobility','unusual reproduction','conspiracy-driven'] 78 | interests_list = ['religion','philosophy','economics','sports','politics','legends and myth','history','nature','horticulture','handicrafts', 79 | 'foods','wines and spirits','gambling','drugs','art','sculpture','music','theatre', 80 | 'antiques','buttons','coins','guns','swords','hats','insects','sea shells','stones','tools','toys','minerals', 81 | 'airbrushing','blacksmithing','calligraphy','candle-making','ceramics','crocheting','digital arts','photography', 82 | 'drawing','embroidery','fashion','glassblowing','graffiti','illusion','jewelry making','knitting','nail art','needlepoint', 83 | 'origami','painting','pottery','sewing','singing','storytelling','taxidermy','dancing'] 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | sql3_select_tb_t5 = """ SELECT s.location, 93 | s.remarks, 94 | t.population 95 | FROM system_stats s 96 | LEFT JOIN traveller_stats t 97 | ON s.location = t.location 98 | WHERE t.main_world = 1""" 99 | 100 | c.execute(sql3_select_tb_t5) 101 | allrows = c.fetchall() 102 | 103 | 104 | 105 | for row in allrows: 106 | 107 | 108 | location = str(row[0]) 109 | remarks = str(row[1]) 110 | population = int(row[2]) 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | if population == 0: 119 | age = 'n/a' 120 | appearance = 'n/a' 121 | tendency = 'n/a' 122 | materialism = 'n/a' 123 | honesty = 'n/a' 124 | bravery = 'n/a' 125 | work_ethic = 'n/a' 126 | social_conflict = 'n/a' 127 | consumerism = 'n/a' 128 | spiritual_outlook = 'n/a' 129 | status_quo_outlook = 'n/a' 130 | custom = 'n/a' 131 | interests = 'n/a' 132 | common_skills = 'n/a' 133 | 134 | 135 | 136 | else: 137 | 138 | age = random.choice(age_list) 139 | appearance = random.choice(appearance_list) 140 | tendency = random.choice(tendency_list) 141 | materialism = random.choice(materialism_list) 142 | honesty = random.choice(honesty_list) 143 | bravery = random.choice(bravery_list) 144 | work_ethic = random.choice(work_ethic_list) 145 | social_conflict = random.choice(social_conflict_list) 146 | consumerism = random.choice(consumerism_list) 147 | spiritual_outlook = random.choice(spiritual_outlook_list) 148 | status_quo_outlook = random.choice(status_quo_outlook_list) 149 | custom = random.choice(custom_role_list) + ': ' + random.choice(custom_list) 150 | interests = random.choice(interests_list) 151 | common_skills = '' 152 | 153 | 154 | 155 | 156 | 157 | dc_common_skills = { 158 | 'Va': 'Vacc Suit-0 ', 159 | 'Ag': 'Animal-0 ', 160 | 'As': 'Zero G-0 ', 161 | 'De': 'Survival-0 ', 162 | 'Fl': 'Hostile Env-0 ', 163 | 'Ga': 'Trader-0 ', 164 | 'He': 'Hostile Env-0 ', 165 | 'Oc': 'Hi G-0 ', 166 | 'Wa': 'Seafarer-0 ', 167 | 'Lo': 'Flyer-0 ', 168 | 'Ni': 'Driver-0 ', 169 | 'Hi': 'Streetwise-0 ', 170 | 'Pa': 'Trader-0 ', 171 | 'Na': 'Survey-0 ', 172 | 'Pi': 'JOT-0 ', 173 | 'In': 'Elec or Mech-0 ', 174 | 'Po': 'Steward-0 ', 175 | 'Pr': 'Craft-0 ', 176 | 'Ri': 'Art-0 '} 177 | 178 | common_skill_list = [] 179 | for key in dc_common_skills: 180 | if key in remarks: 181 | common_skill_list.append(dc_common_skills[key]) 182 | common_skill_list = set(common_skill_list) 183 | for each in common_skill_list: 184 | common_skills += each 185 | 186 | 187 | 188 | 189 | sqlcommand = ''' INSERT INTO perceived_culture ( 190 | location, 191 | age, 192 | appearance, 193 | tendency, 194 | materialism, 195 | honesty, 196 | bravery, 197 | work_ethic, 198 | social_conflict, 199 | consumerism, 200 | spiritual_outlook, 201 | status_quo_outlook, 202 | custom, 203 | interests, 204 | common_skills) 205 | VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ''' 206 | 207 | 208 | body_row = (location, 209 | age, 210 | appearance, 211 | tendency, 212 | materialism, 213 | honesty, 214 | bravery, 215 | work_ethic, 216 | social_conflict, 217 | consumerism, 218 | spiritual_outlook, 219 | status_quo_outlook, 220 | custom, 221 | interests, 222 | common_skills) 223 | 224 | 225 | c.execute(sqlcommand, body_row) 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | conn.commit() 234 | conn.close() 235 | 236 | 237 | 238 | 239 | 240 | -------------------------------------------------------------------------------- /far_trader.py: -------------------------------------------------------------------------------- 1 | def generate_far_trader_stats(seed_number,db_name): 2 | 3 | # Far Trader 4 | # by Sean Nelson 5 | 6 | # A program to teach Sean the Python programming language 7 | # The goal is to create a table with mainworld merchant stats 8 | # Using GURPS Far Tarder info 9 | 10 | import sqlite3 11 | import random 12 | 13 | 14 | 15 | def create_tb_far_trader_table(): 16 | sql_create_tb_far_trader_table = """CREATE TABLE far_trader( 17 | id INTEGER PRIMARY KEY AUTOINCREMENT, 18 | location TEXT, 19 | wtn INTEGER, 20 | gwp INTEGER, 21 | exchange REAL, 22 | needs TEXT, 23 | wants TEXT) 24 | ;""" 25 | 26 | c.execute('DROP TABLE IF EXISTS far_trader') 27 | c.execute(sql_create_tb_far_trader_table) 28 | 29 | def get_exchange(starport, tech_level): 30 | starport_ex_dict = {'A':1,'B':0.95,'C':0.90,'D':0.85,'E':0.80,'X':0.20} 31 | tech_mod = (15 - tech_level) * 0.05 32 | exchange = starport_ex_dict[starport] - tech_mod 33 | if exchange < 0: exchange = 0 34 | return exchange 35 | 36 | def get_wants_needs(): 37 | goods_list = open("trade_goods.csv", "r").readlines() 38 | wants = '' 39 | needs = '' 40 | 41 | for x in range(0,12): 42 | goods_left = len(goods_list) 43 | good_picked = goods_list[random.randrange(0,goods_left)] 44 | good_fixed = good_picked.rstrip('\n') 45 | goods_list.remove(good_picked) 46 | if x < 6: 47 | wants += good_fixed + '; ' 48 | else: 49 | needs += good_fixed + '; ' 50 | 51 | 52 | return wants, needs 53 | 54 | # MAIN PROGRAM 55 | 56 | conn = sqlite3.connect(db_name) 57 | c = conn.cursor() 58 | create_tb_far_trader_table() 59 | 60 | sql3_select_tb_t5 = """ SELECT t.location, 61 | t.starport, 62 | t.population, 63 | t.tech_level, 64 | s.remarks 65 | FROM traveller_stats t 66 | LEFT JOIN system_stats s 67 | ON t.location = s.location 68 | WHERE main_world = 1 """ 69 | 70 | c.execute(sql3_select_tb_t5) 71 | allrows = c.fetchall() 72 | 73 | for row in allrows: 74 | # print (row[0]) 75 | location = str(row[0]) 76 | starport = str(row[1]) 77 | population = int(row[2]) 78 | tech_level = int(row[3]) 79 | remarks = str(row[4]) 80 | if population > 0: 81 | 82 | uwtn = -100 83 | tl_mod = -100 84 | pop_mod = -100 85 | port_mod = -100 86 | wtn = -100 87 | 88 | pop_mod = round(population/2,1) 89 | if tech_level <= 2: tl_mod = -0.5 90 | elif tech_level <= 5: tl_mod = 0 91 | elif tech_level <= 8: tl_mod = 0.5 92 | elif tech_level <= 11: tl_mod = 1 93 | elif tech_level <= 13: tl_mod = 1.5 94 | else: tl_mod = 2 95 | 96 | uwtn = round(tl_mod + pop_mod,2) 97 | # print (uwtn) 98 | 99 | port_dict = { 'A':(1.5,1,1,0.5,0.5,0,0,0), 100 | 'B':(1,1,0.5,0.5,0,0,-0.5,-1), 101 | 'C':(1,0.5,0.5,0,0,-0.5,-1,-1.5), 102 | 'D':(0.5,0.5,0,0,-0.5,-1,-1.5,-2), 103 | 'E':(+0.5,0,0,-0.5,-1,-1.5,-2,-2.5), 104 | 'X':(0,0,-2.5,-3,-3.5,-4,-4.5,-5)} 105 | 106 | mod_uwtn = int(uwtn) 107 | if mod_uwtn > 7: mod_uwtn = 7 108 | elif mod_uwtn < 0: mod_uwtn = 0 109 | 110 | port_mod = port_dict[starport][mod_uwtn] 111 | # print (port_mod) 112 | 113 | wtn = uwtn + port_mod 114 | # print (wtn) 115 | 116 | bpr = -100 117 | bpr_list = (55,85,135,220,350,560,895,1430,2290,3660,5860,9375,15000,24400,40000,60000) 118 | tl_lookup = tech_level 119 | if tl_lookup > 15: tl_lookup = 15 120 | bpr = bpr_list[tl_lookup] 121 | # print(bpr) 122 | 123 | bpr_trade_mod = 1 124 | 125 | if 'Ri' in remarks: bpr_trade_mod = bpr_trade_mod * 1.6 126 | if 'In' in remarks: bpr_trade_mod = bpr_trade_mod * 1.4 127 | if 'Ag' in remarks: bpr_trade_mod = bpr_trade_mod * 1.2 128 | if 'Po' in remarks: bpr_trade_mod = bpr_trade_mod * 0.8 129 | if 'Ni' in remarks: bpr_trade_mod = bpr_trade_mod * 0.8 130 | if 'As' in remarks: bpr_trade_mod = bpr_trade_mod * 0.8 131 | elif 'De' in remarks: bpr_trade_mod = bpr_trade_mod * 0.8 132 | elif 'Fl' in remarks: bpr_trade_mod = bpr_trade_mod * 0.8 133 | elif 'Ic' in remarks: bpr_trade_mod = bpr_trade_mod * 0.8 134 | elif 'Va' in remarks: bpr_trade_mod = bpr_trade_mod * 0.8 135 | 136 | bpr_trade = bpr * bpr_trade_mod 137 | # print(bpr_trade) 138 | 139 | gwp = round(bpr_trade * population * 10,0) 140 | # print(gwp) 141 | 142 | exchange = get_exchange(starport, tech_level) 143 | 144 | 145 | wants, needs = get_wants_needs() 146 | 147 | 148 | 149 | else: 150 | wtn = 0 151 | gwp = 0 152 | exchange = 0 153 | wants = '' 154 | needs = '' 155 | 156 | 157 | 158 | sqlcommand = ''' INSERT INTO far_trader ( location, 159 | wtn, 160 | gwp, 161 | exchange, 162 | wants, 163 | needs) 164 | VALUES(?, ?, ?, ?, ?, ?) ''' 165 | 166 | 167 | body_row = (location, 168 | wtn, 169 | gwp, 170 | exchange, 171 | wants, 172 | needs) 173 | 174 | 175 | c.execute(sqlcommand, body_row) 176 | 177 | conn.commit() 178 | conn.close() -------------------------------------------------------------------------------- /generate_menu.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | v 1.0.0 2022-11-17 4 | v 1.0.1 2024-05-25 - updated version number to reflect changes in first_in_generation 5 | """ 6 | import FreeSimpleGUI as sg 7 | 8 | import logging 9 | import warnings 10 | from traveller_master import make_sector_master 11 | 12 | 13 | try: 14 | import pyi_splash 15 | # Update the text on the splash screen 16 | pyi_splash.update_text("Processing...") 17 | 18 | # Close the splash screen. It does not matter when the call 19 | # to this function is made, the splash screen remains open until 20 | # this function is called or the Python program is terminated. 21 | pyi_splash.close() 22 | 23 | except: 24 | logging.debug('Skipped Splash - Running locally') 25 | 26 | 27 | class Creation_Choices: 28 | def __init__(self, random_seed, sector_name, sector_density, settlement_mod): 29 | self.random_seed = random_seed 30 | self.sector_name = sector_name 31 | self.sector_density = sector_density 32 | self.settlement_mod = settlement_mod 33 | 34 | warnings.simplefilter(action='ignore', category=FutureWarning) 35 | 36 | sg.theme('DarkBlue') 37 | 38 | left_layout = [ 39 | [sg.Text("PARAMETERS")], 40 | [sg.Text('System Density',tooltip = 'Chance for system in each parsec')], 41 | [sg.Text('Settlement Style',tooltip = 'Optionally modify stats based on location')], 42 | [sg.Text('Sector Name',tooltip = 'Files will be created in /sector_db')], 43 | [sg.Text('Random Seed',tooltip = 'Using the same seed with the same density will produce the same sector')] 44 | ] 45 | 46 | middle_layout = [ 47 | 48 | [sg.Text("OPTIONS")], 49 | [sg.Radio('Sparse', "RADIO1", key = '-SPARSE-', tooltip='1 in 6 chance per parsec', default=True), 50 | sg.Radio('Scattered',"RADIO1", key = '-SCATTERED-',tooltip='2 in 6 chance per parsec'), 51 | sg.Radio('Standard', "RADIO1", key = '-STANDARD-', tooltip='3 in 6 chance per parsec')], 52 | [sg.Radio('Normal', "RADIO2", key = '-NORMAL-', tooltip='Normal Traveller Rules', default=True), 53 | sg.Radio('Diminishing',"RADIO2", key = '-DIMINISHING-',tooltip='Population increased in center, lowered in rim')], 54 | 55 | [sg.InputText(size=(10,1), key=('-NAME-'))], 56 | [sg.InputText(size=(4,1),key=('-SEED-'))] 57 | ] 58 | 59 | 60 | 61 | layout = [ 62 | [sg.Text("""Generate Window""")], 63 | [sg.HSeparator()], 64 | [sg.Column(left_layout), 65 | sg.VSeparator(), 66 | sg.Column(middle_layout) 67 | 68 | ], 69 | [sg.Button('Generate'), sg.Button('Cancel')] 70 | 71 | ] 72 | 73 | # Create the Window 74 | window = sg.Window("""Bartleby's Sector Builder v1.1""", layout) 75 | 76 | 77 | # Event Loop to process "events" and get the "values" of the inputs 78 | while True: 79 | 80 | event, values = window.read() 81 | if event == sg.WIN_CLOSED or event == 'Cancel': # if user closes window or clicks cancel 82 | print('Close') 83 | break 84 | 85 | else: 86 | print('You entered ', values['-SPARSE-'], 87 | values['-SCATTERED-'], 88 | values['-STANDARD-'], 89 | values['-NAME-'], 90 | values['-SEED-'], 91 | values['-NORMAL-'], 92 | values['-DIMINISHING-']) 93 | 94 | if values['-SPARSE-'] == True: 95 | density_input = 6 96 | elif values['-SCATTERED-'] == True: 97 | density_input = 5 98 | else: 99 | density_input = 4 100 | 101 | if values['-NORMAL-'] is True: 102 | settlement_mod = 0 103 | else: 104 | settlement_mod = 1 105 | 106 | 107 | sector_name_input = values['-NAME-'] 108 | random_seed_input = values['-SEED-'] 109 | 110 | 111 | 112 | decisions_provided = Creation_Choices( 113 | random_seed_input, 114 | sector_name_input, 115 | density_input, 116 | settlement_mod) 117 | 118 | 119 | make_sector_master(decisions_provided) 120 | 121 | break 122 | 123 | 124 | 125 | window.close() -------------------------------------------------------------------------------- /images/agricultural.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/agricultural.png -------------------------------------------------------------------------------- /images/asteroid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/asteroid.png -------------------------------------------------------------------------------- /images/barren.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/barren.png -------------------------------------------------------------------------------- /images/blank.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/blank.png -------------------------------------------------------------------------------- /images/cold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/cold.png -------------------------------------------------------------------------------- /images/corrosive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/corrosive.png -------------------------------------------------------------------------------- /images/exotic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/exotic.png -------------------------------------------------------------------------------- /images/garden.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/garden.png -------------------------------------------------------------------------------- /images/gas giant.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/gas giant.PNG -------------------------------------------------------------------------------- /images/heavy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/heavy.png -------------------------------------------------------------------------------- /images/hipop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/hipop.png -------------------------------------------------------------------------------- /images/hot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/hot.png -------------------------------------------------------------------------------- /images/important.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/important.png -------------------------------------------------------------------------------- /images/industrial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/industrial.png -------------------------------------------------------------------------------- /images/light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/light.png -------------------------------------------------------------------------------- /images/lopop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/lopop.png -------------------------------------------------------------------------------- /images/mask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/mask.png -------------------------------------------------------------------------------- /images/moon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/moon.png -------------------------------------------------------------------------------- /images/naval.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/naval.png -------------------------------------------------------------------------------- /images/non_agricultural.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/non_agricultural.png -------------------------------------------------------------------------------- /images/non_industrial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/non_industrial.png -------------------------------------------------------------------------------- /images/ocean.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/ocean.png -------------------------------------------------------------------------------- /images/planet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/planet.png -------------------------------------------------------------------------------- /images/prison.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/prison.png -------------------------------------------------------------------------------- /images/scout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/scout.png -------------------------------------------------------------------------------- /images/ship.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/ship.png -------------------------------------------------------------------------------- /images/tbd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/tbd.png -------------------------------------------------------------------------------- /images/vacuum.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/vacuum.png -------------------------------------------------------------------------------- /images/wealthy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/images/wealthy.png -------------------------------------------------------------------------------- /journey_data.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | # Journey Data 4 | 5 | 6 | Created on Wed Oct 20 16:35:07 2021 7 | 8 | @author: sean 9 | """ 10 | 11 | def build_journey_table(seed_number,db_name): 12 | 13 | import sqlite3 14 | import pandas as pd 15 | import numpy as np 16 | 17 | 18 | conn = sqlite3.connect(db_name) 19 | c = conn.cursor() 20 | 21 | 22 | try: 23 | sql_orbital = '''SELECT location_orbit, location, stellar_distance, size, stellar_orbit_no from orbital_bodies''' 24 | df_orbital_bodies = pd.read_sql_query(sql_orbital,conn) 25 | except: 26 | print(db_name,'Journey Table: Failed building orbital table') 27 | 28 | 29 | try: 30 | sql_stellar = '''SELECT location, radius as stellar_radius, companion_class from stellar_bodies''' 31 | df_stellar = pd.read_sql_query(sql_stellar,conn) 32 | except: 33 | print(db_name,'Journey Table: Failed building stellar table') 34 | 35 | 36 | 37 | try: 38 | 39 | df_orbital_bodies['left_match'] = df_orbital_bodies.location_orbit.str.slice(0, 6) 40 | 41 | 42 | df_stellar['match'] = df_stellar['location'] + '-' + df_stellar['companion_class'] 43 | 44 | 45 | df_journey = pd.merge(df_orbital_bodies,df_stellar, how='left', left_on='left_match', right_on='match') 46 | 47 | except: 48 | print('Journey Table: Failed at df merge') 49 | 50 | 51 | 52 | try: 53 | df_journey['stellar_mask_Mm'] = df_journey['stellar_radius'] * 200 * 149598.073 # convert radius in AU to Mm 54 | stellar_mask_Mm = df_journey['stellar_mask_Mm'] 55 | 56 | 57 | df_journey['planetary_mask_Mm'] = round(df_journey['size'] * 1.690 * 100,3) 58 | df_journey['planetary_distance_Mm'] = df_journey['stellar_distance'] * 149598.073 59 | 60 | 61 | planet_Mm = df_journey['planetary_distance_Mm'] 62 | planet_mask = df_journey['planetary_mask_Mm'] 63 | 64 | 65 | df_journey['planetary_mask_end_Mm'] = planet_Mm + planet_mask 66 | planet_mask_end = df_journey['planetary_mask_end_Mm'] 67 | 68 | 69 | # Set the status of any stellar masking 70 | conditions = [ stellar_mask_Mm > planet_mask_end, 71 | (stellar_mask_Mm < planet_mask_end) & (stellar_mask_Mm > planet_Mm), 72 | stellar_mask_Mm < planet_Mm ] 73 | choices = [ 'total', 'partial', 'none' ] 74 | df_journey['planet_stellar_masked'] = np.select(conditions, choices, default=np.nan) 75 | 76 | df_journey['jump_point_Mm'] = np.where(stellar_mask_Mm > planet_mask_end, 77 | stellar_mask_Mm - planet_Mm, 78 | planet_mask_end - planet_Mm) 79 | 80 | except: 81 | print('Journey Table: Failed at calculations') 82 | 83 | try: 84 | for x in range(1,8): 85 | d_a = df_journey['jump_point_Mm']*1000000/(9.8 * x) 86 | sqrt_d_a = d_a**0.5 87 | ix_hrs = 'hrs_' + str(x) + 'g' 88 | df_journey[ix_hrs] = round(sqrt_d_a*2/3600,1) 89 | except: 90 | print('Journey Table: Failed at Acceleration Measurements') 91 | 92 | 93 | df_journey.to_sql('journey_data', conn) 94 | 95 | conn.commit() 96 | c.close() 97 | conn.close() 98 | 99 | -------------------------------------------------------------------------------- /mainworld_calculator.py: -------------------------------------------------------------------------------- 1 | def generate_mainworld_scores(db_name): 2 | #!/usr/bin/python 3 | 4 | # Mainworld Calculator 5 | # by Sean Nelson 6 | 7 | # The goal is to read the Orbital Bodies table generated from the 8 | # First In program and score the potential for each body being a mainworld 9 | 10 | # The output is a new column in the Orbital Bodies Table. It needs to run after First In. 11 | 12 | 13 | 14 | # Open the SQLite 3 database 15 | 16 | import sqlite3 17 | import pandas as pd 18 | import numpy as np 19 | 20 | conn = sqlite3.connect(db_name) 21 | c = conn.cursor() 22 | 23 | 24 | 25 | 26 | sql3_select = """ SELECT o.location_orbit, 27 | o.location, 28 | o.gravity, 29 | o.hydrographics, 30 | o.wtype, 31 | o.atmos_composition, 32 | o.climate, 33 | o.stellar_distance, 34 | o.temperature, 35 | o.zone, 36 | j.planet_stellar_masked 37 | FROM orbital_bodies o 38 | LEFT JOIN journey_data j 39 | ON o.location_orbit = j.location_orbit""" 40 | 41 | # print('trying to load into DF') 42 | try: 43 | df = pd.read_sql_query(sql3_select,conn) 44 | # print('df load clearly worked') 45 | except: 46 | print('Problem - df failed') 47 | 48 | 49 | # print(df.describe()) 50 | 51 | 52 | 53 | 54 | col = 'wtype' 55 | conditions = [ df[col][0] == 'O', df[col][0] == 'H', df[col] == 'Gas Giant', df[col] == 'Belt'] 56 | choices = [ 50000, -1000, -1000000, -500] 57 | df["wtype_mod"] = np.select(conditions, choices, default=0) 58 | 59 | 60 | 61 | col = 'gravity' 62 | conditions = [ df[col] > 2, (df[col] <= 2) & (df[col]>= 0.5), df[col] <= 0.5 ] 63 | choices = [ -5000, 1000,0 ] 64 | df["grav_mod"] = np.select(conditions, choices, default=0) 65 | 66 | 67 | col = 'planet_stellar_masked' 68 | conditions = [ df[col] == 'total', df[col] == 'none', df[col] == 'partial' ] 69 | choices = [ -5000, 1000, 200] 70 | df["mask_mod"] = np.select(conditions, choices, default=0) 71 | 72 | 73 | col = 'atmos_composition' 74 | conditions = [ df[col] == 'Standard', df[col]=='Tainted',df[col]=='Corrosive'] 75 | choices = [ 50000, 10000,-1000 ] 76 | df["atmos_mod"] = np.select(conditions, choices, default=0) 77 | 78 | col = 'temperature' 79 | conditions = [ (df[col] <= 283) & (df[col]>238), 80 | (df[col] > 283) & (df[col] < 309), 81 | (df[col] >= 309) & (df[col] >= 324)] 82 | choices = [ 1000, 2000,1000 ] 83 | df["temp_mod"] = np.select(conditions, choices, default=-1000) 84 | df["temp_mod"] = df["temp_mod"] - (((df["temperature"] - 283) ** 2)/10) 85 | 86 | # print('after update') 87 | # print(df.describe()) 88 | df['mainworld_calc'] = df["wtype_mod"] + df["grav_mod"] + df["atmos_mod"] + df["temp_mod"] + df["mask_mod"] 89 | df['mainworld_status'] = 'N' 90 | 91 | df_mainworld_eval = df[['location', 92 | 'location_orbit', 93 | 'wtype_mod', 94 | 'grav_mod', 95 | 'atmos_mod', 96 | 'temp_mod', 97 | 'mainworld_calc', 98 | 'mainworld_status']] 99 | 100 | # print(df.head()) 101 | 102 | try: 103 | df_mainworld_eval.to_sql('main_world_eval', conn, if_exists='replace') 104 | except: 105 | print('Could not write back to sql') 106 | 107 | 108 | conn.commit() 109 | conn.close() 110 | -------------------------------------------------------------------------------- /mainworld_selector.py: -------------------------------------------------------------------------------- 1 | def choose_mainworld(db_name): 2 | #!/usr/bin/python 3 | 4 | # Mainworld Selector 5 | 6 | # The goal is to read the Orbital Bodies table generated from the 7 | # First In program and adjust by the Mainworld_Calc module 8 | 9 | # The output is a new column in the Orbital Bodies Table. It needs to run after First In and Mainworld_Calc 10 | 11 | 12 | 13 | import sqlite3 14 | import FreeSimpleGUI as sg 15 | 16 | import traceback 17 | import sys 18 | 19 | 20 | 21 | 22 | conn = sqlite3.connect(db_name) 23 | c = conn.cursor() 24 | 25 | sql3_select_loc = """ SELECT location 26 | FROM stellar_bodies 27 | WHERE orbits > 0""" 28 | 29 | sql3_select_locorb = """ SELECT location, 30 | location_orbit, 31 | mainworld_calc 32 | FROM main_world_eval 33 | WHERE location = ? """ 34 | 35 | sql3_insert_status = """ UPDATE main_world_eval 36 | SET mainworld_status = 'Y' 37 | WHERE location_orbit = ? """ 38 | 39 | loc_list = list() 40 | 41 | 42 | c.execute(sql3_select_loc) 43 | allrows = c.fetchall() 44 | for row in allrows: 45 | 46 | loc_list.append(row[0]) 47 | 48 | loc_list = set(loc_list) 49 | loc_len = len(loc_list) 50 | for j,n in enumerate(loc_list): 51 | 52 | sg.one_line_progress_meter('Universe Generation Underway', j+1, loc_len, 'System Count') 53 | 54 | 55 | c.execute(sql3_select_locorb,(n,)) 56 | allrows = c.fetchall() 57 | top_calc = -1000000 58 | top_locorb = 0 59 | for row in allrows: 60 | if row[2] > top_calc: 61 | top_calc = row[2] 62 | top_locorb = row[1] 63 | 64 | if top_calc >= -1000000: 65 | # print('Trying to write to DB') 66 | try: 67 | c.execute(sql3_insert_status,[top_locorb,]) 68 | conn.commit() 69 | except: 70 | print('Write failed',top_locorb) 71 | print('SQLite traceback: ') 72 | exc_type, exc_value, exc_tb = sys.exc_info() 73 | print(traceback.format_exception(exc_type, exc_value, exc_tb)) 74 | 75 | else: 76 | print('Location ERROR!!',top_calc,top_locorb) 77 | # 78 | 79 | 80 | conn.close() 81 | -------------------------------------------------------------------------------- /names.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/names.csv -------------------------------------------------------------------------------- /non_mw.py: -------------------------------------------------------------------------------- 1 | def generate_non_mainworlds(seed_number,db_name): 2 | 3 | 4 | # The goal is to read the Orbital Bodies table generated from the 5 | # First In program and and build a new table using Traveller 5 stats 6 | # Non Mainworld bodies. Mainworld bodies are produced in Travellerization 7 | 8 | 9 | 10 | import sqlite3 11 | import random 12 | import logging 13 | from traveller_functions import roll_dice 14 | 15 | random.seed(seed_number) 16 | 17 | 18 | def get_system_name(name_list): 19 | names_left = len(name_list) 20 | name_picked = name_list[random.randrange(0,names_left)] 21 | name_fixed = name_picked.rstrip('\n') 22 | name_list.remove(name_picked) 23 | return name_fixed 24 | 25 | 26 | def capture_mainworld_stats(): 27 | sql3_select_tb_t5 = """ SELECT t.location, 28 | t.population, 29 | t.government, 30 | g.wtype, 31 | g.zone 32 | FROM traveller_stats t 33 | LEFT JOIN orbital_bodies g 34 | WHERE main_world = 1 """ 35 | 36 | c.execute(sql3_select_tb_t5) 37 | allrows = c.fetchall() 38 | mw_dict = {} 39 | for row in allrows: 40 | mw_dict[row[0]] = { 'population' :row[1], 41 | 'government' :row[2], 42 | 'wtype' :row[3], 43 | 'zone' :row[4]} 44 | return mw_dict 45 | 46 | 47 | def get_population(location,mw): 48 | 49 | pop_present = True 50 | pop_mod = -2 51 | 52 | if mw['zone'] == 'Inner Zone': 53 | if 'Hostile' in mw['wtype'] or 'Greenhouse' in mw['wtype']: 54 | pop_present = False 55 | else: 56 | pop_mod = -4 57 | 58 | elif mw['zone'] == 'Outer Zone': 59 | if 'Hostile' in mw['wtype']: 60 | pop_present = False 61 | elif 'Icy' in mw['wtype']: 62 | pop_mod = -6 63 | 64 | 65 | 66 | if pop_present: 67 | dice = roll_dice(2,'Population with mod: '+str(pop_mod),location, conn, c) + pop_mod 68 | 69 | if dice >= mw['population']: 70 | dice = mw['population'] - 1 71 | 72 | if dice < 0: dice = 0 73 | 74 | else: 75 | dice = 0 76 | 77 | return dice 78 | 79 | def get_spaceport(location,population): 80 | 81 | if population == 0: 82 | spaceport = 'Y' 83 | else: 84 | dice = roll_dice(1,'Spaceport',location, conn, c) - population 85 | if dice >= 4: spaceport = 'F' 86 | elif dice == 3: spaceport = 'G' 87 | elif 1 <= dice <=2 : spaceport = 'H' 88 | else: spaceport = 'Y' 89 | 90 | 91 | return spaceport 92 | 93 | def get_atmosphere(pressure,composition): 94 | c_atmosphere = -1 95 | if pressure == 0: c_atmosphere = 0 96 | elif pressure == 0.1: c_atmosphere = 1 97 | elif composition == 'Exotic': c_atmosphere = 10 98 | elif composition == 'Corrosive': c_atmosphere = 11 99 | elif composition == 'GG': c_atmosphere = 1 100 | elif composition == 'Standard': 101 | if pressure < 0.5: c_atmosphere = 3 102 | elif pressure < 0.8: c_atmosphere = 5 103 | elif pressure < 1.2: c_atmosphere = 6 104 | elif pressure < 1.5: c_atmosphere = 8 105 | else: c_atmosphere = 13 106 | elif composition == 'Tainted': 107 | if pressure < 0.5: c_atmosphere = 2 108 | elif pressure < 0.8: c_atmosphere = 4 109 | elif pressure < 1.2: c_atmosphere = 7 110 | elif pressure < 1.5: c_atmosphere = 9 111 | else: c_atmosphere = 12 112 | 113 | return c_atmosphere 114 | 115 | 116 | 117 | def get_government(location, population, mw_government): 118 | if mw_government == '6': 119 | dice = 6 120 | else: 121 | dice = roll_dice(2,'Government',row[0], conn, c) + population - 7 122 | if dice < 0: dice = 0 123 | elif dice > 15: dice = 15 124 | if population == 0: dice = 0 125 | return dice 126 | 127 | def get_law_level(location, government): 128 | dice = roll_dice(2,'Law Level',row[0], conn, c) + government - 7 129 | if dice < 0: dice = 0 130 | elif dice > 15: dice = 15 131 | if population == 0: dice = 0 132 | return dice 133 | 134 | def get_tech_level(location, starport, size, atmosphere, hydrographics, population, government): 135 | 136 | starport_mod = -100 137 | starport_mod_dict = {'A':6, 'B':4, 'C':2, 'X':-4} 138 | if starport in starport_mod_dict.keys(): 139 | starport_mod = starport_mod_dict[starport] 140 | else: starport_mod = 0 141 | 142 | size_mod = -100 143 | size_mod_dict = {'0':2, '1':2, '2':1, '3':1, '4':1} 144 | if str(size) in size_mod_dict.keys(): 145 | size_mod = size_mod_dict[str(size)] 146 | else: size_mod = 0 147 | 148 | int_atmos = int(atmosphere) 149 | atmosphere_mod = -100 150 | if int_atmos <= 3: atmosphere_mod = 1 151 | elif int_atmos >= 10: atmosphere_mod = 1 152 | else: atmosphere_mod = 0 153 | 154 | hydro_mod = -100 155 | if hydrographics == 9: hydro_mod = 1 156 | elif hydrographics == 10: hydro_mod = 2 157 | else: hydro_mod = 0 158 | 159 | pop_mod = -100 160 | if population <= 5: pop_mod = 1 161 | elif population == 9: pop_mod = 2 162 | elif population >= 10: pop_mod = 4 163 | else: pop_mod = 1 164 | 165 | gov_mod = -100 166 | if government == 0: gov_mod = 1 167 | elif government == 5: gov_mod = 1 168 | elif government == 13: gov_mod = -2 169 | else: gov_mod = 0 170 | 171 | dice = roll_dice(1, 'Tech roll', location, conn, c) \ 172 | + starport_mod \ 173 | + size_mod \ 174 | + atmosphere_mod \ 175 | + hydro_mod \ 176 | + pop_mod \ 177 | + gov_mod 178 | # print ('Tech',starport,dice,starport_mod,size_mod,atmosphere_mod,hydro_mod,pop_mod,gov_mod) 179 | if population == 0: dice = 0 180 | return dice 181 | 182 | 183 | 184 | # MAIN PROGRAM 185 | 186 | print('Generating non-main worlds') 187 | print('processing....') 188 | 189 | name_list = open("names.csv", "r").readlines() 190 | 191 | conn = sqlite3.connect(db_name) 192 | c = conn.cursor() 193 | 194 | try: 195 | mw_dict = capture_mainworld_stats() 196 | except: 197 | logging.debug('Failed getting mainworld stats into dictionary') 198 | 199 | try: 200 | 201 | sql3_select_locorb = """ SELECT m.location_orbit, 202 | m.location, 203 | o.atmos_pressure, 204 | o.atmos_composition, 205 | o.body, 206 | o.hydrographics, 207 | o.size 208 | FROM main_world_eval m 209 | LEFT JOIN orbital_bodies o 210 | ON o.location_orbit = m.location_orbit 211 | WHERE mainworld_status != 'Y' """ 212 | 213 | c.execute(sql3_select_locorb) 214 | allrows = c.fetchall() 215 | except: 216 | logging.debug('Problem with selecting from main_world_eval and orbital bodies') 217 | 218 | for row in allrows: 219 | 220 | if row[4] != "Gas Giant": 221 | 222 | try: 223 | 224 | population = get_population(row[0],mw_dict[row[1]]) 225 | 226 | 227 | 228 | spaceport = get_spaceport(row[0],population) 229 | atmosphere = get_atmosphere(row[2],row[3]) 230 | hydrographics = row[5] 231 | size = row[6] 232 | 233 | government = get_government(row[1], population,mw_dict[row[1]]['government']) 234 | law_level = get_law_level(row[1], government) 235 | tech_level = get_tech_level(row[1], spaceport, size, atmosphere, hydrographics, population, government) 236 | 237 | 238 | except Exception as e: 239 | print(e) 240 | logging.debug('Failed in Non-mainworld generation') 241 | 242 | 243 | else: 244 | population = 0 245 | spaceport = 'Y' 246 | atmosphere = 15 247 | hydrographics = 0 248 | size = row[6] 249 | government = 0 250 | law_level = 0 251 | tech_level = 0 252 | 253 | 254 | main_world = 0 255 | 256 | 257 | 258 | 259 | system_name = get_system_name(name_list) 260 | 261 | 262 | sqlcommand = ''' INSERT INTO traveller_stats(location_orb, 263 | location, 264 | system_name, 265 | starport, 266 | size, 267 | atmosphere, 268 | hydrographics, 269 | population, 270 | government, 271 | law, 272 | tech_level, 273 | main_world) 274 | VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ''' 275 | 276 | 277 | body_row = (str(row[0]), 278 | str(row[1]), 279 | system_name, 280 | spaceport, 281 | size, 282 | atmosphere, 283 | hydrographics, 284 | population, 285 | government, 286 | law_level, 287 | tech_level, 288 | main_world) 289 | 290 | 291 | c.execute(sqlcommand, body_row) 292 | 293 | 294 | conn.commit() 295 | conn.close() -------------------------------------------------------------------------------- /routes_short_path.py: -------------------------------------------------------------------------------- 1 | def create_route_xml(seed_number,db_name, settlement_mod): 2 | 3 | # Create an xboat route table and extract for traveller map 4 | 5 | import sqlite3 6 | import pandas as pd 7 | import numpy as np 8 | import networkx as nx 9 | import warnings 10 | 11 | def offset_to_cube(location): 12 | x = int(location[0:2]) 13 | y = int(location[2:4]) 14 | q = x 15 | r = y - (q + (q&1)) / 2 16 | s = -q - r 17 | return(q,r,s) 18 | 19 | def cube_to_offset(location): 20 | x = location[0] 21 | y = int(location[1] + (x + (x&1)) / 2) 22 | if x > 9: 23 | x_string = str(x) 24 | else: 25 | x_string = '0' + str(x) 26 | if y > 9: 27 | y_string = str(y) 28 | else: 29 | y_string = '0' + str(y) 30 | 31 | return (x_string+y_string) 32 | 33 | 34 | def cube_direction(direction): 35 | return cube_direction_vectors[direction] 36 | 37 | def cube_add(cube, vec): 38 | return (cube[0] + vec[0], cube[1] + vec[1], cube[2] + vec[2]) 39 | 40 | def cube_neighbor(cube, direction): 41 | return cube_add(cube, cube_direction(direction)) 42 | 43 | 44 | def cube_subtract(a, b): 45 | return (a[0] - b[0], a[1] - b[1], a[2] - b[2]) 46 | 47 | 48 | def cube_distance(a, b): 49 | vec = cube_subtract(a, b) 50 | return (abs(vec[0]) + abs(vec[1]) + abs(vec[2])) / 2 51 | 52 | def jump_range(center,j_range): 53 | results = [] 54 | for q in range(-j_range,j_range+1): 55 | for r in range(-j_range,j_range+1): 56 | for s in range(-j_range,j_range+1): 57 | if q + r + s == 0: 58 | results.append(cube_add(center,[q, r, s])) 59 | return results 60 | 61 | 62 | def off_distance(o_start,o_end): 63 | try: 64 | c_start = offset_to_cube(o_start) 65 | except: 66 | print('C_start failed to convert to cube',o_start) 67 | 68 | 69 | try: 70 | c_end = offset_to_cube(o_end) 71 | except: 72 | print('C_end failed to convert to cube',o_end) 73 | 74 | 75 | return int(cube_distance(c_start,c_end)) 76 | 77 | 78 | 79 | ###################################################################### 80 | 81 | warnings.simplefilter(action='ignore', category=pd.errors.PerformanceWarning) 82 | warnings.simplefilter(action='ignore', category=FutureWarning) 83 | print('processing...') 84 | 85 | 86 | cube_direction_vectors = [ 87 | (+1, 0, -1), 88 | (+1, -1, 0), 89 | (0, -1, +1), 90 | (-1, 0, +1), 91 | (-1, +1, 0), 92 | (0, +1, -1) 93 | ] 94 | 95 | 96 | 97 | 98 | 99 | conn = sqlite3.connect(db_name) 100 | sql3_select = """ SELECT s.location, s.ix 101 | FROM system_stats s 102 | LEFT JOIN traveller_stats t 103 | ON t.location = s.location 104 | WHERE t.main_world = 1""" 105 | 106 | try: 107 | df = pd.read_sql_query(sql3_select,conn,index_col='location') 108 | 109 | 110 | except: 111 | print('Problem - df failed') 112 | 113 | df['ix'] = df['ix'].str.replace('{','') 114 | df['ix'] = df['ix'].str.replace('}','') 115 | df['ix'] = df['ix'].astype(int) 116 | 117 | df['ix_flag'] = np.where(df['ix'] >= 4,1,0) 118 | 119 | #df = df.loc[df['ix_flag'] == 1] 120 | 121 | l_location = list(df.index) 122 | 123 | for l in l_location: 124 | df[l] = df.index 125 | df[l] = df[l].apply(off_distance,args=(l,)) 126 | 127 | 128 | df_imp = df.loc[df['ix_flag'] == 1] 129 | l_important = list(df_imp.index) 130 | 131 | 132 | d_distance = df.to_dict('index') 133 | 134 | 135 | 136 | conn.commit() 137 | conn.close() 138 | ######################################################################## 139 | 140 | 141 | G = nx.Graph() 142 | 143 | elist = [] 144 | 145 | for loc in l_location: 146 | for loc2 in l_location: 147 | if loc < loc2: 148 | if d_distance[loc][loc2] <= 4: 149 | elist.append([loc,loc2]) 150 | 151 | 152 | G.add_edges_from(elist) 153 | 154 | 155 | path_list = [] 156 | used_list = [] 157 | #max_list = 8 158 | 159 | min_list_max = 4 160 | max_list_max = 20 161 | if settlement_mod == 1: 162 | max_list_max = 5 # reduces route connections in diminishing sectors 163 | 164 | for max_list in range(min_list_max,max_list_max): 165 | for loc in l_important: 166 | for loc2 in l_important: 167 | one_chain = [] 168 | if loc < loc2 and d_distance[loc][loc2] <= max_list: 169 | if max_list <= 6 or (loc not in used_list): 170 | used_list.append(loc) 171 | one_chain = nx.shortest_path(G,source=loc,target=loc2) 172 | for x,dest in enumerate(one_chain): 173 | if x < (len(one_chain)-1): 174 | path_list.append([dest,one_chain[x+1]]) 175 | 176 | 177 | route_text = '' 178 | 179 | for each in path_list: 180 | 181 | route_text += """ """ + '\n' 183 | 184 | 185 | important_text = '' 186 | 187 | for each in l_important: 188 | 189 | important_text += """ """ + '\n' 191 | 192 | 193 | 194 | 195 | 196 | file_name = db_name + '_routes.txt' 197 | with open(file_name, 'w') as f: 198 | f.write('' + '\n' \ 199 | + '' + '\n' \ 200 | + '' + db_name + '' + '\n' \ 201 | + '...' + '\n' \ 202 | + route_text 203 | + '' + '\n' \ 204 | + '' + '\n' \ 205 | + important_text 206 | + '' + '\n' \ 207 | + '') 208 | 209 | f.close() 210 | print('Routes complete') 211 | 212 | -------------------------------------------------------------------------------- /sector_db/example-66.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/sector_db/example-66.db -------------------------------------------------------------------------------- /sector_db/example-66.db_routes.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | sector_db/example-66.db 4 | ... 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | -------------------------------------------------------------------------------- /sector_db/example-66.db_tab.txt: -------------------------------------------------------------------------------- 1 | Hex Name UWP Remarks {Ix} (Ex) [Cx] Nobility Bases Zone PBG W Allegiance Stars 2 | 0101 Ma bar A6B4730-A Fl {+2} (569-4) [694D] B - - 101 4 Im M5 V M5 V 3 | 0102 Britten B7B7865-B Fl Ph {+2} (87D-2) [5A57] eB - - 806 8 Im K0 V 4 | 0103 Inaciolandia A7B1968-E Fl He Hi In {+4} (B89+0) [6D8F] fEB N - 613 7 Im K5 V 5 | 0104 Castelvetro di Mod B40099A-B Va Hi Na In {+4} (A8B+1) [5D36] fEB N - 303 6 Im M5 V 6 | 0106 Tabapua C200333-A Va Lo {+0} (921+4) [3369] B - - 214 7 Im M0 V 7 | 0108 Monmouth C100553-C Va Ni {+0} (B46+4) [254D] B - - 414 7 Im K5 V 8 | 0109 Kerekegyhaza CAA7455-7 Fl Ni {-2} (A31-1) [6279] B S - 704 7 Im M0 V 9 | 0110 Sashakrot B10067B-D Va Ni Na {+1} (E52-2) [674F] B S - 115 7 Im M5 V 10 | 0120 Marcolandia A200978-D Va Hi Na In {+4} (B89+0) [9D6A] fEB N - 905 8 Im K5 V 11 | 0121 Seminole C7B7369-7 Fl Lo {-2} (321+2) [1116] B S - 903 7 Im M0 V 12 | 0127 Santa Clarita C6A5586-7 Fl Ni Px {-2} (741+0) [1316] B S - 401 8 Im G5 V 13 | 0129 Vlijmen E300247-4 Va Lo {-3} (611-2) [2194] B - - 507 8 Im w D 14 | 0134 Limeil-Brevannes B200759-E Va Na Pi {+2} (F67+0) [B94F] DB N - 625 8 Im M0 V 15 | 0135 Mori C8B28CF-A Fl He Ph {+1} (C7A-4) [B945] eB S A 104 8 Im K5 V 16 | 0136 Ajlun C20037C-7 Va Lo {-2} (521-2) [1189] B - - 904 6 Im M5 V 17 | 0139 Werel D7B1526-5 Fl He Ni Px {-3} (640-2) [5285] B S - 813 8 Im G0 V 18 | 0140 Cabot A200646-E Va Ni Na {+1} (552+1) [378F] B - - 103 6 Im M5 V 19 | 0201 Guntramsdorf E975100-4 Lo {-3} (701+4) [1127] B - - 613 8 Im G0 V 20 | 0203 Deira B200775-D Va Na Pi {+2} (96C-1) [497E] DB S - 804 7 Im M0 V 21 | 0207 Alpignano D300566-8 Va Ni {-3} (D40-1) [12A7] B - - 904 7 Im M5 V 22 | 0209 Narvik AEB39A6-A Fl Hi In {+4} (D8E-1) [6D5F] fEB - - 213 8 Im K0 V 23 | 0216 Akimotohiro CDB5454-A Fl Ni {+0} (931-2) [245A] B - - 301 9 Im F0 V 24 | 0218 Magalang B200685-A Va Ni Na {+1} (F54+4) [376A] B - - 715 7 Im M5 V 25 | 0219 Doha E200112-4 Va Lo {-3} (301-1) [1113] B - - 304 6 Im M5 V 26 | 0221 Uptrix C200553-9 Va Ni {-1} (F42+1) [545C] B - - 206 8 Im M0 V 27 | 0237 Nova Olinda C2006AA-6 Va Ni Na {-2} (654+0) [8476] B S - 104 7 Im M5 V 28 | 0302 Featherstone ADB3CDF-A Fl Hi In {+5} (CBA+1) [EF96] fEB NS A 313 6 Im K5 V M5 V w D 29 | 0307 Newport C200598-8 Va Ni {-2} (B43-2) [5399] B S - 903 7 Im K5 V 30 | 0310 Neuenhof C200430-B Va Ni {+0} (C32-1) [2437] B - - 214 8 Im M0 V 31 | 0313 Kriftel E8A0000-0 He Ba {-3} (400+1) [0000] B - - 012 8 Im G5 V 32 | 0314 Cabries C6A058C-5 He Ni {-2} (940+2) [6351] B - - 210 4 Im K5 V M5 V 33 | 0315 Sarema B685856-8 Ga Ph Pa Ri {+1} (379+0) [6978] eCcB N - 800 5 Im F5 V M5 V 34 | 0324 Bartica C200564-7 Va Ni {-2} (A43+2) [7325] B - - 705 7 Im M5 V 35 | 0325 Dawn CBB1643-9 Fl He Ni {-1} (C51+4) [4557] B - - 313 8 Im K5 V 36 | 0327 Carthage CDB6885-9 Fl Ph {+0} (C75+1) [5838] eB - - 303 8 Im K0 V 37 | 0335 Fegyvernek A20089D-F Va Ph Na Pi {+2} (B7E+5) [9A4F] eDB N - 702 4 Im M5 V M5 V 38 | 0406 Badacsony B6A0885-9 He Ph {+1} (87A+1) [8974] eB N - 703 8 Im K0 V 39 | 0411 University Heights D6B4122-6 Fl Lo {-3} (801+1) [4145] B S - 615 7 Im M5 V 40 | 0418 Nezhdanova DAB4366-5 Fl Lo Px {-3} (821+0) [3112] B S - 703 7 Im M0 V 41 | 0421 Teng C7A7599-5 Fl Ni {-2} (744+5) [4384] B - - 303 8 Im K0 V 42 | 0430 Georg C6A6536-5 Fl Ni {-2} (C42-5) [7311] B - - 400 5 Im G0 V M5 V 43 | 0431 Ataco A000955-F As Va Hi Na In {+4} (788+0) [8D5B] fEB - - 200 3 Im M0 V w D 44 | 0433 Mwene-Ditu D20047B-4 Va Ni {-3} (630+2) [3124] B S - 805 8 Im M0 V 45 | 0437 Cavenago di Brianz A20068B-B Va Ni Na {+1} (A56-1) [7788] B N - 706 7 Im M0 V 46 | 0440 Talsi D200465-4 Va Ni {-3} (530+3) [3148] B - - 914 7 Im M0 V 47 | 0501 Sao Vicente do Sul X8B8100-1 Fl Lo {-3} (801+0) [5161] B - R 300 4 Im K5 V K5 V 48 | 0508 Torit D5A0122-8 He Lo {-3} (B01+0) [6186] B S - 503 5 Im M0 V M5 V 49 | 0511 Von Flue C200576-8 Va Ni {-2} (A40+3) [A329] B S - 400 3 Im M5 V M5 V 50 | 0521 Fremantle C200554-A Va Ni {+0} (F41-2) [659D] B - - 508 8 Im w D 51 | 0522 Datian C7A2556-B Fl He Ni Px {+0} (843-1) [256D] B - - 703 9 Im F5 V 52 | 0534 Omolon C6A3272-9 Fl Lo {-1} (911+1) [1127] B - - 914 8 Im K5 V 53 | 0535 Pavlovskiy Posad C200422-6 Va Ni {-2} (C32-1) [1277] B - - 706 8 Im M0 V 54 | 0537 Novikov B9A686A-7 Fl Ph {+0} (67B+0) [9885] eB - - 804 7 Im M0 V 55 | 0538 Waltenhofen C200362-B Va Lo {+0} (B21-1) [2369] B S - 505 7 Im M0 V 56 | 0607 Grand Rapids BAB5655-A Fl Ni {+1} (B52+2) [976B] B N - 504 8 Im M0 V 57 | 0608 Salina E95359A-7 Ni Po {-3} (741-2) [7246] B - - 913 9 Im F0 V 58 | 0609 Ernest EBB6415-6 Fl Ni {-3} (830+2) [1143] B - - 100 4 Im K0 V M5 V 59 | 0615 Sparta C76A485-6 Wa Ni {-2} (433+0) [2217] B - - 304 8 Im K5 V 60 | 0616 Petite-Rosselle A000AAB-F As Va Hi Na In {+4} (C9E+0) [8E1F] fEB N - 515 7 Im w D 61 | 0618 Manzar A200665-B Va Ni Na {+1} (C57+2) [2779] B - - 615 6 Im M5 V 62 | 0620 Ivanka B769697-C Ni Ri {+2} (955+2) [6827] CB S - 914 8 Im K0 V 63 | 0623 Mordrus B7A6852-9 Fl Ph {+2} (678+2) [8A34] eB NS - 403 7 Im M0 V 64 | 0626 Baarle-Nassau A9A4877-A Fl Ph {+2} (B79+4) [6A8D] eB - - 601 3 Im M0 V M5 V 65 | 0634 Bouabout A200898-D Va Ph Na Pi {+2} (C79-1) [9A7C] eDB S - 804 8 Im G0 V 66 | 0636 Jinzhou B30078A-A Va Na Pi {+2} (A6B+1) [A927] DB S - 505 7 Im M0 V 67 | 0639 Oued Naanaa D200305-A Va Lo {-1} (B21+0) [22AC] B S - 605 8 Im K5 V 68 | 0701 Mendillo B200653-D Va Ni Na {+1} (D54-3) [973B] B - - 603 8 Im G5 V 69 | 0702 Ettlingen C200204-7 Va Lo {-2} (611+3) [2116] B S - 515 8 Im M0 V 70 | 0703 Tarrafal AAB1887-D Fl He Ph {+2} (977+2) [7A4C] eB - - 603 9 Im F0 V 71 | 0705 Lida A4107A7-F Na Pi {+2} (A69+1) [599D] DB N - 504 7 Im K5 V 72 | 0709 Tinipuka D864514-4 Ni Ag Pr {-2} (643+4) [7344] CcB S - 700 3 Im K0 V K5 V 73 | 0711 Essex BAB2640-B Fl He Ni {+1} (B52-2) [872B] B - - 212 9 Im F0 V 74 | 0714 Liuba CCB0410-7 He Ni {-2} (630-1) [5268] B - - 703 8 Im K5 V 75 | 0721 Tarendo A200753-B Va Na Pi {+2} (869-1) [B94A] DB - - 704 7 Im M0 V 76 | 0725 Maltahohe C200355-B Va Lo {+0} (921+2) [535C] B - - 505 7 Im M5 V 77 | 0728 Ruteng CBB5322-9 Fl Lo {-1} (A21-2) [321B] B - - 203 8 Im G0 V 78 | 0729 Nasarawa C300521-B Va Ni {+0} (343+0) [9579] B S - 410 4 Im G0 III M5 III 79 | 0733 Birchington C8A1438-A Fl He Ni Px {+0} (734+0) [343B] B S - 300 4 Im K5 V K5 V 80 | 0737 Watari C969467-9 Ni {-1} (A34-2) [133A] B S - 413 8 Im G5 V 81 | 0738 Dolero CAB2577-8 Fl He Ni Px {-2} (B40+1) [3359] B - - 602 7 Im K5 V 82 | 0801 Bloomington A8A2676-C Fl He Ni Px {+1} (D55-2) [471A] B N - 313 8 Im K5 V 83 | 0802 Actopan D867340-2 Ga Lo {-3} (821+0) [3141] B S - 511 9 Im F0 V 84 | 0807 Vawkavysk C200100-B Va Lo {+0} (B01-3) [112D] B - - 405 8 Im M0 V 85 | 0808 Greeley E200331-7 Va Lo {-3} (521+0) [3167] B - - 405 8 Im M0 V 86 | 0809 Refuge C881576-8 Ni Pr {-2} (840+4) [6358] cB - - 900 3 Im G5 V w D 87 | 0812 Niemeyer C8A76A5-8 Fl Ni {-2} (C51+0) [3467] B S - 704 7 Im M0 V 88 | 0815 Sao Mateus do Mara C200377-A Va Lo {+0} (E21-1) [132B] B - - 306 8 Im K5 V 89 | 0816 Salpeter B2007AB-D Va Na Pi {+2} (C69-2) [694E] DB S - 406 8 Im K5 V 90 | 0817 Bauschinger C200559-8 Va Ni {-2} (F40-4) [538C] B - - 104 6 Im M5 V 91 | 0818 Sollenberger B6B7768-7 Fl {+0} (A65-4) [4778] B - - 603 8 Im K5 V 92 | 0819 Mbanza Kongo E5B3310-8 Fl Lo {-3} (821+3) [1183] B - - 601 4 Im M0 V M5 V 93 | 0820 Hiwasa C843555-6 Ni Po {-2} (843+3) [5387] B - - 713 9 Im F5 V 94 | 0821 Bartolini E510200-8 Lo {-3} (F11+2) [411B] B - - 203 6 Im M5 V 95 | 0823 Colindres B8B4664-7 Fl Ni {-1} (855-3) [6547] B - - 913 7 Im K5 V 96 | 0826 Millman C200796-8 Va Na Pi {-1} (665-3) [C61D] DB S - 804 8 Im G5 V 97 | 0828 Ilmenau A200873-D Va Ph Na Pi {+2} (F7D+2) [BA5A] eDB N - 906 7 Im M5 V 98 | 0832 Stur C6B9485-B Fl Ni {+0} (D31-2) [349B] B S - 305 7 Im M0 V 99 | 0834 Balingoan C8A0416-5 He Ni {-2} (434+0) [3245] B S - 802 8 Im G5 V 100 | 0840 Dabu B6A4767-B Fl {+2} (A6C-3) [794D] B - - 213 8 Im G0 V 101 | 0901 Koronadal C200468-6 Va Ni {-2} (730-3) [5263] B S - 213 7 Im M0 V 102 | 0905 Deikoon A200EDB-E Va Hi Na In {+4} (8DE+3) [EF3F] fEB - - 504 7 Im M0 V 103 | 0907 Inhambupe C866566-8 Ga Ni Ag Pr {-1} (C43-3) [7435] CcB S - 201 5 Im K5 V M5 V 104 | 0909 Temascaltepec de G B9B3865-7 Fl Ph {+0} (57A-2) [3856] eB - - 712 5 Im M0 V M5 V 105 | 0919 Alpicat B5A6669-A Fl Ni {+1} (B53+0) [877D] B - - 104 6 Im M5 V 106 | 0920 Santa Fe DBB0646-7 He Ni {-3} (652+2) [7368] B - - 223 9 Im F5 V 107 | 0921 Lake C200345-B Va Lo {+0} (921+1) [1397] B - - 805 8 Im M0 V 108 | 0922 Beringovskiy B7479BD-A Hi In {+4} (E8D+2) [9D57] fEB - - 603 8 Im K0 V 109 | 0924 Bedwas C8B3575-7 Fl Ni {-2} (641+3) [3378] B - - 911 6 Im M5 V w D w D 110 | 0928 London C653550-8 Ni Po {-2} (B40+1) [5318] B - - 304 8 Im G5 V 111 | 0935 Copiague B200747-A Va Na Pi {+2} (F69+2) [2947] DB - - 504 4 Im w D w D 112 | 0938 Tarim B8C2896-8 Fl He Ph {+0} (D77+1) [984B] eB - - 413 9 Im F0 V 113 | 1003 Zephyrhills C200566-B Va Ni {+0} (F41-1) [756E] B S - 905 7 Im M0 V 114 | 1006 Cassis D200120-7 Va Lo {-3} (401+3) [1165] B - - 304 6 Im M5 V 115 | 1010 Bryan CDB2344-7 Fl Lo {-2} (421-3) [218A] B S - 503 9 Im F5 V 116 | 1013 Ross E6A9110-5 Fl Lo {-3} (701+1) [1166] B - - 114 7 Im M0 V 117 | 1014 Norwalk A7A56B9-A Fl Ni Px {+1} (D53+2) [272A] B N - 503 9 Im F0 V 118 | 1019 Stowbtsy C200366-6 Va Lo {-2} (A21-1) [1147] B - - 703 7 Im M0 V 119 | 1023 Monte Alegre de Mi C200200-C Va Lo {+0} (911-3) [527D] B - - 202 4 Im M0 V M5 V 120 | 1025 Eldersburg C778110-4 Lo {-2} (A01+1) [2113] B S - 713 8 Im K5 V 121 | 1027 Weeze DBB6451-4 Fl Ni {-3} (730+4) [1186] B S - 922 9 Im F0 V 122 | 1028 Tyrnyauz C000233-C As Va Lo {+0} (C11+0) [222F] B S - 924 8 Im G5 V 123 | 1029 Itanhomi A200996-E Va Hi Na In {+4} (989-3) [6D2F] fEB - - 104 7 Im K5 V 124 | 1033 Oulad Chikh B2009CD-C Va Hi Na In {+4} (D88-1) [6D8B] fEB - - 308 8 Im w D 125 | 1034 San Angelo C6A68CA-7 Fl Ph {-1} (677-1) [7768] eB - - 323 9 Im F5 V 126 | 1039 South Beloit C663267-9 Lo {-1} (C11+0) [3117] B - - 700 4 Im G0 V G5 V 127 | 1040 Catunda A000663-F As Va Ni Na {+1} (A53-1) [577E] B S - 210 4 Im K5 V w D 128 | 1105 La Sarre C300587-8 Va Ni {-2} (A40-1) [439B] B S - 303 4 Im w D w D 129 | 1108 Upton B300688-9 Va Ni Na {+0} (951+5) [267A] B - - 504 7 Im M0 V 130 | 1111 Chililabombwe A86799E-E Ga Hi Pr {+3} (98B+0) [6C1A] EcB - - 703 8 Im K0 V 131 | 1113 Neulengbach C200440-9 Va Ni {-1} (E32-2) [233C] B - - 614 7 Im M5 V 132 | 1114 Lutzen C6B3264-8 Fl Lo {-2} (811+1) [318D] B - - 510 8 Im M0 III 133 | 1118 Liski A100696-E Va Ni Na {+1} (C56+0) [578A] B - - 204 6 Im M5 V 134 | 1119 Leyla DEB2100-5 Fl Lo {-3} (801-3) [1145] B - - 303 8 Im G5 V 135 | 1120 Omachi D200520-5 Va Ni {-3} (642-5) [6275] B - - 907 7 Im w D 136 | 1126 Babi Ngepet E200235-7 Va Lo {-3} (A11-2) [4169] B - - 606 8 Im M0 V 137 | 1132 Touggourt B200694-9 Va Ni Na {+0} (B55+2) [4656] B S - 513 8 Im G5 V 138 | 1135 Tsuda CAB458B-7 Fl Ni {-2} (940-2) [239A] B S - 413 8 Im G0 V 139 | 1136 Erfurt C200415-B Va Ni {+0} (F31+1) [346C] B - - 424 8 Im K5 V 140 | 1137 Whirl B7B4855-D Fl Ph {+2} (D7A+1) [BA3F] eB S - 413 8 Im G5 V 141 | 1201 Nalut C9A2765-6 Fl He {-1} (765+1) [A6A5] B - - 414 9 Im F0 V 142 | 1209 Lianzhou C7B2512-8 Fl He Ni {-2} (844+1) [2336] B - - 804 8 Im K0 V 143 | 1211 Media B51077A-C Na Pi {+2} (766-4) [595B] DB - - 503 7 Im M0 V 144 | 1212 Bauta C200535-9 Va Ni {-1} (F43+1) [6435] B - - 205 7 Im M0 V 145 | 1213 Ha Long B5A57A6-9 Fl {+1} (F68+3) [685B] B - - 715 8 Im M0 V 146 | 1218 Morgantown B20068B-A Va Ni Na {+1} (A57+2) [374E] B S - 604 6 Im M5 V 147 | 1221 Antigonish C200512-9 Va Ni {-1} (744+2) [544A] B S - 900 3 Im F0 V F5 V 148 | 1223 Reggio Emilia C9A5421-6 Fl Ni {-2} (630+3) [4259] B S - 605 8 Im K0 V 149 | 1225 Despujols E6A0330-5 He Lo {-3} (421-4) [2131] B - - 912 9 Im A5 V 150 | 1232 Siegsdorf B200656-E Va Ni Na {+1} (B55-3) [87AC] B - - 104 7 Im M0 V 151 | 1234 Clarkston D782338-7 Lo {-3} (721+1) [1178] B - - 204 8 Im G0 V 152 | 1301 Oleksandrivsk B200658-A Va Ni Na {+1} (F53+3) [475B] B S - 907 7 Im w D 153 | 1302 Baishaling EBB3100-4 Fl Lo {-3} (301+3) [11A4] B - - 704 8 Im K5 V 154 | 1309 Santa Teresinha C200588-6 Va Ni {-2} (A42-3) [4358] B - - 705 6 Im M5 V 155 | 1312 Lecointe C7B1646-A Fl He Ni Px {+0} (B51+1) [4667] B - - 702 8 Im G0 V 156 | 1316 Silves C000166-A As Va Lo {+0} (A01-4) [111B] B - - 913 7 Im K5 V 157 | 1325 Chudniv B8B1656-C Fl He Ni Px {+1} (F57+0) [9789] B - - 604 8 Im K5 V 158 | 1326 Manfredonia C20035A-B Va Lo {+0} (D21-2) [133A] B - - 707 8 Im M0 V 159 | 1328 Hockessin B8A3785-A Fl {+2} (967+1) [794A] B S - 722 9 Im F0 V 160 | 1329 Brampton E8A0300-6 He Lo {-3} (421-3) [1145] B - - 402 8 Im K0 V 161 | 1331 Tzecmaun X200000-0 Va Ba {-3} (900+0) [0000] B - R 007 7 Im w D 162 | 1335 Toots A200745-D Va Na Pi {+2} (C66+1) [A91E] DB - - 404 6 Im M5 V 163 | 1405 Galliera Veneta B7A7648-A Fl Ni {+1} (B53-2) [774A] B N - 304 8 Im K5 V 164 | 1408 Kingston C993541-8 Ni {-2} (940-1) [5368] B S - 802 9 Im F0 V 165 | 1411 Warren C000335-C As Va Lo {+0} (921+4) [635C] B - - 714 8 Im K5 V 166 | 1414 Huercal-Overa B4B36BD-9 Fl Ni {+0} (E54+2) [B62A] B - - 406 7 Im w D 167 | 1418 Crao A200758-E Va Na Pi {+2} (E66+1) [497E] DB N - 504 6 Im M5 V 168 | 1419 Murchie A9D1875-D Ph {+2} (B7B-4) [DA3B] eB S - 700 3 Im A0 V A5 V 169 | 1423 Sundaiga C200404-9 Va Ni {-1} (931+0) [5357] B - - 204 6 Im M5 V 170 | 1425 Guilford C20038B-A Va Lo {+0} (D21-1) [3369] B S - 504 7 Im M0 V 171 | 1431 Espenak A200987-C Va Hi Na In {+4} (B8C-1) [4D6C] fEB - - 514 8 Im K5 V 172 | 1432 Xiuyanyu E7A2224-3 Fl He Lo {-3} (811+1) [4126] B - - 800 4 Im G5 V M5 V 173 | 1433 Lenti D200446-9 Va Ni {-2} (E30-5) [427A] B S - 906 7 Im M5 V 174 | 1434 Setvak CBB3640-6 Fl Ni {-2} (654-2) [8494] B S - 604 8 Im K0 V 175 | 1437 Bismark E510167-7 Lo {-3} (801+0) [1168] B - - 100 2 Im w D w D 176 | 1438 Baroy C6A7534-8 Fl Ni {-2} (644+5) [6368] B S - 812 8 Im K0 V 177 | 1506 Burgos B6858BB-B Ga Ph Pa Ri {+3} (A7C-1) [6B6C] eCcB N - 112 8 Im G5 V 178 | 1509 Talne A200ACB-D Va Hi Na In {+4} (A9A+1) [7E5E] fEB S - 805 8 Im K5 V 179 | 1510 Simiti E200455-7 Va Ni {-3} (C31-3) [4159] B - - 205 7 Im M0 V 180 | 1515 Pauls Valley C20077A-8 Va Na Pi {-1} (E65+1) [663C] DB S - 704 6 Im M5 V 181 | 1516 Baishan C200599-A Va Ni {+0} (F43+2) [8538] B - - 204 8 Im G5 V 182 | 1517 Laubach B00089D-A As Va Ph Na Pi {+2} (E7A+1) [8A35] eDB S - 225 8 Im K5 V 183 | 1518 Bambari EDB3238-8 Fl Lo {-3} (B11+0) [115A] B - - 704 8 Im K0 V 184 | 1525 Brikama C2008BF-B Va Ph Na Pi {+1} (C7A+3) [694A] eDB - A 406 7 Im w D 185 | 1533 Cruzeta A200689-B Va Ni Na {+1} (B52+2) [B73A] B - - 113 5 Im M0 V M5 V 186 | 1534 Karinthy A510785-B Na Pi {+2} (76B+2) [A97E] DB - - 600 4 Im M0 V M5 V 187 | 1537 Madler C8A8476-7 Fl Ni {-2} (733+1) [7275] B - - 504 8 Im K5 V 188 | 1538 Douglas C200458-9 Va Ni {-1} (D30+0) [4367] B S - 905 7 Im M0 V 189 | 1540 Pelham C200411-A Va Ni {+0} (F36-3) [1459] B S - 825 8 Im M0 V 190 | 1601 Pourkaviani C100410-8 Va Ni {-2} (A31-1) [8258] B S - 305 6 Im M5 V 191 | 1602 Joao Lisboa D20065A-8 Va Ni Na {-3} (F50+2) [8358] B - - 104 7 Im M0 V 192 | 1606 Manzanillo E200330-7 Va Lo {-3} (821+0) [7157] B - - 903 7 Im M0 V 193 | 1610 Florida Ridge C5A0556-6 He Ni {-2} (A42-1) [4335] B S - 100 3 Im K5 V M5 V 194 | 1614 Sarospatak C66637A-4 Ga Lo {-2} (421-4) [5132] B S - 515 7 Im M5 V 195 | 1615 Monroe C8BA77C-A Fl {+1} (66B+0) [484B] B - - 503 7 Im M0 V 196 | 1618 Bni Khloug E200165-4 Va Lo {-3} (801+1) [1136] B - - 104 7 Im M5 V 197 | 1620 San Francisco B9B4640-C Fl Ni {+2} (A56+2) [B85B] B NS - 703 8 Im K5 V 198 | 1621 Chicomendez DCB3424-3 Fl Ni {-3} (731+2) [1134] B S - 503 7 Im K5 V 199 | 1622 Fagnes DAB5572-5 Fl Ni {-3} (442+2) [A23A] B - - 802 6 Im M5 V 200 | 1625 Europaeus AAB3874-9 Fl Ph {+1} (577+0) [89A5] eB - - 702 8 Im K0 V 201 | 1626 Leeds D100338-7 Va Lo {-3} (421-1) [1194] B S - 904 6 Im M5 V 202 | 1627 Fossett A200987-F Va Hi Na In {+4} (A8B-4) [9D4F] fEB S - 114 8 Im G0 V 203 | 1632 Wagman B7A1673-B Fl He Ni {+1} (753+4) [578E] B N - 702 8 Im G0 V 204 | 1634 Krugersdorp B6B68A9-7 Fl Ph {+0} (378+1) [6874] eB N - 213 8 Im G0 V 205 | 1635 Mourmelon-le-Grand D2006A8-6 Va Ni Na {-3} (851-2) [1328] B - - 304 6 Im M5 V 206 | 1636 Baleno B881656-B Ni Ri {+2} (753-1) [888A] CB S - 703 9 Im F0 V 207 | 1706 Magenta C200521-9 Va Ni {-1} (740+1) [442A] B - - 703 6 Im M0 V M5 V M5 V 208 | 1709 Pirapozinho C300244-B Va Lo {+0} (E11+2) [1229] B - - 606 7 Im w D 209 | 1711 Terlingen C200420-7 Va Ni {-2} (733+2) [523A] B - - 804 6 Im M5 V 210 | 1713 Konopleva C100534-C Va Ni {+0} (744+3) [555A] B - - 304 7 Im w D 211 | 1718 Passo do Sertao D200612-6 Va Ni Na {-3} (550+4) [63A1] B S - 504 7 Im M0 V 212 | 1720 Ishim CCB5566-7 Fl Ni Px {-2} (541+1) [534A] B S - 604 8 Im K0 V 213 | 1722 Chingola C200734-6 Va Na Pi {-1} (862+0) [667A] DB - - 904 7 Im M0 V 214 | 1726 Bellach C6A489A-8 Fl Ph {-1} (873+5) [671A] eB S - 602 9 Im G0 V 215 | 1730 Oenone EAB4259-8 Fl Lo {-3} (C11-1) [2169] B - - 603 8 Im G5 V 216 | 1732 Yanbol A766984-D Ga Hi Pr {+3} (88A-2) [AC4F] EcB N - 302 9 Im A5 V 217 | 1801 Blumenthal E773352-6 Lo {-3} (521-4) [1156] B - - 104 8 Im K0 V 218 | 1803 Kunitachi C9A4514-6 Fl Ni {-2} (A41+0) [4374] B S - 904 8 Im K0 V 219 | 1804 Ridgewood C200413-6 Va Ni {-2} (830+0) [525A] B S - 804 7 Im M0 V 220 | 1807 Arcos de la Fronte B7A8664-C Fl Ni {+1} (953+2) [672E] B - - 103 8 Im K0 V 221 | 1809 Hamilton ACB5897-9 Fl Ph {+1} (F77+4) [7919] eB N - 914 8 Im G5 V 222 | 1812 Akranes B300699-9 Va Ni Na {+0} (E54-4) [8629] B - - 214 8 Im K0 V 223 | 1813 Chernolesskoye A5D88A7-C Ph {+2} (67A-1) [7A4C] eB - - 300 3 Im G0 V K5 V 224 | 1817 Nieuwenhagen A200736-E Va Na Pi {+2} (C68-3) [792F] DB N - 825 7 Im M5 V 225 | 1823 Tenango del Aire D9A5427-6 Fl Ni Px {-3} (630+1) [8146] B S - 423 8 Im G5 V 226 | 1825 Portimao C20058B-A Va Ni {+0} (C46+2) [45A5] B S - 305 7 Im M0 V 227 | 1828 Tricomi D200564-7 Va Ni {-3} (740+0) [6259] B S - 906 8 Im K5 V 228 | 1830 Venissieux A2007BC-B Va Na Pi {+2} (C67-3) [9969] DB - - 605 7 Im M5 V 229 | 1833 Maron C7A0663-A He Ni {+0} (A53+0) [465C] B S - 210 5 Im G5 V w D 230 | 1835 Glasgow A200976-D Va Hi Na In {+4} (F8F-2) [8D2B] fEB - - 408 8 Im w D 231 | 1840 Bakum C7B6510-7 Fl Ni {-2} (840+1) [4368] B S - 405 8 Im K0 V 232 | 1901 Photographica B200655-E Va Ni Na {+1} (953+3) [971F] B - - 304 7 Im M0 V 233 | 1906 Irig E200000-0 Va Ba {-3} (200+1) [0000] B - - 004 4 Im w D w D 234 | 1907 Sobrance C200300-A Va Lo {+0} (E21-3) [337A] B - - 608 8 Im w D 235 | 1910 Truckee C6A5114-5 Fl Lo {-2} (B01-1) [4124] B - - 705 8 Im M0 V 236 | 1913 Yamada B8A3885-9 Fl Ph {+1} (F78-1) [4915] eB N - 623 8 Im K0 V 237 | 1914 Trzebnica A6A0610-E He Ni {+1} (853+2) [475F] B - - 311 8 Im G5 V 238 | 1916 Sao Jose de Uba D200325-4 Va Lo {-3} (821-1) [3154] B S - 107 8 Im M0 V 239 | 1918 Oberwil A886955-F Ga Hi Pr {+3} (B8B+3) [6C2F] EcB - - 403 9 Im F5 V 240 | 1921 Avanhandava C510203-7 Lo {-2} (A11+3) [2187] B - - 901 4 Im M5 V M5 V 241 | 1923 Parksville CBB55A8-A Fl Ni Px {+0} (945+2) [656C] B - - 403 8 Im G5 V 242 | 1924 Belo Campo A200659-D Va Ni Na {+1} (E55+3) [372A] B - - 324 8 Im G0 V 243 | 1930 Christineli B200747-C Va Na Pi {+2} (F66+0) [B999] DB - - 308 8 Im w D 244 | 1931 Epicles AAB89BB-A Fl Hi In {+5} (F8C+0) [5E69] fEB NS - 104 7 Im M0 V 245 | 1934 Dzoavits A2007A7-C Va Na Pi {+2} (E69-3) [3938] DB - - 715 8 Im M0 V 246 | 1935 San Gregorio Atzom C200479-A Va Ni {+0} (C31+0) [5489] B - - 204 6 Im M5 V 247 | 1936 Amphios D200410-4 Va Ni {-3} (930+1) [4185] B - - 902 5 Im K5 V M5 V 248 | 1937 Mcvittie B8B5665-8 Fl Ni {+0} (A51+0) [864B] B NS - 604 7 Im M0 V 249 | 1938 Hirai C200565-7 Va Ni {-2} (A40+5) [7327] B S - 304 8 Im M0 V 250 | 2006 Uthumphon Phisai C7B8532-5 Fl Ni {-2} (543+1) [2316] B - - 302 6 Im M5 V 251 | 2012 Owerri E200441-7 Va Ni {-3} (331+3) [7133] B - - 104 8 Im K0 V 252 | 2015 Goianinha D1006AE-A Va Ni Na {-1} (D51+4) [952F] B S - 506 8 Im w D 253 | 2016 Kamoun C200520-A Va Ni {+0} (D44-5) [5585] B - - 805 7 Im M0 V 254 | 2019 Krautter E7B2466-5 Fl He Ni Px {-3} (930+2) [5117] B - - 700 4 Im M0 V M5 V 255 | 2024 Carllewis A20078B-A Va Na Pi {+2} (D67-2) [2947] DB - - 206 8 Im M0 V 256 | 2029 Polyphont A100610-D Va Ni Na {+1} (653+3) [872B] B N - 201 3 Im M5 V M5 V 257 | 2035 Milton Keynes C6A7458-6 Fl Ni {-2} (631+2) [3241] B S - 504 7 Im M0 V 258 | 2036 Vigia CBB7330-A Fl Lo {+0} (C21+5) [234D] B - - 205 8 Im K5 V 259 | 2037 Iitti BBB5661-9 Fl Ni {+0} (A52+0) [6636] B N - 604 8 Im K0 V 260 | 2107 Cristina C200488-6 Va Ni {-2} (532-2) [6252] B S - 502 7 Im M0 V 261 | 2109 Stafford BAB5741-A Fl {+2} (A68-2) [4949] B - - 305 8 Im K0 V 262 | 2110 Perols CBB3669-9 Fl Ni Px {-1} (853+3) [454C] B - - 902 9 Im F5 V 263 | 2112 Gloor C200553-8 Va Ni {-2} (F40-3) [533A] B - - 404 7 Im M0 V 264 | 2113 Bourgeois C200103-C Va Lo {+0} (E01-1) [111F] B - - 315 7 Im w D 265 | 2116 Bonnyrigg A20069B-D Va Ni Na {+1} (952-1) [578D] B - - 614 6 Im M5 V 266 | 2123 Asp D200333-7 Va Lo {-3} (521-5) [5147] B - - 200 3 Im M0 V M5 V 267 | 2126 Friedafein D200100-8 Va Lo {-3} (D01-1) [1168] B - - 607 7 Im w D 268 | 2131 Beni Fouda C662467-5 Ni {-2} (732+3) [1218] B - - 603 8 Im K0 V 269 | 2132 Liberato Salzano C100223-7 Va Lo {-2} (411-2) [2189] B S - 106 7 Im w D 270 | 2133 Ipiranga C200335-9 Va Lo {-1} (F21+3) [7219] B - - 314 6 Im M5 V 271 | 2135 Waterford ABB0EEE-D He Hi In {+4} (6DD-4) [FF2E] fEB - A 704 7 Im K5 V 272 | 2136 Jerome B200751-D Va Na Pi {+2} (966-2) [694C] DB - - 702 4 Im M5 V M5 V 273 | 2138 Gilgit CBB2369-5 Fl He Lo Px {-2} (721-1) [1166] B - - 700 4 Im M0 V M5 V 274 | 2201 Goppingen A200625-A Va Ni Na {+1} (A54-1) [5728] B S - 606 7 Im w D 275 | 2204 Petaluma E7A0202-5 He Lo {-3} (711+1) [1146] B - - 223 9 Im F5 V 276 | 2207 Clarke C6A2643-A Fl He Ni {+0} (B53+1) [7667] B - - 912 8 Im G0 V 277 | 2209 Dreyfus A000656-E As Va Ni Na {+1} (954+4) [678E] B S - 702 4 Im M5 V w D 278 | 2212 Holger C6A1100-8 Fl He Lo {-2} (D01+2) [1156] B - - 205 8 Im K0 V 279 | 2214 Argentan D962205-3 Lo {-3} (611+0) [4155] B - - 702 9 Im F0 V 280 | 2217 Agramunt C200557-A Va Ni {+0} (F45-2) [7537] B - - 315 8 Im M0 V 281 | 2218 Spilamberto C200785-6 Va Na Pi {-1} (361-4) [7664] DB - - 904 7 Im M0 V 282 | 2220 Rochefort A200863-C Va Ph Na Pi {+2} (57D-1) [AA1C] eDB N - 600 2 Im M5 V M5 V 283 | 2223 Flammario B7A2662-8 Fl He Ni {-1} (A50-3) [5547] B N - 913 9 Im G0 V 284 | 2224 Shantix B100656-A Va Ni Na {+1} (C53+0) [275B] B S - 114 6 Im M5 V 285 | 2226 Commerson C300479-A Va Ni {+0} (A34+4) [6488] B - - 103 4 Im M5 V M5 V 286 | 2229 Rye B7A09B8-B He Hi In {+4} (D88+0) [BD8C] fEB - - 922 9 Im F0 V 287 | 2230 Itaguara C300558-8 Va Ni {-2} (D43-1) [6329] B - - 504 7 Im M0 V 288 | 2234 Aberdeen C6A7121-7 Fl Lo {-2} (501-4) [4127] B - - 103 8 Im K0 V 289 | 2236 Hilden B2008CA-8 Va Ph Na Pi {+0} (97C+0) [8835] eDB N - 804 7 Im M0 V 290 | 2237 Oulad Tayeb C9A5200-7 Fl Lo {-2} (511+1) [1156] B - - 104 8 Im K0 V 291 | 2307 Youngtown C9B5420-5 Fl Ni {-2} (C31-1) [2237] B S - 613 9 Im G0 V 292 | 2309 Marcin A100977-E Va Hi Na In {+4} (F8C-4) [BD3E] fEB - - 805 6 Im M5 V 293 | 2311 Cliffside Park ECB5421-6 Fl Ni {-3} (530-1) [4166] B - - 900 5 Im F5 V K5 V 294 | 2312 Shapleya B200777-D Va Na Pi {+2} (A68-2) [A98C] DB S - 214 9 Im G0 V 295 | 2319 Frasso C200340-9 Va Lo {-1} (A21-2) [7297] B S - 904 6 Im M5 V 296 | 2325 Heek C200520-A Va Ni {+0} (B41+0) [659B] B - - 513 9 Im F0 V 297 | 2326 Baidoa C200432-7 Va Ni {-2} (B34+1) [7224] B S - 700 4 Im G0 V M5 V 298 | 2327 Misratah B200677-A Va Ni Na {+1} (853-1) [4789] B N - 704 7 Im M0 V 299 | 2331 Hifleischer CAB2510-6 Fl He Ni {-2} (843+0) [231B] B - - 304 8 Im G5 V 300 | 2332 Rybalka CAB4533-9 Fl Ni {-1} (B40+4) [6478] B S - 604 8 Im K0 V 301 | 2335 Camp Hill E785336-5 Ga Lo {-3} (721+1) [8181] B - - 514 8 Im K5 V 302 | 2337 Acailandia D200234-6 Va Lo {-3} (611+0) [5143] B - - 504 6 Im M5 V 303 | 2339 Tabuk C200788-7 Va Na Pi {-1} (666-3) [8694] DB - - 714 7 Im M0 V 304 | 2401 Asheboro C200343-A Va Lo {+0} (D21-5) [131B] B S - 804 7 Im M0 V 305 | 2404 Uzhhorod C7A6231-8 Fl Lo {-2} (C11-2) [314A] B S - 113 8 Im K0 V 306 | 2406 Arruda dos Vinhos B10077B-C Va Na Pi {+2} (D66-1) [593F] DB - - 605 6 Im M5 V 307 | 2409 Bridge City B100746-C Va Na Pi {+2} (D65-1) [3957] DB - - 905 7 Im M5 V 308 | 2411 Penarroya-Pueblonu CDB0641-5 Ni {-2} (651+1) [9487] B S - 304 8 Im K0 V 309 | 2412 Gamovo C200120-B Va Lo {+0} (D01+0) [412A] B - - 904 7 Im M0 V 310 | 2415 Saint-Michel-Chef- C200202-9 Va Lo {-1} (A11+3) [115A] B S - 814 6 Im M5 V 311 | 2420 Cee A300FDE-D Va Hi Na In {+4} (FE7+1) [FF5B] fEB S A 505 8 Im w D 312 | 2422 Marlene C892340-7 He Lo {-2} (421+1) [5178] B - - 600 2 Im K5 V M5 V K5 V 313 | 2424 Ouderkerk aan den CAB4579-9 Fl Ni Px {-1} (C42-3) [848C] B - - 402 8 Im G0 V 314 | 2428 Cristopolis C797322-7 Lo {-2} (821+1) [4157] B - - 410 4 Im F0 V F5 V 315 | 2429 Bitias C200424-7 Va Ni {-2} (534-2) [5279] B - - 606 7 Im M5 V 316 | 2436 Lebofsky B200888-8 Va Ph Na Pi {+0} (F72+4) [9869] eDB - - 807 7 Im w D 317 | 2504 Panying C78A6AA-7 Wa Ni Ri {-1} (B55+5) [6557] CB - - 604 8 Im G5 V 318 | 2505 Piqua D200320-8 Va Lo {-3} (A21-2) [613D] B S - 305 8 Im M0 V 319 | 2510 Dietramszell X8B5000-0 Fl Ba {-3} (600-5) [0000] B - R 004 7 Im M5 V 320 | 2511 Fresno B400948-B Va Hi Na In {+4} (88E+1) [6D3E] fEB N - 301 6 Im M5 V 321 | 2513 South Portland C7A4431-9 Fl Ni {-1} (935-2) [435C] B - - 713 8 Im G5 V 322 | 2518 Orpheus DBB4557-7 Fl Ni Px {-3} (743+2) [5235] B - - 903 7 Im K5 V 323 | 2519 Samashki D510485-5 Ni {-3} (630+2) [3151] B S - 804 6 Im M5 V 324 | 2523 Penalva do Castelo A760A83-E De Hi {+3} (A99+1) [BD2B] EB - - 903 9 Im F5 V 325 | 2525 Neuilly-sur-Seine A8A279D-B Fl He {+2} (566-2) [992A] B N - 203 8 Im G5 V 326 | 2528 Heubach C200520-6 Va Ni {-2} (740-1) [4335] B - - 602 9 Im G0 V 327 | 2530 Bad Camberg A2009CC-E Va Hi Na In {+4} (B8B+0) [BD7F] fEB - - 825 8 Im M0 V 328 | 2531 Khadyzhensk C000000-0 As Va Ba {-2} (700+0) [0000] B - - 001 3 Im M5 V M5 V 329 | 2532 Westville B000766-D As Va Na Pi {+2} (667-3) [89AC] DB - - 912 9 Im F0 V 330 | 2533 Gregory A200833-D Va Ph Na Pi {+2} (F7B-1) [5A99] eDB N - 915 8 Im M0 V 331 | 2536 Greci D200354-7 Va Lo {-3} (621-1) [81AC] B - - 107 8 Im w D 332 | 2601 Ulla A20079D-E Va Na Pi {+2} (A67+2) [895F] DB - - 704 6 Im M5 V 333 | 2604 Oulad Yaich C200430-9 Va Ni {-1} (F31+3) [4336] B - - 305 8 Im M0 V 334 | 2612 Kakuda C200311-7 Va Lo {-2} (A21-1) [5188] B S - 204 8 Im M0 V 335 | 2617 Fukayacho C76A222-A Wa Lo {+0} (511+1) [326E] B - - 410 4 Im G0 V M5 V 336 | 2624 Ducrosa A631675-D Ni Na Po {+1} (A53+5) [775D] B - - 704 8 Im K5 V 337 | 2625 Pagegiai A2008AC-A Va Ph Na Pi {+2} (E77-1) [BA87] eDB - - 324 8 Im K0 V 338 | 2626 Taggia B200AA6-C Va Hi Na In {+4} (C9A-2) [FE59] fEB - - 303 7 Im M0 V 339 | 2630 Graceleanor C300457-B Va Ni {+0} (A33+1) [6429] B S - 414 6 Im M5 V 340 | 2633 Zapotlan de Juarez C5A7562-7 Fl Ni {-2} (342+0) [431B] B S - 803 7 Im K5 V 341 | 2634 Tasiusaq B200865-A Va Ph Na Pi {+2} (D7B+0) [8A77] eDB - - 804 7 Im M0 V 342 | 2635 Africa D200242-9 Va Lo {-2} (A11-4) [418A] B S - 703 6 Im M5 V 343 | 2638 Junction City A859988-D Hi {+3} (78C-2) [EC5F] EB N - 801 5 Im K0 V M5 V 344 | 2702 Soledad C200586-6 Va Ni {-2} (B40-5) [A312] B - - 106 8 Im M0 V 345 | 2706 Tha Mai A2009DF-C Va Hi Na In {+4} (F88-3) [CD2E] fEB - A 315 7 Im M5 V 346 | 2708 Glennville B200640-C Va Ni Na {+1} (754+3) [472B] B N - 804 6 Im M5 V 347 | 2710 Brecon C4A6576-9 Fl Ni {-1} (B45+2) [949A] B - - 902 10 Im A0 V 348 | 2711 Cambria C200202-C Va Lo {+0} (D11+1) [229B] B - - 615 8 Im M0 V 349 | 2716 Bintulu EAB0366-8 He Lo {-3} (F21-3) [2143] B - - 107 8 Im w D 350 | 2717 Careiro C200456-B Va Ni {+0} (A35+0) [448F] B S - 100 1 Im M0 V M5 V 351 | 2720 Port Orchard C200776-A Va Na Pi {+1} (868-3) [B86E] DB - - 900 1 Im K5 V K5 V 352 | 2721 Allington A200656-C Va Ni Na {+2} (C56-2) [6887] B NS - 415 8 Im K5 V 353 | 2727 Akihiro A89A88B-B Wa Ph Pi {+2} (979+2) [5A6D] eDB - - 610 9 Im M0 III 354 | 2730 Hard E200245-6 Va Lo {-3} (411+1) [1135] B - - 805 7 Im M5 V 355 | 2732 Mans CAB5441-8 Fl Ni {-2} (C31+3) [8267] B S - 913 7 Im K5 V 356 | 2736 Nordlingen B863610-7 Ni Ri {+0} (752+0) [7682] CB S - 612 9 Im A5 V 357 | 2737 San Pietro Vernoti C6A5510-9 Fl Ni {-1} (741+2) [243B] B S - 901 4 Im M0 V M5 V 358 | 2738 Goncharova B8B3753-B Fl {+2} (B6D-1) [6919] B N - 804 7 Im M0 V 359 | 2807 Kirkham C200214-6 Va Lo {-2} (B11-2) [616A] B - - 904 7 Im M5 V 360 | 2809 Argentona C5A3343-A Fl Lo {+0} (821-3) [134A] B S - 911 9 Im F5 V 361 | 2810 Werkendam C200675-6 Va Ni Na {-2} (752-3) [4455] B S - 803 6 Im M5 V 362 | 2814 Grote D6A4331-7 Fl Lo {-3} (821+4) [1156] B - - 304 8 Im K0 V 363 | 2817 Cuyo C200000-0 Va Ba {-2} (700-3) [0000] B S - 007 8 Im w D 364 | 2823 Allauch E5A0446-5 He Ni {-3} (930+3) [1125] B - - 103 8 Im G5 V 365 | 2825 Sakurano C100458-D Va Ni {+0} (D32-2) [647F] B S - 905 6 Im M5 V 366 | 2826 Donggou CDB2411-8 Fl Ni {-2} (D34-1) [4279] B S - 204 8 Im K5 V 367 | 2827 Pino D200533-7 Va Ni {-3} (340-2) [8238] B S - 204 7 Im M0 V 368 | 2830 Santa Mariana C7B4321-9 Fl Lo {-1} (821-1) [522A] B S - 313 7 Im K5 V 369 | 2833 Gaya C200000-0 Va Ba {-2} (900+1) [0000] B - - 014 7 Im w D 370 | 2834 Dahart B100840-B Va Ph Na Pi {+2} (A75-2) [3A4B] eDB N - 114 6 Im M5 V 371 | 2836 Meiganga C7C4498-A Fl Ni {+0} (336+0) [7459] B - - 500 5 Im G5 V K5 V 372 | 2837 Casinhas C963466-4 Ni {-2} (434-1) [32A4] B S - 414 8 Im K0 V 373 | 2839 Valday B2008CC-A Va Ph Na Pi {+2} (875+2) [6A9D] eDB S - 104 7 Im M5 V 374 | 2901 Florencia B8B0799-8 He {+0} (A65+0) [A716] B - - 913 9 Im F5 V 375 | 2903 Muzambinho A200945-B Va Hi Na In {+4} (A88+1) [5D3E] fEB N - 906 8 Im M0 V 376 | 2904 Canto do Buriti C200123-7 Va Lo {-2} (501+2) [314C] B - - 215 8 Im K5 V 377 | 2905 New Romney E6A2230-3 Fl He Lo {-3} (611-2) [4121] B - - 302 7 Im M0 V 378 | 2919 Audincourt C200544-8 Va Ni {-2} (B41-4) [735A] B - - 415 7 Im M0 V 379 | 2920 Reno C200686-A Va Ni Na {+0} (C51+3) [667A] B - - 715 8 Im K5 V 380 | 2921 Pidhorodne C200354-7 Va Lo {-2} (621+0) [2176] B - - 204 6 Im M5 V 381 | 2922 Volodarsk C100323-B Va Lo {+0} (421+3) [436D] B - - 801 2 Im M5 V M5 V 382 | 2929 Zychlin A200DCE-F Va Hi Na In {+4} (DCC+2) [8F4F] fEB N A 606 8 Im M0 V 383 | 2934 Cupcini B200877-8 Va Ph Na Pi {+0} (B76-4) [9894] eDB - - 104 7 Im M0 V 384 | 2936 Midland Park E8A4100-8 Fl Lo {-3} (801+2) [4198] B - - 703 8 Im K0 V 385 | 2938 Wyoming CBB5452-9 Fl Ni {-1} (A32+3) [433A] B S - 504 8 Im G0 V 386 | 2940 Votuporanga D685547-3 Ga Ni Ag Pr {-2} (442+0) [5391] CcB S - 914 8 Im K0 V 387 | 3001 Mrirt B865643-9 Ga Ni Ag Ri {+2} (A57+3) [A81D] CB - - 904 8 Im K5 V 388 | 3002 Orland Park C5A1536-7 Fl He Ni Px {-2} (840+0) [A345] B - - 804 6 Im M5 V 389 | 3003 Krasnyy Sulin E200611-5 Va Ni Na {-3} (952+3) [8345] B - - 707 8 Im w D 390 | 3004 Dijon B7779AB-9 Hi In {+3} (98B-2) [BC89] EB - - 814 8 Im K0 V 391 | 3005 Katy A7A4621-9 Fl Ni {+0} (655+0) [A62C] B N - 904 8 Im K0 V 392 | 3011 Lessek E200100-A Va Lo {-1} (901-4) [1157] B - - 705 6 Im M5 V 393 | 3014 Sopelana C200522-6 Va Ni {-2} (744+0) [4355] B S - 104 6 Im M5 V 394 | 3015 Fagioli B0009AF-D As Va Hi Na In {+5} (88C-3) [AE59] fEB NS - 712 6 Im M5 V 395 | 3020 Manchester C745312-5 Lo {-2} (521+4) [8165] B S - 613 8 Im G0 V 396 | 3025 Campanha A9456AA-D Ni Ag {+2} (F55+4) [887C] CB - - 322 8 Im G5 V 397 | 3031 Ixtlahuaca B000625-9 As Va Ni Na {+0} (A54-4) [8657] B - - 122 8 Im G0 V 398 | 3037 Irshansk X000348-2 As Va Lo {-3} (A21+3) [1153] B - R 214 7 Im M0 V 399 | 3105 Bulboaca B20079D-8 Va Na Pi {+1} (C67+1) [7884] DB NS - 714 9 Im F5 V 400 | 3111 Ajdovscina C200576-8 Va Ni {-2} (840+0) [4364] B - - 105 7 Im M5 V 401 | 3113 Clermont-l Herault BCB6853-C Fl Ph {+2} (278-4) [3A19] eB S - 900 4 Im K0 V M5 V 402 | 3116 Sitrian B656999-8 Ga Hi {+1} (C8A-3) [7A24] EB - - 403 9 Im G0 V 403 | 3117 Dubois ABB6999-D Fl Hi In {+4} (98C+2) [AD7C] fEB N - 303 8 Im K5 V 404 | 3119 Immokalee C7A4664-A Fl Ni {+0} (853+0) [4659] B S - 110 5 Im K0 V K5 V 405 | 3121 Agrat Bat Mahlat A8A4637-A Fl Ni Px {+1} (E52-3) [9768] B - - 803 8 Im K0 V 406 | 3123 Waldems A000858-F As Va Ph Na Pi {+2} (D7C+0) [5A6F] eDB N - 614 8 Im K5 V 407 | 3125 Hirbovat C200320-6 Va Lo {-2} (621+0) [2126] B - - 502 4 Im M5 V M5 V 408 | 3127 Kath B300899-8 Va Ph Na Pi {+0} (979+3) [8815] eDB - - 600 4 Im M0 V M5 V 409 | 3130 Lunca Cetatuii C200540-8 Va Ni {-2} (843+3) [534A] B S - 504 8 Im G5 V 410 | 3133 Guabiruba C200510-6 Va Ni {-2} (844+3) [2383] B - - 107 7 Im w D 411 | 3135 Kryl A200864-A Va Ph Na Pi {+3} (E78+0) [9B68] eDB NS - 505 8 Im M0 V 412 | 3137 Nele E200204-7 Va Lo {-3} (A11+1) [2177] B - - 815 6 Im M5 V 413 | 3138 Hall in Tirol A2009CD-F Va Hi Na In {+4} (F8B+0) [AD6F] fEB - - 506 8 Im K5 V 414 | 3139 Elsenfeld D200532-6 Va Ni {-3} (840+0) [622A] B - - 604 7 Im M0 V 415 | 3201 Tennyo D200323-9 Va Lo {-2} (F21-2) [316A] B S - 807 8 Im w D 416 | 3202 Kratchmarov D200576-6 Va Ni {-3} (943+5) [6263] B S - 603 6 Im M5 V 417 | 3203 Sardinata A2009A9-E Va Hi Na In {+4} (B8E-3) [9D9E] fEB - - 314 7 Im M0 V 418 | 3204 Kircheva E6A0132-7 He Lo {-3} (801-2) [1143] B - - 623 7 Im K5 V 419 | 3205 Spring Lake Park C200466-6 Va Ni {-2} (932-4) [2247] B - - 504 8 Im K5 V 420 | 3208 Nkob C200405-9 Va Ni {-1} (931+1) [1358] B S - 604 6 Im M5 V 421 | 3215 Caldas de Montbuy A000945-D As Va Hi Na In {+4} (E8D+2) [8D3B] fEB - - 424 7 Im M0 V 422 | 3217 Roja C2005A7-8 Va Ni {-2} (E43+5) [3359] B - - 705 7 Im M5 V 423 | 3218 Channahon C790449-4 De He Ni {-2} (730-3) [1265] B - - 303 8 Im G5 V 424 | 3219 Valadon C200541-9 Va Ni {-1} (F44+1) [845C] B - - 307 7 Im w D 425 | 3220 Chippewa Falls C200301-9 Va Lo {-1} (D21+0) [4256] B - - 603 6 Im M5 V 426 | 3223 Buhlertal E100300-9 Va Lo {-2} (921-2) [111A] B - - 304 6 Im M5 V 427 | 3225 Leicester E100200-A Va Lo {-1} (D11-2) [117A] B - - 405 6 Im M5 V 428 | 3228 Arbutus C8A8255-B Fl Lo {+0} (A11+3) [223C] B - - 603 8 Im K5 V 429 | 3231 Santa Maria C9A1164-9 Fl He Lo {-1} (A01+1) [512A] B S - 804 9 Im G0 V 430 | 3233 Hagonoy C5A0556-6 He Ni {-2} (844-2) [2335] B S - 805 7 Im M0 V 431 | 3239 Monett B200755-9 Va Na Pi {+1} (C6A-3) [A869] DB - - 515 7 Im M0 V 432 | -------------------------------------------------------------------------------- /sector_db/traveller_map_poster.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
8 | Choose the sector title.db_tab.txt file 9 | 10 | Choose the appropriate title.db_routes.txt file 11 | 12 | 13 |
14 |

15 | 16 | -------------------------------------------------------------------------------- /splash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/splash.jpg -------------------------------------------------------------------------------- /splash_browser.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/splash_browser.jpg -------------------------------------------------------------------------------- /sunburst.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartlebythecoder/traveller-universe-creator/d421bf2971851a3866e4cf563ab681540656eafd/sunburst.ico -------------------------------------------------------------------------------- /tables/Orbital Eccentricity Table.txt: -------------------------------------------------------------------------------- 1 | 3,0.05 2 | 4,0.1 3 | 5,0.2 4 | 6,0.3 5 | 7,0.4 6 | 8,0.4 7 | 9,0.5 8 | 10,0.5 9 | 11,0.5 10 | 12,0.6 11 | 13,0.6 12 | 14,0.7 13 | 15,0.7 14 | 16,0.8 15 | 17,0.9 16 | 18,0.95 -------------------------------------------------------------------------------- /tables/Orbital Separation Table.txt: -------------------------------------------------------------------------------- 1 | 6,Very Close,0.05 2 | 9,Close,0.5 3 | 11,Moderate,2 4 | 14,Wide,10 5 | 15,Distant,50 -------------------------------------------------------------------------------- /tables/Planet Density Table.txt: -------------------------------------------------------------------------------- 1 | 3,3.2,2.3 2 | 5,4.4,1.6 3 | 8,5.3,1.8 4 | 1000,5.9,1.9 -------------------------------------------------------------------------------- /tables/Star Characteristics III.txt: -------------------------------------------------------------------------------- 1 | B5,15000,1800,7.0,0.058,0.0 2 | A0,9500,106,4.0,0.035,0.2 3 | A5,8200,43,2.5,0.030,0.6 4 | F0,7200,20,1.5,0.027,3.0 5 | F5,6400,17,1.0,0.031,10 6 | G0,6000,34,1.0,0.050,10 7 | G5,5800,43,1.1,0.060,7.5 8 | K0,5300,60,1.1,0.085,7.5 9 | K5,4400,220,1.2,0.24,5.8 10 | M0,3900,330,1.2,0.37,5.8 11 | M5,3200,930,1.4,0.92,3.6 -------------------------------------------------------------------------------- /tables/Star Characteristics V.txt: -------------------------------------------------------------------------------- 1 | O5,45000,790000,30,0.14,0.0 2 | B5,15000,830,5.4,0.040,0.1 3 | A0,9500,54,2.7,0.025,0.5 4 | A5,8200,14,1.9,0.017,1.4 5 | F0,7200,6.5,1.6,0.015,2.5 6 | F5,6400,2.9,1.3,0.013,4.5 7 | G0,6000,1.5,1.1,0.011,7.3 8 | G5,5800,0.79,0.94,0.0082,12 9 | K0,5300,0.42,0.81,0.0071,19 10 | K5,4400,0.15,0.62,0.0062,41 11 | M0,3900,0.063,0.50,0.0051,79 12 | M5,3200,0.011,0.21,0.0032,190 -------------------------------------------------------------------------------- /tables/World Type Table.txt: -------------------------------------------------------------------------------- 1 | Large,Hostile (SG),Hostile (SG),Hostile (SG)*,Hostile (SG)*, Lost 2 | Standard,Greenhouse,Ocean*,Hostile (N)*,Hostile (A)*,Lost 3 | Small,Desert,Desert*,Desert*,Hostile (A)*,Lost 4 | Very Small,Rockball,Rockball,Rockball,Icy Rockball,Lost 5 | Tiny,Rockball,Rockball,Rockball,Icy Rockball,Lost 6 | Gas Giant,Gas Giant,Gas Giant,Gas Giant,Gas Giant,Lost 7 | Belt,Belt,Belt,Belt,Belt,Lost 8 | Lost,Lost,Lost,Lost,Lost,Lost -------------------------------------------------------------------------------- /test_export_def.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon May 13 13:14:48 2024 4 | 5 | @author: sean 6 | """ 7 | from export_sector import export_sector 8 | 9 | db = 'sector_db/example-66.db' 10 | ss = 'A' 11 | 12 | export_sector(db, ss) -------------------------------------------------------------------------------- /trade_goods.csv: -------------------------------------------------------------------------------- 1 | Accountings 2 | Adhesives 3 | Aged Meats 4 | Allotropes 5 | Alloys 6 | Anagathics 7 | ANIFX Blocker 8 | ANIFX Dyes 9 | ANIFX Emitters 10 | Antibiotics 11 | Antidotes 12 | Antifungals 13 | Anti-Matter 14 | Antique Art 15 | Antiques 16 | Antiseptics 17 | Antitoxins 18 | Antivirals 19 | Archeologicals 20 | Armor 21 | Aromatics 22 | Art 23 | Artifacts 24 | Attractants 25 | Aware Blockers 26 | Awareness Pinger 27 | Backups 28 | Biologics 29 | Branded Clothes 30 | Branded Devices 31 | Branded Drinks 32 | Branded Foods 33 | Branded Oxygen 34 | Branded Tools 35 | Branded Vacc Suits 36 | Bulk Abrasives 37 | Bulk Carbon 38 | Bulk Carbs 39 | Bulk Copper 40 | Bulk Dusts 41 | Bulk Ephemerals 42 | Bulk Fats 43 | Bulk Fibers 44 | Bulk Foodstuffs 45 | Bulk Gases 46 | Bulk Herbs 47 | Bulk Ices 48 | Bulk Iron 49 | Bulk Metals 50 | Bulk Minerals 51 | Bulk Nitrates 52 | Bulk Nutrients 53 | Bulk Organics 54 | Bulk Oxygen 55 | Bulk Particulates 56 | Bulk Pelts 57 | Bulk Petros 58 | Bulk Pharma 59 | Bulk Precipitates 60 | Bulk Protein 61 | Bulk Spices 62 | Bulk Synthetics 63 | Bulk Textiles 64 | Bulk Woods 65 | Candies 66 | Carbons 67 | Catalysts 68 | Chelates 69 | Coinage 70 | Cold Light Blocks 71 | Cold Sleep Pills 72 | Cold Welders 73 | Collectible Books 74 | Collectibles 75 | Combat Drug 76 | Combination 77 | Contemplatives 78 | Corrosives 79 | Counter-prions 80 | Crafted Devices 81 | Cryo Alloys 82 | Cryogems 83 | Currency 84 | Databases 85 | Decoctions 86 | Decorations 87 | Delicacies 88 | Disposables 89 | Dominants 90 | Drinkable Lymphs 91 | Dupe Masterpieces 92 | Echostones 93 | Educationals 94 | Edutainments 95 | Electronics 96 | Emotion Lighting 97 | Encapsulants 98 | Envirosuits 99 | Ephemerals 100 | Excretions 101 | Exotic Aromatics 102 | Exotic Crystals 103 | Exotic Fauna 104 | Exotic Flora 105 | Exotic Fluids 106 | Exotic Sauces 107 | Expert Systems 108 | Famous Wafers 109 | Fast Drug 110 | Fauna 111 | Fermented Fluids 112 | Filter Mask 113 | Fine Aromatics 114 | Fine Art 115 | Fine Carpets 116 | Fine Dusts 117 | Fine Furs 118 | Fission Suppressant 119 | Flavored Air 120 | Flavored Drinks 121 | Flavored Waters 122 | Flavorings 123 | Flill 124 | Flora 125 | Flowers 126 | Fluidic Timepieces 127 | Fluidics 128 | Foodstuffs 129 | Fossils 130 | Fruit Delicacies 131 | Fused Metals 132 | Gallium 133 | Gemstones 134 | Germanes 135 | Gold 136 | Gravitics 137 | Group Symbols 138 | Hats 139 | Health Foods 140 | Heat Pumps 141 | Holo Sculpture 142 | Holo-Companions 143 | Hummingsand 144 | Ices 145 | Improvements 146 | Incenses 147 | Incomprehensibles 148 | Insidiants 149 | Insulants 150 | Iridium Sponge 151 | IR Emitters 152 | Iridescents 153 | Isotopes 154 | Jewelry 155 | Juices 156 | Lanthanum 157 | Lek Emitters 158 | Light-Sensitives 159 | Livestock 160 | Luminescents 161 | Mag Emitters 162 | Magnetics 163 | Mandates 164 | Masterpieces 165 | Meat Delicacies 166 | Mechanicals 167 | Meson Barriers 168 | Metals 169 | Minerals 170 | Money Cards 171 | Monumental Art 172 | Motile Plants 173 | Museum Items 174 | Music 175 | Musical Instruments 176 | Navigators 177 | Nectars 178 | Noisemakers 179 | Non-Fossil Carcasses 180 | Nostrums 181 | Novel Flavorings 182 | Nutraceuticals 183 | Obsoletes 184 | Ores 185 | Organic Gems 186 | Organic Polymers 187 | Osmancies 188 | Painkillers 189 | Palliatives 190 | Panacea 191 | Parts 192 | Pattern Creators 193 | Pelts 194 | Percept Blockers 195 | Pheromones 196 | Photonics 197 | Pigments 198 | Platinum 199 | Plutonium 200 | Polymer Sheets 201 | Polymers 202 | Pseudomones 203 | Radioactive Ores 204 | Radioactives 205 | Radium 206 | Rare Minerals 207 | Raw Sensings 208 | Reactive Plants 209 | Reactive Woods 210 | Reclamation Suits 211 | Recordings 212 | Regulations 213 | Reparables 214 | Replicating Clays 215 | Repulsant 216 | Respirators 217 | Restoratives 218 | Robots 219 | Secretions 220 | Seedstock 221 | Self-Defenders 222 | Self-Solving Puzzles 223 | ShimmerCloth 224 | Silanes 225 | Silver 226 | Skin Tones 227 | Slow Drug 228 | Sludges 229 | Software 230 | Soothants 231 | Sophont Cuisine 232 | Sophont Hats 233 | Soundmakers 234 | Sparx 235 | Spices 236 | Stimulants 237 | Strange Crystals 238 | Strange Seeds 239 | Synchronizations 240 | Tactiles 241 | Textiles 242 | Thorium 243 | Tisanes 244 | Unusual Dusts 245 | Unusual Fluids 246 | Unusual Ices 247 | Unusual Minerals 248 | Unusual Rocks 249 | Upgrades 250 | Uranium 251 | Used Goods 252 | Vacc Gems 253 | Vacc Suit Patches 254 | Vacc Suit Scents 255 | Variable Tattoos 256 | VHDUS Blocker 257 | VHDUS Dyes 258 | VHDUS Emitters 259 | Vision Suppressant 260 | Wafers 261 | Warm Leather 262 | Weapons 263 | Wines 264 | Writings 265 | -------------------------------------------------------------------------------- /traveller_functions.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Dec 2 16:35:43 2021 4 | 5 | v 1.1.0e 2024-05-24 Added error variables to debug log in try/excepts 6 | 7 | 8 | @author: sean 9 | """ 10 | 11 | import random 12 | import logging 13 | import requests 14 | 15 | 16 | # used for images on browser and export 17 | def get_remarks_list(): 18 | remarks_list = [['In', 'industrial'], 19 | ['Ag', 'agricultural'], 20 | ['Hi', 'hipop'], 21 | ['Lo', 'lopop'], 22 | ['Ba', 'barren'], 23 | ['Na', 'non_agricultural'], 24 | ['Ni', 'non_industrial'], 25 | ['Px', 'prison']] 26 | return remarks_list 27 | 28 | def integer_root(expo,num): 29 | num = float(num) 30 | root_expo = 1/expo 31 | return float(num ** root_expo) 32 | 33 | def tohex(dec): 34 | hex_digits = "0123456789ABCDEFGHJ" 35 | if dec > 18: 36 | dec = 18 37 | return hex_digits[dec % 17] 38 | 39 | 40 | def hex_to_int(hex_val): 41 | response = hex_val 42 | try: 43 | hex_list = ['A','B','C','D','E','F','G','H'] 44 | hex_dict = {'H': 17, 45 | 'G': 16, 46 | 'F': 15, 47 | 'E': 14, 48 | 'D': 13, 49 | 'C': 12, 50 | 'B': 11, 51 | 'A': 10} 52 | if hex_val in hex_list: response = int(hex_dict[hex_val]) 53 | else: response = int(response) 54 | return response 55 | except Exception as e: 56 | logging.debug(f'failed hex_to_int with {hex_val} {e}') 57 | 58 | def cx_values(cx): 59 | het_no = hex_to_int(cx[0]) 60 | acc_no = hex_to_int(cx[1]) 61 | sta_no = hex_to_int(cx[2]) 62 | sym_no = hex_to_int(cx[3]) 63 | return (het_no,acc_no,sta_no,sym_no) 64 | 65 | 66 | def roll_dice(no_dice, why, location, conn, c): 67 | 68 | 69 | no_dice_loop = no_dice + 1 #increment by one for the FOR loop 70 | sum_dice = 0 71 | for dice_loop in range (1,no_dice_loop): 72 | sum_dice = sum_dice + random.randrange(1,7) 73 | 74 | c.execute("INSERT INTO die_rolls (location, number, reason, total) VALUES(?, ?, ?, ?)", 75 | (str(location), 76 | no_dice, 77 | why, 78 | sum_dice)) 79 | 80 | return sum_dice 81 | 82 | def get_description(upp_type,upp_value): 83 | description = '' 84 | if upp_type == 'starport': 85 | dy_upp = {'A': 'Excellent', 86 | 'B': 'Good', 87 | 'C': 'Routine', 88 | 'D': 'Poor', 89 | 'E': 'Frontier', 90 | 'F': 'Spaceport - Good', 91 | 'G': 'Spaceport - Poor', 92 | 'H': 'Spaceport - Basic', 93 | 'X': 'None', 94 | 'Y': 'None'} 95 | elif upp_type == 'remarks': 96 | dy_upp = {'Oc': 'Ocean World', 97 | 'Va': 'Vacuum', 98 | 'Wa': 'Water World', 99 | 'Ba': 'Barren', 100 | 'Di': 'Dieback', 101 | 'Lo': 'Low Population', 102 | 'Hi': 'Hi Population', 103 | 'Ni': 'Non-Industrial', 104 | 'Ph': 'Pre-High Population', 105 | 'Pa': 'Pre-Agricultural', 106 | 'Ag': 'Agricultural', 107 | 'Na': 'Non-Agrictultural', 108 | 'Px': 'Prison or Exile Camp', 109 | 'Pi': 'Pre-Industrial', 110 | 'In': 'Industrial', 111 | 'Po': 'Poor', 112 | 'Pr': 'Pre-Rich', 113 | 'Ri': 'Rich', 114 | 'As': 'Asteroid Belt', 115 | 'De': 'Desert', 116 | 'Fl': 'Fluid', 117 | 'Ga': 'Garden World', 118 | 'Ic': 'Ice-Capped', 119 | 'He': 'Hell World'} 120 | elif upp_type == 'atmosphere': 121 | dy_upp = {'0': 'Vacuum', 122 | '1': 'Trace', 123 | '2': 'Very thin, tainted', 124 | '3': 'Thin', 125 | '4': 'Thin, tainted', 126 | '5': 'Thin', 127 | '6': 'Standard', 128 | '7': 'Standad, tainted', 129 | '8': 'Dense', 130 | '9': 'Dense, tainted', 131 | 'A': 'Exotic', 132 | 'B': 'Corrosive', 133 | 'C': 'Insidious', 134 | 'D': 'Dense High', 135 | 'E': 'Thin Low', 136 | 'F': 'Unusual' 137 | } 138 | elif upp_type == 'size': 139 | dy_upp = {'0': 'Asteroid Belt', 140 | '1': '1,000 miles 1,600 km', 141 | '2': '2,000 miles 3,200 km', 142 | '3': '3,000 miles 4,800 km', 143 | '4': '4,000 miles 6,400 km', 144 | '5': '5,000 miles 8,000 km', 145 | '6': '6,000 miles 9,600 km', 146 | '7': '7,000 miles 11,200 km', 147 | '8': '8,000 miles 12,800 km', 148 | '9': '9,000 miles 14,400 km', 149 | 'A': '10,000 miles 16,000 km', 150 | 'B': '11,000 miles 17,600 km', 151 | 'C': '12,000 miles 19,200 km', 152 | 'D': '13,000 miles 20,800 km', 153 | 'E': '14,000 miles 22,400 km', 154 | 'F': '15,000 miles 24,000 km'} 155 | elif upp_type == 'government': 156 | dy_upp = {'0': 'No Government Structure.', 157 | '1': 'Company/ Corporation.', 158 | '2': 'Participating Democracy', 159 | '3': 'Self-Perpetuating Oligarchy', 160 | '4': 'Representative Democracy', 161 | '5': 'Feudal Technocracy', 162 | '6': 'Captive Government / Colony', 163 | '7': 'Balkanization', 164 | '8': 'Civil Service Bureaucracy', 165 | '9': 'Impersonal Bureaucracy', 166 | 'A': 'Charismatic Dictatorship' , 167 | 'B': 'Non-Charismatic Dictatorship', 168 | 'C': 'Charismatic Oligarchy', 169 | 'D': 'Religious Dictatorship', 170 | 'E': 'Religious Autocracy', 171 | 'F': 'Totalitarian Oligarchy'} 172 | elif upp_type == 'law': 173 | dy_upp = { 174 | '0': 'No Law. No prohibitions.', 175 | '1': 'Low Law. Prohibition of WMD, Psi weapons.', 176 | '2': 'Low Law. Prohibition of “Portable” Weapons.', 177 | '3': 'Low Law. Prohibition of Acid, Fire, Gas.', 178 | '4': 'Moderate Law. Prohibition of Laser, Beam.', 179 | '5': 'Moderate Law. No Shock,EMP,Rad, Mag, Grav.', 180 | '6': 'Moderate Law. Prohibition of Machineguns.', 181 | '7': 'Moderate Law. Prohibition of Pistols.', 182 | '8': 'High Law. Open display of weapons prohibited.', 183 | '9': 'High Law. No weapons outside the home.', 184 | 'A': 'Extreme Law. All weapons prohibited.', 185 | 'B': 'Extreme Law. Continental passports required.', 186 | 'C': 'Extreme Law. Unrestricted invasion of privacy.', 187 | 'D': 'Extreme Law. Paramilitary law enforcement.', 188 | 'E': 'Extreme Law. Full-fledged police state.', 189 | 'F': 'Extreme Law. Daily life rigidly controlled.', 190 | 'G': 'Extreme Law. Disproportionate punishment.', 191 | 'H': 'Extreme Law. Legalized oppressive practices.', 192 | 'J': 'Extreme Law. Routine oppression.' 193 | } 194 | 195 | if upp_type == 'remarks': 196 | for x, y in dy_upp.items(): 197 | if upp_value.find(x) >= 0: 198 | description += y + '. ' 199 | else: 200 | description = dy_upp[upp_value] 201 | return description 202 | 203 | 204 | def get_subsector_number_list(subsector): 205 | 206 | def get_string(num): 207 | str_num = str(num) 208 | if len(str_num) == 1: str_num = '0' + str_num 209 | return str_num 210 | 211 | def get_ranges(front_limits,back_limits): 212 | return (range(front_limits[0],front_limits[1]+1), range(back_limits[0], back_limits[1]+1)) 213 | 214 | 215 | subsector_number_list = [] 216 | 217 | 218 | subsector_letter_dictionary = { 219 | 'A': [[1,8],[1,10]], 220 | 'B': [[9,16],[1,10]], 221 | 'C': [[17,24],[1,10]], 222 | 'D': [[25,32],[1,10]], 223 | 224 | 'E': [[1,8],[11,20]], 225 | 'F': [[9,16],[11,20]], 226 | 'G': [[17,24],[11,20]], 227 | 'H': [[25,32],[11,20]], 228 | 229 | 'I': [[1,8],[21,30]], 230 | 'J': [[9,16],[21,30]], 231 | 'K': [[17,24],[21,30]], 232 | 'L': [[25,32],[21,30]], 233 | 234 | 'M': [[1,8],[31,40]], 235 | 'N': [[9,16],[31,40]], 236 | 'O': [[17,24],[31,40]], 237 | 'P': [[25,32],[31,40]] 238 | 239 | } 240 | 241 | 242 | 243 | front_limits = subsector_letter_dictionary[subsector][0] 244 | back_limits = subsector_letter_dictionary[subsector][1] 245 | front_digits, back_digits = get_ranges(front_limits,back_limits) 246 | 247 | 248 | 249 | for each_front in front_digits: 250 | for each_back in back_digits: 251 | front_str = get_string(each_front) 252 | back_str = get_string(each_back) 253 | subsector_number_list.append(front_str+back_str) 254 | 255 | return subsector_number_list 256 | 257 | 258 | #### saves an image from an api call 259 | def save_downloaded_image(response, png_name): 260 | """Saves the downloaded image content to a file.""" 261 | with open(png_name, 'wb') as f: 262 | f.write(response.content) 263 | 264 | 265 | 266 | #### API call for image download using html POST method 267 | #### Makes use of save_down_load_image above 268 | #### Currently only used for travellermap.com 269 | 270 | def download_image_via_api(api_image_parameters): 271 | 272 | url = api_image_parameters.url 273 | tab = api_image_parameters.files['file'] 274 | routes = api_image_parameters.files['metadata'] 275 | png_name = api_image_parameters.image_name 276 | 277 | # File parms to upload 278 | files = { 279 | 'file': open(tab, 'rb'), 280 | 'metadata': open(routes, 'rb') 281 | } 282 | 283 | try: 284 | response = requests.post(url, files=files) 285 | response.raise_for_status() # Raise exception for non-200 status codes 286 | 287 | # Check if response is binary data (optional) 288 | if 'Content-Type' in response.headers and response.headers['Content-Type'].startswith('image/'): 289 | # Handle binary response (e.g., save image to file) 290 | save_downloaded_image(response, png_name) 291 | logging.debug("File Saved") 292 | else: 293 | logging.debug('API response received but unexpected format') 294 | 295 | except Exception as e: 296 | logging.debug(f'Failed api {e}') 297 | 298 | 299 | 300 | #### culture details object used for browser and export 301 | class Culture_details: 302 | def __init__(self, 303 | age, 304 | appearance, 305 | tendency, 306 | materialism, 307 | honesty, 308 | bravery, 309 | social_conflict, 310 | work_ethic, 311 | consumerism, 312 | spiritual_outlook, 313 | status_quo_outlook, 314 | custom, 315 | interest, 316 | common_skills, 317 | materialism_symbol=None, 318 | honesty_symbol=None, 319 | bravery_symbol=None, 320 | social_conflict_symbol=None, 321 | work_ethic_symbol=None, 322 | consumerism_symbol=None): 323 | self.age = age 324 | self.appearance = appearance 325 | self.tendency = tendency 326 | self.materialism = materialism 327 | self.honesty = honesty 328 | self.bravery = bravery 329 | self.social_conflict = social_conflict 330 | self.work_ethic = work_ethic 331 | self.consumerism = consumerism 332 | self.spiritual_outlook = spiritual_outlook 333 | self.status_quo_outlook = status_quo_outlook 334 | self.custom = custom 335 | self.interest = interest 336 | self.common_skills = common_skills 337 | self.materialism_symbol = materialism_symbol 338 | self.honesty_symbol = honesty_symbol 339 | self.bravery_symbol = bravery_symbol 340 | self.social_conflict_symbol = social_conflict_symbol 341 | self.work_ethic_symbol = work_ethic_symbol 342 | self.consumerism_symbol = consumerism_symbol 343 | 344 | @staticmethod 345 | def convert_culture_to_symbol(culture_object): 346 | 347 | materialism_dict = { 348 | 'minimal possessions': '--', 349 | 'average' : '0', 350 | 'modest possessions' : '+', 351 | 'covet possessions' : '++', 352 | 'n/a' : 'n/a' 353 | } 354 | 355 | honesty_dict = { 356 | 'scrupulous' : '++', 357 | 'honour-bound' : '++', 358 | 'truthful' : '+', 359 | 'average' : '0', 360 | 'untrustworthy' : '-', 361 | 'deceitful' : '--', 362 | 'n/a' : 'n/a' 363 | 364 | } 365 | 366 | bravery_dict = { 367 | 'foolhardy' : '++', 368 | 'brave' : '+', 369 | 'average' : '0', 370 | 'cautious' : '-', 371 | 'reject bravery as an ideal' : '--', 372 | 'n/a' : 'n/a' 373 | 374 | } 375 | 376 | work_ethic_dict = { 377 | 'beyond driven' : '++', 378 | 'driven' : '+', 379 | 'average' : '0', 380 | 'relaxed' : '-', 381 | 'very relaxed' : '--', 382 | 'n/a' : 'n/a' 383 | 384 | } 385 | 386 | social_conflict_dict = { 387 | 'thrive on conflict' : '++', 388 | 'enjoy conflict' : '+', 389 | 'average' : '0', 390 | 'conflict adverse' : '-', 391 | 'conflict phobic' : '--', 392 | 'n/a' : 'n/a' 393 | 394 | } 395 | 396 | consumerism_dict = { 397 | 'wasteful' : '++', 398 | 'spendthrift' : '+', 399 | 'average' : '0', 400 | 'miserly' : '-', 401 | 'conservative spender' : '--', 402 | 'n/a' : 'n/a' 403 | 404 | } 405 | 406 | # Update symbol attributes 407 | updated_culture_object = Culture_details( 408 | culture_object.age, 409 | culture_object.appearance, 410 | culture_object.tendency, 411 | culture_object.materialism, 412 | culture_object.honesty, 413 | culture_object.bravery, 414 | culture_object.social_conflict, 415 | culture_object.work_ethic, 416 | culture_object.consumerism, 417 | culture_object.spiritual_outlook, 418 | culture_object.status_quo_outlook, 419 | culture_object.custom, 420 | culture_object.interest, 421 | culture_object.common_skills, 422 | materialism_symbol=materialism_dict.get(culture_object.materialism), 423 | honesty_symbol=honesty_dict.get(culture_object.honesty), 424 | bravery_symbol=bravery_dict.get(culture_object.bravery), 425 | social_conflict_symbol=social_conflict_dict.get(culture_object.social_conflict), 426 | work_ethic_symbol=work_ethic_dict.get(culture_object.work_ethic), 427 | consumerism_symbol=consumerism_dict.get(culture_object.consumerism) 428 | ) 429 | 430 | return updated_culture_object 431 | 432 | 433 | # An object used to hold and pass decisions for API image downloads 434 | # Currently only traveller_map API is used 435 | # Used in export_sector and browse_sector 436 | class Api_image_parameters: 437 | def __init__(self, 438 | url, 439 | files, 440 | image_name): 441 | self.url = url # Url of API 442 | self.files = files # File parms to send to API 443 | self.image_name = image_name # File name of the downloaded image 444 | 445 | -------------------------------------------------------------------------------- /traveller_map.py: -------------------------------------------------------------------------------- 1 | def build_travellermap_file(db_name,sector_name): 2 | 3 | 4 | #v 1.0.1a 2024-05-23 updated to handle NULL remarks and leverage traveller_functions 5 | #v 1.1.0e 2024-05-24 Added error variables to debug log in try/excepts 6 | 7 | 8 | # Traveller Map 9 | # Generate a TravellerMap-like extract for import into Traveller Map or other programs 10 | import sqlite3 11 | import logging 12 | from traveller_functions import tohex 13 | 14 | 15 | 16 | def pad_space(detail,total): 17 | 18 | logging.debug(f'Detail: {detail} Total: {total}') 19 | if detail is not None: 20 | spaces = total - len(detail) 21 | else: 22 | detail = '' 23 | spaces = total 24 | for x in range(0,spaces): 25 | detail += ' ' 26 | return detail 27 | 28 | 29 | # Main Program 30 | 31 | 32 | 33 | conn = sqlite3.connect(db_name) 34 | c = conn.cursor() 35 | 36 | sql3_select_locorb = """ SELECT t.*, 37 | s.* 38 | FROM traveller_stats t 39 | LEFT JOIN system_stats s 40 | ON t.location = s.location 41 | WHERE t.main_world = 1 42 | """ 43 | 44 | 45 | c.execute(sql3_select_locorb) 46 | allrows = c.fetchall() 47 | 48 | 49 | 50 | conn.commit() 51 | conn.close() 52 | 53 | 54 | ### Produce an extract T5 tab delimited file for use with Traveller Map 55 | 56 | trav_filename = db_name + '_tab' + '.txt' 57 | with open(trav_filename, 'w') as f: 58 | f.write('Hex' + '\t' \ 59 | + 'Name' + '\t' \ 60 | + 'UWP' + '\t' \ 61 | + 'Remarks' + '\t' \ 62 | + '{Ix}' + '\t' \ 63 | + '(Ex)' + '\t' \ 64 | + '[Cx]' + '\t' \ 65 | + 'Nobility' + '\t' \ 66 | + 'Bases' + '\t' \ 67 | + 'Zone' + '\t' \ 68 | + 'PBG' + '\t' \ 69 | + 'W' + '\t' \ 70 | + 'Allegiance' + '\t' \ 71 | + 'Stars' + '\n') 72 | 73 | for row in allrows: 74 | location = row[2] 75 | name = row[3] 76 | uwp = (row[4] \ 77 | + tohex(int(row[5]))\ 78 | + tohex(int(row[6])) \ 79 | + tohex(int(row[7])) \ 80 | + tohex(int(row[8])) \ 81 | + tohex(int(row[9])) \ 82 | + tohex(int(row[10])) \ 83 | + '-' 84 | + tohex(int(row[11]))) 85 | 86 | remarks = row[15] 87 | ix = row[16] 88 | ex = row[17] 89 | cx = row[18] 90 | n = row[19] 91 | bases = row[20] 92 | zone = row[21] 93 | pbg = row[22] 94 | w = row[23] 95 | allegiance = row[24] 96 | stars=row[25] 97 | 98 | tab = '\t' 99 | try: 100 | f.write(location + tab + 101 | name + tab + 102 | uwp + tab + 103 | remarks + tab + 104 | ix + tab + 105 | ex + tab + 106 | cx + tab + 107 | n + tab + 108 | bases + tab + 109 | zone + tab + 110 | pbg + tab + 111 | w + tab + 112 | allegiance + tab + 113 | stars + 114 | '\n') 115 | except Exception as e: 116 | logging.debug(f'Failed to update {trav_filename} {uwp} {e}') 117 | 118 | 119 | ### Produce a column-specific file for use with PyMapGen 120 | 121 | trav_filename = 'sector_db\sec_m01_m01.dat' 122 | with open(trav_filename, 'w') as f: 123 | f.write( 124 | """ 125 | 126 | 127 | # """ + sector_name + """ 128 | # 0,0 129 | 130 | # Name:""" + sector_name + """\n 131 | 132 | # Milieu: 133 | 134 | # Credits: 135 | 136 | # Source: 137 | 138 | # Subsector A: A 139 | # Subsector B: B 140 | # Subsector C: C 141 | # Subsector D: D 142 | # Subsector E: E 143 | # Subsector F: F 144 | # Subsector G: G 145 | # Subsector H: H 146 | # Subsector I: I 147 | # Subsector J: J 148 | # Subsector K: K 149 | # Subsector L: L 150 | # Subsector M: M 151 | # Subsector N: N 152 | # Subsector O: O 153 | # Subsector P: P 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | Hex Name UWP Remarks {Ix} (Ex) [Cx] N B Z PBG W A Stellar 162 | ---- -------------------- --------- ------------------------- ------ ------- ------ ---- -- - --- -- ---- --------------- 163 | """ 164 | ) 165 | 166 | for row in allrows: 167 | location = row[2] 168 | 169 | name = pad_space(row[3],20) 170 | 171 | uwp = (row[4] \ 172 | + tohex(int(row[5]))\ 173 | + tohex(int(row[6])) \ 174 | + tohex(int(row[7])) \ 175 | + tohex(int(row[8])) \ 176 | + tohex(int(row[9])) \ 177 | + tohex(int(row[10])) \ 178 | + '-' 179 | + tohex(int(row[11]))) 180 | 181 | 182 | 183 | remarks = pad_space(row[15],25) 184 | ix = pad_space(row[16],6) 185 | ex = pad_space(row[17],7) 186 | cx = pad_space(row[18],6) 187 | n = pad_space(row[19],4) 188 | bases = pad_space(row[20],2) 189 | zone = row[21] 190 | pbg = row[22] 191 | w = pad_space(row[23],2) 192 | allegiance = pad_space(row[24],4) 193 | stars=pad_space(row[25],15) 194 | 195 | tab = ' ' 196 | try: 197 | f.write(location + tab + 198 | name + tab + 199 | uwp + tab + 200 | remarks + tab + 201 | ix + tab + 202 | ex + tab + 203 | cx + tab + 204 | n + tab + 205 | bases + tab + 206 | zone + tab + 207 | pbg + tab + 208 | w + tab + 209 | allegiance + tab + 210 | stars + 211 | '\n') 212 | except Exception as e: 213 | logging.debug(f'Failed to update {trav_filename} {location} {e}') 214 | 215 | 216 | -------------------------------------------------------------------------------- /traveller_master.py: -------------------------------------------------------------------------------- 1 | def make_sector_master(decisions_provided): 2 | 3 | # Master program for Traveller universe build 4 | 5 | 6 | from first_in_generation import generate_stars 7 | from mainworld_calculator import generate_mainworld_scores 8 | from mainworld_selector import choose_mainworld 9 | from travellerization import add_traveller_stats 10 | from traveller_map import build_travellermap_file 11 | from non_mw import generate_non_mainworlds 12 | from far_trader import generate_far_trader_stats 13 | from journey_data import build_journey_table 14 | from routes_short_path import create_route_xml 15 | from culture import create_culture_stats 16 | import FreeSimpleGUI as sg 17 | 18 | 19 | seed_number = decisions_provided.random_seed 20 | sector_name = decisions_provided.sector_name 21 | settlement_mod = decisions_provided.settlement_mod 22 | 23 | 24 | 25 | db_name = 'sector_db/' + sector_name + '.db' 26 | print('Generating Stars and Planets') 27 | generate_stars(db_name,decisions_provided) 28 | print('Building the Journey stats table') 29 | build_journey_table(seed_number,db_name) 30 | print('Evaluating each orbital body for main world suitability') 31 | generate_mainworld_scores(db_name) 32 | print('Finalizing main world selections and building mainworld table') 33 | choose_mainworld(db_name) 34 | print('Travellerizing the main worlds') 35 | add_traveller_stats(seed_number,db_name,settlement_mod) 36 | print('Building Traveller Map extract') 37 | build_travellermap_file(db_name,sector_name) 38 | print('Adding data for non-main worlds') 39 | generate_non_mainworlds(seed_number,db_name) 40 | print('Building Far Trader table') 41 | generate_far_trader_stats(seed_number,db_name) 42 | print('Building Culture table') 43 | create_culture_stats(seed_number,db_name) 44 | print('Building Routes file') 45 | create_route_xml(seed_number,db_name,settlement_mod) 46 | 47 | 48 | sg.popup('Sector completed successfully') 49 | 50 | -------------------------------------------------------------------------------- /travellerization.py: -------------------------------------------------------------------------------- 1 | def add_traveller_stats(seed_number,db_name,settlement_mod): 2 | 3 | 4 | # The goal is to read the Orbital Bodies table generated from the 5 | # First In program and adjust by the Mainworld_Calc module 6 | # and build a new table using Traveller 5 stats 7 | 8 | # The output is a new Traveller5 table. It needs to run after First In, Mainworld_Calc, 9 | # and Mainworld Selector 10 | 11 | 12 | 13 | # COMPLETE: Need to add a routine to get belts and GG totals 14 | # COMPLETE: Stellar information added from all three stellar tables 15 | # 2024-05-11 COMPLETE: updated pbg to include secondary and tertiary stars 16 | 17 | 18 | 19 | import sqlite3 20 | import random 21 | import traceback 22 | import sys 23 | 24 | from traveller_functions import tohex, roll_dice 25 | 26 | random.seed(seed_number) 27 | 28 | 29 | def capture_primary_stats(): 30 | sql3_select_p_stars = """ SELECT location, 31 | luminosity_class, 32 | spectral_type, 33 | belts, 34 | gg, 35 | orbits 36 | FROM stellar_bodies WHERE companion_class = '0' """ 37 | 38 | c.execute(sql3_select_p_stars) 39 | allrows = c.fetchall() 40 | p_stars_dict = {} 41 | for row in allrows: 42 | p_stars_dict[row[0]] = { 'p_lumc' :row[1], 43 | 'p_spec' :row[2], 44 | 'no_belts' :row[3], 45 | 'no_gg' :row[4], 46 | 'worlds' :row[5]} 47 | 48 | return p_stars_dict 49 | 50 | def capture_secondary_stats(): 51 | sql3_select_c_stars = """ SELECT location, 52 | luminosity_class, 53 | spectral_type, 54 | belts, 55 | gg, 56 | orbits 57 | FROM stellar_bodies WHERE companion_class = '1' """ 58 | 59 | c.execute(sql3_select_c_stars) 60 | allrows = c.fetchall() 61 | c_stars_dict = {} 62 | for row in allrows: 63 | c_stars_dict[row[0]] = { 'c_lumc' :row[1], 64 | 'c_spec' :row[2], 65 | 'no_belts' :row[3], 66 | 'no_gg' :row[4], 67 | 'worlds' :row[5]} 68 | 69 | return c_stars_dict 70 | 71 | def capture_tertiary_stats(): 72 | sql3_select_t_stars = """ SELECT location, 73 | luminosity_class, 74 | spectral_type, 75 | belts, 76 | gg, 77 | orbits 78 | FROM stellar_bodies WHERE companion_class = '2' OR companion_class = '1.1' """ 79 | 80 | c.execute(sql3_select_t_stars) 81 | allrows = c.fetchall() 82 | t_stars_dict = {} 83 | for row in allrows: 84 | t_stars_dict[row[0]] = { 't_lumc' :row[1], 85 | 't_spec' :row[2], 86 | 'no_belts' :row[3], 87 | 'no_gg' :row[4], 88 | 'worlds' :row[5]} 89 | 90 | return t_stars_dict 91 | 92 | 93 | 94 | 95 | 96 | def create_traveller_tables(): 97 | 98 | 99 | sql_create_system_stats_table = """CREATE TABLE system_stats( 100 | id INTEGER PRIMARY KEY AUTOINCREMENT, 101 | location TEXT, 102 | remarks TEXT, 103 | ix TEXT, 104 | ex TEXT, 105 | cx TEXT, 106 | n TEXT, 107 | bases TEXT, 108 | zone TEXT, 109 | pbg TEXT, 110 | w TEXT, 111 | allegiance TEXT, 112 | stars TEXT 113 | );""" 114 | c.execute('DROP TABLE IF EXISTS system_stats') 115 | c.execute(sql_create_system_stats_table) 116 | 117 | 118 | 119 | sql_create_traveller_stats_table = """CREATE TABLE traveller_stats( 120 | id INTEGER PRIMARY KEY AUTOINCREMENT, 121 | location_orb TEXT, 122 | location TEXT, 123 | system_name TEXT, 124 | starport TEXT, 125 | size INTEGER, 126 | atmosphere INTEGER, 127 | hydrographics INTEGER, 128 | population INTEGER, 129 | government INTEGER, 130 | law INTEGER, 131 | tech_level INTEGER, 132 | main_world INTEGER 133 | 134 | 135 | );""" 136 | c.execute('DROP TABLE IF EXISTS traveller_stats') 137 | c.execute(sql_create_traveller_stats_table) 138 | 139 | 140 | 141 | def get_system_name(name_list): 142 | names_left = len(name_list) 143 | name_picked = name_list[random.randrange(0,names_left)] 144 | name_fixed = name_picked.rstrip('\n') 145 | name_list.remove(name_picked) 146 | return name_fixed 147 | 148 | def get_starport(location, population): 149 | # Using First In rules for Starport 150 | c_starport = 'Z' 151 | 152 | dice = roll_dice(3,'Starport A',location, conn, c) 153 | if population >= 6: 154 | if dice < population + 3: 155 | c_starport = 'A' 156 | return c_starport 157 | 158 | dice = roll_dice(3,'Starport B',location, conn, c) 159 | if population >= 6: 160 | if dice < population + 6: 161 | c_starport = 'B' 162 | return c_starport 163 | 164 | dice = roll_dice(3,'Starport C',location, conn, c) 165 | if dice < population + 9: 166 | c_starport = 'C' 167 | return c_starport 168 | 169 | dice = roll_dice(3,'Starport D',location, conn, c) 170 | if dice < population + 8: 171 | c_starport = 'D' 172 | return c_starport 173 | 174 | dice = roll_dice(3,'Starport E',location, conn, c) 175 | if dice < 15: 176 | c_starport = 'E' 177 | return c_starport 178 | 179 | dice = roll_dice(3,'Starport X',location, conn, c) 180 | c_starport = 'X' 181 | return c_starport 182 | 183 | def get_population(location,settlement_mod): 184 | pop_adjustment = 0 185 | if settlement_mod == 1: # The Settlement Style of Diminished was chosen 186 | xcoord = int(location[0:2]) # location hex first two characters 187 | ycoord = int(location[2:4]) # location hex second two characters 188 | if xcoord >= 12 and xcoord <=22 and ycoord >= 14 and ycoord <=26: 189 | pop_adjustment = 3 190 | elif xcoord <= 4 or xcoord >= 29 or ycoord <=4 or ycoord >=37: 191 | pop_adjustment= -2 192 | 193 | dice = roll_dice(2,'Population, settlement adj ' + str(pop_adjustment),location, conn, c) - 2 + pop_adjustment 194 | 195 | if dice < 0: dice = 0 196 | if dice >= 10: 197 | dice = roll_dice(2,'Population after 10+ roll',location, conn, c) + 3 198 | return dice 199 | 200 | def get_belts(location,gg_belt_stats): 201 | c_no_belts = -1 202 | c_no_belts = gg_belt_stats[location]['no_belts'] 203 | return c_no_belts 204 | 205 | def get_gg(location,gg_belt_stats): 206 | c_no_gg = -1 207 | c_no_gg = gg_belt_stats[location]['no_gg'] 208 | return c_no_gg 209 | 210 | 211 | def get_pop_mod(location,population): 212 | if population != 0: 213 | c_pop_mod = str(random.randrange(1,10)) 214 | else: 215 | c_pop_mod = 0 216 | return c_pop_mod 217 | 218 | def get_bases(location, starport): 219 | str_base = 'X' 220 | base_list = list() 221 | if starport == 'D': 222 | dice = roll_dice(2,'Scout Base',location, conn, c) 223 | if dice <= 7: base_list.append('S') 224 | elif starport == 'C': 225 | dice = roll_dice(2,'Scout Base',location, conn, c) 226 | if dice <= 6: base_list.append('S') 227 | elif starport == 'B': 228 | dice1 = roll_dice(2,'Scout Base',location, conn, c) 229 | dice2 = roll_dice(2,'Naval Base',location, conn, c) 230 | if dice1 <= 5: base_list.append('S') 231 | if dice2 <= 5: base_list.append('N') 232 | elif starport == 'A': 233 | dice1 = roll_dice(2,'Scout Base',location, conn, c) 234 | dice2 = roll_dice(2,'Naval Base',location, conn, c) 235 | if dice1 <= 4: base_list.append('S') 236 | if dice2 <= 6: base_list.append('N') 237 | 238 | if 'S' in base_list: 239 | if 'N' in base_list: 240 | str_base = 'NS' 241 | else: str_base = 'S' 242 | elif 'N' in base_list: str_base = 'N' 243 | else: str_base = '-' 244 | 245 | return str_base 246 | 247 | def get_atmosphere(pressure,composition): 248 | c_atmosphere = -1 249 | if pressure == 0: c_atmosphere = 0 250 | elif pressure == 0.1: c_atmosphere = 1 251 | elif composition == 'Exotic': c_atmosphere = 10 252 | elif composition == 'Corrosive': c_atmosphere = 11 253 | elif composition == 'GG': c_atmosphere = 1 #need a function to get GG moon data 254 | elif composition == 'Standard': 255 | if pressure < 0.5: c_atmosphere = 3 256 | elif pressure < 0.8: c_atmosphere = 5 257 | elif pressure < 1.2: c_atmosphere = 6 258 | elif pressure < 1.5: c_atmosphere = 8 259 | else: c_atmosphere = 13 260 | elif composition == 'Tainted': 261 | if pressure < 0.5: c_atmosphere = 2 262 | elif pressure < 0.8: c_atmosphere = 4 263 | elif pressure < 1.2: c_atmosphere = 7 264 | elif pressure < 1.5: c_atmosphere = 9 265 | else: c_atmosphere = 12 266 | 267 | return c_atmosphere 268 | 269 | def get_hydrographics(body, hydro): 270 | c_hydro = -1 271 | if body == 'Gas Giant': c_hydro = 1 # add a function to get a GG moon data 272 | else: c_hydro = hydro 273 | return c_hydro 274 | 275 | def get_size(body, size): 276 | c_size = -1 277 | if body == 'Gas Giant': c_size = '1' # add a function to get a GG moon data 278 | else: c_size = size 279 | return c_size 280 | 281 | def get_government(location, population): 282 | dice = roll_dice(2,'Government',row[0], conn, c) + population - 7 283 | if dice < 0: dice = 0 284 | elif dice > 15: dice = 15 285 | if population == 0: dice = 0 286 | return dice 287 | 288 | def get_law_level(location, government): 289 | dice = roll_dice(2,'Law Level',row[0], conn, c) + government - 7 290 | if dice < 0: dice = 0 291 | elif dice > 15: dice = 15 292 | if population == 0: dice = 0 293 | return dice 294 | 295 | def get_tech_level(location, starport, size, atmosphere, hydrographics, population, government): 296 | 297 | starport_mod = -100 298 | starport_mod_dict = {'A':6, 'B':4, 'C':2, 'X':-4} 299 | if starport in starport_mod_dict.keys(): 300 | starport_mod = starport_mod_dict[starport] 301 | else: starport_mod = 0 302 | 303 | size_mod = -100 304 | size_mod_dict = {'0':2, '1':2, '2':1, '3':1, '4':1} 305 | if str(size) in size_mod_dict.keys(): 306 | size_mod = size_mod_dict[str(size)] 307 | else: size_mod = 0 308 | 309 | int_atmos = int(atmosphere) 310 | atmosphere_mod = -100 311 | if int_atmos <= 3: atmosphere_mod = 1 312 | elif int_atmos >= 10: atmosphere_mod = 1 313 | else: atmosphere_mod = 0 314 | 315 | hydro_mod = -100 316 | if hydrographics == 9: hydro_mod = 1 317 | elif hydrographics == 10: hydro_mod = 2 318 | else: hydro_mod = 0 319 | 320 | pop_mod = -100 321 | if population <= 5: pop_mod = 1 322 | elif population == 9: pop_mod = 2 323 | elif population >= 10: pop_mod = 4 324 | else: pop_mod = 1 325 | 326 | gov_mod = -100 327 | if government == 0: gov_mod = 1 328 | elif government == 5: gov_mod = 1 329 | elif government == 13: gov_mod = -2 330 | else: gov_mod = 0 331 | 332 | dice = roll_dice(1, 'Tech roll', location, conn, c) \ 333 | + starport_mod \ 334 | + size_mod \ 335 | + atmosphere_mod \ 336 | + hydro_mod \ 337 | + pop_mod \ 338 | + gov_mod 339 | # print ('Tech',starport,dice,starport_mod,size_mod,atmosphere_mod,hydro_mod,pop_mod,gov_mod) 340 | if population == 0: dice = 0 341 | return dice 342 | # 343 | def remark_check(*varg): 344 | rem_result = False 345 | for i in varg: 346 | if i[0] in i[1]: 347 | rem_result = True 348 | # print ('In',i[0],i[1]) 349 | else: 350 | # print ('Out',i[0],i[1]) 351 | return False 352 | return rem_result 353 | 354 | 355 | 356 | def get_remarks(starport, size, atmosphere, hydrographics, population, government): 357 | remarks_list = list() 358 | 359 | if remark_check((size,(0,)),(atmosphere,(0,)),(hydrographics,(0,))): 360 | remarks_list.append('As') 361 | 362 | atm_de = (2,3,4,5,6,7,8,9) 363 | hyd_de = (0,) 364 | if remark_check((atmosphere,atm_de),(hydrographics,hyd_de)): 365 | remarks_list.append('De') 366 | 367 | atm_fl = (10,11,12) 368 | hyd_fl = (1,2,3,4,5,6,7,8,9,10) 369 | if remark_check((atmosphere,atm_fl),(hydrographics,hyd_fl)): 370 | remarks_list.append('Fl') 371 | 372 | siz_ga = (6,7,8) 373 | atm_ga = (5,6,8) 374 | hyd_ga = (5,6,7) 375 | if remark_check((size,siz_ga),(atmosphere,atm_ga),(hydrographics,hyd_ga)): 376 | remarks_list.append('Ga') 377 | 378 | siz_he = (3,4,5,6,7,8,9,10,11,12) 379 | atm_he = (2,4,7,9,10,11,12) 380 | hyd_he = (0,1,2) 381 | if remark_check((size,siz_he),(atmosphere,atm_he),(hydrographics,hyd_he)): 382 | remarks_list.append('He') 383 | 384 | atm_ic = (0,1) 385 | hyd_ic = (1,2,3,4,5,6,7,8,9,10) 386 | # print (atmosphere, hydrographics) 387 | # if remark_check((atmosphere,atm_ic),(hydrographics,hyd_ic)): 388 | # print('Found ice! <<<<<<<<<<<<<<<<<<<<<<<<<') 389 | # remarks_list.append('Ic') 390 | # else: print ('No Ice!') 391 | # print (remarks_list) 392 | 393 | 394 | siz_oc = (10,11,12) 395 | atm_oc = (3,4,5,6,7,8,9,10,11,12) 396 | hyd_oc = (10,) 397 | if remark_check((size,siz_oc),(atmosphere,atm_oc),(hydrographics,hyd_oc)): 398 | remarks_list.append('Oc') 399 | 400 | if atmosphere == 0: remarks_list.append('Va') 401 | 402 | siz_wa = (3,4,5,6,7,8,9,10) 403 | atm_wa = (3,4,5,6,7,8,9) 404 | hyd_wa = (10,) 405 | if remark_check((size,siz_wa),(atmosphere,atm_wa),(hydrographics,hyd_wa)): 406 | remarks_list.append('Wa') 407 | 408 | pop_ba = (0,) 409 | gov_ba = (0,) 410 | law_ba = (0,) 411 | if remark_check((population,pop_ba),(government,gov_ba),(law_level,law_ba)): 412 | remarks_list.append('Ba') 413 | 414 | if 0 < population < 4: remarks_list.append('Lo') 415 | 416 | if 3 < population < 7: remarks_list.append('Ni') 417 | 418 | if population == 8: remarks_list.append('Ph') 419 | 420 | if population > 8: remarks_list.append('Hi') 421 | 422 | atm_pa = (4,5,6,7,8,9) 423 | hyd_pa = (4,5,6,7,8) 424 | pop_pa = (4,8) 425 | if remark_check((atmosphere,atm_pa),(hydrographics,hyd_pa),(population,pop_pa)): 426 | remarks_list.append('Pa') 427 | 428 | atm_ag = (4,5,6,7,8,9) 429 | hyd_ag = (4,5,6,7,8) 430 | pop_ag = (5,6,7) 431 | if remark_check((atmosphere,atm_ag),(hydrographics,hyd_ag),(population,pop_ag)): 432 | remarks_list.append('Ag') 433 | 434 | atm_na = (0,1,2,3) 435 | hyd_na = (0,1,2,3) 436 | pop_na = (6,7,8,9,10,11,12,13,14,15,16,17,18,19,20) 437 | if remark_check((atmosphere,atm_na),(hydrographics,hyd_na),(population,pop_na)): 438 | remarks_list.append('Na') 439 | 440 | atm_px = (2,3,10,11) 441 | hyd_px = (1,2,3,4,5) 442 | pop_px = (3,4,5,6) 443 | law_px = (6,7,8,9) 444 | if remark_check((atmosphere,atm_px),(hydrographics,hyd_px),(population,pop_px),(law_level,law_px)): 445 | remarks_list.append('Px') 446 | 447 | atm_pi = (0,1,2,3,4,7,9) 448 | pop_pi = (7,8) 449 | if remark_check((atmosphere,atm_pi),(population,pop_pi)): 450 | remarks_list.append('Pi') 451 | 452 | atm_in = (0,1,2,3,4,7,9,10,11,12) 453 | pop_in = (9,10,11,12,13,14,15,16,17,18,19,20) 454 | if remark_check((atmosphere,atm_in),(population,pop_in)): 455 | remarks_list.append('In') 456 | 457 | atm_po = (2,3,4,5) 458 | hyd_po = (0,1,2,3) 459 | if remark_check((atmosphere,atm_po),(hydrographics,hyd_po)): 460 | remarks_list.append('Po') 461 | 462 | atm_pr = (6,8) 463 | pop_pr = (5,9) 464 | if remark_check((atmosphere,atm_pr),(population,pop_pr)): 465 | remarks_list.append('Pr') 466 | 467 | 468 | atm_ri = (6,8) 469 | pop_ri = (6,7,8) 470 | if remark_check((atmosphere,atm_ri),(population,pop_ri)): 471 | remarks_list.append('Ri') 472 | 473 | 474 | 475 | return remarks_list 476 | 477 | def get_ix(uwp, remarks, bases): 478 | c_ix = 0 479 | if uwp[0] in ('A','B'): c_ix += 1 480 | elif uwp[0] in ('D','E','X'): c_ix -= 1 481 | if uwp[8] in ['A','B','C','D','E','F','G','H']: c_ix += 1 482 | if uwp[8] in ['G','H']: c_ix += 1 483 | if uwp[8] in ['0','1','2','3','4','5','6','7','8']: c_ix -= 1 484 | if 'Ag' in remarks: c_ix += 1 485 | if 'Hi' in remarks: c_ix += 1 486 | if 'In' in remarks: c_ix += 1 487 | if 'Ri' in remarks: c_ix += 1 488 | if uwp[4] in ['0','1','2','3','4','5','6']: c_ix -= 1 489 | if 'N' in bases: 490 | if 'S' in bases: c_ix += 1 491 | 492 | return c_ix 493 | 494 | def get_ex(ix, tech_level, population, remarks, belts, gg, location): 495 | 496 | resources = roll_dice(2, 'Ex res', location, conn, c) 497 | if tech_level >= 8: 498 | resources += gg 499 | resources += belts 500 | if resources < 0: resources = 0 501 | resources = tohex(resources) 502 | 503 | labor = population - 1 504 | if labor < 0: labor = 0 505 | labor = tohex(labor) 506 | 507 | if 'Ba' in remarks: infrastructure = 0 508 | elif 'Lo' in remarks: infrastructure = 1 509 | elif 'Ni' in remarks: infrastructure = roll_dice(1, 'Ex infra Ni',location, conn, c) + ix 510 | else: infrastructure = roll_dice(2, 'Ex infra', location, conn, c) + ix 511 | if infrastructure < 0: infrastructure = 0 512 | infrastructure = tohex(infrastructure) 513 | 514 | 515 | efficiency = roll_dice(2, "Ex eff", location, conn, c) - 7 516 | if efficiency < 0: 517 | return('(' + str(resources) + str(labor) + str(infrastructure) + str(efficiency) + ')') 518 | else: 519 | return('(' + str(resources) + str(labor) + str(infrastructure) + '+' + str(efficiency) + ')') 520 | 521 | 522 | def get_cx(population, ix, tech_level, location): 523 | homogeneity = population + roll_dice(2, 'Cx homo', location, conn, c) - 7 524 | if homogeneity < 1: homogeneity = 1 525 | homogeneity = tohex(homogeneity) 526 | 527 | acceptance = int(population) + int(ix) 528 | if acceptance < 1: acceptance = 1 529 | acceptance = tohex(acceptance) 530 | 531 | 532 | strangeness = roll_dice(2, 'Cx strange', location, conn, c) - 7 + 5 533 | if strangeness < 1: strangeness = 1 534 | strangeness = tohex(strangeness) 535 | 536 | symbols = (roll_dice(2, 'Cx symbols', location, conn, c) - 7 + tech_level) 537 | if symbols < 1: symbols = 1 538 | symbols = tohex(symbols) 539 | 540 | if population == 0: return '[0000]' 541 | else: return ('[' + str(homogeneity) + str(acceptance) + str(strangeness) + str(symbols) + ']') 542 | 543 | 544 | def get_zone(starport, population, government, law_level): 545 | zone = '-' 546 | if starport == 'X': 547 | zone = 'R' 548 | 549 | if (government + law_level) >= 31: zone = 'R' 550 | elif (government + law_level) >= 26: zone = 'A' 551 | 552 | return zone 553 | 554 | def get_noble(remarks, ix): 555 | noble_list = list() 556 | 557 | if ix >= 4: noble_list.append('f') 558 | if ('Hi') in remarks: noble_list.append('E') 559 | elif ('In') in remarks: noble_list.append('E') 560 | if ('Ph') in remarks: noble_list.append('e') 561 | if ('Pi') in remarks: noble_list.append('D') 562 | if ('Ri') in remarks: noble_list.append('C') 563 | elif ('Ag') in remarks: noble_list.append('C') 564 | if ('Pa') in remarks: noble_list.append('c') 565 | elif ('Pr') in remarks: noble_list.append('c') 566 | 567 | noble_list.append('B') 568 | 569 | return noble_list 570 | 571 | def get_worlds(location,gg_belt_stats): 572 | c_no_worlds = -1 573 | c_no_worlds = gg_belt_stats[location]['worlds'] 574 | return c_no_worlds 575 | 576 | def get_stars(location,p_star_dict,c_star_dict,t_star_dict): 577 | stars_text = str(p_star_dict[location]['p_spec']) + ' ' +str(p_star_dict[location]['p_lumc']) 578 | if location in c_star_dict.keys(): 579 | stars_text = stars_text + ' ' + str( 580 | c_star_dict[location]['c_spec']) + ' ' +str( 581 | c_star_dict[location]['c_lumc']) 582 | 583 | if location in t_star_dict.keys(): 584 | stars_text = stars_text + ' ' + str( 585 | t_star_dict[location]['t_spec']) + ' ' +str( 586 | t_star_dict[location]['t_lumc']) 587 | return stars_text 588 | 589 | 590 | # MAIN PROGRAM 591 | 592 | conn = sqlite3.connect(db_name) 593 | c = conn.cursor() 594 | 595 | 596 | # system names are pulled randomly from the names.txt file 597 | name_list = open("names.csv", "r").readlines() 598 | 599 | p_stars_dict = capture_primary_stats() 600 | c_stars_dict = capture_secondary_stats() 601 | t_stars_dict = capture_tertiary_stats() 602 | 603 | sector_population = 0 604 | create_traveller_tables() 605 | 606 | try: 607 | 608 | sql3_select_locorb = """ SELECT m.location_orbit, 609 | m.location, 610 | o.atmos_pressure, 611 | o.atmos_composition, 612 | o.body, 613 | o.hydrographics, 614 | o.size 615 | FROM main_world_eval m 616 | LEFT JOIN orbital_bodies o 617 | ON m.location_orbit = o.location_orbit 618 | WHERE m.mainworld_status = 'Y' """ 619 | 620 | c.execute(sql3_select_locorb) 621 | allrows = c.fetchall() 622 | 623 | 624 | except: 625 | print('Houston - travellerization reading problem') 626 | print('SQLite traceback: ') 627 | exc_type, exc_value, exc_tb = sys.exc_info() 628 | print(traceback.format_exception(exc_type, exc_value, exc_tb)) 629 | 630 | 631 | 632 | for row in allrows: 633 | 634 | system_name = get_system_name(name_list) 635 | # print(row[0],system_name,'Getting Population and System Details') 636 | population = get_population(row[0],settlement_mod) 637 | sector_population += 10**population 638 | pop_mod = get_pop_mod(row[0],population) 639 | 640 | 641 | belts = get_belts(row[1],p_stars_dict) 642 | gg = get_gg(row[1],p_stars_dict) 643 | w = get_worlds(row[1],p_stars_dict) 644 | 645 | if row[1] in c_stars_dict: 646 | belts += get_belts(row[1],c_stars_dict) 647 | gg += get_gg(row[1],c_stars_dict) 648 | w += get_worlds(row[1],c_stars_dict) 649 | 650 | if row[1] in t_stars_dict: 651 | belts += get_belts(row[1],t_stars_dict) 652 | gg += get_gg(row[1],t_stars_dict) 653 | w += get_worlds(row[1],t_stars_dict) 654 | 655 | 656 | 657 | 658 | 659 | 660 | pbg = str(str(pop_mod) + str(belts) + str(gg)) 661 | starport = get_starport(row[0], population) 662 | bases = get_bases(row[0],starport) 663 | 664 | 665 | # print(row[0],system_name,'Getting World Details') 666 | try: 667 | atmosphere = get_atmosphere(row[2],row[3]) 668 | hydrographics = get_hydrographics(row[4],row[5]) 669 | size = get_size(row[4],row[6]) 670 | government = get_government(row[0], population) 671 | law_level = get_law_level(row[0], government) 672 | tech_level = get_tech_level(row[0], starport, size, atmosphere, hydrographics, population, government) 673 | remarks = get_remarks(starport, size, atmosphere, hydrographics, population, government) 674 | str_remarks = ' '.join(remarks) 675 | 676 | except: 677 | print('Failed with standard stats') 678 | break 679 | 680 | 681 | try: 682 | int_size = size 683 | uwp = (starport + tohex(int(size))\ 684 | + tohex(int(atmosphere)) \ 685 | + tohex(int(hydrographics)) \ 686 | + tohex(int(population)) \ 687 | + tohex(int(government)) \ 688 | + tohex(int(law_level)) \ 689 | + '-' 690 | + tohex(int(tech_level))) 691 | 692 | ix = get_ix(uwp, remarks, bases) 693 | if ix >= 0: 694 | str_ix = ('{+' + str(ix) + '}') 695 | else: 696 | str_ix = ('{' + str(ix) + '}') 697 | 698 | ex = get_ex(ix, tech_level, population, remarks, belts, gg, row[1]) 699 | 700 | cx = get_cx(population, ix, tech_level, row[1]) 701 | 702 | zone = get_zone(starport, population, government, law_level) 703 | allegiance = 'Im' 704 | n_list = get_noble(remarks, ix) 705 | n = ''.join(n_list) 706 | stars = get_stars(row[1],p_stars_dict,c_stars_dict, t_stars_dict) 707 | main_world = 1 708 | 709 | except: 710 | print('Failed with detailed stats and UWP creation') 711 | break 712 | 713 | 714 | 715 | 716 | ############################################################################################## 717 | # Traveler stats will have mainworlds and non-mainworlds 718 | # System stats are for stats common for the entire system 719 | 720 | try: 721 | 722 | sqlcommand = '''INSERT INTO traveller_stats(location_orb, 723 | location, 724 | system_name, 725 | starport, 726 | size, 727 | atmosphere, 728 | hydrographics, 729 | population, 730 | government, 731 | law, 732 | tech_level, 733 | main_world) 734 | VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ''' 735 | 736 | body_row = (str(row[0]), 737 | str(row[1]), 738 | system_name, 739 | starport, 740 | size, 741 | atmosphere, 742 | hydrographics, 743 | population, 744 | government, 745 | law_level, 746 | tech_level, 747 | main_world) 748 | # print(body_row) 749 | c.execute(sqlcommand, body_row) 750 | 751 | except: 752 | print('Failed to write into Traveller Stats') 753 | break 754 | 755 | try: 756 | 757 | 758 | 759 | sqlcommand = ''' INSERT INTO system_stats( 760 | location, 761 | bases, 762 | pbg, 763 | remarks, 764 | ix, 765 | ex, 766 | cx, 767 | zone, 768 | n, 769 | allegiance, 770 | w, 771 | stars) 772 | VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ''' 773 | 774 | 775 | 776 | body_row = (str(row[1]), 777 | bases, 778 | pbg, 779 | str_remarks, 780 | str_ix, 781 | ex, 782 | cx, 783 | zone, 784 | n, 785 | allegiance, 786 | w, 787 | stars) 788 | 789 | 790 | 791 | # print(body_row) 792 | c.execute(sqlcommand, body_row) 793 | except: 794 | print('Failed to write into System Stats') 795 | break 796 | 797 | 798 | 799 | 800 | sector_population /= 1000000000000 801 | print('Sector population approx.',round(sector_population,2), 'trillion sophonts.') 802 | conn.commit() 803 | conn.close() --------------------------------------------------------------------------------