DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T h

⟦ff3306f15⟧ TextFile

    Length: 17784 (0x4578)
    Types: TextFile
    Names: »hotel.c«

Derivation

└─⟦b20c6495f⟧ Bits:30007238 EUUGD18: Wien-båndet, efterår 1987
    └─⟦this⟧ »EUUGD18/General/Hotel/hotel.c« 

TextFile

/*
 * This program (called "Hotel") is copyright 1989 to Scott R. Turner,
 * in both source code and executable form.  Permission is given to 
 * copy both the source code and the executable under the following
 * conditions:
 * 
 * COPYING POLICIES
 * 
 *   1. You may copy and distribute verbatim copies of Hotel code as you
 * receive it, in any medium, provided that you conspicuously and
 * appropriately publish on each file a valid copyright notice such as
 * "Copyright (C) 1989 Scott R. Turner", and keep intact the copyright
 * and license notices on all files.  You may charge a distribution fee for the
 * physical act of transferring a copy, but that fee may not exceed
 * your actual costs in creating and delivering the copy.
 * 
 *   2. You may modify your copy or copies of Hotel or any portion of it,
 * and copy and distribute such modifications under the terms of
 * Paragraph 1 above, provided that you also do the following:
 * 
 *     a) cause the modified files to carry prominent notices stating
 *     who last changed such files and the date of any change; and
 * 
 *     b) cause the whole of any work that you distribute or publish,
 *     that in whole or in part contains or is a derivative of Hotel
 *     or any part thereof, to be licensed at no charge to all third
 *     parties on terms identical to those contained in this License
 *     Agreement (except that you may choose to grant more extensive
 *     warranty protection to third parties, at your option).
 *
 *   3. You may not copy, sublicense, distribute or transfer Hotel
 * except as expressly provided under this License Agreement.  Any attempt
 * otherwise to copy, sublicense, distribute or transfer Hotel is void and
 * your rights to use Hotel under this License agreement shall be
 * automatically terminated.  However, parties who have received computer
 * software programs from you with this License Agreement will not have
 * their licenses terminated so long as such parties remain in full compliance.
 * 
 *   4.  Under no circumstances may you charge for copies of Hotel, for copies
 * of any program containing code from Hotel in whole or in part, or for 
 * any software package or collection of programs or code that contains Hotel
 * in whole or part.
 *
 */ 
/*
 *  hotel.c
 *  Scott R. Turner
 *  9/7/88
 *
 *  The main program and declarations for Hotel.
 *
 */

#include "defs.h"
#include <signal.h>

#ifdef TURBO
#include <stdlib.h>
#include <time.h>
#endif

/* Hotels Array */

hotel hotels[MAXHOTELS+1];

/* Players Array */

player players[MAXPLAYERS+1];

/* Board */

int board[MAXBOARD+1][MAXBOARD+1];

/* Run-time variables */

int numplayers, numhotels, boardsize, numshares, numtiles, startcash;
int turn, maxbuy, debug, pnum;

/* Externals */

extern int share_cost();
extern void majority_bonus();
extern void print_board();
extern int new_hotel();
extern void anykey_help();

void endgame()
{
  nocrmode();
  echo();
  endwin();
  printf("\n");
  exit(1);
};

/*
 *  Quit - a little subroutine to handle the quit key.
 *
 */

void quit()
{
  char ans;
  
  printw("\nAre you sure you want to quit? (Y/N) ");
  refresh();
  ans = getch();
  if (ans == 'Y' || ans == 'y') {
    echo();
    nocrmode();
    endwin();
    exit(1);
  };
};

main ()
{
  int i,j,k,x,y,hotelchoice,played;
  int purchases[MAXHOTELS];
  extern long time();
#ifdef UNIX
  extern void srandom();
#endif
  extern void  human_play();
  extern int  human_new();
  extern int  human_save();
  extern void  human_buy();
  extern void  human_liquidate();
  extern void  comp_play();
  extern int  comp_new();
  extern int  comp_save();
  extern void  get_maj_strategy();
  extern void  comp_liquidate();

  /* Debug flag. */
  
  debug = 0;
  
  /* Initialize random number generator. */

#ifdef UNIX
  srandom((int) time(0));
#else
  randomize();
#endif

  /*
     Curses Initialization.
   */
   
  initscr();
#ifdef UNIX
  nonl();
#endif UNIX
  crmode();

  /* Initialize game. */

  init();
  noecho();

  signal(SIGINT,endgame);
#ifdef UNIX
  signal(SIGQUIT,endgame);
#endif

  /* Game Cycle:

        For each player in the game, execute their "play" and "buy"
	behaviors.
  */
  
  turn = 1;

  while(!game_over()) {

    for (i=1;i<=numplayers;i++) {
    
       /*
        *  If a player has a tile that is permanently unplayable because
        *  it would cause the merger of two safe hotel chains, then he
        *  is allowed to discard that tile and draw a new one.  We do this
        *  automatically for each player at the beginning of each round.
        *
        */

      for(k=1;k<=boardsize;k++)
        for(j=1;j<=boardsize;j++)
	  if (unplayable(k,j)) {
	    if (board[k][j] < 0 && board[k][j] != UNPLAYABLE) 
	    	newtile(-board[k][j]);
	    board[k][j] = UNPLAYABLE;
	  };
	  
      pnum = i;
      move((boardsize+5),0);
      clrtobot();
      played = 0;

      /*
       *  The "play" behavior returns (in &x and &y) the location of
       *  the tile the player would like to play this round.
       *
       */

      if (players[i].strategy) {
	 /*  A computer player. */
	comp_play(i,&x,&y);
	printw("%s played (%d,%d).\n",players[i].name,x,y);
      } else {
      	/*  Human player. */
      	human_play(i,&x,&y);
      };

      /*
       *  If the move is legal and on the board and owned by the player,
       *  then make the move.
       *
       */
      
      if (x > 0 && y > 0 && x <= boardsize && y <= boardsize && legal_play(x,y) &&
	  board[x][y] == -i) {
	
	/*  Place the tile on the board. */
	
	board[x][y] = UNUSED;
	
	/* Check to see if the play merged two hotel chains, or added to a hotel. */

	hotel_merge(x,y,i);

	/* Check to see if the play created a new hotel chain. */

	if (new_hotel(x,y)) {

	  /* It did, so let him choose the hotel chain. */
	  
	  if (players[i].strategy) {
	    hotelchoice = comp_new(i,x,y);
	  } else {
	    hotelchoice = human_new(i,x,y);
          };
	  make_new_hotel(hotelchoice,x,y,i);
	};
	played = 1;
	
      };

      /*
       *  The player is done placing his tile.  Now execute his buy
       *  behavior, which returns (in purchases) the stocks he chooses
       *  to buy.
       *
       */
	
      if (players[i].strategy) {
        get_maj_strategy(i,purchases);
	print_board(human_player);
	refresh();
	any_key();
      } else {
        human_buy(i,purchases);
      };
      /*
       *  Actually make the purchases, checking to see that the
       *  the shares are available, the hotel exists, and he has the money.
       *
       */

      for (j=1;j<=numhotels;j++)
	if (purchases[j] != 0 && hotels[j].size > 0 &&
	    purchases[j] <= hotels[j].shares &&
	    purchases[j]*share_cost(j) <= players[i].cash) {

	  hotels[j].shares -= purchases[j];
	  players[i].shares[j] += purchases[j];
	  players[i].cash -= purchases[j]*share_cost(j);
	};

      /*  Give the player a new tile to replace the one just played. */

      if (played==1) newtile(i);
	  
    };
    turn++;
  };
  standings();
  endwin();
  exit(1);
};

/*
 *  game_over checks to see whether or not the end conditions
 *  have become satisfied.
 *
 */

game_over()
{
  int i,x,y;
  int done = 1, any = 0, available = 0;
  
  /* Game is over if all the hotels on the board are safe,
   * or any chain is >  42% of the board, or if no one has
   * a legal play.
   */
  
  /*
   *  No legal plays...?
   *
   */

  for(x=1;x<=boardsize;x++)
    for(y=1;y<=boardsize;y++)
      if (board[x][y] <= 0 && board[x][y] != UNPLAYABLE &&
          !unplayable(x,y)) available++;
      
  if (!available) return(1);

  /*
   *  Big hotel?
   *
   */
  for(i=1;i<=numhotels;i++)
    if(hotels[i].size > (.42 * boardsize * boardsize)) return(1);
  /*
   *  Just started?
   *
   */
  for(i=1;i<=numhotels;i++)
    if(hotels[i].size > 0) {
    	any = 1;
	break;
    };
  if (!any) return(0);
  for(i=1;i<=numhotels;i++)
    if(hotels[i].size > 0 && !safe(i)) {
      done = 0;
      break;
    };
  return(done);
  
};
 
