The International Simutrans Forum

 

Author Topic: How to get started on balancing properly  (Read 23324 times)

0 Members and 1 Guest are viewing this topic.

Offline moblet

  • *
  • Posts: 250
Re: How to get started on balancing properly
« Reply #70 on: January 28, 2011, 09:16:22 AM »
I'll start a list of the dynamics/features
Great start Hajo. Can we split this post into a new topic on the documentation board? Sounds like it might be a useful exercise for familiar and unfamiliar participants alike. I suggest largely completing the exercise for Standard before starting on Experimental.
Quote from: James
Take the private cars, for example: the code for whether passengers use a private car is bound up in the same code as decides where their destinations are and which route to take for the trip generally;
I don't know what you mean by "bound up". These are two separate computational tasks, one of which applies to other modes, so there is no reason for them to be "bound up" and a compelling case for coding them as separate modules. They are connected, but from a programming design perspective all that is required is that one module invokes the other. If they have not been designed and built as separate modules then this is an example of complexity, and increased difficulty of subsequent alteration, that would not occur in a program that was carefully designed before it was built. That is not criticise the work that's been done, especially as it's an open source, volunteer gig, just an observation of how things seem to me, and perhaps a case for documenting, consolidating, and maybe reconfiguring in places, the way the Standard engine works.
Quote from: James
Is congestion a separate feature from private car ownership? Is it part of the private car model; or is it part of the city growth model? Do we have to count it twice, once when we deal with passenger routing and once when we deal with the city growth model, or do we describe the city growth model incompletely and then describe the congestion model, including its impact on city growth, elsewhere?
The answers to these questions should have been determined before any of it was coded. Perhaps they were, and maybe the decisions were documented somewhere.
Quote from: James
One might attempt a narrative, but it would be very, very, very long: essentially, a natural language version of all of the economic aspects of the code.
The reason this narrative has been dragging on for so long is because we don't have any such document. What I envisage is more a case of a few points against each feature/dynamic describing what assumptions are made in the sim, and what the sim is supposed to represent. If we don't have that, and the code is our only reference, then we can't evaluate the code against what it's supposed to achieve, or properly consider alternative ways of coding.
Quote from: James
Might it not be better simply to use existing sources of information (the wiki and the list of Simutrans-Experimental specific features)?
I think Hajo answered this question by starting his list. He didn't link to anything existing.
Quote from: James
I am still not quite sure what such a list might look like or how exactly it might be used (the latter, of course, informing the former).
Think of it as a specification rather than a list.
Quote from: James
Interesting - you edit your cities? There's divergence of play style there, I think.
The point is not about playing styles, it's about the long-term consequences of not thinking far enough ahead.
Quote from: James
How do you see the significance of the fact that Experimental is not a stand-alone project, but is parasitic on Simutrans-Standard, its raison d'etre simply being to do whatever Simutrans-Standard does with more economic and operational depth and realism?
Experimental is not parasitic, it is an extension of Standard. Experimental doesn't pick bits out of Standard, it builds additional features upon Standard's foundations. To take a parasitic approach one doesn't need to understand how Standard works, just identify the bits one wants to steal. To build an extension one has to first understand the foundation upon which one is building. So I see a process like this:
1. Document and understand what Standard does, and also what it doesn't do
2. Identify everything that Standard does that is undesirable, and what it doesn't do that is desirable
3. Develop designs that integrate the desired changes into the simplest possible game, coded in the simplest possible way
4. Assess each design against criteria such as coding hours required, modularity, expandability, simplicity of calibration, etc, and select one
5. Start building, and so on
Quote from: James
In practical terms, how would one use a list/narrative of dynamics for balancing purposes? Is the idea that it would make it easier to see what is missing or mis-designed?
One needs to know the specification to balance, but the specification has many more uses than that, including seeing what is missing or sub-optimally designed.
Quote from: James
I do have a good idea in the abstract of what I want Simutrans-Experimental to be, but it is not always easy to think of all the things that need to be simulated at once.
Can you articulate that vision? Because I can't see what it is, and am concerned that it may contain fatal contradictions that will only otherwise become apparent after investing hundreds of hours into development trying to make work what cannot work (an open-ended process if one never realises that it can't work). I think a couple of steps have been skipped, including developing those abstract ideas into practical ones before rushing into coding Experimental. No, it is not easy to visualise everything at once, although it does become easier with practice. If all of those things are not documented in one place then (1) you will have great difficulty entertaining all of them at once and (2) no one else will be able to contribute.
Quote from: James
The difficulty that I have had so far is knowing exactly what dynamics I need to be modelling in the first place; but perhaps you could assist with that? Indeed, is that in a sense what you're proposing here...?
How can one possibly build a simulation model without first deciding what dynamics need to be modelled? Even if the model was sound one wouldn't know what it represented or how to interact with it. I can help, but only in a culture of developing understanding before trying to build the finished product.
Quote from: James
what had you imagined that an overall system view would look like? How detailed need the descriptions of the features/dynamics be, and to what extent would we need painstakingly to calculate every economically significant emergent property of other features/dynamics or sets thereof? What do you imagine us then doing with that information such as to create the most efficient approach to realising the design goal of a well-balanced Simutrans-Experimental: in other words, a well balanced version of Simutrans that does what Simutrans-Standard does, but with greater economic and operational depth and realism? I'm not quite clear at this juncture about what exactly that such a process would entail.
I think we need to follow the steps I listed above.
As for evaluating what dynamics to include, it usually only takes a few minutes to evaluate the kind of difference a feature could make, and it's a good filter - if someone keeps insisting that an insignificant dynamic must be included, it shouldn't be difficult to produce a few calculations to shut them up and help them set their mind on something more significant. The evaluation process is what tells us which trees are big, and which ones are small. That is a picture that develops in one's mind over time, but the nature of a collaborative distributed effort means it also needs to be documented so we don't have to keep repeating our conclusions. To revisit some Simutrans history, this post lists things that "will never happen" because they were deemed insignificant, too difficult etc, but it doesn't present the assumptions/calculations that were used to reach the conclusions. Others have since revisited them and now some of these things are happening. We need to "prove" that something does or doesn't add anything to the game, or quantify the difference it will make so we can reach a sensible and defensible decision on whether or not to include it. Sounds like work but it takes out all the stress and confusion, and stops all the "why didn't you include x?" traffic, so in my experience it ends up feeling like less work.
Quote from: sdog
Extracting the relevant dynamics from a system, describing, analysing and finaly recommending a change seems to be exactly the thing Moblet is doing for a living?
Good guess sdog! I have a degree in applied mathematics and used to analyse and model supply chains and expansion options for a living, especially mine-rail-port systems. Lost my health a few years back and had to quit work. Health is slowly improving - not long ago I couldn't have kept up with this discussion.
Quote from: sdog
i think my texts often read quite overcritical
In the ones I've seen that is very rarely the case, and you seem to be aware of when translation difficulties could cause misunderstanding. You engage with the issues on a technical level, not a personal one, and you get straight to the heart of them, which is really valuable.
Quote from: sdog
James i think you did in particular a brilliant start with experimental. If it's only analysed nothing ever gets done, by just implementing concepts that seemed obviously missing you get things moveing.
I'll second this.

