edu.cuny.cat.Game.java Source code

Java tutorial

Introduction

Here is the source code for edu.cuny.cat.Game.java

Source

/*
 * JCAT - TAC Market Design Competition Platform
 * Copyright (C) 2006-2010 Jinzhong Niu, Kai Cai
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 */

package edu.cuny.cat;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.DirectoryFileFilter;
import org.apache.commons.io.filefilter.SuffixFileFilter;
import org.apache.log4j.Logger;

import edu.cuny.cat.comm.CatpInfrastructure;
import edu.cuny.cat.comm.CatpServerUnavailableException;
import edu.cuny.cat.comm.SocketBasedInfrastructureImpl;
import edu.cuny.cat.server.IdentityOffice;
import edu.cuny.event.EventEngine;
import edu.cuny.prng.GlobalPRNG;
import edu.cuny.util.Galaxy;
import edu.cuny.util.Parameter;
import edu.cuny.util.ParameterDatabase;
import edu.cuny.util.Parameterizable;
import edu.cuny.util.Utils;

/**
 * <p>
 * Main class of jcat when jcat is run as a Java application to launch a game,
 * including a {@link GameServer} and multiple {@link GameClient}s.
 * </p>
 * 
 * <p>
 * <b>Parameters</b>
 * </p>
 * 
 * <table>
 * 
 * <tr>
 * <td valign=top><i></i><tt>cat.server</tt><br>
 * <font size=-1></font></td>
 * <td valign=top>(the parameter base for GameServer)</td>
 * </tr>
 * 
 * <tr>
 * <td valign=top><i></i><tt>cat.agent.n</tt><br>
 * <font size=-1>int >=0</font></td>
 * <td valign=top>(the number of subpopulations of trader clients)</td>
 * </tr>
 * 
 * <tr>
 * <td valign=top><i></i><tt>cat.agent.</tt><i>n</i><br>
 * <font size=-1>class, being or inheriting TraderClient</font></td>
 * <td valign=top>(the class of trader clients for the <i>n</i>th trader
 * subpopulation)</td>
 * </tr>
 * 
 * <tr>
 * <td valign=top><i></i><tt>cat.specialist.n</tt><br>
 * <font size=-1>int >=0</font></td>
 * <td valign=top>(the number of subpopulations of specialist clients)</td>
 * </tr>
 * 
 * <tr>
 * <td valign=top><i></i><tt>cat.specialist.</tt><i>n</i><br>
 * <font size=-1>class, being or inheriting MarketClient</font></td>
 * <td valign=top>(the class of market clients for the <i>n</i>th specialist
 * subpopulation)</td>
 * </tr>
 * 
 * <tr>
 * <td valign=top><i></i><tt>cat.specialist.</tt><i>optional.dir</i><br>
 * <font size=-1>string</font></td>
 * <td valign=top>(the directory to look for the configurations of optional
 * market clients)</td>
 * </tr>
 * 
 * <tr>
 * <td valign=top><i></i><tt>cat.specialist.</tt><i>optional.base</i><br>
 * <font size=-1>string</font></td>
 * <td valign=top>(the base of parameters used to specify the configurations of
 * optional market clients in the parameter files located in
 * <code>cat.specialist.optional.dir</code>)</td>
 * </tr>
 * 
 * </table>
 * 
 * @author Jinzhong Niu
 * @version $Revision: 1.62 $
 */

public final class Game {

    public static final int minorVersion = 17;

    public static final int majorVersion = 0;

    public static final String NAME = "JCAT";

    public static final String EMAIL = "jcat-discuss@lists.sourceforge.net";

    public static final String BANNER = ""
            // + "--------------------------------------------------------------\n"
            + Game.NAME + " " + Game.getVersion() + " - (C) 2006-2010 Jinzhong Niu, Kai Cai. \n\n" + Game.NAME
            + " comes with ABSOLUTELY NO WARRANTY. This is free software,\n"
            + "and you are welcome to redistribute it under certain conditions;\n"
            + "see the GNU General Public license for more details.\n" + "\n"
            + "This is alpha test software.  Please report any bugs, issues\n" + "or suggestions to " + Game.EMAIL
            + ".\n" + "\n" + "Acknowledgements\n" + " " + Game.NAME
            + " contains classes from the following open-source packages: \n"
            + " * JASA (Java Auction Simulator API), (C) 2001-2006 Steve Phelps\n"
            + " * JAF (Java Application Framework), (C) 1999-2007 Jinzhong Niu\n"
            + " * ECJ (Evolutionary Computation in Java), (C) 2005 Sean Luke\n" + " * \n"
            // + "--------------------------------------------------------------\n"
            + "\n";

