[comp.sources.games] v08i022: choice - two handed trick-taking card game, Part01/02

billr@saab.CNA.TEK.COM (Bill Randle) (09/14/89)

Submitted-by: Scott Turner <srt@lanai.cs.ucla.edu>
Posting-number: Volume 8, Issue 22
Archive-name: choice/Part01

	[This is a neat curses-based card game. It compiles and
	 runs fine on my Sun 3/60 (OS 3.5).	-br]

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 1 (of 2)."
# Contents:  README MANIFEST choice.c comp.c human.c makefile
# Wrapped by billr@saab on Thu Sep 14 09:46:46 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(3309 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
X			A Note From the Author
X
X  Leslie Kim is a card fanatic who introduced me to a two player card
Xgame called Choice.  It's an interesting game as far as two player
Xcard games go, having some of the same elements as games such as
XBridge and Spades.
X
X  Shortly thereafter (Sept. 1988) I started working on a program to
Xplay Choice.  I thought that it would be fairly easy to build a
Xprogram that would be a good to excellent player.
X
X  I was wrong.
X
X  Bidding turned out to be much more difficult than I expected.  For
Xover a year I've experimented with different bidding schemes.  The
Xfirst was the rule-based bidder included in this version of the
Xprogram, though the rules have changed a bit since the original
Xversion.
X
X  I then tried neural networks.  I wrote several different neural
Xnetwork bidders, using various representations of both the input
X(cards in a suit) and the output (expected number of tricks in the
Xsuit).  None of these bidders was able to hold its own against the
Xrule-based bidder, even after considerable training.  This may say
Xsomething about the general applicability of neural networks, since
XChoice appears on the surface to be an almost ideal domain.  Or it may
Xsay something about my ability to build a neural network.
X
X  My next effort was to try and model bidding as a linear best
Xapproximation, by dividing the solution domain into 64 bins (based on
Xwhether or not the suit was trump, what the high card holding in the
Xsuit was, and how strong the remaining cards were).  After gathering
Xstatistics on some 50,000+ hands, the approximations seemed to be
Xfairly close for the common bins.
X
X  It still couldn't beat the rule-based bidder.
X
X  So I built a bidder that cheated.  At least it can beat the
Xrule-based bidder.  Some of the time.
X
X  At the moment I'm stymied, but the program as I've distributed it
Xincludes hooks for people interested in trying to build better
Xbidders.  The -a option (autoplay) will play the computer against
Xitself and gather statistics on the results.  This is useful for
Xstatistical studies and for training neural networks.  The -d option
X(debug) will print voluminous debug statements which may allow you to
Xdetermine whether or not your bidder is doing as expected.  The
Xfunction meta_bid in comp.c can be easily modified to add more
Xstrategies.
X
X  I appreciate everyone who wrote and praised the code for Hotel - I'm
Xafraid you aren't going to find Choice quite so palatable.  It has had
Xa much more haphazard development and the code reflects that.  Still,
XI think that it is adequately commented and that a competent
Xprogrammer shouldn't have any problem modifying it.
X
XThis code has been compiled under BSD 4.2, SunOS and on a PC using
XTurbo C 2.0.  There may well be bugs in the comp_bid4, which is fairly
Xnew, and in the computer player routines in general, since I've
Xdiscovered the odd bug off and on in those routines for some time.
X
XThe scoring routines are a little hacky.  They keep all the scores in
Xone file and there can be problems with multiple players.  It probably
Xshould be re-written.  The filename is in "defs.h"; you'll have to 
Xchange it before making the program.  Scores only work under Unix.
X
XI'd appreciate hearing about any bugs and/or receiving patches at
Xsrt@cs.ucla.edu.
X
X  Have fun.
X
X					-- Scott R. Turner
END_OF_FILE
if test 3309 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'MANIFEST' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'MANIFEST'\"
else
echo shar: Extracting \"'MANIFEST'\" \(455 characters\)
sed "s/^X//" >'MANIFEST' <<'END_OF_FILE'
X   File Name		Archive #	Description
X-----------------------------------------------------------
X MANIFEST                   1	This shipping list
X README                     1	
X choice.c                   1	
X comp.c                     1	
X defs.h                     2	
X human.c                    1	
X instruct.c                 2	
X makefile                   1	
X my_wgets.c                 2	
X my_wgets.h                 2	
X utils.c                    2	
END_OF_FILE
if test 455 -ne `wc -c <'MANIFEST'`; then
    echo shar: \"'MANIFEST'\" unpacked with wrong size!
fi
# end of 'MANIFEST'
fi
if test -f 'choice.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'choice.c'\"
else
echo shar: Extracting \"'choice.c'\" \(15768 characters\)
sed "s/^X//" >'choice.c' <<'END_OF_FILE'
X/*
X * This program (called "Choice") is copyright 1989 to Scott R. Turner,
X * in both source code and executable form.  Permission is given to 
X * copy both the source code and the executable under the following
X * conditions:
X * 
X *   1. You may copy and distribute verbatim copies of Choice code as you
X * receive it, in any medium, provided that you conspicuously and
X * appropriately publish on each file a valid copyright notice such as
X * "Copyright (C) 1989 Scott R. Turner", and keep intact the copyright
X * and license notices on all files.  You may charge a distribution fee
X * for the physical act of transferring a copy, but that fee may not
X * exceed your actual costs in creating and delivering the copy.
X * 
X *   2. You may modify your copy or copies of Choice or any portion of it,
X * and copy and distribute such modifications under the terms of
X * Paragraph 1 above, provided that you also do the following:
X * 
X *     a) cause the modified files to carry prominent notices stating
X *     who last changed such files and the date of any change; and
X * 
X *     b) cause the whole of any work that you distribute or publish,
X *     that in whole or in part contains or is a derivative of Choice
X *     or any part thereof, to be licensed at no charge to all third
X *     parties on terms identical to those contained in this License
X *     Agreement (except that you may choose to grant more extensive
X *     warranty protection to third parties, at your option).
X *
X *   3. You may not copy, sublicense, distribute or transfer Choice
X * except as expressly provided under this License Agreement.  Any attempt
X * otherwise to copy, sublicense, distribute or transfer Choice is void and
X * your rights to use Choice under this License agreement shall be
X * automatically terminated.  However, parties who have received computer
X * software programs from you with this License Agreement will not have
X * their licenses terminated so long as such parties remain in full compliance.
X * 
X *   4.  Under no circumstances may you charge for copies of Choice, for copies
X * of any program containing code from Choice in whole or in part, or for 
X * any software package or collection of programs or code that contains Choice
X * in whole or part.
X *
X */
X
X/*
X *  choice.c
X *  Wed Sep 14 15:35:51 1988 -- Scott R. Turner
X *
X *  The main file for Choice.
X *
X */
X
X#include "defs.h"
X#include <signal.h>
X#include <sys/types.h>
X#ifdef UNIX
X#include <pwd.h>
X#include <sys/file.h>
X#else
X#include <stdlib.h>
X#include <time.h>
X#endif 
X
Xdeckt deck;
Xplayer players[2];
Xint lead, trump;
Xint tricknum, topbid;
Xint debug,autoplay;
XFILE *stats, *trumps, *debugf;
Xint suit_tricks[4];
Xint nums[4],len[4],hl[4];
Xfloat expect[4];
X
X/*
X *  Score variables
X *
X */
X
Xtypedef struct sscore {
X  int who;               /* UID of player */
X  int mygames;            /* Games player has won. */
X  int compgames;          /* Games computer has won. */
X  int myscore;           /* Current "my" score. */
X  int compscore;         /* Current computer score. */
X} scoret;
X
Xscoret scores[100];
Xint uid;
Xint hisnum;
Xint scoresexist;
Xint numscores;
X
X/*
X *  External Functions
X *
X */
X
Xextern void shuffle();
Xextern void do_header();
Xextern void print_hand();
Xextern char *card_string();
Xextern void human_choice();
Xextern void comp_choice();
Xextern int human_bid();
Xextern int meta_bid();
Xextern int human_trump();
Xextern int comp_trump();
Xextern void human_play();
Xextern void comp_play();
Xextern void human_respond();
Xextern void comp_respond();
Xextern void comp_inform();
Xextern void instruct();
Xextern void do_bids();
Xextern void comp_init();
X  
Xvoid endgame()
X{
X  if (autoplay) {
X    close(stats);
X    close(trumps);
X  } else {
X    doscore();
X  };
X  if (debug == 10) {
X    close(debugf);
X  };
X  endwin();
X  if (autoplay) printf("Autoplay is %d.\n",autoplay);
X  exit(1);
X};
X
Xvoid usage()
X{
X  printf("Usage: choice [-s] [-h]\n");
X};
X 
Xmain(argc,argv)
X    int argc;
X    char **argv;
X{
X  int rank,suit,current,i,j, argn;
X  char ans;
X  int start,end;
X  int whose, bid, done;
X  int first, second;
X  int trumpsuit, suit1, rank1, suit2, rank2;
X#ifdef UNIX
X  extern void srandom();
X  extern long time();
X#endif
X
X
X/*
X *  System dependencies, initializing RNG
X *
X */
X  
X#ifdef UNIX
X  signal(SIGINT,endgame);
X  signal(SIGQUIT,endgame);
X  srandom(time(0) ^ getpid() << 16);
X#else
X  randomize();
X#endif
X
X/*
X *  Initialize variables.
X *
X */
X  
X  start = randum(1);
X  autoplay = end = tricknum = topbid = debug = 0;
X  players[0].score = 0;
X  players[1].score = 0;
X  players[0].games = 0;
X  players[1].games = 0;
X  players[1].computer = 1;   /*  Default computer strategy. */
X  players[0].computer = 0;
X
X  /*
X   *  Under UNIX, get the current running score.
X   *
X   */
X  
X  getscore();
X
X  /*
X   *  Options
X   *
X   *  -a = autoplay
X   *  -h = hard (cheating bidder)
X   *  -s = score
X   *  -d = debug
X   *
X   */
X
X  argn = 0;
X  while (++argn < argc) {
X    if (strlen(argv[argn]) != 2 || argv[argn][0] != '-') usage();
X    switch(argv[argn][1]) {
X      case 'a':
X	printf("How many games? ");
X	scanf("%d",&autoplay);
X	printf("Strategy for You? ");
X	scanf("%d",&players[0].computer);
X	printf("Strategy for Me? ");
X	scanf("%d",&players[1].computer);
X	end = 1;
X	stats = fopen("stats","at+");
X	trumps = fopen("trumps","at+");
X	break;
X      case 'h':
X	players[1].computer = 3;
X	break;
X      case 'd':
X        debug = 10;
X	debugf = fopen("debug","at+");
X	break;
X      case 's':
X	printf("               Running Score\n\n");
X	printf("              You     |     Me\n");
X	printf("            ----------+---------\n");
X	printf("               %d             %d\n\n",
X 	   scores[hisnum].mygames,
X	   scores[hisnum].compgames);
X	exit(1);
X      default:
X	usage();
X	exit(1);
X      }
X  };
X
X  /*
X   *  Curses initialization.
X   *
X   */
X  
X  initscr();
X  crmode();
X  noecho();
X
X  /*
X   *  Instructions?
X   *
X   */
X
X  clear();
X  printw("Do you need instructions (y/n)? ");
X  refresh();
X  clear();
X  ans = getch();
X  if (ans == 'y' || ans == 'Y') instruct();
X  do_header();
X
X  /*
X   *  Game Loop
X   *
X   */
X  
X  do {
X
X    /*
X     *  For initialization, shuffle the deck and initialize
X     *  the player structures.
X     */
X  
X    shuffle(&deck);
X    init_player(&players[0]);
X    init_player(&players[1]);
X
X    /*
X     *  First part - selecting a hand.
X     *
X     */
X    
X    current = 1;
X    for(i=1;i<=13;i++) {
X      for (j=0;j<=1;j++) {
X	if(players[j].computer)
X	  comp_choice(&players[j],current,&deck);
X	else
X	  human_choice(&players[j],current,&deck);
X	current += 2;
X      };
X      print_hand(&players[0],2);
X      if (debug || autoplay) {
X	print_hand(&players[1],18);
X	refresh();
X      };
X      refresh();
X    };
X
X    /*
X     *  Second part - Finding a trump suit
X     *
X     */
X
X    done = 0;
X    whose = -1;
X    topbid = 0;
X    i = start;
X    start = (start + 1) % 2;
X    do {
X      if (players[i].computer) {
X	bid = meta_bid(&players[i],topbid,i);
X      } else {
X	bid = human_bid(&players[i],topbid);
X      };
X      if (bid > topbid && bid < 14 && bid > 0) {
X	topbid = bid;
X	whose = i;
X	if (players[i].computer) {
X	  /* Inform human he's been overbid. */
X	  move(IOLINE1,0);
X	  clrtoeol();
X	  printw("The computer bids %d.",bid);
X	};
X      };
X      if (bid == 0 && topbid > 0) {
X	done = 1;
X	if (players[i].computer) {
X	  /* Inform human he's won. */
X	  move(IOLINE1,0);
X	  clrtoeol();
X	  printw("Okay, it's all yours.");
X	};
X      };
X      i = (i + 1) % 2;
X    } while (!done);
X
X    /*
X     *  Step 3 - Determine Trump Suit
X     *
X     */
X
X    if (players[whose].computer) {
X      trumpsuit = comp_trump(&players[whose],topbid);
X    } else {
X      trumpsuit = human_trump(&players[whose],topbid);
X    };
X
X    if (debug == 10) {
X        fprintf(debugf,"Contract won by %d, bidding %d, trumps %c.\n",
X            whose, topbid, suitchars[trumpsuit]);
X	};
X                    
X    
X    for(i=0;i<=1;i++) {
X      players[i].whose = 0;
X      players[i].trump = trumpsuit;
X      players[i].num = topbid;
X      players[i].tricks = 0;
X      for(j=0;j<=3;j++)
X	players[i].shownout[j] = 0;
X      if (players[i].computer) comp_init(&players[i]);
X    };
X    players[whose].whose = 1;
X
X    move(2,44);
X    printw("%c",suitchars[trumpsuit]);
X    move(2,58);
X    printw("%d",topbid);
X    move(2,61);
X    if(whose == 1) {
X      printw("Me ");
X    } else {
X      printw("You");
X    };
X    refresh();
X
X    /*
X     * Step 4 -- The play.
X     *
X     */
X
X    first = whose;
X    tricknum = 1;
X    if (autoplay) {
X      /*
X       *  Keep track of some of this stuff for
X       *  analysis purposes.
X       *
X       */
X      get_stats(&players[whose],nums,len,hl);
X      for(i=0;i<=3;i++) suit_tricks[i] = 0;
X    };
X       
X    do {
X      second = (first + 1) % 2;
X      if(players[first].computer) {
X	comp_play(&players[first],trumpsuit,&suit1,&rank1);
X      } else {
X	human_play(&players[first],trumpsuit,&suit1,&rank1);
X      };
X      if(players[second].computer) {
X	comp_respond(&players[second],trumpsuit,suit1,rank1,&suit2,&rank2);
X	move(IOLINE1,0);
X	clrtoeol();
X	printw("I play %s.",card_string(suit2,rank2));
X      } else {
X	human_respond(&players[second],trumpsuit,suit1,rank1,&suit2,&rank2);
X      };
X      if (players[first].computer) {
X	comp_inform(&players[first],suit1,rank1,suit2,rank2);
X      };
X      players[first].cards[suit1][rank1] = PLAYED;
X      players[second].cards[suit2][rank2] = PLAYED;
X      if (debug == 10) {
X	fprintf(debugf,"On trick %2d, [%1d] %s",
X		tricknum, first, card_string(suit1,rank1));
X        fprintf(debugf,"-- [%1d] %s | %1d.\n",		
X	        second, card_string(suit2, rank2),
X	        winner(trumpsuit,suit1,rank1,suit2,rank2));
X	};
X      if ( winner(trumpsuit,suit1,rank1,suit2,rank2) ) {
X	players[first].tricks++;
X	if (whose == first) suit_tricks[suit1]++;
X      } else {
X	players[second].tricks++;
X	if (whose == second) suit_tricks[suit2]++;
X	first = second;
X      };
X      move(IOLINE2,0);
X      clrtoeol();
X      if (players[first].computer) {
X	printw("I won that trick.");
X      } else {
X	printw("You won that trick.");
X      };
X      print_hand(&players[0],2);
X      if (debug || autoplay) print_hand(&players[1],18); 
X      update_scores();
X      refresh();
X      if (!autoplay) any_key();
X      /* Check to see if someone has gone down... */
X      if (players[0].whose && players[1].tricks > (13 - topbid) && !autoplay) {
X	tricknum = 14;
X      };
X      if (players[1].whose && players[0].tricks > (13 - topbid) && !autoplay) {
X	tricknum = 14;
X      };
X    } while (tricknum++ < 13);
X    if (autoplay) {
X      /*
X       *  Keep track of some of this stuff for
X       *  analysis purposes.
X       *
X       */
X      for(i=0;i<=3;i++) 
X        if (i == trumpsuit) {
X            fprintf(trumps,"%d %d %d %d\n",nums[i],hl[i],len[i],suit_tricks[i]);
X        } else {
X            fprintf(stats,"%d %d %d %d\n",nums[i],hl[i],len[i],suit_tricks[i]);
X	};
X	fflush(stats);
X	fflush(trumps);
X    };
X
X    /*
X     *  Score this hand.
X     *
X     */
X
X    move(IOLINE1,0);
X    clrtoeol();
X    if (whose == 1) {
X      printw("I ");
X    } else {
X      printw("You ");
X    };
X    if (players[whose].tricks < topbid) {
X      printw("went down for %d",-(topbid*10));
X      players[whose].score -= (topbid*10);
X    } else {
X      printw("made for %d",(topbid*10) + (players[whose].tricks - topbid));
X      players[whose].score += (topbid*10) + (players[whose].tricks - topbid);
X    };
X    players[0].tricks = 0;
X    players[1].tricks = 0;
X    print_hand(&players[0],2);
X    if (debug) print_hand(&players[1],18);
X    update_scores();
X    refresh();
X    if (!autoplay) any_key();
X
X    if(players[whose].score >= 250) {
X      move(IOLINE2,0);
X      clrtoeol();
X      if (whose == 1) {
X	printw("I ");
X      } else {
X	printw("You ");
X      };
X      printw("won a game!");
X      players[whose].games++;
X      players[0].score = 0;
X      players[1].score = 0;
X      update_scores();
X      if (!autoplay) {
X	move(IOLINE3,0);
X	clrtoeol();
X	printw("Play another?");
X	refresh();
X	ans = getch();
X	if (islower(ans)) ans = toupper(ans);
X	end = 1;
X	if (ans == 'Y') end = 0;
X      } else {
X        autoplay--;
X      };
X    };
X  } while (!end || autoplay);
X  endgame();
X};
X
X/*
X *  winner returns TRUE if the first player won the trick.
X *
X */
X
Xint winner(trump,s1,r1,s2,r2)
X     int trump,s1,r1,s2,r2;
X{
X  /* Rough */
X  if(s2 == trump && s1 != trump)
X    return(0);
X  /* Pitch */
X  else if (s1 != s2)
X    return(1);
X  /* r1 is high */
X  else if (r1 > r2)
X    return(1);
X  /* r2 is high */
X  else return(0);
X};
X
Xany_key()
X{
X  char ans;
X  
X  move(IOLINE3,0);
X  clrtoeol();
X  printw("--- Hit any Key to Continue ---");
X  refresh();
X  ans = getch();
X  if (ans == REFRESHKEY) {
X    refresh(curscr);
X  };
X  if (ans == QUITKEY) {
X    endgame();
X  };
X};
X
Xdoscore()
X{
X#ifdef UNIX
X  struct passwd *getpwuid();
X  struct passwd *pwst;
X  int i;
X  char *name, ans;
X  FILE *scorefile;
X
X  if (!scoresexist || hisnum > 100) {
X    return(0);
X  };
X
X  /* Update the score. */
X  if (tricknum < 13 && tricknum != 0) {
X    /* Assume he's ditching */
X    players[1].score += (topbid * 10);
X    if (players[1].score >= 250) {
X      players[1].games++;
X    };
X  };
X
X  /* Found him, so update this score. */
X  scores[hisnum].mygames = players[0].games;
X  scores[hisnum].compgames = players[1].games;
X  scores[hisnum].myscore = players[0].score;
X  scores[hisnum].compscore = players[1].score;
X
X  /* Write out the revised scorefile. */
X  
X  scorefile = fopen(SCOREFILE,"w");
X  if(!scorefile) return(0);
X  for(i=1;i<=numscores;i++) {
X    fprintf(scorefile,"%d %d %d %d %d\n",
X	   scores[i].who,
X	   scores[i].mygames,
X	   scores[i].compgames,
X	   scores[i].myscore,
X	   scores[i].compscore);
X  };
X  close(scorefile);
X  clear();
X  printw("               Running Score\n\n");
X  printw("              You     |     Me\n");
X  printw("            ----------+---------\n");
X  printw("               %d             %d\n\n\n",
X 	   scores[hisnum].mygames,
X	   scores[hisnum].compgames);
X  printw("Hit any key to continue.");
X  refresh();
X  ans = getch();
X#endif UNIX  
X};
X
Xgetscore()
X{
X#ifdef UNIX
X  struct passwd *getpwuid();
X  struct passwd *pwst;
X  int i;
X  char *name, ans;
X  FILE *scorefile;
X
X  uid = getuid();
X
X  scoresexist = 1;
X  scorefile = fopen(SCOREFILE,"r");
X  if(!scorefile) {
X    printf("Cannot open scorefile.  Hit return to continue.");
X    (void) getchar();
X    scoresexist = 0;
X    return(0);
X  };
X  
X  /*  Read the scorefile */
X  numscores = 0;
X  while(numscores <= 100 && !feof(scorefile)) {
X    numscores++;
X    fscanf(scorefile,"%d %d %d %d %d",
X	   &scores[numscores].who,
X	   &scores[numscores].mygames,
X	   &scores[numscores].compgames,
X	   &scores[numscores].myscore,
X	   &scores[numscores].compscore);
X/*    printf("Found %d %d %d %d %d\n",
X	   scores[numscores].who,
X	   scores[numscores].mygames,
X	   scores[numscores].compgames,
X	   scores[numscores].myscore,
X	   scores[numscores].compscore); */
X  };
X
X  /* Remove the spurious extra score caused by the
X     trailing carriage return in the score file.
X   */
X  numscores--;
X
X  /*  See if you can find him somewhere in the scorefile */
X  for(i=0;i<=numscores;i++) {
X    if (scores[i].who == (int) uid) {
X      goto done;
X    };
X  };
X  
X done:
X  
X  if (i <= numscores) {
X    players[1].games = scores[i].compgames;
X    players[0].games = scores[i].mygames;
X    players[0].score = scores[i].myscore;
X    players[1].score = scores[i].compscore;
X    hisnum = i;
X  } else if (numscores < 100) {
X    /* Add him to the score file. */
X    numscores++;
X    hisnum = numscores;
X    scores[numscores].who = uid;
X    scores[numscores].mygames = players[0].games = 0;
X    scores[numscores].compgames = players[1].games = 0;
X    scores[numscores].myscore = players[0].score = 0;
X    scores[numscores].compscore = players[1].score = 0;
X  } else {
X    hisnum = 101;
X  };
X  close(scorefile);
X#endif UNIX  
X};
END_OF_FILE
if test 15768 -ne `wc -c <'choice.c'`; then
    echo shar: \"'choice.c'\" unpacked with wrong size!
