If you are completely new to state machines, I've written a very basic implementation to help get you started.
Code: [View]
unit classBaseStateMachine; interface uses SysUtils, Generics.collections; type TStateMethod = function:integer of object; TBaseStatemachine = class(TObject) protected fCurrentState : integer; fStateList : TDictionary; procedure addState(stateIndex:integer;aStateMethod:TStateMethod); public constructor create; destructor Destroy; override; procedure process; procedure initialiseStates; virtual; abstract; property currentState:integer read fCurrentState write fCurrentState; end; implementation procedure TBaseStateMachine.addState(stateIndex:integer;aStateMethod:TStateMethod); begin if (fStateList.ContainsKey(stateIndex)) then begin raise exception.createFmt('%s.addState - Add failed, duplicate state %d',[self.className,stateIndex]); end; fStateList.Add(stateIndex,aStateMethod); end; constructor TBaseStateMachine.create; begin inherited; fStateList:=TDictionary .create; fCurrentState:=0; initialiseStates; end; destructor TBaseStateMachine.Destroy; begin try fStateList.free; except end; inherited; end; procedure TBaseStateMachine.process; var nextState : integer; begin if (fStateList.ContainsKey(fCurrentState)) then begin nextState:=fStateList.Items[fCurrentState]; if (nextState<>0) then begin fCurrentState:=nextState; end; end else begin raise exception.createFmt('%s.process failed - Unknown state %d',[self.className,fCurrentState]); end; end; end.
You can also use initialiseStates to setup any internal variables.
To use it, you simply call process each time you want to run the current state. The appropriate function will be executed and the machine will switch to a new state if the return value of the function is not 0.
As an example, here is a very basic state machine that counts up to 100 and then back down to 0 before counting back to 100 etc. ad infinitum.
Interface
Code: [View]
const STATE_COUNTINGUP = 1; STATE_COUNTINGDOWN = 2; type TMyStateMachine = class(TBaseStateMachine) protected fCounter : integer; public procedure initialiseStates; override; function stateCountingUp:integer; function stateCountingDown:integer; property counter:integer read fCounter; end;
Code: [View]
procedure TMyStateMachine.initialiseStates; begin addState(STATE_COUNTINGUP,stateCountingUp); addState(STATE_COUNTINGDOWN,stateCountingDown); fCurrentState:=STATE_COUNTINGUP; fCounter:=0; end; function TMyStateMachine.stateCountingUp; begin result:=0; inc(fCounter); if (fCounter>=100) then begin result:=STATE_COUNTINGDOWN; end; end; function TMyStateMachine.stateCountingDown; begin result:=0; dec(fCounter); if (fCounter<=0) then begin result:=STATE_COUNTINGUP; end; end;
Code: [View]
procedure TfrmBasicStateMachine.FormCreate(Sender: TObject); begin fMyStateMachine:=TMyStateMachine.create; end; procedure TfrmBasicStateMachine.FormDestroy(Sender: TObject); begin try fMyStateMachine.free; except end; end; procedure TfrmBasicStateMachine.tmrTickTimer(Sender: TObject); begin tmrTick.enabled:=false; fMyStateMachine.process; lblCount.caption:=intToStr(fMyStateMachine.counter); lblCurrentState.caption:=intToStr(fMyStateMachine.currentState); tmrTick.enabled:=true; end;
All the source code for this example is included in the attached ZIP file.
vBulletin Message