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

Traveller Utilities Cookbook

robject

SOC-14 10K
Admin Award
Marquis
Executive Summary

As a software developer and user, I'd like to see programmers here collaborating loosely. I suspect that forums are too informal for technical collaboration, unless there are some simple and explicit contracts for simple or atomic Traveller-related programming tasks. So, my latest thought is on producing small utilities.

What do you mean by Utility?

A utility is a small software component that does one thing (='atomic'), and has two interfaces:

</font>
  • A package, which is a modular chunk of code that other programs can directly use.</font>
  • A tool, which is a minimal wrapper around the package, so users can run it or other programs can call it indirectly. The output would default to plain text, or XML with an -xml option, YAML with a -yaml option, and maybe even HTML with an -html option.</font>
A List of Potential Utilities

I can imagine a large number of utilities that could encourage software development, as well as provide strategic tools to whoever needs it. The nice thing is that most of these concepts have already been coded by many of us in the past; therefore the solution space is well known, and conversion into utilities is theoretically easy.

</font>
  • A distance-time-acceleration utility (done - robject).</font>
  • A simple interstellar distance calc utility (done - robject).</font>
  • An inter-sector distance util (note that this tool can actually use the simpler interstellar distance cal utility).</font>
  • A dice roller (done - robject).</font>
  • A converter from decimal to "Traveller Decimal" and back (done - robject).</font>
  • A Revised Stellar Gen UWP utility.</font>
  • A Book3 world gen utility. (started - FlightCommanderSolidute).</font>
  • A task utility.</font>
  • An animal encounter table gen utility.</font>
  • A basic career gen term roller (all but skills?) (started - robject).-</font>
  • A random skill picker (?)</font>
  • A benefits picker.</font>
  • A basic random character generator. (started - robject)</font>
  • A starship weapon attack utility.</font>
  • A starship damage utility.</font>
  • An alien race gen utility.</font>
  • A world orbit renderer.</font>
  • A star renderer.</font>
  • A subsector map renderer.</font>
  • A 'fixed' random number gen utility *.</font>
*: By 'fixed' I mean that the algorithm is coded, rather than provided by language internals; this way, random numbers are still random, but every tool which uses it has the same mapping of seeds to values. This kind of consistency can be useful for certain types of inter-process communications.

Target Languages

My preference is to see Java, Perl, and Python versions of utilities, and some Ruby wouldn't hurt, either. JavaScript can be handy in some cases, too.

The fun begins when one starts to think about how the same utility written in a different language should produce the same output. But that's best resolved informally, since there is no authority vested in a loose confederation, and a political body to handle this would surely kill the project before it even begins.


Why all the Fuss?

Well there's not a big need for a portable dice roller, but some of the other utilities would really be nice to be already done and available, since many of us are reinventing the wheel when we write code. In addition, it may make it easier for us to write more complex Traveller programs -- the concept is related to Bottom-Up Programming:

http://www.paulgraham.com/progbot.html


Thoughts?
 
Example - simple hexmap distance calculator

Contract:

method: distance( row1, col1, row2, col2 )

parameters: row1, col1 are from one hex location, and row2, col2 are from another hex location.

return: the distance (an integer number) between the two hexes.

Tool Usage: hdc c1 r1 c2 r2


</font>
  • Python package - HexmapDistance.py
    </font><blockquote>code:</font><hr /><pre style="font-size:x-small; font-family: monospace;">class HexmapDistance:
    "A simple hexmap distance formula."

    def distance( self, col1, row1, col2, row2 ):
    a1 = row1 + int( col1/2 )
    a2 = row2 + int( col2/2 )

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

    if d1 >= d2 and d1 >= d3:
    return int(d1)

    if d2 >= d1 and d2 >= d3:
    return int(d2)

    return int(d3)</pre>[/QUOTE]</font>
  • Python tool - hdc.py
    </font><blockquote>code:</font><hr /><pre style="font-size:x-small; font-family: monospace;">from HexmapDistance import HexmapDistance
    import sys

    hd = HexmapDistance()

    print hd.distance( float(sys.argv[1]),
    float(sys.argv[2]),
    float(sys.argv[3]),
    float(sys.argv[4]) )</pre>[/QUOTE]</font>
  • Perl package - HexmapDistance.pm
    </font><blockquote>code:</font><hr /><pre style="font-size:x-small; font-family: monospace;">package HexmapDistance;
    # "A simple hexmap distance formula."
    sub new { bless {}, shift }

    sub distance
    {
    my ($s, $col1, $row1, $col2, $row2) = @_;

    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( $d1, $d2, $d3 ))[0];
    }
    1;</pre>[/QUOTE]</font>
  • Perl tool - hdc.pl
    </font><blockquote>code:</font><hr /><pre style="font-size:x-small; font-family: monospace;">use HexmapDistance;

    my $hd = new HexmapDistance;

    print $hd->distance( @ARGV );</pre>[/QUOTE]</font>
  • Java package + tool: HexmapDistance.java
    </font><blockquote>code:</font><hr /><pre style="font-size:x-small; font-family: monospace;">public class HexmapDistance
    {
    public int distance( int row1, int col1, int row2, int col2 )
    {
    int a1 = row1 + Math.round( col1/2 );
    int a2 = row2 + Math.round( col2/2 );

    int d1 = Math.abs( a1 - a2 );
    int d2 = Math.abs( col1 - col2 );
    int d3 = Math.abs( (a1 - col1) - (a2 - col2) );

    if (d1 >= d2 && d1 >= d3)
    return d1;

    if (d2 >= d1 && d2 >= d3)
    return d2;

    return d3;
    }

    public static void main( String[] args ) throws Exception
    {
    System.out.println
    (
    new HexmapDistance().distance
    (
    Integer.parseInt( args[0] ),
    Integer.parseInt( args[1] ),
    Integer.parseInt( args[2] ),
    Integer.parseInt( args[3] )
    )
    );
    }
    }</pre>[/QUOTE]</font>
 
Example - insystem travel calculator

Contract:
Calculate distance-time-acceleration for stationary-to-stationary insystem trips.

methods:
</font>
  • distance( Gs, hours ): return AUs travelled.</font>
  • time( AUs, Gs ): return hours spent in travel.</font>
  • acceleration( AUs, hours ): return Gs required for trip.</font>
Tool Usage: it mode parm1 parm2
Where mode is "AUs" to compute distance, "Gs" to compute acceleration, or "hours" to compute time.
If the mode is AUs, then
</font>
  • parm1 is "Gs"</font>
  • parm2 is "hours"</font>
If the mode is Gs, then
</font>
  • parm1 is "AUs"</font>
  • parm2 is "hours"</font>
If the mode is hours, then
</font>
  • parm1 is "AUs"</font>
  • parm2 is "Gs"</font>
</font>
  • Python package - InsystemTravel.py
    </font><blockquote>code:</font><hr /><pre style="font-size:x-small; font-family: monospace;">import math

    class InsystemTravel:
    "stationary-to-stationary distance/acceleration/time conversions"

    def acceleration( self, aus, hours ):
    km = aus * 150000000
    seconds = hours * 3600
    accel = 4.0 * km / (seconds * seconds)
    Gs = int( accel*100 )/100.0
    return Gs

    def time( self, aus, Gs ):
    km = aus * 150000000
    hours = int( 2 * math.sqrt( km / Gs ) / 36 ) / 100.0
    return hours

    def distance( self, Gs, hours ):
    seconds = hours * 3600
    km = Gs * seconds * seconds / 4
    au = int( km/1500000 )/100.0
    return au</pre>[/QUOTE]</font>
  • Python tool - it.py
    </font><blockquote>code:</font><hr /><pre style="font-size:x-small; font-family: monospace;">from InsystemTravel import InsystemTravel
    import sys

    it = InsystemTravel()

    mode = sys.argv[1]

    if cmp(mode, 'AUs') == 0:
    print it.distance( float(sys.argv[2]), float(sys.argv[3]) )

    if cmp(mode, 'Gs') == 0:
    print it.acceleration( float(sys.argv[2]), float(sys.argv[3]) )

    if cmp(mode, 'hours') == 0:
    print it.time( float(sys.argv[2]), float(sys.argv[3]) )</pre>[/QUOTE]</font>
  • Perl package - InsystemTravel.pm
    </font><blockquote>code:</font><hr /><pre style="font-size:x-small; font-family: monospace;">package InsystemTravel;
    # "stationary-to-stationary distance/acceleration/time conversions"
    use strict;

    sub new { bless {}, shift }

    sub acceleration
    {
    my ($self, $aus, $hours ) = @_;

    my $km = $aus * 150000000;
    my $seconds = $hours * 3600;
    my $accel = 4.0 * $km / ($seconds * $seconds);
    my $Gs = int( $accel*100 )/100.0;
    return $Gs;
    }

    sub time
    {
    my ( $self, $aus, $Gs ) = @_;
    my $km = $aus * 150000000;
    my $hours = int( 2 * sqrt( $km / $Gs ) / 36 ) / 100.0;
    return $hours;
    }

    sub distance
    {
    my ( $self, $Gs, $hours ) = @_;
    my $seconds = $hours * 3600;
    my $km = $Gs * $seconds * $seconds / 4;
    my $au = int( $km/1500000 )/100.0;
    return $au;
    }
    1;</pre>[/QUOTE]</font>
  • Perl tool - it.pl
    </font><blockquote>code:</font><hr /><pre style="font-size:x-small; font-family: monospace;">use InsystemTravel;

    my $it = new InsystemTravel;
    my $opt = shift;

    print $it->distance( @ARGV ) if $opt =~ /aus/i;
    print $it->time( @ARGV ) if $opt =~ /hours/i;
    print $it->acceleration( @ARGV ) if $opt =~ /gs/i;</pre>[/QUOTE]</font>
  • Java package + tool: InsystemTravel.java
    </font><blockquote>code:</font><hr /><pre style="font-size:x-small; font-family: monospace;">public class InsystemTravel
    {
    public double acceleration( double aus, double hours )
    {
    double km = aus * 150000000;
    double seconds = hours * 3600;
    double accel = 4.0 * km / (seconds * seconds);
    double Gs = (int)( accel*100 )/100.0;
    return Gs;
    }

    public double time( double aus, double Gs )
    {
    double km = aus * 150000000;
    double hours = (int)( 2 * Math.sqrt( km / Gs ) / 36 ) / 100.0;
    return hours;
    }

    public double distance( double Gs, double hours )
    {
    double seconds = hours * 3600;
    double km = Gs * seconds * seconds / 4;
    double au = (int)( km/1500000 )/100.0;
    return au;
    }

    public static void main( String[] args ) throws Exception
    {
    String command = args[0];
    double arg1 = Double.parseDouble( args[1] );
    double arg2 = Double.parseDouble( args[2] );
    double out = -1.0;

    InsystemTravel it = new InsystemTravel();

    if ( command.equalsIgnoreCase( "AUs" ) )
    out = it.distance( arg1, arg2 );

    else if ( command.equalsIgnoreCase( "hours" ) )
    out = it.time( arg1, arg2 );

    else if ( command.equalsIgnoreCase( "Gs" ) )
    out = it.acceleration( arg1, arg2 );

    System.out.println( out );
    }
    }</pre>[/QUOTE]</font>
 
Example - Dice roller

Contract: Simulate the rolling of a number of a certain type of dice.

Methods:
roll( faces, count, dm ), where faces is the sides of the die, count is the number of dice to roll, and dm is a number to add to the total.

pin( value, min, max ) returns a number based on these rules:
</font><blockquote>code:</font><hr /><pre style="font-size:x-small; font-family: monospace;">value if min <= value <= max
min if value < min
max if value > max</pre>[/QUOTE]Tool Usage: dr faces count dm
</font>
  • Python package - DiceRoller.py
    </font><blockquote>code:</font><hr /><pre style="font-size:x-small; font-family: monospace;">import random

    class DiceRoller:
    "a simple dice roller"

    def roll( self, faces, count, dm ):
    for i in range( count ):
    dm += int( random.random() * faces + 1)
    return dm

    def pin( value, min, max ):
    if value < min:
    return min
    if value > max:
    return max
    return value</pre>[/QUOTE]</font>
  • Python tool - dr.py
    </font><blockquote>code:</font><hr /><pre style="font-size:x-small; font-family: monospace;">from DiceRoller import DiceRoller
    import sys

    dr = DiceRoller()

    if len(sys.argv) == 2:
    print dr.roll( int( sys.argv[1] ), 1, 0 )

    if len(sys.argv) == 3:
    print dr.roll( int( sys.argv[1] ), int( sys.argv[2] ), 0 )

    if len(sys.argv) == 4:
    print dr.roll( int( sys.argv[1] ), int( sys.argv[2] ), int( sys.argv[3] ) )</pre>[/QUOTE]</font>
  • Perl package - DiceRoller.pm
    </font><blockquote>code:</font><hr /><pre style="font-size:x-small; font-family: monospace;">package DiceRoller;

    sub new { bless {}, shift }

    sub roll
    {
    my ($self, $faces, $count, $dm) = @_;
    $count = 1 unless $count;
    $dm += int(rand($faces)+1) for 1..$count;
    return $dm;
    }

    sub pin
    {
    my ($value, $min, $max) = @_;
    return $min if $value < $min;
    return $max if $value > $max;
    return $value;
    }
    1;</pre>[/QUOTE]</font>
  • Perl tool - dr.pl
    </font><blockquote>code:</font><hr /><pre style="font-size:x-small; font-family: monospace;">use DiceRoller;
    my $dr = new DiceRoller;
    print $dr->roll( @ARGV );</pre>[/QUOTE]</font>
  • Java package & tool - DiceRoller.java
    </font><blockquote>code:</font><hr /><pre style="font-size:x-small; font-family: monospace;">public class DiceRoller
    {
    public int roll( int faces, int count, int dm )
    {
    for( int i=0; i<count; i++ )
    dm += (int)(Math.random() * faces + 1);
    return dm;
    }

    public int pin( int value, int min, int max )
    {
    if ( value < min ) return min;
    if ( value > max ) return max;
    return value;
    }

    public static void main( String[] args ) throws Exception
    {
    int faces = 0;
    int count = 0;
    int dm = 0;

    if ( args.length > 0 )
    faces = Integer.parseInt( args[0] );

    if ( args.length > 1 )
    count = Integer.parseInt( args[1] );

    if ( args.length > 2 )
    dm = Integer.parseInt( args[2] );

    System.out.println( new DiceRoller().roll( faces, count, dm ) );
    }
    }</pre>[/QUOTE]</font>
 
Example - Traveller Decimal Converter

Contract: To provide conversion from decimal numbers to "Traveller Decimal" -- an alphanumeric numbering system which skips characters I and O.

Methods:
t2d( traveller_digit ) - converts a Traveller Decimal digit to its real decimal
value.

d2t( decimal_number ) - converts a decimal number (from 0 to 33 only) to its Traveller Decimal digit equivalent.

Tool Usage: td mode number ...
Where 'mode' is either t2d or d2t. If more than one number is given on the command line, then the outputs will be on one line, separated by a space.

