• Welcome to the new COTI server. We've moved the Citizens to a new server. Please let us know in the COTI Website issue forum if you find any problems.
  • We, the systems administration staff, apologize for this unexpected outage of the boards. We have resolved the root cause of the problem and there should be no further disruptions.

Unifying Python world generation tools

simonh

SOC-12
Looking on Makhidkarun currently there are I think five different projects for generating world data, one of them mine. I was about to merge mine into py_tools, but I'm considering what would be a responsible way to do that.

I've long wanted to genuinely collaborate on these tools with others, as a community. Recently I've been working with Leitz to do that under the py_tools project because I think the right way to do that is under a single repository, otherwise I suspect all our tools will remain siloed. In doing so I've followed Leitz' example and refactored my code as libraries, with a separate command line interface script.

I think this is the right way to go. It also opens up the possibility of providing a single interface script that can call on multiple different libraries on the back end. Once our code is in a single repository I hope it would be a lot easier to share code and call into each other's stuff. For example Golan's scripts do star gen which mine doesn't.

Anyway, it's a thought.

Simon Hibbs
 
I agree. My goal is to learn and there are few better ways to learn than to practice in public. Not my idea, by the way, but a good one.

While looking at Simon's code and asking some questions on IRC I was prodded to try something new. It worked for Simon's code and I used it to make my own better as well. I learned.

Some of you probably know a lot already; feel free to stop in and help me learn. And help us provide stuff to the community.
 
ShawnDriscoll started a thread about "Which Edition?" for making worlds. I'm working through a similar issue with the character stuff in py_tools.

I'm reading "Object-Oriented Analysis and Design" (Booch, 2nd ed) and working the class structure to deal with various types of character. Python has multiple inheritance which might be one option; a T5_Character could be a subclass of Character and T5_CharacterTools.

Right now the way I'm leaning is that the Character attributes are mostly hash values. There are some very basic methods in the class that set up the minimum framework for data, and a couple that populate data not provided. A goal is to have methods or classes that will take different types of input (yaml, json, csv, sql, mongodb), create/update the character, and then other classes/methods to output into some format.

A python dict does well to store data and it can be laid out in json format which can be put into MongoDB. Using a key/value pair makes the data structure very extensible. For example, if I want a "Supplement 4" version output of a character I don't need to worry about positional arguments; I just don't include the "keirsey" or "plot" values the data stores.

My primary use case is for fiction writing. It will probably work for gamers as well; I'm adding information about the characters as they develop. Between "generate_basic()" and "fill_out_char()" method I can get good data on characters. However, I add relationships, personal plot arcs, temperaments, appearance, political affiliations, etc. Stuff most folks won't need but are useful. The issue with something like json/MongoDB is that it's a lot harder to edit as plain text. Some folks may want yaml or csv. Or we get around to writing tkinter interfaces into the datastores. :)

My answer to "Which Edition?" is "Yes, as I get to it.". :rofl:
 
ShawnDriscoll started a thread about "Which Edition?" for making worlds. I'm working through a similar issue with the character stuff in py_tools.

I'm reading "Object-Oriented Analysis and Design" (Booch, 2nd ed) and working the class structure to deal with various types of character. Python has multiple inheritance which might be one option; a T5_Character could be a subclass of Character and T5_CharacterTools.

Right now the way I'm leaning is that the Character attributes are mostly hash values. There are some very basic methods in the class that set up the minimum framework for data, and a couple that populate data not provided. A goal is to have methods or classes that will take different types of input (yaml, json, csv, sql, mongodb), create/update the character, and then other classes/methods to output into some format.

A python dict does well to store data and it can be laid out in json format which can be put into MongoDB. Using a key/value pair makes the data structure very extensible. For example, if I want a "Supplement 4" version output of a character I don't need to worry about positional arguments; I just don't include the "keirsey" or "plot" values the data stores.

My primary use case is for fiction writing. It will probably work for gamers as well; I'm adding information about the characters as they develop. Between "generate_basic()" and "fill_out_char()" method I can get good data on characters. However, I add relationships, personal plot arcs, temperaments, appearance, political affiliations, etc. Stuff most folks won't need but are useful. The issue with something like json/MongoDB is that it's a lot harder to edit as plain text. Some folks may want yaml or csv. Or we get around to writing tkinter interfaces into the datastores. :)

My answer to "Which Edition?" is "Yes, as I get to it.". :rofl:

Looking at the code I've written (T5 worldgen, some chargen and cargogen stuff too) ... I tend to use class variables rather than dicts - character.str rather than character['str']. This is a personal preference - it feels "cleaner" to me than using a dict. A dict is easily extensible, but it's no big deal to add your data in a child class. Adding a json_export() method isn't a big deal, although it is something you need to plan for rather than json.dumps() (although you'd need to provide your own json_export() in any child class).

I suspect this preference may be in the minority here and I don't have a problem with that. From a collaborative perspective it makes sense to go with what people are most comfortable with.

And to wrench this briefly back to which Python version to use, I'm primarily working in 2.7 (default python version on my system) while trying to maintain python3 compatibility. Unit tests get run with both nosetests and nosetests3. I've yet to run into major compatibility issues, but if pressed I'd probably stick with 2.7.
 
Ah, I meanet "use dicts internally." Mostly. And that's just what I do, not what might be best. My guess is I need to re-read PEP-8 and see what habits I need to change.

The py_tools repo tests, such as they are, go under Travis-CI for 2.6, 2.7, 3.6, and nightly. The latter for no real reason except "want to". I code under 2.6.6 and test 3.6.2 locally before pushing up.

Happy to work with you on this, if you like. I have the pre-release CD for T5.
 
Coding encouragement quote:

"In our experience, the design of classes and objects is an incremental, iterative process. Frankly, except for the most trivial abstractions, we have never been able to define a class exactly right the first time." -- Grady Booch, "Object-Oriented Analysis and Design with Applications", (2nd ed) section 3.6, page 136.

If someone of his caliber can't get things right the first time then I'm guessing we can muddle around a bit.

I'll add roll_flux to base_tools.py, and some more tests. What else do we need to be able to support the different world types?
 
The problem with putting attributes in a dict is that for access outside the class you don't want to have to expose the dict directly. That looks awkward.

print(world.atmosphere)

versus

print(world.attributes['atmosphere'])

Though of course you can use a dict internally and use properties to expose it externally.

For the Traveller mapping app I'm toying with on my iPad I have quite a complex implementation of a World object, but that's because I have a lot of different functionality that interacts with it. Here's my AttributeValue class:

Code:
class AttributeValue(object):
    def __init__(self, attribute, code):
        self.attribute = attribute
        self.name = attribute.name
        self.code = code
        self.description = attribute.description(code)
        self.details = attribute.details(code)
    
    def get_label(self):
        return '{0} - {1}'.format(self.code,
                                  self.description)
    
    def __str__(self):
        return self.code

This is useful because it means for each attribute I can directly access various details of the attribute. It's hex code value, the text description of the value, even it's name so I can iterate over a collection of them and determine the name of each one directly. Actually I need to add a property to access the attribute value as an integer.

These attribute objects are initialised from attribute data that looks like this

Code:
starport_data = \
     {"Name": "Starport",
      "Codes": ['X', 'E', 'D', 'C', 'B', 'A'],
     "Code Data": \
          OrderedDict([("X", {"Label": "None",
                "Description": "No facilities for landing spacecraft are available." +\
                ""}),
          ("E", {"Label": "Landing Pad",
                "Description": "Basic landing area and shelter only." +\
                ""}),
          ("D", {"Label": "Basic Facility",
                "Description": "Only basic landing and service facilities are available." +\
                ""}),
          ("C", {"Label": "Small Spaceport",
                "Description": "Comprehensive landing and docking facilities, " +\
                "suitable for routine operations. No shipyard and only basic maintenance " +\
                "facilities are available."}),
          ("B", {"Label": "Spaceport",
                "Description": "Comprehensive landing and docking facilities. " +\
                "Includes a shipyard capable of constructing and overhauling in-sytem spacecraft. " +\
                ""}),
          ("A", {"Label": "Starport",
                "Description": "A full starport with many landing and docking sites. " +\
                "Includes a shipyard capable of building and overhauling any kind of spacecraft, " +\
                "including ships with interstellar capability."})]
          )
      }

Code:
class World(object):
    STARPORT = 0
    SIZE = 1
    ATMOSPHERE = 2
    HYDROGRAPHICS = 3
    POPULATION = 4
    GOVERNMENT = 5
    LAW_LEVEL = 6
    TECH_LEVEL = 7
    
    def __init__(self, name=None, stats=None):
        stats = stats if stats else WorldGen.getWorldStats()
        self.id = uuid.uuid4()
        
        self.name = name
        self.starport = AttributeValue(
                                attribute=STARPORT,
                                code=stats[0])
