/** Bombs-Must-Detonate Finite State Machine Library * * The following library allows coding of agents as finite * state machines. * **/ include "stdlib.bmd" include "lib_agent.bmd" /* Definitions */ struct fsmStateInformation { environment latestEnvironment; point latestPosition; point latestBombDetonation; }; struct fsmTransitionTarget { int id; float weight; }; struct fsmTransition { fsmTransitionTarget list targets; (fsmStateInformation) -> bool list predicates; }; struct fsmState { int id; (void) -> void init; callbackSet callbacks; fsmTransition list transitionList; }; struct finiteStateMachine { fsmState list states; fsmState currentState; fsmStateInformation stateInformation; (point) -> void initialize; }; //Generate a unique Id int fsmGenerateId (); /* Implementations */ //Generate a unique id int __fsm_state_id; int fsmGenerateId () { if (null __fsm_state_id) { __fsm_state_id = 1; return 0; } else { __fsm_state_id += 1; return __fsm_state_id - 1; } } finiteStateMachine __active_fsm; //Processes the predicates associated with each transition in the list to determine the state target fsmTransitionTarget list getTransition(fsmTransition list tList, fsmStateInformation info, int curStateId) { for (int i; tList != nil[fsmTransition]; tList = cdr tList;) { fsmTransition curTrans = car tList; (fsmStateInformation) -> bool list curPreds = curTrans.predicates; bool isValid = true; for(int j; curPreds != nil[(fsmStateInformation) -> bool]; curPreds = cdr curPreds;) { (fsmStateInformation) -> bool curPredicate = car curPreds; if (~curPredicate(info)) { isValid = false; break; } } if(isValid) { return curTrans.targets; } } //No valid transition encountered: stay in current state fsmTransitionTarget targ; targ.id = curStateId; targ.weight = 1.0; return [targ]; } float addTargetWeights(fsmTransitionTarget t, float x) { return t.weight + x; } int getTargetState(fsmTransitionTarget list targetOptions) { float sum = list_foldr(addTargetWeights, 0.0, targetOptions); //Generate a random number between 0 and sum float rnd = 0.0; !(rand(),rnd); rnd *= sum; //Find the corresponding transition fsmTransitionTarget curOption = car targetOptions; while( (sum > curOption.weight) && (cdr targetOptions != nil[fsmTransitionTarget]) ) { sum -= curOption.weight; targetOptions = cdr targetOptions; curOption = car targetOptions; } return curOption.id; } void advanceFSM(finiteStateMachine fsm){ int newStateId = getTargetState( getTransition( fsm.currentState.transitionList, fsm.stateInformation, fsm.currentState.id)); //!(print("Advancing to state ")); //!(print(newStateId)); //!(print("\n")); fsmState list stateList = fsm.states; fsmState curState; for(int i = 0; stateList != nil[fsmState]; stateList = cdr stateList;) { curState = car stateList; if(curState.id = newStateId) { break; } } if (fsm.currentState.id != curState.id) { curState.callbacks.onInit = fsm.initialize; (void) -> void init = curState.init; fsm.currentState = curState; init(); } return; } /* Call back functions for the active finite state machine: 1. Update relevant state information 2. Take a transition 3. Forward the callback */ void __fsm_handle_onInit(point pos) { (point) -> void forwardTo = __active_fsm.initialize; forwardTo(pos); return; } move __fsm_handle_onMoveRequest(environment env) { __active_fsm.stateInformation.latestEnvironment = env; __active_fsm.stateInformation.latestPosition = env.playerStatus.position; advanceFSM(__active_fsm); (environment) -> move forwardTo = __active_fsm.currentState.callbacks.onMoveRequest; return forwardTo(env); } void __fsm_handle_onTeammateDeath() { advanceFSM(__active_fsm); (void) -> void forwardTo = __active_fsm.currentState.callbacks.onTeammateDeath; forwardTo(); return; } void __fsm_handle_onBombDetonate(point pos) { __active_fsm.stateInformation.latestBombDetonation = pos; advanceFSM(__active_fsm); (point) -> void forwardTo = __active_fsm.currentState.callbacks.onBombDetonate; forwardTo(pos); return; } void __fsm_handle_onDeath() { advanceFSM(__active_fsm); (void) -> void forwardTo = __active_fsm.currentState.callbacks.onDeath; forwardTo(); return; } // Sets the active fsm and registers all callbacks void registerFSM(finiteStateMachine fsm) { !(print("registerFSM has been called.\n")); __active_fsm = fsm; callbackSet set; set.onInit = __fsm_handle_onInit; set.onMoveRequest = __fsm_handle_onMoveRequest; set.onTeammateDeath = __fsm_handle_onTeammateDeath; set.onBombDetonate = __fsm_handle_onBombDetonate; set.onDeath = __fsm_handle_onDeath; registerCallbackSet(set); return; }