/*
 *  make_new_hotel makes a new hotel (hotel #n) around x,y.
 *
 */
  
make_new_hotel(n,x,y,pl)
     int n,x,y,pl;
{
  int change,i,j;
  
  board[x][y] = n;
  hotels[n].size++;
  spread_hotel(n,x,y);

  /*  Give the player one free share, if available. */

  if (hotels[n].shares > 0) {
    hotels[n].shares -= 1;
    players[pl].shares[n] += 1;
  };

};
		
/*
 *  hotel_merge checks to see if the newly placed tile at (x,y) merges two 
 *  hotel chains.
 * 
 */  
	   
hotel_merge(x,y,p)
  int x,y,p;
{
  int adj[MAXHOTELS+1],shares[MAXHOTELS+1];
  int i, j, k, count, max, dup, winner, hot, maxhot;
  int hold, sell, trade, numtraded, cp;
  int max1, max2, num1, num2, first, second;

  /*
   *  Start by making a list of all the hotels that this 
   *  new play is adjacent to.
   * 
   */

  for(i=1;i<=numhotels;i++) {
    adj[i] = 0;
    shares[i] = 0;
  };
  count = 0;
  max =  0;
  dup = 0;

  for(i= -1;i<2;i++)
    for(j= -1;j<2;j++) {
	hot = board[x+i][y+j];
	if ((i*j == 0) && (i != 0 || j != 0) &&
	    ((x+i) > 0) && ((y+j) > 0) &&
	    ((x+i) <= boardsize) && ((y+j) <= boardsize) &&
	    (hot > 0) && (hot != UNUSED)) {
	  if (!adj[hot]) count++;
	  adj[hot] = hotels[hot].size;
	  shares[hot] = share_cost(hot);
	};
      };

  for(i=1;i<=numhotels;i++) 
    if (adj[i] == max) {
      dup++;
    } else if (adj[i] > max) {
      dup = 0;
      max = adj[i];
      maxhot = i;
    };
      
  /*  If count = 0, we weren't adjacent to anything.  */

  if (count == 0) return(0);
                                                                                                 
  /*  If count = 1, we were adjacent to a single hotel, so spread that hotel. */

  if (count == 1) {
	 spread_hotel(maxhot,x,y);
	 return(1);
       };
  
  /* If count > 1, then we are merging two or more chains. */      

  /* If dup > 0, then we have two chains of the same size,
   * so the player gets to choose which to dump.  If dup = 0,
   * then there is a clear winner, and we find it.
   */

  if (dup > 0) 
    if (players[p].strategy) {
        maxhot = comp_save(p,max,adj);
    } else {
        maxhot = human_save(p,max,adj);
    };

  /*
   *  Hand out the majority bonuses for all the sunken hotels.
   *
   */

  for(i=1;i<=numhotels;i++)
    if(i != maxhot && adj[i] != 0) do_maj_bonuses(i);

  /*
   *  After the other chains have been sunk, we need to give people
   *  a chance to liquidate, starting with the current player and 
   *  continuing in play order.
   *
   */

  for(j=1;j<=numhotels;j++)
    if(adj[j] && j != maxhot)
      for(i=0;i<numplayers;i++){
	cp = ((p+i)%numplayers) + 1;
	if (players[cp].shares[j] > 0) {

          if (players[cp].strategy) {
            comp_liquidate(cp,maxhot,j,&sell,&trade);
	  } else {
            human_liquidate(cp,maxhot,j,&sell,&trade);
	  };

	  /* Sell as many as he asked. */

	  if (sell <= players[cp].shares[j] && sell > 0) {
	    players[cp].cash += sell*share_cost(j);
	    players[cp].shares[j] -= sell;
	    hotels[j].shares += sell;
	  };
	  
	  /* Trade as many as possible. */

	  if (trade <= players[cp].shares[j] && trade > 0) {
	    numtraded = (hotels[maxhot].shares <= (int) (trade / 2)) ? (hotels[maxhot].shares * 2) :
	      trade;
	    players[cp].shares[j] -= numtraded;
	    hotels[j].shares += numtraded;
	    players[cp].shares[maxhot] += round(numtraded / 2);
	    hotels[maxhot].shares -= round(numtraded / 2);
	  };

	  /* Everything left gets held. */
	};
	
      };

  /*
   *  Merge all the chains other than the winner into the winner.
   *
   */

  for(i=1;i<=numhotels;i++)
   if(i != maxhot && adj[i] != 0)
     for(x=1;x<=boardsize;x++)   
        for(y=1;y<=boardsize;y++)
          if (board[x][y] == i) {
             board[x][y] = maxhot;
             hotels[i].size--;
             hotels[maxhot].size++;
          };

  /*
   *  Finally, spread the winning hotel, in case the new tile also
   *  touched some unused tiles.
   *
   */
  
  spread_hotel(maxhot,x,y);
  
};

