/*
	xgtsim2.c -- An XForms Game Theory Simulator (Second Version)
	
	All code segments that have changed since the earlier
	version of this program (ie. last month's article) are
	commented with *NEW*.   

	On a "standard" Linux system, this program can be
	compiled with the command:
	
	gcc -lX11 -lforms -lm xgtsim2.c -o xgtsim2
	
	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 xgtsim2.c -o xgtsim2

*/


/*
	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 nubmers	*/

/*
	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 strategies 	*/
#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;
FL_FORM		*about_window;		/*	*NEW*		*/ 
FL_FORM		*help_window;		/*	*NEW*		*/ 

/*
	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;

/*
	*NEW* XPM variable to hold the pixmap data for the
	      xgtsim ``logo''.
*/

static char * xgtsim_logo[];

/*
	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;
		state_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]));
			} 
		}
	}
}

/*
	*NEW* Need the ability to reset the payoff display
	      when, for example, a saved configuration file
	      is loaded.
*/

void reset_payoffs()
{
	char a_string[256];
	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++)
			{
				sprintf(a_string,"%.3f", payoffs[i][j][k]); 
				fl_set_input(payoff_inputs[i][j][k], a_string);
			} 
		}
	}
}

/*
	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);
}

/*
	*NEW* We use this routine to make the player and payoff
	      windows conform to any changes that occured as a
	      result of the user making choices from the settings
	      menu.
*/
void sync_windows_to_settings()
{
	/*
		Update the payoffs window.
	*/
	reset_payoffs();
	/*
		Here we call row_or_column twice, since that is
		the routine that sets strategy values in the
		display. We make sure to do it in the order that
		will leave the current choice of row_or_column
		unaffected.
	*/
	if(row_or_column == 0)
	{
		set_row_or_column(stop_button, 1);
		set_row_or_column(stop_button, 0);
	}
	else
	{
		set_row_or_column(stop_button, 0);
		set_row_or_column(stop_button, 1);
	}
}

/*
	*NEW* This function sets the payoffs to a classic prisonner's
	      dilemma and makes both types of players play tit for tat.
*/
void set_pd_tit_for_tat()
{
	int i,j,k;
	/*	
		First, set the payoffs to Prisonner's Dilemma values
		Assume ACTION_A = Cooperate and ACTION_B = Defect.
	*/
	payoffs[ACTION_A][ACTION_A][0]=3.0;
	payoffs[ACTION_A][ACTION_A][1]=3.0;
	payoffs[ACTION_B][ACTION_B][0]=1.0;	
	payoffs[ACTION_B][ACTION_B][1]=1.0;	
	payoffs[ACTION_A][ACTION_B][0]=0.0;	
	payoffs[ACTION_A][ACTION_B][1]=5.0;	
	payoffs[ACTION_B][ACTION_A][0]=5.0;	
	payoffs[ACTION_B][ACTION_A][1]=0.0;	
	/*
		Now set the strategies
 	*/
	for(i = 0; i < NUMB_TYPES; i++)
	{
		state_actions[i][0] = ACTION_A;
		state_actions[i][1] = ACTION_B;
		state_transitions[i][0][0] = 1;
		state_transitions[i][1][0] = 2;
		state_transitions[i][0][1] = 1;
		state_transitions[i][1][1] = 2;
		for(j = 2; j < NUMB_STATES; j++)
		{
			state_actions[i][j] = ACTION_A;
			for(k = 0; k < NUMB_ACTIONS; k++)
			{
				state_transitions[i][k][j] = 1;
			}
		}
	}
	/*
		Now we update the windows
	*/
	sync_windows_to_settings();
}

/*
	*NEW* This routine is called when Settings->Random is selected
	      by the user. It randomizes all settings and then 
	      updates the display by calling sync_windows_to_settings();
*/
void randomize_settings()
{
	int i,j,k;
	/*
		First, randomize the 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;
			}
		} 
	}
	/*
		Next Randomize the strategies
	*/
	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] = (rand() % NUMB_STATES) + 1;
			}
		}
	}
	/*
		Now we update the windows
	*/
	sync_windows_to_settings();
}
/*
	*NEW* Load a previously saved configuration
*/

void load_config()
{
	int i,j,k;
	FILE *filepointer;
	const char *filename;
	fl_use_fselector(1);
	fl_set_fselector_title("Load Configuration");
	filename = fl_show_fselector("Load","","*","");
	if(filename == NULL || strlen(filename) == 0)
	{
		return;
	}
	filepointer = fopen(filename,"r");
	if(!filepointer)
	{
		fl_show_alert("Could not open requested file for reading","","",1);
		return;
	}
	/*
		Load the payoffs
	*/
	for(i = 0; i < NUMB_ACTIONS; i++)
	{
		for(j=0; j < NUMB_ACTIONS; j++)
		{
			for(k = 0; k < NUMB_TYPES; k++)
			{
				fscanf(filepointer,"%f\n", &payoffs[i][j][k]);
			}
		} 
	}
	/*
		Load the strategies
	*/
	for(i = 0; i < NUMB_TYPES; i++)
	{
		for(j = 0; j < NUMB_STATES; j++)
		{
			fscanf(filepointer,"%i\n", &state_actions[i][j]);
			for(k = 0; k < NUMB_ACTIONS; k++)
			{
				fscanf(filepointer,"%i\n", &state_transitions[i][k][j]);
			}
		}
	}
	fl_show_message("File Succesfully Loaded","","");
	fclose(filepointer);
	/*
		Now update the display to reflect
		the values that were just loaded in
	*/
	sync_windows_to_settings();
}

/*
	*NEW* Save the current configuration
*/

void save_config()
{
	int i,j,k;
	FILE *filepointer;
	const char *filename;
	fl_use_fselector(0);
	fl_set_fselector_title("Save");
	filename = fl_show_fselector("Dataset to Save","","*","");
	if(filename == NULL || strlen(filename) == 0)
	{
		return;
	}
	filepointer = fopen(filename,"w");
	if(!filepointer)
	{
		fl_show_alert("Could not open requested file for writing","","",1);
		return;
	}
	/*
		Save the payoffs
	*/
	for(i = 0; i < NUMB_ACTIONS; i++)
	{
		for(j=0; j < NUMB_ACTIONS; j++)
		{
			for(k = 0; k < NUMB_TYPES; k++)
			{
				fprintf(filepointer,"%f\n", payoffs[i][j][k]);
			}
		} 
	}
	/*
		Save the strategies
	*/
	for(i = 0; i < NUMB_TYPES; i++)
	{
		for(j = 0; j < NUMB_STATES; j++)
		{
			fprintf(filepointer,"%i\n", state_actions[i][j]);
			for(k = 0; k < NUMB_ACTIONS; k++)
			{
				fprintf(filepointer,"%i\n",state_transitions[i][k][j]);
			}
		}
	}
	fl_show_message("File Succesfully Saved","","");
	fclose(filepointer);
}

/*
	*NEW* This is where we carry out the routines called
	      when the user selects an item from the File Menu
*/


void file_menu_routines(FL_OBJECT *obj, long argument)
{
	int choice;
	/*
		Figure out which menu item was selected
	*/
	choice = fl_get_menu(obj);
	if(choice == 1)		/* About was selected	*/
		fl_show_form(about_window,FL_PLACE_FREE,FL_FULLBORDER,"About xgtsim");
	if(choice == 2)		/* Load was selected	*/
		load_config();
	if(choice == 3)		/* Save was selected	*/
		save_config();
	if(choice == 4) 	/* Quit was selected 	*/	
		quit_xgtsim(obj, 0);
}

/*
	*NEW* This is where we carry out the routines called
	      when the user selects an item from the Settings Menu
*/

void settings_menu_routines(FL_OBJECT *obj, long argument)
{
	int choice;
	/*
		Figure out which menu item was selected
	*/
	choice = fl_get_menu(obj);
	if(choice == 1)		/* PD tit for tat	*/
		set_pd_tit_for_tat();
	if(choice == 2)		/* Random was selected	*/
		randomize_settings();
}

/*
	*NEW* This is where we carry out the routines called
	      when the user selects an item from the Help Menu
*/

void help_menu_routines(FL_OBJECT *obj, long argument)
{
	int choice;
	/*
		Figure out which menu item was selected
	*/
	choice = fl_get_menu(obj);
	if(choice == 1)		/* About was selected	*/
		fl_show_form(about_window,FL_PLACE_FREE,FL_FULLBORDER,"About xgtsim");
	if(choice == 2)		/* Use was selected	*/
		fl_show_alert("Sorry, Help is not fully implemented",
			      "Please see the August 1997 Edition of Linux Journal for further details",
			      "or http://a42.com/~thor/xforms",1);
}

/*
	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).

		*NEW* We've made the main window a little bigger,
		      added some pull down menus, and set resize/gravity
		      values so that the main window resizes nicely.
	*/
	main_window = fl_bgn_form(FL_NO_BOX, 290, 100);
		obj = fl_add_box(FL_UP_BOX,0,0,290,100,"");
		obj = fl_add_button(FL_NORMAL_BUTTON,10,55,60,30,"Players");
			fl_set_object_callback(obj, display_forms, 1);
			fl_set_object_resize(obj, FL_RESIZE_NONE);
		obj = fl_add_button(FL_NORMAL_BUTTON,80,55,60,30,"Payoffs");
			fl_set_object_callback(obj, display_forms, 2);
			fl_set_object_resize(obj, FL_RESIZE_NONE);
		obj = fl_add_button(FL_NORMAL_BUTTON,150,55,60,30,"Run");
			fl_set_object_callback(obj, display_forms, 3);
			fl_set_object_resize(obj, FL_RESIZE_NONE);
		obj = fl_add_button(FL_NORMAL_BUTTON,220,55,60,30,"Quit");
			fl_set_object_callback(obj, quit_xgtsim, 1);
			fl_set_object_resize(obj, FL_RESIZE_NONE);
	/*
		*NEW* Here's where we add the menus, but first an UP_BOX
		      to place them on.
	*/
		obj = fl_add_box(FL_UP_BOX,0,0,290,40,"");
			fl_set_object_resize(obj, FL_RESIZE_X);
			fl_set_object_gravity(obj, FL_NorthWest, FL_NorthEast);
			
	/*
		*NEW* Now for the menu items. Notice that Help's gravity
		      is set to the East so that it will always remain
		      close to right edge of the main window.		
	*/
		obj = fl_add_menu(FL_PULLDOWN_MENU,10,10,40,20,"File");
			fl_set_object_boxtype(obj,FL_FLAT_BOX);
			fl_addto_menu(obj, "About");
			fl_addto_menu(obj, "Load");
			fl_addto_menu(obj, "Save");
			fl_addto_menu(obj, "Quit");
			fl_set_object_callback(obj, file_menu_routines,0);
			fl_set_object_resize(obj, FL_RESIZE_NONE);
			fl_set_object_gravity(obj, FL_NorthWest, FL_NorthWest);
		obj = fl_add_menu(FL_PULLDOWN_MENU,50,10,60,20,"Settings");
			fl_set_object_boxtype(obj,FL_FLAT_BOX);
			fl_addto_menu(obj, "PD Tit for Tat");
			fl_addto_menu(obj, "Random");
			fl_set_object_callback(obj, settings_menu_routines,0);
			fl_set_object_resize(obj, FL_RESIZE_NONE);
			fl_set_object_gravity(obj, FL_NorthWest, FL_NorthWest);
		obj = fl_add_menu(FL_PULLDOWN_MENU,245,10,40,20,"Help");
			fl_set_object_boxtype(obj,FL_FLAT_BOX);
			fl_addto_menu(obj, "About");
			fl_addto_menu(obj, "Use");
			fl_set_object_callback(obj, help_menu_routines,0);
			fl_set_object_resize(obj, FL_RESIZE_NONE);
			fl_set_object_gravity(obj, FL_NorthEast, FL_NorthEast);
	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,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,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] = 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);
	/*
		*NEW* The about window displays simple
		      information about xgtsim and shows 
		      a pixmap of the ``logo''.
	*/
	about_window = fl_bgn_form(FL_NO_BOX, 360, 80);
		obj = fl_add_box(FL_UP_BOX,0,0,360,80,"");
		obj = fl_add_box(FL_DOWN_BOX,10,10,76,61,"");
		obj = fl_add_pixmap(FL_NORMAL_PIXMAP,13,13,70,55,"");
		fl_set_pixmapbutton_data(obj,xgtsim_logo);
		obj = fl_add_browser(FL_NORMAL_BROWSER,100,10,250,61,"");
		fl_add_browser_line(obj,"@cXGTSIM2");
		fl_add_browser_line(obj," ");
		fl_add_browser_line(obj,"@cA Simple Game Theory Simulator");
		fl_add_browser_line(obj,"@c(C) 1997 Thor Sigvaldason. No Rights Reserved.");
	fl_end_form();
	fl_set_form_atclose(about_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);
}

/*
	*NEW* This is the pixmap data for the xgtsim ``logo''
*/

static char * xgtsim_logo[] = {
/* width height num_colors chars_per_pixel */
"    70    55        4            1",
/* colors */
". c #f80000",
"# c #f8a0a0",
"a c #f82c28",
"b c #f8fcf8",
/* pixels */
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbaabbbbbbbbbbbbbbbbb",
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb#.abbbbbbbbbbbbbbbbb",
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbba.abbbbbbbbbbbbbbbbb",
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb##bbbb#bbbb#..abbbbbbbbbbbbbbbbb",
"bbbbbbbbbbbbbbbbbb#aaaaaa#aaaaabbbb#a....a#a..##a...a##bbbbbbbbbbbbbbb",
"bbbbbbbbbbbbbbbbbb#a....a#a...abbba..a#a...aa.#........#bbbbbbbbbbbbbb",
"bbbbbbbbbbbbbbbbbbbba...ab#.abbbb#...#bba...b#bb#...abbbbbbbbbbbbbbbbb",
"bbbbbbbbbbbbbbbbbbbbba...a.abbbbba...#bba...bbbb#...abbbbbbbbbbbbbbbbb",
"bbbbbbbbbbbbbbbbbbbbb#.....bbbbbba...#bba...bbbb#...abbbbbbbbbbbbbbbbb",
"bbbbbbbbbbbbbbbbbbbbbb#....#bbbbbb...ab#...abbbb#...abbbbbb##bbbbbbbbb",
"bbbbbbbbbbbbbbbbbbbbbb#.....#bbbbbba......#bbbbb#...abbbbbb.abbbbbbbbb",
"bbbbbbbbbbbbbbbbbbbbb#.aa....bbbbb#a.a###bbbbbbb#...abbbbb#.#bbbbbbbbb",
"bbbbbbbbbbbbbbbbbbbb#..bba...abbbb...a##bbbbbbbb#...ab#.bba.bbbbbbbbbb",
"bbbbbbbbbbbbbbbbbb#a...##......#b#........a#bbbbb....a.abbaabbbbbbbbbb",
"bbbbbbbbbbbbbbbbbb#aaaa##aaaaaa#bba.........abbbb#a...abbb.abbbbbbbbbb",
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbaa........bbbbbbb#bbbbb.abbbbbbbbbb",
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbba..bbb##a...#bbbbbbbbbaaa.#bbbbbbbbbb",
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb...bbbbbba..bbbbbbbbb#.a..bbbbbbbbbbb",
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb...#bbbbb..abbbbbbbbb#aba.bbbbbbbbbbb",
"bbbbbbbbbbbbbbbbbbbbbbbb#aa##bbbb#...aaaa..#bbbbbbb###a.bb#bbbbbbbbbbb",
"bbbbbbbbbbbbbbbb#bbbbbbb.....a#bbbb#aaaaa#bbbba#bb#...aabbbbbbbbbbbbbb",
"bbbbbbbbbbbbbbb#.#bbbbba.#..aaabbbbbbbbbbbbbbb.ab#.a#a.abbbbbbbbbbbbbb",
"bbbbbbbbbbbbbbbaa.###aa.#b##bb#aabbbbbbbbbbaa#a.a.abbb##bbbbbbbbbbbbbb",
"bbbbbbbbbbbbbb#.#aa....abbbbbbba.#bbbbbbbb#....a..#bbbbbbbbbbbbbbbbbbb",
"bbbbbbbbbbbbbba.bba...a#bbbbbbbbaa###bbbba..aa.aaabbbbbbbbbbbbbbbbbbbb",
"bbbbbbbbbbbbbbaabb#aa#bbb#bbbbbb#a...#b#a.aa#a.#bbbbbbbbbbbbbbbbbbbbbb",
"bbbbbbbbbbbbb#.#bbbbbbb#...#bbbbbb#a..a..a#bbb#bbbbbbbbbbbbbbbbbbbbbbb",
"bbbbbbbbbbb#b#.bbbbbbbba...abbbbbbbb#.a.abbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"bbbbbbbbaaa.aa.bbbbbbbba...abbbbbbbbb##.abbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"bbbbbaaa.aaaa..bbbbbbbbba.abbbbbbbbbbbb#bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"bbbbbaaa.#bbb##bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"bbbbba#b#bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"bbbbba#bbbbbbaa.aa#.#bb###aabbb###aab#a..abbba..a#bbbbbbbbbbbbbbbbbbbb",
"bbbbba#bbbbba..aa...#b.....abb#.....#......#a.....#bbbbbbbbbbbbbbbbbbb",
"bbbbba#bbbb#...#bba.#b#a...abbb#.....a#a....a#a...abbbbbbbbbbbbbbbbbbb",
"bbbbbbbbbbb#...a#b#a#bb#...abbbba...#bba...abbb....bbbbbbbbbbbbbbbbbbb",
"bbbbbbbbbbbb.....a#bbbb#...abbbba...bbb#...abbb....bbbbbbbbbbbbbbbbbbb",
"bbbbbbbbbbbb#......#bbb#...abbbba...bbb#...abbb....bbbbbbbbbbbbbbbbbbb",
"bbbbbbbbbbbbbba.....#bb#...abbbba...bbb#...abbb....bbbbbbbbbbbbbbbbbbb",
"bbbbbbbbbbb#.#b#a...abb#...abbbba...bbb#...abbb....bbbbbbbbbbbbbbbbbbb",
"bbbbbbbbbbb#.abbba..abb#...abbbba...bbb#...abbb....bbbbbbbbbbbbbbbbbbb",
"bbbbbbbbbbb#..a#ba..#baa....a##a....a##a....#ba....a#bbbbbbbbbbbbbbbbb",
"bbbbbbbbbbb#.a.....abb.......##......#a.....a#......abbbbbbbbbbbbbbbbb",
"bbbbbbbbbbbbbbb###bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
};
