Finite State Machines (FSM)
A finite-state machine (FSM) is a model of computation. Every state machine is comprised of its states and transitions.
Example implementation in C
enum game_transition_e { GAME_TRANSITION_NONE, GAME_TRANSITION_TO_MENU, GAME_TRANSITION_TO_GAME, GAME_TRANSITION_EXIT }; struct game_transition_t { game_transition_e type; }; enum game_state_e { GAME_STATE_MENU, GAME_STATE_INGAME, GAME_STATE_EXIT }; struct game_state_t { game_state_e type; }; #include <stdio.h> int main() { game_state_t game_state; game_state.type = GAME_STATE_MENU; game_transition_t game_transition; game_transition.type = GAME_TRANSITION_NONE; int game_running = 1; while (game_running) { // detect transition game_transition.type = GAME_TRANSITION_NONE; char input = getchar(); switch (input) { case 'x': game_transition.type = GAME_TRANSITION_EXIT; break; case 'm': game_transition.type = GAME_TRANSITION_TO_MENU; break; } switch (game_state.type) { case GAME_STATE_MENU: { switch (game_transition.type) { case GAME_TRANSITION_EXIT: { game_state.type = GAME_STATE_EXIT; } break; case GAME_TRANSITION_NONE: default: { } break; } } break; case GAME_STATE_INGAME: { } break; case GAME_STATE_EXIT: { game_running = 0; } break; } // log printf("TRANSITION: %u\n", game_transition.type); printf("STATE: %u\n", game_state.type); } };
Implementation using X-Macros in C
Using X-Macros, one can define states and transitions in a centralized place and define default state transition (plus offer strings for all enums).
/* NOTE: for this example, game states and transitions are identical */ #define GAME_STATES(X) \ X(NONE) \ X(MENU) \ X(INGAME) \ X(EXIT) typedef enum game_transition_e { #define TRANSITION_ENUM(state, ...) GAME_TRANSITION_TO_##state, GAME_STATES(TRANSITION_ENUM) GAME_TRANSITION_COUNT } game_transition_e; const char* game_transition_strings[GAME_TRANSITION_COUNT] = { #define TRANSITION_ENUM_STRING(state, ...) #state, GAME_STATES(TRANSITION_ENUM_STRING) }; typedef enum game_state_e { #define STATE_ENUM(state, ...) GAME_STATE_##state, GAME_STATES(STATE_ENUM) GAME_STATE_COUNT } game_state_e; const char* game_state_strings[GAME_STATE_COUNT] = { #define STATE_ENUM_STRING(state, ...) #state, GAME_STATES(STATE_ENUM_STRING) }; #define DEFAULT_TRANSITION(state, ...) if (game_transition.type == GAME_TRANSITION_TO_##state) { game_state.type = GAME_STATE_##state; } typedef struct game_state_t { game_state_e type; } game_state_t; typedef struct game_transition_t { game_transition_e type; } game_transition_t; #include <stdio.h> #include <unistd.h> int main() { game_state_t game_state; game_state.type = GAME_STATE_MENU; game_transition_t game_transition; game_transition.type = GAME_TRANSITION_TO_NONE; int game_running = 1; while (game_running) { /* log */ printf("STATE: %s\n", game_state_strings[game_state.type]); printf("TRANSITION: %s\n", game_transition_strings[game_transition.type]); /* detect transition */ game_transition.type = GAME_TRANSITION_TO_NONE; char input = getchar(); getchar(); /* consume newline */ switch (input) { case 'x': game_transition.type = GAME_TRANSITION_TO_EXIT; break; case 'm': game_transition.type = GAME_TRANSITION_TO_MENU; break; case 'g': game_transition.type = GAME_TRANSITION_TO_INGAME; break; } switch_again: switch (game_state.type) { case GAME_STATE_MENU: { switch (game_transition.type) { case GAME_TRANSITION_TO_EXIT: { game_state.type = GAME_STATE_EXIT; goto switch_again; /* avoid one frame of delay */ } break; case GAME_TRANSITION_TO_NONE: { } break; default: { GAME_STATES(DEFAULT_TRANSITION) } break; } } break; case GAME_STATE_INGAME: { switch (game_transition.type) { case GAME_TRANSITION_TO_EXIT: { game_state.type = GAME_STATE_MENU; /* go to menu before exiting */ } break; case GAME_TRANSITION_TO_NONE: { } break; default: { GAME_STATES(DEFAULT_TRANSITION) } break; } } break; case GAME_STATE_EXIT: { game_running = 0; } break; } } };