🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: comp.lang.c.moderated
Article #2446

Re: State engine

#2446
From: Kazimir Kylheku
Date: Mon, 01 Apr 1996 13:19
178 lines
4386 bytes
In article <4jmcoa$lri@solutions.solon.com>,
terres  <terres@servidor.dgsca.unam.mx> wrote:
>Hello!
>
>I think that when you have complex problem like many conditions, paths 
>and cases, a traditional program could be to big and inneficient on 
>execution.
>
>I have seen a system that uses a state engine that triggers some pieces 
>of code when an encounters take effect between: states & stimuli.
>
>Like this:
>        Stimuli
>State | 1  2  3
>1     |           
>2     |
>3     |
>
>When some conditions are acomplished some specific pieces of code are 
>executed and that pieces send the control to another crosspoint.
>
>I want to know how to implement this idea in C.

There are all kinds of ways. One is to simply have nested switch statements:

	switch (state) {
	case 1:
		switch(stimulus) {
			case 1:
				do_something();
				state = 2;
				break;
			case 2:
				do_something_else();
				state = 3;
				break;
			case 3:
				break;
		}
		break;
	case 2:
		break;
	case 3:
		break;
	default:
		/* shouldn't reach this	*/
		break;
	}

You fill in the missing bits from the table. If you move a lot of the work to
functions, you can keep the switch statement manageable.

Another way is to assing to each state/input combination a function. The
functions don't have to be unique. For instance, if you have a lot of
state/input pairs that do nothing, you can assign to them a function called
idle(). To each state/input combination, you also assign a number which tells
it what state to go into next after the action is called:

enum states {
	IDLE, HUNGRY, SLEEPING, IRRITATED, NUMSTATES
};

enum inputs {
	FOOD, BELL, NEW_AGE_MUSIC, TIMER, NUMINPUTS
};

typedef void (* action_fun)(void);

struct actiongoto {
	action_fun action;
	int go;
};

void idle(void);
void eat(void);
void salivate(void);
void growl(void);

static struct actiongoto statetable[NUMSTATES][NUMINPUTS] =
{
	{	/* IDLE state	*/
		{ idle, IDLE },		/* FOOD input		*/
		{ idle, IDLE },		/* BELL input		*/
		{ idle, SLEEPING },	/* NEW_AGE_MUSIC input	*/
		{ idle, HUNGRY },	/* TIMER input		*/
	},
	{	/* HUNGRY state	*/
		{ eat, IDLE },		/* eat FOOD, be idle	*/
		{ salivate, HUNGRY },	/* salivate on BELL	*/
		{ growl, IRRITATED },	/* growl on music	*/
		{ idle, HUNGRY },	/* stay hungry on timer	*/
	},	/* SLEEPING state */
	{
		{ idle, SLEEPING },
		{ idle, IDLE },		/* awake on bell	*/
		{ idle, SLEEPING },	/* stay asleep w/ music	*/
		{ idle, SLEEPING },
	},	/* IRRITATED state */
	{
		{ eat, IDLE },		/* soothing food	*/
		{ growl, IRRITATED },	/* bell is irritating	*/
		{ growl, IRRITATED },	/* so is new age music	*/
		{ idle, HUNGRY },	/* irritation timeout	*/
	}
};

To use the state table, you look at what state you are in, and what the input
is, and lookup the appropriate actiongoto structure in the two-dimensional
table. You invoke the given function pointer, and advance to the given state.

Assuming you start in the IDLE state, and a TIMER input happens, you call
function idle() and advance to the HUNGRY state. Then, when a BELL input
arrives, you salivate() and stay in the HUNGRY state, and so forth:

#include <stdio.h>

unsigned int getnextinput(void)

{
	unsigned int input;
	char *menu = 
		"Enter input\n\n"
		"0. FOOD\n"
		"1. BELL\n"
		"2. NEW_AGE_MUSIC\n"
		"3. TIMER\n";

	puts(menu);
	do {
		fputs(">",stdout);
		scanf("%u",&input);
	} while (input >= NUMINPUTS);

	return input;
}

int main()

{
	unsigned int input;
	unsigned int state = 0;

	while(1) {
		input = getnextinput();
		statetable[state][input].action();
		state = statetable[state][input].go;
	}

	return 0;
}

void eat(void)

{
	puts("Munch munch!");
}

void salivate(void)

{
	puts("drool!");
}

void growl(void)

{
	puts("grr!");
}

void idle(void)

{
	puts("yawn!");
}



-- 
Technical Details
Message-ID: <4jol5l$1qe@solutions.solon.com>
Path: arc1-ca.pugleaf.net!solon.com!not-for-mail
References: <4jmcoa$lri@solutions.solon.com>

Show Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!solon.com!not-for-mail
From: c2a192@ugrad.cs.ubc.ca (Kazimir Kylheku)
Newsgroups: comp.lang.c.moderated
Subject: Re: State engine
Date: 1 Apr 1996 07:19:49 -0600
Organization: Computer Science, University of B.C., Vancouver, B.C., Canada
Sender: clc@solutions.solon.com
Approved: clc@solutions.solon.com
Message-ID: <4jol5l$1qe@solutions.solon.com>
References: <4jmcoa$lri@solutions.solon.com>
NNTP-Posting-Host: solutions.solon.com