From d1dcf4e5f3d6c0ea5c25719c07531c25fbeec256 Mon Sep 17 00:00:00 2001 From: alandau Date: Sun, 3 Nov 2013 22:27:06 -0800 Subject: [PATCH 1/2] Add more Javadocs, especially in the StateMachine. --- .../gamer/statemachine/StateMachineGamer.java | 70 ++++--- src/org/ggp/base/util/statemachine/Move.java | 9 +- src/org/ggp/base/util/statemachine/Role.java | 14 +- .../base/util/statemachine/StateMachine.java | 186 +++++++++++++++--- 4 files changed, 211 insertions(+), 68 deletions(-) diff --git a/src/org/ggp/base/player/gamer/statemachine/StateMachineGamer.java b/src/org/ggp/base/player/gamer/statemachine/StateMachineGamer.java index dc48179ff..32bea8e10 100644 --- a/src/org/ggp/base/player/gamer/statemachine/StateMachineGamer.java +++ b/src/org/ggp/base/player/gamer/statemachine/StateMachineGamer.java @@ -24,9 +24,9 @@ * Almost every player should subclass this class, since it provides the common * methods for interpreting the match history as transitions in a state machine, * and for keeping an up-to-date view of the current state of the game. - * + * * See @SimpleSearchLightGamer, @HumanGamer, and @RandomGamer for examples. - * + * * @author evancox * @author Sam */ @@ -41,17 +41,17 @@ public abstract class StateMachineGamer extends Gamer * Defines which state machine this gamer will use. * @return */ - public abstract StateMachine getInitialStateMachine(); - + public abstract StateMachine getInitialStateMachine(); + /** * Defines the metagaming action taken by a player during the START_CLOCK * @param timeout time in milliseconds since the era when this function must return * @throws TransitionDefinitionException * @throws MoveDefinitionException * @throws GoalDefinitionException - */ + */ public abstract void stateMachineMetaGame(long timeout) throws TransitionDefinitionException, MoveDefinitionException, GoalDefinitionException; - + /** * Defines the algorithm that the player uses to select their move. * @param timeout time in milliseconds since the era when this function must return @@ -66,45 +66,41 @@ public abstract class StateMachineGamer extends Gamer * Defines any actions that the player takes upon the game cleanly ending. */ public abstract void stateMachineStop(); - + /** * Defines any actions that the player takes upon the game abruptly ending. */ public abstract void stateMachineAbort(); - + // ===================================================================== // Next, methods which can be used by subclasses to get information about // the current state of the game, and tweak the state machine on the fly. - + /** - * Returns the current state - * @return the current state + * Returns the current state of the game. */ public final MachineState getCurrentState() { return currentState; } - + /** - * Returns the current role - * @return the current role + * Returns the role that this gamer is playing as in the game. */ public final Role getRole() { return role; } - + /** - * Returns the state machine. This is used for calculating the next state and other operations such as computing - * the legal moves for all players - * - * @return a state machine + * Returns the state machine. This is used for calculating the next state and other operations, such as computing + * the legal moves for all players, whether states are terminal, and the goal values of terminal states. */ public final StateMachine getStateMachine() { return stateMachine; } - + /** * Cleans up the role, currentState and stateMachine. This should only be * used when a match is over, and even then only when you really need to @@ -113,24 +109,24 @@ public final StateMachine getStateMachine() */ protected final void cleanupAfterMatch() { role = null; - currentState = null; + currentState = null; stateMachine = null; setMatch(null); setRoleName(null); } - + /** * Switches stateMachine to newStateMachine, playing through the match * history to the current state so that currentState is expressed using * a MachineState generated by the new state machine. - * + * * This is not done in a thread-safe fashion with respect to the rest of * the gamer, so be careful when using this method. - * + * * @param newStateMachine the new state machine */ protected final void switchStateMachine(StateMachine newStateMachine) { - try { + try { MachineState newCurrentState = newStateMachine.getInitialState(); Role newRole = newStateMachine.getRoleFromConstant(getRoleName()); @@ -139,19 +135,19 @@ protected final void switchStateMachine(StateMachine newStateMachine) { for(List nextMove : theMoveHistory) { List theJointMove = new ArrayList(); for(GdlTerm theSentence : nextMove) - theJointMove.add(newStateMachine.getMoveFromTerm(theSentence)); + theJointMove.add(newStateMachine.getMoveFromTerm(theSentence)); newCurrentState = newStateMachine.getNextStateDestructively(newCurrentState, theJointMove); } - + // Finally, switch over if everything went well. role = newRole; - currentState = newCurrentState; + currentState = newCurrentState; stateMachine = newStateMachine; } catch (Exception e) { GamerLogger.log("GamePlayer", "Caught an exception while switching state machine!"); GamerLogger.logStackTrace("GamePlayer", e); } - } + } // ===================================================================== // Finally, methods which are overridden with proper state-machine-based @@ -159,12 +155,12 @@ protected final void switchStateMachine(StateMachine newStateMachine) { // around the ordinary metaGame() and selectMove() functions, calling the // new stateMachineMetaGame() and stateMachineSelectMove() functions after // doing the state-machine-related book-keeping. - + /** * A wrapper function for stateMachineMetaGame. When the match begins, this * initializes the state machine and role using the match description, and * then calls stateMachineMetaGame. - */ + */ @Override public final void metaGame(long timeout) throws MetaGamingException { @@ -184,7 +180,7 @@ public final void metaGame(long timeout) throws MetaGamingException throw new MetaGamingException(e); } } - + /** * A wrapper function for stateMachineSelectMove. When we are asked to * select a move, this advances the state machine up to the current state @@ -219,7 +215,7 @@ public final GdlTerm selectMove(long timeout) throws MoveSelectionException throw new MoveSelectionException(e); } } - + @Override public void stop() throws StoppingException { try { @@ -247,7 +243,7 @@ public void stop() throws StoppingException { throw new StoppingException(e); } } - + @Override public void abort() throws AbortingException { try { @@ -258,10 +254,10 @@ public void abort() throws AbortingException { GamerLogger.logStackTrace("GamePlayer", e); throw new AbortingException(e); } - } - + } + // Internal state about the current state of the state machine. private Role role; private MachineState currentState; - private StateMachine stateMachine; + private StateMachine stateMachine; } \ No newline at end of file diff --git a/src/org/ggp/base/util/statemachine/Move.java b/src/org/ggp/base/util/statemachine/Move.java index b8f97e982..ff393e6c9 100644 --- a/src/org/ggp/base/util/statemachine/Move.java +++ b/src/org/ggp/base/util/statemachine/Move.java @@ -4,7 +4,14 @@ import org.ggp.base.util.gdl.grammar.GdlTerm; - +/** + * A Move represents a possible move that can be made by a role. Each + * player makes exactly one move on every turn. This includes moves + * that represent passing, or taking no action. + *