etc...


The down site of this is I can't have code directly set the value of an attribute, instead the World class has setters to update attributes using either a hex code or an integer.

Then in the World class each attribute member contains an object of this class.

e.g.

Code:
    def set_code(self, name, code):
        if name == self.starport.name:
            self.starport = AttributeValue(attribute=STARPORT,
                                           code=code)
etc...

And access the uwp like this:

Code:
    def uwp(self):
        return '{0}{1}{2}{3}{4}{5}{6}-{7}'.format(self.starport.code,
                                                  self.size.code,
                                                  self.atmosphere.code,
                                                  self.hydrographics.code,
                                                  self.population.code,
                                                  self.government.code,
                                                  self.law_level.code,
                                                  self.tech_level.code)

This might be a bit over-engineered as a general solution though.

I don't have this in Github because github integration from Pythonista is a bit painful, though this is changing.

Simon
 
My code is open-source so feel free to integrate it into any project :-)

I am also working on an upgraded version with better structure (OOP) but this takes time...
 
I've been looking at the code and I really like T5_worldgen. Its far more complete than my own world gen stuff in github and very nicely architected.

egor045 - I'm just poking around but I've created a branch for my own experimentation. I've got at least one commit for a minor issue, for which I've created a pull request, and might have a few more as I explore the code base.

Simon Hibbs
 
I've been looking at the code and I really like T5_worldgen. Its far more complete than my own world gen stuff in github and very nicely architected.

egor045 - I'm just poking around but I've created a branch for my own experimentation. I've got at least one commit for a minor issue, for which I've created a pull request, and might have a few more as I explore the code base.

Simon Hibbs

I've merged the pull request - happy to see someone else work on the code.

At some stage I want to extend it out to do full system generation, which may involve some substantial code changes (I got it to the point needed to set up a couple of subsectors for a Solo campaign). I'll be sure to put forward an outline here before I start making changes, just in case I upset some stuff you might be working on.
 
Looking on Makhidkarun currently there are I think five different projects for generating world data, one of them mine. I was about to merge mine into py_tools, but I'm considering what would be a responsible way to do that.

I did a CT6-ish world generator a while back in Python, but the code base is pretty rough. The bit that might be salvageable is a module that renders subsectors in SVG and book 6-ish listings from a JSON file.
 
Any upgrades to Starbase are greatly anticipated.

Regarding which ruleset... what is the purpose? If it's for consumption by the future Traveller community, then I would go with Mongoose II. T5 is not going to catch on with the broader community for some time if ever.

I would suggest getting interest with those individuals who are buying products, starting out in Traveller and generally growing as a population currently which are the Mongoose II people. The inability to put together custom maps is really inhibiting on people experimenting with Traveller and their own ATU. If you fill that gap and get people using your code, you can always branch out with different generation rulesets.

But if you focus on T5, you're limiting the audience right out of the gate. If T5 is what you want to use it for, cool, it's your project. But these types of programs, Starbase for example, are a great and easy tool that can get folks out there jazzed up and using Traveller in different ways. Once that happens, maybe they'll get interested enough to get into T5 at a later date. And a ruleset maybe could be waiting for them.

I spent hours with Universe and it really encouraged me to dig further and learn more about Traveller. It got me excited about the possibilities.

On a sidenote, if you were to put together an SWN ruleset, a lot of folks out there would be excited about it also. I've used Starbase for it easily enough but it'd be nice to not have to write in all of the different tech levels, "UWP" stuff.

Both Mongoose and SWN have growing player bases currently and should be the focus.
 
T5 is the direction that worldgen and subsector mapping has gone. New Traveller players are using the travellermap site. It makes sense that shared apps for Traveller worldgen and mapping align with T5. SWN is not Traveller.
 
T5 is the direction that worldgen and subsector mapping has gone. New Traveller players are using the travellermap site. It makes sense that shared apps for Traveller worldgen and mapping align with T5. SWN is not Traveller.

While T5 is a bit crunchier than I prefer, Shawn is right. Unifying on the Traveller Map is a step forward for all of us. The joy of Object Oriented programming is that we can sub-class to whatever output format (CT, MgT, Cepheus) we like. That's my plan, anyway.

That also means, if you like, you could code for T5 Traveller Map to SWN. :coffeesip:
 
Back
Top