Welcome, guest | Sign In | My Account | Store | Cart

A simple state machine framework that could be used for AI or long processing operations. A simple example is provided as well.

Python 3.2 required

Python, 151 lines
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
from time import sleep
from random import randint, shuffle

class StateMachine(object):
    ''' Usage:  Create an instance of StateMachine, use set_starting_state(state) to give it an
        initial state to work with, then call tick() on each second (or whatever your desired
        time interval might be. '''

    def set_starting_state(self, state):
        ''' The entry state for the state machine. '''
        state.enter()
        self.state = state
        
    def tick(self):
        ''' Calls the current state's do_work() and checks for a transition '''
        next_state = self.state.check_transitions()
        
        if next_state is None:
            # Stick with this state
            self.state.do_work()
        else:
            # Next state found, transition to it
            self.state.exit()
            next_state.enter()
            self.state = next_state
        
    
class BaseState(object):
    ''' Usage: Subclass BaseState and override the enter(), do_work(), and exit() methods. 
        
            enter()    -- Setup for your state should occur here.  This likely includes adding 
                          transitions or initializing member variables.
                       
            do_work()  -- Meat and potatoes of your state.  There may be some logic here that will
                          cause a transition to trigger.
                          
            exit()     -- Any cleanup or final actions should occur here.  This is called just
                          before transition to the next state.
    '''
    
    def add_transition(self, condition, next_state):
        ''' Adds a new transition to the state.  The "condition" param must contain a callable
            object.  When the "condition" evaluates to True, the "next_state" param is set as
            the active state. '''
        # Enforce transition validity
        assert(callable(condition))
        assert(hasattr(next_state, "enter"))
        assert(callable(next_state.enter))
        assert(hasattr(next_state, "do_work"))
        assert(callable(next_state.do_work))
        assert(hasattr(next_state, "exit"))
        assert(callable(next_state.exit))
        
        # Add transition
        if not hasattr(self, "transitions"):
            self.transitions = []
        self.transitions.append((condition, next_state))

    def check_transitions(self):
        ''' Returns the first State thats condition evaluates true (condition order is randomized) '''
        if hasattr(self, "transitions"):
            shuffle(self.transitions)
            for transition in self.transitions:
                condition, state = transition
                if condition():
                    return state

    def enter(self):
        pass
        
    def do_work(self):
        pass
        
    def exit(self):
        pass


##################################################################################################
############################### EXAMPLE USAGE OF STATE MACHINE ###################################
##################################################################################################
class WalkingState(BaseState):
    def enter(self):
        print("WalkingState: enter()")
        def condition(): return randint(1, 5) == 5
        self.add_transition(condition, JoggingState())
        self.add_transition(condition, RunningState())
        
    def do_work(self):
        print("Walking...")
        
    def exit(self):
        print("WalkingState: exit()")
        
        
class JoggingState(BaseState):
    def enter(self):
        print("JoggingState: enter()")
        self.stamina = randint(5, 15)
        def condition(): return self.stamina <= 0
        self.add_transition(condition, WalkingState())
        
    def do_work(self):
        self.stamina -= 1
        print("Jogging ({0})...".format(self.stamina))
        
    def exit(self):
        print("JoggingState: exit()")
        
        
class RunningState(BaseState):
    def enter(self):
        print("RunningState: enter()")
        self.stamina = randint(5, 15)
        def walk_condition(): return self.stamina <= 0
        self.add_transition(walk_condition, WalkingState())
        
        def trip_condition(): return randint(1, 10) == 10
        self.add_transition(trip_condition, TrippingState())
        
    def do_work(self):
        self.stamina -= 2
        print("Running ({0})...".format(self.stamina))
        
    def exit(self):
        print("RunningState: exit()")
        
        
class TrippingState(BaseState):
    def enter(self):
        print("TrippingState: enter()")
        self.tripped = False
        def condition(): return self.tripped
        self.add_transition(condition, WalkingState())
        
    def do_work(self):
        print("Tripped!")
        self.tripped = True
        
    def exit(self):
        print("TrippingState: exit()")
        

if __name__ == "__main__":
    state = WalkingState()
    
    state_machine = StateMachine()
    state_machine.set_starting_state(state)
    
    while True:
        state_machine.tick()
        sleep(1)