+ * Note that Move objects are not intrinsically tied to a role. They + * only express the action itself. + */ @SuppressWarnings("serial") public class Move implements Serializable { diff --git a/src/org/ggp/base/util/statemachine/Role.java b/src/org/ggp/base/util/statemachine/Role.java index f132d6d0f..7df978f90 100644 --- a/src/org/ggp/base/util/statemachine/Role.java +++ b/src/org/ggp/base/util/statemachine/Role.java @@ -9,6 +9,12 @@ import org.ggp.base.util.gdl.grammar.GdlRelation; +/** + * A Role represents the name used for a player in a game description. + *

+ * The list of roles defined in a game description can be extracted + * using the {@link #computeRoles(List)} method. + */ @SuppressWarnings("serial") public class Role implements Serializable { @@ -47,10 +53,10 @@ public String toString() { return name.toString(); } - + /** * Compute all of the roles in a game, in the correct order. - * + *

* Order matters, because a joint move is defined as an ordered list * of moves, in which the order determines which player took which of * the moves. This function will give an ordered list in which the roles @@ -61,12 +67,12 @@ public static List computeRoles(List description) List roles = new ArrayList(); for (Gdl gdl : description) { if (gdl instanceof GdlRelation) { - GdlRelation relation = (GdlRelation) gdl; + GdlRelation relation = (GdlRelation) gdl; if (relation.getName().getValue().equals("role")) { roles.add(new Role((GdlConstant) relation.get(0))); } } } return roles; - } + } } \ No newline at end of file diff --git a/src/org/ggp/base/util/statemachine/StateMachine.java b/src/org/ggp/base/util/statemachine/StateMachine.java index 8f85be84a..277960a95 100644 --- a/src/org/ggp/base/util/statemachine/StateMachine.java +++ b/src/org/ggp/base/util/statemachine/StateMachine.java @@ -26,18 +26,62 @@ public abstract class StateMachine // Stubs for implementations // ============================================ // The following methods are required for a valid - // state machine implementation. + // state machine implementation. + /** + * Initializes the StateMachine to describe the given game rules. + *

+ * This method should only be called once, and it should be called before any + * other methods on the StateMachine. + */ public abstract void initialize(List description); - + /** + * Returns the goal value for the given role in the given state. Goal values + * are always between 0 and 100. + * + * @throws GoalDefinitionException if there is no goal value or more than one + * goal value for the given role in the given state. If this occurs when this + * is called on a terminal state, this indicates an error in either the game + * description or the StateMachine implementation. + */ public abstract int getGoal(MachineState state, Role role) throws GoalDefinitionException; + /** + * Returns true if and only if the given state is a terminal state (i.e. the + * game is over). + */ public abstract boolean isTerminal(MachineState state); + /** + * Returns a list of the roles in the game, in the same order as they + * were defined in the game description. + *

+ * The result will be the same as calling {@link Role#computeRoles(List)} + * on the game rules used to initialize this state machine. + */ public abstract List getRoles(); + /** + * Returns the initial state of the game. + */ public abstract MachineState getInitialState(); + /** + * Returns a list containing every move that is legal for the given role in the + * given state. + * + * @throws MoveDefinitionException if the role has no legal moves. This indicates + * an error in either the game description or the StateMachine implementation. + */ // TODO: There are philosophical reasons for this to return Set rather than List. public abstract List getLegalMoves(MachineState state, Role role) throws MoveDefinitionException; - + + /** + * Returns the next state of the game given the current state and a joint move + * list containing one move per role. + * + * @param moves A list containing one move per role. The moves should be + * listed in the same order as roles are listed by {@link #getRoles()}. + * @throws TransitionDefinitionException indicates an error in either the + * game description or the StateMachine implementation. + */ public abstract MachineState getNextState(MachineState state, List moves) throws TransitionDefinitionException; // The following methods are included in the abstract StateMachine base so @@ -64,35 +108,52 @@ public Move getMoveFromTerm(GdlTerm term) { // as needed by state machines. Clients should assume // the contracts for these methods hold, regardless // of the state machine implementation they pick. - - // Override this to perform some extra work (like trimming a cache) once per move. - // CONTRACT: Should be called once per move. + + /** Override this to perform some extra work (like trimming a cache) once per move. + *

+ * CONTRACT: Should be called once per move. + */ public void doPerMoveWork() {} - - // Override this to provide memory-saving destructive-next-state functionality. - // CONTRACT: After calling this method, "state" should not be accessed. + + /** Override this to provide memory-saving destructive-next-state functionality. + *

+ * CONTRACT: After calling this method, "state" should not be accessed. + */ public MachineState getNextStateDestructively(MachineState state, List moves) throws TransitionDefinitionException { return getNextState(state, moves); } - - // Override this to allow the state machine to be conditioned on a particular current state. - // This means that the state machine will only handle portions of the game tree at and below - // the given state; it no longer needs to properly handle earlier portions of the game tree. - // This constraint can be used to optimize certain state machine implementations. - // CONTRACT: After calling this method, the state machine never deals with a state that - // is not "theState" or one of its descendants in the game tree. + + /** Override this to allow the state machine to be conditioned on a particular current state. + * This means that the state machine will only handle portions of the game tree at and below + * the given state; it no longer needs to properly handle earlier portions of the game tree. + * This constraint can be used to optimize certain state machine implementations. + *

+ * CONTRACT: After calling this method, the state machine never deals with a state that + * is not "theState" or one of its descendants in the game tree. + */ public void updateRoot(MachineState theState) { ; } - + // ============================================ // Implementations of convenience methods // ============================================ - + public String getName() { return this.getClass().getSimpleName(); - } - + } + + /** + * Returns a list containing every joint move possible in the given state. + * A joint move consists of one move for each player + *

+ * The list of possible joint moves is the Cartesian product of the lists + * of legal moves available for each player. + *

+ * If only one player has more than one legal move, then the number of + * joint moves returned will equal the number of possible moves for that + * player. + */ public List> getLegalJointMoves(MachineState state) throws MoveDefinitionException { List> legals = new ArrayList>(); @@ -106,6 +167,11 @@ public List> getLegalJointMoves(MachineState state) throws MoveDefini return crossProduct; } + /** + * Returns a list of every joint move possible in the given state in which + * the given role makes the given move. This will be a subset of the list + * of joint moves given by {@link #getLegalJointMoves(MachineState)}. + */ public List> getLegalJointMoves(MachineState state, Role role, Move move) throws MoveDefinitionException { List> legals = new ArrayList>(); @@ -125,6 +191,12 @@ public List> getLegalJointMoves(MachineState state, Role role, Move m return crossProduct; } + /** + * Returns a list containing every possible next state of the game after + * the given state. The list will contain one entry for every possible + * joint move that could be played; as such, a single machine state could + * be included multiple times. + */ public List getNextStates(MachineState state) throws MoveDefinitionException, TransitionDefinitionException { List nextStates = new ArrayList(); @@ -135,6 +207,14 @@ public List getNextStates(MachineState state) throws MoveDefinitio return nextStates; } + /** + * Returns a map from each move that is legal for the given role in + * the given state to the list of possible resulting states if that + * move is chosen. + *

+ * If the given role is the only role with more than one legal move, + * then each list of states in the map will only contain one state. + */ public Map> getNextStates(MachineState state, Role role) throws MoveDefinitionException, TransitionDefinitionException { Map> nextStates = new HashMap>(); @@ -164,6 +244,12 @@ protected void crossProductLegalMoves(List> legals, List> } private Map roleIndices = null; + /** + * Returns a mapping from a role to the index of that role, as in + * the list returned by {@link #getRoles()}. This may be a faster + * way to check the index of a role than calling {@link List#indexOf(Object)} + * on that list. + */ public Map getRoleIndices() { if(roleIndices == null) { @@ -176,7 +262,17 @@ public Map getRoleIndices() return roleIndices; } - + + /** + * Returns the goal values for each role in the given state. The goal values + * are listed in the same order the roles are listed in the game rules, which + * is the same order in which they're returned by {@link #getRoles()}. + * + * @throws GoalDefinitionException if there is no goal value or more than one + * goal value for any one role in the given state. If this occurs when this + * is called on a terminal state, this indicates an error in either the game + * description or the StateMachine implementation. + */ public List getGoals(MachineState state) throws GoalDefinitionException { List theGoals = new ArrayList(); for (Role r : getRoles()) { @@ -185,6 +281,10 @@ public List getGoals(MachineState state) throws GoalDefinitionException return theGoals; } + /** + * Returns a random joint move from among all the possible joint moves in + * the given state. + */ public List getRandomJointMove(MachineState state) throws MoveDefinitionException { List random = new ArrayList(); @@ -195,6 +295,10 @@ public List getRandomJointMove(MachineState state) throws MoveDefinitionEx return random; } + /** + * Returns a random joint move from among all the possible joint moves in + * the given state in which the given role makes the given move. + */ public List getRandomJointMove(MachineState state, Role role, Move move) throws MoveDefinitionException { List random = new ArrayList(); @@ -209,25 +313,55 @@ public List getRandomJointMove(MachineState state, Role role, Move move) t return random; } + /** + * Returns a random move from among the possible legal moves for the + * given role in the given state. + */ public Move getRandomMove(MachineState state, Role role) throws MoveDefinitionException { List legals = getLegalMoves(state, role); return legals.get(new Random().nextInt(legals.size())); } + /** + * Returns a state chosen at random from the possible next states of the + * game. + *

+ * The distribution among states is based on the possible joint moves. + * This is not necessarily uniform among the possible states themselves, + * as multiple joint moves may result in the same state. + */ public MachineState getRandomNextState(MachineState state) throws MoveDefinitionException, TransitionDefinitionException { List random = getRandomJointMove(state); return getNextState(state, random); } + /** + * Returns a random next state of the game from the possible next states + * resulting from the given role playing the given move. + *

+ * The distribution among states is based on the possible joint moves. + * This is not necessarily uniform among the possible states themselves, + * as multiple joint moves may result in the same state. + *

+ * If the given role is the only role with more than one legal move, then + * there is only one possible next state for this method to return. + */ public MachineState getRandomNextState(MachineState state, Role role, Move move) throws MoveDefinitionException, TransitionDefinitionException { List random = getRandomJointMove(state, role, move); return getNextState(state, random); } - - public MachineState performDepthCharge(MachineState state, final int[] theDepth) throws TransitionDefinitionException, MoveDefinitionException { + + /** + * Returns a terminal state derived from repeatedly making random joint moves + * until reaching the end of the game. + * + * @param theDepth an integer array, the 0th element of which will be set to + * the number of state changes that were made to reach a terminal state. + */ + public MachineState performDepthCharge(MachineState state, final int[] theDepth) throws TransitionDefinitionException, MoveDefinitionException { int nDepth = 0; while(!isTerminal(state)) { nDepth++; @@ -237,8 +371,8 @@ public MachineState performDepthCharge(MachineState state, final int[] theDepth) theDepth[0] = nDepth; return state; } - - public void getAverageDiscountedScoresFromRepeatedDepthCharges(final MachineState state, final double[] avgScores, final double[] avgDepth, final double discountFactor, final int repetitions) throws TransitionDefinitionException, MoveDefinitionException, GoalDefinitionException { + + public void getAverageDiscountedScoresFromRepeatedDepthCharges(final MachineState state, final double[] avgScores, final double[] avgDepth, final double discountFactor, final int repetitions) throws TransitionDefinitionException, MoveDefinitionException, GoalDefinitionException { avgDepth[0] = 0; for (int j = 0; j < avgScores.length; j++) { avgScores[j] = 0; @@ -257,5 +391,5 @@ public void getAverageDiscountedScoresFromRepeatedDepthCharges(final MachineStat for (int j = 0; j < avgScores.length; j++) { avgScores[j] /= repetitions; } - } + } } \ No newline at end of file From 9b5aa79738acb6c76cbc4a6a1f75a808ea5d1e6f Mon Sep 17 00:00:00 2001 From: alandau Date: Mon, 4 Nov 2013 23:20:45 -0800 Subject: [PATCH 2/2] Fix up one Javadocs comment. --- src/org/ggp/base/util/statemachine/StateMachine.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/org/ggp/base/util/statemachine/StateMachine.java b/src/org/ggp/base/util/statemachine/StateMachine.java index 277960a95..e9aab5784 100644 --- a/src/org/ggp/base/util/statemachine/StateMachine.java +++ b/src/org/ggp/base/util/statemachine/StateMachine.java @@ -145,7 +145,8 @@ public String getName() { /** * Returns a list containing every joint move possible in the given state. - * A joint move consists of one move for each player + * A joint move consists of one move for each role, with the moves in the + * same ordering that their roles have in {@link #getRoles()}. *

* The list of possible joint moves is the Cartesian product of the lists * of legal moves available for each player.