fi
# end of 'choice.c'
fi
if test -f 'comp.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'comp.c'\"
else
echo shar: Extracting \"'comp.c'\" \(22267 characters\)
sed "s/^X//" >'comp.c' <<'END_OF_FILE'
X/*
X * This program (called "Choice") is copyright 1989 to Scott R. Turner,
X * in both source code and executable form.  Permission is given to 
X * copy both the source code and the executable under the following
X * conditions:
X * 
X *   1. You may copy and distribute verbatim copies of Choice code as you
X * receive it, in any medium, provided that you conspicuously and
X * appropriately publish on each file a valid copyright notice such as
X * "Copyright (C) 1989 Scott R. Turner", and keep intact the copyright
X * and license notices on all files.  You may charge a distribution fee
X * for the physical act of transferring a copy, but that fee may not
X * exceed your actual costs in creating and delivering the copy.
X * 
X *   2. You may modify your copy or copies of Choice or any portion of it,
X * and copy and distribute such modifications under the terms of
X * Paragraph 1 above, provided that you also do the following:
X * 
X *     a) cause the modified files to carry prominent notices stating
X *     who last changed such files and the date of any change; and
X * 
X *     b) cause the whole of any work that you distribute or publish,
X *     that in whole or in part contains or is a derivative of Choice
X *     or any part thereof, to be licensed at no charge to all third
X *     parties on terms identical to those contained in this License
X *     Agreement (except that you may choose to grant more extensive
X *     warranty protection to third parties, at your option).
X *
X *   3. You may not copy, sublicense, distribute or transfer Choice
X * except as expressly provided under this License Agreement.  Any attempt
X * otherwise to copy, sublicense, distribute or transfer Choice is void and
X * your rights to use Choice under this License agreement shall be
X * automatically terminated.  However, parties who have received computer
X * software programs from you with this License Agreement will not have
X * their licenses terminated so long as such parties remain in full compliance.
X * 
X *   4.  Under no circumstances may you charge for copies of Choice, for copies
X * of any program containing code from Choice in whole or in part, or for 
X * any software package or collection of programs or code that contains Choice
X * in whole or part.
X *
X */
X
X/*
X *  comp.c
X *  Thu Sep 15 10:56:32 1988 -- Scott R. Turner
X *
X *  This file contains the routines for the computer player.
X *
X *  Thu May 11 09:30:24 1989 -- Scott "CBS" Turner
X *
X *  Added new bidder - comp_bid3.
X *
X *  Added new bidder - comp_bid4 - this one cheats by playing out
X *  the hand quietly.
X *
X */
X
X#include "defs.h"
Xextern char *card_string();
Xextern void get_card();
Xextern int randum();
Xextern int winner();
Xextern int any_key();
Xextern FILE *debugf;
X
Xcomp_choice(p,current,deck)
X     player *p;
X     int current;
X     deckt *deck;
X{
X  int rank, suit, num;
X  
X  get_card(current,deck,&rank,&suit);
X
X  /* Current Rules:
X   *
X   * (1) Always take an A or a K.
X   * (2) Keep points if you have 2+ points.
X   * (3) Always pass up a spot card in a void or singleton.
X   * (4) Take any card in a suit with 4+ points.
X   * (5) Take any card above a 9.
X   *
X   * * Mon Oct 10 16:03:55 1988 -- Scott Turner
X   *
X   * Modified so that at some point the computer picks out a
X   * (probable) trump suit and starts collecting that suit.
X   *
X   */
X
X  /* Choose_trump selects a trump suit based on the current
X   * holding.  If it is less than 3 points, then we just ignore
X   * it, and select as if we had no trump suit.
X   */
X  
X  (*p).trump = choose_trump(p);
X  if (suit_points(p,(*p).trump) < 3) (*p).trump = -1;
X  num = suit_size(p,0)+suit_size(p,1)+suit_size(p,2)+suit_size(p,3);
X
X  /* Keep any card in the trump suit. */
X  
X  if (suit == (*p).trump) {
X    goto keepcard;
X  };
X
X  /* Always pass up a spot card in a void or
X   * singleton suit.
X   */
X
X  if (num > 4 && suit_size(p,suit) < 2 && rank < KING) {
X    goto secondcard;
X  };
X
X  /* Keep any Royalty. */
X  
X  if (rank >= JACK) {
X    goto keepcard;
X  };
X
X  /* Take any card in a suit with 5+ points. */
X
X  if (suit_points(p,suit) >= 5) {
X    goto keepcard;
X  };
X
X  /* Take any card in a 5+ length suit. */
X  
X  if (suit_size(p,suit) > 5) {
X    goto keepcard;
X  }
X
X  /* Otherwise take a second card. */
X  
X secondcard:
X
X  if (debug) {
X    move(16,0);
X    printw("Discarded the %s.",card_string(suit,rank));
X  };
X  (*p).cards[suit][rank] = BURIED;
X  get_card(++current,deck,&rank,&suit);
X  if (debug) {
X    move(17,0);
X    printw("Got the %s.",card_string(suit,rank));
X    refresh();
X  };
X  (*p).cards[suit][rank] = MINE;
X  return(1);
X  
X keepcard:
X
X  if (debug) {
X    move(16,0);
X    clrtoeol();
X    move(17,0);
X    printw("Got the %s.",card_string(suit,rank));
X    refresh();
X  };
X  
X  /* Note that get_card doesn't actually update the deck,
X   * so we don't have to get & discard the second card in
X   * this routine.  It is done in main.
X   */
X  
X  (*p).cards[suit][rank] = MINE;
X  return(1);
X
X};
X
X/*
X *  This routine chooses a trumpsuit.
X *
X */
X
Xint choose_trump(p)
X     player *p;
X{
X  int trumprank[4],suit,toprank,ts;
X  
X  /*
X   *  Rank suits for trump potential.
X   *
X   */
X  toprank = 0;
X  ts = 0;
X  for(suit=0;suit<=3;suit++) {
X    /*
X     *  New ranking scheme = points + size**2
X     *
X     *  This makes KJxx better than AKx, which
X     *  I think is reasonable.  For this game,
X     *  length is more important than strength.
X     *
X     */
X    trumprank[suit] = suit_points(p,suit) +
X      suit_size(p,suit) * suit_size(p,suit);
X    if (trumprank[suit] > toprank) {
X      ts = suit;
X      toprank = trumprank[suit];
X    };
X  };
X  return(ts);
X};  
X
X/*
X *  Bidding is probably the toughest part of this game.  Pick
X *  a trump suit, figure to lose all the missing honors, figure
X *  to win all A + K, and figure to win an extra trick somewhere.
X *
X */
X
Xint comp_bid(p,topbid,me)
X     player *p;
X     int topbid,me;
X{
X  int suit,rank;
X  int winners,losers,tricks,num;
X
X  /*
X   *  Count tricks:
X   *
X   *  (1) Cards at the top (i.e., AK..) are winners.
X   *  (2) Missing cards are losers, that must be covered
X   *      by winners.
X   *  (3) Assume he doesn't have more than 4 cards.
X   *
X   */
X
X  tricks = 0;
X  for(suit=0;suit<=3;suit++) {
X      num = 0;
X      losers = 0;
X      winners = 0;
X      for(rank=ACE;rank>=2;rank--) {
X	if (num++ > 4) break;
X	if((*p).cards[suit][rank] == MINE && losers <= 0) {
X	  winners++;
X	} else if ((*p).cards[suit][rank] == MINE) {
X	  losers--;
X	} else if ((*p).cards[suit][rank] == BURIED) {
X	  continue;
X	} else if (losers < 2) {
X	  losers++;
X	};
X      };
X      /*
X       *  At this point, winners is the number of sure winners.
X       *
X       *  Of the rest of the tricks, we'll probably win 1/2 out
X       *  to 4 and then all the rest.
X       *
X       */
X
X      if (suit_size(p,suit) > 4) {
X	winners += (4-winners)/2 + (suit_size(p,suit) - 4);
X      } else {
X	winners += (suit_size(p,suit) - winners)/2;
X      };
X      tricks += winners;
X    };
X  
X  if (debug) {
X    move(15,0);
X    printw("Tricks is %d\n",tricks);
X  };
X  return(tricks);
X
X};
X
X/*
X *  royalty_num
X *
X *  This is a simple little routine that returns the royalty
X *  holding in a suit as a number by converting the holding into
X *  a base 2 number using (AKQJ0) as the five digits.
X *
X */
X
Xint royalty_num(p,suit)
X     player *p;
X     int suit;
X{
X  int total;
X
X  total = 0;
X  if ((*p).cards[suit][ACE] == MINE) total = 16;
X  if ((*p).cards[suit][KING] == MINE) total += 8;
X  if ((*p).cards[suit][QUEEN] == MINE) total += 4;
X  if ((*p).cards[suit][JACK] == MINE) total += 2;
X  if ((*p).cards[suit][10] == MINE) total++;
X  return(total);
X};
X
X/*
X * lenoutside
X *
X * Returns the number of cards held in a suit outside
X * the royalty.
X *
X */
X
Xint lenoutside(p,suit)
X     player *p;
X     int suit;
X{
X  int total,i;
X  total = 0;
X  for(i=2;i<=9;i++)
X    if ((*p).cards[suit][i] == MINE) total++;
X  return(total);
X};
X
X/*
X * highlow
X *
X *  Determines whether the average of the 
X *  outside cards is above or below 5.
X *
X */
X
Xint highlow(p,suit)
X     player *p;
X     int suit;
X{
X  int total,i,num;
X  total = 0;
X  num = 0;
X  for(i=2;i<=9;i++)
X    if ((*p).cards[suit][i] == MINE) {
X        total += i;
X	num++;
X    };
X  if (num && ((float) total) / ((float) num) > 5.0) {
X    return(1);
X  } else {
X    return(0);
X  };
X    
X};
X/*
X *  comp_bid4 cheats.
X *
X */
X 
Xint comp_bid4(p,topbid,m)
X     player *p;
X     int topbid,m;
X{
X    player tmp[2];
X    int other, i, j, first, trumpsuit, tricknum, second;
X    int suit1, rank1, suit2, rank2;
X    void comp_init();
X    int comp_play(), comp_respond(), comp_inform();
X
X    /*
X     *  First copy me and him.
X     *
X     */
X     
X    if (debug) {
X        move(16,0);
X	clrtoeol();
X	printw("About to copy players.\n");
X	refresh();
X    };
X    
X    other = (m + 1) % 2;
X    tmp[0].score = players[m].score;
X    tmp[1].score = players[other].score;
X    tmp[0].games = players[m].games;
X    tmp[1].games = players[other].games;
X    tmp[0].computer = players[m].computer;
X    tmp[1].computer = 1;
X    for(i=0;i<=3;i++) 
X        for(j=0;j<=14;j++) {
X            tmp[0].cards[i][j] = players[m].cards[i][j];
X	    tmp[1].cards[i][j] = players[other].cards[i][j];
X	};
X    trumpsuit = choose_trump(&tmp[0]);
X    for(i=0;i<=1;i++) {
X      tmp[i].whose = 0;
X      tmp[i].trump = trumpsuit;
X      tmp[i].tricks = 0;
X      for(j=0;j<=3;j++)
X	tmp[i].shownout[j] = 0;
X    };
X    tmp[0].whose = 1;
X    comp_init(&tmp[0]);
X    comp_init(&tmp[1]);
X    /*
X     *  Now actually play out the hand (both side computer) to 
X     *  see how many tricks you will win.
X     *
X     */
X    first = 0;
X    tricknum = 1;
X    if (debug) {
X        move(16,0);
X	clrtoeol();
X	printw("About to enter play section.\n");   
X	refresh();
X    };
X    if (debug == 10) {
X        fprintf(debugf,"------Bidding------\n");
X    };
X    do {
X      second = (first + 1) % 2;
X      comp_play(&tmp[first],trumpsuit,&suit1,&rank1);
X      comp_respond(&tmp[second],trumpsuit,suit1,rank1,&suit2,&rank2);
X      comp_inform(&tmp[first],suit1,rank1,suit2,rank2);
X      tmp[first].cards[suit1][rank1] = PLAYED;
X      tmp[second].cards[suit2][rank2] = PLAYED;
X      if (debug == 10) {
X	fprintf(debugf,"On trick %2d, [%1d] %s",
X		tricknum, first, card_string(suit1,rank1));
X        fprintf(debugf,"-- [%1d] %s | %1d.\n",		
X	        second, card_string(suit2, rank2),
X	        winner(trumpsuit,suit1,rank1,suit2,rank2));
X      };
X      if ( winner(trumpsuit,suit1,rank1,suit2,rank2) ) {
X	tmp[first].tricks++;
X      } else {
X	tmp[second].tricks++;
X	first = second;
X      };
X    } while (tricknum++ < 13);
X    if (debug == 10) {
X        fprintf(debugf,"------Done------\n");
X    };
X    if (debug) {
X        move(16,0);
X        clrtoeol();
X        printw("I made %d, Opponent made %d.\n",tmp[0].tricks,tmp[1].tricks);
X        refresh();
X    };
X    /*
X     *  However many we got, that's what we got.
X     *
X     */
X    (*p).trump = tmp[0].trump;
X    return(tmp[0].tricks);
X};
X
X/*
X *  Meta_bid gets a bid from one of the bidding routines and 
X *  then does the "meta" reasoning based on the points left,
X *  etc.
X *
X */
X
Xint meta_bid(p,topbid,me)
X    player *p;
X    int topbid, me;
X{    
X  int other,tricks,tricks2,need;
X
X  (*p).trump = choose_trump(p);
X  other = (me + 1) % 2;
X  switch ((*p).computer) {
X    case 1: tricks = comp_bid(p,topbid,me);
X            break;
X   default: tricks = comp_bid4(p,topbid,me);
X            if (debug) {
X                move(16,0);
X		printw("Cheating bids %d.\n",tricks);
X	    };
X	    /*
X	     *  Slightly different meta rules for this 
X	     *  one.  We'll sack if there is less than a 
X	     *  250 point difference between the two scores
X	     *  and we think he'll make.
X	     *
X	     */
X	    if (tricks < topbid && topbid != 13) {
X	        /*  Will he make? */
X                tricks2 = comp_bid4(&players[other],0,other);
X		/*  If he'll make, and win, and we're only 250 behind, sack. */
X		if (tricks2 >= topbid && 
X		    (players[other].score + (topbid*10) > 250) &&
X		    (players[other].score - players[me].score) <= 250) {
X		        tricks = topbid + 1;
X		};
X	    };
X            if (tricks > topbid) {
X                return(tricks);
X            } else {
X                return(0);
X            };
X  };	    
X      
X  /*
X   *  If we are close to winning, then we don't have to
X   *  bid the whole schmeer.
X   *
X   */
X  need = ((250 - players[me].score) / 10.0) + 0.5;
X  if (need <= tricks && topbid < need) {
X    /*  Bid only what we need to win, if possible. */
X    tricks = need;
X    if (debug) {
X      move(16,0);
X      printw("Bidding what we need.\n");
X    };
X  } else if (need <= tricks && topbid > need && topbid <= tricks) {
X    /*  Bid just over him, if possible. */
X    tricks = topbid + 1;
X    if (debug) {
X      move(16,0);
X      printw("Bidding over him.\n");
X    };
X  } else if (topbid == tricks && players[me].score - players[other].score > 200) {
X    /*  Bid +1 if a comfortable lead. */
X    tricks = topbid + 1;
X    if (debug) {
X        move(16,0);
X	printw("Bidding on a comfortable lead.");
X    };
X  } else if ((topbid-tricks) == 0 && topbid < 11 && randum(10) > 5) {
X    tricks = topbid + 1;
X    if (debug) {
X      move(16,0);
X      printw("Bidding +1.\n");
X    };
X  } else if ((topbid * 10 + players[other].score) >= 250 && topbid < 10 &&
X	     (topbid-tricks) <= 1) {
X    tricks = topbid + 1;
X    if (debug) {
X      move(16,0);
X      printw("Forcing to avoid game.\n");
X    };
X  };
X
X  if (tricks > 13) tricks = 13;
X
X  if (tricks > topbid) {
X    return(tricks);
X  } else {
X    return(0);
X  };
X
X};
X
Xint get_stats(p,nums,len,hl)
X     player *p;
X     int *nums,*len,*hl;
X{
X  int i;
X
X  for(i=0;i<=3;i++) {
X    nums[i] = royalty_num(p,i);
X    len[i] = lenoutside(p,i);
X    hl[i] = highlow(p,i);
X  };
X};
X    
X
X/*
X *  The trumpsuit is calculated above.
X *
X */
X
Xint comp_trump(p,topbid)
X     player *p;
X     int topbid;
X{
X  return((*p).trump);
X};
X  
X/*
X *  comp_play
X *
X *  This function determines what the computer will play when
X *  it is his lead.
X *
X */
X
Xcomp_play(p,trump,suit,rank)
X     player *p;
X     int trump, *suit, *rank;
X{
X  int i,n;
X
X  /*  Current Rules
X   *
X   *  (1) Draw trump if it is your contract.
X   *  (2) Run any suit he's shown out in.
X   *  (3) If AK+ in a suit, then play suit from the top.
X   *  (4) Play from longest suit.
X   *
X   */
X
X  if (debug) {
X    move(16,0);
X    printw("Shownout= %d %d %d %d.\n",(*p).shownout[0],(*p).shownout[1],
X            (*p).shownout[2],(*p).shownout[3]);
X  };
X  /* Draw Trump */
X  
X  if ((*p).whose && !(*p).shownout[trump] && suit_size(p,trump)) {
X    if (debug) {
X      move(15,0);
X      printw("Drawing trump.\n");
X    };
X    *rank = high_card(p,trump);
X    *suit = trump;
X    return(1);
X  };
X
X  /* Run Outside Suits in which he is shown out. */
X  
X  for(i=0;i<=3;i++) 
X    if (i != trump && (*p).shownout[i] && suit_size(p,i) > 0) {
X      if (debug) {
X        move(15,0);
X        printw("Running outside.\n");
X      };
X      *suit = i;
X      *rank = high_card(p,*suit);
X      return(1);
X    };
X  
X  /*
X   *  If you have just one suit and trump left and he hasn't shown
X   *  out in that suit but has shown out in trump, then run trump
X   *  at him before playing out the last suit.
X   *
X   *  Calculating this is kind of ugly. First we count the number
X   *  of suits, keeping the length of the last one.  Then if there
X   *  was only one suit of one card, and we have trump, we go for it.
X   *
X   */
X  n = 0;
X  for(i=0;i<=3;i++) 
X    if (i != trump && suit_size(p,i)) {
X        n++;
X    };
X  if (n == 1 && suit_size(p,trump) > (suit_size(p,i)+1)) {
X    if (debug) {
X      move(15,0);
X      printw("Running trump.\n");
X    };
X    *suit = trump;
X    *rank = high_card(p,*suit);
X    return(1);
X  };
X    
X  /* Play Strong Suits */
X/*
X  for(i=0;i<=3;i++)
X    if (i != trump && control(p,i)) {
X      *suit = i;
X      *rank = high_card(p,*suit);
X      return(1);
X    };
X*/  
X  
X  /* Else lead longest suit. */
X  
X    if (debug) {
X      move(15,0);
X      printw("Playing long suit.\n");
X    };
X  *suit = long_suit(p,trump);
X  *rank = high_card(p,*suit);
X  return(1);
X  
X};
X
X/*
X * control checks for one round of control in the
X * given suit.
X *
X */
X
Xint control(p,suit)
X     player *p;
X     int suit;
X{
X  int rank;
X
X  for(rank=ACE;rank>=2;rank--) {
X    if ((*p).cards[suit][rank] == MINE) {
X      return(1);
X    };
X    if ((*p).cards[suit][rank] == PLAYED ||
X	(*p).cards[suit][rank] == BURIED ||
X	(*p).cards[suit][rank] == HEPLAYED) {
X      continue;
X    };
X    break;
X  };
X
X  return(0);
X};
X
X/*
X *  high_card finds the highest ranked card in a suit, returning
X *  0 if no card.
X *
X */
X
Xint high_card(p,suit)
X     player *p;
X     int suit;
X{
X   int i;
X
X   for(i=14;i>=2;i--)
X     if ((*p).cards[suit][i] == MINE) return(i);
X
X   return(0);
X
X };
X
Xint low_card(p,suit)
X     player *p;
X     int suit;
X{
X   int i;
X
X   for(i=2;i<=14;i++)
X     if ((*p).cards[suit][i] == MINE) return(i);
X
X   return(0);
X
X };
X
Xint low_card_above(p,suit,rank)
X     player *p;
X     int suit,rank;
X{
X   int i;
X
X   for(i=(rank+1);i<=14;i++)
X     if ((*p).cards[suit][i] == MINE) return(i);
X
X   return(0);
X
X };
X
X/*
X *  long_suit returns the longest (non-trump if possible) suit
X *  in the hand.
X *
X */
X
Xint long_suit(p,trump)
X     player *p;
X     int trump;
X{
X  int suit, longsuit, suitlen;
X
X  suitlen = 0;
X  for(suit=0;suit<=3;suit++) 
X    if (suit != trump && suit_size(p,suit) && (*p).len[suit] > suitlen) {
X      suitlen = (*p).len[suit];
X      longsuit = suit;
X    };
X
X  if (suitlen) return(longsuit);
X  else return(trump);
X  
X};
X
X/*
X *  suit_size returns the length of a suit.
X *
X */
X
Xint suit_size(p,suit)
X     player *p;
X     int suit;
X{
X  int rank;
X  int size;
X
X  size = 0;
X
X  for(rank=2;rank<=14;rank++)
X    if((*p).cards[suit][rank] == MINE) size++;
X
X  return(size);
X};
X
X/*
X *  suit_points returns the points in a suit.
X *
X */
X
Xint suit_points(p,suit)
X     player *p;
X     int suit;
X{
X  int rank, points;
X
X  points = 0;
X
X  for(rank=JACK;rank<=ACE;rank++)
X    if((*p).cards[suit][rank] == MINE) points += (rank - JACK) + 1;
X
X  return(points);
X  
X};
X    
X/*
X * missing_points returns the points in a suit
X * that might be in an opponent's hand.  * */
X
Xint missing_points(p,suit)
X     player *p;
X     int suit;
X{
X  int rank, points;
X
X  points = 0;
X
X  for(rank=JACK;rank<=ACE;rank++)
X    if((*p).cards[suit][rank] == UNKNOWN) points += (rank - JACK) + 1;
X
X  return(points);
X  
X};
X
X/*
X *  calc_stop_len returns the number of cards we have to retain
X *  in a suit in order to have a stopper.
X *
X */
X  
Xint calc_stop_len(p,suit)
X     player *p;
X     int suit;
X{
X  int stoplen,rank;
X  
X  stoplen = 1;
X
X  for(rank=ACE;rank>=2;rank--)
X    if((*p).cards[suit][rank] == UNKNOWN) {
X      stoplen++;
X      continue;
X    } else if ((*p).cards[suit][rank] == MINE) {
X      break;
X    };
X
X  return(stoplen);
X  
X};
X
X/*
X *  comp_respond picks a card to play in response to a
X *  lead by the human.
X *
X */
X
Xcomp_respond(p,trump,suit1,rank1,suit2,rank2)
X     player *p;
X     int trump,suit1,rank1,*suit2,*rank2;
X{
X  int size[4],highs[4],lows[4],stoplen[4], outside,
X      suit,longsuit,longlen,runsuit,maxlen;
X  /*
X   *  Note what the player led.
X   *
X   */
X  (*p).cards[suit1][rank1] = HEPLAYED;
X  
X  /*  
X   *  Current Rules
X   *
X   *  (1) Follow suit.
X   *      (a) Win if possible.
X   *      (b) Else play lowest.
X   *  (2) Trump if possible.
X   *  (3) If you have a suit to run
X   *        If you have stoppers in all other suits
X   *        Then pitch below stoppers in other suits.
X   *        Else pitch from running suit
X   *  (4) Else pitch lowest from longest.
X   *
X   */
X
X  longlen = 0;
X  maxlen = 0;
X  runsuit = 5;
X  longsuit = 5;
X  for(suit=0;suit<=3;suit++) {
X    size[suit] = suit_size(p,suit);
X    if (size[suit] > longlen) {
X      longlen = size[suit];
X      longsuit = suit;
X    };
X    if ((*p).shownout[suit] && size[suit] > maxlen) {
X      maxlen = size[suit];
X      runsuit = suit;
X    };
X    highs[suit] = high_card(p,suit);
X    lows[suit] = low_card(p,suit);
X    stoplen[suit] = calc_stop_len(p,suit);
X  };
X  if (debug == 10) {
X    fprintf(debugf,"Maxlen is %d.  Runsuit is %d.  Longsuit is %d.\n",
X            maxlen, runsuit, longsuit);
X	};
X
X  if(highs[suit1] > rank1) {
X    /* We can win this trick. */
X    *suit2 = suit1;
X    *rank2 = low_card_above(p,suit1,rank1);
X  } else if (highs[suit1] > 0) {
X    /* We have this suit but can't win. */
X    *suit2 = suit1;
X    *rank2 = lows[suit1];
X  } else if (size[trump] > 0) {
X    /* We can win with trump. */
X    *suit2 = trump;
X    *rank2 = lows[trump];
X  } else {
X
X    /* Pitching Rules
X     *
X     * (1) If we have a running suit
X     *       If we have a stopper in the other suit
X     *       Then pitch below stoppers
X     *       Else pitch running suit
X     * (2) Pitch lowest from longest.
X     *
X     */
X    if (maxlen) {
X      /* Other suit stopped? */
X      outside = 0;
X      for (suit=0;suit<=3;suit++)
X	if (suit != trump && suit != runsuit &&
X	    size[suit] > 0 &&
X	    size[suit] >= stoplen[suit]) {
X	  if (size[suit] > stoplen[suit]) {
X	    outside = suit;
X	    goto pitchoutside;
X	  };
X	} else {
X	  goto pitchrunning;
X	};
X
X    pitchrunning:
X
X      if (debug == 10) {
X        fprintf(debugf,"Pitchrunning %d %d.\n",runsuit,lows[runsuit]);
X      };
X      *suit2 = runsuit;
X      *rank2 = lows[runsuit];
X      return(1);
X
X    pitchoutside:
X      
X      if (debug == 10) {
X        fprintf(debugf,"Pitchoutside %d %d.\n",outside,lows[outside]);
X      };
X      *suit2 = outside;
X      *rank2 = lows[outside];
X      return(1);
X      
X    } else {  /* If no maxlen, ie., no shown out suit. */
X
X      *suit2 = long_suit(p,trump);
X      *rank2 = lows[*suit2];
X      if (debug == 10) {
X        fprintf(debugf,"Other %d %d.\n", *suit2, *rank2);
X      };
X      return(1);
X    };
X    
X  };
X  return(1);
X};
X
X/*
X *  comp_inform informs the computer what card the human
X *  played on the computer's lead.
X *
X */
X    
Xcomp_inform(p,s1,r1,s2,r2)
X     player *p;
X     int s1,r1,s2,r2;
X{
X  /*
X   *  Note what he played.
X   *
X   */
X
X  (*p).cards[s2][r2] = HEPLAYED;
X
X  /*
X   *  Did he show out?
X   *
X   */
X
X  if (s1 != s2) (*p).shownout[s1] = 1;
X};
X
X/*
X *  comp_init
X *
X *  Initializes the computer player.  Used to store a count
X *  of original suit lengths for use in playing out the hand.
X *
X */
X 
Xvoid comp_init(p)
X    player *p;
X{
X    int i;
X    
X    for(i=0;i<=3;i++) 
X        (*p).len[i] = suit_size(p,i);
X        
X};
END_OF_FILE
if test 22267 -ne `wc -c <'comp.c'`; then
    echo shar: \"'comp.c'\" unpacked with wrong size!
