Created
August 14, 2026 21:38
-
-
Save Frityet/d75aa8dff0341ffd74897e82a0cb69be to your computer and use it in GitHub Desktop.
FSM.tl
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| -- fsm.tl | |
| ------------------------------------------------------------ | |
| -- Common state interface | |
| ------------------------------------------------------------ | |
| local interface State | |
| tag: string | |
| end | |
| ------------------------------------------------------------ | |
| -- Concrete states | |
| -- | |
| -- `where` tells Teal how to distinguish each record | |
| -- at runtime when using `is`. | |
| ------------------------------------------------------------ | |
| local record Idle is State | |
| where self.tag == "idle" | |
| end | |
| local record Running is State | |
| where self.tag == "running" | |
| speed: number | |
| end | |
| local record Jumping is State | |
| where self.tag == "jumping" | |
| velocity_y: number | |
| end | |
| ------------------------------------------------------------ | |
| -- The actual state type | |
| -- | |
| -- Not an enum. | |
| -- A state is one of three completely different records. | |
| ------------------------------------------------------------ | |
| local type PlayerState = | |
| Idle | |
| | Running | |
| | Jumping | |
| ------------------------------------------------------------ | |
| -- Input to our state machine | |
| ------------------------------------------------------------ | |
| local record Input | |
| move: boolean | |
| jump: boolean | |
| landed: boolean | |
| end | |
| ------------------------------------------------------------ | |
| -- Constructors | |
| ------------------------------------------------------------ | |
| local function idle(): Idle | |
| return { | |
| tag = "idle", | |
| } | |
| end | |
| local function running(speed: number): Running | |
| return { | |
| tag = "running", | |
| speed = speed, | |
| } | |
| end | |
| local function jumping(velocity_y: number): Jumping | |
| return { | |
| tag = "jumping", | |
| velocity_y = velocity_y, | |
| } | |
| end | |
| ------------------------------------------------------------ | |
| -- Transition function | |
| ------------------------------------------------------------ | |
| local function step( | |
| state: PlayerState, | |
| input: Input | |
| ): PlayerState | |
| if state is Idle then | |
| -- state is statically known to be Idle here | |
| if input.jump then | |
| return jumping(12) | |
| end | |
| if input.move then | |
| return running(5) | |
| end | |
| return state | |
| elseif state is Running then | |
| -- state is statically Running here. | |
| -- Therefore Running-only data is available: | |
| print("running at", state.speed) | |
| if input.jump then | |
| return jumping(12) | |
| end | |
| if not input.move then | |
| return idle() | |
| end | |
| return state | |
| elseif state is Jumping then | |
| -- state is statically Jumping here. | |
| print("vertical velocity:", state.velocity_y) | |
| if input.landed then | |
| return idle() | |
| end | |
| return state | |
| end | |
| return state | |
| end | |
| ------------------------------------------------------------ | |
| -- Display helper | |
| ------------------------------------------------------------ | |
| local function show(state: PlayerState) | |
| if state is Idle then | |
| print("IDLE") | |
| elseif state is Running then | |
| print("RUNNING, speed =", state.speed) | |
| elseif state is Jumping then | |
| print("JUMPING, vy =", state.velocity_y) | |
| end | |
| end | |
| ------------------------------------------------------------ | |
| -- Run it | |
| ------------------------------------------------------------ | |
| local state: PlayerState = idle() | |
| show(state) | |
| -- IDLE | |
| state = step(state, { | |
| move = true, | |
| jump = false, | |
| landed = false, | |
| }) | |
| show(state) | |
| -- RUNNING, speed = 5 | |
| state = step(state, { | |
| move = true, | |
| jump = true, | |
| landed = false, | |
| }) | |
| show(state) | |
| -- JUMPING, vy = 12 | |
| state = step(state, { | |
| move = false, | |
| jump = false, | |
| landed = true, | |
| }) | |
| show(state) | |
| -- IDLE |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment