ftp.nice.ch/pub/next/games/board/Risk.0.97.s.tar.gz#/RiskSource0.97/Risk/Help/06_CPReference.rtf

This is 06_CPReference.rtf in view mode; [Download] [Up]

Risk
Computer Player Reference

This section of the Help discusses how to implement your own computer player strategies in Objective C.

Computer Players are always subclasses of the class ComputerPlayer (or a subclass of ComputerPlayer).  Each computer player resides in its own directory inside the Risk.app wrapper.  The players which come with Risk are no different from your own.  The directory inside the app wrapper has a very specific name:  ClassName.cp.  That is the directory's name must be the class' name (exactly) followed by ".cp".  Inside this directory is at least a mach-o file named ClassName.o.  At launch, Risk searches it's app wrapper.  It dynamically loads all object files it can find with a path like ClassName.cp/ClassName.o.  Other files such as .nib files can be put in the directory as well as you need them.  It is important that the class name and the filenames correspond exactly as stated above.  See the Chaotic.cp directory in the Risk distribution for an example of how this works.

For examples of how to use auxilliary files with your computer player, see the source code for the Diagnostic computer player class.  It uses a .nib file to put an information panel on the screen.  In fact the Diagnostic class is a good class to make your own computer player a subclass of during development because of its debugging facilities.  Be advised though, that players based on Diagnostic are very slow compared to players based on ComputerPlayer.  If you do your developing with Diagnostic, make sure to switch to ComputerPlayer when you're done testing.

Two auxilliary files are used automatically by Risk if they are present.  When someone chooses about from the Setup Panel, an about panel is brought up.  If your computer player directory contains files named ClassName.tiff and ClassName.rtf (one or both), they are used in the about panel in the appropriate places.  The rich text file can be any length (it gets put in a ScrollView).  The tiff file should be a 48 by 48 icon like an app icon.  It is put on a disabled button in the about panel.

The ComputerPlayer class implements several utility methods which you will use in your own code as well as defining the methods which you must implement.

Instance Variables

The ComputerPlayer class has several few instance variables which are all set automatically and should not be changed:

myPlayerNum:  An int from 0 to 5 representing which row in the game setup panel is you.  You normally use this to see if a country is yours, because you can query a country as to what player occupies it.  This variable is set on object creation, and should not be altered.

rng:  An instance of the class Random.  This is entirely for your use.  See the Random.h file for how to use it.  It is created on object creation and seeded with the current time.

theMover, theGameSetup, theMapView, theCardManager:  These outlets are set on object creation to point to key objects of the app.  You do NOT need to send messages to ANY of these objects.  They are used internally by the utility routines (see below).  I have tried to give supported access to everything you might need through safe utility calls.  Hack if you must, but don't be surprised when you crash and burn.

Object Creation

The ComputerPlayer class' designated initializer is -initPlayerNum:(int)pnum mover:mover gameSetup:gamesetup mapView:mapview cardManager:cardmanager.  As stated above, the instance variables set by this method should not be modified under penalty of crashing Risk.  The rng is also allocated during creation.  You can override this method provided that the first thing you do in your version is [super initPlayerNum:...].  Note that when Risk creates the coputer player objects at the beginning of a game, it calls this method.  If you provide an init method with different arguments, it will NOT be called.

Object Death

When the game ends (actually when a new game is started) Risk will free your computer player.  It is important that if you allocate instance variables, you free them.  This means overriding - free.  The last thing you do in your - free method should be return [super free].

Supplied Utilities

The ComputerPlayer class has several methods which are not meant to be overridden.  These methods are utilities for interacting with Risk and finding out certain things about the game status.  They are listed below.  These are the ONLY recommended ways of interacting with Risk.

IMPORTANT NOTE: Any List that is returned by these methods belongs to you.  You must free it when you are done.  You may modify it as you like.  The objects (Countries or Cards) inside a List returned by these methods do NOT belong to you.  You may NOT alter them, and you should NOT free them.  You may query the objects in these lists though (indeed, this is the purpose of these methods).

Map Utilities

- countryList  -  Returns a List object of all the countries.
- myCountries  -  Returns a List object of all the countries you currently occupy.
- myCountriesWithAvailableArmies  -  Returns a List object of all your countries with more than one army in them.  This is a list of all the countries which you own and can attack or fortify from.
- neighborsTo:country  -  Returns a List object of all the countries which neighbor the country passed to it.
- countriesInContinent:(int)continent  -  Returns a List object of all the countries in the given continent.  Constants are provided in the ComputerPlayer.h file for the continent numbers.
- playersCountries:(int)pnum  -  Returns a List object of all the countries occupied by the given player.  [self playersCountries:myPlayerNum] is identical to [self myCountries].
- unoccupiedCountries  -  Returns a List object of all the countries which are not occupied by any player.  This only makes sense (returns non-nil) at the beginning of the game when countries are being chosen.