</font>
  • Python package - TravellerDecimal.py
    </font><blockquote>code:</font><hr /><pre style="font-size:x-small; font-family: monospace;">class TravellerDecimal:
    "a simple class to convert Traveller Decimal digits to numbers and back"

    d2t_list = [ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
    'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R',
    'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' ]

    t2d_map = { '0':0, '1':1, '2':2, '3':3, '4':4, '5':5, '6':6, '7':7, '8':8, '9':9,
    'A':10, 'B':11, 'C':12, 'D':13, 'E':14, 'F':15, 'G':16, 'H':17,
    'J':18, 'K':19, 'L':20, 'M':21, 'N':22, 'P':23, 'Q':24, 'R':25,
    'S':26, 'T':27, 'U':28, 'V':29, 'W':30, 'X':31, 'Y':32, 'Z':33 }

    def t2d( self, tnum ):
    return self.t2d_map[tnum]

    def d2t( self, num ):
    return self.d2t_list[num]</pre>[/QUOTE]</font>
  • Python tool - td.py
    </font><blockquote>code:</font><hr /><pre style="font-size:x-small; font-family: monospace;">from TravellerDecimal import TravellerDecimal
    import sys

    td = TravellerDecimal()

    mode = sys.argv[1]

    for num in sys.argv[2:]:
    if cmp( mode, "t2d" ) == 0:
    print td.t2d( num ),

    if cmp( mode, "d2t" ) == 0:
    print td.d2t( int(num) ),</pre>[/QUOTE]</font>
  • Perl package - TravellerDecimal.pm
    </font><blockquote>code:</font><hr /><pre style="font-size:x-small; font-family: monospace;">package TravellerDecimal;

    my @d2t = ( '0'..'9', 'A'..'H', 'J'..'N', 'P'..'Z' );
    my $count = 0;
    my %t2d = map { $_, $count++ } @d2t;

    sub new { bless {}, shift }

    sub d2t
    {
    my ($self, $index) = @_;
    return $d2t[ $index ];
    }

    sub t2d
    {
    my ($self, $index) = @_;
    return $t2d{ $index };
    }
    1;</pre>[/QUOTE]</font>
  • Perl tool - td.pl
    </font><blockquote>code:</font><hr /><pre style="font-size:x-small; font-family: monospace;">use TravellerDecimal;

    my $td = new TravellerDecimal;

    my $mode = shift;

    foreach (@ARGV)
    {
    print $td->t2d( $_ ) if $mode =~ /t2d/i;
    print $td->d2t( $_ ) if $mode =~ /d2t/i;
    print " ";
    }</pre>[/QUOTE]</font>
 
Contract:
Generate a world following the process outlined in Classic Traveller Book 3.

Methods:
generate: Generates one world.

Tool Usage:
No arguments.

Tool Output:
A world profile, including indicators for base and gas giant presence:

X123456-7 NS G

eg:

A697255-E N G

Notes:
Is it Ok, legally speaking, to post all of this stuff? It essentially describes, to those who can read it, stuff that is copyrighted. If it's not Ok, I'll snip it and replace it with one of the many "alternate" processes.