fi
# end of 'comp.c'
fi
if test -f 'human.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'human.c'\"
else
echo shar: Extracting \"'human.c'\" \(7689 characters\)
sed "s/^X//" >'human.c' <<'END_OF_FILE'
X/*
X * This program (called "Choice") is copyright 1989 to Scott R. Turner,
X * in both source code and executable form.  Permission is given to 
X * copy both the source code and the executable under the following
X * conditions:
X * 
X *   1. You may copy and distribute verbatim copies of Choice code as you
X * receive it, in any medium, provided that you conspicuously and
X * appropriately publish on each file a valid copyright notice such as
X * "Copyright (C) 1989 Scott R. Turner", and keep intact the copyright
X * and license notices on all files.  You may charge a distribution fee
X * for the physical act of transferring a copy, but that fee may not
X * exceed your actual costs in creating and delivering the copy.
X * 
X *   2. You may modify your copy or copies of Choice or any portion of it,
X * and copy and distribute such modifications under the terms of
X * Paragraph 1 above, provided that you also do the following:
X * 
X *     a) cause the modified files to carry prominent notices stating
X *     who last changed such files and the date of any change; and
X * 
X *     b) cause the whole of any work that you distribute or publish,
X *     that in whole or in part contains or is a derivative of Choice
X *     or any part thereof, to be licensed at no charge to all third
X *     parties on terms identical to those contained in this License
X *     Agreement (except that you may choose to grant more extensive
X *     warranty protection to third parties, at your option).
X *
X *   3. You may not copy, sublicense, distribute or transfer Choice
X * except as expressly provided under this License Agreement.  Any attempt
X * otherwise to copy, sublicense, distribute or transfer Choice is void and
X * your rights to use Choice under this License agreement shall be
X * automatically terminated.  However, parties who have received computer
X * software programs from you with this License Agreement will not have
X * their licenses terminated so long as such parties remain in full compliance.
X * 
X *   4.  Under no circumstances may you charge for copies of Choice, for copies
X * of any program containing code from Choice in whole or in part, or for 
X * any software package or collection of programs or code that contains Choice
X * in whole or part.
X *
X *//*
X *  human.c
X *  Wed Sep 14 17:15:46 1988 -- Scott R. Turner
X *
X *  This file contains the routines that let the human player
X *  choose and play cards.
X *
X */
X#include "defs.h"
X#include "my_wgets.h"
Xextern char *card_string();
Xextern void get_card();
Xextern void endgame();
X
Xvoid beep()
X{
X  printf("%c",7);
X};
X
Xchar gc()
X{
X  char ans;
X
X  do {
X    ans = getch();
X    if (islower(ans)) ans = toupper(ans);
X    if (ans == QUITKEY) {
X      endgame();
X    };
X    if (ans == SHOWKEY) {
X      print_hand(players[1],18);
X    };
X    if (ans == REFRESHKEY) {
X      wrefresh(curscr);
X    };
X    if (ans == DEBUGKEY) {
X      if (debug) {
X	debug = 0;
X      } else {
X	debug = 1;
X      };
X    };
X  } while (ans == SHOWKEY || ans == REFRESHKEY || ans == DEBUGKEY);
X  return(ans);
X};
X      
X
Xhuman_choice(p,current,deck)
X     player *p;
X     int current;
X     deckt *deck;
X{
X  int rank, suit;
X  char ans;
X  
X  /* First guy */
X  get_card(current,deck,&rank,&suit);
X  move(IOLINE1,0);
X  clrtoeol();
X  printw("Would you like the %s?",card_string(suit,rank));
X  refresh();
X  do {
X    ans = gc();
X  } while (ans != 'Y' && ans != 'N');
X  if(ans == 'Y') {
X    (*p).cards[suit][rank] = MINE;
X  } else {
X    get_card(++current,deck,&rank,&suit);
X    (*p).cards[suit][rank] = MINE;
X    move(IOLINE2,0);
X    clrtoeol();
X    printw("You got the %s.\n",card_string(suit,rank));
X  };
X};
X
Xint human_bid(p,topbid)
X     player *p;
X     int topbid;
X{
X  int bid;
X  char bidstr[80];
X  
X  move(IOLINE1,0);
X  clrtoeol();
X  printw("The current top bid is %d.\n",topbid);
X  do {
X    move(IOLINE2,0);
X    clrtoeol();
X    printw("What would you like to bid? ");
X    refresh();
X    echo();
X    my_getstr(bidstr,80,0);
X    sscanf(bidstr,"%d",&bid);
X    noecho();
X  } while (bid != 0 && bid <= topbid);
X
X  return(bid);
X  
X};
X
Xint human_trump(p,topbid)
X     player *p;
X     int topbid;
X{
X  char trumpsuit;
X
X  move(IOLINE1,0);
X  clrtoeol();
X  printw("What trump suit? ");
X  refresh();
X
X  do {
X    trumpsuit = gc();
X  } while (trumpsuit != 'S' &&
X	   trumpsuit != 'H' &&
X	   trumpsuit != 'D' &&
X	   trumpsuit != 'C');
X
X  switch (trumpsuit) {
X  case 'S': return(0);
X  case 'H': return(1);
X  case 'D': return(2);
X  case 'C': return(3);
X  };
X};
X
X  
X/*
X *  human_play selects a card to lead.
X *
X */
X
Xhuman_play(p,trump,suit,rank)
X     player *p;
X     int trump,*suit,*rank;
X{
X  char ans;
X  int unambig;
X  /*
X   * Let the player type, accepting suits and ranks.
X   * As soon as she types something unambiguous and
X   * legal, play that.
X   *
X   */
X
X  move(IOLINE1,0);
X  clrtoeol();
X  printw("Your lead: ");
X  refresh();
X  *rank = -1;
X  *suit = -1;
X  do {
X    ans = gc();
X    if (strchr("234567890JQKASHCD",ans)) {
X      addch(ans);
X      refresh();
X    } else {
X      beep();
X    };
X    if (ans >= '2' && ans <= '9') {
X      *rank = ans - '0';
X    } else {
X      switch (ans) {
X      case '0':
X	*rank = 10;
X	break;
X      case 'J':
X	*rank = JACK;
X	break;
X      case 'Q':
X	*rank = QUEEN;
X	break;
X      case 'K':
X	*rank = KING;
X	break;
X      case 'A':
X	*rank = ACE;
X	break;
X      case 'S':
X	*suit = SPADES;
X	break;
X      case 'H':
X	*suit = HEARTS;
X	break;
X      case 'D':
X	*suit = DIAMONDS;
X	break;
X      case 'C':
X	*suit = CLUBS;
X	break;
X      };
X    };
X    unambig = unambiguous(p,*suit,*rank);
X    if (unambig > -1) *suit = unambig;
X    if(*rank != -1 && *suit != -1 && unambig == -1) {
X      move(IOLINE2,0);
X      clrtoeol();
X      printw("Sorry, that's not a legal play.");
X      refresh();
X      *rank == -1;
X      *suit == -1;
X    };
X  } while (unambig == -1);
X
X};
X
Xint unambiguous(p,suit,rank)
X     player *p;
X     int suit,rank;
X{
X  int i,j,s;
X
X  if (rank == -1) return(-1);  
X  /* If suit = -1, then rank has to be unambiguous. */
X  if (suit == -1) {
X    j = 0;
X    for(i=0;i<=3;i++)
X      if ((*p).cards[i][rank] == MINE) {
X	j++;
X	s = i;
X      };
X    if (j == 1) {
X      return(s);
X    } else {
X      return(-1);
X    };
X  } else if ((*p).cards[suit][rank] == MINE) {
X    return(suit);
X  } else {
X    return(-1);
X  };
X};
X	
X/*
X * human_respond gets a card in response to the computer's lead.
X *
X */
X
Xhuman_respond(p,trump,s1,r1,suit,rank)
X     player *p;
X     int trump,s1,r1,*suit,*rank;
X{
X  char ans;
X  int unambig;
X  /*
X   * Let the player type, accepting suits and ranks.
X   * As soon as she types something unambiguous and
X   * legal, play that.
X   *
X   */
X
X  move(IOLINE1,0);
X  clrtoeol();
X  printw("I lead %s.  Your play? ",card_string(s1,r1));
X  refresh();
X  *rank = -1;
X  *suit = -1;
X  do {
X    ans = gc();
X    if (strchr("234567890JQKASHCD",ans)) {
X      addch(ans);
X      refresh();
X    } else {
X      beep();
X    };
X    if (ans >= '1' && ans <= '9') {
X      *rank = ans - '0';
X    } else {
X      switch (ans) {
X      case '0':
X	*rank = 10;
X	break;
X      case 'J':
X	*rank = JACK;
X	break;
X      case 'Q':
X	*rank = QUEEN;
X	break;
X      case 'K':
X	*rank = KING;
X	break;
X      case 'A':
X	*rank = ACE;
X	break;
X      case 'S':
X	*suit = SPADES;
X	break;
X      case 'H':
X	*suit = HEARTS;
X	break;
X      case 'D':
X	*suit = DIAMONDS;
X	break;
X      case 'C':
X	*suit = CLUBS;
X	break;
X      };
X    };
X    if (suit_size(p,s1) > 0) *suit = s1;
X    unambig = unambiguous(p,*suit,*rank);
X    if (unambig > -1) *suit = unambig;
X    refresh();
X    if(*rank != -1 && *suit != -1 && unambig == -1) {
X      move(IOLINE2,0);
X      clrtoeol();
X      printw("Sorry, that's not a legal play.");
X      refresh();
X      *rank == -1;
X      *suit == -1;
X    };
X  } while (unambig == -1);
X  
X};
END_OF_FILE
if test 7689 -ne `wc -c <'human.c'`; then
    echo shar: \"'human.c'\" unpacked with wrong size!
