-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathStateMachine.elm
73 lines (56 loc) · 2.2 KB
/
StateMachine.elm
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
module StateMachine exposing (..)
import Array exposing (Array)
import Maybe exposing (andThen)
import ArrayToList exposing (indices)
--- TYPES ---
-- Int is used to denote state, but anything outside
-- a StateMachine's states list range is a dead end.
type alias State = Int
type alias Condition a = a -> Bool
type alias Action a = a -> a
type alias Transition a = (Condition a, Action a, State)
-- This FSM keeps track of additional arbitrary info alongside the State.
-- For example, physics data such as position, velocity, acceleration.
-- The first transition in each state's transition list has highest priority.
type alias StateMachine a =
{ state : State
, info : a
, states : Array String
, rules : Array (List (Transition a))
}
--- CREATION ---
-- starting state assumed first element
new : Array String -> a -> StateMachine a
new states info =
{ state = 0
, info = info
, states = states
, rules = Array.repeat (Array.length states) []
}
-- add a transition with lower priority than existing transitions
-- this is slow but it should happen during startup only
addRule : (String, Condition a, Action a, String) -> StateMachine a -> StateMachine a
addRule (src, cond, act, dst) machine =
case indices src machine.states of
[] -> machine
state :: _ -> case indices dst machine.states of
[] -> machine
next :: _ -> case Array.get state machine.rules of
Nothing -> machine
Just stateRules -> { machine | rules =
Array.set state (stateRules ++ [(cond, act, next)]) machine.rules }
--- USAGE ---
-- just update the additional internal info (such as physics data)
apprise : a -> StateMachine a -> StateMachine a
apprise info machine = { machine | info = info }
-- conditionally make a transition based on internal info
step : StateMachine a -> StateMachine a
step machine = case Array.get machine.state machine.rules of
Nothing -> machine
Just stateRules ->
case List.filter (\(cond, _, _) -> cond machine.info) stateRules of
[] -> machine
(_, act, next) :: _ -> { machine | state = next, info = act machine.info }
-- conditionally make a transition based on external info
update : a -> StateMachine a -> StateMachine a
update info = apprise info >> step