Offline jamespetts gb

  • Simutrans-Extended project coordinator
  • Moderator
  • *
  • Posts: 18374
  • Cake baker
    • Bridgewater-Brunel
  • Languages: EN
Re: How to get started on balancing properly
« Reply #71 on: January 28, 2011, 10:37:15 AM »
Extracting the relevant dynamics from a system, describing, analysing and finaly recommending a change seems to be exactly the thing Moblet is doing for a living? The nice thing is, here one could always look it up in the source code, a bit more difficult IRL :-]

Oh to be able to debug reality...

Offline Spike

  • *
  • Posts: 1361
  • First Simutrans Developer and Graphics Artist
Re: How to get started on balancing properly
« Reply #72 on: January 28, 2011, 10:52:38 AM »
Great start Hajo. Can we split this post into a new topic on the documentation board? Sounds like it might be a useful exercise for familiar and unfamiliar participants alike.

I've reposted the list over here:
http://forum.simutrans.com/index.php?topic=6736.msg64966#msg64966

Offline ӔO

  • Devotees (Inactive)
  • *
  • Posts: 2345
  • Hopefully helpful
  • Languages: en, jp
Re: How to get started on balancing properly
« Reply #73 on: January 28, 2011, 11:14:50 AM »
currently, I don't really think there is any feature that needs to be removed from experimental
http://forum.simutrans.com/index.php?topic=1959.0
there are enough features to give differentiation and specialization to various vehicles. It's all a matter of tweaking the settings.

In the previous version, there were not enough passengers generated.
In the current version, there are too many passengers generated.

We'll probably have to bounce around the settings like that for a few versions, until an ideal balance is struck.

Offline Spike

  • *
  • Posts: 1361
  • First Simutrans Developer and Graphics Artist
Re: How to get started on balancing properly
« Reply #74 on: January 28, 2011, 11:23:16 AM »
I know it's very time consuming, but I think it's important that the developer(s) also play the game to get an impression what works well and what doesn't. Nowadays I see many pak set developers depend on spreadsheets and formulas, but at some point the proper tuning can only be done from experience by playing.

Offline jamespetts gb

  • Simutrans-Extended project coordinator
  • Moderator
  • *
  • Posts: 18374
  • Cake baker
    • Bridgewater-Brunel
  • Languages: EN
Re: How to get started on balancing properly
« Reply #75 on: January 28, 2011, 11:35:03 AM »
I don't know what you mean by "bound up". These are two separate computational tasks, one of which applies to other modes, so there is no reason for them to be "bound up" and a compelling case for coding them as separate modules. They are connected, but from a programming design perspective all that is required is that one module invokes the other. If they have not been designed and built as separate modules then this is an example of complexity, and increased difficulty of subsequent alteration, that would not occur in a program that was carefully designed before it was built. That is not criticise the work that's been done, especially as it's an open source, volunteer gig, just an observation of how things seem to me, and perhaps a case for documenting, consolidating, and maybe reconfiguring in places, the way the Standard engine works.

The code in question looks like this:

Code: [Select]
/* this creates passengers and mail for everything is is therefore one of the CPU hogs of the machine
 * think trice, before applying optimisation here ...
 */
