Skip to content

Instantly share code, notes, and snippets.

@cjohnson318
Last active June 27, 2022 17:47
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cjohnson318/0c417a81f3fa95ef17b73642dc0d10eb to your computer and use it in GitHub Desktop.
Save cjohnson318/0c417a81f3fa95ef17b73642dc0d10eb to your computer and use it in GitHub Desktop.
Simple Finite State Machine in Kotlin
import kotlin.test.assertEquals
data class State(val state: String)
data class Event(val event: String)
data class CurrentStateEvent(val state: State, val event: Event)
class TransitionTable {
private var stateTable = mutableMapOf<CurrentStateEvent, State>()
private var actionTable = mutableMapOf<CurrentStateEvent, () -> Unit>()
fun addTransition(current: State, event: Event, next: State) {
val currentStateEvent = CurrentStateEvent(current, event)
stateTable[currentStateEvent] = next
}
fun addActionTransition(current: State, event: Event, next: State, action: () -> Unit) {
val currentStateEvent = CurrentStateEvent(current, event)
stateTable[currentStateEvent] = next
actionTable[currentStateEvent] = action
}
fun getNextState(currentStateEvent: CurrentStateEvent): State? {
return when (currentStateEvent) {
in stateTable -> {
actionTable.get(currentStateEvent)?.invoke()
stateTable[currentStateEvent]
}
else -> null
}
}
fun getStates(): List<State> {
var result = mutableSetOf<State>()
stateTable.filterKeys { result.add(it.state) }
stateTable.filterValues { result.add(it) }
return result.toList()
}
fun getEvents(): List<Event> {
var result = mutableSetOf<Event>()
stateTable.filterKeys { result.add(it.event) }
return result.toList()
}
}
class FiniteStateMachine constructor(initial: State, val transitionTable: TransitionTable, val accepting: List<State>) {
var state = initial
private val error = State("ERROR")
fun send(event: Event): Boolean {
val currentStateEvent = CurrentStateEvent(state, event)
val nextState = transitionTable.getNextState(currentStateEvent) ?: error
println("Received $event. Transitioning from $state to $nextState")
state = nextState
if (state in accepting) {
return true
}
return false
}
fun send(events: List<Event>): Boolean {
var done: Boolean
for (event in events) {
done = send(event)
if (done) {
return true
}
}
return false
}
}
fun main(args: Array<String>) {
val locked = State("locked")
val unlocked = State("unlocked")
val addCoin = Event("add-coin")
val pushStile = Event("push-stile")
val addCoinAction = {
println("> COIN ADDED CALLBACK")
}
val pushStileAction = {
println("> PUSH STILE CALLBACK")
}
val table = TransitionTable()
table.addActionTransition(locked, addCoin, unlocked, addCoinAction)
table.addTransition(locked, pushStile, locked)
table.addTransition(unlocked, addCoin, unlocked)
table.addActionTransition(unlocked, pushStile, locked, pushStileAction)
val fsm = FiniteStateMachine(locked, table, listOf(unlocked))
val events = listOf(addCoin, pushStile)
val accepted = fsm.send(events)
if (accepted) {
println("In accepting state ${fsm.state}")
} else {
println("Not in accepting state ${fsm.state}")
}
println(fsm.transitionTable.getStates())
println(fsm.transitionTable.getEvents())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment