Random galaxy setup options

For what's not in 'Top Priority Game Design'. Post your ideas, visions, suggestions for the game, rules, modifications, etc.

Moderator: Oberlus

Post Reply
Message
Author
User avatar
Vezzra
Release Manager, Design
Posts: 6095
Joined: Wed Nov 16, 2011 12:56 pm
Location: Sol III

Random galaxy setup options

#1 Post by Vezzra »

There is another design discussion that started on github in the comment section of a PR, which I think should be continued here on the forum. The PR in question is PR#484, "Setup random". The github discussion starts here.

These have been the comments so far:
Vezzra wrote:Ok, I've taken a brief look at this, and I don't think this implementation will work as intended. I guess this PR has been made in an attempt to implement this one request on the forum where one guy suggested to to offer a "random" option for several settings (like the galaxy shape already has), to make for a more challenging game because you don't know what to expect.

Assuming that's the case, and if I understood that proposal correctly, these "random" options should work like the "random" galaxy shape - instead of you picking the setting, the game picks one randomly for you.

In this case what we want is that one option gets randomly chosen, but after that the game should stick to that option and continue as if the player had chosen it. However, the way that is implemented in this PR the random choice is triggered each time the setting is accessed.

Which would be fine if each setting that offers the "random" option would be queried only once, but we can't rely on that, as the redundant implementation of the PyGalaxySetupData class and use in turn_events.py shows. Each time the setting gets accessed, it will randomly re-determine the actual option.

Moreover, the galaxy setup options are also exposed to FOCS. The FOCS interface will of course access the GalaxySetupData instance on the C++ side, which has nothing to do with anything implemented on the Python side (actually yielding the "random" option instead of a randomly picked "real" option).

I don't think that this has been the idea. I think the only way to implement that is to move the handling of the random option into the backend C++ code, the GalaxySetupData struct to be precise. This structure needs to be extended to hold the original settings the player has chosen (those are needed to be shown to the player in the pedia, and maybe in some other cases?), and the actual settings the game uses and exposes to FOCS and Python. The actual random picking can be triggered by a dedicated member function, or when the setting is first accessed (will require wrapping access to the settings with getters and setters).

@geoffthemedio, correct me if I got anything wrong here?
Geoff the Medio wrote:Re-picking one of the options each time was intentional. It probably doesn't actually matter for the universe generation scripts though, as other than for some logging calls, the randomized galaxy setup info doesn't appear to be accessed repeatedly. I might have missed something, though.

I didn't consider the use of the galaxy setup settings in content scripts, though that could be easily made to work the same way in Variable::Eval

Part of the idea was to keep the actual choice unknown to the player, rather than picking and displaying one of the options for the player.
Geoff the Medio wrote:Admittedly, having a new value each times it's checked for several of the setup options that determine the probabilities of things happening in scripts will produce results somewhat different from what might be expected.
Lets continue this discussion here.

User avatar
Geoff the Medio
Programming, Design, Admin
Posts: 13587
Joined: Wed Oct 08, 2003 1:33 am
Location: Munich

Re: Random galaxy setup options

#2 Post by Geoff the Medio »

To avoid potentially weird results if the frequencies are polled repeatedly by content scripts, and get different results each time, the result returned could be made to depend only on a hash of the random seed.

A related question is: how important is it that the randomly-selected values for frequencies or galaxy ages are consistent between universe generation scripts and values used in FOCS scripts in later turns? If they need to be consistent, then the determination of the value would need to be done in C++, and not in Python as currently implemented in the pull request.

All this is made a bit more complicated because I'm hoping to not indicate to players what the actual value of options that are specified as "Random" is, meaning not storing that value, and just recalculating it every time it's requested in Python or a ValueRef.

User avatar
Vezzra
Release Manager, Design
Posts: 6095
Joined: Wed Nov 16, 2011 12:56 pm
Location: Sol III

Re: Random galaxy setup options

#3 Post by Vezzra »

The problem with the current implementation is that the results most likely will be not only unexpected, but confusing and inconsistent, both for players and scripters.

The fundamental issue is that the different frequency settings of the GalaxySetupData structure are accessed in quite different ways. A good example for that is what you already mentioned:
Geoff the Medio wrote:It probably doesn't actually matter for the universe generation scripts though, as other than for some logging calls, the randomized galaxy setup info doesn't appear to be accessed repeatedly. I might have missed something, though.
For most of the various parts of the universe generation (placing of specials, natives, etc.) your observation that the galaxy setup info only gets accessed once (usually passed as a parameter to a function which does the placing/distribution) is correct - but not for all. The function generating the systems for example gets the complete structure passed in, and the age and planet density options are accessed for each system and planet:

Code: Select all

def generate_systems(pos_list, gsd):
    """
    Generates and populates star systems at all positions in specified list.
    """
    sys_list = []
    for position in pos_list:
        star_type = pick_star_type(gsd.age)
        system = fo.create_system(star_type, "", position[0], position[1])
        if system == fo.invalid_object():
            # create system failed, report an error and try to continue with next position
            util.report_error("Python generate_systems: create system at position (%f, %f) failed"
                              % (position[0], position[1]))
            continue
        sys_list.append(system)
        for orbit in range(fo.sys_get_num_orbits(system)):
            # check for each orbit if a planet shall be created by determining planet size
            planet_size = planets.calc_planet_size(star_type, orbit, gsd.planet_density, gsd.shape)
            if planet_size in planets.planet_sizes:
                # ok, we want a planet, determine planet type and generate the planet
                planet_type = planets.calc_planet_type(star_type, orbit, planet_size)
                if fo.create_planet(planet_size, planet_type, system, orbit, "") == fo.invalid_object():
                    # create planet failed, report an error and try to continue with next orbit
                    util.report_error("Python generate_systems: create planet in system %d failed" % system)
    return sys_list