    public static final String P_CAT = "cat";

    public static final String P_VERSION = "version";

    public static final String P_NAME = "name";

    public static final String P_INFRASTRUCTURE = "infrastructure";

    public static final String P_GUI_APPENDER = "appender";

    public static final String P_SERVER = "server";

    public static final String P_AGENT = "agent";

    public static final String P_SPECIALIST = "specialist";

    public static final String P_OPTIONAL = "optional";

    public static final String P_DIR = "dir";

    public static final String P_BASE = "base";

    public static final String P_NUM = "n";

    // used to generate temporary names for clients
    protected static IdentityOffice identityOffice;

    protected static ClientSynchronizer clientSynchronizer;

    static Logger logger = Logger.getLogger(Game.class);

    public static String getDefaultParameterFile() {
        return "params/cat.params";
    }

    /**
     * retrieves the name of the parameter file from command line arguments; or
     * the default file is used if no argument is present.
     * 
     * @param args
     *          command line arguments
     * @return the name of the parameter file
     */
    public static String getParameterFile(final String[] args) {

        if (args.length < 1) {
            return Game.getDefaultParameterFile();
        } else {
            return args[0];
        }
    }

    /**
     * initializes {@link edu.cuny.util.Galaxy}, {@link GameServer},
     * {@link MarketClient}s, and {@link TraderClient}s.
     * 
     * @param args
     *          command line arguments
     */
    public static void main(final String[] args) {

        System.out.println(Game.getBanner());

        try {

            Game.setupObjectRegistry();

            Game.setupPreferences(null, args, true);

            /*
             * separating creation and starting of clients and the server aims to
             * display logging of initialization of server later than that of clients;
             * in the meanwhile, clients won't start and try to connect until the
             * server is ready, and the server won't start the game until all (local)
             * clients are already connected.
             */

            final Collection<? extends TraderClient> traders = Game.createTraders();

            final Collection<? extends MarketClient> markets = Game.createMarkets();

            final Collection<? extends MarketClient> optionalMarkets = Game.createOptionalMarkets();

            final GameServer server = Game.createServer();

            Game.startTraders(traders);

            Game.startMarkets(markets);

            Game.startMarkets(optionalMarkets);

            Game.cleanUpAfterInitialization();

            Game.startServer(server);

        } catch (final Exception e) {
            e.printStackTrace();
            Game.cleanupObjectRegistry();
            System.exit(1);
        }
    }

    public static void setupObjectRegistry() {

        // backup the old default system
        Galaxy.getInstance().put(Game.P_CAT, String.class, Galaxy.getInstance().getDefaultSystem());

        Galaxy.getInstance().setDefaultSystem(Game.P_CAT);

        final EventEngine eventEngine = new EventEngine();
        eventEngine.start();
        Galaxy.getInstance().put(Game.P_CAT, EventEngine.class, eventEngine);

        final GlobalPRNG globalPRNG = new GlobalPRNG();
        Galaxy.getInstance().put(Game.P_CAT, GlobalPRNG.class, globalPRNG);
    }

    public static void cleanupObjectRegistry() {
        Galaxy.getInstance().getTyped(Game.P_CAT, EventEngine.class).stop();

        // restore the old default system
        Galaxy.getInstance().setDefaultSystem(Galaxy.getInstance().getTyped(Game.P_CAT, String.class));

        Galaxy.getInstance().remove(Game.P_CAT);

    }

    public static void setupPreferences(final URL url, final String args[]) {
        Game.setupPreferences(url, args, true);
    }

    /**
     * does the following:
     * <ul>
     * <li>initializes log4j with the parameter file</li>
     * <li>loads a parameter file into a {@link ParameterDatabase}</li>
     * <li>invokes {@link #setupPreferences(ParameterDatabase)} to setup
     * preferences using the <code>ParameterDatabase</code>;
     * </ul>
     * 
     * @param url
     *          where the parameter file is located.
     * @param args
     *          command line arguments transferred from <code>main()</code>.
     * @param startLogging
     *          configures log4j for logging if true, or not otherwise.
     */
    public static void setupPreferences(URL url, final String args[], final boolean startLogging) {

        try {

            // when running in applet, url is not null.

            if (url == null) {
                url = ParameterDatabase.getURL(Game.getParameterFile(args));
            }

            if (url == null) {
                Utils.fatalError("Failed to load parameter database: " + Game.getParameterFile(args));
            }

            // log4j

            if (startLogging) {
                org.apache.log4j.PropertyConfigurator.configure(url);
            }

            // parameter database

            Game.logger.info("loading preferences ...");
            Game.logger.info("\n");

            ParameterDatabase parameters = null;
            if (args == null) {
                parameters = new ParameterDatabase(url);
            } else {
                parameters = new ParameterDatabase(url, args);
            }

            Game.setupPreferences(parameters);

        } catch (final IOException e) {
            e.printStackTrace();
            System.exit(1);
        }
    }

