org.ggp.base.player.gamer.statemachine.StateMachineGamer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of alloy-ggp-base Show documentation
Show all versions of alloy-ggp-base Show documentation
A modified version of the GGP-Base library for Alloy.
The newest version!
package org.ggp.base.player.gamer.statemachine;
import java.util.ArrayList;
import java.util.List;
import org.ggp.base.player.gamer.Gamer;
import org.ggp.base.player.gamer.exception.AbortingException;
import org.ggp.base.player.gamer.exception.MetaGamingException;
import org.ggp.base.player.gamer.exception.MoveSelectionException;
import org.ggp.base.player.gamer.exception.StoppingException;
import org.ggp.base.util.gdl.grammar.GdlTerm;
import org.ggp.base.util.logging.GamerLogger;
import org.ggp.base.util.statemachine.MachineState;
import org.ggp.base.util.statemachine.Move;
import org.ggp.base.util.statemachine.Role;
import org.ggp.base.util.statemachine.StateMachine;
import org.ggp.base.util.statemachine.exceptions.GoalDefinitionException;
import org.ggp.base.util.statemachine.exceptions.MoveDefinitionException;
import org.ggp.base.util.statemachine.exceptions.TransitionDefinitionException;
/**
* The base class for Gamers that rely on representing games as state machines.
* 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
*/
public abstract class StateMachineGamer extends Gamer
{
// =====================================================================
// First, the abstract methods which need to be overriden by subclasses.
// These determine what state machine is used, what the gamer does during
// metagaming, and how the gamer selects moves.
/**
* Defines which state machine this gamer will use.
* @return
*/
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
* @return Move - the move selected by the player
* @throws TransitionDefinitionException
* @throws MoveDefinitionException
* @throws GoalDefinitionException
*/
public abstract Move stateMachineSelectMove(long timeout) throws TransitionDefinitionException, MoveDefinitionException, GoalDefinitionException;
/**
* 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 of the game.
*/
public final MachineState getCurrentState()
{
return currentState;
}
/**
* 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, 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
* free up resources that the state machine has tied up. Currently, it is
* only used in the Proxy, for players designed to run 24/7.
*/
protected final void cleanupAfterMatch() {
role = 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 {
MachineState newCurrentState = newStateMachine.getInitialState();
Role newRole = newStateMachine.getRoleFromConstant(getRoleName());
// Attempt to run through the game history in the new machine
List> theMoveHistory = getMatch().getMoveHistory();
for(List nextMove : theMoveHistory) {
List theJointMove = new ArrayList();
for(GdlTerm theSentence : nextMove)
theJointMove.add(newStateMachine.getMoveFromTerm(theSentence));
newCurrentState = newStateMachine.getNextStateDestructively(newCurrentState, theJointMove);
}
// Finally, switch over if everything went well.
role = newRole;
currentState = newCurrentState;
stateMachine = newStateMachine;
} catch (Exception e) {
GamerLogger.log("GamePlayer", "Caught an exception while switching state machine!");
GamerLogger.logStackTrace("GamePlayer", e);
}
}
/**
* A function that can be used when deserializing gamers, to bring a
* state machine gamer back to the internal state that it has when it
* arrives at a particular game state.
*/
public final void resetStateFromMatch() {
stateMachine = getInitialStateMachine();
stateMachine.initialize(getMatch().getGame().getRules());
currentState = stateMachine.getMachineStateFromSentenceList(getMatch().getMostRecentState());
role = stateMachine.getRoleFromConstant(getRoleName());
}
// =====================================================================
// Finally, methods which are overridden with proper state-machine-based
// semantics. These basically wrap a state-machine-based view of the world
// 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
{
try
{
stateMachine = getInitialStateMachine();
stateMachine.initialize(getMatch().getGame().getRules());
currentState = stateMachine.getInitialState();
role = stateMachine.getRoleFromConstant(getRoleName());
getMatch().appendState(currentState.getContents());
stateMachineMetaGame(timeout);
}
catch (Exception e)
{
GamerLogger.logStackTrace("GamePlayer", e);
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
* and then calls stateMachineSelectMove to select a move based on that
* current state.
*/
@Override
public final GdlTerm selectMove(long timeout) throws MoveSelectionException
{
try
{
stateMachine.doPerMoveWork();
List lastMoves = getMatch().getMostRecentMoves();
if (lastMoves != null)
{
List moves = new ArrayList();
for (GdlTerm sentence : lastMoves)
{
moves.add(stateMachine.getMoveFromTerm(sentence));
}
currentState = stateMachine.getNextState(currentState, moves);
getMatch().appendState(currentState.getContents());
}
return stateMachineSelectMove(timeout).getContents();
}
catch (Exception e)
{
GamerLogger.logStackTrace("GamePlayer", e);
throw new MoveSelectionException(e);
}
}
@Override
public void stop() throws StoppingException {
try {
stateMachine.doPerMoveWork();
List lastMoves = getMatch().getMostRecentMoves();
if (lastMoves != null)
{
List moves = new ArrayList();
for (GdlTerm sentence : lastMoves)
{
moves.add(stateMachine.getMoveFromTerm(sentence));
}
currentState = stateMachine.getNextState(currentState, moves);
getMatch().appendState(currentState.getContents());
getMatch().markCompleted(stateMachine.getGoals(currentState));
}
stateMachineStop();
}
catch (Exception e)
{
GamerLogger.logStackTrace("GamePlayer", e);
throw new StoppingException(e);
}
}
@Override
public void abort() throws AbortingException {
try {
stateMachineAbort();
}
catch (Exception e)
{
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;
}