fi
# end of 'human.c'
fi
if test -f 'makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'makefile'\"
else
echo shar: Extracting \"'makefile'\" \(559 characters\)
sed "s/^X//" >'makefile' <<'END_OF_FILE'
X#  Makefile for Choice
X
XOBJECTS = utils.o choice.o human.o instruct.o comp.o my_wgets.o
XTCOBJECTS = utils.obj choice.obj human.obj instruct.obj comp.obj my_wgets.obj
XCFLAGS=	-DUNIX -O
X
X.c.obj:
X	tcc -ml -c -O -Z -w -w-pro -f $<
X
Xchoice: $(OBJECTS)
X	cc -o choice $(OBJECTS) -lcurses -ltermcap 
X
Xtcchoice: $(TCOBJECTS)
X	tcc -ml -echoice $(TCOBJECTS) lcurses.lib
X
Xchoice.obj: defs.h
Xutils.obj: defs.h
Xhuman.obj: defs.h my_wgets.h
Xcomp.obj: defs.h
Xinstruct.obj: defs.h
X
Xchoice.o: defs.h
Xutils.o: defs.h
Xhuman.o: defs.h my_wgets.h
Xcomp.o: defs.h
Xinstruct.o: defs.h
END_OF_FILE
if test 559 -ne `wc -c <'makefile'`; then
    echo shar: \"'makefile'\" unpacked with wrong size!
fi
# end of 'makefile'
fi
echo shar: End of archive 1 \(of 2\).
cp /dev/null ark1isdone
MISSING=""
for I in 1 2 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked both archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0