- (BOOL)occupyCountry:country  -  occupies the given country placing one army in it.  Returns YES if the country was occupied, otherwise NO.

Card Utilities

- myCards  -  Returns a List object of all the cards in your hand.
- allMyCardSets  -  Returns a Storage object with a description string "[3@]" (ie. each element in the Storage is an array of three Card objects).  The elements in the Storage represent every possible card set in your hand.  This method returns nil if there are no card sets possible.  You must free the Storage object when you are done with it.  (A card set is three cards which can be turned in)
- bestSet  -  Returns a List object of three cards which represent the best possible set you could turn in at the moment.  This method returns nil if there is no card set in your hand.  To determine which of all possible sets is best, this method looks for the set 1) with the least jokers in it, and 2) with the most countries which you occupy in it.  It does not take into account things like the proximity of the countries to the action or anything amorphous like that.
- (int)playCards:cardList  -  Given a List object of a card set, this method plays the cards and returns the number of armies you get for them.  If the cardList does not make a real set, this returns -1.

Place Army Utilities

- (BOOL)placeArmies:(int)numArmies inCountry:country  -  Places numArmies armies in country.  Returns YES if successful and NO if not.  This method allows you to cheat by placing as many armies as you want in any of your countries whenever you want, so be careful.

Attack Utilities

The attack utilities all take similar arguments (the last 7 arguments are identical in each of the 4 attack methods)  These arguments are explained once here.
fromCountry:  The country object which you are attacking from (must be occupied by you).
toCountry:  The country object which you are attacking (must not be occupied by you).
victory:  A pointer to a BOOL which will be set on return to reflect whether you conquered the toCountry.
fromArmies:  A pointer to an int which will be set on return to the number of armies left in the fromCountry.
toArmies:  A pointer to an int which will be set on return to the number of armies in the toCountry.  If victory is true, these armies are yours and were moved from the fromCountry by Risk as the minimum number of armies needed to occupy the new country after attacking.  If victory is false, these armies belong to the opponent and represent how many armies he has left after the attack.
vanquished:  A pointer to a BOOL which will be set on return to reflect whether the opponent (the player who occupied the toCountry) was completely annihilated.  If this is YES you may have gotten some new cards, and if you have more than five, you should turn in cards before doing anything else.
wewin:  A pointer to a BOOL which will be set on return to reflect whether you have won the game as a result of this attack.