    /**
     * sets up the preferences of JCAT using the parameter database.
     * 
     * @param parameters
     */
    public static void setupPreferences(final ParameterDatabase parameters) {

        final Parameter base = new Parameter(Game.P_CAT);

        Galaxy.getInstance().put(Game.P_CAT, ParameterDatabase.class, parameters);

        // software version

        parameters.set(base.push(Game.P_VERSION), Game.getVersion());
        parameters.set(base.push(Game.P_NAME), Game.getName());

        // random number generators

        final GlobalPRNG prng = Galaxy.getInstance().getTyped(Game.P_CAT, GlobalPRNG.class);
        prng.setup(parameters, base);

        // catp infrastructure

        final CatpInfrastructure infrast = parameters.getInstanceForParameter(base.push(Game.P_INFRASTRUCTURE),
                null, CatpInfrastructure.class);
        if (infrast instanceof Parameterizable) {
            ((Parameterizable) infrast).setup(parameters, base.push(Game.P_INFRASTRUCTURE));
        }
        Galaxy.getInstance().put(Game.P_CAT, CatpInfrastructure.class, infrast);

        /*
         * when communication is synchronous, use a single random number engine
         * since there is only one thread.
         */
        if (infrast.isSynchronous()) {
            prng.setUseMultiEngine(false);
        }

        //

        Game.identityOffice = new IdentityOffice();
        Game.clientSynchronizer = new ClientSynchronizer();

    }

    /**
     * creates an instance of {@link GameServer} and initializes it with parameter
     * database in the object galaxy.
     */
    public static GameServer createServer() {

        Game.logger.info("\n");
        Game.logger.info("creating server ...");
        Game.logger.info("\n");

        final ParameterDatabase parameters = Galaxy.getInstance().getTyped(Game.P_CAT, ParameterDatabase.class);
        final Parameter base = new Parameter(Game.P_CAT);

        final GameServer server = new GameServer();
        server.setup(parameters, base.push(Game.P_SERVER));

        return server;
    }

    public static void startServer(final GameServer server) {
        Game.logger.info("\n");
        Game.logger.info("starting server ...");
        Game.logger.info("\n");

        server.start();
    }

    /**
     * creates multiple traders, each an instance of {@link TraderClient},
     * initializes them with parameter database in the object galaxy, and starts
     * them.
     * 
     * @throws InstantiationException
     *           if fails to instantiate trader clients
     * @throws IllegalAccessException
     */
    public static Collection<? extends TraderClient> createTraders()
            throws InstantiationException, IllegalAccessException {

        Game.logger.info("\n");
        Game.logger.info("creating traders ...");
        Game.logger.info("\n");

        final ParameterDatabase parameters = Galaxy.getInstance().getTyped(Game.P_CAT, ParameterDatabase.class);
        final Parameter base = new Parameter(Game.P_CAT);

        final Parameter typeParam = base.push(Game.P_AGENT);

        final int numTypes = parameters.getInt(typeParam.push(Game.P_NUM), null, 1);

        final Collection<TraderClient> traderColl = new ArrayList<TraderClient>();

        for (int t = 0; t < numTypes; t++) {

            final Parameter typeParamT = typeParam.push("" + t);

            final Class<? extends TraderClient> traderClass = parameters.getClassForParameter(typeParamT, null,
                    TraderClient.class);

            final int numAgents = parameters.getInt(typeParamT.push(Game.P_NUM), null, 0);

            Game.logger.info("agent population " + t + "\n\t" + numAgents + " agent(s) of type "
                    + parameters.getString(typeParamT, null));

            final TraderClient traders[] = new TraderClient[numAgents];
            for (int i = 0; i < numAgents; i++) {
                traders[i] = traderClass.newInstance();
                traders[i].setup(parameters, typeParamT);
                if (i == 0) {
                    if (traders[i].getId() != null) {
                        Game.logger.info(
                                Utils.indent(Utils.indent("* " + GameClient.P_ID + ": " + traders[i].getId())));
                    }
                    Game.logger.info(Utils.indent(Utils.indent(traders[i].getAgent().toString())));
                }

                if (traders[i].getId() == null) {
                    traders[i].setId(traders[i].getType());
                }

                if (numAgents > 1) {
                    traders[i].setId(Game.identityOffice.createIdentity(traders[i].getId()));
                }

                traderColl.add(traders[i]);
            }
        }

        Game.logger.info("\n");

        return traderColl;
    }