</font>
  • Perl package - TravellerWorld.pm
    </font><blockquote>code:</font><hr /><pre style="font-size:x-small; font-family: monospace;">package TravellerWorld;

    use DiceRoller;

    my %TBL_S =
    (
    2 => ['A',0,0,1],
    3 => ['A',0,0,1],
    4 => ['A',0,0,1],
    5 => ['B',0,0,1],
    6 => ['B',0,0,1],
    7 => ['C',0,1,1],
    8 => ['C',1,1,1],
    9 => ['D',1,1,1],
    10 => ['E',1,1,0],
    11 => ['E',1,1,0],
    12 => ['X',1,1,0],
    );

    my %TBL_T =
    (
    0 => [0,2,1,0,0,1],
    1 => [0,2,1,0,1,0],
    2 => [0,1,1,0,1,0],
    3 => [0,1,1,0,1,0],
    4 => [0,1,0,0,1,0],
    5 => [0,0,0,0,1,1],
    6 => [0,0,0,0,0,0],
    7 => [0,0,0,0,0,0],
    8 => [0,0,0,0,0,0],
    9 => [0,0,0,1,2,0],
    10 => [6,0,1,2,4,0],
    11 => [4,0,1,0,0,0],
    12 => [2,0,1,0,0,0],
    13 => [0,0,1,0,0,-2],
    14 => [0,0,1,0,0,0],
    15 => [0,0,0,0,0,0],
    'A' => [6,0,1,2,4,0],
    'B' => [4,0,1,0,0,0],
    'C' => [2,0,1,0,0,0],
    'D' => [0,0,1,0,0,-2],
    'E' => [0,0,1,0,0,0],
    'F' => [0,0,0,0,0,0],
    'X' => [-4,0,0,0,0,0],
    );

    sub new
    {
    my $pkg = shift;
    my $obj = bless
    {
    ATTR =>
    {
    PORT => undef,
    SIZE => undef,
    ATMO => undef,
    HYDR => undef,
    POPU => undef,
    GOVT => undef,
    LAWL => undef,
    TECH => undef
    },
    NAVAL => undef,
    SCOUT => undef,
    GIANT => undef,
    }, $pkg;
    return $obj;
    }

    sub generate
    {
    my $self = shift;
    my $dice = new DiceRoller;
    my $m = 0;

    $self->{ATTR}{PORT} = $TBL_S{$dice->roll(6, 2)}[0];

    $self->{NAVAL} = $self->{ATTR}{PORT} =~ /[AB]/
    ? $TBL_S{$dice->roll(6, 2)}[1]
    : 0;

    $m = $self->{ATTR}{PORT} eq 'A' && -3
    || $self->{ATTR}{PORT} eq 'B' && -2
    || $self->{ATTR}{PORT} eq 'C' && -1
    || 0;

    $self->{SCOUT} = $self->{ATTR}{PORT} =~ /[ABCD]/
    ? $TBL_S{DiceRoller::pin($dice->roll(6, 2, $m), 2, 12)}[2]
    : 0;

    $self->{GIANT} = $TBL_S{$dice->roll(6, 2)}[3];

    $self->{ATTR}{SIZE} = $dice->roll(6, 2, -2);

    $m = -7;
    $m += $self->{ATTR}{SIZE};
    $self->{ATTR}{ATMO} = $self->{ATTR}{SIZE} > 0
    ? DiceRoller::pin($dice->roll(6, 2, $m), 0, 12)
    : 0;

    $m = -7;
    $m += $self->{ATTR}{SIZE};
    $m += $self->{ATTR}{ATMO} <= 1 ? -4 : 0;
    $m += $self->{ATTR}{ATMO} >= 10 ? -4 : 0;
    $self->{ATTR}{HYDR} = $self->{ATTR}{SIZE} > 1
    ? DiceRoller::pin($dice->roll(6, 2, $m), 0, 12)
    : 0;

    $self->{ATTR}{POPU} = $dice->roll(6, 2, -2);

    $m = -7;
    $m += $self->{ATTR}{POPU};
    $self->{ATTR}{GOVT} = DiceRoller::pin($dice->roll(6, 2, $m), 0, 14);

    $m = -7;
    $m += $self->{ATTR}{GOVT};
    $self->{ATTR}{LAWL} = DiceRoller::pin($dice->roll(6, 2, $m), 0, 10);

    $m = 0;
    $m += $TBL_T{$self->{ATTR}{PORT}}[0];
    $m += $TBL_T{$self->{ATTR}{SIZE}}[1];
    $m += $TBL_T{$self->{ATTR}{ATMO}}[2];
    $m += $TBL_T{$self->{ATTR}{HYDR}}[3];
    $m += $TBL_T{$self->{ATTR}{POPU}}[4];
    $m += $TBL_T{$self->{ATTR}{GOVT}}[5];
    $self->{ATTR}{TECH} = DiceRoller::pin($dice->roll(6, 1, $m), 0, 15);

    return $self;
    }

    1;</pre>[/QUOTE]</font>
  • Perl tool - wg.pl
    </font><blockquote>code:</font><hr /><pre style="font-size:x-small; font-family: monospace;">package main;

    use TravellerDecimal;

    sub world_profile
    {
    my $w = shift;
    my $c = new TravellerDecimal;

    print $w->{ATTR}{PORT},
    $c->d2t($w->{ATTR}{SIZE}),
    $c->d2t($w->{ATTR}{ATMO}),
    $c->d2t($w->{ATTR}{HYDR}),
    $c->d2t($w->{ATTR}{POPU}),
    $c->d2t($w->{ATTR}{GOVT}),
    $c->d2t($w->{ATTR}{LAWL}),
    '-',
    $c->d2t($w->{ATTR}{TECH}),
    ' ',
    $w->{NAVAL} ? 'N' : ' ',
    $w->{SCOUT} ? 'S' : ' ',
    ' ',
    $w->{GIANT} ? 'G' : ' ',
    "\n";
    }

    world_profile(new TravellerWorld->generate);</pre>[/QUOTE]</font>
 
Example - basic random chargen

Note - I should really write some of the helper packages for career resolution, then use them to beef up this package.

Contract: generate a character from a service, number of terms, age, UPP, and the number of skills that person should have.

Methods:
</font>
  • setSkillsPerTerm(n)</font>
  • setServices(list of services)</font>
  • gen()</font>
Output from gen: a string with the following data:

Service(terms) age UPP Skills:skill_count

Example:
</font><blockquote>code:</font><hr /><pre style="font-size:x-small; font-family: monospace;">Merchant(4) 34y AC5578 Skills:16</pre>[/QUOTE]</font>
  • Perl package - RandomCharacter.pm
    </font><blockquote>code:</font><hr /><pre style="font-size:x-small; font-family: monospace;">package RandomCharacter;
    use DiceRoller;
    use TravellerDecimal;

    my $dr = new DiceRoller;
    my $td = new TravellerDecimal;
    my @services;
    my $skillsPerTerm = 4;

    sub new { bless {}, shift }

    sub setSkillsPerTerm
    {
    my $self = shift;
    $skillsPerTerm = shift;
    }

    sub setServices
    {
    my $self = shift;
    @services = @_;
    }

    sub gen
    {
    my $self = shift;
    my $service = $services[ rand(@services) ];
    my $upp = '';

    $upp .= $td->d2t( $dr->roll( 6, 2 ) ) for 0..5;

    my $terms = $dr->roll( 6, 1 );
    my $age = 18 + 4 * $terms;

    my $skills = $terms * $skillsPerTerm;

    return ($service, $terms, $age, $upp, $skills);
    }
    1;</pre>[/QUOTE]</font>
  • Perl tool - rc.pl
    </font><blockquote>code:</font><hr /><pre style="font-size:x-small; font-family: monospace;">use RandomCharacter;

    my $rc = new RandomCharacter;

    $rc->setServices( 'Merchant', 'Scout', 'Army', 'Navy', 'Marine', 'Rogue' );
    $rc->setSkillsPerTerm( 4 );

    my ($service, $terms, $age, $upp, $skills ) = $rc->gen, "\n";

    print "$service($terms) ${age}y $upp Skills:$skills\n";</pre>[/QUOTE]</font>
 
Originally posted by robject:
Thoughts?
One of the goals of the Universe package was to provide a common data platform for projects just like this. After all, why re-invent the wheel?