void stadt_t::step_passagiere()
{
//@author: jamespetts
// Passenger routing and generation metrics.
const uint32 local_passengers_min_distance = welt->get_einstellungen()->get_local_passengers_min_distance();
const uint32 local_passengers_max_distance = welt->get_einstellungen()->get_local_passengers_max_distance();
const uint32 midrange_passengers_min_distance = welt->get_einstellungen()->get_midrange_passengers_min_distance();
const uint32 midrange_passengers_max_distance = welt->get_einstellungen()->get_midrange_passengers_max_distance();
const uint32 longdistance_passengers_min_distance = welt->get_einstellungen()->get_longdistance_passengers_min_distance();
const uint32 longdistance_passengers_max_distance = welt->get_einstellungen()->get_longdistance_passengers_max_distance();

const uint16 min_local_tolerance = welt->get_einstellungen()->get_min_local_tolerance();
const uint16 max_local_tolerance = max(0, welt->get_einstellungen()->get_max_local_tolerance() - min_local_tolerance);
const uint16 min_midrange_tolerance = welt->get_einstellungen()->get_min_midrange_tolerance();
const uint16 max_midrange_tolerance = max( 0, welt->get_einstellungen()->get_max_midrange_tolerance() - min_midrange_tolerance);
const uint16 min_longdistance_tolerance = welt->get_einstellungen()->get_min_longdistance_tolerance();
const uint16 max_longdistance_tolerance = max(0, welt->get_einstellungen()->get_max_longdistance_tolerance() - min_longdistance_tolerance);

const uint8 passenger_packet_size = welt->get_einstellungen()->get_passenger_routing_packet_size();
const uint8 passenger_routing_local_chance = welt->get_einstellungen()->get_passenger_routing_local_chance();
const uint8 passenger_routing_midrange_chance = welt->get_einstellungen()->get_passenger_routing_midrange_chance();

// DBG_MESSAGE("stadt_t::step_passagiere()", "%s step_passagiere called (%d,%d - %d,%d)\n", name, li, ob, re, un);
// long t0 = get_current_time_millis();

// post oder pax erzeugen ?
// "post or generate pax"
const ware_besch_t* wtyp;
if (simrand(400) < 300)
{
wtyp = warenbauer_t::passagiere;
}
else
{
wtyp = warenbauer_t::post;
}
const int history_type = (wtyp == warenbauer_t::passagiere) ? HIST_PAS_TRANSPORTED : HIST_MAIL_TRANSPORTED;

// restart at first buiulding?
if (step_count >= buildings.get_count())
{
step_count = 0;
}
if(buildings.get_count()==0)
{
return;
}
const gebaeude_t* gb = buildings[step_count];

// prissi: since now backtravels occur, we damp the numbers a little
const int num_pax =
(wtyp == warenbauer_t::passagiere) ?
(gb->get_tile()->get_besch()->get_level()      + 6) >> 2 :
max(1,(gb->get_tile()->get_besch()->get_post_level() + 5) >> 4);


// Hajo: track number of generated passengers.
city_history_year[0][history_type+1] += num_pax;
city_history_month[0][history_type+1] += num_pax;

// create pedestrians in the near area?
if (welt->get_einstellungen()->get_random_pedestrians() && wtyp == warenbauer_t::passagiere)
{
haltestelle_t::erzeuge_fussgaenger(welt, gb->get_pos(), num_pax);
}

// suitable start search
const koord k = gb->get_pos().get_2d();
const planquadrat_t* plan = welt->lookup(k);
const halthandle_t* halt_list = plan->get_haltlist();

minivec_tpl<halthandle_t> start_halts(plan->get_haltlist_count());
for (int h = plan->get_haltlist_count() - 1; h >= 0; h--)
{
halthandle_t halt = halt_list[h];
if (halt->is_enabled(wtyp)  &&  !halt->is_overcrowded(wtyp->get_catg_index()))
{
start_halts.append(halt);
}
}

INT_CHECK( "simcity 2282" );

// Check whether this batch of passengers has access to a private car each.
// Check run in batches to save computational effort.
const sint16 private_car_percent = wtyp == warenbauer_t::passagiere ? get_private_car_ownership(welt->get_timeline_year_month()) : 0;
// Only passengers have private cars
const bool has_private_car = private_car_percent > 0 ? simrand(100) <= (uint16)private_car_percent : false;

// Record the most useful set of information about why passengers cannot reach their chosen destination:
//  Too slow > overcrowded > no route. Tiebreaker: higher destination preference.
koord best_bad_destination;
uint8 best_bad_start_halt;
bool too_slow_already_set;

//Only continue if there are suitable start halts nearby, or the passengers have their own car.
if(start_halts.get_count() > 0 || has_private_car)
{
//Add 1 because the simuconf.tab setting is for maximum *alternative* destinations, whereas we need maximum *actual* desintations
// const uint8 max_destinations = has_private_car ? 1 : (welt->get_einstellungen()->get_max_alternative_destinations() < 16 ? welt->get_einstellungen()->get_max_alternative_destinations() : 15) + 1;
// Passengers with a private car will not tolerate second best destinations,
// and will use their private car to get to their first choice destination
// regardless of whether they might go to other destinations by public transport.

// Now that private cars also have a journey time tolerance and check whether the roads are connected, passengers *might*
// not be able to get to their destination by private car either, so the above is redundant.
const uint8 max_destinations = (welt->get_einstellungen()->get_max_alternative_destinations() < 16 ? welt->get_einstellungen()->get_max_alternative_destinations() : 15) + 1;

// Find passenger destination
for (int pax_routed = 0; pax_routed < num_pax; pax_routed += passenger_packet_size)
{

/* number of passengers that want to travel
* Hajo: for efficiency we try to route not every
* single pax, but packets. If possible, we do 7 passengers at a time
* the last packet might have less then 7 pax
* Number now not fixed at 7, but set in simuconf.tab (@author: jamespetts)*/

int pax_left_to_do = min(passenger_packet_size, num_pax - pax_routed);

// search target for the passenger
pax_zieltyp will_return;

const uint8 destination_count = simrand(max_destinations) + 1;

// Split passengers: between local, midrange and long-distance
// according to the percentages set in simuconf.tab.
// Note: a random town will be found if there are no towns within range.
const uint8 passenger_routing_choice = simrand(100);
const journey_distance_type range =
passenger_routing_choice <= passenger_routing_local_chance ?
local :
passenger_routing_choice <= (passenger_routing_local_chance + passenger_routing_midrange_chance) ?
midrange : longdistance;
const uint16 tolerance =
wtyp != warenbauer_t::passagiere ?
0 :
range == local ?
simrand(max_local_tolerance) + min_local_tolerance :
range == midrange ?
simrand(max_midrange_tolerance) + min_midrange_tolerance :
simrand(max_longdistance_tolerance) + min_longdistance_tolerance;
destination destinations[16];
for(int destinations_assigned = 0; destinations_assigned <= destination_count; destinations_assigned ++)
{
if(range == local)
{
//Local - a designated proportion will automatically go to destinations within the town.
if((float)passenger_routing_choice <= adjusted_passenger_routing_local_chance)
{
// Will always be a destination in the current town.
destinations[destinations_assigned] = finde_passagier_ziel(&will_return, 0, local_passengers_max_distance, k);
}
else
{
destinations[destinations_assigned] = finde_passagier_ziel(&will_return, local_passengers_min_distance, local_passengers_max_distance, k);
}
}
else if(range == midrange)
{
//Medium
destinations[destinations_assigned] = finde_passagier_ziel(&will_return, midrange_passengers_min_distance, midrange_passengers_max_distance, k);
}
else
//else if(range == longdistance)
{
//Long distance
destinations[destinations_assigned] = finde_passagier_ziel(&will_return, longdistance_passengers_min_distance, longdistance_passengers_max_distance, k);  //"Ziel" = "target" (Google)
}
}

INT_CHECK( "simcity 2371" );

uint8 current_destination = 0;

route_status route_good = no_route;

const uint16 max_walking_distance = welt->get_einstellungen()->get_max_walking_distance();
uint16 car_minutes = 65535;

best_bad_destination = destinations[0].location;
best_bad_start_halt = 0;
too_slow_already_set = false;

while(route_good != good && route_good != private_car_only && current_destination < destination_count)
{
// Dario: Check if there's a stop near destination
const planquadrat_t* dest_plan = welt->lookup(destinations[current_destination].location);
const halthandle_t* dest_list = dest_plan->get_haltlist();

// Knightly : we can avoid duplicated efforts by building destination halt list here at the same time
minivec_tpl<halthandle_t> destination_list(dest_plan->get_haltlist_count());

halthandle_t start_halt;

// Check whether the destination is within walking distance first.
// @author: jamespetts, December 2009
if(accurate_distance(destinations[current_destination].location, k) <= max_walking_distance)
{
// Passengers will always walk if they are close enough.
route_good = can_walk;
}

if(route_good != can_walk)
{
for (int h = dest_plan->get_haltlist_count() - 1; h >= 0; h--)
{
halthandle_t halt = dest_list[h];
if (halt->is_enabled(wtyp))
{
destination_list.append(halt);
ITERATE(start_halts, i)
{
if(halt == start_halts[i])
{
route_good = can_walk;
start_halt = start_halts[i];
// Mail does not walk, but people delivering it do.
break;
}
}
}
}
}

// check, if they can walk there?

if (route_good == can_walk)
{
break;
}

// ok, they are not in walking distance
// Check whether public transport can be used.
ware_t pax(wtyp); //Journey start information needs to be added later.
pax.set_zielpos(destinations[current_destination].location);
pax.menge = (wtyp == warenbauer_t::passagiere ? pax_left_to_do : 1 );
//"Menge" = volume (Google)

// now, finally search a route; this consumes most of the time

uint16 best_journey_time = 65535;
uint8 best_start_halt = 0;

ITERATE(start_halts,i)
{
halthandle_t current_halt = start_halts[i];

uint16 current_journey_time = current_halt->find_route(&destination_list, pax, best_journey_time);
if(current_journey_time < best_journey_time)
{
best_journey_time = current_journey_time;
best_start_halt = i;
}
if(pax.get_ziel().is_bound())
{
route_good = good;
}
}

// Check first whether the best route is outside
// the passengers' tolerance.

if(route_good == good && tolerance > 0 && best_journey_time > tolerance)
{
route_good = too_slow;

if(!too_slow_already_set)
{
best_bad_destination = destinations[current_destination].location;
best_bad_start_halt = best_start_halt;
too_slow_already_set = true;
}
}
else
{
// All passengers will use the quickest route.
if(start_halts.get_count() > 0)
{
start_halt = start_halts[best_start_halt];
}
}

INT_CHECK("simcity.cc 2882");

if(has_private_car)
{
const uint32 straight_line_distance = accurate_distance(k, destinations[current_destination].location);
uint16 time_per_tile = 65535;
switch(destinations[current_destination].type)
{
case 1:
//Town
time_per_tile = check_road_connexion_to(destinations[current_destination].object.town);
break;
case FACTORY_PAX:
time_per_tile = check_road_connexion_to(destinations[current_destination].object.industry);
break;
case TOURIST_PAX:
time_per_tile = check_road_connexion_to(destinations[current_destination].object.attraction);
break;
default:
//Some error - this should not be reached.
dbg->error("simcity.cc", "Incorrect destination type detected");
};

if(time_per_tile < 65535)
{
// *Tenths* of minutes used here.
car_minutes =  time_per_tile * straight_line_distance;
}
else
{
car_minutes = 65535;
}
}

if(car_minutes <= tolerance)
{
if(route_good != good)
{
// The passengers can get to their destination by car but not by public transport.
// Therefore, they will certainly use their car.
route_good = private_car_only;
}

else
{
// The passengers can get to their destination both by car and public transport.
// Choose which they prefer.

// Now, decide whether passengers would prefer to use their private cars,
// even though they can travel by public transport.
const uint32 distance = accurate_distance(destinations[current_destination].location, k);

//Weighted random.
uint8 private_car_chance = simrand(100);
if(private_car_chance >= 1)
{
// The basic preference for using a private car if available.
sint16 car_preference = welt->get_einstellungen()->get_base_car_preference_percent();

//First, adjust for distance. For very long-distance journies, cars are less popular.

if(distance > (midrange_passengers_max_distance * 3))
{
if(distance >= longdistance_passengers_max_distance)
{
car_preference /= 10;
}
else
{
const float proportion = ((float)distance - (float)(midrange_passengers_max_distance * 3.0F)) / (float)longdistance_passengers_max_distance;
car_preference /= (10 * proportion);
}
}

// Secondly, congestion. Drivers will turn to public transport if the origin or destination towns are congested.

// This percentage of drivers will prefer to use the car however congested that it is.
const sint16 always_prefer_car_percent = welt->get_einstellungen()->get_always_prefer_car_percent();

//Average congestion of origin and destination towns, and, at the same time, reduce factor.
uint8 congestion_total;
if(destinations[current_destination].type == 1 && destinations[current_destination].object.town != NULL)
{
congestion_total = (city_history_month[0][HIST_CONGESTION] + destinations[current_destination].object.town->get_congestion()) / 4;
}
else
{
congestion_total = (city_history_month[0][HIST_CONGESTION] / 1.33);
}
car_preference = ((car_preference - congestion_total) > always_prefer_car_percent) ? car_preference - congestion_total : always_prefer_car_percent;

// Thirdly adjust for service quality of the public transport.
// Compare best journey speed on public transport with the
// likely private car journey time.

INT_CHECK( "simcity 3004" );

const float proportion = ((float)best_journey_time / (float)car_minutes) * car_minutes > best_journey_time ? 1.25F : 0.75F;
car_preference *= proportion;

// If identical, no adjustment.

//Fourthly, the number of unhappy passengers at the start station compared with the number of happy passengers.
float unhappy_factor = start_halt->get_unhappy_proportion(0);

if(unhappy_factor > 0.8F)
{
private_car_chance /= unhappy_factor;
}

//Finally, determine whether the private car is used.
if(private_car_chance <= car_preference)
{
// Private cars chosen by preference even though public transport is possible.
route_good = private_car_only;
}
}
}
}

if(route_good == can_walk)
{
// so we have happy passengers

// Passengers who can walk to their destination may be happy about it,
// but they are not happy *because* the player has made them happy.
// Therefore, they should not show on the station's happy graph.
// @author: jamespetts, December 2009

//start_halt->add_pax_happy(pax_left_to_do);

merke_passagier_ziel(destinations[0].location, COL_DARK_YELLOW);
if (welt->get_einstellungen()->get_random_pedestrians() && wtyp == warenbauer_t::passagiere)
{
haltestelle_t::erzeuge_fussgaenger(welt, start_halt->get_basis_pos3d(), pax_left_to_do);
}

// They should show that they have been transported, however, since
// these figures are used for city growth calculations.
city_history_year[0][history_type] += pax_left_to_do;
city_history_month[0][history_type] += pax_left_to_do;
}

else if(route_good == good)
{
// Passengers can and will use public transport.
pax.arrival_time = welt->get_zeit_ms();
pax.set_origin(start_halt);
start_halt->starte_mit_route(pax);
merke_passagier_ziel(destinations[current_destination].location, COL_YELLOW);
}

else if(route_good == private_car_only)
{
// Passengers have either chosen to use their car or have no other choice.
stadt_t* const destination_town = destinations[current_destination].type == 1 ? destinations[current_destination].object.town : NULL;
set_private_car_trip(num_pax, destination_town);
merke_passagier_ziel(destinations[current_destination].location, COL_TURQUOISE);
#ifdef DESTINATION_CITYCARS
erzeuge_verkehrsteilnehmer(k, step_count, destinations[current_destination].location);
}
#endif

// send them also back
// (Calculate a return journey)
if(will_return != no_return && route_good == good || route_good == private_car_only)
{
// this comes most of the times for free and balances also the amounts!
halthandle_t ret_halt = pax.get_ziel();
bool return_in_private_car = private_car_only || !ret_halt.is_bound();

if(!return_in_private_car)
{
// we just have to ensure, the ware can be delivered at this station
bool found = false;
for (uint i = 0; i < plan->get_haltlist_count(); i++)
{
halthandle_t test_halt = halt_list[i];

if(test_halt->is_enabled(wtyp) && (start_halt == test_halt || test_halt->get_connexions(wtyp->get_catg_index())->access(start_halt) != NULL))
{
found = true;
start_halt = test_halt;
break;
}
}

// now try to add them to the target halt
if(!ret_halt->is_overcrowded(wtyp->get_catg_index()))
{
// prissi: not overcrowded and can recieve => add them
if(found)
{
ware_t return_pax(wtyp, ret_halt);
if(  will_return != town_return  &&  wtyp==warenbauer_t::post  )
{
// attractions/factory generate more mail than they recieve
return_pax.menge = pax_left_to_do * 3;
}
else
{
// use normal amount for return pas/mail
return_pax.menge = pax_left_to_do;
}
return_pax.set_zielpos(k);
return_pax.set_ziel(start_halt);
if(ret_halt->find_route(return_pax) != 65535)
{
return_pax.arrival_time = welt->get_zeit_ms();
ret_halt->starte_mit_route(return_pax);
merke_passagier_ziel(destinations[current_destination].location, COL_YELLOW);
//ret_halt->add_pax_happy(pax_left_to_do);
// Experimental 7.2 and onwards does this when passengers reach their destination
}
}
else
{
// no route back
if(car_minutes < 65535)
{
// This assumes that the journey time in both directions is identical: but
// this may not be so if there are one-way routes.
return_in_private_car = true;
}
else
{
ret_halt->add_pax_no_route(pax_left_to_do);
merke_passagier_ziel(destinations[current_destination].location, COL_DARK_ORANGE);
}
}
}
else
{
// Return halt crowded. Either return by car or mark unhappy.
if(car_minutes < 65535)
{
return_in_private_car = true;
}
else
{
ret_halt->add_pax_unhappy(pax_left_to_do);
merke_passagier_ziel(destinations[current_destination].location, COL_RED);
}
}
}

if(return_in_private_car)
{
if(car_minutes < 65535)
{
// Do not check tolerance, as they must come back!
stadt_t* const destination_town = destinations[0].type == 1 ? destinations[0].object.town : NULL;
set_private_car_trip(num_pax, destination_town);
merke_passagier_ziel(destinations[current_destination].location, COL_TURQUOISE);
#ifdef DESTINATION_CITYCARS
//citycars with destination
erzeuge_verkehrsteilnehmer(k, step_count, destinations[0].location);
#endif
}
else
{
if(ret_halt.is_bound())
{
ret_halt->add_pax_no_route(pax_left_to_do);
}
merke_passagier_ziel(destinations[current_destination].location, COL_DARK_ORANGE);
}
}
} // Returning passengers

INT_CHECK( "simcity 3128" );
current_destination ++;
} // While loop (route_good)

if((route_good != good && route_good != private_car_only) && start_halts.get_count() > 0)
{
// Passengers/mail cannot reach their destination at all, either by public transport or private car,
// *but* there are some stops in their locality. Record their inability to get where they are going
// at those local stops accordingly.
halthandle_t start_halt = start_halts[best_bad_start_halt]; //If there is no route, it does not matter where passengers express their unhappiness.
if(!has_private_car || car_minutes > tolerance)
{
// If the passengers are able to use a private car, then they should not record as "no route"
if(too_slow_already_set)
{
if(start_halt.is_bound())
{
start_halt->add_pax_too_slow(pax_left_to_do);
}
merke_passagier_ziel(best_bad_destination, COL_LIGHT_PURPLE);
}
else
{
if(start_halt.is_bound())
{
start_halt->add_pax_no_route(pax_left_to_do);
}
merke_passagier_ziel(destinations[0].location, COL_DARK_ORANGE);
}
}
}

} // For loop (passenger/mail packets)

} // If statement (are some start halts, or passengers have private car)

else
{
// The unhappy passengers will be added to all crowded stops
// however, there might be no stop too
// NOTE: Because of the conditional statement in the original loop,
// reaching this code means that passengers must not be able to use
// a private car for this journey.

// all passengers without suitable start:
// fake one ride to get a proper display of destinations (although there may be more) ...
pax_zieltyp will_return;
//const koord ziel = finde_passagier_ziel(&will_return);
destination destination_now = finde_passagier_ziel(&will_return);

// First, check whether the passengers can *walk*. Just because
// they do not have a start halt does not mean that they cannot
// walk to their destination!
const double tile_distance = accurate_distance(k, destination_now.location);
if(tile_distance < welt->get_einstellungen()->get_max_walking_distance())
{
// Passengers will walk to their destination if it is within the specified range.
// (Default: 1.5km)
merke_passagier_ziel(destination_now.location, COL_DARK_YELLOW);
city_history_year[0][history_type] += num_pax;
city_history_month[0][history_type] += num_pax;
}
else
{
// If the passengers cannot walk, they will be unhappy.

bool crowded_halts = false;
for(  uint h=0;  h<plan->get_haltlist_count(); h++  )
{
halthandle_t halt = halt_list[h];
if (halt->is_enabled(wtyp))
{
halt->add_pax_unhappy(num_pax);
crowded_halts = true;
}
}

// Only show as being overcrowded if there are, in fact, potentially suitable but overcrowded stops.
if(crowded_halts)
{
merke_passagier_ziel(destination_now.location, COL_RED);
}

else
{
merke_passagier_ziel(destination_now.location, COL_DARK_ORANGE);
}
// we do not show no route for destination stop!
}
}

// long t1 = get_current_time_millis();
// DBG_MESSAGE("stadt_t::step_passagiere()", "Zeit f�r Passagierstep: %ld ms\n", (long)(t1 - t0));
}

A very rough overview of how it works is as follows:

* Cities generate passengers from particular buildings. The numbers depend on the density of the buildings and the passenger factor set in simuconf.tab. The numbers generated are logged appropriately.
* The passengers are either given or not given access to a private car, using a weighted random system, the weightings being based on the percentage of passengers with access to a private car at that point in time (set by using an external file, privatecar.tab).
* Passengers check whether there are any places where they can embark on transport ('bus stops, railway stations, etc.) within their radius.
* The passengers are assigned a destination building using a weighted random system based on the type of building (city building, industry, tourist attraction) and geographical distance from the origin (short-range, mid-range, long-distance*).
* Passengers are assigned a journey time tolerance level.
* Passengers check whether they can get to their destination, either by walking (if it is very close), taking the player's transport or taking their private car.
* If passengers can walk, they do so.
* If the passengers can take their private car (if they have one) but not any other means, they do so if the journey time does not exceed their tolerance.
* If passengers can use player transport but no other means, they calculate the quickest journey on player transport, and use that if the journey time does not exceed their tolerance.
* If passengers can use either private car or player transport, they evaluate which to use based on factors such as the relative speed of the journeys, the congestion in the origin and destination cities (the most important factor) and a fixed car preference value set in simuconf.tab. There is a certain percentage of passengers who will always take their private cars if they possibly can - this percentage is also set in simuconf.tab. They then take the appropriate mode, if the journey time does not exceed their tolerance.
* Once the passengers know which route that they are taking, their journey is logged in the appropriate ways. If a private car is used, an appropriate car graphic is generated, and traffic (and therefore congestion) numbers are adjusted accordingly in the origin and (if applicable) destination cities.
* If the passengers cannot reach their destination by any means, this too is logged (differentially depending on whether there is no route, or there is a route that exceeds their tolerance).

The above process also works for mail, except that mail cannot either walk or take a private car. There is also a mechanism to generate returning passengers, which, for the sake of parsimony, I have omitted from the rough description above. This is what I mean by "bound up", and it does not seem obvious that one could or should have a separate module for the private car journeys here.

* I am considering removing this element, as it might be that the journey time tolerances feature can be made, more realistically, to have the desired effect of this in any event.

Quote
The answers to these questions should have been determined before any of it was coded. Perhaps they were, and maybe the decisions were documented somewhere.

Ahh, I never really saw the need to have a conceptual separation of features into discrete units (as, after all, reality is not so compartmentalised). The approach was always simply to simulate, in so far as relevant and practicable, the various dynamics of reality and their interaction with one another.

Quote
The reason this narrative has been dragging on for so long is because we don't have any such document. What I envisage is more a case of a few points against each feature/dynamic describing what assumptions are made in the sim, and what the sim is supposed to represent. If we don't have that, and the code is our only reference, then we can't evaluate the code against what it's supposed to achieve, or properly consider alternative ways of coding.

So that I have an idea of where we're heading with this, can you give me an example/extract, perhaps based on the features described above relating to the passenger generation code?

Quote
The point is not about playing styles, it's about the long-term consequences of not thinking far enough ahead.

Ahh, that was more a casual aside than anything related to the main discussion!

Quote
Experimental is not parasitic, it is an extension of Standard. Experimental doesn't pick bits out of Standard, it builds additional features upon Standard's foundations. To take a parasitic approach one doesn't need to understand how Standard works, just identify the bits one wants to steal. To build an extension one has to first understand the foundation upon which one is building. So I see a process like this:
1. Document and understand what Standard does, and also what it doesn't do
2. Identify everything that Standard does that is undesirable, and what it doesn't do that is desirable
3. Develop designs that integrate the desired changes into the simplest possible game, coded in the simplest possible way
4. Assess each design against criteria such as coding hours required, modularity, expandability, simplicity of calibration, etc, and select one
5. Start building, and so on

This looks like the approach that one might adopt if one were starting Experimental to-morrow. Presumably, what we need to do now is to document and understand what Experimental does and doesn't do and evaluate then what of that is undesirable or missing?

Quote
Can you articulate that vision? Because I can't see what it is, and am concerned that it may contain fatal contradictions that will only otherwise become apparent after investing hundreds of hours into development trying to make work what cannot work (an open-ended process if one never realises that it can't work). I think a couple of steps have been skipped, including developing those abstract ideas into practical ones before rushing into coding Experimental. No, it is not easy to visualise everything at once, although it does become easier with practice. If all of those things are not documented in one place then (1) you will have great difficulty entertaining all of them at once and (2) no one else will be able to contribute.

The vision that I have for Experimental might be considered rather abstract, but it is this: given the basic elements and design assumptions of Simutrans (the simulation of operating a transport company by owning and running vehicles and ways, etc.), develop a version of it that will mean that the decisions that are made in the game by players have as similar effects as possible/practical on the things simulated by the game as in reality, such that a player playing such as to take game-optimal decisions will make the same decisions (in so far as possible) as, within the constraints of what is actually modelled by Simutrans, would have been similarly optimal decisions in reality, with as similar a set of consequences (within the constraints of what is actually modelled by Simutrans) as possible, such that, given realistic starting parameters (the giving of which is also a design goal of Experimental in so far as that is not possible in Standard), the development of transport networks in Simutrans by players making game-optimal decisions will follow as closely as possible the actual historical development of transport networks as they were actually developed, or might have been developed were it not for outside factors (wars, nationalisations, etc.) that are not inherent to the economics of transportation but were historically accidental, whilst still maintaining a game that is engaging and fun to play (at least for those who can tolerate a relatively high degree of complexity), and further (and relatedly) to mean that players making decisions in the game can make them on the basis of an abstract or heuristic understanding of real-world transportation, without having to engage in Simutrans-specific number crunching to calculate the optimal choice. At the same time, certain ancillary or secondary features that are not directed at that primary goal, but improve the user experience of the game (such as the vehicle replacer, or perhaps more cosmetic features such as the mooted livery variation system) are also desirable to be added along the way so long as they do not interfere with the primary goal. I like the idea that players (and, indeed, developers!) can learn about why things are as they are in transport terms by playing the game and seeing the various economic elements interact (which is one reason that I am particularly keen on using historical figures where possible).