/*
 *  Spread_hotel iteratively changes unused tiles into the hotel
 *  they are next to. 
 *
 */

spread_hotel(n,x,y)
  int n,x,y;
{
  int i,j,change;
  
  change = 1;

  while(change){
    change = 0;
    for(x=1;x<=boardsize;x++)
      for(y=1;y<=boardsize;y++)
	if(board[x][y] == n)
	  for(i= -1;i<2;i++)
	    for(j= -1;j<2;j++)
	      if((i*j == 0) && (i != 0 || j != 0) && (x+i) > 0 && (x+i) <= boardsize &&
		 (y+j) > 0 && (y+j) <= boardsize &&
		 board[x+i][y+j] == UNUSED) {
		board[x+i][y+j] = n;
		hotels[n].size++;
		change = 1;
	      };
  };

};
 
/*
 *  safe returns true if a hotel is safe, i.e., > 10
 *
 */

safe(hot)
     int hot;
{
  if (hotels[hot].size > 10) return(1);
  else return(0);
};

/*
 *  legal_play returns false if the play would create a (numhotels + 1) hotel
 *
 */

legal_play(x,y)
     int x,y;
{
  int i,j,new;

  /* Is the new tile adjacent to an existing hotel? */

  new = 1;
  for(i= -1;i<2;i++)
    for(j= -1;j<2;j++)
      if ( i*j == 0 && (i != 0 || j != 0) &&
	  ((x+i) > 0) && ((y+j) > 0) &&
	  ((x+i) <= boardsize) && ((y+j) <= boardsize) &&
	  (board[x+i][y+j] > 0) && (board[x+i][y+j] < UNUSED)) {
        new = 0;
	break;
      };

  if (new == 0) return(1);

  /* Is there an UNUSED adjacent? */

  new = 0;
  for(i= -1;i<2;i++)
    for(j= -1;j<2;j++)
      if ( i*j == 0 && (i != 0 || j != 0) &&
	  ((x+i) > 0) && ((y+j) > 0) &&
	  ((x+i) <= boardsize) && ((y+j) <= boardsize) &&
	  (board[x+i][y+j] == UNUSED)) {
	new = 1;
	goto done;
      };
  
 done:

  if (new == 0) return(1);
  
  /* Is there an available hotel? */

  new = 0;
  for(i=1;i<=numhotels;i++)
    if(hotels[i].size == 0) {
     new = 1;
     break;
   };

  return(new);

};

/*
 *  unplayable returns true if the play would merge two "safe" hotels.
 *
 */

unplayable(x,y)
     int x,y;
{
  
  int i,j,count,adj[MAXHOTELS+1];

  for(i=1;i<=numhotels;i++)
    adj[i] = 0;

  for(i= -1;i<2;i++)
    for(j= -1;j<2;j++)
      if (i*j == 0 && (i != 0 || j != 0) &&
	  ((x+i) > 0) && ((y+j) > 0) &&
	  ((x+i) <= boardsize) && ((y+j) <= boardsize) &&
	  (board[x+i][y+j] != UNUSED) &&
	  (board[x+i][y+j] > 0) &&
	  safe(board[x+i][y+j]))
	adj[board[x+i][y+j]] = 1;

  count = 0;
  for(i=1;i<=numhotels;i++)
    if(adj[i]) count++;
    
  if (count > 1 && debug) {
  	printw("(%d, %d): ",x,y);
  	for(i=1;i<=numhotels;i++)
    	  if(adj[i]) printw("%c ",'A'+i-1);
  	printw("%d\n",count);
	refresh();
	any_key();
  };
 
  if (count > 1) return(1);
  else return(0);

};