Assuming your prefered development tools support accessing an ODBC database then a sample database (using open source version of InterBase) and a Logical Design Diagram of same is available for free download from here (assuming you don't want to buy Universe).

Regards PLST
 
That's a great resource, a huge improvement on -- and a worthy replacement of -- all that came before it.

The tools on this topic are waaay on the other end of the spectrum. If Universe is an operating system, the Utilities Cookbook is a set of Unix tools. Lean, mean, and relatively independent, for tactical problem solving, rather than a comprehensive application. For that reason, I'm avoiding a database until there appears to be no choice.
 
Example - simple career term roller.

This needs a lot of improvement... maybe it needs streamlining and/or more definition. And it needs a better output format.

Contract: To iterate one career term for a character. The utility must be provided with numbers for survival, commission, promotion, and continuance. The output is a string of the form:
</font><blockquote>code:</font><hr /><pre style="font-size:x-small; font-family: monospace;">Rx y (codes)</pre>[/QUOTE]Where
</font>
  • R is 'E' for enlisted, or 'O' for officer.</font>
  • x is the character's current Rank number.</font>
  • y is the number of skills received this term.</font>
  • Optional codes are: Wo (wounded/non-survival) Co (commissioned) Pr (promoted) Mu (mustered out).</font>
Here's an example of a very old veteran:
</font><blockquote>code:</font><hr /><pre style="font-size:x-small; font-family: monospace;">E1 4
E1 4
O2 6 Co Pr
O3 5 Pr
O4 5 Pr
O5 5 Pr
O5 4
O6 5 Pr
O7 5 Pr
O7 2 Wo Mu</pre>[/QUOTE]</font>
  • Perl package - Term.pm
    </font><blockquote>code:</font><hr /><pre style="font-size:x-small; font-family: monospace;">package Term;
    use DiceRoller;

    my ($survival, $commission, $promotion, $continuance);
    my $dr = new DiceRoller;
    my $skills = 0;
    my $rank = 1;
    my $commissioned = 0;
    my $done = 0;

    sub new { bless {}, shift }

    sub setParameters
    {
    my $self = shift;
    ($survival, $commission, $promotion, $continuance) = @_;
    }

    sub term
    {
    my $self = shift;
    $skills = 0;
    my $out = $self->survival();

    $out .= $self->commission()
    . $self->promotion()
    . $self->continuance()
    unless $out =~ /Mu/;

    return "E$rank $skills $out" unless $commissioned;
    return "O$rank $skills $out" if $commissioned;
    }

    sub survival
    {
    return '' if $done;
    unless ($dr->roll( 6, 2 ) >= $survival)
    {
    $skills = 2;
    return 'Wo Mu';
    }

    $skills = 4;
    return '';
    }

    sub commission
    {
    return '' if $done;
    return '' if $commissioned;
    return '' unless $dr->roll( 6, 2 ) >= $commission;

    $commissioned = 1;
    $rank = 1;
    $skills++;
    return 'Co ';
    }

    sub promotion
    {
    return '' if $done;
    return '' unless $commissioned;
    return '' unless $dr->roll( 6, 2 ) >= $promotion;

    $skills++;
    $rank++;
    return 'Pr ';
    }

    sub continuance
    {
    return '' if $done;
    return 'Mu ' unless $dr->roll( 6, 2 ) >= $continuance;
    return '';
    }
    1;</pre>[/QUOTE]</font>
  • Perl tool - term.pl
    </font><blockquote>code:</font><hr /><pre style="font-size:x-small; font-family: monospace;">use Term;

    my $term = new Term;

    $term->setParameters( 6, 8, 7, 4 );

    for (;;)
    {
    my $t = $term->term;
    print "$t\n";
    exit if $t =~ /Wo|Mu/;
    }</pre>[/QUOTE]</font>
 
FlightCommanderSolitude, I've added a pin() method to the Dice Rollers, since I don't have enough time to learn how to modify the Python source.
 
Now I'm considering a utility that acts as a sort of generic black box for starship component creation.

This thing would take settings, such as tech level, output mode, and some sort of efficiency variable. Once configured, the box would then take inputs "output level desired", and perhaps a name, and produce a component (calculating volume, power required, fuel required, cost).

The idea is to represent a linear progression of modules in its own black box.
 
robject

Wondering what your thoughts were about PDA's and calculators as targets for these utilities.

I'm guessing that not everyone wants to lug a laptop around just to run utilities of the kind you are suggesting?
 
Back
Top