
SKIRMISH -- Version 1.0
A Programming Game of Strategy and Chaos
by Richard Shock
Copyright 1994

Disclaimer:

This game is in its earliest form.  There are countless bugs, 
glitches, and logical discontinuities.  While I don't see how it 
could possibly screw up anything on your computer, I assume no 
responsibility if you manage to make that happen.

Skirmish is SLOW.  This is partly because I wrote it in clunky old 
Pascal, and partly because I have yet to optimize it.  You would 
find very little elegance in my code, and perhaps several 
oversights.

I would be interested in hearing about any serious suggestions or 
bugs.  If I use your contribution, I'll include your name in the 
next distributed version of Skirmish, assuming there IS one.  (You 
won't receive any cut of registration fees, though.  No big deal, 
since these never come to much.)


Introduction:

Complex systems lend themselves to Chaos, an unpredictable sequence 
of events that are extremely sensitive to initial conditions.  Even 
if every ship in Skirmish has the same set of programs, a chaotic 
outcome will usually result.  Changing the initial positions of a 
single ship by even one unit can lead to a wildly different outcome 
for any particular run.

The good news is that Chaos DOES sometimes follow a pattern.  These 
patterns involve events that recur almost randomly within certain 
boundaries, termed "Strange Attractors."  Imagine for example a 
pendulum swinging in the breeze.  Depending on effectively random 
air patterns, the pendulum's arc may take it around in endless 
patterns, all of which are maximally limited by the length of the 
pendulum's string.  This string might be considered the strange 
attractor's physical manifestation.

In Skirmish, strange attractors amount to STRATEGY.  Although we 
may never know exactly what will happen with a combination of ship 
positions and programming, by methodical experimentation we CAN 
establish that certain fleet configurations tend to win out over 
certain other fleet configurations.  The trick to playing this game 
is to learn these advantaged configurations and apply them against 
your opponents.  Obviously no one will win all the time, so match 
results ought to be averaged.

Broadly speaking, Skirmish reflects how we deal with all real-world 
situations.  Life IS Chaos, and most of our behaviors are designed 
to navigate us into the safe haven of a relatively predictable 
strange attractor.  Isn't the universe easy when you know the 
secret?


Directions:

Skirmish comes with simple Help dialogs that briefly outline the 
basics.  Please refer to these.

For those who want an immediate example, following these steps:

1)  Enter Skirmish.

2)  In the File menu, select Open Configuation.

3)  From this, choose "EXAMPLE1.CON."  Two fleets of ships should 
appear on the screen.

4)  In the Control menu, select Run/Resume.

