|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T h
Length: 17784 (0x4578) Types: TextFile Names: »hotel.c«
└─⟦b20c6495f⟧ Bits:30007238 EUUGD18: Wien-båndet, efterår 1987 └─⟦this⟧ »EUUGD18/General/Hotel/hotel.c«
/* * 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(); };