Documentation ¶
Index ¶
- Variables
- type Condition
- type Effector
- type InitializeSub
- type Logic
- type Machine
- func (m *Machine[Subject, Input, StateData, TransitionData, Effect]) Apply(subject *Subject)
- func (m *Machine[Subject, Input, StateData, TransitionData, Effect]) Init(subject *Subject, input Input)
- func (m *Machine[Subject, Input, StateData, TransitionData, Effect]) ProcessQueue(subject *Subject, input Input)
- func (m *Machine[Subject, Input, StateData, TransitionData, Effect]) Transitions(transitions []Transition[Input, TransitionData], subject *Subject, input Input, ...) int
- func (m *Machine[Subject, Input, StateData, TransitionData, Effect]) Update(subject *Subject, input Input)
- func (m *Machine[Subject, Input, StateData, TransitionData, Effect]) UpdateActive(subject *Subject, input Input)
- type MachineDefinition
- type State
- type StateDefinition
- type Transition
- type UserData
- type UserDataPropertyBase
- type UserDataPropertyTyped
Constants ¶
This section is empty.
Variables ¶
var Area = id.NewArea[uint32, uint16]()
The area for state machine identifiers - to keep maps in this package as small as fast as possible.
Functions ¶
This section is empty.
Types ¶
type Effector ¶
The amount a state effects a subject based on input can be evaluated at state creation or on each update. The effect can be a simple weight or a complex object.
type InitializeSub ¶
type InitializeSub int
A way to control when (if at all) a states sub-machine is initialized.
const ( // Initialize the sub machine before we even call Start. InitializeSubBeforeStart InitializeSub = iota // Initialize the sub after start is called and returns true. InitializeSubAfterStart // Initialze the sub once we active the state on the machine. InitializeSubAfterQueue // Never initialize the sub, behave normally and let the first Update handle it. InitializeSubNever )
type Logic ¶
type Logic[Subject any, Input any, StateData any, TransitionData any, Effect any] struct { // The logic that would return true if the given state is done and the non-live transitions out of it should be evaluted for the next state. IsDone func(*Subject, State[Subject, Input, StateData, TransitionData, Effect]) bool // Starts the given state on the subject, possible from a transition and possible where an unfinished state needs to be outroed so that it may eventually return true for IsDone and // be removed from the active states. Start func(*Subject, State[Subject, Input, StateData, TransitionData, Effect], *Transition[Input, TransitionData], *State[Subject, Input, StateData, TransitionData, Effect]) bool // The logic invoked to apply the active states on the subject. See other Applied properties. Apply func(*Subject, []State[Subject, Input, StateData, TransitionData, Effect]) // If non-zero, no more than this many states should be sent to be applied to the subject. AppliedMax int // If non-nil and there's an applied max that will affect the applied states - this function will sort the states so the preferred states are applied. AppliedPriority func(a State[Subject, Input, StateData, TransitionData, Effect], b State[Subject, Input, StateData, TransitionData, Effect]) bool // If true all active states in all sub machines should be passed to apply - otherwise just the active states in the root machine are sent. AppliedDeep bool // If true only this number of states can be active at a time. Once a queue of possible active states is determined from transitions // only the states that can fit will be added to the active states. ActiveMax int // If non-nil and there's an active max this will sort the states in the queue to prefer the ones at the top. ActivePriority func(a State[Subject, Input, StateData, TransitionData, Effect], b State[Subject, Input, StateData, TransitionData, Effect]) bool // If true all states in the machine are always active and no transition logic is done. Only state updating. ActiveFully bool // If true when active states are removed - should the current order of the active states be preserved or does order not matter? ActiveOrdered bool // If true after transitions are evaluated in an update - should we add any to the active states so they are available in the apply? // If not the active queue will be processed at the start of the next update. ProcessQueueImmediately bool // Timing for when (if at all) a sub machine should be initialized (active states be determined). InitializeSub InitializeSub }
The logic and options used by a state machine to process states. This allows for fuzzy and finite machines to coexist and also provides prioritized multiple state reduction for both finite and fuzzy machines. A state has data or it's own sub machine.
type Machine ¶
type Machine[Subject any, Input any, StateData any, TransitionData any, Effect any] struct { Definition *MachineDefinition[Subject, Input, StateData, TransitionData, Effect] Active id.DenseMap[State[Subject, Input, StateData, TransitionData, Effect], uint16, uint8] ActiveQueue []State[Subject, Input, StateData, TransitionData, Effect] Applicable []State[Subject, Input, StateData, TransitionData, Effect] }
func NewMachine ¶
func (*Machine[Subject, Input, StateData, TransitionData, Effect]) Apply ¶
func (m *Machine[Subject, Input, StateData, TransitionData, Effect]) Apply(subject *Subject)
func (*Machine[Subject, Input, StateData, TransitionData, Effect]) Init ¶
func (m *Machine[Subject, Input, StateData, TransitionData, Effect]) Init(subject *Subject, input Input)
func (*Machine[Subject, Input, StateData, TransitionData, Effect]) ProcessQueue ¶
func (m *Machine[Subject, Input, StateData, TransitionData, Effect]) ProcessQueue(subject *Subject, input Input)
func (*Machine[Subject, Input, StateData, TransitionData, Effect]) Transitions ¶
func (m *Machine[Subject, Input, StateData, TransitionData, Effect]) Transitions(transitions []Transition[Input, TransitionData], subject *Subject, input Input, onlyLive bool, outro *State[Subject, Input, StateData, TransitionData, Effect]) int
func (*Machine[Subject, Input, StateData, TransitionData, Effect]) Update ¶
func (m *Machine[Subject, Input, StateData, TransitionData, Effect]) Update(subject *Subject, input Input)
func (*Machine[Subject, Input, StateData, TransitionData, Effect]) UpdateActive ¶
func (m *Machine[Subject, Input, StateData, TransitionData, Effect]) UpdateActive(subject *Subject, input Input)
type MachineDefinition ¶
type MachineDefinition[Subject any, Input any, StateData any, TransitionData any, Effect any] struct { Logic Logic[Subject, Input, StateData, TransitionData, Effect] States id.DenseMap[StateDefinition[Subject, Input, StateData, TransitionData, Effect], uint16, uint8] Transitions []Transition[Input, TransitionData] }
A machine definition describes the logic, states, and transitions for a machine.
func NewMachineDefinition ¶
func (MachineDefinition[Subject, Input, StateData, TransitionData, Effect]) AddState ¶
func (md MachineDefinition[Subject, Input, StateData, TransitionData, Effect]) AddState(s StateDefinition[Subject, Input, StateData, TransitionData, Effect])
Add the state to the definition.
func (MachineDefinition[Subject, Input, StateData, TransitionData, Effect]) AddTransition ¶
func (md MachineDefinition[Subject, Input, StateData, TransitionData, Effect]) AddTransition(t Transition[Input, TransitionData])
Add the transition to the definition. This should only be done after the states are added, otherwise it will panic.
type State ¶
type State[Subject any, Input any, StateData any, TransitionData any, Effect any] struct { Definition *StateDefinition[Subject, Input, StateData, TransitionData, Effect] Effect Effect Sub *Machine[Subject, Input, StateData, TransitionData, Effect] }
type StateDefinition ¶
type StateDefinition[Subject any, Input any, StateData any, TransitionData any, Effect any] struct { ID id.Identifier Data StateData EffectGet func(Input) Effect EffectConstant Effect EffectLive bool Transitions []Transition[Input, TransitionData] Sub *MachineDefinition[Subject, Input, StateData, TransitionData, Effect] }
A state definition is identifiable, has data, may have a way to compute it's effect (or has a constant effect), has transitions out of it, and possibly a sub-machine.
func (StateDefinition[Subject, Input, StateData, TransitionData, Effect]) GetEffect ¶
func (d StateDefinition[Subject, Input, StateData, TransitionData, Effect]) GetEffect(input Input) Effect
func (StateDefinition[Subject, Input, StateData, TransitionData, Effect]) IsEffectLive ¶
func (d StateDefinition[Subject, Input, StateData, TransitionData, Effect]) IsEffectLive() bool
type Transition ¶
type Transition[Input any, TransitionData any] struct { // The start state if any. Start id.Identifier // The ending state. End id.Identifier // The condition that determines whether we should take this transition. // This is optional and when not specified it's considered to be true. Condition func(Input) bool // If the transition should be evaluated on each update. Live bool // The data associated with this transition - used for applying it to the subject. Data TransitionData }
A transition in a state machine has an ending and optionally a start. A transition without a start is considered global and exists on the machine level (as opposed to state) and is possibly evaluated on init or when the machine has no active or queued states. A transition can be live or dormant. A dormant transition is only evaluated when a state is finished or a machine is empty while a live transition is evaluated on each update.
func (Transition[Input, TransitionData]) IsReady ¶
func (t Transition[Input, TransitionData]) IsReady(input Input) bool
Based on the input - should the transition be taken?
type UserData ¶
type UserData struct {
// contains filtered or unexported fields
}
func NewUserData ¶
func NewUserData(props ...UserDataPropertyBase) UserData
type UserDataPropertyBase ¶
type UserDataPropertyTyped ¶
type UserDataPropertyTyped[V any] interface { UserDataPropertyBase Get(data UserData) V Set(data UserData, value V) }
func Bool ¶
func Bool(defaultValue bool) UserDataPropertyTyped[bool]
func Float ¶
func Float(defaultValue float32) UserDataPropertyTyped[float32]