Quote
How can one possibly build a simulation model without first deciding what dynamics need to be modelled? Even if the model was sound one wouldn't know what it represented or how to interact with it. I can help, but only in a culture of developing understanding before trying to build the finished product.I think we need to follow the steps I listed above.

Ahh, I think that I was not as clear as I might have been - I thought that I did have a good understanding of what needed to be modelled until you pointed out the various missing things that rendered good balancing impossible; I had also periodically realised that important dynamics of which I had not hitherto thought had not been modelled. The problem was more that I don't know what the game is missing in terms of dynamics that I don't know or don't remember exist: it is the epistemological problem of not knowing the full extent of one's ignorance.

Quote
As for evaluating what dynamics to include, it usually only takes a few minutes to evaluate the kind of difference a feature could make, and it's a good filter - if someone keeps insisting that an insignificant dynamic must be included, it shouldn't be difficult to produce a few calculations to shut them up and help them set their mind on something more significant. The evaluation process is what tells us which trees are big, and which ones are small. That is a picture that develops in one's mind over time, but the nature of a collaborative distributed effort means it also needs to be documented so we don't have to keep repeating our conclusions. To revisit some Simutrans history, this post lists things that "will never happen" because they were deemed insignificant, too difficult etc, but it doesn't present the assumptions/calculations that were used to reach the conclusions. Others have since revisited them and now some of these things are happening. We need to "prove" that something does or doesn't add anything to the game, or quantify the difference it will make so we can reach a sensible and defensible decision on whether or not to include it. Sounds like work but it takes out all the stress and confusion, and stops all the "why didn't you include x?" traffic, so in my experience it ends up feeling like less work.

There may be much to be said for this approach, in that case, although I am still a tad unclear on what the list/specification might look like and how detailed that it should be: the example requested above would be much appreciated in that respect.

Quote
I have a degree in applied mathematics and used to analyse and model supply chains and expansion options for a living, especially mine-rail-port systems. Lost my health a few years back and had to quit work. Health is slowly improving - not long ago I couldn't have kept up with this discussion.

I hope, in that case, that our discussions are contributing to, and not detracting from, your health!

Quote from: AEO
In the previous version, there were not enough passengers generated.
In the current version, there are too many passengers generated.

This is a pakset thing, not an Experimental thing, but the point applies, of course, to the pakset development.

Offline moblet

  • *
  • Posts: 250
Re: How to get started on balancing properly
« Reply #76 on: January 28, 2011, 01:56:02 PM »
I know it's very time consuming, but I think it's important that the developer(s) also play the game to get an impression what works well and what doesn't. Nowadays I see many pak set developers depend on spreadsheets and formulas, but at some point the proper tuning can only be done from experience by playing.
That's how I ended up on these boards! I was playing and realised that the costs were inconsistent and that some modes were profitable while others made losses at comparable utilisations. I thought, "I know how to specify these, using return on capital as a benchmark" and started looking into how I could balance the costs, only to discover that neither Standard nor Experimental is designed to support a trade-off between capital and operating costs. Using spreadsheets and formulas to at least generate a reasonable starting set of parameter values is by far the most practical and efficient approach, but I can see why it might not work. The dynamics assumed in the spreadsheets and formulas must match the dynamics used in the sim, and also the sim may not support all the dynamics required for balancing to work (I'm still getting my head around this).

I still play the game but now, I just give myself lots of money at the start and pay no attention to financial performance.
Quote from: James
The code in question looks like this:
OK
1. Let's start with Standard, not Experimental. Maybe pick something that hasn't been altered. In any case I will start drilling down from the overview of Standard into detail in particular areas.
2. This is a complex example and therefore a bad place to start. For demonstration purposes pick something that's as simple and self-contained as possible.
3. The sequence of logic you've documented is substantially different from the sequence of logic in the code. They need to match perfectly. As a result it's hard to make sense of. Before reading the code, I read your description and started writing a flow chart based on how I would code it, but quickly found the description didn't match the sequence of the code, and promptly stopped. When I started reading the code I wondered why "does the passenger have access to a car?" is being asked before the trip is even generated. The first action on my flow chart is "generate trip", the second is "can the player walk?" I then saw that on this question alone the code appears to, avoidably:
a. test for whether passengers who will walk have access to a car
b. test middle and long range trips for the possibility that the player will walk
...and that's as far I've got. I'm not going to engage with all the details of this particular piece of code at this point, as understanding and documenting Standard is my first priority. I would also insist on some quantitative assessment of the value of the dynamics in this piece of code before worrying about the code itself.
Quote from: James
Ahh, I never really saw the need to have a conceptual separation of features into discrete units (as, after all, reality is not so compartmentalised). The approach was always simply to simulate, in so far as relevant and practicable, the various dynamics of reality and their interaction with one another.
Reality is a bit of a mess. It's OK if a player's game simulates reality in that way, but the development and coding of Simutrans needs to be anything but. There's a good reason why expert programmers are considered to be out of touch with the real world - their output is nowhere near as messy.
Quote from: James
This looks like the approach that one might adopt if one were starting Experimental to-morrow. Presumably, what we need to do now is to document and understand what Experimental does and doesn't do and evaluate then what of that is undesirable or missing?
This is the approach that's ultimately unavoidable. We can start using it now, or after you've burned out. I'd prefer the former, since you're highly productive!
Quote from: James
The vision that I have for Experimental...
That was incredibly convoluted. A vision has to be communicable in one line to be effective. Would it be fair to paraphrase it as wanting "a transport network game that behaves as much like the real world as possible, without any unnecessary complexity"?
Quote from: James
I hope, in that case, that our discussions are contributing to, and not detracting from, your health!
So far, so good. Have had enough practice at pacing myself. I don't claim to accomplish much else on a day where I write a couple of posts here, but a couple of posts here is better than nothing. It's good to exercise the brain on this stuff and see how much I remember, in an environment where a mistake doesn't cost millions of dollars.

Offline jamespetts gb

  • Simutrans-Extended project coordinator
  • Moderator
  • *
  • Posts: 18374
  • Cake baker
    • Bridgewater-Brunel
  • Languages: EN
Re: How to get started on balancing properly
« Reply #77 on: January 31, 2011, 11:41:48 PM »
Quote from: Moblet
OK
1. Let's start with Standard, not Experimental. Maybe pick something that hasn't been altered. In any case I will start drilling down from the overview of Standard into detail in particular areas.
2. This is a complex example and therefore a bad place to start. For demonstration purposes pick something that's as simple and self-contained as possible.
3. The sequence of logic you've documented is substantially different from the sequence of logic in the code. They need to match perfectly. As a result it's hard to make sense of. Before reading the code, I read your description and started writing a flow chart based on how I would code it, but quickly found the description didn't match the sequence of the code, and promptly stopped. When I started reading the code I wondered why "does the passenger have access to a car?" is being asked before the trip is even generated. The first action on my flow chart is "generate trip", the second is "can the player walk?" I then saw that on this question alone the code appears to, avoidably:
a. test for whether passengers who will walk have access to a car
b. test middle and long range trips for the possibility that the player will walk
...and that's as far I've got. I'm not going to engage with all the details of this particular piece of code at this point, as understanding and documenting Standard is my first priority.

Ahh, the reason that I chose this example was just to explain why and how passenger generation and private car generation are bound up with each other, as you  had suggested that it might not be good for them to be so bound. Do I understand you to mean, however, that we need to have a precise natural language description (and possibly flowchart) of all the economically significant parts of the code before we can balance? That is likely to be a very, very large amount of work!

Quote
I would also insist on some quantitative assessment of the value of the dynamics in this piece of code before worrying about the code itself.

I am not sure what you mean here; what would such a quantitative assessment entail?

(As an aside, I'm not sure that the following two things really are avoidable:

Quote
a. test for whether passengers who will walk have access to a car
b. test middle and long range trips for the possibility that the player will walk.

As to (a), the code iterates over multiple destinations to implement the alternative destinations feature: it might be that a packet of passengers can walk to their second choice destination, but not the first choice, and could only get to their first choice destination by car. Since the code is the same whatever iteration is current (the use of the for loop), we can't separate the cases.

On (b), we can't be sure that the user won't have specified a minimum distance for either the long-range or middle-range that is less than or equal to the walking distance, as there is considerable flexibility in how pakset maintainers can use the distances feature: they can set all the distance ranges to be the same but vary the journey time tolerances for each type, for example, or set the minimum numbers all the same, but change the maximum numbers. Since these specifications are not unambiguously perverse, the code has to be able to accommodate those possibilities.)

Quote
Reality is a bit of a mess. It's OK if a player's game simulates reality in that way, but the development and coding of Simutrans needs to be anything but. There's a good reason why expert programmers are considered to be out of touch with the real world - their output is nowhere near as messy.

Hmm, interesting. I have always followed the principle that a simulation will work most reliably and accurately, and be the easiest to code, debug and balance if the algorithmic representation of the reality simulated is as similar to that reality in its mechanism as is practicable: the fewer the divergences from the way that things work in reality, the fewer the chances for mistaken assumptions or oversimplifications to create distortions or just plain bugs, and the greater the chance that an emergent property that is also a property of reality that the developer would never have thought consciously to design will manifest.

Quote
That was incredibly convoluted. A vision has to be communicable in one line to be effective. Would it be fair to paraphrase it as wanting "a transport network game that behaves as much like the real world as possible, without any unnecessary complexity"?

Ahh, forgive me: I think that that is my profession manifesting itself just as your splendid graphs are examples of yours; I do hate to miss out the details, and wasn't sure of how precise that you wanted it. Your summary is, in so far as can be expected of a two clause summary of a lengthy paragraph, indeed approximately a correct description of what I am trying to do, although perhaps with the addition of "whilst still being fun" at the end, too, although I accept that what is fun for some is a chore for others (hence the divergence between Standard and Experimental in the first instance).

In any event, as you will have noticed, I have found and replied to the post about documenting the dynamics of Standard to which this discussion relates. Is that the sort of list that you had imagined as sufficing for the purpose of knowing what is included and left out so that we can commence the work of deducing what needs to be included that is not currently included that is of primary importance for balancing?

Offline moblet

  • *
  • Posts: 250
