/* 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);
}
Copyright © 1994 - 2019 Linux Journal. All rights reserved.