/*
 *  standings calculates and  prints the value of everyone at the endgame.
 *
 */

standings()
{
  int i,j,order[MAXPLAYERS+1],tmp;

  clear();
  printw("The game is over!.\n\n");
  for(i=1;i<=numhotels;i++)
    if(hotels[i].size > 0) do_maj_bonuses(i);

  for(i=1;i<=numplayers;i++)
    for(j=1;j<=numhotels;j++)
      if (hotels[j].size > 0) players[i].cash += players[i].shares[j] * share_cost(j);

  /*  Sort  */
  
  for(i=1;i<=numplayers;i++) order[i] = i;

  for(j=1;j<=numplayers;j++)
    for(i=1;i<=(numplayers-1);i++)
      if (players[order[i]].cash < players[order[i+1]].cash) {
	tmp = order[i];
	order[i] = order[i+1];
	order[i+1] = tmp;
      };

  clear();
  printw("Here are the final standings:\n");
  for(i=1;i<=numplayers;i++)
    printw("  (%d) %s:  $%ld\n",i,players[order[i]].name,
      				players[order[i]].cash);
  refresh();
  any_key();
  
};

any_key()

{
  char ans;
  int y,x;

  clrtoeol();
  printw("--- Hit any Key to Continue ---");
  getyx(stdscr,y,x);
  refresh();
topofanykey:
  ans = getch();
  if (islower(ans)) ans = toupper(ans);
/*
 *  Removed in production version...
 *
 
  if (ans == 'D') {
  	if (!debug) debug = 1;
	else debug = 0;
	goto topofanykey;
  };
 
 *
 */
 
  if (ans == REFRESHKEY) {
    wrefresh(curscr);
    goto topofanykey;
  };
  if (ans == HELPKEY) {
    anykey_help();
    goto topofanykey;
  };
  if (ans == QUITKEY) {
    quit();
  };
  move(y,0);
  clrtoeol();
};



do_maj_bonuses(i)
  int i;
{
  int j,k,max1,num1,max2,num2,first,second;

  max1 = 0;
  num1 = 0;
  max2 = 0;
  num2 = 0;

  for(j=1;j<=numplayers;j++) {
    if (players[j].shares[i] > max1) {
      max2 = max1;
      num2 = num1;
      max1 = players[j].shares[i];
      num1 = 1;
    } else if (players[j].shares[i] == max1) {
      num1++;
    } else if (players[j].shares[i] > max2) {
      max2 = players[j].shares[i];
      num2 = 1;
    } else if (players[j].shares[i] == max2) {
      num2++;
    };
  };

  majority_bonus(i,&first,&second);

  if (max2 == 0 || num1 > 1) {

    /*
     *  The top dogs split both.
     *
     */
     
    for(k=1;k<=numplayers;k++)
      if (players[k].shares[i] == max1) {
        if (players[k].strategy == 0) {
	    printw("%s get $%d Majority Bonus for %s\n",
	       players[k].name, round((first + second) / num1), hotels[i].name);
	} else {
	    printw("%s gets $%d Majority Bonus for %s\n",
	       players[k].name, round((first + second) / num1), hotels[i].name);
	};
	players[k].cash += round((first + second) / num1);
      };
  } else
    for (k=1;k<=numplayers;k++)
      if (players[k].shares[i] == max1) {
        if (players[k].strategy == 0) {
	    printw("%s get %d Majority Bonus (First) for %s\n",
	       players[k].name, first, hotels[i].name);
	} else {
	    printw("%s gets %d Majority Bonus (First) for %s\n",
	       players[k].name, first, hotels[i].name);
	};
	players[k].cash += round(first);
      } else if (players[k].shares[i] == max2) {
        if (players[k].strategy == 0) {
	printw("%s get %d Majority Bonus (Second) for %s\n",
	       players[k].name, round(second/num2), hotels[i].name);
	} else {
	    printw("%s gets %d Majority Bonus (Second) for %s\n",
	       players[k].name, round(second/num2), hotels[i].name);
	};
	players[k].cash += round(second/num2);
      };
  refresh();
  any_key();

};