有限狀態機FSM

有限狀態機又簡稱FSM(Finite-State Machine的首字母縮寫)。這個在離散數學裏學過了,它是計算機領域中被普遍使用的數學概念。是表示有限個狀態以及在這些狀態之間的轉移和動做等行爲的數學模型。編譯原理學得好的童鞋應該對FSM不陌生,由於編譯器就用了FMS來作詞法掃描時的狀態轉移。網絡

FSM的概念在網上一搜能夠搜一大堆出來,但估計您也看不大明白。本文將以不同的方式來說述FSM的概念以及實現。ui

現實生活中,狀態是隨處可見的,而且經過不一樣的狀態來作不一樣的事。好比冷了加衣服;餓了吃飯;困了睡覺等。這裏的冷了、餓了、困了是三種不一樣的狀態,而且根據這三個狀態的轉變驅動了不一樣行爲的產生(加衣服、吃飯和睡覺)。this

FSM是什麼

所謂有限狀態機,就是由有限個狀態組成的機器。再看上面舉到的例子:人就是一部機器,能感知三種狀態(冷、餓、困)。因爲氣溫下降因此人會以爲冷;因爲到了吃飯的時間因此以爲餓;因爲晚上12點因此以爲困。狀態的產生以及改變都是由某種條件的成立而出現的。不考慮FSM的內部結構時,它就像是一個黑箱子,以下圖:spa

左邊是輸入一系列條件,FSM經過斷定,而後輸出結果。.net

FSM的處理流程

上圖FSM屏蔽了斷定的過程,事實上FSM是由有限多個狀態組成的,每一個狀態至關於FSM的一個部件。好比要判斷一個整數是否偶數,其實只須要判斷這個整數的最低位是否爲0就好了,代碼以下:設計

$GOPATH/src/fsm_testcode

----main.goblog

01 package main
02  
03 import (
04     "fmt"
05 )
06  
07 func IsEven(num intbool {
08     if num&0x1 == 0x0 {
09         return true
10     }
11  
12     return false
13 }
14  
15 func main() {
16     fmt.Printf("%d is even? %t\n", 4, IsEven(4))
17     fmt.Printf("%d is even? %t\n", 5, IsEven(5))
18 }

1 cd $GOPATH/src/fsm_test
2 $ go build
3 $ ./fsm_test
4 4 is even? true
5 5 is even? false

對數字5來講,它的二進制表示爲0101。二進制只能爲0或1,因此二進制的字符集合爲:{0, 1},對應到FSM來講,就是有2種狀態,分別爲S0和S1。若是用FSM來處理,它老是從左邊讀取(固然也能夠把FSM反過來),也就是從0101最左邊那位開始輸入:首先輸入左邊第一位0,停留在S0狀態,而後輸入第二位1,轉到S1狀態,再輸入第三位0,則又回到S0狀態,最後輸入是後一位1則又回到S1狀態。以下圖所示:接口

上圖忽略了一個很重要的細節,就是0和1是怎麼輸入的。狀態S0和狀態S1是FSM裏的2個小部件,它們分別關聯了0和1(也能夠說是特定的輸入語句),因此只能經過FSM來輸入。當FSM接收到0時,它就交給S0去處理,這時S0就變成當前狀態,而後對S0輸入1,S0則將它交給S1去處理,這時S1就變成當前狀態。如此這般,FSM持有有限多個狀態,它能夠接收輸入並執行狀態轉移(好比將最初的0交給S0去處理)。狀態S0和狀態S1也是如此。遊戲

可是爲何最開始FSM接收輸入的0後會交給S0去處理呢?這是由於FSM的默認狀態是S0。就像是有一臺電視機,它老是有默認的頻道的,您一打開電視機就能夠看到影像,即便是滿屏的雪花點。並且能夠在按下電視機的開關前預先調整頻道,以後也能夠調整頻道。

如何用程序建模

FSM持有有限多個狀態集合,有當前狀態、默認狀態、接收的外部數據等。而且FSM有一系列的行爲:啓動FSM、退出FSM以及狀態轉移等。State(狀態)也會有一系列的行爲:進入狀態,轉移狀態等。而且State還有Action行爲,好比電視機當前頻道正在播放西遊記,切換頻道後就變成了播放封神榜,原理上是同樣的。代碼定義以下:

01 package main
02  
03 // 接口
04 type IFSMState interface {
05     Enter()
06     Exit()
07     CheckTransition()
08 }
09  
10 // State父struct
11 type FSMState struct{}
12  
13 // 進入狀態
14 func (this *FSMState) Enter() {
15     //
16 }
17  
18 // 退出狀態
19 func (this *FSMState) Exit() {
20     //
21 }
22  
23 // 狀態轉移檢測
24 func (this *FSMState) CheckTransition() {
25     //
26 }
27  
28 type FSM struct {
29     // 持有狀態集合
30     states map[string]IFSMState
31     // 當前狀態
32     current_state IFSMState
33     // 默認狀態
34     default_state IFSMState
35     // 外部輸入數據
36     input_data interface{}
37 }
38  
39 // 初始化FSM
40 func (this *FSM) Init() {
41     //
42 }
43  
44 // 添加狀態到FSM
45 func (this *FSM) AddState(key string, state IFSMState) {
46     //
47 }
48  
49 // 設置默認的State
50 func (this *FSM) SetDefaultState(state IFSMState) {
51     //
52 }
53  
54 // 轉移狀態
55 func (this *FSM) TransitionState() {
56     //
57 }
58  
59 // 設置輸入數據
60 func (this *FSM) SetInputData(inputData interface{}) {
61     //
62 }
63  
64 // 重置
65 func (this *FSM) Reset() {
66     //
67 }
68  
69 func main() {
70 }

以上代碼只是初略的定義。咱們知道FSM不是直接去選擇某種狀態,而是根據輸入條件來選擇的。因此能夠定義一張輸入語句和狀態的映射表,本文僅僅簡單實現。

NPC例子

遊戲中一個玩家能夠攜帶寵物,那麼這個 寵物(NPC)就能夠看做是FSM。好比這個寵物在天天8點鐘開始工做(掙金幣),中午12點鐘開始打坐練功。8點鐘和12點鐘就是對這個FSM的輸入語句,對應的狀態則是開始工做和開始打坐練功。代碼實現以下:

001 package main
002  
003 import (
004     "fmt"
005 )
006  
007 // 接口
008 type IFSMState interface {
009     Enter()
010     Exit()
011     CheckTransition(hour intbool
012     Hour() int
013 }
014  
015 // State父struct
016 type FSMState struct{}
017  
018 // 進入狀態
019 func (this *FSMState) Enter() {
020     //
021 }
022  
023 // 退出狀態
024 func (this *FSMState) Exit() {
025     //
026 }
027  
028 // 狀態轉移檢測
029 func (this *FSMState) CheckTransition(hour int) {
030     //
031 }
032  
033 // 打坐
034 type ZazenState struct {
035     hour int
036     FSMState
037 }
038  
039 func NewZazenState() *ZazenState {
040     return &ZazenState{hour: 8}
041 }
042  
043 func (this *ZazenState) Enter() {
044     fmt.Println("ZazenState: 開始打坐")
045 }
046  
047 func (this *ZazenState) Exit() {
048     fmt.Println("ZazenState: 退出打坐")
049 }
050  
051 func (this *ZazenState) Hour() int {
052     return this.hour
053 }
054  
055 // 狀態轉移檢測
056 func (this *ZazenState) CheckTransition(hour intbool {
057     if hour == this.hour {
058         return true
059     }
060  
061     return false
062 }
063  
064 // 工做
065 type WorkerState struct {
066     hour int
067     FSMState
068 }
069  
070 func NewWorkerState() *WorkerState {
071     return &WorkerState{hour: 12}
072 }
073  
074 func (this *WorkerState) Enter() {
075     fmt.Println("WorkerState: 開始工做")
076 }
077  
078 func (this *WorkerState) Exit() {
079     fmt.Println("WorkerState: 退出工做")
080 }
081  
082 func (this *WorkerState) Hour() int {
083     return this.hour
084 }
085  
086 // 狀態轉移檢測
087 func (this *WorkerState) CheckTransition(hour intbool {
088     if hour == this.hour {
089         return true
090     }
091  
092     return false
093 }
094  
095 type FSM struct {
096     // 持有狀態集合
097     states map[string]IFSMState
098     // 當前狀態
099     current_state IFSMState
100     // 默認狀態
101     default_state IFSMState
102     // 外部輸入數據
103     input_data int
104     // 是否初始化
105     inited     bool
106 }
107  
108 // 初始化FSM
109 func (this *FSM) Init() {
110     this.Reset()
111 }
112  
113 // 添加狀態到FSM
114 func (this *FSM) AddState(key string, state IFSMState) {
115     if this.states == nil {
116         this.states = make(map[string]IFSMState, 2)
117     }
118     this.states[key] = state
119 }
120  
121 // 設置默認的State
122 func (this *FSM) SetDefaultState(state IFSMState) {
123     this.default_state = state
124 }
125  
126 // 轉移狀態
127 func (this *FSM) TransitionState() {
128     nextState := this.default_state
129     input_data := this
相關文章
相關標籤/搜索