Re: How to get started on balancing properly
« Reply #78 on: February 02, 2011, 03:07:48 PM »
Do I understand you to mean, however, that we need to have a precise natural language description (and possibly flowchart) of all the economically significant parts of the code before we can balance? That is likely to be a very, very large amount of work!
We need to have thought through how the code interacts with the pakset, then design the logic of code, then write the code and document the principles on which paksets should be parameterised. In an open source volunteer project the intended logic of the code should not reside exclusively inside one person's head, unless of course it's OK if all the work is lost if that person moves on.
Quote from: jamespetts
I am not sure what you mean here; what would such a quantitative assessment entail?
Articulating, and then quantifying, exactly what difference this enhancement would make to the game. If its influence turns out to be very minor in practice then it would not be worth the complexity and effort. In this particular instance I would have the sim output some log files to quantify generated trips and work from those, probably using a spreadsheet to estimate how the numbers would be different under an alternative trip generation algorithm. Without logs one ends up doing too much guessing, and has no data.
Quote from: jamespetts
I have always followed the principle that a simulation will work most reliably and accurately, and be the easiest to code, debug and balance if the algorithmic representation of the reality simulated is as similar to that reality in its mechanism as is practicable: the fewer the divergences from the way that things work in reality, the fewer the chances for mistaken assumptions or oversimplifications to create distortions or just plain bugs, and the greater the chance that an emergent property that is also a property of reality that the developer would never have thought consciously to design will manifest.
A simulation is a model. A model is a simplified representation of a phenomenon or phenomena. In order to tell us anything about the real world, or in this case mimic the real world, the model itself has to be internally coherent and as dynamically faithful as possible. All models are built on assumptions, and those assumptions are critical to the form and operation of the model, as well as the conclusions that can be drawn from its use. In practice assumptions get negotiated in the model building process, as assumptions are typically traded against complexity (e.g. assume all destinations are equally likely or have a tiered destination probability). Assumptions must be documented and never forgotten, and are crucial to pakset balancing. The art in modelling is, as Einstein reputedly said, "make things as simple as possible, but no simpler". The best models are built by first understanding the dynamics of the target, identifying and modelling the ones that are indispensible, and confining the rest to the list of assumptions.
As for the easiest way to build, debug and test, it is this: Select one of the dynamics believed to be indispensible and build the simplest possible algorithm that might possibly be adequate, erring on the side of simplicity. Test it. If it's dynamically adequate, add the next dynamic and repeat. If it needs more complexity, add one unit of complexity and retest, and so on. Never ever go from zero to a highly complex algorithm, or add more than one dynamic at a time to a model (unless they are tried and true components that one thoroughly understands), because this is inefficient and often impossible to debug and calibrate. By following this process one arrives at the minimally complex adequate model with minimal wasted effort, and, if there's no deadline, no stress.
Quote from: James
In any event, as you will have noticed, I have found and replied to the post about documenting the dynamics of Standard to which this discussion relates. Is that the sort of list that you had imagined as sufficing for the purpose of knowing what is included and left out so that we can commence the work of deducing what needs to be included that is not currently included that is of primary importance for balancing?
It's a vital piece of the documentation picture, what I would call a basic overview of the sim's assumptions. The first step towards instructing a pakset builder in how to balance.

Offline jamespetts gb

  • Simutrans-Extended project coordinator
  • Moderator
  • *
  • Posts: 18374
  • Cake baker
    • Bridgewater-Brunel
  • Languages: EN
Re: How to get started on balancing properly
« Reply #79 on: February 02, 2011, 10:07:04 PM »
The issues of feature significance and documentation I have dealt with in the other thread; one thing to which I should perhaps respond here, however, is this:

Quote
The best models are built by first understanding the dynamics of the target, identifying and modelling the ones that are indispensible, and confining the rest to the list of assumptions.

I don't think that this is entirely correct, since one might very well acquire an understanding of the dynamics of the target (at least, in so far as those dynamics consist of emergent properties) precisely by building and running the model.

On the list of dynamics, what I was really getting at is whether the level of detail of the description of the dynamics in the list is adequate for our purposes, as they are general headings more than anything else. If that will suffice, then the exercise may be easier than I had imagined. Do we need to do more work on listing the existing dynamics? I think that I have already put down all or at least nearly all of the dynamics not modelled by either Standard or Experimental of which I can think. It can, however, be difficult at times to work out what exactly all of the modelled dynamics are.
« Last Edit: February 02, 2011, 10:11:14 PM by jamespetts »

Offline moblet

  • *
  • Posts: 250
Re: How to get started on balancing properly
« Reply #80 on: February 04, 2011, 04:46:17 PM »
I don't think that this is entirely correct, since one might very well acquire an understanding of the dynamics of the target (at least, in so far as those dynamics consist of emergent properties) precisely by building and running the model.
Just to clarify what I meant, I was trying to say that one would evaluate all the dynamics thought to be significant, by using analysis and modelling. Having established which dynamics are fundamental and which are peripheral, only include the fundamental ones in the final model. The peripheral dynamics get replaced by "assumptions", meaning a statement of what is assumed in the model, e.g. "flat $/km revenue regardless of distance". A model's list of assumptions are just as important as the model itself, especially for pakset developers.
Quote from: James
On the list of dynamics, what I was really getting at is whether the level of detail of the description of the dynamics in the list is adequate for our purposes, as they are general headings more than anything else.
Different levels of detail are required for different purposes. Major purposes are:
1. Discussion & comparison of coding logic and options
2. Instructions to programmers
3. Writing player help files and user manual
4. What pakset developers need to know
5. Leaving enough of a paper trail such that if any key person moves on, others can pick up from where they left off
I'm not trying to write all of them right now, my immediate goal is #1. But ideally we'd have all five.

Offline jamespetts gb

  • Simutrans-Extended project coordinator
  • Moderator
  • *
  • Posts: 18374
  • Cake baker
    • Bridgewater-Brunel
  • Languages: EN
Re: How to get started on balancing properly
« Reply #81 on: February 05, 2011, 02:26:29 AM »
Quote
1. Discussion & comparison of coding logic and options
2. Instructions to programmers
3. Writing player help files and user manual
4. What pakset developers need to know
5. Leaving enough of a paper trail such that if any key person moves on, others can pick up from where they left off
I'm not trying to write all of them right now, my immediate goal is #1. But ideally we'd have all five.

No. 1 is important because we need to know what is missing in order accurately to simulate the dynamics necessary to make balancing possible? How best to start, do you think?

Offline moblet

  • *
  • Posts: 250
Re: How to get started on balancing properly
« Reply #82 on: February 06, 2011, 10:35:19 AM »
How best to start, do you think?
I proposed a first course of action in the other thread but it passed unnoticed.

Offline jamespetts gb

  • Simutrans-Extended project coordinator
  • Moderator
  • *
  • Posts: 18374
  • Cake baker
    • Bridgewater-Brunel
  • Languages: EN
Re: How to get started on balancing properly
« Reply #83 on: February 06, 2011, 11:59:31 AM »
I proposed a first course of action in the other thread but it passed unnoticed.

Did that first step not include the suggestion that the starting point for making the necessary changes to Experimental be, not Experimental itself, but Standard? If so, I refer to my response on that thread to that suggestion.