WAR and PIECES The simplest definition of a pieces is "the little cardboard counters you move around on the map." In other words, they are the mobile and mutable objects which you and your opponents command. You may remember our definition of a WarGame as "force applied through space and time". In keeping with that, I would like to discuss pieces in those components. Note that not all pieces need to support all these protocols: A. Time This is a very simple protocol: @protocol Clockable -(float) tick:(float)time; @end Basically, a piece gets a clock tick, which (optionally) tells it what the current time is. The piece is responsible for knowing what (if anything) it is supposed to do at that time. It may be something as simple as "reset yourself since the turn has ended", or as complex as "move along a path towards the nearest enemy, and fire when within range". All that information is encapsulated. I do not think we need a protocol for a Clock, since it is either an integral part of the game or called directly from the user interface. More likely, we can just have sample WarClock classes. B. Space A piece needs to know what region it is in: -region; since that is how it and others find out their location. A piece also needs to know how to move. There may not be a general mechanism, but keeping track of movement points is an exceedingly common one, and probably worthy of a protocol. @protocol - (float) MP; // current value - (float) setMP:(float)amount; - (float) subMP:(float)amount; - (float) calcMP:(float)length across:terrain; - (float) checkMP:(float)length across:terrain; - (float) moveMP:(float)length across:terrain; - (float) maxMP; // maximum value - (float) resetMP; // reset, return maximum value @end All these routines return the current MP level, except for "checkMP" which returns the amount left (possibly negative) if the move were to be taken. Note, as usual, that terrain can be 'nil' where it doesn't matter. Yes, checkMP and moveMP are redundant, but sending messages can be expensive across a connection, so its worthwhile to optimize common behavior like this (I think). C. Force There are three principal ways pieces interact with each other. Of course, more complicated pieces will have many more methods, but these are the 'core' of a wargame piece. @protocol Sensor - space; - spaceAt:Location; @end A Sensor is used to locate other pieces. It basically does its work by calling the "fill:in:" method of Region. Actually, I think that should be: Region -fill: in:; and Sensor is just a WarMap that overrides "place:at:". After all, isn't a sensor just something that creates an internal representation of the outside world? Since the Sensor stores the location, it can even introduce errors (for inaccurate sensors). I think Sensor "isa" WarMap instead of 'hasa', but I could be wrong. I think it "hasa" WarSpace (its effective range), which should be set relative to the current location. I don't think they are is any other general information. There could sometimes be fields for sensor types, but that information could just come from the class hierarchy. Combat I view combat as being initiated by the attacker, but managed by the defender. The attacking piece first determines that the intended victim is within range of the given weapon (using a sensor). Then the victim (defender) is told it is being attacked by that weapon. The victim then queries the attacker for various characteristics, then takes the damage. It returns a float, which can be the damage done to the attacker. @protocol Defender -(float) sensedBy:sense at:(Location)location; // could just be a BOOL yes/no -(float) attackedBy:attack at:(Location)location; // controls the flow of battle // returns damage to attacker? -(float) inflict:(float)damage; // separate method, so can be called by others @end @protocol Attacker - (float) strength:sender; - (float) skill:sender; @end This may seem pretty simplistic, but I think it is quite general. For instance, I think this protocol is powerful enough to support full champions combat [if restricted to killing attacks]. Most information that can be easily parametrized is contained in these methods. Other information tends to be "isKindOf" queries. Note that a piece may not respond to the "Attacker" protocol itself, but have sub-objects (i.e., weapons) which it uses to attack. D. Summary As a truism, I think it is true that every piece has to be capable of being attacked. So the 'basic' piece is: @protocol WarPiece -(Location)location; //even if not stored locally, want to be able to find out -region; -(int)owner; @end The owner entry is simply a quick way to check whose side you are own. Since we are potentially dealing with remote object proxies, we can not guarantee that the 'id' of a player object would be the same. And ints are easy and cheap to compare. Note that the owner could refer to an internal variable, for pieces which can change sides in the course of play. E. Example Let me try to give a quick example of how these protocols might be used. This is not meant to be definitive, merely one small possibility to show what the protocols are capable of. Assume senseMap adds any pieces that respond to 'sensedBy:' with a non-zero value. Note that many methods are omitted for simplicity. @class Defending { float dcv; // Defense Combat skill float health; // decremented by inflict, restored by tick. id random; } // Uses champion-style rules // Assumes the existence of a Random class - (float) attackedBy:attacker at:loc { float ocv = [attacker skill:self]; if (![random erf:(ocv - dcv)]) return 0.0; // erf = error function [for a normal distribution] // basically rolls a random number, returns true or false float damage = [random d6:[attacker strength:self] ]; // assumes strength is number of dice of damage [self inflict:damage]; //ouch, applies armor et al. // can even take care of hit locations and so forth return 0.0; // Pure defender, can't hit back } @class Attacking : Defending { id senseMap; id weapons; int currentWeapon; // next usable weapon } - (int) attackFrom:loc // takes current location as an argument // returns number of things attacked { id targets; int i, n; [[self region] fill:senseMap in:[senseMap spaceAt:loc]]; // assume that all weapons use the same sensor targets = [senseMap pieces]; n = [targets count]; for (i=0, i < n; ++i) { id weapon; float damage; id target = [targets objectAt:i]; if ([target owner] == [self owner]) continue; // don't attack your allies! weapon = [weapons objectAt:currentWeapon++]; if (!weapon) break; damage = [target attackedBy:weapon at:loc]; if (damage) [self inflict:damage]; if (health < 0) return i; } return i; } // Attacking classes hit back! // assume it gets automatic hit if in range, and has a weapon - (float) attackedBy:attacker at:loc { Location diff = [self location] - loc; id weapon = [weapons objectAt:currentWeapon]; [super attackedBy:attacker at:loc]; if (diff.mag() < [weapon range]) { ++currentWeapon; return [random d6:[weapon strength:attacker]]; } return 0.0; } @end This is an extremely crude and incomplete example. However, I hope it at least gives you a feel for what we are working towards. It probably also indicates we need to start doing actual implementations, to start beating our theoretical speculations into some correspondence with reality. First, though, we have to figure out how to deal with the user...