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

Working the UWP

robject

SOC-14 10K
Admin Award
Marquis
For the past ten years or so, I've used Perl to work with UWP data. In the last two years, I've compiled that code into a pair of Perl modules which encapsulate these functions.

Here, I'm going to post some of the handier methods, for anyone who wants them.
 
First, parsing in the UWP itself. For this I use three methods, only one of which is truly important.

The first one parses a 'standard' UWP. The beauty of it is that column positions are not very important: the important thing is that field order is maintained. Here's its core.

I assume a line of UWP data is stored in $line.

</font><blockquote>code:</font><hr /><pre style="font-size:x-small; font-family: monospace;"> if ( $line =~ /^\s*(\S.*\S)? # $1 name
\s*(\d{4}) # $2 hex
\s+(\w\w{6}-\w) # $3 uwp
\s+(.*) # $4 codes
\s+(\d{3}) # $5 PBG
(.*) # $6 etc
$/x )
{
$self->_src = 'Std';
$self->name = $1;
$self->loc = $2;

$self->_loadUwp( $3 );
$self->_loadCodes( $4 );
$self->_loadPBG( $5 );

my $etc = $6;

if ( $etc =~ /(\w\w)\s*(.*)/ )
{
$self->allegiance = $1;
$self->_loadStars( $2 );
}
else
{
$self->allegiance = 'Na';
}

#print $self->name, "\n";
return 1;
}</pre>[/QUOTE]
 
This snippet can tell you if a world is within a given "bounding box"; i.e. a rectangular area within a sector.

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

my ($c1,$r1) = $nw =~ /(..)(..)/;
my ($c2,$r2) = $se =~ /(..)(..)/;

my $ch = $self->col();
my $rh = $self->row();

#print "Col: $ch >= $c1 && $ch <= $c2\n";
#print "Row: $rh >= $r1 && $rh <= $r2\n";

return ( $ch >= $c1 && $ch <= $c2
&& $rh >= $r1 && $rh <= $r2 );
}</pre>[/QUOTE]Once we've got that, then we can figure out what subsector a world is in:

</font><blockquote>code:</font><hr /><pre style="font-size:x-small; font-family: monospace;"> sub subsector
{
my $self = shift;
return 'A' if $self->between( '0101', '0810' );
return 'B' if $self->between( '0901', '1610' );
return 'C' if $self->between( '1701', '2410' );
return 'D' if $self->between( '2501', '3210' );
return 'E' if $self->between( '0111', '0820' );
return 'F' if $self->between( '0911', '1620' );
return 'G' if $self->between( '1711', '2420' );
return 'H' if $self->between( '2511', '3220' );
return 'I' if $self->between( '0121', '0830' );
return 'J' if $self->between( '0921', '1630' );
return 'K' if $self->between( '1721', '2430' );
return 'L' if $self->between( '2521', '3230' );
return 'M' if $self->between( '0131', '0840' );
return 'N' if $self->between( '0931', '1640' );
return 'O' if $self->between( '1731', '2440' );
return 'P' if $self->between( '2531', '3240' );
return '?';
}</pre>[/QUOTE]
 
And for the peculiar, here's how I do ring/ray distance calculations.

</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]And here's where I store the ring/ray offsets for commonly used sectors.

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

my %ringray =
(
'gvurrdon' => [ 9880 , 62704 ],
'tuglikki' => [ 9880 , 62736 ],
'spinward marches'=> [ 9920 , 62704 ],
'deneb' => [ 9920 , 62736 ],
'corridor' => [ 9920 , 62768 ],
'vland' => [ 9920 , 62800 ],
'lishun' => [ 9920 , 62832 ],
'antares' => [ 9920 , 62864 ],
'empty quarter' => [ 9920 , 62896 ],
'trojan reaches' => [ 9960 , 62704 ],
'reft' => [ 9960 , 62736 ],
'gushemege' => [ 9960 , 62768 ],
'dagudashaag' => [ 9960 , 62800 ],
'dagudashag' => [ 9960 , 62800 ],
'core' => [ 9960 , 62832 ],
'fornast' => [ 9960 , 62864 ],
'riftspan' => [ 10000 , 62704 ],
'verge' => [ 10000 , 62736 ],
'ilelish' => [ 10000 , 62768 ],
'zarushagar' => [ 10000 , 62800 ],
'massilia' => [ 10000 , 62832 ],
'delphi' => [ 10000 , 62864 ],
'reaver\'s deep' => [ 10040 , 62768 ],
'daibei' => [ 10040 , 62800 ],
'diaspora' => [ 10040 , 62832 ],
'old expanses' => [ 10040 , 62864 ],
'dark nebula' => [ 10080 , 62768 ],
'magyar' => [ 10080 , 62800 ],
'solomani rim' => [ 10080 , 62832 ],
'alpha crucis' => [ 10080 , 62864 ],
);

my ($ringoff, $rayoff) = @{$ringray{ lc $sector }};

my ($ring, $ray) = ($ringoff, $rayoff );

($ring, $ray) = ($ringoff + $uwp->row,
$rayoff + $uwp->col) if $uwp;

$ray -= 62832 if $ray > 62832;

return ($ring, $ray);
}</pre>[/QUOTE]
 
robject - this is some very clever (and reusable) code - thank you so much
 
There are other nifty tools in those packages as well. Whenever I processed sector data, I tended to do a lot of the same things, so I'd put code into those modules (which are available on CPAN: Games::Traveller::UWP and Games::Traveller::UwpTools). So for example, if I didn't trust the trade codes given with Sunbane (and who does?), I could filter them.

Say I want to show the total population of Industrial or Agricultural worlds.

</font><blockquote>code:</font><hr /><pre style="font-size:x-small; font-family: monospace;">use Games::Traveller::UwpTools;

my $tools = new Games::Traveller::UwpTools;

$tools->readFile( 'foo.sec' );

foreach my $uwp ($tools->bySubsector())
{
$uwp->regenerateTradeCodes();

next unless $uwp->isIn() || $uwp->isAg();

$pop += $uwp->countBillionsOfPeople();
push @out, $uwp->toString(), "\n";
}
print "World count: ", scalar @out, "\n";
print "Total pop (bil): $pop\n";
print "Worlds: \n\n";
print join( '', "<data>\n", @out, "</data>\n" );</pre>[/QUOTE]
 
Yoink!



Seriously, thanks for posting this, rob. I think I may try to port this over to VB or make something similar in C#.
 
I have variants of many of these in C# already for my map site; some inspired by Robert's code, some just needed for the special rendering algorithms. I'll try to plunk the source somewhere visible shortly.

BTW, we should probably migrate to another thread for this.
 
Back
Top