Last active
June 27, 2022 17:47
-
-
Save cjohnson318/0c417a81f3fa95ef17b73642dc0d10eb to your computer and use it in GitHub Desktop.
Simple Finite State Machine in Kotlin
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
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