/*
Copyright (C) 2008,2009 Martin Günther <mintar@gmx.de>
This file is part of GgpRatingSystem.
GgpRatingSystem 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 3 of the License, or
(at your option) any later version.
GgpRatingSystem 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.
You should have received a copy of the GNU General Public License
along with GgpRatingSystem. If not, see <http://www.gnu.org/licenses/>.
*/
package ggpratingsystem;
import ggpratingsystem.ratingsystems.ConstantLinearRegressionStrategy;
import ggpratingsystem.ratingsystems.DirectScoresStrategy;
import ggpratingsystem.ratingsystems.DynamicLinearRegressionStrategy;
import ggpratingsystem.ratingsystems.RatingException;
import ggpratingsystem.ratingsystems.RatingSystemType;
import java.io.IOException;
import java.util.Set;
import java.util.logging.Level;
import com.martiansoftware.jsap.CommandLineTokenizer;
import com.martiansoftware.jsap.FlaggedOption;
import com.martiansoftware.jsap.JSAPException;
import com.martiansoftware.jsap.JSAPResult;
import com.martiansoftware.jsap.Parameter;
import com.martiansoftware.jsap.SimpleJSAP;
import com.martiansoftware.jsap.Switch;
import com.martiansoftware.jsap.UnspecifiedParameterException;
import com.martiansoftware.jsap.stringparsers.EnumeratedStringParser;
import com.martiansoftware.jsap.stringparsers.FileStringParser;
public class CommandLineInterface extends SimpleJSAP {
public static final String APPLICATION_CALL = "ggp_rating_system.sh";
public static final String OPTION_INPUT_DIR = "input-dir";
public static final String OPTION_OUTPUT_DIR = "output-dir";
public static final String OPTION_PREVIOUS_RATINGS = "previous";
public static final String OPTION_DYNAMIC_LINEAR_REGRESSION = "dynamic-linear-regression-rating";
public static final String OPTION_CONSTANT_LINEAR_REGRESSION = "constant-linear-regression-rating";
public static final String OPTION_DIRECT_SCORES = "direct-scores-rating";
public static final String OPTION_CSV_OUTPUT = "csv-output";
public static final String OPTION_GNUPLOT_OUTPUT = "gnuplot-output";
public static final String OPTION_HTML_OUTPUT = "html-output";
public static final String OPTION_DEBUG_LEVEL = "debug-level";
public static final String OPTION_HELP = "help";
private boolean messagePrinted = false;
public CommandLineInterface() throws JSAPException {
super(
APPLICATION_CALL,
"Reads a list of General Game Playing XML match files from the given "
+ "input directory, executes the selected rating algorithms on "
+ "the matches and writes the resulting player ratings to files "
+ "in the output directory.",
new Parameter[] {
/* Input directory */
// --input-dir, -i
new FlaggedOption(
OPTION_INPUT_DIR,
FileStringParser.getParser().setMustBeDirectory(true).setMustExist(true),
NO_DEFAULT,
REQUIRED,
'i',
OPTION_INPUT_DIR,
"The directory to read matches from. Must contain a file called "
+ "match_index.csv as well as the match XML files."),
// Checking readability of match_index.csv and XML files is done by
// MatchReader, which is fine (they have to be readable by the
// time MatchReader needs them; checking them now would not help
// much).
/* Output directory */
// --output-dir, -o
new FlaggedOption(
OPTION_OUTPUT_DIR,
FileStringParser.getParser().setMustBeDirectory(true).setMustExist(false),
NO_DEFAULT,
REQUIRED,
'o',
OPTION_OUTPUT_DIR,
"The directory to write output files to."),
/* Previous rating file */
// --previous, -p
new FlaggedOption(
OPTION_PREVIOUS_RATINGS,
FileStringParser.getParser().setMustBeFile(true).setMustExist(true),
NO_DEFAULT,
NOT_REQUIRED,
'p',
OPTION_PREVIOUS_RATINGS,
"The CSV output file of the previous competition, if the previous ratings are to be used to initialize the new ratings."),
/* Rating algorithm selection */
// --dynamic-linear-regression-rating, -d
new FlaggedOption(
OPTION_DYNAMIC_LINEAR_REGRESSION,
INTEGER_PARSER,
NO_DEFAULT,
NOT_REQUIRED,
'd',
OPTION_DYNAMIC_LINEAR_REGRESSION,
"Enables the linear regression rating algorithm, using a dynamic learning rate. " +
"Expects an integer value that is bigger or equal to the maximum number of " +
"MatchSets that will be read."),
// --constant-linear-regression-rating, -c
new FlaggedOption(
OPTION_CONSTANT_LINEAR_REGRESSION,
DOUBLE_PARSER,
NO_DEFAULT,
NOT_REQUIRED,
'c',
OPTION_CONSTANT_LINEAR_REGRESSION,
"Enables the linear regression rating algorithm, using a constant learning rate. " +
"Expects a double value specifying the learning rate to be used (e.g. 1.0)."),
// --direct-scores-rating, -s
new Switch(
OPTION_DIRECT_SCORES,
's',
OPTION_DIRECT_SCORES,
"Enables the direct scores rating algorithm. This is not really a rating algorithm, " +
"but rather sums up the scores received by the players."),
/* ****************** ADD NEW RATING SYSTEMS HERE ****************** */
/* Output selection */
// --csv-output, -v
new Switch(
OPTION_CSV_OUTPUT,
'v',
OPTION_CSV_OUTPUT,
"Enables CSV (comma separated values) output for all rating algorithms."),
// --gnuplot-output, -g
new Switch(
OPTION_GNUPLOT_OUTPUT,
'g',
OPTION_GNUPLOT_OUTPUT,
"Enables gnuplot (data file) output for all rating algorithms."),
// --html-output, -t
new Switch(
OPTION_HTML_OUTPUT,
't',
OPTION_HTML_OUTPUT,
"Enables HTML output for all rating algorithms."),
/* ****************** ADD NEW OUTPUT METHODS HERE ****************** */
/* Debug level */
// --debug-level, -l
new FlaggedOption(
OPTION_DEBUG_LEVEL,
EnumeratedStringParser.getParser(
"OFF; SEVERE; WARNING; INFO; CONFIG; FINE; FINER; FINEST; ALL",
false, false),
"INFO",
NOT_REQUIRED,
'l',
OPTION_DEBUG_LEVEL,
"Sets the level of debug output. One of the following (in order of "
+ "increasing verbosity): "
+ "OFF; SEVERE; WARNING; INFO; CONFIG; FINE; FINER; FINEST; ALL. "
+ "If the debug level is at FINE or higher, detailed statistical "
+ "information about the linear regression will be written to the "
+ "output directory.")
/* Help */
// --help option is added by SimpleJSAP
}
);
}
@Override
public boolean messagePrinted() {
return messagePrinted;
}
@Override
public JSAPResult parse(String cmdLine) {
String[] args = CommandLineTokenizer.tokenize(cmdLine);
return (parse(args));
}
@Override
public JSAPResult parse(String[] args) {
JSAPResult config;
config = super.parse(args);
try {
/* parameter checking */
this.messagePrinted = super.messagePrinted();
boolean existsEnabledRatingAlgorithm =
config.contains(OPTION_DYNAMIC_LINEAR_REGRESSION)
|| config.contains(OPTION_CONSTANT_LINEAR_REGRESSION)
|| config.getBoolean(OPTION_DIRECT_SCORES);
/* ****************** ADD NEW RATING SYSTEMS HERE ****************** */
if (!existsEnabledRatingAlgorithm) {
messagePrinted = true;
if (!config.getBoolean(OPTION_HELP)) {
System.err.println("Error: At least one rating algorithm must be enabled.");
}
}
boolean existsEnabledOutputAlgorithm =
config.getBoolean(OPTION_CSV_OUTPUT)
|| config.getBoolean(OPTION_GNUPLOT_OUTPUT)
|| config.getBoolean(OPTION_HTML_OUTPUT);
/* ****************** ADD NEW OUTPUT METHODS HERE ****************** */
if (!existsEnabledOutputAlgorithm) {
messagePrinted = true;
if (!config.getBoolean(OPTION_HELP)) {
System.err.println("Error: At least one output algorithm must be enabled.");
}
}
if (messagePrinted) {
// if user hasn't asked for help, "beat him with a clue stick", as the JSAP manual says
if (!config.getBoolean(OPTION_HELP)) {
System.err.println();
System.err.println("Type " + APPLICATION_CALL + " --" + OPTION_HELP + " for help.");
}
config.addException(null, new JSAPException("Help message printed."));
}
} catch (UnspecifiedParameterException e) {
if (!config.getBoolean(OPTION_HELP)) {
System.err.println();
System.err.println("Type " + APPLICATION_CALL + " --" + OPTION_HELP + " for help.");
}
throw e;
}
return config;
}
public static void main(String[] args) throws JSAPException, IOException, RatingException {
CommandLineInterface commandLineInterface = new CommandLineInterface();
JSAPResult jsap = commandLineInterface.parse(args);
if (!jsap.success()) {
throw new JSAPException("Command line parsing failed.");
}
//////////////////////////////////////////////////////
// now configure everything //
//////////////////////////////////////////////////////
Configuration configuration = new Configuration();
/* configure debug level */
String debugLevel = jsap.getString(OPTION_DEBUG_LEVEL).toUpperCase();
Level level = Level.parse(debugLevel);
configuration.setDebugLevel(level);
/* configure input dir */
MatchSetReader matchSetReader = new FileMatchSetReader(jsap.getFile(OPTION_INPUT_DIR), configuration);
configuration.setMatchReader(matchSetReader);
/* configure output dir */
Configuration.setOutputDir(jsap.getFile(OPTION_OUTPUT_DIR));
/* configure previous ratings file */
configuration.setPreviousRatings(jsap.getFile(OPTION_PREVIOUS_RATINGS));
/* configure rating algorithms */
if (jsap.contains(OPTION_DYNAMIC_LINEAR_REGRESSION)) {
int maxMatchSets = jsap.getInt(OPTION_DYNAMIC_LINEAR_REGRESSION);
configuration.addRatingSystem(new DynamicLinearRegressionStrategy(maxMatchSets));
}
if (jsap.contains(OPTION_CONSTANT_LINEAR_REGRESSION)) {
double learningRate = jsap.getDouble(OPTION_CONSTANT_LINEAR_REGRESSION);
configuration.addRatingSystem(new ConstantLinearRegressionStrategy(learningRate));
}
if (jsap.getBoolean(OPTION_DIRECT_SCORES)) {
configuration.addRatingSystem(new DirectScoresStrategy());
}
/* ****************** ADD NEW RATING SYSTEMS HERE ****************** */
/* make ignore list */
Set<Player> ignorePlayers = new IgnorePlayerSet(jsap.getFile(OPTION_INPUT_DIR), configuration.getPlayerSet());
/* configure output methods */
for (RatingSystemType type : configuration.getEnabledRatingSystems()) {
if (jsap.getBoolean(OPTION_CSV_OUTPUT)) {
configuration.addCSVOutputBuilder(type, ignorePlayers);
}
if (jsap.getBoolean(OPTION_GNUPLOT_OUTPUT)) {
configuration.addGnuplotOutputBuilder(type, ignorePlayers);
}
if (jsap.getBoolean(OPTION_HTML_OUTPUT)) {
configuration.addHtmlOutputBuilder(type, ignorePlayers);
}
/* ****************** ADD NEW OUTPUT METHODS HERE ****************** */
}
configuration.run();
}
}