gb_games.w
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:19k
源码类别:

通讯编程

开发平台:

Visual C++

  1. % This file is part of the Stanford GraphBase (c) Stanford University 1993
  2. @i boilerplate.w %<< legal stuff: PLEASE READ IT BEFORE MAKING ANY CHANGES!
  3. @i gb_types.w
  4. deftitle{GB_,GAMES}
  5. prerequisites{GB_,GRAPH}{GB_,IO}
  6. @* Introduction. This GraphBase module contains the |games| subroutine,
  7. which creates a family of undirected graphs based on college football
  8. scores. An example of the use of this procedure can be
  9. found in the demo program {sc FOOTBALL}.
  10. @(gb_games.h@>=
  11. extern Graph *games();
  12. @ The subroutine call |games|(|n|, |ap0_weight|, |upi0_weight|, |ap1_weight|,
  13. |upi1_weight|, |first_day|, |last_day|, |seed|)
  14. constructs a graph based on the information in .{games.dat}.
  15. Each vertex of the graph corresponds to one of 120 football teams
  16. at American colleges and universities (more precisely, to the 106 college
  17. football teams of division I-A together with the 14 division I-AA teams
  18. of the Ivy League and the Patriot League).
  19. Each edge of the graph corresponds to one of the 638 games played
  20. between those teams during the 1990 season.
  21. An arc from vertex~|u| to vertex~|v| is assigned a length representing
  22. the number of points scored by |u| when playing~|v|. Thus the graph
  23. isn't really ``undirected,'' although it is true that its arcs are
  24. paired (i.e., that |u| played~|v| if and only if |v| played~|u|).
  25. A truly undirected graph with the same vertices and edges can be obtained
  26. by applying the |complement| routine of {sc GB_,BASIC}.
  27. The constructed graph will have $min(n,120)$ vertices. If |n| is less
  28. than 120, the |n| teams will be selected by assigning a weight to
  29. each team and choosing the |n| with largest weight, using random
  30. numbers to break ties in case of equal weights. Weights are computed
  31. by the formula
  32. $$ |ap0_weight|cdot|ap0|+|upi0_weight|cdot|upi0|
  33.    +|ap1_weight|cdot|ap1|+|upi1_weight|cdot|upi1|, $$
  34. where |ap0| and |upi0| are the point scores given to a team in the
  35. Associated Press and United Press International polls at the beginning
  36. of the season, and |ap1| and |upi1| are the similar scores given at
  37. the end of the season. (The \{ap} scores were obtained by asking 60
  38. sportswriters to choose and rank the top 25 teams, assigning 25 points
  39. to a team ranked 1st and 1 point to a team ranked 25th; thus the
  40. total of each of the \{ap} scores, summed over all teams,
  41. is 19500. The \{upi} scores were
  42. obtained by asking football coaches to choose and rank the top 15
  43. teams, assigning 15 points to a team ranked 1st and 1 point to a team
  44. ranked 15th. In the case of \{upi0}, there were 48 coaches voting,
  45. making 5760 points altogether; but in the case of \{upi1}, 59 coaches
  46. were polled, yielding a total of 7080 points. The coaches agreed not
  47. to vote for any team that was on probation for violating NCAA rules,
  48. but the sportswriters had no such policy.)
  49. Parameters |first_day| and |last_day| can be used to vary the number of
  50. edges; only games played between |first_day| and |last_day|, inclusive,
  51. will be included in the constructed graph. Day~0 was August~26, 1990,
  52. when Colorado and Tennessee competed in the Disneyland Pigskin Classic.
  53. Day~128 was January~1, 1991, when the final end-of-season bowl games
  54. were played. About half of each team's games were played between day~0 and
  55. day~50. If |last_day=0|, the value of |last_day| is automatically
  56. increased to~128.
  57. As usual in GraphBase routines, you can set |n=0| to get the default
  58. situation where |n| has its maximum value. For example, either
  59. |games(0,0,0,0,0,0,0,0)| or |games(120,0,0,0,0,0,0,0)| produces the full graph;
  60. |games(0,0,0,0,0,50,0,0)| or |games(120,0,0,0,0,50,0,0)|
  61. or |games(120,0,0,0,0,50,128,0)| produces the graph for the last half
  62. of the season. One way to select a subgraph containing the
  63. 30 ``best'' teams is to ask for |games(30,0,0,1,2,0,0,0)|, which adds
  64. the votes of the sportswriters to the votes of the coaches
  65. (considering that a coach's first choice is worth 30 points
  66. while a sportswriter's first choice is worth only 25). It turns out
  67. that 67 of the teams did not receive votes in any of the four polls;
  68. the subroutine call |games(53,1,1,1,1,0,0,0)| will pick out the 53 teams
  69. that were selected at least once by some sportswriter or coach, and
  70. |games(67,-1,-1,-1,-1,0,0,0)| will pick out the 67 that were not.
  71. A~random selection of 60 teams can be obtained by calling
  72. |games(60,0,0,0,0,0,0,s)|. Different choices of the seed number~|s|
  73. will produce different selections in a system-independent manner;
  74. any value of |s| between 0 and $2^{31}-1$ is permissible.
  75. If you ask for |games(120,0,0,0,0,0,0,s)| with different choices of~|s|,
  76. you always get the full graph, but the vertices will appear in different
  77. (random) orderings depending on~|s|.
  78. Parameters |ap0_weight|, |upi0_weight|, |ap1_weight|, and |upi1_weight| must be
  79. at most $2^{17}=131072$ in absolute value.
  80. @d MAX_N 120
  81. @d MAX_DAY 128
  82. @d MAX_WEIGHT 131072
  83. @d ap u.I /* Associated Press scores: |(ap0<<16)+ap1| */
  84. @d upi v.I /* United Press International scores |(upi0<<16)+upi1| */
  85. @ Most of the teams belong to a ``conference,'' and they play against
  86. almost every other team that belongs to the same conference. For
  87. example, Stanford and nine other teams belong to the
  88. Pacific Ten conference. Eight of Stanford's eleven games were against
  89. other teams of the Pacific Ten; the other three were played against
  90. Colorado (from the Big Eight), San Jos'e State (from the Big West)
  91. and Notre Dame (which is independent). The graphs produced by |games|
  92. therefore illustrate ``cliquey'' patterns of social interaction.
  93. Eleven different conferences are included in .{games.dat}. Utility
  94. field |z.S| of a vertex is set to the name of a team's conference, or to |NULL|
  95. if that team is independent. (Exactly 24 of the I-A football teams
  96. were independent in 1990.) Two teams |u| and |v| belong to the same
  97. conference if and only if |u->conference==v->conference| and
  98. |u->conference!=NULL|.
  99. @d conference z.S
  100. @ Each team has a nickname, which is recorded in utility field |y.S|.
  101. For example, Georgia Tech's team is called the Yellow Jackets.
  102. Six teams (Auburn, Clemson, Memphis State, Missouri, Pacific, and
  103. Princeton) are called the Tigers, and five teams
  104. (Fresno State, Georgia, Louisiana Tech, Mississippi State,
  105. Yale) are called the Bulldogs. But most of the teams have a unique
  106. nickname, and 94 distinct nicknames exist.
  107. A shorthand code for team names is also provided, in the |abbr| field.
  108. @d nickname y.S
  109. @d abbr x.S
  110. @ If |a| points to an arc from |u| to |v|, utility field |a->a.I| contains
  111. the value 3 if |u| was the home team, 1 if |v| was the home team, and 2 if both
  112. teams played on neutral territory. The date of that game, represented
  113. as a integer number of days after August~26, 1990, appears in utility
  114. field |a->b.I|. The arcs in each vertex list |v->arcs| appear in reverse order
  115. of their dates: last game first and first game last.
  116. @d HOME 1
  117. @d NEUTRAL 2 /* this value is halfway between |HOME| and |AWAY| */
  118. @d AWAY 3
  119. @d venue a.I
  120. @d date b.I
  121. @(gb_games.h@>=
  122. #define ap @[u.I@] /* repeat the definitions in the header file */
  123. #define upi @[v.I@]
  124. #define abbr @[x.S@]
  125. #define nickname @[y.S@]
  126. #define conference @[z.S@]
  127. #define HOME 1
  128. #define NEUTRAL 2
  129. #define AWAY 3
  130. #define venue @[a.I@]
  131. #define date @[b.I@]
  132. @ If the |games| routine encounters a problem, it returns |NULL|
  133. (.{NULL}), after putting a code number into the external variable
  134. |panic_code|. This code number identifies the type of failure.
  135. Otherwise |games| returns a pointer to the newly created graph, which
  136. will be represented with the data structures explained in {sc GB_,GRAPH}.
  137. (The external variable |panic_code| is itself defined in {sc GB_,GRAPH}.)
  138. @d panic(c) @+{@+panic_code=c;@+gb_trouble_code=0;@+return NULL;@+}
  139. @ The CEE/ file .{gb_games.c} has the following overall shape:
  140. @p
  141. #include "gb_io.h" /* we will use the {sc GB_,IO} routines for input */
  142. #include "gb_flip.h"
  143.  /* we will use the {sc GB_,FLIP} routines for random numbers */
  144. #include "gb_graph.h" /* we will use the {sc GB_,GRAPH} data structures */
  145. #include "gb_sort.h" /* and |gb_linksort| for sorting */
  146. @h@#
  147. @<Type declarations@>@;
  148. @<Private variables@>@;
  149. @<Private functions@>@;
  150. @#
  151. Graph *games(n,ap0_weight,upi0_weight,ap1_weight,upi1_weight,
  152.      first_day,last_day,seed)
  153.   unsigned long n; /* number of vertices desired */
  154.   long ap0_weight; /* coefficient of |ap0| in the weight function */
  155.   long ap1_weight; /* coefficient of |ap1| in the weight function */
  156.   long upi0_weight; /* coefficient of |upi0| in the weight function */
  157.   long upi1_weight; /* coefficient of |upi1| in the weight function */
  158.   long first_day; /* lower cutoff for games to be considered */
  159.   long last_day; /* upper cutoff for games to be considered */
  160.   long seed; /* random number seed */
  161. {@+@<Local variables@>@;@#
  162.   gb_init_rand(seed);
  163.   @<Check that the parameters are valid@>;
  164.   @<Set up a graph with |n| vertices@>;
  165.   @<Read the first part of .{games.dat} and compute team weights@>;
  166.   @<Determine the |n| teams to use in the graph@>;
  167.   @<Put the appropriate edges into the graph@>;
  168.   if (gb_close()!=0)
  169.     panic(late_data_fault);
  170.       /* something's wrong with |"games.dat"|; see |io_errors| */
  171.   gb_free(working_storage);
  172.   if (gb_trouble_code) {
  173.     gb_recycle(new_graph);
  174.     panic(alloc_fault); /* oops, we ran out of memory somewhere back there */
  175.   }
  176.   return new_graph;
  177. }
  178. @ @<Local var...@>=
  179. Graph *new_graph; /* the graph constructed by |games| */
  180. register long j,k; /* all-purpose indices */
  181. @ @<Check that the parameters are valid@>=
  182. if (n==0 || n>MAX_N) n=MAX_N;
  183. if (ap0_weight>MAX_WEIGHT || ap0_weight<-MAX_WEIGHT ||
  184.     upi0_weight>MAX_WEIGHT || upi0_weight<-MAX_WEIGHT ||@|
  185.     ap1_weight>MAX_WEIGHT || ap1_weight<-MAX_WEIGHT ||
  186.     upi1_weight>MAX_WEIGHT || upi1_weight<-MAX_WEIGHT)
  187.   panic(bad_specs); /* the magnitude of at least one weight is too big */
  188. if (first_day<0) first_day=0;
  189. if (last_day==0 || last_day>MAX_DAY) last_day=MAX_DAY;
  190. @ @<Set up a graph with |n| vertices@>=
  191. new_graph=gb_new_graph(n);
  192. if (new_graph==NULL)
  193.   panic(no_room); /* out of memory before we're even started */
  194. sprintf(new_graph->id,"games(%lu,%ld,%ld,%ld,%ld,%ld,%ld,%ld)",
  195.   n,ap0_weight,upi0_weight,ap1_weight,upi1_weight,first_day,last_day,seed);
  196. strcpy(new_graph->util_types,"IIZSSSIIZZZZZZ");
  197. @* Vertices.
  198. As we read in the data, we construct a list of nodes, each of which contains
  199. a team's name, nickname, conference, and weight. After this list
  200. has been sorted by weight, the top |n| entries will be the vertices of the 
  201. new graph.
  202. @<Type decl...@>=
  203. typedef struct node_struct { /* records to be sorted by |gb_linksort| */
  204.   long key; /* the nonnegative sort key (weight plus $2^{30}$) */
  205.   struct node_struct *link; /* pointer to next record */
  206.   char name[24]; /* |"College Name"| */
  207.   char nick[22]; /* |"Team Nickname"| */
  208.   char abb[6]; /* |"ABBR"| */
  209.   long a0,u0,a1,u1; /* team scores in press polls */
  210.   char *conf; /* pointer to conference name */
  211.   struct node_struct *hash_link; /* pointer to next .{ABBR} in hash list */
  212.   Vertex *vert; /* vertex corresponding to this team */
  213. } node;
  214. @ The data in .{games.dat} appears in two parts. The first 120 lines
  215. have the form
  216. $$hbox{tt ABBR College Name(Team Nickname)Conference;a0,u0;a1,u1}$$
  217. and they give basic information about the teams. An internal abbreviation code
  218. .{ABBR} is used to identify each team in the second part of the data.
  219. The second part presents scores of the games, and it
  220. contains two kinds of lines. If the first character of a line is
  221. `.>', it means ``change the current date,'' and the remaining
  222. characters specify a date as a one-letter month code followed by the day
  223. of the month. Otherwise the line gives scores of a game, using the
  224. .{ABBR} codes for two teams. The scores are separated by `.@@' if
  225. the second team was the home team and by `.,' if both teams were on
  226. neutral territory.
  227. For example, two games were played on December 8, namely the annual Army-Navy
  228. game and the California Raisin Bowl game. These are recorded in three lines
  229. of .{games.dat} as follows:
  230. $$vbox{halign{tt#hfilcr
  231. >D8cr
  232. NAVY20@@ARMY30cr
  233. SJSU48,CMICH24cr}}$$
  234. We deduce that Navy played at Army's home stadium, losing 20 to~30;
  235. moreover, San Jos'e State played Central Michigan on neutral territory and
  236. won, 48 to~24. (The California Raisin Bowl is traditionally a playoff between
  237. the champions of the Big West and Mid-American conferences.)
  238. @ In order to map .{ABBR} codes to team names, we use a simple
  239. hash coding scheme. Two abbreviations with the same hash address are
  240. linked together via the |hash_link| address in their node.
  241. The constants defined here are taken from the specific data in .{games.dat},
  242. because this routine is not intended to be perfectly general.
  243. @d HASH_PRIME 1009
  244. @<Private v...@>=
  245. static long ma0=1451,mu0=666,ma1=1475,mu1=847;
  246.              /* maximum poll values in the data */
  247. static node *node_block; /* array of nodes holding team info */
  248. static node **hash_block; /* array of heads of hash code lists */
  249. static Area working_storage; /* memory needed only while |games| is working */
  250. static char **conf_block; /* array of conference names */
  251. static long m; /* the number of conference names known so far */
  252. @ @<Read the first part of .{games.dat} and compute team weights@>=
  253. node_block=gb_typed_alloc(MAX_N+2,node,working_storage);
  254.  /* leave room for string overflow */
  255. hash_block=gb_typed_alloc(HASH_PRIME,node*,working_storage);
  256. conf_block=gb_typed_alloc(MAX_N,char*,working_storage);
  257. m=0;
  258. if (gb_trouble_code) {
  259.   gb_free(working_storage);
  260.   panic(no_room+1); /* nowhere to copy the data */
  261. }
  262. if (gb_open("games.dat")!=0)
  263.   panic(early_data_fault); /* couldn't open |"games.dat"| using
  264.         GraphBase conventions; |io_errors| tells why */
  265. for (k=0; k<MAX_N; k++) @<Read and store data for team |k|@>;
  266. @ @<Read and store...@>=
  267. {@+register node *p;
  268.   register char *q;
  269.   p=node_block+k;
  270.   if (k) p->link=p-1;
  271.   q=gb_string(p->abb,' ');
  272.   if (q>&p->abb[6] || gb_char()!=' ')
  273.     panic(syntax_error); /* out of sync in .{games.dat} */
  274.   @<Enter |p->abb| in the hash table@>;
  275.   q=gb_string(p->name,'(');
  276.   if (q>&p->name[24] || gb_char()!='(')
  277.     panic(syntax_error+1); /* team name too long */
  278.   q=gb_string(p->nick,')');
  279.   if (q>&p->nick[22] || gb_char()!=')')
  280.     panic(syntax_error+2); /* team nickname too long */
  281.   @<Read the conference name for |p|@>;
  282.   @<Read the press poll scores for |p| and compute |p->key|@>;
  283.   gb_newline();
  284. }
  285. @ @<Enter |p->abb| in the hash table@>=
  286. {@+long h=0; /* the hash code */
  287.   for (q=p->abb;*q;q++)
  288.     h=(h+h+*q)%HASH_PRIME;
  289.   p->hash_link=hash_block[h];
  290.   hash_block[h]=p;
  291. }
  292. @ @<Read the conference name for |p|@>=
  293. {
  294.   gb_string(str_buf,';');
  295.   if (gb_char()!=';') panic(syntax_error+3); /* conference name clobbered */
  296.   if (strcmp(str_buf,"Independent")!=0) {
  297.     for (j=0;j<m;j++)
  298.       if (strcmp(str_buf,conf_block[j])==0) goto found;
  299.     conf_block[m++]=gb_save_string(str_buf);
  300.  found:p->conf=conf_block[j];
  301.   }
  302. }
  303. @ The key value computed here will be between 0 and~$2^{31}$, because of
  304. the bound we've imposed on the weight parameters.
  305. @<Read the press poll scores for |p| and compute |p->key|@>=
  306. p->a0=gb_number(10);
  307. if (p->a0>ma0 || gb_char()!=',') panic(syntax_error+4);
  308.   /* first AP score clobbered */
  309. p->u0=gb_number(10);
  310. if (p->u0>mu0 || gb_char()!=';') panic(syntax_error+5);
  311.   /* first UPI score clobbered */
  312. p->a1=gb_number(10);
  313. if (p->a1>ma1 || gb_char()!=',') panic(syntax_error+6);
  314.   /* second AP score clobbered */
  315. p->u1=gb_number(10);
  316. if (p->u1>mu1 || gb_char()!='n') panic(syntax_error+7);
  317.   /* second UPI score clobbered */
  318. p->key=ap0_weight*(p->a0)+upi0_weight*(p->u0)
  319.         +ap1_weight*(p->a1)+upi1_weight*(p->u1)+0x40000000;
  320. @ Once all the nodes have been set up, we can use the |gb_linksort|
  321. routine to sort them into the desired order. It builds 128
  322. lists from which the desired nodes are readily accessed in decreasing
  323. order of weight, using random numbers to break ties.
  324. We set the abbreviation code to zero in every team that isn't chosen. Then
  325. games involving that team will be excluded when edges are generated below.
  326.  
  327. @<Determine the |n| teams to use in the graph@>=
  328. {@+register node *p; /* the current node being considered */
  329.   register Vertex *v=new_graph->vertices; /* the next vertex to use */
  330.   gb_linksort(node_block+MAX_N-1);
  331.   for (j=127; j>=0; j--)
  332.     for (p=(node*)gb_sorted[j]; p; p=p->link) {
  333.       if (v<new_graph->vertices+n) @<Add team |p| to the graph@>@;
  334.       else p->abb[0]=''; /* this team is not being used */
  335.     }
  336. }
  337. @ @<Add team |p| to the graph@>=
  338. {
  339.   v->ap=((long)(p->a0)<<16)+p->a1;
  340.   v->upi=((long)(p->u0)<<16)+p->u1;
  341.   v->abbr=gb_save_string(p->abb);
  342.   v->nickname=gb_save_string(p->nick);
  343.   v->conference=p->conf;
  344.   v->name=gb_save_string(p->name);
  345.   p->vert=v++;
  346. }
  347. @* Arcs.
  348. Finally, we read through the rest of .{games.dat}, adding a pair of
  349. arcs for each game that belongs to the selected time interval
  350. and was played by two of the selected teams.
  351. @<Put the appropriate edges into the graph@>=
  352. {@+register Vertex *u,*v;
  353.   register long today=0; /* current day of play */
  354.   long su,sv; /* points scored by each team */
  355.   long ven; /* |HOME| if |v| is home team, |NEUTRAL| if on neutral ground */
  356.   while (!gb_eof()) {
  357.     if (gb_char()=='>') @<Change the current date@>@;
  358.     else gb_backup();
  359.     u=team_lookup();
  360.     su=gb_number(10);
  361.     ven=gb_char();
  362.     if (ven=='@@') ven=HOME;
  363.     else if (ven==',') ven=NEUTRAL;
  364.     else panic(syntax_error+8); /* bad syntax in game score line */
  365.     v=team_lookup();
  366.     sv=gb_number(10);
  367.     if (gb_char()!='n') panic(syntax_error+9);
  368.       /* bad syntax in game score line */
  369.     if (u!=NULL && v!=NULL && today>=first_day && today<=last_day)
  370.       @<Enter a new edge@>;
  371.     gb_newline();
  372.   }
  373. }
  374. @ @<Change the current...@>=
  375. {@+register char c=gb_char(); /* month code */
  376.   register long d; /* day of football season */
  377.   switch(c) {
  378.   case 'A': d=-26;@+break; /* August */
  379.   case 'S': d=5;@+break; /* thirty days hath September */
  380.   case 'O': d=35;@+break; /* October */
  381.   case 'N': d=66;@+break; /* November */
  382.   case 'D': d=96;@+break; /* December */
  383.   case 'J': d=127;@+break; /* January */
  384.   default: d=1000;
  385.   }
  386.   d+=gb_number(10);
  387.   if (d<0 || d>MAX_DAY) panic(syntax_error-1); /* date was clobbered */
  388.   today=d;
  389.   gb_newline(); /* now ready to read a non-date line */
  390. }
  391. @ @<Private f...@>=
  392. static Vertex *team_lookup() /* read and decode an abbreviation */
  393. {@+register char *q=str_buf; /* position in |str_buf| */
  394.   register long h=0; /* hash code */
  395.   register node *p; /* position in hash list */
  396.   while (gb_digit(10)<0) {
  397.    *q=gb_char();
  398.    h=(h+h+*q)%HASH_PRIME;
  399.    q++;
  400.   }
  401.   gb_backup(); /* prepare to re-scan the digit following the abbreviation */
  402.   *q=''; /* null-terminate the abbreviation just scanned */
  403.   for (p=hash_block[h];p;p=p->hash_link)
  404.     if (strcmp(p->abb,str_buf)==0) return p->vert;
  405.   return NULL; /* not found */
  406. }
  407. @ We retain the convention of {sc GB_,GRAPH} that the arc from |v| to |u|
  408. appears immediately after a matching arc from |u| to |v| when |u<v|.
  409. @<Enter a new edge@>=
  410. {@+register Arc *a;
  411.   if (u>v) {@+register Vertex *w; register long sw;
  412.     w=u;@+u=v;@+v=w;
  413.     sw=su;@+su=sv;@+sv=sw;
  414.     ven=HOME+AWAY-ven;
  415.   }
  416.   gb_new_arc(u,v,su);
  417.   gb_new_arc(v,u,sv);
  418.   a=u->arcs; /* a pointer to the new arc */
  419.   if (v->arcs!=a+1) panic (impossible+9); /* can't happen */
  420.   a->venue=ven;@+(a+1)->venue=HOME+AWAY-ven;
  421.   a->date=(a+1)->date=today;
  422. }
  423. @* Index. As usual, we close with an index that
  424. shows where the identifiers of \{gb_games} are defined and used.