Skip to content

Commit

Permalink
Full java-doc and misc. stuff
Browse files Browse the repository at this point in the history
+[MOD] Full java-doc
+[MOD] Out-sourced error messages to ErrorMessages.java
-[MOD] Used constants where it is sensible
-[MOV] Renamed Program.java to SolveGame.java
  • Loading branch information
Zabuzard committed Jun 3, 2016
1 parent 441196d commit 177d304
Show file tree
Hide file tree
Showing 17 changed files with 759 additions and 105 deletions.
94 changes: 83 additions & 11 deletions src/de/tischner/nashfinder/NashFinder.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import de.tischner.nashfinder.game.util.ActionProfile;
import de.tischner.nashfinder.game.util.PlayerAction;
import de.tischner.nashfinder.game.util.SupportSet;
import de.tischner.nashfinder.locale.ErrorMessages;
import de.tischner.nashfinder.nash.NashEquilibrium;
import de.tischner.nashfinder.util.EExpectedUtilty;
import de.tischner.nashfinder.util.SetUtil;
Expand All @@ -26,21 +27,64 @@
import net.sf.javailp.SolverFactoryLpSolve;

/**
* Class that finds nash equilibria in games.
*
* @author Daniel Tischner
*
*/
public final class NashFinder {

/**
* The timeout for LCP solving in milliseconds.
*/
private static final int TIMEOUT_MILLIS = 1000;
/**
* If solving the LCP should output detailed information or not.
*/
private static final int VERBOSE_VALUE = 0;

/**
* Game to solve.
*/
private final StrategicGame<String, String> mGame;
/**
* Results, after {@link #computeNashEquilibria()} was called.
*/
private final Map<List<SupportSet<String, String>>, NashEquilibrium<String, String>> mResults;
/**
* Specific support sets to solve the game for.
*/
private final List<SupportSet<String, String>> mSpecificSupportSets;
/**
* If the game should be solved for specific given support sets.
*/
private final boolean mUseSpecificSupportSets;

public NashFinder(final String gameName) {
this(gameName, null);
/**
* Creates a new NashFinder that is able to solve the given game. After
* creation, use {@link #computeNashEquilibria()} and then get the results,
* for example by {@link #printResults()}.
*
* @param gamePath
* Path to the game file in the json-format
*/
public NashFinder(final String gamePath) {
this(gamePath, null);
}

/**
* Creates a new NashFinder that is able to solve the given game for given
* support sets. After creation, use {@link #computeNashEquilibria()} and
* then get the results, for example by {@link #printResults()}.
*
* @param gameFileName
* Path to the game file in the json-format
* @param specificSupportSets
* Specific support sets to solve the game for. The format is
* <tt>[H,T][T]</tt>, where every player has the given actions.
* For example, player 1 has actions <tt>H</tt> and <tt>T</tt>,
* whereas player 2 only has action <tt>T</tt>.
*/
public NashFinder(final String gameFileName, final String specificSupportSets) {
mUseSpecificSupportSets = specificSupportSets != null && specificSupportSets.length() != 0;
mGame = StrategicGameParser.parseStrategicGameJson(gameFileName);
Expand All @@ -54,6 +98,10 @@ public NashFinder(final String gameFileName, final String specificSupportSets) {
mResults = new LinkedHashMap<>();
}

/**
* Computes nash equilibria of the given game. Results can be get, for
* example, with {@link #printResults()}.
*/
public void computeNashEquilibria() {
List<List<SupportSet<String, String>>> supportSetsToProcess = buildSupportSets();
for (List<SupportSet<String, String>> supportSets : supportSetsToProcess) {
Expand All @@ -62,8 +110,8 @@ public void computeNashEquilibria() {
SupportSet<String, String> secondPlayerSet = supportSets.get(1);

SolverFactory factory = new SolverFactoryLpSolve();
factory.setParameter(Solver.VERBOSE, 0);
factory.setParameter(Solver.TIMEOUT, 1000);
factory.setParameter(Solver.VERBOSE, VERBOSE_VALUE);
factory.setParameter(Solver.TIMEOUT, TIMEOUT_MILLIS);
Problem problem = new Problem();
Linear linear = new Linear();
linear.add(1, EExpectedUtilty.FIRST_PLAYER);
Expand All @@ -84,6 +132,10 @@ public void computeNashEquilibria() {
}
}

/**
* Prints the results of the game to the console. Results are obtained by
* using {@link #computeNashEquilibria()} prior to this method.
*/
public void printResults() {
System.out.println(this);
}
Expand Down Expand Up @@ -112,6 +164,19 @@ public String toString() {
return result.toString();
}

/**
* Adds the constraints for the given player constellation to the given LCP,
* for finding nash equilibria.
*
* @param problem
* Problem to add constraints to
* @param protagonistSet
* Support set of the protagonist player
* @param antagonistSet
* Support set of the antagonist player
* @param protagonistExpectedUtiltyVar
* Key to save the expected utility of the protagonist with
*/
private void addConstraintsForConstellation(final Problem problem, final SupportSet<String, String> protagonistSet,
final SupportSet<String, String> antagonistSet, final EExpectedUtilty protagonistExpectedUtiltyVar) {
String protagonist = protagonistSet.getPlayer();
Expand Down Expand Up @@ -152,6 +217,13 @@ private void addConstraintsForConstellation(final Problem problem, final Support
}
}

/**
* Builds and gets the support set constellations to use for solving the
* current game for.
*
* @return List of support set constellations for solving the current game
* for
*/
private List<List<SupportSet<String, String>>> buildSupportSets() {
List<List<SupportSet<String, String>>> supportSets = new LinkedList<>();

Expand All @@ -161,13 +233,13 @@ private List<List<SupportSet<String, String>>> buildSupportSets() {
for (SupportSet<String, String> supportSet : mSpecificSupportSets) {
String player = supportSet.getPlayer();
if (!mGame.hasPlayer(player)) {
throw new IllegalArgumentException("The given support set is not valid, it may be corrupt.");
throw new IllegalStateException(ErrorMessages.SUPPORT_SET_INVALID);
}
Iterator<String> actionIter = supportSet.getActions();
while (actionIter.hasNext()) {
String action = actionIter.next();
if (!mGame.hasPlayerAction(player, action)) {
throw new IllegalArgumentException("The given support set is not valid, it may be corrupt.");
throw new IllegalStateException(ErrorMessages.SUPPORT_SET_INVALID);
}
}
}
Expand All @@ -193,13 +265,13 @@ private List<List<SupportSet<String, String>>> buildSupportSets() {
}
if (firstPlayerActions == null || secondPlayerActions == null || firstPlayer == null
|| secondPlayer == null) {
throw new IllegalArgumentException("Could not built support sets. The given game may be corrupt.");
throw new IllegalArgumentException(ErrorMessages.BUILD_SUPPORT_SETS_GAME_INVALID);
}

Set<Set<String>> productOfFirstPlayer = SetUtil.powerSet(firstPlayerActions);
Set<Set<String>> productOfSecondPlayer = SetUtil.powerSet(secondPlayerActions);
for (Set<String> firstPlayerSet : productOfFirstPlayer) {
for (Set<String> secondPlayerSet : productOfSecondPlayer) {
Set<Set<String>> powerOfFirstPlayer = SetUtil.powerSet(firstPlayerActions);
Set<Set<String>> powerOfSecondPlayer = SetUtil.powerSet(secondPlayerActions);
for (Set<String> firstPlayerSet : powerOfFirstPlayer) {
for (Set<String> secondPlayerSet : powerOfSecondPlayer) {
List<SupportSet<String, String>> supportSetConstellation = new LinkedList<>();
supportSetConstellation.add(new SupportSet<String, String>(firstPlayer, firstPlayerSet));
supportSetConstellation.add(new SupportSet<String, String>(secondPlayer, secondPlayerSet));
Expand Down
48 changes: 0 additions & 48 deletions src/de/tischner/nashfinder/Program.java

This file was deleted.

76 changes: 76 additions & 0 deletions src/de/tischner/nashfinder/SolveGame.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package de.tischner.nashfinder;

import de.tischner.nashfinder.locale.ErrorMessages;

/**
* Command line program that solves strategic games by solving <i>linear
* complementary problems</i> (LCP).
*
* @author Daniel Tischner
*
*/
public final class SolveGame {

/**
* Argument index of the game file to solve.
*/
private static final int GAME_FILE_ARG_INDEX = 0;
/**
* Maximal length of arguments that are accepted.
*/
private static final int MAX_ARG_LENGTH = 2;
/**
* Length of arguments that are required.
*/
private static final int REQUIRED_ARG_LENGTH = 1;
/**
* Argument index of the specific support sets to solve the game for.
*/
private static final int SUPPORT_SETS_ARG_INDEX = 1;

/**
* Starts the command line program for solving strategic games.
*
* @param args
* The first argument is the game to solve, as path to a
* json-file. The second argument is optional and specifies a
* specific support sets to solve the game. If not given, the
* game is solved for all possible support set combinations.<br/>
* <br/>
* An example call would be:
* <tt>java SolveGame matching-pennies.json
* "[H,T][T]"</tt>
*/
public static void main(final String[] args) {
// The first argument is not optional and specifies the game file to use
// for computation
if (args == null || args.length < REQUIRED_ARG_LENGTH) {
throw new IllegalArgumentException(ErrorMessages.SOLVE_GAME_WRONG_ARGUMENT_NUMBER);
}
// The second argument is optional and may specify a specific support
// sets to use for computation
boolean useSpecificSupportSets = args.length >= MAX_ARG_LENGTH;
String specificSupportSets = null;
if (useSpecificSupportSets) {
specificSupportSets = args[SUPPORT_SETS_ARG_INDEX];
}
String gameFileName = args[GAME_FILE_ARG_INDEX];

NashFinder nashFinder = null;
if (useSpecificSupportSets) {
nashFinder = new NashFinder(gameFileName, specificSupportSets);
} else {
nashFinder = new NashFinder(gameFileName);
}

nashFinder.computeNashEquilibria();
nashFinder.printResults();
}

/**
* Utility class. No implementation.
*/
private SolveGame() {

}
}
Loading

0 comments on commit 177d304

Please sign in to comment.