• 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.

Web-based Automatic Sector Data / Map pdf Generator

I got the table for generating trade routes from the 1977 edition of LBB3 (Thanks Sigg!) and I'm going to write a little program that does the following:

1. For each star system, determine all neighboring systems within 1, 2, 3, or 4 hexes.

-- The math: Compare two systems' XXxx and xxXX pairs and continue only if the difference in ID of both pairs is less than or equal to 4.

2. Compare starport codes and roll D6 on the route table to determine if a route exists

3. if a route exists, add 'route IDxx IDxx' to a running text file

4. (optional) delete duplicate routes (routes with opposite source and destination systems).

5. feed the final file into the sector generation script

6. (options) I thought about encouraging trade routes across polity borders, and trying to encourage connections of 'long routes'... but that last one will be complex mathematically.

and voila! ... the generation system will have automatic generation and display of trade routes. Any ideas or suggestions from the esteemed Travellers of CotI ?
 
I think the math for neighboring systems is incorrect. For example, consider 0101 and 0305: the delta is (2,4), meeting your criteria. However, they are five hexes apart.

The duplicate routes issue is problematic: ideally you'd only check each hex pair once. Eg, you'd never check for a route from 0101 -> 0202 and then check again for a route from 0202 -> 0101. I think.
 
Yeah - I was thinking about this problem on the beach - the duplicate routes issue can be bypassed by sorting the sec file by position ID and only considering the 'remaining' systems as eligible for routes...

As far as the first issue, I may have to also require that the TOTAL difference between the hexes is also less than 5 (real numbers)... thoughts?
 
Distance formula: will this help?

</font><blockquote>code:</font><hr /><pre style="font-size:x-small; font-family: monospace;"> sub _distance
{
my ($row1, $col1, $row2, $col2) = @_;

my $a1 = ($row1 + int($col1/2));
my $a2 = ($row2 + int($col2/2));

my $d1 = abs( $a1 - $a2 );
my $d2 = abs( $col1 - $col2 );
my $d3 = abs( ($a1 - $col1) - ( $a2 - $col2 ) );

#return (reverse sort { $a <=> $b } ( $d1, $d2, $d3 ))[0];
return (sort { $a <=> $b } ($d1, $d2, $d3))[2];
}</pre>[/QUOTE]Duplicate routes: before you check route A->B, first make sure you haven't checked route B->A? Alternately, always store routes with the "low hex" first, then make sure you're always checking destinations at higher hexes for each source?
 
robject - that's what I was thinking. Sort the sector file so you're sure you can only consider systems 'ahead' in the file or limit the search to systems with higher IDxx values.