5)  Sit back and watch the battle.  (Select Stop from the Control 
menu when you've seen enough.)


Programming Manual:

Skirmish ships use programs written in a very limited microcode.  
You may compose Skirmish programs with the onboard dialog boxes, or 
in any application that edits ASCII text.  Microcode programs have 
a suffix of either .ABS or .IFS, respectively for Absolute programs 
and Contingency programs.

Programs in a ship's Absolute program slot supersede all other 
programming, causing the ship to ignore everything in its 
Contingency and Default program slots.  Contingency programs have a 
set of conditions that are checked each turn;  if the conditions 
are met, the Contingency program's results suddenly become 
absolute, superseding all other Contingency and Default programming 
until they finish.  Programs in the Default slot are run whenever 
no Contingency conditions are matched.  Default programs are never 
absolute, and will be interrupted at any step to carry out a 
triggered Contingency result.

Both Absolute and Default programs will loop back to their 
beginning once they finish.

After you've loaded a particular ship's slots with programs and 
return to the main window, Skirmish automatically compiles the 
programs listed for that ship.  If one of these programs has a bug 
in syntax or logic, a message box will come up to tell you the 
incorrect program line.  No part of that program will remain 
compiled to the ship.

Basic microcode statements are as follows:

* filename -- any previously written microcode program can be 
included in another microcode program by starting a line with * 
(asterisk) and the desired microcode's proper directory location 
and filename.  In fact, this is the usual method the Contingency 
Program Dialog utilizes to write a result.  Note that the "* 
filename" form can be recursed approximately five deep: * file 
within * file within * file within * file within * file.

ANGLE n -- Turns a ship to angle n.  "n" is an integer between 0 
and 359.  "n" greater than 360 will cause the ship to turn at a 
random angle.

DEADSTOP -- This will bring a ship to an immediate halt, against 
all known laws of physics.

DELAY n -- A ship will do nothing for a period defined by n turns 
(or "steps," as displayed in the Skirmish Window Text during a 
run).  "n" is a positive integer.

EVADE identity d [also, SEEK identity d] -- Turns a ship in the 
direction of the nearest object matching the stated "identity," 
then adds an angle of d.  [Identities include ENEMY, ENEMYMISSILE, 
ENEMYMOTHERSHIP, ALLY, ALLYMISSILE, ALLYMOTHERSHIP, and SELF.]  
"EVADE ENEMY 180" means to turn 180 degrees away from the nearest 
enemy ship.  "EVADE ENEMY 0" would turn the ship directly TOWARD 
the nearest enemy (hence the alternative, SEEK).  "n" is an integer 
degree value from 0 to 359.  If n>360 or EVADE SELF n, the ship 
will turn in a random direction.

EVADE PARALLEL identity d -- Much like the simple evasion, except 
that it gives you the angle of the ship you wish to evade 
(identity), then adds d degrees to that.  This can be useful for 
preventing allies from running into each other by putting them back 
on a parallel course.

FIRE -- Fires a ballistic missile in the direction the ship is 
turned.  Missile speed is partly dependent on ship speed.  Missiles 
last only 15 turns before deactivating and disappearing.  Missiles 
never collide with other missiles, and are not programable.

IF (condition)(condition)... -- The header of all Contingency 
programs, but capable inclusion into the body of both Contingency 
and Absolute programs.  See "Contingency Arrangements" for a full 
discussion and examples.

SEEK identity n -- (See EVADE.)

SELFDESTRUCT -- Blows up a ship so that everything within a 50 unit 
radius is also destroyed.  (Motherships destroy everything within 
100 units.)  Most often used when a ship is within range of more 
than two enemies at once.

SPEED n -- accelerates a ship to n units every turn.  If n is 
greater than 30, speed is set randomly.


Contingency Arrangements

Contingency programs always begin with a collection of conditions, 
each bounded by parentheses.  Conditions take the general form:

(identity RANGE vvv NUMBER www RATE xxx BEARING yyy zzz)

where identities include ENEMY, ENEMYMISSILE, ENEMYMOTHERSHIP, 
ALLY, ALLYMISSILE, ALLYMOTHERSHIP, and SELF, and www, xxx, yyy, and 
zzz are integers lengthened to three digits by initial zeroes.  
Please note that spacing in this statement is RIGID;  there must be 
ONE AND ONLY ONE space between each trait and its numeric value.

Only one trait (RANGE, NUMBER, RATE, BEARING) need be stated for 
each conditional statement, though all may be included.

There is an implicit AND between all conditions and traits.

(OR's can be approximated by other Contingency programs with 
similar results.  For example, if you wanted a ship to SELFDESTRUCT 
when it was in range of either an ALLY or an ENEMY, you'd give that 
ship one contingency program to SELFDESTRUCT near an ALLY and 
another to SELFDESTRUCT near an enemy.  Though why you'd want to do 
this, I have no idea.)

The relationship between each trait and its numeric value is 
assumed to be "less than or equal two."  A "greater than" 
relationship can be accomplished by placing ">" in the space 
between the trait and its value, eg. (ENEMY NUMBER>002).

Results for Contingencies are always bounded by BEGIN and END.  
(The Pascal/Basic/etc. "THEN" isn't necessary.)  Statements on 
lines between BEGIN and END can be indented to aid in coherency, 
but it isn't necessary.

Since Contigency program results become absolute until they finish 
their sequence, it's best to keep these result sequences BRIEF.  
This avoids interfering with other Contingencies, which might just 
save the life of your ship.  For instance, if a ship has a 
Contingency program to constantly dodge ally ships, you don't want 
it looking the other way while trying to fly in formation.

Now for Contingency examples:

IF (ENEMY RANGE 050 NUMBER>001)
BEGIN
  SELFDESTRUCT
END

The purpose of this Contingency Program (included with the Skirmish 
package as SBOMB1.IFS) is fairly clear:  If there is more than one 
enemy within a range of 50 units, self destruct.  This would of 
course give your ship a 2:1 kill ratio.

Note that only one condition with two traits is mentioned.

IF (ENEMY RANGE 050 NUMBER>001) (ALLY RANGE 050 NUMBER 000)
BEGIN
  SELFDESTRUCT
END

This variation on SBOMB2.IFS shows the beginnings of Skirmish's 
slightly weird logic.  Not only must two or more enemies be in a 
range of 50 units, zero allies must be in that same range.  (This 
makes sense, unless you want to lose your acceptable kill ratio.)  
For you non-programmers, Condition 1 AND Condition true must both 
be true for the ship to self destruct.

Let's say we wanted a ship to self destruct if two or more enemies 
were in range OR a certain number of missiles were in range.  Since 
SELFDESTRUCT wipes out missiles as well as ships, we might 
sacrifice one of our ships to clear a particularly thick hailstorm 
of fire.  While we could write:

IF (ENEMY RANGE 050 NUMBER>001) (ALLY RANGE 050 NUMBER 000)
      (ENEMYMISSILE RANGE 050 NUMBER>010)

THIS WOULDN'T DO MUCH.  What we'd be saying was that the ship would 
self destruct only when (1) more than two enemies were in range AND 
(2) no allies were in range AND (3) MORE THAN TEN MISSILES WERE IN 
RANGE.  (NOTE:  all parentheses-enclosed conditions in a 
Contingency statement should be on a SINGLE LINE.  The example here 
just wraps down to the next line in order to save space.)

What we WANTED was for the ship to self destruct when ten missiles 
or more are in range, without consideration for ships.  All you'd 
need to do is write:

IF (ENEMYMISSILE RANGE 050 NUMBER>010)

And then put this program in one of your ship's Contingency slots.  
This program is SBOMB3.IFS.  It might be loaded into a ship like 
this:

Contingency #1 = SBOMB2.IFS
Contingency #2 = SBOMB3.IFS
Contingency #3 =
Contingency #4 =
...

[I know I'm taking this slowly, but bear with me.  The thinking IS 
a little convoluted.]

THAT's how we create OR statements.  Parallel Contingencies ALL 
constitute one big OR here.

Now things get very skewed.  What would do you think I'd be saying 
if I wrote the following?

IF (ENEMY RANGE 100 NUMBER 003 RATE 010)

You MIGHT believe this condition would come true if 3 enemies were 
in a range of 100 units and all were going at rate (speed) 10.  You 
might believe it, but you'd be wrong.  Since there's an implied 
"less than or equal to" for each of these traits, ANY rate less 
than or equal to 10 would make it true.  Therefore, this statement 
would come true if 2 ships were 50 units away and one was moving at 
speed 4 while the other moved at speed 9.  Only if one of these 
ships suddenly went to speed 11 or greater would the statement be 
false.

The fourth possible trait, BEARING, is slightly different.  A 
ship's bearing in this case is simply the absolute angle at which 
it's traveling, from 0 to 359.  Note that in the original blank IF 
statement, both yyy and zzz came after BEARING.  These two 
quantities represent an arc, such as arc between 20 degrees and 30 
degrees or between 0 degrees and 90 degrees.  If you wanted to be 
very specific for a condition, you might try:

IF (ENEMY BEARING 090 090)

Meaning that if an enemy (since range is unspecified, that means 
ANY enemy) is traveling at exactly a 90 degree angle, the condition 
is true.  It would probably be more useful to open up the 
possibilities, such as:

IF (ENEMY BEARING 030 050)

Which gives the enemy ships a good 20 degree arc to match.

"Greater than" is slightly different with BEARING as well:

IF (ENEMY BEARING>030 050)

This would mean that an enemy can move in any arc EXCEPT 30-50 to 
make the statement come true.  That's a good 340 degrees of 
possibilities.

Contingency statements in Skirmish have a single alternative to the 
normal trait set:

IF (ENEMY COLLISION)

COLLISION checks to see if your current ship is on a collision 
course with an the identity, in this case an enemy.  Since range 
isn't specified, it's very likely any two ships will eventually 
collide (unless they're moving at parallel angles).  An open 
COLLISION like this leads to some erratic and unnecessary events.  
To alleviate this, use:

IF (ENEMY COLLISION RANGE 100)

(Or whatever range you want.)  This simply checks to see if your 
ship will collide with an enemy ship within a range of 100 units.

No other trait except RANGE currently works with COLLISION.  Note 
also that RANGE>www is equivalent to RANGE www here.


The Skirmish Microcode Failings

Obviously the Skirmish microcode interpreter is NOT a complete 
language.  Some of its glaring omissions include:

-- Variables
-- Subprogram handling
-- Repeat routines (such as for-do statements in Pascal)
-- Broad Boolean logic in conditional statements (such as NOT, OR,
   AND, <, and =)
-- Arithmetic functions

Whew, quite a list!  Still, we're dealing with a purposefully 
limited abstract system here.  Spaceships in a rather blank 
environment have little need to balance checkbooks or parse 
colloquial English.  Once again, Skirmish is a game, not a new 
programming language.

In the future I MAY add some of new features, but don't bet on it.  
Improvements will all depend on comments from the people who use 
this program.


Possible Methods For Playing Skirmish

While there's a lot to be learned from giving every ship in both 
fleets the same programming, weird and wonderful things begin 
happening when each ship is an individual.  Perhaps one of your 
ships will be stationary sniper, waiting in one spot until enemy 
ships come within range, then turning on them and blasting away.  
(Sometimes this seems to work best for the Motherships, which 
shouldn't enter the fray.)  Perhaps you'll designate a single ship 
as a dedicated assassin, forcing it to ignore everything else and 
seek out only the enemy Mothership (KAMIKAZE.ABS).  Perhaps you'll 
use one of your ships as a missile screen, self destructing should 
too many missiles come near it.  Perhaps you'll program ships to go 
into berserker mode when only a certain number of their allies 
remain (such as LAST.IFS).  The possibilities are nearly endless.

Should you find a human opponent, games become even more 
intriguing.  Since positioning of ships can be a vital advantage, 
both sides should want an arrangement that best reacts to their 
enemy's.  You might flip a coin to see you positions all his ships 
first, or you might take turns positioning one ship at a time.

Adding programs between opponents could be either open or secret.  
If the procedure is open, you might take turns as you did with 
positioning.  Secret programming might lead to some VERY surprising 
games.

But aside from what I've programmed into the system, Skirmish has 
no hard and fast rules.  Losers might be the first ones to lose 
their Motherships, they might be determined by the fewest number of 
ships left after a set time, or they might be the side with no 
ships remaining in the end.  It's up to you.


Inclusions and Explanations

Along with the SKIRMISH.ZIP package you should find the following 
Configuration Files and microcode Program Files:

EXAMPLE1.CON -- Mutual destruction, a tie.  (You see that a lot
                when both sides have the same programming, as in
                the majority of these example configurations.)

EXAMPLE2.CON -- Green wins against a geometric Red formation.

EXAMPLE3.CON -- Red wins in a lopsided arrow formation.

EXAMPLE4.CON -- Green wins by keeping back one extra ship until all
                of its allies are destroyed.  (See LAST.IFS.)

EXAMPLE5.CON -- Green wins by using one KAMIKAZE.ABS ship.  Red's
                Mothership tries to escape with AUTODEF.IFS and
                three stationary ships using DEFENDER.IFS.

HUNT.ABS -- Aims the ship toward the nearest enemy, sets it in
            motion, fires, then repeats the process.  Surprisingly
            effective for its simplicity.  Good for both Absolute
            or Default slots.

JUGGERNT.ABS -- The Ship always runs straight ahead, but aims on
                the fly to fire at enemies.  A good program for the
                Absolute slot.

BERSERK.ABS -- Ship aims at nearest enemies, dives at speed 20, and
               fires a steady stream.  (A good reason to create
               programs that fire at any ship traveling faster than
               10.)

KAMIKAZE.ABS -- Dives directly at enemy mothership, speed 25, while
                firing steadily.  Self destructs when its work is
                done.

AUTODEF.IFS -- If an enemy goes into BERSERK.ABS or KAMIKAZE.ABS
               speed (>15), the ship will run at right angles to
               the attack.  Most useful for Motherships under
               attack with KAMIKAZE.ABS (see EXAMPLE5.CON).

DEFENDER.IFS -- The ship fires at any enemy running at speed of
                greater than 15 (see EXAMPLE5.CON).

SHOOT.IFS -- If an enemy comes within range (experiment with
             varying range), the ship aims and fires at it without
             moving from its spot.

SBOMB1.IFS -- If the ship comes within range of two or more
              enemies, it self destructs, taking them with it.

SBOMB2.IFS -- If the ships comes within range of two or more
              enemies, and if no allies are in range, it self
              destructs.

SBOMB3.IFS -- The ship self destructs of two many missiles come
              near it, screening ships behind it.

DODGE-EN.IFS -- If a ship is going to collide with an enemy, it
                dodges.  (Doesn't always work, for reasons you'll
                have to figure out.)

DODGE-AL.IFS -- If a ship is going to collide with an ally, it
                takes a course parallel to that ally.

LAST.IFS -- If there are only two allies left alive, the ship goes
            into a HUNT.ABS-style pattern, aiming, diving, and
            firing.  (Sometimes it's useful to keep HUNT.ABS out of
            a ship's Default slot, so that it just sits until
            needed.  This program brings it into action.)

These are only the barest examples.  I don't begin to understand 
this game;  I only wrote it.

Good Luck,
Richard Shock
January, 1994