Essentially it makes a dramatic difference if a scripter decides to read a setting once from the GalaxySetupData structure and uses the once read value for the rest of the computations, or reads the setting repeatedly, every time it's needed. The universe generation scripts weren't implemented with the idea in mind that reading a settings value yields different results each time. Without further adjustments this will yield very different results for the different settings. While the placement of natives, specials and monsters (IIRC) will pick one option and stick to that (so that you actually get one of the none/low/med/high distributions), "random" age and planet density will get applied very differently. You will most certainly not get a consistently young/mature/ancient galaxy, and a consistently low/medium/high planet density.

For the player that will be very confusing, because the "random" option behaves so differently for different settings.

As far as the scripters are concerned, I don't want them to have to pay attention to something like that. Of course, on the Python side we can take care of the problem quite easily, just implement the PyGalaxySetupData class in a way that it only does the random pick on first access, store the picked value internally and return the stored value on all subsequent calls.

That can, in theory, even be extended to the turn event scripts, as they are run within the same Python interpreter instance, with the same context, so all we need to do is move the GalaxySetupData class to a common module, and the data will persist between universe generation and turn events. Unless you save and reload the game, however - that's why I said, "in theory". In practice we need to implement a mechanism that provides the means to store and retrieve data on save and load. As that is needed anyway and will have to be implemented at some point, this isn't really an issue.

But all this doesn't get around the problem that Python and FOCS are still completely disconnected. You already raised that point too:
Geoff the Medio wrote:how important is it that the randomly-selected values for frequencies or galaxy ages are consistent between universe generation scripts and values used in FOCS scripts in later turns?
IIRC the main reason why we exposed those settings to FOCS was so that content scripters could adjust calculations for certain stats/values/whatever to the settings the player had chosen, and therefore to what the galaxy/map is expected to look/be like based on those settings. Examples:
  • Almost every Experimentors related calculation factors in the AI aggression setting, the calculation of the turn they start to spawn their monsters also factor in galaxy size and planet density.
  • Moster frequency is checked by all specials that need to decide if a guardian is spawned or not.
  • Tech costs for the Singularity Of Transcendence tech is based on galaxy size.
  • There have already been ideas brought up to scale PP/RP/whatever costs for whatever items based on map size/age/planet density to better scale costs with the scale of the game.
As all those operate on the assumption that the option value they get actually reflect what got used by the universe generation process, I think it's obvious what kind of issues this will cause. The results will be highly unexpected and most likely not what has been intended.

Even that aside, any content script that base calculations on one of the settings will most likely be set up in a way that expect "stable" values for the galaxy settings. For example, monster nest scripting that bases their spawning of new monsters on the monster frequency setting (would make sense, wouldn't it?) would deliver results probably not quite to the scripters expectations and intentions.

Speaking of which, that brings me to the second major flaw I see with that approach, which will affect content scripts especially (because contrary to Python they can't access the settings once and store the retrieved value): The "random" setting won't really deliver random results when used in way where the settings option value gets retrieved every time it's needed for a computation, because it will statistically "even out" (can I say that in English this way, or does that sound stupid?). Over the course of the game, or the placement/distribution/whatever of many items during universe generation, I'd expect the "random" setting to produce results that are probably different, but still similar to the respective average/medium settings. Even if the results were very different to medium, they would probably be consistently comparable in the sense that they will create a certain "kind" of distribution that is characteristic for the "random" setting, like all the other options will produce results that are characteristic for them.

What you certainly won't get is a distribution that is characteristic of one randomly picked option, but that's actually what we want.

That leaves the question, why we'd want to go with an apporach that has such obvious issues, instead of just go with the simple pick once and stick with that solution. If I understand correctly, your main concern is that the player needs to stay in the dark about the options that got picked, because not knowing what kind of galaxy/map he is on is the point of the whole thing after all.

But that can be achieved without resorting to picking a new value each time a setting is accessed. Basically what the GalaxySetupData structure has to do is provide and keep track of two values for each setting: the one the player has chosen in the game setup dialog (I'll refer to that as the "selected option") and the one the game actually uses for all related computations (I'll refer to that as the "actual option"). Python needs to be able to read both, and write back the actual options. FOCS probably only needs to be able to read the actual options. The player only ever gets to see the selected options, whereever they are presented to him.

Extending the GalaxySetupData structure (and adjusting the Python and FOCS bindings) accordingly shouldn't be too hard I guess, and I think that is the simplest and most straightforward solution. Your idea of providing a hash based on the RNG seed to ensure that the same "random" value gets picked each time a setting is accessed could be done too I guess, but ensuring that both Python and FOCS produce the same results, and the required getter/setter framework will probably more complicated than just extending GalaxySetupData as I suggested above.

User avatar
Geoff the Medio
Programming, Design, Admin
Posts: 13587
Joined: Wed Oct 08, 2003 1:33 am
Location: Munich

Re: Random galaxy setup options

#4 Post by Geoff the Medio »

Vezzra wrote:Python needs to be able to ... write back the actual options.
Why?

User avatar
Vezzra
Release Manager, Design
Posts: 6095
Joined: Wed Nov 16, 2011 12:56 pm
Location: Sol III

Re: Random galaxy setup options

#5 Post by Vezzra »

Geoff the Medio wrote:
Vezzra wrote:Python needs to be able to ... write back the actual options.
Why?
Um, that statement was based on the assumption that the random picking would still occur on the Python side, so Python would have to be able to write the picks back. Your pseudo-random picking based on the seed hash approach is simpler (once we get around the remaining issues), so moot point.

Post Reply