Thanks for the perl code - If you've not already tackled it, I'll give a wrapper for it a whirl (for example, a wrapper to feed it a sec file and a 'current ID', and return a list of 'within 4 neighbors'. After determining the neighbors, each one will have a chance based on the starports and distance as per the table Sigg reprinted.

Basically, in the final analysis I want to feed the program a SEC-format sector file and get a file with route data as described above (route IDxx IDxx) for compatibility with sec2pdf.
 
I have the "list of hexes within N parsecs" function in my code (view source) at:

http://www.travellermap.com/tmp/pathfind.htm

Note that the function is really easy to write given a distance function - just iterate over the hexes from -N to +N in the x and y axes and feed it into a distance formula like Robert's. There's doubtless a better algorithm (e.g. that spirals around the target hex) but unless this is the time critical chunk of the code...

I posted a Perl version of that page to TML a while back. I can resend if requested.
 
Joshua - I'd love to see that Perl code. I figure the routine can parse the sector, find all the A and B pop worlds, and connect 'em with routes.

What do you folks think?
 
Hi guys!

I updated my script - I'm about halfway towards finishing the 'totally random' LBB2-style route generation algorithm.

I modified 'allygen', to include the following code. It will randomly determine whether a trade route is present, if both systems are D-class starports or higher... but it won't apply modifiers to the likelihood based on distance, the systems' ports, and it doesn't exclude prior attempts (nor, apparently, the occasional 'X' port).

Could use a little help from you Perl gurus


Here's the code I added to the breathtakingly cool 'allygen.pl':


</font><blockquote>code:</font><hr /><pre style="font-size:x-small; font-family: monospace;"># routermatic! - generate trade routes and like it

foreach (sort bycoolness keys %Port) {
my ($c,$r) = /^(..)(..)$/;
next if $Port{$c.$r} gt "C";
foreach my $dist (1..index("DCBA",$Port{$c.$r})) {
for my $dir (0..5) {
#for each direction
my ($cn,$rn) = neighbor($c,$r,$dist,$dir);
# ignore out-sector hexes
next if $cn < 1 or $cn > 32 or $rn < 1
or $rn > 40;
next if $Port{$cn.$rn} gt "C";
if ($WorldAlly[$cn][$rn]) {

if (((int(rand(6)+1) ) > 5)) {
$Aneighbor{$I} = sprintf("route %02d%02d %02d%02d",$c,$r,$cn,$rn,"$CR\n");
print $Aneighbor{$I},"$CR\n";
};
}
}
}
}</pre>[/QUOTE]And here's what a sector's worth of routes so generated looks like:

Sabani_routes.jpg

Any advice would be most appreciated!
 
I do not know if it is a glitch of some sort (route generation, rendering error, whatever), intentional, visual result of the size of the jpeg, or total coincidence, but I only see trade routes that are along one of the 6 hex-edge directions.
 
Originally posted by MaineCoon:
I do not know if it is a glitch of some sort (route generation, rendering error, whatever), intentional, visual result of the size of the jpeg, or total coincidence, but I only see trade routes that are along one of the 6 hex-edge directions.
You are correct. Do you program in perl? Mind improving the snippet I provided?
 
So... I don't have time right now to wade through all 9 pages here, but would it be possible to import UWPs into the program and generate new subsector and sector maps and planet lists? (EDIT: well I just waded through it and I'm still not sure that question's been answered already
).

Looks pretty darn cool though
. Unfortunately perl is utterly unparseable gibberish to me, but I acknowledge its power in capable hands ;) .
 
BTW, just scanning through the thread... Mick, if you want to incorporate my Revised Stellar Generation Rules then go ahead.

They shouldn't actually that much harder to incorporate than the usual book 6 rules - there's just a few more modifiers (well, and that star-ordering thing, but you could get by without that).
 
NOTE: I'm doing this raw, without trying the code. So expect typos/glitches.

In your snippet, replace this bit which sets up an iteration over the neighbors of $c,$n out to $dist:

</font><blockquote>code:</font><hr /><pre style="font-size:x-small; font-family: monospace;">for my $dir (0..5) { # for each direction
my ($cn,$rn) = neighbor($c,$r,$dist,$dir);
# ignore out-sector hexes
next if $cn < 1 or $cn > 32 or $rn < 1 or $rn > 40;</pre>[/QUOTE]With this:

</font><blockquote>code:</font><hr /><pre style="font-size:x-small; font-family: monospace;">foreach ( reachable( sprintf("%02d%02d", $c, $r ), $dist ) )
{
my( $cn, $rn ) = /^(..)(..)$/;</pre>[/QUOTE]And grab the "reachable" function from my code at http://www.travellermap.com/tmp/pathfind.pl.txt

Mal: Many Perl coders make full use of Perl-isms which make understanding the code difficult. This is akin to C++ coders using the full power of templates (as is done in STL), or JavaScript coders playing with prototype decoration. When writing code for public consumption it's best to avoid such things unless extremely precisely documented - maintainability of code is far more important than most inexperienced developers understand.

Even in my snippet above I have a few such Perl-isms: the foreach implicitly assigns to $_ and the regular expression implicitly tests against $_. I normally try to avoid this and use explicit variables and so on, but in this case it matches the coding style where it's being inserted.
 
Joshua: This is so nice of you. Thanks for giving me such a great head start. It's much appreciated.

Mal - thanks, yeah - I think that, much like the trade routes issue, I'll offer a 'canon' yes/no flag that will use LBB or 'more realistic rules' for stellar, planetary and route gen. I'd like to explore using your rules, as they look quite cool indeed.


And yes - you can absolutely pass it a SEC-format file (and a list of alliances and routes, if you've got 'em) and It'll render an existing sector's world data rather than only generate fresh UPP's.
 
Update: just finished integrating Joshua's snippet suggestion.

Gruish_routes.jpg


I think it's coming along very nicely!

The rules are still totally random, without removal of duplicates, etc., but they look a lot better already!

I'm also spelunking Joshua's pathfind program for other inclusions (grab the top pop worlds and chain 'em, etc.) - thanks again!
 
Well, my snippets are functions which each do part of a job. Also, I think I'm lacking the "finalizer" which checks route legs for sanity.

The first one simply gathers up important worlds.

</font><blockquote>code:</font><hr /><pre style="font-size:x-small; font-family: monospace;"> sub getImportantWorlds
{
my $self = shift;
my $lax = shift;
my %uwps = @_;
my %important = ();

foreach my $loc (keys %uwps)
{
my $uwp = $uwps{$loc};
$important{ $loc } = $uwp
if $uwp->isImportant($lax)
|| $uwp->isCp()
|| $uwp->isCx();
}

return %important;
}</pre>[/QUOTE]The second one plots the shortest path between a group of passed-in worlds (presumably the ones gathered above, but any group of worlds will do).

</font><blockquote>code:</font><hr /><pre style="font-size:x-small; font-family: monospace;"> sub shortestRoutes
{
my $self = shift;
my %uwps = @_;

my %route = ();

foreach my $loc1 (keys %uwps)
{
my $uwp1 = $uwps{ $loc1 };
foreach my $loc2 (keys %uwps)
{
next if $loc1 == $loc2;
my $uwp2 = $uwps{ $loc2 };
my $dest = $uwps{ $route{ $loc1 } || $loc2 };

if ($self->distance( $uwp1, $dest ) >= $self->distance( $uwp1, $uwp2 ))
{
$route{ $loc1 } = $loc2;
}
}
}
return %route;
}</pre>[/QUOTE]The next function checks those routes and splits them in half if they're too long. This one can be called iteratively. It would probably work best with another function that simply counts the number of too-long routes in the list.

</font><blockquote>code:</font><hr /><pre style="font-size:x-small; font-family: monospace;"> sub divideRoutes
{
my $self = shift;
my $routeref = shift;
my $uwpref = shift;

my %routes = %$routeref;
my %uwps = %$uwpref;

foreach my $loc1 (keys %routes)
{
my $loc2 = $routes{ $loc1 };
my $dist = _distance2( $loc1, $loc2 );
next unless $dist > 4;

foreach my $iloc (keys %uwps)
{
if ( _distance2( $loc1, $iloc ) < 1+$dist/2
&& _distance2( $loc2, $iloc ) < 1+$dist/2 )
{
$routes{ $loc1 } = $iloc;
$routes{ $iloc } = $loc2;
last;
}
}
}

return %routes;
}</pre>[/QUOTE]The final one discards routes that are too long.

</font><blockquote>code:</font><hr /><pre style="font-size:x-small; font-family: monospace;"> sub killLongRoutes
{
my $self = shift;
my $routeref = shift;
my $uwpref = shift;
my $max_len = shift;

my %routes = %$routeref;
my %uwps = %$uwpref;

foreach my $loc1 (keys %routes)
{
my $loc2 = $routes{ $loc1 };
my $dist = _distance2( $loc1, $loc2 );

delete $routes{ $loc1 } if $dist > $max_len;
}
return %routes;
}</pre>[/QUOTE]
 
Robject - those snippets look snippetlicious!

Seriously, I've gotta glean 'em and learn 'em and get a sense for where they should go in the mix (whether inside 'allygen redux' or a separate process).

Thanks so much for the help and ideas, folks! Hopefully this system will be online soon.
 
Back
Top