This article is published on the loose blog http://blog.csdn.net/lanphaday. you are welcome to download this article. Violators must investigate.
[Python] Simple implementation of Finite State Machine (FSM)
Brief IntroductionFinite State Machine (FSM) is an algorithm. To put it simply, A finite state machine is composed of a group of States, an initial state, input, and conversion functions that convert from input to existing state to the next state. In the 23 Design Patterns of gof, the state mode is an object-oriented state machine that can be adapted to very complex state management. Currently, FSM is widely used in search engine word segmentation, compiler implementation, and game development. In game development, FSM is usually used to implement NPC control. For example, when an NPC is attacked, it chooses to escape or attack back based on its health and strength. It is generally implemented using FSM. There are many methods to implement FSM, which cannot be simply described as superior or inferior. However, in modern development, object-oriented implementation is generally recommended because of its reusability and robustness, in addition, there are good adaptability when demand changes.
PracticeThe theory should also come back to practice. Let's explore the implementation of FSM through examples. First of all, let us assume that there is a world in which there is only one car that never lacks power. Cars are of the next generation, and there is no backward equipment such as the throttle steering wheel, there are only two mutex buttons-stop and run. As time passes, the car stops and stops Based on the driver's operations. The following code implements this function:
While true: Key = get_key () # press the key if key = "stop": Stop (CAR) Elif key = "run": Go (CAR) Keep (CAR) # maintaining the original state |
Long live a programmer who has completed the functions and is intuitive and concise! But at this time, the customer (Planning or players) thought it was too boring to stop and stop. They wanted to turn around, turn left, and turn right, and we had to add more if... else branch; they want more cars, and we need to add loops in every branch; they don't just want cars, they also want to play truck, in this case, we need to judge whether the current car supports this operation in the branch cycle (for example, the truck loading and unloading cargo car does not support this operation); They ...... This while loop is finally infinitely large, and we realize that this design is indeed a bit problematic, so we try to implement FSM in another way. First, let's implement car ):
Class car (object ):Def stop (Self): Print "Stop !!! "Def go (Self): Print" goooooo !!! " |
There are only two methods: stop and go. The stop and run buttons are executed respectively. Next we will write two state management classes to process the processes that need to work when the button is pressed, popped up, and maintained:
Class stop_fsm (base_fsm ):Def enter_state (self, OBJ): Print "Car % s enter stop state! "% (ID (OBJ) def exec_state (self, OBJ): Print" Car % s in stop state! "% (ID (OBJ) obj. Stop () def exit_state (self, OBJ): Print" Car % s exit stop state! "% (ID (OBJ )) |
Class run_fsm (base_fsm ):Def enter_state (self, OBJ): Print "Car % s enter run state! "% (ID (OBJ) def exec_state (self, OBJ): Print" Car % s in run state! "% (ID (OBJ) obj. Go () def exit_state (self, OBJ): Print" Car % s exit run state! "% (ID (OBJ )) |
Stop_fsm and run_fsm both inherit from base_fsm. base_fsm is a pure Virtual Interface Class:
Class base_fsm (object ):Def enter_state (self, OBJ): Raise notimplementederror () def exec_state (self, OBJ): Raise notimplementederror () def exit_state (self, OBJ): Raise notimplementederror () |
Enter_state is called when OBJ enters a certain State-usually used for initialization; exit_state is also called when it leaves a certain State-usually for cleanup; exec_state is called at each frame. It usually performs some necessary work, such as checking its own message queue and processing the message. In the exec_state functions of stop_fsm and run_fsm classes, the stop and go functions of the object are called to keep the car in its original state. So far, car has not been exposed to FSM, so we need to provide an interface for it to have a FSM:
Def attach_fsm (self, state, FSM): Self. FSM = FSM self. curr_state = State |
We also need to provide a status conversion function for the car:
Def change_state (self, new_state, new_fsm): Self. curr_state = new_state self. FSM. exit_state (Self) self. FSM = new_fsm self. FSM. enter_state (Self) self.fsm.exe c_state (Self) |
Provide a function for car to maintain the status:
Def keep_state (Self): self.fsm.exe c_state (Self) |
Now there are only two States, but we know that the requirements will change at any time, so we 'd better have a state machine manager to manage these states:
Class fsm_mgr (object ):Def _ init _ (Self): Self. _ fsms ={} self. _ fsms [0] = stop_fsm () self. _ fsms [1] = run_fsm () def get_fsm (self, State): return self. _ fsms [State] def frame (self, objs, State): For OBJ in objs: If State = obj. curr_state: obj. keep_state () else: obj. change_state (State, self. _ fsms [State]) |
The most important function of fsm_mgr is frame, which is called at each frame. Here, frame determines whether to maintain or change the object state based on the current state of the object and the current input. At this time, our instance is basically complete. But one thing we need to do is to build a world to drive the state machine:
Class world (object ):Def Init (Self): Self. _ cars = [] self. _ fsm_mgr = fsm_mgr () self. _ init_car () def _ init_car (Self): for I in xrange (1): # producing car TMP = car () TMP. attach_fsm (0, self. _ fsm_mgr.get_fsm (0) self. _ cars. append (TMP) def _ frame (Self): Self. _ fsm_mgr.frame (self. _ cars, state_factory () def run (Self): While true: Self. _ frame () sleep (0.5) |
The Code shows that there are car objects and fsm_mgr objects in the world. In the run function, the _ frame function (FPS = 2) is executed every seconds ), the _ frame function only drives fsm_mgr to refresh the object. The new command is obtained from the state_factory function. This function is used to simulate the driver's operation (press one of the stop or run buttons):
Def state_factory (): Return random. randint (0, 1) |
Now we need to initialize the world to run our FSM!
If _ name _ = "_ main _": World = World () World. INIT () World. Run () |
Run the above Code with the python interpreter. We can see that the program continuously outputs the car status:
... Car8parts 392 exit run state! Car81_392 enter stop state! Car8453392 in stop state! Stop !!! Car8453392 in stop state! Stop !!! Car8453392 exit stop state! Car81_392 enter run state! Car81_392 in run state! Goooooo !!! Car8453392 exit run state! Car81_392 enter stop state! Car8453392 in stop state! Stop !!! Car8453392 exit stop state! Car81_392 enter run state! Car81_392 in run state! Goooooo !!! Car81_392 in run state! Goooooo !!! Car8453392 exit run state! Car81_392 enter stop state! Car8453392 in stop state! Stop !!! Car8453392 in stop state! Stop !!! Car8453392 exit stop state! Car81_392 enter run state! Car81_392 in run state! Goooooo !!!...... |
ConclusionNow let's look back at our previous problems: 1. Players want more cars, such as turning around. We can turn around the car by adding a back method, and then inherit a back_fsm from base_fsm to process the redirection. Then, add a back_fsm instance in fsm_mgr and enable state_factory to generate the redirection command. It sounds much more complicated than the while + If method. Otherwise, only back_fsm and fsm_mgr add back_fsm instances here are unique. The other two methods must be executed. 2. Players need more cars. This is too simple for the object-oriented FSM implementation. We just need to modify the production quantity in world. _ init_car. 3. Players need more models, such as truck. Derive a truck from the car, and then add the loading and unloading interfaces. The biggest change is to make some judgments when converting the truck status. For example, you cannot directly switch from the loading status to the starting status, but load, stop, and start again. Through the analysis of these simple problems, we can see that FSM is designed in an object-oriented way. When the demand changes, only the code is added or deleted, there is still little need to change the existing code, and the scalability, adaptability and robustness of the program are greatly improved; therefore, it is wise to apply the object-oriented FSM in Game Development with a huge world, rich species, complex states, and staggered conditions. Another point is that the object-oriented FSM can easily simulate the message mechanism, which facilitates the clarity of modules and easier to design orthogonal programs.