    public static void startTraders(final Collection<? extends TraderClient> traderColl) {
        if (traderColl == null) {
            return;
        }

        Game.logger.info("\n");
        Game.logger.info("starting traders ...");
        Game.logger.info("\n");

        final TraderClient traders[] = traderColl.toArray(new TraderClient[0]);
        for (final TraderClient trader : traders) {
            if ((Galaxy.getInstance().getTyped(Game.P_CAT, CatpInfrastructure.class)).isSynchronous()) {
                Game.clientSynchronizer.countMe();
                trader.run();
                Game.clientSynchronizer.waitForClients();
            } else {
                /* start a separate thread only when communication is asynchronous */
                new Thread(trader).start();
            }
        }
    }

    /**
     * creates multiple market/specialist clients using the default parameter base
     * for specialists.
     * 
     * @throws InstantiationException
     *           if fails to instantiate market clients
     * @throws IllegalAccessException
     * 
     * @see #createMarkets(Parameter)
     */
    public static Collection<? extends MarketClient> createMarkets()
            throws InstantiationException, IllegalAccessException {
        final Parameter base = new Parameter(Game.P_CAT);
        final Parameter typeParam = base.push(Game.P_SPECIALIST);

        return Game.createMarkets(typeParam);
    }

    /**
     * creates multiple market/specialist clients, each an instance of
     * {@link MarketClient}, initializes them with parameter database in
     * {@link edu.cuny.util.ObjectRegistry} using the given parameter base.
     * 
     * @throws InstantiationException
     *           if fails to instantiate market clients
     * @throws IllegalAccessException
     */
    public static Collection<? extends MarketClient> createMarkets(final Parameter base)
            throws InstantiationException, IllegalAccessException {

        final ParameterDatabase parameters = Galaxy.getInstance().getTyped(Game.P_CAT, ParameterDatabase.class);

        return Game.createMarkets(parameters, base);
    }

    /**
     * creates multiple market/specialist clients, each an instance of
     * {@link MarketClient}, initializes them with the given parameter database
     * and using the given parameter base.
     * 
     * @param parameters
     * @param base
     * @throws InstantiationException
     * @throws IllegalAccessException
     */
    public static Collection<? extends MarketClient> createMarkets(final ParameterDatabase parameters,
            final Parameter base) throws InstantiationException, IllegalAccessException {

        Game.logger.info("creating markets defined at " + base.toString() + " ...");
        Game.logger.info("\n");

        final int numTypes = parameters.getInt(base.push(Game.P_NUM), null, 1);

        final Collection<MarketClient> marketColl = new ArrayList<MarketClient>();

        for (int t = 0; t < numTypes; t++) {

            final Parameter typeParamT = base.push("" + t);

            final Class<? extends MarketClient> marketClass = parameters.getClassForParameter(typeParamT, null,
                    MarketClient.class);

            final int numMarkets = parameters.getInt(typeParamT.push(Game.P_NUM), null, 0);

            Game.logger.info("specialist population " + t + "\n\t" + numMarkets + " specialist(s) of type "
                    + parameters.getString(typeParamT, null));

            final MarketClient markets[] = new MarketClient[numMarkets];
            for (int i = 0; i < numMarkets; i++) {
                markets[i] = marketClass.newInstance();
                markets[i].setup(parameters, typeParamT);
                if (i == 0) {
                    if (markets[i].getId() != null) {
                        Game.logger.info(
                                Utils.indent(Utils.indent("* " + GameClient.P_ID + ": " + markets[i].getId())));
                    }
                    Game.logger.info(Utils.indent(Utils.indent(markets[i].getAuctioneer().toString())));
                }

                if (markets[i].getId() == null) {
                    markets[i].setId(markets[i].getType());
                }

                if (numMarkets > 1) {
                    markets[i].setId(Game.identityOffice.createIdentity(markets[i].getId()));
                }

                marketColl.add(markets[i]);
            }
        }

        Game.logger.info("\n");

        return marketColl;
    }

    /**
     * creates multiple market/specialist clients, each an instance of
     * {@link MarketClient}, based on parameter files in the specified directory
     * and its subdirectories. Each of these parameter files define a set of
     * market clients using the specified parameter base.
     * 
     * For example
     * 
     * <pre>
     * cat.specialist.optional.dir = params/elites
     * cat.specialist.optional.base = elites
     * </pre>
     * 
     * specifies to look for parameter files in the directory
     * <code>params/elites</code> and all market clients are configured using the
     * parameter base <code>elites</code>.
     * 
     * @throws InstantiationException
     * @throws IllegalAccessException
     */
    @SuppressWarnings("unchecked")
    public static Collection<? extends MarketClient> createOptionalMarkets()
            throws InstantiationException, IllegalAccessException {
        final Parameter optionalParam = new Parameter(Game.P_CAT).push(Game.P_SPECIALIST).push(Game.P_OPTIONAL);

        final ParameterDatabase parameters = Galaxy.getInstance().getTyped(Game.P_CAT, ParameterDatabase.class);

        if (!parameters.exists(optionalParam.push(Game.P_DIR))
                && !parameters.exists(optionalParam.push(Game.P_BASE))) {
            // no optional markets defined
            Game.logger.info("no optional market defined at " + optionalParam);
            return null;
        }

        final File dir = parameters.getFile(optionalParam.push(Game.P_DIR), null);
        if ((dir == null) || !dir.exists() || !dir.isDirectory()) {
            Game.logger.fatal("Directory for optional markets does NOT exist !");
            Game.logger.fatal(dir.toString());
            Utils.fatalError();
            return null;
        } else {
            final String optionalBase = parameters.getString(optionalParam.push(Game.P_BASE), null);
            Game.logger.info("\n");
            if (optionalBase == null) {
                Game.logger.info("No optional markets configured.");
                Game.logger.info("\n");
                return null;
            } else {
                Game.logger.info("creating optional markets defined in " + dir + " at " + optionalBase + " ...");

                final Collection<MarketClient> marketColl = new ArrayList<MarketClient>();

                final Collection<File> files = FileUtils.listFiles(dir, new SuffixFileFilter(".params"),
                        DirectoryFileFilter.INSTANCE);

                final ParameterDatabase root = new ParameterDatabase();
                ParameterDatabase parametersPerFile = null;
                Collection<? extends MarketClient> marketCollPerFile = null;
                final Parameter base = new Parameter(optionalBase);
                for (final File file : files) {
                    try {
                        Game.logger.info("reading " + file.toString());
                        parametersPerFile = new ParameterDatabase(file);
                    } catch (final FileNotFoundException e) {
                        e.printStackTrace();
                        continue;
                    } catch (final IOException e) {
                        e.printStackTrace();
                        continue;
                    }
                    root.addParent(parameters);
                    root.addParent(parametersPerFile);

                    marketCollPerFile = Game.createMarkets(root, base);
                    marketColl.addAll(marketCollPerFile);
                    root.removeParents();
                }

                return marketColl;
            }
        }
    }

    public static void startMarkets(final Collection<? extends MarketClient> marketColl) {
        if (marketColl == null) {
            return;
        }

        Game.logger.info("\n");
        Game.logger.info("starting markets ...");
        Game.logger.info("\n");

        final MarketClient markets[] = marketColl.toArray(new MarketClient[0]);
        for (final MarketClient market : markets) {
            if (Galaxy.getInstance().getTyped(Game.P_CAT, CatpInfrastructure.class).isSynchronous()) {
                Game.clientSynchronizer.countMe();
                market.run();
                Game.clientSynchronizer.waitForClients();
            } else {
                /* start a separate thread only when communication is asynchronous */
                new Thread(market).start();
            }
        }
    }

    public static void cleanUpAfterInitialization() {
        Game.identityOffice = null;
        Game.clientSynchronizer.dispose();
        Game.clientSynchronizer = null;
    }

    public static void makeSureUnsynchronousInfrastructure() throws CatpServerUnavailableException {
        final CatpInfrastructure infra = Galaxy.getInstance().getTyped(Game.P_CAT, CatpInfrastructure.class);
        if ((infra == null) || infra.isSynchronous()) {
            final String err = Game.P_CAT + "." + Game.P_INFRASTRUCTURE
                    + " is not configured or should be configured to use "
                    + SocketBasedInfrastructureImpl.class.getName()
                    + " since you are trying to run clients separately from the game server !";
            throw new CatpServerUnavailableException(err);
        }
    }

    public static String getBanner() {
        return Game.BANNER;
    }

    public static String getVersion() {
        return Game.majorVersion + "." + Game.minorVersion;
    }

    public static String getName() {
        return Game.NAME;
    }
}