Note that the attack methods return a BOOL  (YES if the attack took place, NO if it couldn't).  None of the return parameters are valid if the method returns NO.  vanquished and wewin are only valid if victory is YES.

- (BOOL)attackOnceFrom:fromCountry to:toCountry victory:(BOOL *)victory fromArmies:(int *)fromArmies toArmies:(int *)toArmies vanquished:(BOOL *)vanquished weWin:(BOOL *)wewin  -  This method attacks only one time and returns.

- (BOOL)attackTimes:(int)times from:fromCountry to:toCountry victory:(BOOL *)victory fromArmies:(int *)fromArmies toArmies:(int *)toArmies vanquished:(BOOL *)vanquished weWin:(BOOL *)wewin  -  This method attacks until it has attacked times times or it has conquered the country or it can't attack any more.

- (BOOL)attackUntilLeft:(int)untilLeft from:fromCountry to:toCountry victory:(BOOL *)victory fromArmies:(int *)fromArmies toArmies:(int *)toArmies vanquished:(BOOL *)vanquished weWin:(BOOL *)wewin  -  This method attacks until the fromCountry has untilLeft armies or less  or it has conquered the country or it can't attack any more.

- (BOOL)attackUntilCantFrom:fromCountry to:toCountry victory:(BOOL *)victory fromArmies:(int *)fromArmies toArmies:(int *)toArmies vanquished:(BOOL *)vanquished weWin:(BOOL *)wewin  -  This method attacks only until either it conquers the toCountry or it can't continue attacking.

Post-Attack and Fortify Utilities

- (BOOL)moveArmies:(int)numArmies from:fromCountry to:toCountry  -  Moves numArmies armies from fromCountry to toCountry.  Returns YES if it did, and NO if it couldn't.  You can cheat with this method.  It is meant to be used to advance armies after an attack or to fortify at the end of your turn.  Using it at any other time, or using it to shunt armies across the world in one step is cheating, but this method won't stop you.

- (int)fortifyRule  -  Returns the current fortify rule in effect.  This can change between turns so ask each time, don't cache it.  The constants for the fortifyRule are in ComputerPlayer.h.  Probably the hardest part of a computer player is getting it to play by the fortifyRule.  See the Chaotic computer player's source for an example of the bare minimum you must do if you want to fortify.

Subclass Responsibilities

There are three mandatory and 2 optional methods which are left for subclasses to implement.  These are primarily the methods which Risk uses to tell you when it is your turn to do something.  Each is discussed below.  Risk ignores the return values of all these methods.

- yourChooseCountry  -  You will get this message when during the beginning of the game it is your turn to pick an unoccupied country to occupy.  You should pick an unoccupied country and pass it to - occupyCountry:.  This is all you should do.  (In particular, do not place armies or try to attack here, that's cheating).

- yourInitialPlaceArmies:(int)numArmies  -  You get this message when during the beginning of the game it is your turn to place initial armies in your countries.  You should place numArmies armies in a country or countries you occupy using - placeArmies: inCountry:.  You should not do anything else.  If you place more than numArmies armies, you are cheating.  If you place less, too bad.

- yourTurnWithArmies:(int)numArmies andCards:(int)numCards  -  This is the main message you will get during a game.  It is sent by Risk to you when it is your turn.  A Risk move is divided into three phases: placing armies (and turning in cards), attacking, and fortifying.  When you receive this message, the minimum you should do is turn in cards if you have over four, and place numArmies armies in your countries.  After this, you may attack if you wish.  After attacking (if you wish) you may fortify armies.  It is up to you to follow the rules: nobody likes a cheater.

- youWereAttacked:country by:(int)player  -  This message is sent every time you are attacked by another player.  It is optional for you to implement this method.  If you do implement this method it is vital that you do nothing except remember the offense so you can plot your revenge.  In other words, this method is called while it is still someone else's turn, so you are not allowed to place armies, turn in cards, attack, or fortify.

- youLostCountry:country to:(int)player  -  This message is sent every time you lose a country to another player.  It is optional for you to implement this method.  If you do implement this method the same restrictions apply that aply for - youWereAttacked.  You are only allowed to remember, not to act from within this method.

Pitfalls and Gotchas

The API I have chosen for computer players has advantages and disadvantages.  The advantage is that it is pretty simple and straightforward.  The disadvantage is that it allows cheating and worse.  This section will try to detail where and when you have to be careful about cheating.
The other option for an API would be one that does not allow cheating, but the interface would have gotten very complicated.  I have opted for a simple interface, but that means that you must be a little bit careful.  See the source code for my Chaotic class for a well-behaved computer player.

The first thing you should be sure of is that you do only what you are supposed to in response to any message.  The ONLY thing you should (and must) do in response to - yourChooseCountry is occupy an UNOCCUPIED country.  occupyCountry will let you steal away a country from another player if you want, but that is not polite.  It will also let you occupy more than one country, but that is also not polite.
The only thing - yourInitialPlaceArmies: should (and must) do is place the number of armies you are supposed to.  It is possible to place more or less than numArmies, so be careful here.  It is cheating to place more, and it is stupid to place less (you will just lose the extra ones).  
The absolute minimum you should do in response to -yourTurnWithArmies: andCards: is turn in cards if you have more than four and place numArmies armies.  Again, it is possible to place more or less armies, but you shouldn't.  Also it is possible to horde cards (keep getting them and never turn them in).  This could be bad news if Risk runs out of cards because you're holding all of them.  The flow of actions in this method should be as follows:  turn in cards if you can/want, place armies (numArmies plus whatever you might have gotten for turning in cards), attack if you want, fortify if you want.  You should never place armies or turn in cards after you start attacking except if you vanquish a player, get his cards, and thus end up with more than four cards, in which case you MUST (by the rules) turn in card sets then and there until you have four or less cards left.  In this case, you should then place the armies you got for turning in cards, and continue attacking if you wish.  Once you have started fortifying, you may not attack again.
If you implement - youWereAttacked: by: or - youLostCountry: to:, you must not call - occupyCountry, - playCards:, -placeArmies::, any of the - attack... methods, or - moveArmies::: from within those methods.
Another consideration is that not returning from any method you implement will cause Risk to hang.
Fortifying is the hardest thing to do by the rules.  See Chaotic's implementation for a minmum framework.  The major considerations are that you shouldn't move any army more than once, and you should make sure to play by the fortifying rule currently in effect.

All this sounds comlicated, but it isn't really.  For a clearer explanation by example, see the source for Chaotic.

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.