|
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 c
Length: 21117 (0x527d) Types: TextFile Names: »comptbl.c«
└─⟦b20c6495f⟧ Bits:30007238 EUUGD18: Wien-båndet, efterår 1987 └─⟦this⟧ »EUUGD18/General/Cubes/comptbl.c«
/* vi:set sw=4 ts=4: */ #ifndef lint static char sccsid[] = "@(#)comptbl.c 5.1 (G.M. Paris) 89/01/22"; #endif lint /* ** ** cubes 5.1 Copyright 1989 Gregory M. Paris ** Permission granted to redistribute on a no charge basis. ** All other rights are reserved. ** */ /* ** This module is a real mixed bag. Various functions and definitions ** that were necessary to compile a standalone history file analyzer ** have been moved here from all over, but mainly from cubeserver.c. ** It still takes some dummy stubs to compile... */ #include <stdio.h> #include <syslog.h> #include <strings.h> #include "cubes.h" extern int winscore; extern boolean inprogress; extern winpref gametype; extern boolean nameinuse(); extern boolean iskamelion(); extern boolean kamelionplaying(); extern unsigned nhist; extern history *hist; extern history *histbyname(); extern char *malloc(); extern char *moniker(); player plr[PLAYERS]; /* defined here for standalone use */ extern strategy st_basic, st_merrier, st_respect, st_twohater, st_greedy; extern strategy st_picky, st_finicky, st_choosy, st_kamelion, st_fickle; extern temper te_robot, te_antsy, te_tracker, te_chicken, te_gambler; extern temper te_crafty, te_shakey, te_zeno, te_goalie, te_best; extern temper te_kamelion, te_schizo; /* ** comptbl: list of available computer players ** comments indicate the source of each name */ computer comptbl[] = { { "CUBEX", &st_basic, &te_robot, Nopref }, /* original */ { "UNIBLAB", &st_merrier, &te_antsy, Fickle }, /* Jetsons */ { "Rosie", &st_choosy, &te_goalie, Nopref }, /* Jetsons */ { "Brainiac", &st_twohater, &te_chicken, Standard }, /* comics */ { "Wotan", &st_greedy, &te_goalie, Nopref }, /* Dr. Who */ { "K-2", &st_choosy, &te_robot, Nopref }, /* Dr. Who */ { "K-9", &st_basic, &te_goalie, Nopref }, /* Dr. Who */ { "M5", &st_merrier, &te_tracker, Nopref }, /* Star Trek */ { "NOMAD", &st_respect, &te_goalie, Nopref }, /* Star Trek */ { "Landrew", &st_twohater, &te_gambler, Blitz }, /* Star Trek */ { "Norman", &st_choosy, &te_zeno, Nopref }, /* Star Trek */ { "Stella 500", &st_picky, &te_antsy, Nopref }, /* Star Trek */ { "Alice 3", &st_choosy, &te_tracker, Nopref }, /* Star Trek */ { "The Old Man", &st_merrier, &te_chicken, Fstand }, /* T. Zone */ { "Agnes", &st_respect, &te_gambler, Blitz }, /* T. Zone */ { "Dr. Theopolis", &st_twohater, &te_robot, Nopref }, /* B. Rogers */ { "Tweekie", &st_greedy, &te_antsy, Nopref }, /* B. Rogers */ { "Gort", &st_picky, &te_tracker, Nopref }, /* tDtESS */ { "Robbie", &st_basic, &te_chicken, Standard }, /* various */ { "HAL9000", &st_merrier, &te_gambler, Fblitz }, /* Clarke */ { "SAL9000", &st_respect, &te_robot, Nopref }, /* Clarke */ { "Mother", &st_twohater, &te_antsy, Nopref }, /* Alien */ { "Ashe", &st_greedy, &te_tracker, Nopref }, /* Alien */ { "Bishop", &st_picky, &te_chicken, Standard }, /* Aliens */ { "W.O.P.R.", &st_basic, &te_gambler, Blitz }, /* War Games */ { "The Terminator", &st_merrier, &te_robot, Nopref }, /* title role */ { "Number Five", &st_respect, &te_antsy, Nopref }, /* title role */ { "Jenkins", &st_twohater, &te_tracker, Nopref }, /* Simak */ { "R. Daneel Olivaw",&st_greedy, &te_chicken, Standard }, /* Asimov */ { "GAX", &st_picky, &te_gambler, Blitz }, /* Software */ { "TEX", &st_basic, &te_robot, Nopref }, /* Software */ { "MEX", &st_choosy, &te_antsy, Nopref }, /* Software */ { "BEX", &st_respect, &te_tracker, Nopref }, /* Software */ { "DEX", &st_choosy, &te_chicken, Standard }, /* Software */ { "LIBEX", &st_greedy, &te_gambler, Blitz }, /* Wetware */ { "Mr. Frostee", &st_picky, &te_robot, Nopref }, /* Software */ { "Ralph Numbers", &st_basic, &te_antsy, Nopref }, /* Software */ { "Wagstaff", &st_merrier, &te_goalie, Nopref }, /* Software */ { "Berenice", &st_respect, &te_chicken, Standard }, /* Wetware */ { "Emul", &st_twohater, &te_goalie, Nopref }, /* Wetware */ { "Ulalume", &st_greedy, &te_robot, Nopref }, /* Wetware */ { "Oozer", &st_picky, &te_goalie, Nopref }, /* Wetware */ { "Kkandio", &st_basic, &te_tracker, Nopref }, /* Wetware */ { "Jander Panell", &st_finicky, &te_crafty, Fickle }, /* Asimov */ { "Eddie", &st_picky, &te_shakey, Fickle }, /* HHGttG */ { "Artoo Detoo", &st_basic, &te_zeno, Nopref }, /* Star Wars */ { "Marvin", &st_finicky, &te_tracker, Nopref }, /* HHGttG */ { "Giskard Reventlov",&st_picky, &te_crafty, Fickle }, /* Asimov */ { "Speedy", &st_basic, &te_shakey, Fblitz }, /* Asimov */ { "See Threepio", &st_finicky, &te_zeno, Nopref }, /* Star Wars */ { "R. Geronimo", &st_basic, &te_crafty, Fickle }, /* Asimov */ { "Pondermatic", &st_finicky, &te_shakey, Fstand }, /* HHGttG */ { "Deep Thought", &st_picky, &te_zeno, Nopref }, /* HHGttG */ { "Xoanon", &st_finicky, &te_antsy, Fickle }, /* Dr. Who */ { "R. Sammy", &st_merrier, &te_crafty, Fickle }, /* Asimov */ { "Helen", &st_merrier, &te_zeno, Nopref }, /* Wetware */ { "Huey", &st_merrier, &te_shakey, Blitz }, /* Sil. Run. */ { "Duey", &st_respect, &te_shakey, Blitz }, /* Sil. Run. */ { "Luey", &st_choosy, &te_shakey, Blitz }, /* Sil. Run. */ { "the robot (B9)", &st_finicky, &te_robot, Nopref }, /* L.i.Space */ { "SV7", &st_respect, &te_zeno, Nopref }, /* Dr. Who */ { "Earth", &st_choosy, &te_gambler, Fstand }, /* HHGttG */ { "Roderick", &st_respect, &te_crafty, Fickle }, /* Sladek */ { "Tik-Tok", &st_choosy, &te_crafty, Fickle }, /* Sladek */ { "IG-88", &st_finicky, &te_goalie, Nopref }, /* S. Wars II */ { "Roger Korby", &st_twohater, &te_zeno, Fickle }, /* Star Trek */ { "Rayna Kapec", &st_finicky, &te_chicken, Nopref }, /* Star Trek */ { "V'ger", &st_finicky, &te_gambler, Nopref }, /* ST:TMP */ { "M. Gargantubrain",&st_greedy, &te_crafty, Standard }, /* HHGttG */ { "Neutron Wrangler",&st_greedy, &te_shakey, Standard }, /* HHGttG */ { "M.P. Titan Muller",&st_greedy, &te_zeno, Standard }, /* HHGttG */ { "Ruk", &st_twohater, &te_crafty, Fickle }, /* Star Trek */ { "Andrea", &st_twohater, &te_shakey, Fickle }, /* Star Trek */ { "Orac", &st_choosy, &te_best, Nopref }, /* Blake's 7 */ /* ** If you have more names, mail them to me... ** ** Kamelion must be last. */ { "Kamelion", &st_kamelion, &te_kamelion, Fickle } /* Dr. Who */ }; #define COMPUTERS (sizeof comptbl / sizeof comptbl[0]) int computers = COMPUTERS; computer *pkamelion = &comptbl[COMPUTERS - 1]; computer proxy = { "**proxy**", &st_fickle, &te_schizo, Nopref }; /* ** initcomptbl: initialize computer table (no-op) */ initcomptbl() { } /* ** compbyname: return a pointer to the computer with the matching name */ computer * compbyname(name) register char *name; { register int n; for(n = 0;n < computers;++n) if(strncmp(name, comptbl[n].c_name, IDLEN) == 0) return &comptbl[n]; return (computer *)0; } /* ** addcomp: add the COMP computer ** a game is never in progress when this happens */ addcomp(c) int c; { plr[c].p_fd = -1; plr[c].p_score = 0; plr[c].p_onboard = False; plr[c].p_stat = Computer; plr[c].p_id[0] = '\0'; /* non-proxies are null */ plr[c].p_computer = &comptbl[0]; strcpy(plr[c].p_name, plr[c].p_computer->c_name); setpref(c); } /* ** pickproxy: give this player the proxy personality ** Doesn't change p_stat, in case this is a temporary proxy. */ pickproxy(c) int c; { if(plr[c].p_computer != &proxy) plr[c].p_computer = &proxy; if(plr[c].p_mood == NOMOOD) plr[c].p_mood = randint(MAXMOOD); /* don't change preference */ } /* ** nextupcmp: comparison function for producing next-up order ** sorts computers before humans ** sorts the COMP computer before other computers ** sorts computers to have best play more often than worst ** NOTE: histsort() must be called first for proper operation. */ nextupcmp(h1, h2) history *h1, *h2; { register int diff; /* ** Computers before humans. */ if((h1->h_computer == 0) != (h2->h_computer == 0)) return h1->h_computer == 0 ? 1 : -1; /* ** COMP computer before all others. */ if(h1->h_computer == &comptbl[0]) return -1; if(h2->h_computer == &comptbl[0]) return 1; /* ** We cycle through the list of computers, but with the modification ** that the higher ranked players will play more often than the lower. */ diff = 2 * (h1->h_lastgame - h2->h_lastgame); diff += h1->h_rank - h2->h_rank; if(diff != 0) return diff; /* ** For tie breaking, the player with the least games played goes next. */ if((diff = h1->h_games - h2->h_games) != 0) return diff; /* ** For further tiebreaking, prefer more wins, higher points, and ** higher turn average, in that order. (Note: games equal here.) */ if((diff = h2->h_wins - h1->h_wins) != 0) return diff; if((diff = h2->h_points - h1->h_points) != 0) return diff; if((diff = h2->h_avgturn - h1->h_avgturn) != 0) return diff; return 0; } /* ** pickcomputer: give this player an inactive computer personality */ pickcomputer(c) int c; { register int comp; register history *thist; unsigned size; static boolean allinhist = False; plr[c].p_stat = Computer; plr[c].p_id[0] = '\0'; /* not a proxy */ plr[c].p_mood = randint(MAXMOOD); /* mood set on entering */ /* ** Usually, we'd like to select computers that haven't played much ** (the more highly ranked the better), but this method will never ** pick a computer that's not in the history file. What we do then ** is randomly select this method, and if it fails, we still fall ** back on random selection from the comptbl. */ thist = 0; if(randint(100) <= 85) { size = nhist * sizeof *hist; if((thist = (history *)malloc(size)) == 0) syslog(LOG_WARNING, "pickcomputer: no memory for dup history"); } if(thist != 0) { /* ** Copy the history info and sort it into a desirable order. ** Step through the list to find a computer player. */ bcopy((char *)hist, (char *)thist, (int)size); qsort((char *)thist, (int)nhist, sizeof *thist, nextupcmp); for(comp = 0;comp < (int)nhist;++comp) { if((thist+comp)->h_computer == 0) continue; if(randint(3) != 1) /* 67% chance */ continue; plr[c].p_computer = (thist+comp)->h_computer; if(iskamelion(c) == True) { if(kamelionplaying(c) == True) continue; setkamelion(c); } else { if(nameinuse(c, plr[c].p_computer->c_name) == True) continue; strcpy(plr[c].p_name, plr[c].p_computer->c_name); setpref(c); if(inprogress == True) { /* picky late joiners */ switch(plr[c].p_pref) { case Fstand: if(gametype != Standard) continue; break; case Fblitz: if(gametype != Blitz) continue; break; case Standard: if(gametype != Standard) continue; break; case Blitz: if(gametype != Blitz) continue; break; } } } free((char *)thist); return; } free((char *)thist); } /* ** Check to see if there are any computer players that have never played. ** If one is found use it. If none are found, make sure we don't waste ** our time here again. We don't do this every time we get here because ** we want to ration out the new computers. */ if(allinhist == False && randint(100) <= 20) { for(comp = 1;comp < computers;++comp) { if(comptbl[comp].c_name[0] == '\0') /* placeholder */ continue; if(histbyname(comptbl[comp].c_name) != 0) continue; plr[c].p_computer = &comptbl[comp]; if(iskamelion(c) == True) { if(kamelionplaying(c) == True) continue; setkamelion(c); } else { if(nameinuse(c, plr[c].p_computer->c_name) == True) continue; strcpy(plr[c].p_name, plr[c].p_computer->c_name); setpref(c); if(inprogress == True) { /* semi-picky late joiners */ switch(plr[c].p_pref) { case Fstand: if(gametype != Standard) continue; break; case Fblitz: if(gametype != Blitz) continue; break; } } } return; } allinhist = True; } /* ** Randomly select a computer from the comptbl. This routine cannot ** fail as long as PLAYERS is less than the number of computers. */ for(;;) { comp = randint(computers - 1); /* don't pick zero */ if(comptbl[comp].c_name[0] == '\0') /* placeholder */ continue; plr[c].p_computer = &comptbl[comp]; if(iskamelion(c) == True) { if(kamelionplaying(c) == True) continue; setkamelion(c); } else { if(nameinuse(c, plr[c].p_computer->c_name) == True) continue; strcpy(plr[c].p_name, plr[c].p_computer->c_name); setpref(c); if(inprogress == True) { /* semi-picky late joiners */ switch(plr[c].p_pref) { case Fstand: if(gametype != Standard) continue; break; case Fblitz: if(gametype != Blitz) continue; break; } } } return; } } /* ** setkamelion: Kamelion adopts the personality of other computers ** and the moniker of other players */ setkamelion(c) { register int comp; register char *aka; register history *phist; /* ** Pick a computer player's personality for Kamelion to mimic. ** Kamelion wants to be a good player, so we pick from the top ** of the history (best ranked players). */ for(comp = 0;comp < nhist;++comp) { phist = hist + comp; if(phist->h_computer == 0 || phist->h_computer == pkamelion) continue; if(inprogress == True) { /* semi-picky late joiners */ switch(phist->h_computer->c_pref) { case Fstand: if(gametype != Standard) continue; break; case Fblitz: if(gametype != Blitz) continue; break; } } if(randint(4) != 1) /* 75% chance */ continue; pkamelion->c_strategy->s_func = phist->h_computer->c_strategy->s_func; pkamelion->c_temper->t_func = phist->h_computer->c_temper->t_func; pkamelion->c_pref = phist->h_computer->c_pref; break; } /* ** If the above method failed, just select randomly from comptbl. */ if(comp >= nhist) { do { comp = randint(computers - 1); /* don't pick zero, COMP */ } while(&comptbl[comp] == pkamelion); pkamelion->c_strategy->s_func = comptbl[comp].c_strategy->s_func; pkamelion->c_temper->t_func = comptbl[comp].c_temper->t_func; pkamelion->c_pref = comptbl[comp].c_pref; } /* ** Pick a name for Kamelion to assume. Kamelion appears to be a proxy. */ while(nameinuse(c, (aka = moniker())) == True) ; strncpy(plr[c].p_name, aka, NAMELEN); plr[c].p_name[NAMELEN-1] = '\0'; strncpy(plr[c].p_id, pkamelion->c_name, IDLEN); plr[c].p_id[IDLEN-1] = '\0'; /* ** Make sure that p_computer is set. Set preference. */ plr[c].p_computer = pkamelion; setpref(c); } /* ** setpref: set p_pref for a computer player */ setpref(c) { player *p = &plr[c]; if(p->p_computer != 0) { if((p->p_pref = p->p_computer->c_pref) == Fickle) { switch(randint(9)) { case 1: case 5: p->p_pref = Standard; break; case 2: case 6: p->p_pref = Blitz; break; case 3: p->p_pref = Fstand; break; case 4: p->p_pref = Fblitz; break; default: p->p_pref = Nopref; break; } } } } /* ** iscomp: return True if player is the COMP computer */ boolean iscomp(c) { if(plr[c].p_stat != Computer) return False; if(plr[c].p_computer != &comptbl[0]) return False; return True; } /* ** isproxy: return True if player is a proxy computer ** There are two kinds of proxies, permanent and temporary. ** Permanent proxy: p_stat == Computer, p_computer == &proxy. ** Temporary proxy: p_stat == Active, p_computer == &proxy. */ boolean isproxy(c) { if(plr[c].p_computer == &proxy) if(plr[c].p_stat == Computer || plr[c].p_stat == Active) return True; return False; } /* ** iskamelion: return True if player is Kamelion */ boolean iskamelion(c) { if(plr[c].p_stat != Computer) return False; if(plr[c].p_computer != pkamelion) return False; return True; } /* ** nameinuse: return True if name in use by other than numbered player */ boolean nameinuse(c, name) int c; char *name; { register int cc; for(cc = 0;cc < PLAYERS;++cc) if(cc != c && plr[cc].p_stat != Inactive) if(strncmp(plr[cc].p_name, name, NAMELEN-1) == 0) return True; return False; } /* ** kamelionplaying: return True if Kamelion is already playing */ boolean kamelionplaying(comp) { register int c; for(c = 0;c < PLAYERS;++c) if(c != comp && iskamelion(c) == True) return True; return False; } /* ** highscore: find highest score amongst other players ** If phighc is not zero, stores index of high scorer ** or -1 if there's a tie. */ highscore(comp, phighc) int comp, *phighc; { register int c; int high, highc; high = 0, highc = -1; for(high = c = 0;c < PLAYERS;++c) if(c != comp) { switch(plr[c].p_stat) { case Computer: case Active: if(plr[c].p_score > high) high = plr[(highc = c)].p_score; else if(plr[c].p_score == high) highc = -1; break; } } if(phighc != 0) *phighc = highc; return high; } /* ** sc_: structure for sorting scores */ struct sc_ { int sc_c, sc_p; }; /* ** sc_comp: compare struct sc_ values, largest sc_p first */ static int sc_comp(s1, s2) struct sc_ *s1, *s2; { return s2->sc_p - s1->sc_p; } /* ** closescore: find the highest score that's close enough to compete with ** If closec is not zero, stores index of the selected scorer ** or -1 if there's a tie. */ closescore(comp, pclosec) int comp, *pclosec; { register int c; int mine, diff; int high, highc; struct sc_ sc[PLAYERS]; #define UPPERLIM 3000 /* beyond this is too far ahead */ #define LOWERLIM -1000 /* below this is too far behind */ /* ** Sort the scores best first. */ for(c = 0;c < PLAYERS;++c) { if((sc[c].sc_c = c) == comp) { sc[c].sc_p = -1; /* cause end search */ continue; } switch(plr[c].p_stat) { case Computer: case Active: sc[c].sc_p = plr[c].p_score; break; default: sc[c].sc_p = -2; /* cause end search */ break; } } qsort((char *)sc, PLAYERS, sizeof sc[0], sc_comp); /* ** Find a close score to track. */ mine = plr[comp].p_score; high = 0, highc = -1; for(c = 0;c < PLAYERS && sc[c].sc_p >= 0;++c) { if((diff = sc[c].sc_p - mine) > UPPERLIM) continue; if(diff < LOWERLIM && c > 0) --c; high = sc[c].sc_p; if(pclosec != 0) { if(c+1 >= PLAYERS || sc[c+1].sc_p != high) highc = sc[c].sc_c; } break; } if(pclosec != 0) *pclosec = highc; return high; #undef UPPERLIM #undef LOWERLIM } /* ** lowscore: report the lowest score ** Also calculates an average squander value. */ lowscore(psqu) int *psqu; { register int cc, low, squ, n; #define HIGHVAL (2^15) /* "impossible" high score */ /* ** Look for lowest score. Sum squandered points. */ low = HIGHVAL, squ = 0, n = 0; for(cc = 0;cc < PLAYERS;++cc) { switch(plr[cc].p_stat) { case Active: case Computer: if(plr[cc].p_score < low) low = plr[cc].p_score; if(psqu != 0) squ += plr[cc].p_squander, ++n; break; } } if(low == HIGHVAL) low = 0; /* ** If a non-null pointer was passed, calculate an ** average squandered value and pass it back. */ if(psqu != 0) { if(n == 0) *psqu = 0; /* no players?! */ else { squ = (squ + n - 1) / n; /* average, rounded up */ squ = squ - (squ % P_FIVE); /* mult. of P_FIVE, rounded down */ *psqu = squ; } } return low; #undef HIGHVAL } /* ** winner: return winner number or -1 if none */ winner() { register int c; register int high, next; register int highc; /* ** Find high score. If less than winscore, no winner. */ for(high = c = 0;c < PLAYERS;++c) { switch(plr[c].p_stat) { case Computer: case Active: if(plr[c].p_score > high) high = plr[(highc = c)].p_score; break; } } if(high < winscore) return -1; /* ** Find next to highest score. If margin is less than ** WINMARGIN, there's no winner yet. */ for(next = c = 0;c < PLAYERS;++c) { if(c == highc) continue; switch(plr[c].p_stat) { case Computer: case Active: if(plr[c].p_score > next) next = plr[c].p_score; break; } } if(high - next < WINMARGIN) return -1; /* ** We have a winner! */ return highc; } /* ** plrcmp: player comparison function */ plrcmp(p1, p2) player *p1, *p2; { int diff; /* ** Sort by status first. */ if(p1->p_stat == p2->p_stat) { switch(p1->p_stat) { case Inactive: case Watching: case Waiting: return 0; } } else { switch(p2->p_stat) { case Computer: diff = 3; break; /* Computer == Active */ case Active: diff = 3; break; /* Computer == Active */ case Waiting: diff = 2; break; case Watching: diff = 1; break; default: diff = 0; break; } switch(p1->p_stat) { case Computer: diff -= 3; break; /* Computer == Active */ case Active: diff -= 3; break; /* Computer == Active */ case Waiting: diff -= 2; break; case Watching: diff -= 1; break; default: break; } if(diff != 0) return diff; } /* ** Sort in order of increasing score. */ if((diff = p1->p_score - p2->p_score) != 0) return diff; /* ** Put Computer players after humans. */ if(p1->p_stat != p2->p_stat) return (p1->p_stat == Computer) ? 1 : -1; /* ** Sort alphabetically. */ return strcmp(p1->p_name, p2->p_name); }