package org.ggp.base.util.statemachine; import java.util.List; import java.util.Set; 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.logging.GamerLogger; 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 org.ggp.base.util.statemachine.implementation.prover.ProverStateMachine; /** * The FailsafeStateMachine is a wrapper around a particular state machine. * It will catch errors/exceptions being thrown from that state machine, and * fall back to a regular prover if the state machine fails. It's not totally * clear that this is helpful, but it's an additional layer of bullet-proofing * in case anything goes wrong. * * @author Sam Schreiber */ public class FailsafeStateMachine extends StateMachine { private StateMachine theBackingMachine = null; private List<Gdl> gameDescription; public FailsafeStateMachine (StateMachine theInitialMachine) { theBackingMachine = theInitialMachine; } @Override public String getName() { if(theBackingMachine != null) { return "Failsafe(" + theBackingMachine.getName() + ")"; } return "Failsafe(null)"; } @Override public synchronized void initialize(List<Gdl> description) { this.gameDescription = description; if(attemptLoadingInitialMachine()) return; GamerLogger.logError("StateMachine", "Failsafe Machine: failed to load initial state machine. Falling back..."); if(attemptLoadingProverMachine()) return; GamerLogger.logError("StateMachine", "Failsafe Machine: catastrophic failure to load *any* state machine. Cannot recover."); GamerLogger.logError("StateMachine", "Failsafe Machine: cannot recover from current state. Shutting down."); theBackingMachine = null; } private void failGracefully(Exception e1, Error e2) { if(e1 != null) GamerLogger.logStackTrace("StateMachine", e1); if(e2 != null) GamerLogger.logStackTrace("StateMachine", e2); GamerLogger.logError("StateMachine", "Failsafe Machine: graceful failure mode kicking in."); if(theBackingMachine.getClass() != ProverStateMachine.class) { GamerLogger.logError("StateMachine", "Failsafe Machine: online failure for " + theBackingMachine.getClass() + ". Attempting to restart with a standard prover."); if(attemptLoadingProverMachine()) return; } theBackingMachine = null; GamerLogger.logError("StateMachine", "Failsafe Machine: online failure for regular prover. Cannot recover."); } private boolean attemptLoadingInitialMachine() { try { theBackingMachine.initialize(gameDescription); GamerLogger.log("StateMachine", "Failsafe Machine: successfully activated initial state machine for use!"); return true; } catch(Exception e1) { } catch(ThreadDeath d) { throw d; } catch(Error e2) { } return false; } private boolean attemptLoadingProverMachine() { try { StateMachine theStateMachine = new ProverStateMachine(); theStateMachine.initialize(gameDescription); theBackingMachine = theStateMachine; GamerLogger.log("StateMachine", "Failsafe Machine: successfully loaded traditional prover."); return true; } catch(Exception e1) { } catch(ThreadDeath d) { throw d; } catch(OutOfMemoryError e) { throw e; } catch(Error e2) { } return false; } @Override public int getGoal(MachineState state, Role role) throws GoalDefinitionException { if(theBackingMachine == null) return 0; try { return theBackingMachine.getGoal(state, role); } catch(GoalDefinitionException ge) { throw ge; } catch(Exception e) { failGracefully(e, null); } catch(ThreadDeath d) { throw d; } catch(OutOfMemoryError e) { throw e; } catch(Error e) { failGracefully(null, e); } return getGoal(state, role); } @Override public MachineState getInitialState() { if(theBackingMachine == null) return null; try { return theBackingMachine.getInitialState(); } catch(Exception e) { failGracefully(e, null); } catch(ThreadDeath d) { throw d; } catch(OutOfMemoryError e) { throw e; } catch(Error e) { failGracefully(null, e); } return getInitialState(); } @Override public List<Move> getLegalMoves(MachineState state, Role role) throws MoveDefinitionException { if(theBackingMachine == null) return null; try { return theBackingMachine.getLegalMoves(state, role); } catch(MoveDefinitionException me) { throw me; } catch(Exception e) { failGracefully(e, null); } catch(OutOfMemoryError e) { throw e; } catch(ThreadDeath d) { throw d; } catch(Error e) { failGracefully(null, e); } return getLegalMoves(state, role); } @Override public Move getRandomMove(MachineState state, Role role) throws MoveDefinitionException { if(theBackingMachine == null) return null; try { return theBackingMachine.getRandomMove(state, role); } catch(MoveDefinitionException me) { throw me; } catch(Exception e) { failGracefully(e, null); } catch(OutOfMemoryError e) { throw e; } catch(ThreadDeath d) { throw d; } catch(Error e) { failGracefully(null, e); } return getRandomMove(state, role); } @Override public MachineState getMachineStateFromSentenceList(Set<GdlSentence> sentenceList) { if(theBackingMachine == null) return null; try { return theBackingMachine.getMachineStateFromSentenceList(sentenceList); } catch(Exception e) { failGracefully(e, null); } catch(ThreadDeath d) { throw d; } catch(OutOfMemoryError e) { throw e; } catch(Error e) { failGracefully(null, e); } return getMachineStateFromSentenceList(sentenceList); } @Override public Move getMoveFromTerm(GdlTerm term) { if(theBackingMachine == null) return null; try { return theBackingMachine.getMoveFromTerm(term); } catch(Exception e) { failGracefully(e, null); } catch(ThreadDeath d) { throw d; } catch(OutOfMemoryError e) { throw e; } catch(Error e) { failGracefully(null, e); } return getMoveFromTerm(term); } @Override public MachineState getNextState(MachineState state, List<Move> moves) throws TransitionDefinitionException { if(theBackingMachine == null) return null; try { return theBackingMachine.getNextState(state, moves); } catch(TransitionDefinitionException te) { throw te; } catch(Exception e) { failGracefully(e, null); } catch(ThreadDeath d) { throw d; } catch(OutOfMemoryError e) { throw e; } catch(Error e) { failGracefully(null, e); } return getNextState(state, moves); } @Override public MachineState getNextStateDestructively(MachineState state, List<Move> moves) throws TransitionDefinitionException { if(theBackingMachine == null) return null; try { return theBackingMachine.getNextStateDestructively(state, moves); } catch(TransitionDefinitionException te) { throw te; } catch(Exception e) { failGracefully(e, null); } catch(ThreadDeath d) { throw d; } catch(OutOfMemoryError e) { throw e; } catch(Error e) { failGracefully(null, e); } return getNextStateDestructively(state, moves); } @Override public Role getRoleFromConstant(GdlConstant constant) { if(theBackingMachine == null) return null; try { return theBackingMachine.getRoleFromConstant(constant); } catch(Exception e) { failGracefully(e, null); } catch(ThreadDeath d) { throw d; } catch(OutOfMemoryError e) { throw e; } catch(Error e) { failGracefully(null, e); } return getRoleFromConstant(constant); } @Override public List<Role> getRoles() { if(theBackingMachine == null) return null; try { return theBackingMachine.getRoles(); } catch(Exception e) { failGracefully(e, null); } catch(ThreadDeath d) { throw d; } catch(OutOfMemoryError e) { throw e; } catch(Error e) { failGracefully(null, e); } return getRoles(); } @Override public boolean isTerminal(MachineState state) { if(theBackingMachine == null) return false; try { return theBackingMachine.isTerminal(state); } catch(Exception e) { failGracefully(e, null); } catch(ThreadDeath d) { throw d; } catch(OutOfMemoryError e) { throw e; } catch(Error e) { failGracefully(null, e); } return isTerminal(state); } @Override public MachineState performDepthCharge(MachineState state, int[] theDepth) throws TransitionDefinitionException, MoveDefinitionException { if(theBackingMachine == null) return null; try { return theBackingMachine.performDepthCharge(state, theDepth); } catch (TransitionDefinitionException te) { throw te; } catch (MoveDefinitionException me) { throw me; } catch(Exception e) { failGracefully(e, null); } catch(ThreadDeath d) { throw d; } catch(OutOfMemoryError e) { throw e; } catch(Error e) { failGracefully(null, e); } return performDepthCharge(state, theDepth); } @Override public void getAverageDiscountedScoresFromRepeatedDepthCharges(MachineState state, double[] avgScores, double[] avgDepth, double discountFactor, int repetitions) throws TransitionDefinitionException, MoveDefinitionException, GoalDefinitionException { if(theBackingMachine == null) return; try { theBackingMachine.getAverageDiscountedScoresFromRepeatedDepthCharges(state, avgScores, avgDepth, discountFactor, repetitions); return; } catch (TransitionDefinitionException te) { throw te; } catch (MoveDefinitionException me) { throw me; } catch (GoalDefinitionException ge) { throw ge; } catch(Exception e) { failGracefully(e, null); } catch(ThreadDeath d) { throw d; } catch(OutOfMemoryError e) { throw e; } catch(Error e) { failGracefully(null, e); } getAverageDiscountedScoresFromRepeatedDepthCharges(state, avgScores, avgDepth, discountFactor, repetitions); } @Override public void updateRoot(MachineState theState) { if(theBackingMachine == null) return; try { theBackingMachine.updateRoot(theState); return; } catch(Exception e) { failGracefully(e, null); } catch(ThreadDeath d) { throw d; } catch(OutOfMemoryError e) { throw e; } catch(Error e) { failGracefully(null, e); } updateRoot(theState); } public StateMachine getBackingMachine() { return theBackingMachine; } }