home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / games / volume13 / okbridge / part04 / input.c.ab
Text File  |  1992-01-12  |  31KB  |  1,106 lines

  1. static int Reserved_message (message)
  2.      char *message;
  3. /* Compares the given message to the list of card and bid names.  If a
  4.    match is found, then returns true.  Otherwise, returns false.
  5. */
  6. {
  7.   char compare_buff[100], *ch;
  8.   int i;
  9.  
  10.   if (!strlen(message))
  11.     return (1);
  12.  
  13.   strcpy (compare_buff, message);
  14.   ch = compare_buff;
  15.   for (ch = compare_buff; *ch != '\0'; ch++)
  16.     *ch = toupper(*ch);
  17.  
  18.   for (i=0; i < 52; i++)
  19.     if (!strcmp(card_names[i], compare_buff))
  20.       return (1);
  21.  
  22.   for (i=0; i < 38; i++)
  23.     if (!strcmp(bid_names[i], compare_buff))
  24.       return (1);
  25.  
  26.   return (0);
  27.   
  28. };
  29.  
  30. static load_email_file (filename)
  31.      char *filename;
  32. /* Attempts to read a sequence of deals from the email duplicate file
  33.  . with name filename.
  34.  */
  35. {
  36.   char msg_buf [80];
  37.  
  38.   total_no_deals = current_deal_no = 0;
  39.   current_board = NULL;
  40.   if (email_record != NULL)
  41.     Free_Email_Duplicate_Struct (email_record);
  42.   email_record = NULL;
  43.   current_deal_no = 
  44.     Read_Email_Duplicate_File (filename, &email_record);
  45.   if (current_deal_no) {
  46.     if (email_record != NULL)
  47.       Free_Email_Duplicate_Struct 
  48.     (email_record);
  49.     email_record = NULL;
  50.     if (current_deal_no > 0) {    
  51.       sprintf (msg_buf, "%s %d: %s", "FORMAT ERROR NEAR LINE", 
  52.            codefile_line_no, email_error_message);
  53.       Display_Player_Comment ("MODERATOR", msg_buf);
  54.     } else {
  55.       sprintf (msg_buf, "%s %s: %s", "ERROR IN READING", filename,
  56.            sys_errlist[errno]);
  57.       Display_Player_Comment ("MODERATOR", msg_buf);
  58.     };
  59.     email_record = New_Email_Duplicate_Struct ();
  60.     current_deal_no = 0;
  61.     replaying_mode = 0;
  62.   } else {
  63.     total_no_deals = email_record->nboards;
  64.     current_board = email_record->board_list;
  65.     replaying_mode = 1;
  66.     sprintf (msg_buf, "LOADED %d BOARDS FROM %s.",
  67.          total_no_deals, filename);
  68.     Display_Player_Comment ("MODERATOR", msg_buf);
  69.   };
  70.   ns_pair_no = Add_Email_Pair (email_record,
  71.                    player_names[PLAYER_NORTH], 
  72.                    player_names[PLAYER_SOUTH]);
  73.   ew_pair_no = Add_Email_Pair (email_record,
  74.                    player_names[PLAYER_EAST], 
  75.                    player_names[PLAYER_WEST]);
  76. };
  77.  
  78. static void save_email_file (filename)
  79.      char *filename;
  80. {
  81.   int error_flag;
  82.   char msg_buf[80];
  83.   
  84.   error_flag = Write_Email_Duplicate_File (filename, 1, email_record);
  85.   if (error_flag) {
  86.     sprintf (msg_buf, "%s %s: %s", "ERROR IN WRITING", filename,
  87.          sys_errlist[errno]);
  88.     Display_Player_Comment ("MODERATOR", msg_buf);
  89.   } else {
  90.     sprintf (msg_buf, "WROTE %d %s %s.",
  91.          email_record->nboards, "ENCODED BOARDS TO", filename);
  92.     Display_Player_Comment ("MODERATOR", msg_buf);
  93.     sprintf (msg_buf, "%s.plain", filename);
  94.     error_flag = Write_Email_Duplicate_File (msg_buf, 0, email_record);
  95.     sprintf (filename,"%s", msg_buf);
  96.     if (error_flag) {
  97.       sprintf (msg_buf, "%s %s: %s",
  98.            "ERROR IN WRITING", filename, sys_errlist[errno]);
  99.       Display_Player_Comment ("MODERATOR", msg_buf);
  100.       error_flag = 0;
  101.     } else {
  102.       sprintf (msg_buf, "WROTE %d %s %s.",
  103.            email_record->nboards, "UNENCODED BOARDS TO", filename);
  104.       Display_Player_Comment ("MODERATOR", msg_buf);
  105.     };
  106.   };
  107. };
  108.  
  109. static process_player_command (pc)
  110.     player_command pc;
  111. /* Determines whether the player command pc should be handled internally
  112.  . by the input module, or whether it should be passed upwards.  If it will
  113.  . be handled internally, then performs the appropriate action.  If it will
  114.  . be passed upwards, then places it in an appropriate queue to be handled
  115.  . later.
  116.  */
  117. {
  118.     char msg_buf[100], *word, *filename;
  119.     int mils;    /* milliseconds elapsed between ping and echo */
  120.     int error_flag;
  121.     struct timeval ping_end;
  122.  
  123.     if ((pc->player_no == local_player) && disabled(pc->command)) {
  124.         Display_Status ("THIS COMMAND CANNOT BE USED NOW");
  125.         return;
  126.     };
  127. /*
  128.     if ((pc->command != CMD_HELLO) && !players_here[pc->player_no])
  129.       return;
  130. */
  131.  
  132.     switch (pc->command) {
  133.         case CMD_ERROR:
  134.             break;    /* do nothing for erroneous commands. */
  135.         case CMD_VULN:
  136.             enqueue_command (pc);
  137.             break;
  138.         case CMD_RDEAL:
  139.             enqueue_command (pc);
  140.             if ((pc->player_no != local_player) &&
  141.                 (game_mode != DEALING_MODE)) {
  142.               Display_Player_Comment ("MODERATOR",
  143.                   "THE CARDS HAVE NOW BEEN DEALT.");
  144.             };
  145.             break;
  146.         case CMD_BID:
  147.             enqueue_command (pc);
  148.             break;
  149.         case CMD_PLAY:
  150.             enqueue_command (pc);
  151.             break;
  152.         case CMD_FINISH:
  153.             enqueue_command (pc);
  154.             break;
  155.         case CMD_HELLO:
  156.             if (players_here[pc->player_no]) {
  157.               sprintf (msg_buf, 
  158.                   "THERE ARE TWO PEOPLE CLAIMING THE SEAT OF %s",
  159.                   local_player_names [pc->player_no]);
  160.               Display_Player_Comment ("MODERATOR", msg_buf);
  161.               send_message_talk (msg_buf);
  162.               Terminate_Program ("PROGRAM TERMINATING");
  163.             };
  164.             if (pc->player_no == local_player) {
  165.                 /* The following statement should never
  166.                  * be executed: */
  167.                 break;
  168.             } else {
  169.                 send_message_ack ();
  170.                 player_names[pc->player_no] =
  171.                   strdup(pc->data.version_name +
  172.                      VERSION_LENGTH);
  173.                 if (strlen(player_names[pc->player_no]) > 8)
  174.                   player_names[pc->player_no][8] = '\0';
  175.                      error_flag = verify_compatibility 
  176.                   (pc->player_no, pc->data.version_name);
  177.                 if (!error_flag) {
  178.                   sprintf (msg_buf,
  179.                        "%s HAS JOINED THE GAME AS %s",
  180.                        player_names[pc->player_no],
  181.                        local_player_names[pc->player_no]);
  182.                   Display_Player_Comment ("MODERATOR", 
  183.                               msg_buf);
  184.                   if (ring_my_bell) ring_bell ();
  185.                   players_here [pc->player_no] = 1;
  186.                 }
  187.             };
  188. #ifdef TWOPLAYER_MODE
  189.             players_here [player_partner[pc->player_no]] = 1;
  190. #endif
  191.             break;
  192.         case CMD_ACK:
  193.                 verify_compatibility (pc->player_no,
  194.                           pc->data.version_name);
  195.                 player_names[pc->player_no] =
  196.              strdup(pc->data.version_name+VERSION_LENGTH);
  197.             if (strlen(player_names[pc->player_no]) > 8)
  198.               player_names[pc->player_no][8] = '\0';
  199.             if (pc->player_no == local_player)
  200.                 send_message_ack ();
  201. /*
  202.             else if (game_mode != STARTUP_MODE) {
  203.                 sprintf (msg_buf, 
  204.                     "ACKNOWLEDGMENT RECEIVED FROM %s", 
  205.                     player_names[pc->player_no]);
  206.                 Display_Player_Comment ("MODERATOR", msg_buf);
  207.             }
  208. */
  209.             else if (!players_here[pc->player_no]) {
  210.                 sprintf (msg_buf,
  211.                     "%s HAS JOINED THE GAME AS %s",
  212.                      player_names[pc->player_no],
  213.                      local_player_names[pc->player_no]);
  214.                 Display_Player_Comment ("MODERATOR", msg_buf);
  215.             };
  216.             players_here [pc->player_no] = 1;
  217. #ifdef TWOPLAYER_MODE
  218.             players_here [player_partner[pc->player_no]] = 1;
  219. #endif
  220.             break;
  221.         case CMD_TALK:
  222.             if (pc->player_no == local_player) {
  223.                     if (Reserved_message (pc->data.message))
  224.                     break;
  225.                 send_message_talk (pc->data.message);
  226.             };
  227.             Display_Player_Comment (player_names[pc->player_no],
  228.                         pc->data.message);
  229.             players_here [pc->player_no] = 1;
  230.             break;
  231.         case CMD_COMMENT:
  232.             Display_Player_Comment ("MODERATOR", pc->data.message);
  233.             break;
  234.         case CMD_QUIT:
  235.             if (pc->player_no == local_player)
  236.                     Quit_program ();
  237.             else {
  238.                 sprintf (msg_buf, "%s HAS QUIT.",
  239.                     player_names[pc->player_no]);
  240.                 Display_Player_Comment ("MODERATOR",
  241.                             msg_buf);
  242.                 if (game_mode == STARTUP_MODE)
  243.                   players_here[pc->player_no] = 0;
  244.                 Close_Network_Connection 
  245.                   (local_player_names[pc->player_no]);
  246.             };
  247. /*            soft_abort (); */
  248.             break;
  249.         case CMD_HELP:
  250.             display_help (pc->data.topic);
  251.             Refresh_Display ();
  252.             break;
  253.         case CMD_BELL:
  254.             if (pc->data.bell == 0)  ring_my_bell = 0;
  255.             if (pc->data.bell == 1)  ring_my_bell = 1;
  256.             if (ring_my_bell) word = "ON";
  257.             else          word = "OFF";
  258.             sprintf (msg_buf, "THE BELL IS NOW %s", word);
  259.             Display_Player_Comment ("MODERATOR", msg_buf);
  260.             if (ring_my_bell) ring_bell ();
  261.             break;
  262.             case CMD_DEFAULT:
  263.             if (pc->data.defaalt == 0) default_plays = 0;
  264.             if (pc->data.defaalt == 1) default_plays = 1;
  265.             if (default_plays) word = "ON";
  266.             else               word = "OFF";
  267.             sprintf (msg_buf, "DEFAULT INPUT MODE IS NOW %s", 
  268.                  word);
  269.             Display_Player_Comment ("MODERATOR", msg_buf);
  270.             break;
  271.             case CMD_REVIEW:
  272.             Review_Bidding ();
  273.             break;
  274.         case CMD_PROMPT:
  275.             if (pc->data.prompt == 0) prompt_dummy = 0;
  276.             if (pc->data.prompt == 1) prompt_dummy = 1;
  277.             if (prompt_dummy) word = "WILL";
  278.             else              word = "WILL NOT";
  279.             sprintf (msg_buf, 
  280.                  "THE DUMMY %s BE PROMPTED AFTER EACH TRICK",
  281.                  word);
  282.             Display_Player_Comment ("MODERATOR", msg_buf);
  283.             break;
  284.         case CMD_PING:
  285.             if (pc->player_no == local_player) {
  286.                 gettimeofday (&ping_start, NULL);
  287.                 send_message_ping ();
  288.             } else
  289.                 send_message_echo (pc->player_no);
  290.             break;
  291.         case CMD_ECHO:
  292.             players_here[pc->player_no] = 1;
  293.             if (pc->data.ping_source == local_player) {
  294.                 gettimeofday (&ping_end, NULL);
  295.                 mils = (ping_end.tv_sec - ping_start.tv_sec)
  296.                        * 1000;
  297.                 mils += ping_end.tv_usec / 1000;
  298.                 mils -= ping_start.tv_usec / 1000;
  299.                 sprintf (msg_buf,
  300.                 "ECHO RECEIVED FROM %-10s IN %7.2f SECONDS",
  301.                      player_names[pc->player_no], 
  302.                      ((float) mils) * 0.001);
  303.                 Display_Player_Comment ("MODERATOR", msg_buf);
  304.             };
  305.             break;
  306.         case CMD_CLAIM:
  307.             if (pc->player_no == local_player)
  308.               process_claim_offer (pc->data.tricks);
  309.             else if (local_player != dummy)
  310.               process_claim_response (pc->data.tricks);
  311.             break;
  312.         case CMD_RESP:
  313.             claim_responses++;
  314.             if (!pc->data.response) {
  315. /*
  316.               sprintf (msg_buf, 
  317.                    "THE CLAIM OFFER WAS DECLINED BY %s",
  318.                    player_names[pc->player_no]);
  319. */
  320.               if (!claim_rejected) {
  321.                 sprintf (msg_buf, 
  322.                      "THE CLAIM OFFER WAS DECLINED.");
  323.                 Display_Player_Comment ("MODERATOR", msg_buf);
  324.               };
  325.               claim_rejected = 1;
  326.             };
  327.             break;
  328.             case CMD_SCORE:
  329.             scoring_mode_known = 1;
  330.             scoring_mode = pc->data.scoring;
  331.             switch (scoring_mode) {
  332.             case RUBBER_SCORING:
  333.               Display_Player_Comment ("MODERATOR",
  334.                 "WE ARE PLAYING RUBBER BRIDGE.");
  335.               break;
  336.             case CHICAGO_SCORING:
  337.               Display_Player_Comment ("MODERATOR",
  338.                 "WE ARE PLAYING CHICAGO BRIDGE.");
  339.               break;
  340.             case DUPLICATE_SCORING:
  341.               Display_Player_Comment ("MODERATOR",
  342.                 "WE ARE PLAYING DUPLICATE BRIDGE.");
  343.               break;
  344.             case EMAIL_SCORING:
  345.               Display_Player_Comment ("MODERATOR",
  346.                 "WE ARE PLAYING EMAIL DUPLICATE BRIDGE.");
  347.               break;
  348.                         case IMP_SCORING:
  349.                           Display_Player_Comment ("MODERATOR",
  350.                             "WE ARE PLAYING IMP BRIDGE.");
  351.                           break;
  352.             };
  353.             break;
  354.             case CMD_LOG:
  355.             if (pc->data.filename[0] == '\0') {
  356.               if (logfile == NULL)
  357.                 Display_Player_Comment ("MODERATOR",
  358.                   "THERE IS NO OPEN LOGFILE.");
  359.               else {
  360.                 fclose (logfile);
  361.                 logfile = NULL;
  362.                 Display_Player_Comment ("MODERATOR",
  363.                   "THE LOG FILE HAS BEEN CLOSED.");
  364.               };
  365.             } else {
  366.               if (logfile != NULL) fclose (logfile);
  367.               if (pc->data.filename[0] == '+') {
  368.                 filename = pc->data.filename + 1;
  369.                 logfile = fopen (filename, "a");
  370.               } else {
  371.                 filename = pc->data.filename;
  372.                 logfile = fopen (filename, "w");
  373.               };
  374.               if (logfile == NULL) {
  375.                 sprintf (msg_buf, "%s ERROR OPENING %s",
  376.                      sys_errlist[errno], filename);
  377.                 Display_Player_Comment ("MODERATOR", msg_buf);
  378.               } else {
  379.                 sprintf (msg_buf, "NOW LOGGING TO %s",
  380.                      filename);
  381.                 Display_Player_Comment ("MODERATOR", msg_buf);
  382.               };
  383.             };
  384.             break;
  385.         case CMD_DEAL:
  386.             total_no_deals = pc->data.nhands;
  387.             current_deal_no = 0;
  388.             if (email_record != NULL)
  389.                 Free_Email_Duplicate_Struct (email_record);
  390.             email_record = New_Email_Duplicate_Struct ();
  391.             ns_pair_no = Add_Email_Pair (email_record,
  392.                 player_names[PLAYER_NORTH], 
  393.                 player_names[PLAYER_SOUTH]);
  394.             ew_pair_no = Add_Email_Pair (email_record,
  395.                 player_names[PLAYER_EAST], 
  396.                 player_names[PLAYER_WEST]);
  397.             if (total_no_deals < 0)
  398.                 Display_Player_Comment ("MODERATOR",
  399.                     "ENTERING CONTINUOUS DEAL MODE.");
  400.             else {
  401.                 sprintf (msg_buf, "%s %d DEALS.",
  402.                     "BEGINNING A SEQUENCE OF",
  403.                     total_no_deals);
  404.                 Display_Player_Comment ("MODERATOR", msg_buf);
  405.             };
  406.             replaying_mode = 0;
  407.             current_board = NULL;
  408.             break;
  409.         case CMD_LOAD:
  410.             load_email_file (pc->data.filename);
  411.             break;
  412.         case CMD_SAVE:
  413.             save_email_file (pc->data.filename);
  414.             break;
  415.             case CMD_REPLAY:
  416.             load_email_file (pc->data.filename);
  417.             if (replaying_mode)
  418.               autosave_file = strdup (pc->data.filename);
  419.             break;
  420.         default:
  421.             sprintf (msg_buf, "PLAYER %d, CODE %d\n",
  422.                 pc->player_no, pc->command);
  423.             Display_Player_Comment ("INTERNAL ERROR!", msg_buf);
  424.     };
  425. };
  426.  
  427. static player_input (rmt_player, ib, default_command)
  428.     int rmt_player; input_buffer ib; char *default_command;
  429. /* This procedure monitors the network and the keyboard simultaneously,
  430.  . waiting for a command to arrive.  The local player is free to type at the
  431.  . keyboard, placing characters into the buffer ib.  When he presses
  432.  . enter, a command is issued.  By default, the command which is issued
  433.  . is obtained by concatenating the player's input to the string given
  434.  . in default command.  However, if the player's command begins with a
  435.  . slash '/', then it is interpreted directly as the command.  The exit
  436.  . condition for this procedure is determined by the input value of player.
  437.  . If player = -1, then the procedure exits as soon as any kind of message
  438.  . is received.  If player is in the range 0..3, then the procedure exits
  439.  . only when a play is received from the corresponding player.  No other
  440.  . values for player are valid.
  441.  */
  442. {
  443.     struct player_command_struct pcs;
  444.     char command_buf[100];
  445.     int polling;
  446.  
  447.  
  448. /*    Clear_Status_Display();        */
  449.     print (ib->row, 1, default_command);
  450.     update_input_buffer (ib, '\0');
  451.     if (rmt_player < 0)
  452.         polling = 1;
  453.     else if (rmt_player < 4)
  454.         polling = !command_available(rmt_player);
  455.     polling = 1;
  456.     while (polling) {
  457.         if (message_available()) {
  458.             receive_player_command (&pcs);
  459.             if (pcs.player_no >= 0) {
  460.                 process_player_command (&pcs);
  461.                 polling = 0;
  462.             };
  463.         };
  464.         if (char_avail()) {
  465.             if (update_input_buffer(ib, input_char())) {
  466.                 Clear_Status_Display ();
  467.                 pcs.player_no = local_player;
  468.                 if (ib->buf[0] == '/')
  469.                     ps_copy (command_string, ib->buf+1);
  470.                 else {
  471.                     sprintf (command_buf, "%s %s",
  472.                         default_command, ib->buf);
  473.                     ps_copy (command_string, command_buf);
  474.                 };
  475.                 clear_input_buffer (ib);
  476.                 parse_player_command (command_string, &pcs);
  477.                 if (pcs.command == CMD_ERROR)
  478.                     Display_Status (parsing_errmsg);
  479.                 else {
  480.                     process_player_command (&pcs);
  481.                     polling = 0;
  482.                 };
  483.                 update_input_buffer (ib, '\0');
  484.             };
  485.         };
  486.         if ((3 >= rmt_player) && (rmt_player >= 0))
  487.             polling = !command_available(rmt_player);
  488.     };
  489.  
  490. };
  491.  
  492. static input_remote_play (rmt_player, play, pc)
  493.     int rmt_player; long play; player_command pc;
  494. /* As input, receives the index of a remote player as rmt_player and
  495.  . a bit string of allowable plays as play.  Waits for the specified
  496.  . player to make one of the plays listed in the bit string play.
  497.  . If we receive any other type of play, then an error message is
  498.  . generated.  Otherwise, creates the play into the structure pc.
  499.  */
  500. {
  501.     char error_message [100];
  502.     int waiting;
  503.  
  504.     waiting = 1;
  505.     while (waiting) {
  506.             if (QUEUE_EMPTY(rmt_player))
  507.           player_input (rmt_player,  talk_buffer, "TALK ");
  508.         dequeue_command (rmt_player, pc);
  509.         if (pc != NULL) {
  510.             if (pc->command & play)
  511.                 waiting = 0;
  512.             else {
  513.                 sprintf (error_message,
  514.              "EXPECTING PLAY %ld FROM %d; RECEIVED PLAY %ld",
  515.                  play, rmt_player, pc->command);
  516.                 Display_Player_Comment ("NETWORK ERROR", 
  517.                  error_message);
  518.             };
  519.         };
  520.     };
  521. };
  522.  
  523. Initialize_Input_Buffers ()
  524. /* Establishes and initializes the buffers that are used for reading
  525.  . input from the terminal.
  526.  */
  527. {
  528.     talk_buffer=(input_buffer) malloc (sizeof(struct input_buffer_struct));
  529.     play_buffer=(input_buffer) malloc (sizeof(struct input_buffer_struct));
  530.     ask_buffer =(input_buffer) malloc (sizeof(struct input_buffer_struct));
  531.  
  532.     talk_buffer->row = TALK_ROW;
  533.     talk_buffer->col = TALK_COL + 6;
  534.     talk_buffer->length = TALK_LENGTH - 6;
  535.     talk_buffer->pos = 0;
  536.     talk_buffer->buf[0] = '\0';
  537.  
  538.     play_buffer->row = PLAY_ROW;
  539.     play_buffer->col = PLAY_COL + 6;
  540.     play_buffer->length = PLAY_LENGTH - 6;
  541.     play_buffer->pos = 0;
  542.     play_buffer->buf[0] = '\0';
  543.  
  544.     ask_buffer->row = TALK_ROW + 1;
  545.     ask_buffer->col = 0;
  546.     ask_buffer->length = 5;
  547.     ask_buffer->pos = 0;
  548.     ask_buffer->buf[0] = '\0';
  549.  
  550.     email_record = NULL;
  551. };
  552.  
  553. Initialize_Input ()
  554. /* Initializes the higher-level state of the network connections,
  555.  . including doing the initial handshaking with the other players.
  556.  */
  557. {
  558.     int i, wait;
  559.     char msg_buf[80];
  560.  
  561.     initialize_command_queue();
  562.  
  563.     command_string = ps_alloc (127);
  564.     command_disabled  = 0;
  565.     disable(CMD_RDEAL);
  566.     disable(CMD_BID);
  567.     disable(CMD_PLAY);
  568.     disable(CMD_FINISH);
  569.     disable(CMD_HELLO);
  570.     disable(CMD_ACK);
  571.     disable(CMD_ECHO);
  572.     disable(CMD_RESP); 
  573.     disable(CMD_SCORE);
  574.     disable(CMD_CLAIM);
  575.     disable(CMD_DEAL);
  576.     disable(CMD_LOAD);
  577.     enable(CMD_SAVE);
  578.  
  579. #ifdef LOOPBACK_MODE
  580.     for (i = 0; i < 4; i++) players_here[i] = 1;    /* DBG */
  581.     wait = 0;
  582. #else
  583.     for (i = 0; i < 4; i++) players_here[i] = 0;
  584.     players_here[local_player] = 1;
  585.     wait = 1;
  586. #ifdef TWOPLAYER_MODE
  587.     players_here[player_partner[local_player]] = 1;
  588. #endif
  589. #endif
  590.  
  591.     /* First, wait for the other players to arrive: */
  592.     send_message_hello ();
  593.     while (wait) {
  594.         player_input (-1, talk_buffer, "TALK ");
  595.         wait = 0;
  596.         for (i = 0; i < 4; i++)
  597.             if (!players_here[i]) wait = 1;
  598.     };
  599.  
  600.         /* Now we reach an agreement about which scoring convention
  601.            will be used.  In this implementation, north makes the decision
  602.            and the others follow this decision. */
  603.         if (local_player == PLAYER_NORTH)
  604.       send_message_score ();
  605.     else {
  606.       while (!scoring_mode_known)
  607.         player_input (-1, talk_buffer, "TALK ");
  608.     };
  609.  
  610.     /* Now that we have all of this information we can initialize the
  611.        email duplicate structures.  Even though we may not be playing
  612.        email duplicate, we still record all of the hands in case one
  613.        of the players would like to save them.
  614.     */
  615.     email_record = New_Email_Duplicate_Struct ();
  616.     current_board = NULL;
  617.     ns_pair_no = Add_Email_Pair (email_record,
  618.         player_names[PLAYER_NORTH], 
  619.         player_names[PLAYER_SOUTH]);
  620.     ew_pair_no = Add_Email_Pair (email_record,
  621.         player_names[PLAYER_EAST], 
  622.         player_names[PLAYER_WEST]);
  623.     current_deal_no = total_no_deals = 0;
  624. };
  625.  
  626. static shuffle_the_deck (cards)
  627.     deal cards;
  628. /* Using the algorithm suggested by Douglas Foxvog.  Thanks, Doug! */
  629. {
  630.     int i, t, c;
  631.     deal shuffle;
  632.  
  633.     for (i = 0; i < 52; i++) 
  634.         shuffle [i] = i;
  635.     for (i = 0; i < 51; i++) {
  636.         c = random (52 - i);
  637.         t = shuffle[i+c]; 
  638.         shuffle[i+c] = shuffle[i];
  639.         shuffle[i] = t;
  640.     };
  641.     for (i = 0; i < 52; i++)
  642.         cards[shuffle[i]] = (i % 4);
  643.         
  644. };
  645.  
  646. static void Print_Continue_Message (saved_already)
  647.      int saved_already;
  648. {
  649.     if ((total_no_deals == 0) && !replaying_mode) {
  650.       Display_Player_Comment ("MODERATOR",
  651.         "TO BEGIN PLAY, TYPE /DEAL [nhands] OR /LOAD filename.");
  652.     } else {
  653.       Display_Player_Comment ("MODERATOR",
  654.         "ALL BOARDS HAVE NOW BEEN PLAYED.");
  655.       if (!saved_already)
  656.         Display_Player_Comment ("MODERATOR",
  657.                 "TYPE /SAVE filename TO SAVE THE RESULTS,"); 
  658.       Display_Player_Comment ("MODERATOR",
  659.         "THEN /DEAL [nhands] OR /LOAD filename TO CONTINUE PLAY.");
  660.     };
  661. };
  662.  
  663. static int Email_Deal_Available () 
  664. {
  665.     if (replaying_mode)
  666.         return (current_board != NULL);
  667.     else
  668.         return (current_deal_no != total_no_deals);
  669. };
  670.  
  671. input_hand (current_deal)
  672.     deal current_deal;
  673. /* If this player is the dealer, then shuffles the deck and deals the
  674.    cards to everyone.  Otherwise, waits for the dealer to tell what
  675.    the new shuffle is.  Stores the new deal in current_deal.
  676. */
  677. {
  678.     struct player_command_struct pcs;
  679.     int i, saved;
  680.     char message_buf[80];
  681.  
  682. #ifdef LOOPBACK_MODE
  683.     int dealer;
  684.     dealer = local_player;
  685. #endif
  686. #ifdef TWOPLAYER_MODE
  687.     dealer = dealer % 2;
  688. #endif
  689.  
  690.     if (current_board != NULL)
  691.         current_board = current_board -> next;
  692.     if ((scoring_mode == EMAIL_SCORING) && (local_player == PLAYER_NORTH)){
  693.         if (!Email_Deal_Available()) {
  694.                 saved = 0;
  695.                 if (autoload_file != NULL) {
  696.               load_email_file (autoload_file);
  697.               autoload_file = NULL;
  698.               if (!replaying_mode)
  699.                 autosave_file = NULL;
  700.                 } else if (autosave_file != NULL) {
  701.               save_email_file (autosave_file);
  702.               autosave_file = NULL;
  703.               saved = 1;
  704.             };
  705.             enable (CMD_DEAL);
  706.             enable (CMD_LOAD);
  707.             enable (CMD_REPLAY);
  708.             if (!Email_Deal_Available())
  709.               Print_Continue_Message (saved);
  710.             while (!Email_Deal_Available ())
  711.                 player_input (-1, talk_buffer, "TALK ");
  712.             disable (CMD_DEAL);
  713.             disable (CMD_LOAD);
  714.             disable (CMD_REPLAY);
  715.         };
  716.         if (replaying_mode) {
  717.             for (i = 0; i < 52; i++)
  718.                 current_deal[i] = current_board->deal[i];
  719.             vulnerable[SIDE_NS] = current_board->ns_vulnerable;
  720.             vulnerable[SIDE_EW] = current_board->ew_vulnerable;
  721.             dealer = current_board->dealer;
  722.         } else {
  723.             shuffle_the_deck (current_deal);
  724.             current_board = Add_Email_Board (email_record,
  725.                 current_deal);
  726.             current_board->ns_vulnerable = vulnerable[SIDE_NS];
  727.             current_board->ew_vulnerable = vulnerable[SIDE_EW];
  728.             current_board->dealer = dealer;
  729.         };
  730.         send_message_vuln (dealer, vulnerable[SIDE_NS], 
  731.                    vulnerable[SIDE_EW]);
  732.         send_message_rdeal (current_deal);
  733.     } else if (scoring_mode == EMAIL_SCORING) {
  734.         Display_Status ("WAITING FOR DEAL ... ");
  735.         input_remote_play (PLAYER_NORTH, CMD_VULN, &pcs);
  736.         vulnerable[SIDE_NS] = 
  737.             (pcs.data.vulnerable >> SIDE_NS) & 1;
  738.         vulnerable[SIDE_EW] =
  739.             (pcs.data.vulnerable >> SIDE_EW) & 1;
  740.         dealer = pcs.data.vulnerable >> 2;
  741.         input_remote_play (PLAYER_NORTH, CMD_RDEAL, &pcs);
  742.         for (i = 0; i < 52; i++)
  743.             current_deal[i] = pcs.data.deal[i];
  744.         Clear_Status_Display ();
  745.     } else if (dealer == local_player) {
  746.         shuffle_the_deck (current_deal);
  747.         send_message_rdeal (current_deal);
  748.     } else {
  749.         Display_Status ("WAITING FOR DEAL ... ");
  750.         input_remote_play (dealer, CMD_RDEAL, &pcs);
  751.         for (i = 0; i < 52; i++)
  752.             current_deal[i] = pcs.data.deal[i];
  753.         Clear_Status_Display ();
  754.     };
  755.     if (current_board == NULL) {
  756.         current_board = Add_Email_Board (email_record, current_deal);
  757.         current_board->ns_vulnerable = vulnerable[SIDE_NS];
  758.         current_board->ew_vulnerable = vulnerable[SIDE_EW];
  759.         current_board->dealer = dealer;
  760.     };
  761.     current_deal_no += 1;
  762.  
  763. };
  764.  
  765. static display_valid_bids (minimum_bid, double_ok, redouble_ok)
  766.     int minimum_bid, double_ok, redouble_ok;
  767. {
  768.     char double_string[40], bid_string[80];
  769.  
  770.     if (double_ok)
  771.         sprintf (double_string, "; DOUBLE IS OK");
  772.     else if (redouble_ok)
  773.         sprintf (double_string, "; REDOUBLE IS OK");
  774.     else
  775.         double_string[0] = '\0';
  776.  
  777.     sprintf (bid_string, "ERROR -- MINIMUM BID IS %s%s",
  778.          bid_names[minimum_bid], double_string);
  779.     Display_Status (bid_string);
  780. };
  781.  
  782. static display_valid_plays (current_hand)
  783.     hand current_hand;
  784. {
  785.     char card_string [60], card_message[80];
  786.     int i, j, c;
  787.  
  788.     c = 0;
  789.     for (i = 0; i < 52; i++) {
  790.         if (current_hand[i]) {
  791.             for (j = 0; card_names[i][j] != '\0'; j++)
  792.                 card_string[c++] = card_names[i][j];
  793.             card_string[c++] = ' ';
  794.         };
  795.     };
  796.     card_string[c++] = '\0';
  797.     sprintf (card_message,"ERROR -- VALID PLAYS ARE %s", card_string);
  798.     Display_Status (card_message);
  799. };
  800.  
  801. static int legal_bid (bid, minimum, double_ok, redouble_ok)
  802.     int bid, double_ok, redouble_ok;
  803. {
  804.     if (bid < 0)
  805.         return (0);
  806.     else if (bid == BID_PASS)
  807.         return (1);
  808.     else if (bid == BID_DOUBLE)
  809.         return (double_ok);
  810.     else if (bid == BID_REDOUBLE)
  811.         return (redouble_ok);
  812.     else
  813.         return (minimum <= bid);
  814. };
  815.  
  816. int input_bid (rmt_player, minimum_bid, double_ok, redouble_ok)
  817.     int rmt_player, minimum_bid, double_ok, redouble_ok;
  818. /* Waits for the indicated player to make a bid.  If the player is the
  819.  * one sitting at this terminal, then prompts for the play from the screen.
  820.  * Otherwise, waits for the play to come from the network.  Returns the
  821.  * index of the card played.  The input parameter minimum gives the index
  822.  * of the minimum acceptable contract bid.  The parameters double_ok and
  823.  * redouble_ok are boolean flags which indicate respectively if it is ok
  824.  * to bid double or redouble.
  825.  */
  826. {
  827.     int no_bid, bid;
  828.     struct player_command_struct pcs;
  829.  
  830.     if (rmt_player == local_player) {
  831.         no_bid = 1;
  832.         enable (CMD_BID);
  833.         if (ring_my_bell) ring_bell ();
  834.         play_buffer->length = TALK_LENGTH - 6;
  835.         default_input = "PASS";
  836.         while (no_bid) {
  837.             player_input (local_player, play_buffer, "BID ");
  838.             dequeue_command (local_player, &pcs);
  839.             if (pcs.command != CMD_BID)
  840.                 bid = -1;
  841.             else
  842.                 bid = pcs.data.bid;
  843.             no_bid = !legal_bid(bid, minimum_bid, double_ok,
  844.                     redouble_ok);
  845.             if (no_bid)
  846.                 display_valid_bids (minimum_bid, double_ok,
  847.                     redouble_ok);
  848.         };
  849.         play_buffer->length = PLAY_LENGTH - 6;
  850.         send_message_bid (bid);
  851.         disable (CMD_BID);
  852.         default_input = NULL;
  853.     } else {
  854.         input_remote_play (rmt_player, CMD_BID, &pcs);
  855.         bid = pcs.data.bid;
  856.     };
  857.     return (bid);
  858. };
  859.  
  860. static int minimum_card (ch)
  861.   hand ch;
  862. /* Returns the card of least rank held in the hand ch. */
  863. {
  864.   int suit_order[4], suit, mc, i, j;
  865.  
  866.   for (i = j = 0; i < 4; i++)
  867.     if (i != trump_suit)
  868.       suit_order [j++] = i;
  869.   if (trump_suit < 4)
  870.     suit_order [3] = trump_suit;
  871.  
  872.   for (i = 0; i < 4; i++) {
  873.     suit = suit_order [i];
  874.     for (j = 0; j < 13; j++) {
  875.       mc = suit * 13 + j;
  876.       if (ch[mc]) return (mc);
  877.     };
  878.   };
  879.   return (-1);
  880. };
  881.  
  882. static int no_cards (ch)
  883.   hand ch;
  884. {
  885.   int i, n;
  886.  
  887.   for (i = n = 0; i < 52; i++)
  888.     if (ch[i]) n++;
  889.   return (n);
  890. };
  891.  
  892. static set_default_suit (ch)
  893.   hand ch;
  894. /* Examines the cards in the hand ch.  If all of the cards are from a single
  895.    suit, then sets default_suit to that suit.  Otherwise, sets default_suit
  896.    to -1.
  897. */
  898. {
  899.   int i, d;
  900.  
  901.   d = default_suit = -1;
  902.   for (i = 0; i < 52; i++)
  903.     if (ch[i]) {
  904.       if (d == -1)
  905.     d = suit_of (i);
  906.       else if (d != suit_of(i))
  907.     return;
  908.     };
  909.   default_suit = d;
  910.         
  911. };
  912.  
  913. int input_play (rmt_player, current_hand)
  914.     int rmt_player; hand current_hand;
  915. /* Waits for the indicated player to play a card.  If that player is the
  916.  * one sitting at the terminal, then prompts from the screen for the play.
  917.  * In this case, current_hand gives a list of the valid cards which may
  918.  * be played.  If the indicated player is a remote player, then waits for
  919.  * the play to arrive through the network.  If the return value is 
  920.  * nonnegative, then it is the index of card played.  If the return value
  921.  * is -k-1, then k specifies the number of additional tricks claimed
  922.  * by the contracting team.
  923.  */
  924. {
  925.     int no_play, play;
  926.     struct player_command_struct pcs;
  927.     char *dp;
  928.     char temp_buffer[60];
  929.  
  930.     if (local_player == rmt_player) {
  931.         no_play = 1;
  932.         enable (CMD_PLAY);
  933.         if (local_player == declarer) enable (CMD_CLAIM);
  934.         if (ring_my_bell) ring_bell ();
  935.         default_input = card_names[minimum_card(current_hand)];
  936.         if (default_plays && (no_cards(current_hand) == 1)) {
  937.                for (dp = default_input; *dp != '\0'; dp++)
  938.                    play_buffer->buf[play_buffer->pos++] = *dp;
  939.             play_buffer->buf[play_buffer->pos] = '\0';
  940.                 play_buffer->defaulted = 1;
  941.         };
  942.         set_default_suit (current_hand);
  943.         while (no_play) {
  944.             player_input (local_player, play_buffer, "PLAY ");
  945.             dequeue_command (local_player, &pcs);
  946.             if (pcs.command == CMD_FINISH) {
  947.                     play = -pcs.data.tricks - 1;
  948.                 no_play = 0;
  949.             } else if (pcs.command == CMD_PLAY) {
  950.                 play = pcs.data.card;
  951.                 no_play = !current_hand[play];
  952.                 if (!no_play)
  953.                   send_message_play (play);
  954.             };
  955.             if (no_play)
  956.                 display_valid_plays (current_hand);
  957.         };
  958.         default_input = NULL;
  959.         disable (CMD_PLAY);
  960.         disable (CMD_CLAIM);
  961.     } else {
  962.         input_remote_play (rmt_player, CMD_PLAY | CMD_FINISH, &pcs);
  963.         if (pcs.command == CMD_FINISH)
  964.           play = -pcs.data.tricks - 1;
  965.         else
  966.           play = pcs.data.card;
  967.     };
  968.     return (play);
  969. };
  970.  
  971. input_acknowledgment (line)
  972.     int line;
  973. /* Displays the message "PRESS RETURN TO CONTINUE" on the given screen line,
  974.  * unless:
  975.  *   line == -1, in which case the message is displayed on the
  976.  *               status line, or
  977.  *   line == -2, in which case no message is displayed,
  978.  *   line == -3, in which case no message is displayed, and the
  979.  *      program enters TALK mode, exiting when a RETURN is pressed
  980.  *      on a blank line.
  981.  * Then waits for the user to press, RETURN.
  982.  */
  983. {
  984.     int ch;
  985.     struct player_command_struct pcs;
  986.  
  987.     if (line == -1) {
  988.         Clear_Status_Display ();
  989.         Display_Status ("PRESS RETURN TO CONTINUE");
  990.     } else if (line >= 0) {
  991.         print (line, 1, "PRESS RETURN TO CONTINUE");
  992.                 set_cursor (line, 1 + strlen("PRESS RETURN TO CONTINUE"));
  993.         };
  994.  
  995.     if (ring_my_bell) ring_bell ();
  996.     waiting_for_acknowledgment = 1;
  997.     while (waiting_for_acknowledgment) {
  998.       if (line == -3)
  999.         player_input (-1, talk_buffer, "TALK ");
  1000.       else {
  1001.         if (message_available()) {
  1002.             receive_player_command (&pcs);
  1003.             if (pcs.player_no >= 0) 
  1004.                 process_player_command (&pcs);
  1005.         };
  1006.         if (char_avail()) {
  1007.             ch = input_char ();
  1008.             waiting_for_acknowledgment = (ch != '\012') 
  1009.                                       && (ch != '\015');
  1010.             if (ch == '\022')
  1011.               Refresh_Display ();
  1012.         };
  1013.           };
  1014.     };
  1015.     if (line != -3)
  1016.       while (char_avail()) input_char ();
  1017.     Clear_Status_Display ();
  1018.     
  1019. };
  1020.  
  1021. input_answer (question)
  1022.      char *question;
  1023. /* Displays the question on the status line and waits for the
  1024.  * local player to press 'y' or 'n'.  Returns 1 if 'y' was entered
  1025.  * and 0 if 'n' was entered.
  1026.  */
  1027. {
  1028.     int polling, ch;
  1029.     struct player_command_struct pcs;
  1030.  
  1031.     waiting_for_acknowledgment = 0;
  1032.     Display_Status (question);
  1033.     ask_buffer->col = strlen(question) + 2;
  1034.     clear_input_buffer (ask_buffer);
  1035.     default_input = "NO";
  1036.     if (ring_my_bell) ring_bell ();
  1037.     for (polling = 1; polling;) {
  1038.       if (message_available()) {
  1039.         receive_player_command (&pcs);
  1040.         if (pcs.player_no >= 0) 
  1041.           process_player_command (&pcs);
  1042.       };
  1043.       if (char_avail())
  1044.         if(update_input_buffer (ask_buffer, input_char())) {
  1045.           ch = ask_buffer->buf[0];
  1046.           polling = ((ch != 'y') && (ch != 'Y')
  1047.              && (ch != 'n') && (ch != 'N'));
  1048.           clear_input_buffer (ask_buffer);
  1049.         };
  1050.     };
  1051.     default_input = NULL;
  1052.     Clear_Status_Display ();
  1053.     update_input_buffer (talk_buffer, '\0');
  1054.     return ((ch == 'y') || (ch == 'Y'));
  1055.     
  1056. };
  1057.  
  1058. process_claim_offer (t)
  1059.      int t;
  1060. {
  1061.   char error_buf[80];
  1062.   struct player_command_struct pcs;
  1063.  
  1064.   if ((t + trick) > 14) {
  1065.     sprintf (error_buf, "THERE ARE ONLY %d TRICKS WHICH CAN BE CLAIMED", 
  1066.          14 - trick);
  1067.     Display_Status (error_buf);
  1068.     return;
  1069.   };
  1070.  
  1071.   claim_responses = 0;
  1072.   claim_rejected  = 0;
  1073.   send_message_claim (t);
  1074.   Display_Status ("WAITING FOR A RESPONSE TO YOUR OFFER...");
  1075.  
  1076.   disable (CMD_CLAIM);
  1077.   while (claim_responses < 2)
  1078.     player_input (-1, talk_buffer, "TALK ");
  1079.   enable (CMD_CLAIM);
  1080.  
  1081.   Clear_Status_Display ();
  1082.  
  1083.   if (!claim_rejected) {
  1084.     send_message_finish (t);
  1085.     pcs.command = CMD_FINISH;
  1086.     pcs.player_no = local_player;
  1087.     pcs.data.tricks = t;
  1088.     enqueue_command (&pcs);
  1089.   };
  1090. };
  1091.   
  1092. process_claim_response (t)
  1093.      int t;
  1094. {
  1095.   int response;
  1096.   char question_buffer [80];
  1097.  
  1098.   Display_Hand (declarer);
  1099.   sprintf (question_buffer, "DECLARER CLAIMS %d TRICKS.  DO YOU ACCEPT [YN]?",
  1100.        t);
  1101.   response = input_answer (question_buffer);
  1102.   Clear_Hand (declarer);
  1103.   update_input_buffer (talk_buffer, '\0');
  1104.   send_message_respond (response);
  1105. };
  1106.