//
// @(#)AIDemo.java 12/2003
//
// Copyright 2003 Zachary DelProposto.
//
// This module (AIDemo.java) is example code; all or part of this code
// may be freely used in your own programs without attribution and without
// restriction. Note that that is not the case for most other jDip source
// and binary modules; see their licensing information for details.
//
package dip.misc;
import dip.order.*; // orders
import dip.order.result.*; // results of orders
import dip.world.*; // 'main' things (Units, Provinces, etc.)
import dip.world.variant.VariantManager; // Loads variants (maps, etc.)
import dip.world.variant.data.*; // variant data
import dip.process.*; // adjudication
import java.util.List;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Iterator;
import java.io.*;
/**
* This class is meant to be an illustrative example of how to use
* the adjudication routines to evaluate moves, as might be needed
* by an AI (computer) player or the like.
* <p>
* It is to be run from the command line. Most exceptions will cause
* an abort, and be displayed on the command line.
* <p>
* Normally, jDip will create a World object, which has an initial TurnState.
* Orders and units are added to the TurnState. When the TurnState is resolved,
* a new TurnState is added to the world.
* <p>
* In our case, we will be doing things a bit differently. We are not trying to
* create an entire game (though we could!).
* <p>
* This demo will set up a position involving all 7 powers on a Standard map
* (this position is NOT the initial starting position). The goal is to find
* the set of orders which will allow Germany to take the Russian province
* of Warsaw, which contains a supply center. We use the adjudicator to
* evaluate several order sets until we find the order set that will accomplish
* our goal. A secondary goal is to ensure any German units don't get dislodged
* by Russia.
* <p>
* Note that this is not an AI, and just a demo. As such, the other powers
* are all assumed to have their units Hold in position. Furthermore, the order
* sets are not generated by an algorithm, but pre-selected.
* <p>
* N.B.: note the potential naming conflict between dip.world.Map and java.util.Map.
*/
public class AIDemo
{
// constants
/** Directory name where variants are stored */
private static final String VARIANT_DIR = "variants";
private static final String VARIANT_NAME = "Standard";
/** Command-line entry point */
public static void main(String[] args)
throws Exception // allow any exceptions through
{
new AIDemo();
}// main()
/** Create the AIDemo */
public AIDemo()
throws Exception
{
// create the World
World world = createStandardWorld();
// create a position to evaluate
Position position = createPosition(world);
// create order sets
createOrders(world.getMap(), position);
// evaluate order sets
}// AIDemo()
/**
* Create the World. The World, and its components, are required for
* most operations by jDip.
*/
private World createStandardWorld()
throws Exception
{
// Get default variant directory, where the jDip variants are stored.
// This assumes the standard jdip package layout.
//
File defaultVariantSearchDir = null;
if(System.getProperty("user.dir") == null)
{
defaultVariantSearchDir = new File(".", VARIANT_DIR);
}
else
{
defaultVariantSearchDir = new File(System.getProperty("user.dir"), VARIANT_DIR );
}
System.out.println("Variant diretory: "+defaultVariantSearchDir);
// Parse the variants in the variant directory/directories.
// if no variants are found, this will display (using a Swing dialog)
// an error message. The 'false' sets if we are using XML validation;
// we generally do not want to.
//
VariantManager.init(new File[]{defaultVariantSearchDir}, false);
System.out.println("VariantManager initilization complete.");
// Load the variant (VARIANT_NAME) that we want.
// Throw an error if it isn't found!
//
Variant variant = VariantManager.getVariant(VARIANT_NAME, VariantManager.VERSION_NEWEST);
if(variant == null)
{
throw new IOException("Cannot find variant "+VARIANT_NAME);
}
System.out.println("Variant "+VARIANT_NAME+" found.");
// Create the World object. The World object contains the a dip.world.Map,
// which contains Province and Power information, as well as TurnStates,
// which hold turn and Position information.
//
World newWorld = WorldFactory.getInstance().createWorld(variant);
System.out.println("World created!");
// Set the RuleOptions in the World. This sets the RuleOptions to their
// defaults, which is usually what we want.
//
newWorld.setRuleOptions(RuleOptions.createFromVariant(variant));
// This is to illustrate some features of the dip.world.Map object.
//
Map map = newWorld.getMap();
Power[] powers = map.getPowers();
System.out.println("\nPowers in this game:");
for(int i=0; i<powers.length; i++)
{
System.out.println(" "+powers[i]);
}
// how do we get a specific power?
//
Power aPower = map.getPower("france");
System.out.println("\ngetPower(): "+aPower);
System.out.println("People from "+aPower+" are called "+aPower.getAdjective());
// what about a province?
//
Province prov = map.getProvince("spa");
System.out.println("\nProvince testing:");
System.out.println(" prov full name: "+prov.getFullName());
System.out.println(" prov abbreviation: "+prov.getShortName());
System.out.println(" all TOUCHING provinces:");
Location[] touchLocs = prov.getAdjacentLocations(Coast.TOUCHING);
for(int i=0; i<touchLocs.length; i++)
{
System.out.println(" "+touchLocs[i].getProvince());
}
// what about a Location? (A Location is a Province + a Coast)
//
Location loc1 = map.parseLocation("spa/sc"); // South Coast of Spain
Location loc2 = map.parseLocation("spa/nc"); // North Coast of Spain
System.out.println("\nLocation testing:");
System.out.println(" "+loc1.toLongString()+" and "+loc2.toLongString());
System.out.println(" same location?: "+loc1.equals(loc2));
System.out.println(" same province?: "+loc1.isProvinceEqual(loc2));
System.out.println(" adjacent? "+loc1.isAdjacent(loc2));
System.out.println(" adjacent Locations to "+loc1.toString()+":");
Location[] adjLocs = loc1.getProvince().getAdjacentLocations(loc1.getCoast());
for(int i=0; i<adjLocs.length; i++)
{
System.out.println(" "+adjLocs[i]);
}
// a test: the province "spa" and the location "spa/sc" as well as
// "spa/nc" all share the EXACT SAME Province reference. This is not true
// of Locations, however (why? because to do that, Locations would have
// to be interned like String objects can be).
//
// It is always safe to use equals() if you are not sure.
//
System.out.println("\nEqualities:");
System.out.println(" loc1 and prov: same province? "+loc1.isProvinceEqual(prov));
System.out.println(" loc2 and prov: same province? "+(loc2.getProvince() == prov)); // referential equality!
return newWorld;
}// createStandardWorld()
/**
* Set up some positions (via a TurnState and a Position object) so that
* we can evaluate orders. This uses the Position within the very first
* TurnState object in the World. In this case, the very first TurnState
* object in the Standard variant has:
* <ul>
* <li>All home supply centers have a unit</li>
* <li>Home supply centers are set with their home power</li>
* <li>No powers have been eliminated</li>
* </ul>
*/
private Position createPosition(World w)
{
// get the initial position
Position pos = w.getLastTurnState().getPosition();
// a Map reference, for convenience
final Map map = w.getMap();
// All Power and Province are immutable references. Thus they must
// be obtained from the Map object.
//
// add some extra units
// set extra German units
//
Power germany = map.getPower("germany");
Unit u = new Unit(germany, Unit.Type.ARMY);
u.setCoast(Coast.LAND); // Army units always must be in Coast.LAND (== Coast.NONE)
pos.setUnit(map.getProvince("pru"), u);
// NOTE: it would be VERY BAD to use the same unit we created above, and also
// insert it in another province. Why? Because when a one province has a unit
// moved or destroyed, the other province would have the same. So don't do that.
//
u = new Unit(germany, Unit.Type.ARMY);
u.setCoast(Coast.LAND);
pos.setUnit(map.getProvince("sil"), u);
u = new Unit(germany, Unit.Type.ARMY);
u.setCoast(Coast.LAND);
pos.setUnit(map.getProvince("gal"), u);
// set extra Russian units
//
Power russia = map.getPower("russia");
u = new Unit(russia, Unit.Type.ARMY);
u.setCoast(Coast.LAND);
pos.setUnit(map.getProvince("lvn"), u);
System.out.println("\nInitial position created.");
return pos;
}// createPosition()
/**
* Given the position that we created, make several different sets of
* orders that we can check to see which is best.
* <p>
* All non-russian, non-german units will not be given orders (they will
* Hold by default).
* <p>
* We return an Array of Lists (a somewhat unusual construct...)
*/
private List[] createOrders(Map map, Position pos)
{
// get the OrderFactory. The default order factory is OrderFactory.getDefault().
OrderFactory orderFactory = OrderFactory.getDefault();
// power constants (note: we could have set these globally, as they
// are the same, referentially, as when we obtained them in
// createPosition()
//
final Power russia = map.getPower("russia");
final Power germany = map.getPower("germany");
// Russian Orders
// ==============
// A war S lvn-pru
// A lvn-pru
// A mos-lvn
List russianOrders = new ArrayList();
russianOrders.add(orderFactory.createSupport(russia,
makeLocation(pos, map.getProvince("war")),
Unit.Type.ARMY,
makeLocation(pos, map.getProvince("lvn")),
russia,
Unit.Type.ARMY,
makeLocation(pos, map.getProvince("pru"))
));
russianOrders.add(orderFactory.createMove(russia,
makeLocation(pos, map.getProvince("lvn")),
Unit.Type.ARMY,
makeLocation(pos, map.getProvince("pru"))
));
russianOrders.add(orderFactory.createMove(russia,
makeLocation(pos, map.getProvince("mos")),
Unit.Type.ARMY,
makeLocation(pos, map.getProvince("lvn"))
));
// we're just making 2 sets of german orders
//
List[] germanOrders = new List[2];
// German Orders: 1
// ================
// A pru-war
// A sil S A pru-war
// A gal S A pru-war
germanOrders[0] = new ArrayList();
germanOrders[0].add(orderFactory.createMove(
germany,
makeLocation(pos, map.getProvince("pru")),
Unit.Type.ARMY,
makeLocation(pos, map.getProvince("war"))
));
germanOrders[0].add(orderFactory.createSupport(
germany,
makeLocation(pos, map.getProvince("sil")),
Unit.Type.ARMY,
makeLocation(pos, map.getProvince("pru")),
germany,
Unit.Type.ARMY,
makeLocation(pos, map.getProvince("war"))
));
germanOrders[0].add(orderFactory.createSupport(
germany,
makeLocation(pos, map.getProvince("gal")),
Unit.Type.ARMY,
makeLocation(pos, map.getProvince("pru")),
germany,
Unit.Type.ARMY,
makeLocation(pos, map.getProvince("war"))
));
// German Orders: 2
// ================
// A pru S A sil-war
// A sil-war
// A gal S A sil-war
germanOrders[1] = new ArrayList();
germanOrders[1].add(orderFactory.createSupport(
germany,
makeLocation(pos, map.getProvince("pru")),
Unit.Type.ARMY,
makeLocation(pos, map.getProvince("sil")),
germany,
Unit.Type.ARMY,
makeLocation(pos, map.getProvince("war"))
));
germanOrders[1].add(orderFactory.createMove(
germany,
makeLocation(pos, map.getProvince("sil")),
Unit.Type.ARMY,
makeLocation(pos, map.getProvince("war"))
));
germanOrders[1].add(orderFactory.createSupport(
germany,
makeLocation(pos, map.getProvince("gal")),
Unit.Type.ARMY,
makeLocation(pos, map.getProvince("sil")),
germany,
Unit.Type.ARMY,
makeLocation(pos, map.getProvince("war"))
));
// create combined orders sets for all powers
//
List[] orderLists = new List[2];
for(int i=0; i<orderLists.length; i++)
{
orderLists[i] = new ArrayList();
orderLists[i].addAll(russianOrders);
orderLists[i].addAll(germanOrders[i]);
}
System.out.println("Created "+orderLists.length+" sets of orders to evaluate.");
return orderLists;
}// createOrders()
/** Make a Location for a Unit */
private Location makeLocation(Position pos, Province prov)
{
return new Location(prov, pos.getUnit(prov).getCoast());
}// makeLocation()
/**
* Evaluate orders, stopping when we have reached our goal
* (occupying Warsaw).
* <p>
* Note that there are many ways to evaluate the success of an order set.
* One method is to use the Position object, and check where units are.
* Another method is to look at the order results via TurnState.getResultList().
* Iterating through the OrderResults, once can determine which orders are
* successful and (if adjudicator statistical reporting is enabled) the
* attack:defense statistics involved).
*/
private void evaluateOrders(World world, Position position, List[] orderSets)
{
/*
we will evaluate by finding the BEST order that takes the sc
(first check via hasUnit())
then the one with the most attack strength
*/
}// evaluateOrders()
}// class AIDemo