All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.ggp.base.util.statemachine.StateMachine Maven / Gradle / Ivy

The newest version!
package org.ggp.base.util.statemachine;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;

import org.ggp.base.util.GoalTuplePool;
import org.ggp.base.util.GoalTuplePool.GoalTuplePoolNode;
import org.ggp.base.util.gdl.grammar.Gdl;
import org.ggp.base.util.gdl.grammar.GdlConstant;
import org.ggp.base.util.gdl.grammar.GdlSentence;
import org.ggp.base.util.gdl.grammar.GdlTerm;
import org.ggp.base.util.propnet.architecture.Component;
import org.ggp.base.util.propnet.architecture.PropNet;
import org.ggp.base.util.statemachine.exceptions.GoalDefinitionException;
import org.ggp.base.util.statemachine.exceptions.MoveDefinitionException;
import org.ggp.base.util.statemachine.exceptions.TransitionDefinitionException;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;


/**
 * Provides the base class for all state machine implementations.
 */
public abstract class StateMachine
{
    // ============================================
    //          Stubs for implementations
    // ============================================
    //  The following methods are required for a valid
    // 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 // implementations which use alternative Role/Move/State representations // can look up/compute what some Gdl corresponds to in their representation. // They are implemented for convenience, using the default ways of generating // these objects, but they can be overridden to support machine-specific objects. public MachineState getMachineStateFromSentenceList(Set sentenceList) { return new MachineState(sentenceList); } public Role getRoleFromConstant(GdlConstant constant) { return new Role(constant); } public Move getMoveFromTerm(GdlTerm term) { return new Move(term); } /* * Should return a copy of the state machine that can be used in a synchronized * fashion in conjunction with this state machine. Assume that initialize() has * already been called on this machine, and return a machine as if initialize() * has already been called on it. * * If the state machine doesn't depend on internal storage (e.g. * ProverStateMachine), it may return itself. */ public abstract StateMachine getSynchronizedCopy(); // ============================================ // Stubs for advanced methods // ============================================ // // The following methods have functioning stubs, // which can be overridden with full-fledged versions // 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. */ public void doPerMoveWork() {} /** 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. */ 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 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. *

* 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>(); for (Role role : getRoles()) { legals.add(getLegalMoves(state, role)); } List> crossProduct = new ArrayList>(); crossProductLegalMoves(legals, crossProduct, new ArrayDeque()); 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>(); for (Role r : getRoles()) { if (r.equals(role)) { List m = new ArrayList(); m.add(move); legals.add(m); } else { legals.add(getLegalMoves(state, r)); } } List> crossProduct = new ArrayList>(); crossProductLegalMoves(legals, crossProduct, new ArrayDeque()); return crossProduct; } public List> getLegalMovesByRole(MachineState state) throws MoveDefinitionException { List> legalMovesByRole = Lists.newArrayListWithCapacity(getRoles().size()); for (Role role : getRoles()) { legalMovesByRole.add(getLegalMoves(state, role)); } return legalMovesByRole; } /** * 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(); for (List move : getLegalJointMoves(state)) { nextStates.add(getNextState(state, move)); } 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>(); Map roleIndices = getRoleIndices(); for (List moves : getLegalJointMoves(state)) { Move move = moves.get(roleIndices.get(role)); if (!nextStates.containsKey(move)) { nextStates.put(move, new ArrayList()); } nextStates.get(move).add(getNextState(state, moves)); } return nextStates; } protected void crossProductLegalMoves(List> legals, List> crossProduct, Deque partial) { if (partial.size() == legals.size()) { crossProduct.add(new ArrayList(partial)); } else { for (Move move : legals.get(partial.size())) { partial.addLast(move); crossProductLegalMoves(legals, crossProduct, partial); partial.removeLast(); } } } 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) { ImmutableMap.Builder roleIndicesBuilder = ImmutableMap.builder(); List roles = getRoles(); for (int i = 0; i < roles.size(); i++) { roleIndicesBuilder.put(roles.get(i), i); } roleIndices = roleIndicesBuilder.build(); } 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 ImmutableList getGoals(MachineState state) throws GoalDefinitionException { GoalTuplePoolNode curNode = GoalTuplePool.getInitialNode(); for (Role r : getRoles()) { curNode = curNode.get(getGoal(state, r)); } return curNode.getList(); } /** * 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(getRoles().size()); for (Role role : getRoles()) { random.add(getRandomMove(state, role)); } 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(getRoles().size()); for (Role r : getRoles()) { if (r.equals(role)) { random.add(move); } else { random.add(getRandomMove(state, r)); } } return random; } public List getRandomJointMoveWithAnyGebMoves(MachineState state) throws MoveDefinitionException { Map gebMoves = getGebMoves(state); return getRandomJointMoveWithMoves(state, gebMoves); } public List getRandomJointMoveWithMoves(MachineState state, Map specifiedMoves) throws MoveDefinitionException { List moves = new ArrayList(getRoles().size()); for (Role r : getRoles()) { Move suggestedMove = specifiedMoves.get(r); if (suggestedMove != null) { moves.add(suggestedMove); } else { moves.add(getRandomMove(state, r)); } } return moves; } /** * 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); } /** * Returns goals from 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 ImmutableList performDepthCharge(MachineState state, final int[] theDepth) throws TransitionDefinitionException, MoveDefinitionException, GoalDefinitionException { int nDepth = 0; while(!isTerminal(state)) { nDepth++; state = getNextStateDestructively(state, getRandomJointMove(state)); } if(theDepth != null) theDepth[0] = nDepth; return getGoals(state); } // public DepthChargeBatchResult performDepthChargeBatch2(MachineState state) throws TransitionDefinitionException, MoveDefinitionException, GoalDefinitionException { // int[] theDepth = new int[1]; // ImmutableList goals = performDepthCharge(state, theDepth); // return new FlatDepthChargeResult(goals, 1); // } public abstract Map getGebMoves(MachineState state); public abstract MachineState translateState(MachineState state); public abstract boolean isNative(MachineState state); 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; } final int[] depth = new int[1]; for (int i = 0; i < repetitions; i++) { MachineState stateForCharge = state.getCopy(); List goalValues = performDepthCharge(stateForCharge, depth); avgDepth[0] += depth[0]; final double accumulatedDiscountFactor = Math.pow(discountFactor, depth[0]); for (int j = 0; j < avgScores.length; j++) { avgScores[j] += goalValues.get(j) * accumulatedDiscountFactor; } } avgDepth[0] /= repetitions; for (int j = 0; j < avgScores.length; j++) { avgScores[j] /= repetitions; } } public abstract boolean isPropNetBased(); public abstract PropNet getPropNet(); public abstract boolean getComponentValue(MachineState state, Component component); public abstract int getComponentTrueInputsCount(MachineState state, Component component); }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy