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