return to first page linux journal archive
keywordscontents
/*  xgtsim.c -- An XForms Game Theory Simulator
	On a "standard" Linux system, this program can be
	compiled with the command:
	gcc -lX11 -lforms -lm xgtsim.c -o xgtsim
	If that gives you problems, you are probably using a
	RedHat system. In that case, try:
	gcc -lforms -L/usr/X11R6/lib -lXpm -lX11 -lm xgtsim.c -o xgtsim
*/
/*      We need a few include files, the most important of
	which is forms.h (which declares all the functions
	available in libforms)
*/
#include <forms.h>	/* XForms include file			*/
#include <stdlib.h>	/* Standard C stuff			*/
#include <time.h>	/* Need time to seed for random numbers	*/

/*	 A few definitions
*/
#define		NUMB_TYPES	2	/* Two types of players */
#define		NUMB_PLAYERS	20	/* You can change this  
					   parameter to 1 if you 
					   want just one player 
					   of each type 	*/
#define		NUMB_ACTIONS	2	/* Two possible actions */
#define		NUMB_STATES	10	/* You can change this if
					   you want to allow more
					   complex (or simpler)
					   players		*/
#define		ACTION_A	0	/* Value to represent A	*/
#define		ACTION_B	1	/* Value to represent B	*/

/*	 Some global forms variables for the windows we'll be using
*/
FL_FORM		*main_window;
FL_FORM		*player_window;
FL_FORM		*payoff_window;
FL_FORM		*run_window;

/*	We also need to make some forms objects global,
	since they are accessed by different functions
*/
FL_OBJECT	*action_choices[NUMB_STATES];
FL_OBJECT	*transition_inputs[NUMB_ACTIONS][NUMB_STATES];
FL_OBJECT	*payoff_inputs[NUMB_ACTIONS][NUMB_ACTIONS][NUMB_TYPES];
FL_OBJECT	*go_button;
FL_OBJECT	*stop_button;
FL_OBJECT	*column_chart;
FL_OBJECT	*row_chart;
FL_OBJECT	*column_browser;
FL_OBJECT	*row_browser;

/*	Here are a few normal variables to store information
	about players, payoffs, and other parameters
*/
float	payoffs[NUMB_ACTIONS][NUMB_ACTIONS][NUMB_TYPES];
int 	state_actions[NUMB_TYPES][NUMB_STATES];
int	state_transitions[NUMB_TYPES][NUMB_ACTIONS][NUMB_STATES];
int	row_or_column;
int	numb_iterations;
int	abort_flag;

/*	This routine sets some startup values
*/
void set_defaults()
{
	int i,j,k;
	time_t curtime;
	int seedval;
	/*  (Pseudo-)randomly seed the (pseudo-)random number generator
	*/
	curtime = time(NULL);
	seedval = curtime;
	srand(seedval);
	/*
	This variable tells us whether changes in the player window
	should affect the column players or the row players
	Default is to edit column players
	*/
	row_or_column = 0;
	/*
	We'll do 1000 iterations of the game by default
	*/
	numb_iterations = 1000;
	/*
	Set Random Payoffs
	*/
	for(i = 0; i < NUMB_ACTIONS; i++)
	{
		for(j=0; j < NUMB_ACTIONS; j++)
		{
			for(k = 0; k < NUMB_TYPES; k++)
			{
			   payoffs[i][j][k] = (rand() % 1000) / 1000.0;
			}
		} 
	}
	/*   Set random strategies and simple transitions
	*/
	for(i = 0; i < NUMB_TYPES; i++)
	{
		for(j = 0; j < NUMB_STATES; j++)
		{
			k = rand() % 1000;
			if(k < 500) state_actions[i][j] = ACTION_A;
			else state_actions[i][j] = ACTION_B;
			for(k = 0; k < NUMB_ACTIONS; k++)
			{
			    state_transitions[i][k][j] = j+2;
			    if(state_transitions[i][k][j] > NUMB_STATES)
				state_transitions[i][k][j] = 1;
			}
		}
	}
}
/*	The function set_player_values() is called whenever
	the user changes anything in the player window. Note
	that variable row_or_column is already set, so we
	only change the relevant values
*/
void set_player_values(FL_OBJECT *obj, long argument)
{
	int i;
	char a_string[10];
	for(i = 0; i < NUMB_STATES; i++)
	{
		if(fl_get_choice(action_choices[i]) == 1) 
			state_actions[row_or_column][i] = ACTION_A;
		else
			state_actions[row_or_column][i] = ACTION_B;
			istate_transitions[row_or_column][0][i] = 
			    atoi(fl_get_input(transition_inputs[0][i]));
		/*
			Here we check to make sure the user did not
			specify a state value which is less than 1
			or greater than NUMB_STATES
		*/ 
		if(state_transitions[row_or_column][0][i] < 1)
		{
			state_transitions[row_or_column][0][i] = 1;
			sprintf(a_string, "1");
			fl_set_input(transition_inputs[0][i], a_string);
		}
		if(state_transitions[row_or_column][0][i] > NUMB_STATES)
		{
		    state_transitions[row_or_column][0][i] = NUMB_STATES;
		    sprintf(a_string, "%d", NUMB_STATES);
		    fl_set_input(transition_inputs[0][i], a_string);
		}
		state_transitions[row_or_column][1][i] = 
			atoi(fl_get_input(transition_inputs[1][i]));

		if(state_transitions[row_or_column][1][i] < 1)
		{
			state_transitions[row_or_column][1][i] = 1;
			sprintf(a_string, "1");
			fl_set_input(transition_inputs[1][i], a_string);
		}
		if(state_transitions[row_or_column][1][i] > NUMB_STATES)
		{
			state_transitions[row_or_column][1][i] = NUMB_STATES;
			sprintf(a_string, "%d", NUMB_STATES);
			fl_set_input(transition_inputs[1][i], a_string);
		}		
	}
}
/*	Here we read the values from the payoffs window
	into the payoffs array whenever a value is edited
	by the user
*/
void set_payoffs(FL_OBJECT *obj, long argument)
{
	int i,j,k;
	for(i = 0; i < NUMB_ACTIONS; i++)
	{
		for(j = 0; j < NUMB_ACTIONS; j++)
		{
			for(k = 0; k < NUMB_TYPES; k++)
			{
				payoffs[i][j][k] =
				atof(fl_get_input(payoff_inputs[i][j][k]));
			} 
		}
	}
}
/*	This changes the number of iterations
*/
void set_iterations(FL_OBJECT *obj, long argument)
{
	numb_iterations = fl_get_counter_value(obj);
}
/*	Whenever a button is pushed on the main window, this
	routine is called to make the relevant window appear
*/
void display_forms(FL_OBJECT *obj, long which_form)
{
	if(which_form == 1)
		fl_show_form(player_window,FL_PLACE_FREE,FL_FULLBORDER,
			"Player Design");
	if(which_form == 2)
		fl_show_form(payoff_window,FL_PLACE_FREE,FL_FULLBORDER,
			"Edit Payoffs");
	if(which_form == 3)
		fl_show_form(run_window,FL_PLACE_FREE,FL_FULLBORDER,
			"Play the Game");
}
/*	close_forms() tells XForms that it's OK
	to close a window when if the window
	manager receives that signal (ie. if the
	user clicked on their window manager's
	close window icon
*/
int close_forms(FL_FORM *form, void *argument)
{
	/*	We always want to let the user close windows
		If we wanted to prevent a window from being closed
		in this way, we could return FL_IGNORE instead
	*/
	return(FL_OK);
}
/*	 Are we editing row or column players in the players window?
*/
void set_row_or_column(FL_OBJECT *obj, long argument)
{
	int i;
	char a_string[10];
	/*
		Set row_or_column
	*/
	row_or_column = argument;
	/*
		Now update all the objects in the
		player window so that they show
		correct values
	*/
	for(i = 0; i < NUMB_STATES; i++)
	{
		if(state_actions[row_or_column][i] == ACTION_A)
			fl_set_choice_text(action_choices[i], "A");
		else 
			fl_set_choice_text(action_choices[i], "B");
			sprintf(a_string,"%d",
				state_transitions[row_or_column][0][i]);
			fl_set_input(transition_inputs[0][i], a_string)
			sprintf(a_string,"%d",
				state_transitions[row_or_column][1][i]);
			fl_set_input(transition_inputs[1][i], a_string);
	}
}
/*	Shuts down the program
*/
void quit_xgtsim(FL_OBJECT *obj, long argument)
{
	fl_finish();
	exit(0);
}
/*
	When the Stop button is pushed, we set this flag to 1.
*/
void stop_the_game(FL_OBJECT *obj, long argument)
{
	abort_flag = 1;
}
/*	This routine runs the game by matching players,
	and updates information in the run window
*/
void play_the_game(FL_OBJECT *obj, long argument)
{
	int i,j,k,l;
	float column_average, row_average;
	char a_string[256];
	double smallest_payoff, largest_payoff;
	int to_match[NUMB_PLAYERS];
	int column_action;
	int row_action;
	int which_player, players_left;
	int current_state[NUMB_TYPES][NUMB_PLAYERS];
	float current_payoff[NUMB_TYPES][NUMB_PLAYERS];
	/*
		Start all players off in a random state
	*/
	for(i = 0; i < NUMB_TYPES; i++)
	{
		for(j = 0; j < NUMB_PLAYERS; j++)
		{
			current_state[i][j] = rand() % NUMB_STATES;
		}
	}
	/*	Turn off the abort flag, then make the stop
		button appear so the user can abort the run
	*/	
	abort_flag = 0;
	fl_hide_object(go_button);
	fl_show_object(stop_button);
	/*
		Clear the charts
	*/
	fl_clear_chart(column_chart);
	fl_clear_chart(row_chart);
	/*
		Run the simulation
	*/
	for (i = 0; i < numb_iterations; i++)
	{
		/*	Each time through, we want to scale the charts
		*/
		smallest_payoff = payoffs[0][0][0];
		largest_payoff = payoffs[0][0][0];
		for(j = 0; j < NUMB_ACTIONS; j++)
		{
			for(k = 0; k < NUMB_ACTIONS; k++)
			{
				for(l = 0; l < NUMB_TYPES; l++)
				{
				   if(smallest_payoff > payoffs[j][k][l])
					smallest_payoff = payoffs[j][k][l];
				   if(largest_payoff < payoffs[j][k][l])
					largest_payoff = payoffs[j][k][l];
				}
			}
		}
		smallest_payoff = smallest_payoff - 0.05 *
			(largest_payoff - smallest_payoff);
		largest_payoff = largest_payoff + 0.05 *
			(largest_payoff - smallest_payoff);
		fl_set_chart_bounds(column_chart, smallest_payoff,
			largest_payoff);
		fl_set_chart_bounds(row_chart, smallest_payoff,
			largest_payoff);
		/*
		Now we randomly match column players against row players,
		calculate their payoffs, and change their states according
		to their transitions
		*/
		for(j = 0; j < NUMB_PLAYERS; j++)
		{
			to_match[j] = j;
		}
		column_average = 0.0;
		row_average = 0.0;
		players_left = NUMB_PLAYERS;
		for(j = 0; j < NUMB_PLAYERS; j++)
		{
		  which_player = rand() % players_left;
		  column_action = state_actions[0][current_state[0][j]];
		  row_action = state_actions[1][current_state[1]
				[to_match[which_player]]];
		  current_payoff[0][j] = payoffs[state_actions[0]
				[current_state[0][j]]]
	 	    		[state_actions[1][current_state[1]
				[to_match[which_player]]]] [0];
		  column_average = column_average + current_payoff[0][j];
		  current_payoff[1][to_match[which_player]] = 
				payoffs[state_actions[0][current_state[0][j]]]
				[state_actions[1][current_state[1]
				[to_match[which_player]]]] [1];
		  row_average = row_average + current_payoff[1]
				[to_match[which_player]];
		  current_state[0][j] = state_transitions[0] [row_action]
				[current_state[0][j]] - 1;
		  current_state[1][to_match[which_player]] = 
				state_transitions[1] [column_action]
				[current_state[1][to_match[which_player]]]
				- 1;
			for(k = which_player; k < players_left - 1; k++)
			{
				to_match[k] = to_match[k+1];
			}
			players_left--;
		}
		column_average = column_average / NUMB_PLAYERS;
		row_average = row_average / NUMB_PLAYERS;
		/*
		Here's where we update the display with the new information
		from the lastest round of the game
		*/
		fl_add_chart_value(column_chart, column_average, "", FL_RED);
		sprintf(a_string,"%f", column_average);
		fl_clear_browser(column_browser);
		fl_add_browser_line(column_browser,a_string);

		fl_add_chart_value(row_chart, row_average, "", FL_RED);
		sprintf(a_string,"%f", row_average);
		fl_clear_browser(row_browser);
		fl_add_browser_line(row_browser,a_string);
		/*
			Now call fl_check_forms() to see if any information
			has changed (in particular, was the stop button
			pushed?)
		*/
		fl_check_forms();
		if(abort_flag == 1)
		{
			i = numb_iterations;
		}
	}
	/*	We're done this run, so hide the Stop button
		and show the Go button
	*/
	fl_hide_object(stop_button);
	fl_show_object(go_button);
}
/*	This routine creates all our windows/forms and all the
	graphical elements on them
*/
void create_forms()
{
	/*	The "obj" pointer is used to create elements
		on all the various forms. We also need a
		group object to handle groupings
	*/ 
	FL_OBJECT	*obj;
	FL_OBJECT	*group;
	int 		i,j,k;
	char		a_string[10];
	/*
		The main window is used to access all
		the other windows, so we set the callbacks
		to the display_forms() function (except for Quit).
	*/
	main_window = fl_bgn_form(FL_NO_BOX, 290, 50);
		obj = fl_add_box(FL_UP_BOX,0,0,290,50,"");
		obj = fl_add_button(FL_NORMAL_BUTTON,10,10,60,30,"Players");
			fl_set_object_callback(obj, display_forms, 1);
		obj = fl_add_button(FL_NORMAL_BUTTON,80,10,60,30,"Payoffs");
			fl_set_object_callback(obj, display_forms, 2);
		obj = fl_add_button(FL_NORMAL_BUTTON,150,10,60,30,"Run");
			fl_set_object_callback(obj, display_forms, 3);
		obj = fl_add_button(FL_NORMAL_BUTTON,220,10,60,30,"Quit");
			fl_set_object_callback(obj, quit_xgtsim, 1);
	fl_end_form();
	/*
		The Player Window allows the user to set
		up player actions and strategies
	*/
	player_window = fl_bgn_form(FL_NO_BOX, 270, 100 + 20 * NUMB_STATES);
		obj = fl_add_box(FL_UP_BOX,0,0,270,100 + 20 * NUMB_STATES,"");
		obj = fl_add_box(FL_UP_BOX,10,50,250,10,"");
		/*
			This loop creates 10 text labels (one for
			each state) on the player window
		*/
		for(i = 0; i < NUMB_STATES; i++)
		{
		    sprintf(a_string,"%d", i+1);
		    obj = fl_add_text(FL_NORMAL_TEXT,20,90 + i * 20,20,20,i
				a_string);
		    fl_set_object_lalign(obj,FL_ALIGN_LEFT|FL_ALIGN_INSIDE);
		}
		/*	Now we label this column of state numbers
		*/
		obj = fl_add_text(FL_NORMAL_TEXT,10,70,40,20,"States");
		fl_set_object_lalign(obj,FL_ALIGN_LEFT|FL_ALIGN_INSIDE);
		/*
			For each state, we need a choice object to select
			the action in that state
		*/
		for(i = 0; i < NUMB_STATES; i++)
		{
   		   action_choices[i] = 
		     fl_add_choice(FL_NORMAL_CHOICE,60,90+i*20,50,20,"");
		     fl_set_object_boxtype(action_choices[i],FL_EMBOSSED_BOX);
		     fl_addto_choice(action_choices[i],"A");
		     fl_addto_choice(action_choices[i],"B");
		   if(state_actions[0][i] == ACTION_A) 
			fl_set_choice_text(action_choices[i], "A");
		   else fl_set_choice_text(action_choices[i], "B");
		        fl_set_object_callback(action_choices[i], 
				set_player_values, 0);
		}
		/* Label this column of actions
		*/
		obj = fl_add_text(FL_NORMAL_TEXT,60,70,50,20,"Actions");
		   fl_set_object_lalign(obj,FL_ALIGN_LEFT|FL_ALIGN_INSIDE);
		/*
			We also need two lists of which states to jump
			to while agents are "playing" the game
		*/
		for(i = 0; i < NUMB_STATES; i++)
		{
			transition_inputs[0][i] = fl_add_input(FL_INT_INPUT,i
				140,90 + i * 20,30,20,"");
			sprintf(a_string,"%d", 
				state_transitions[0][0][i]);
			fl_set_input(transition_inputs[0][i], a_string); 
			fl_set_object_callback(transition_inputs[0][i], 
				set_player_values, 0);
			transition_inputs[1][i] = fl_add_input(FL_INT_INPUT,
				210,90 + i * 20,30,20,"");
			sprintf(a_string,"%d", 
				state_transitions[0][1][i]);
			fl_set_input(transition_inputs[1][i], a_string); 
			fl_set_object_callback(transition_inputs[1][i],
				set_player_values, 0);
		}
		obj = fl_add_text(FL_NORMAL_TEXT,120,70,70,20,
			"A Transitions");
			fl_set_object_lalign(obj,
				FL_ALIGN_LEFT|FL_ALIGN_INSIDE);
		obj = fl_add_text(FL_NORMAL_TEXT,190,70,70,20,
			"B Transitions");
			 fl_set_object_lalign(obj,
				FL_ALIGN_LEFT|FL_ALIGN_INSIDE);
		/*
		Finally (for this form) we need two buttons to choose
		between column players and row players. Note that we set
		the button type to FL_RADIO button so that XForms will
		ensure that only one is selected at a time
		*/
  		group = fl_bgn_group();
		obj = fl_add_lightbutton(FL_RADIO_BUTTON,10,10,110,30,
			"Column Players");
			fl_set_button(obj, 1);
			fl_set_object_callback(obj, set_row_or_column, 0);
		obj = fl_add_lightbutton(FL_RADIO_BUTTON,150,10,110,30,
			"Row Players");
			fl_set_object_callback(obj, set_row_or_column, 1);
		fl_end_group();
	fl_end_form();
	/*
		When the user clicks on the close window button
		in the title bar, run the following
	*/
	fl_set_form_atclose(player_window, close_forms, 0);
	/*
		Now we create the window for editing payoffs
	*/
	payoff_window = fl_bgn_form(FL_NO_BOX, 260, 180);
		obj = fl_add_box(FL_UP_BOX,0,0,260,180,"");
		/*
			We have two types of players, each of
			which can play 1 of 2 actions, giving
			us four possible outcomes. For each outcome,
			we need a payoff for each type of player.
		*/
		for(i = 0; i < NUMB_ACTIONS; i++)
		{
			for(j =0; j < NUMB_ACTIONS; j++)
			{
			   for(k=0; k < NUMB_TYPES; k++)
		 	   {
			      payoff_inputs[i][j][k] = i
				 fl_add_input(FL_FLOAT_INPUT, 110 + 70 * i,
				 60 + j * 50 + k * 20, 60, 20,"");
			      sprintf(a_string,"%5.5f",
				  payoffs[i][j][k]);
			      fl_set_input(payoff_inputs[i][j][k], a_string);
			      fl_set_object_callback(payoff_inputs[i][j][k], set_payoffs, 0);
			   }
			}
		}
		/*	Here we add some text so that the user understands
			what the values in all those input fields
			represent
		*/
		obj = fl_add_text(FL_NORMAL_TEXT,130,10,110,20,
			"Column Players");
			fl_set_object_lalign(obj,
				FL_ALIGN_LEFT|FL_ALIGN_INSIDE);
		obj = fl_add_text(FL_NORMAL_TEXT,10,100,70,20,
			"Row Players");
			fl_set_object_lalign(obj,
				FL_ALIGN_LEFT|FL_ALIGN_INSIDE);
		obj = fl_add_text(FL_NORMAL_TEXT,80,70,20,20,"A");
			fl_set_object_lalign(obj,
				FL_ALIGN_LEFT|FL_ALIGN_INSIDE);
		obj = fl_add_text(FL_NORMAL_TEXT,130,30,20,20,"A");
			fl_set_object_lalign(obj,
				FL_ALIGN_LEFT|FL_ALIGN_INSIDE);
		obj = fl_add_text(FL_NORMAL_TEXT,200,30,20,20,"B");
			fl_set_object_lalign(obj,
				FL_ALIGN_LEFT|FL_ALIGN_INSIDE);
		obj = fl_add_text(FL_NORMAL_TEXT,80,130,20,20,"B");
			fl_set_object_lalign(obj,
				FL_ALIGN_LEFT|FL_ALIGN_INSIDE);
			fl_end_form();
	fl_set_form_atclose(payoff_window, close_forms, 0);
	/*
	 	The run window lets us set the
	 	number of iterations, start/stop the game, and gives
	 	us visual feedback on a running game.
	*/
	run_window = fl_bgn_form(FL_NO_BOX, 420, 180);
		obj = fl_add_box(FL_UP_BOX,0,0,420,180,"");
		/*
			We want a line graph for both column players and row
			players, so we create charts of type FL_LINE_CHART
		*/
		column_chart = fl_add_chart(FL_LINE_CHART,10,30,190,90,
				"Column Players");
			fl_set_object_lalign(column_chart,
				FL_ALIGN_TOP|FL_ALIGN_LEFT);
			fl_set_chart_maxnumb(column_chart, 100);
		row_chart = fl_add_chart(FL_LINE_CHART,220,30,190,90,
				"Row Players");
			fl_set_object_lalign(row_chart,
				FL_ALIGN_TOP|FL_ALIGN_LEFT);
			fl_set_chart_maxnumb(row_chart, 100);
		/*
			We also create two browsers to give us a place to
			display numerical feedback
		*/
		column_browser = fl_add_browser(FL_NORMAL_BROWSER, 130, 5,
			70, 21,"");
		row_browser = fl_add_browser(FL_NORMAL_BROWSER, 340, 5, 70,
			21,"");
		/*
			Now we add a counter to let us set the number of
			iterations for the game
		*/
		obj = fl_add_counter(FL_NORMAL_COUNTER,60,140,140,30,
				"Iterations");
			fl_set_object_lalign(obj,FL_ALIGN_LEFT);
			fl_set_counter_precision(obj, 0);
			fl_set_counter_bounds(obj, 1, 100000);
			fl_set_counter_step(obj, 1, 100);
			fl_set_counter_value(obj, numb_iterations);
			fl_set_object_callback(obj, set_iterations, 0);
		/*
		We need buttons to start (Go) and stop (Stop) the
		game. we draw them on top of each other, but then hide
		the Stop button. The Go button starts the game
		running by calling play_the_game(). In that routine,
		we hide the Go button and show the Stop button.
		*/
		go_button = fl_add_button(FL_NORMAL_BUTTON,220,140,190,30,
				"Go!");
			fl_set_object_callback(go_button, play_the_game, 0);
		stop_button = fl_add_button(FL_NORMAL_BUTTON,220,140,190,30,
				"Stop!");
			fl_set_object_callback(stop_button, stop_the_game, 0);
			fl_hide_object(stop_button);
	fl_end_form();
	fl_set_form_atclose(run_window, close_forms, 0);
}
int main(int argc, char *argv[])
{
	/*	The first call is to fl_initialize(), which
		sets up XForms and handles relevant command
		line options
	*/
	fl_initialize(&argc, argv,"xldlas", 0, 0);
	/*
		We call set_defaults() to assign initial payoffs
		and strategies, then create_forms() sets up all
		the windows for the program (but doesn't make
		any of them actually appear on the display)
	*/
	set_defaults();	
	create_forms();
	/*
		Now we show the main window and pass control over
		to the user with fl_do_forms();
	*/
        fl_show_form(main_window,FL_PLACE_FREE,FL_FULLBORDER,
		"An XForms Game Theory Simulator");
        fl_do_forms();
	return(0);
}