package net.kennux.cubicworld.fsm; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.HashMap; import java.util.Map.Entry; /** * * @author kennux * */ public class FiniteStateMachine { /** * The states. * Contains all states. */ private ArrayList<IState> states; /** * Contains all transitions. * Entry's key is the transition state from, value is transition state to. */ private HashMap<Entry<IState, IState>, ITransition> transitions; /** * Contains the current state's array list (states) index. */ private int currentState = 0; /** * Overload this constructor! * You must register your states and transitions in your constructor. */ public FiniteStateMachine() { this.states = new ArrayList<IState>(); this.transitions = new HashMap<Entry<IState, IState>, ITransition>(); } /** * @return the currentState */ public int getCurrentState() { return currentState; } /** * Register a state to this state machine. * Returns it's state index in the states list. * * @param state */ protected int registerState(IState state) { this.states.add(state); return this.states.indexOf(state); } /** * Registers a transition from one to another state. * * @param transition * @param fromState * @param toState */ protected void registerTransition(ITransition transition, IState fromState, IState toState) { this.transitions.put(new SimpleEntry<IState, IState>(fromState, toState), transition); } /** * @param currentState * the currentState to set */ public void setCurrentState(int currentState) { this.currentState = currentState; } /** * <pre> * Performs one fsm cycle. * One cycle can update the current state or change the fsm's state from one state to another. * If changing from one state to another, first the leave method on the current state will get called, then the enter method on the next state will get called. * </pre> */ public void update() { // Get current state IState currentState = this.states.get(this.getCurrentState()); // Check all conditions for (Entry<Entry<IState, IState>, ITransition> entry : this.transitions.entrySet()) { // Check? if (entry.getKey().getKey() == currentState) { if (entry.getValue().conditionMet()) { // TRANSITION!! currentState.leave(entry.getKey().getValue()); this.setCurrentState(this.states.indexOf(entry.getKey().getValue())); entry.getKey().getValue().enter(currentState); return; } } } // Update state currentState.update(); } }