-
Notifications
You must be signed in to change notification settings - Fork 1
Utilizing Automata4j
pavl_g edited this page Aug 15, 2023
·
19 revisions
This page describes the use of Automata4j to implement both forms of Finite-State-Automata, the Non-deterministic, and Deterministic patterns.
- Create a Gradle project and add the dependencies.
- The non-deterministic pattern (NDFSA).
- The deterministic pattern (DFSA).
- Recovering from a
TransitionPathNotUniqueException
. - Injecting an NDFSA path into the DFSA pattern.
- More examples.
# initialize a gradle project and build
$ gradle init # select Java and JUnit
$ ./gradlew build
// add the Gradle dependency in build.gradle file
plugins {
id 'application'
}
application {
mainClass.set("com.example.Main")
}
repositories {
// Use Maven Central for resolving dependencies.
mavenCentral()
}
dependencies {
implementation "io.github.software-hardware-codesign:automata4j:1.0.0-alpha"
}
# download the binaries and re-build the project
$ ./gradlew build
- Recall, a SpaceShip traveling from point A to point B with speed [x].
- Recall, state E is an Engine Start state, and state AB is a travel state traveling from point A to point B.
- Recall, the path marked from state E to state AB is the transition path with an input value [x] representing the speed of the SpaceShip.
- An NDFSA pattern can have multiple start states and doesn't assert the uniqueness of a transition path, which means a transition path from state E to state AB with an input value [x] may repeat without any runtime consequences.
- In this case, if a transition path is defined from state E back to state AB with speed [x], the SpaceShip can repeat the previous transition path without any runtime consequences.
To implement this in a Game Loop using an NDFSA pattern, the following implementation routines are available:
- Use a
Transition
object with a Next State assigned as the engine start state (state E), and aTransitionalListener
to assign the concurrent travel state (state AB) when the transition to the start state has been completed.
/* define an NDFSA manager object */
final TransitionalManager transitionalManager = new TransitionalManager();
/* the input value is of type Speed and the tracer object is of type Vector3f
representing the location of the space ship after traveling the required distance */
/* the engine start state */
final AutoState<Speed, Vector3f> stateE = new EngineStart<>();
/* travels from point A back to point B*/
final AutoState<Speed, Vector3f> stateAB = new TravelPath<>();
/* Assigns the engine start state */
transitionalManager.assignNextState(stateE);
/* Use the NDFSA transitional manager to traverse through the states with pre-defined input values */
transitionalManager.transit(new TransitionalListener<Speed, Vector3f>() {
@Override
public void onTransition(AutoState<Speed, Vector3f> presentState) {
/* assigns the next state and starts the transition */
transitionalManager.assignNextState(stateAB);
/* transits to stateAB without dispatching an event on finishing transition */
transitionalManager.transit(null);
}
});
- Use a
TransitionPath
object with 2 AutoStates, a present state, and a next state, theTransitionalManager
assigns and transits to the present state and then assigns the next state, but it will never transit to the next state unless an explicit dispatch to the transit function is invoked in a registeredTransitionalListener
object.
/* define an NDFSA manager object */
final TransitionalManager transitionalManager = new TransitionalManager();
/* the input value is of type Speed and the tracer object is of type Vector3f
representing the location of the space ship after traveling the required distance */
/* the engine start state */
final AutoState<Speed, Vector3f> stateE = new EngineStart<>();
/* travels from point A back to point B*/
final AutoState<Speed, Vector3f> stateAB = new TravelPath<>();
/* Creates a transition path from stateE to stateAB */
final TransitionPath<Speed, Vector3f> transitionPath =
new TransitionPath<>("EngineStart-->StateAB",
stateE, stateAB);
/* Use the NDFSA transitional manager to traverse through the states with pre-defined input values */
transitionalManager.transit(transitionPath, new TransitionalListener<Speed, Vector3f>() {
@Override
public void onTransition(AutoState<Speed, Vector3f> presentState) {
/* transits to stateAB without dispatching an event on finishing transition */
transitionalManager.transit(null);
/* the previous transition path can repeat again since this pattern is an NDFSA */
}
});
- Recall, that the SpaceShip from the previous example is not capable of repeating the transition path that starts by starting the engine and then commands the SpaceShip to travel from point A to point B (in the context of if point B is for example a new planet or a new Galaxy).
- A DeterministicManager comes into play, in which the path "EngineStart-->StateAB" is asserted to be unique and not repeat.
/* define a DFSA manager object */
final TransitionalManager transitionalManager = new DeterministicManager();
/* the input value is of type Speed and the tracer object is of type Vector3f
representing the location of the space ship after traveling the required distance */
/* the engine start state */
final AutoState<Speed, Vector3f> stateE = new EngineStart<>();
/* travels from point A back to point B*/
final AutoState<Speed, Vector3f> stateAB = new TravelPath<>();
/* Creates a transition path from stateE to stateAB */
final TransitionPath<Speed, Vector3f> transitionPath =
new TransitionPath<>("EngineStart-->StateAB",
stateE, stateAB);
/* Use the NDFSA transitional manager to traverse through the states with pre-defined input values */
transitionalManager.transit(transitionPath, new TransitionalListener<Speed, Vector3f>() {
@Override
public void onTransition(AutoState<Speed, Vector3f> presentState) {
/* transits to stateAB without dispatching an event on finishing transition */
transitionalManager.transit(null);
/* subsequent dispatch using this transition path will result in a TransitionPathNotUniqueException */
}
});
- Catching and recovering from a
TransitionPathNotUniqueException
is one of the keys to best practices of using a DFSA pattern. - It's your choice of the type of exception recovery you choose, simple implementations can recover that by displaying a GUI dialog or a user message telling the user that this action (of utilizing this transition path again) is prohibited.
- Other advanced implementations can recover this exception by suggesting other transition paths for the SpaceShip.
- Other implementations will internally increment a counter for the repeated transition paths and recover from this exception by changing the name of the transition path and re-using it (See DFSA Hacking below).
- This snippet shows an example of recovering by using other paths:
transitionalManager.transit(transitionPath, new TransitionalListener<Speed, Vector3f>() {
@Override
public void onTransition(AutoState<Speed, Vector3f> stateE) {
/* transits to stateAB without dispatching an event on finishing transition */
try {
transitionalManager.transit((stateAB) ->
transitionalManager.transit(transitionPath, null));
} catch (TransitionPathNotUniqueException e) {
Logger.getLogger("Suggesting other paths").fine("Using other paths");
final AutoState<Speed, Vector3f> stateBA = new TravelPath<>();
/* reallocates a new chunk for the new transition path */
/* This new transition path achieves the same previous transition path goals
which is to travel from point A to point B*/
/* This new transition path commands the SpaceShip to travel from
point B back to point A, then from point A again to point B*/
transitionPath = new TransitionPath(transitionPath.getName(), stateBA, stateAB);
transitionalManager.transit(transitionPath,
(localStateBA) -> transitionalManager.transit(null));
}
}
});
- There are 2 known ways (or at least hacks!) that could break the TransitionPath uniqueness assertion in a DFSA pattern:
- Using a Transition object instead and simulating a transition path, repeating the same path again.
- Changing the transition path object name using
TransitionPath#setName(String)
before transiting using this path will bypass the assertion of uniqueness.- By bypassing the assertion of transition paths' uniqueness, the DFSA pattern can now have some NDFSA transition patterns, hence a hybrid Finite-State-Automaton pattern is created.
// hacking the DFSA pattern!
transitionalManager.transit(transitionPath, new TransitionalListener<Speed, Vector3f>() {
@Override
public void onTransition(AutoState<Speed, Vector3f> stateE) {
/* transits to stateAB without dispatching an event on finishing transition */
transitionalManager.transit((stateAB) -> {
transitionPath.setName(transitionPath.getName() + "*");
/* runtime consequences have been hypnotized */
transitionalManager.transit(transitionPath, null);
});
}
});