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