/* MakeDraw: make a draw, starting from a ranked list of teams The list of teams should be sorted, such that #1 is at the top, ranking is done by a parameters that controls from which location onwards players are considered all equal. The normal way is to assign a 1st and 2nd seed, the #3/4 seeds are considered equal, and 5-8 can be considered shadow seeds. Players 9-16 can again be clumped together, but considered equal. * * 0.1 12-oct-1994 initial writeup for DC Open 94 - peter teuben * 0.2 2-oct-1999 cleanup for DC Open 99 * 0.3 26-oct-1999 also allow finishing random draw (full=t) * 0.4 31-oct-1999 more work on the 9...16 places * 0.5 2-nov-1999 allow randomize on some levels * 0.6 4-nov-1999 optional add match IDs to matches per round * 0.7 25-oct-2001 matchid -1 won't write that column * 0.8 7-mar-2002 IBF/USAB rules * * Todo: implement shadow and normal seeds properly, use an array of nrandom= ?? (wo)men doubles need alphabetized order of players */ #include #include "draw.h" string defv[] = { "in=\n Input teams for this draw", "out=-\n Output draw, can be used in e.g. drawplot", "p=4\n level depth ; 2**n is number of players ", "seed=0\n Random seed", "full=f\n Finish a full random draw (testing)", "auto=f\n Automatically advance first round byes", "nrandom=0\n Set slot beyond which to randomize [1..2**p]", "matchid=\n Add match id ; one offset per round", "maxplayer=0\n Override maxplayer for a long list (for testing)", "VERSION=0.8\n 11-mar-02 PJT", NULL, }; string usage="Make a drop down draw from a (ranked) list of players"; extern string *burststring(string, string); extern double xrandom(double, double); int parse_team(string, int, int, Team *, int); int rank_cmp(Team *, Team *); int slot_cmp(Team *, Team *); void make_draw(int, Team *, int, int, int); void make_draw_old(int, Team *, int, int, int); void flip(int,int *); void shuffle(int,int *); void setup_byes(int); static int **byes; // will be created by setup_byes nemo_main() { int n, i, i0, idx, p = getiparam("p"); int nid, mid, midx, matchid[256]; char smatchid[128]; Team *team, *w, zero; stream outstr; int nteam, slot, maxplayer=getiparam("maxplayer"); int seed, nrandom; bool Qfull = getbparam("full"); bool Qauto = getbparam("auto"); set_xrandom(getiparam("seed")); for (n=1, i=0; i n) error("Too many players, try setting larger p=%d",p); make_draw(nteam, team, p, n, nrandom); qsort(team,nteam,sizeof(Team),slot_cmp); for (i=0; i 1) { /* loop while still to play */ if (nid) mid = matchid[midx++]; if (n==2) fprintf(outstr,"# and the winner \n"); else fprintf(outstr,"# round of %d at %d with i0=%d\n",n/2,idx+n,i0); for (i=0; i 0) w = &team[i0]; else w = &team[i0+1]; i0 += 2; } } else { // full draw OR not the first round /* decide battle between idx+{i & i+1} */ if (Qauto) { w = &zero; } else { if (strlen(team[idx+i].name) == 0) w = &team[idx+i+1]; else if (strlen(team[idx+i+1].name) == 0) w = &team[idx+i]; else if (xrandom(-1.0,1.0) > 0) w = &team[idx+i]; else w = &team[idx+i+1]; } } if (nid) { if (mid > 0) sprintf(smatchid,"[##%d] ",mid++); else sprintf(smatchid," "); } strcpy(team[idx+n+i/2].name, w->name); team[idx+n+i/2].slot = idx + n + (i + 2)/2; if (Qfull) { if(strlen(team[idx+n+i/2].name)==0) fprintf(outstr,"# %d/%d BYE\n",(i+2)/2,n/2); else fprintf(outstr,"%d/%d %s\n",(i+2)/2,n/2,team[idx+n+i/2].name); } else if (Qauto) { if(strlen(team[idx+n+i/2].name)==0) fprintf(outstr,"%d/%d %s\n",(i+2)/2,n/2,smatchid); else fprintf(outstr,"%d/%d %s\n",(i+2)/2,n/2,team[idx+n+i/2].name); } else fprintf(outstr,"%d/%d %s\n",(i+2)/2,n/2,smatchid); } idx += n; n /= 2; } strclose(outstr); } #define MAXLINELEN 256 int parse_team ( string fname, int p, int maxteam, Team *team, int maxplayer ) { stream instr; char *cp, line[MAXLINELEN]; string *words; int idx, len, nwords, rank=-1, count=0; int i, i1, i2, n = maxteam; if (fname==0 || *fname==0) { if (maxplayer==0) maxplayer=maxteam; warning("Inserting dummy %d players",maxplayer); for (i=0; i= maxteam) warning("Too many entries; maxteam=%d",maxteam); words = burststring(line," \t"); nwords = xstrlen(words,sizeof(string))-1; if (nwords < 1) continue; idx = 0; #if 0 cp = words[idx]; if (isdigit(*cp)) { team[count].id = atoi(cp); idx++; } else team[count].id = count+1; #else team[count].id = count+1; #endif cp = words[idx]; if (isdigit(*cp)) { team[count].rank = atoi(cp); idx++; } else team[count].rank = rank--; cp = words[idx]; if (isdigit(*cp)) { team[count].region = atoi(cp); idx++; } else team[count].region = 0; cp = words[idx]; if (isdigit(*cp)) { team[count].slot = atoi(cp); idx++; } else team[count].slot = 0; /* <--- default !!! */ strcpy(team[count].name,words[idx++]); for (;idx < nwords; idx++) { cp = words[idx]; if (*cp=='#') break; strcat(team[count].name," "); strcat(team[count].name,cp); } dprintf(2,"Reading: %d %s\n",team[count].id, team[count].name); if (maxplayer > 0 && count == maxplayer) { warning("Only using first %d players/teams",maxplayer); break; } count++; } if (count>0) qsort(team,count,sizeof(Team),rank_cmp); return count; } int rank_cmp( Team *i, Team *j ) { return j->rank - i->rank; } int slot_cmp( Team *i, Team *j ) { return i->slot - j->slot; } void make_draw( int nteam, /* number of teams (<= n) */ Team *team, /* point to all teams */ int p, /* level */ int n, /* 2**p, a bit redundant, but handy to keep around */ int nrandom /* if non-zero, randomize beyond this */ ) { int start, todo, i, j, k; int filled[256]; int pick[257], idx[257]; bool Qibf = FALSE; for (i=0; i0) { start++; if (start>n) start=1; if (!filled[start]) k--; } dprintf(2,"Hit at slot=%d filled=%d\n",start,filled[start]); team[idx].slot = start; if (filled[team[idx].slot]) error("Trailing already filled"); filled[team[idx].slot] = 1; dprintf(1,"Putting %d into slot %d\n",idx+1,team[idx].slot); if (++idx >= nteam) return; todo--; } } /* * take an array idx[] of length n, and shuffle its elements * * Note that n<=0 is allowed and nothing interesting will happen */ void shuffle(int n, int *idx) { int k, l, tmp; for (k=n-1; k>0; k--) { l = (int)xrandom(0.0,(double)k+1); if (k==l) continue; tmp= idx[k]; idx[k] = idx[l]; idx[l] = tmp; } } void flip(int n, int *p) { int i, tmp; for (i=0; i < n/2; i++) { tmp = p[n-1-i]; p[n-1-i] = p[i]; p[i] = tmp; } } /* * this elegant (?) algorithm codifies for arbitrary draw size * the draw locations where the byes are supposed to be located * according to IBF/USAB rules. * I still use the 1/n .... n/n notation, i.e. slots in the * draw are numbered 1..n, teams that populate these slots * are 1..m (actually called nteam in the code) */ void setup_byes(int pmax) { int i, n, p; byes = (int **) allocate((pmax+1)*sizeof(int *)); byes[0] = NULL; // actually never need this, p > 0 for (p=1, n=1; p<=pmax; p++, n*=2) { byes[p] = (int *) allocate((n+1)*sizeof(int)); if (p==1) { // first iteration, actually not useful byes[p][0] = 2; byes[p][1] = 0; // terminator } else if (p==2) { // second iteration, setup for the remainder byes[p][0] = 3; byes[p][1] = 2; byes[p][2] = 0; // terminator } else { for (i=0; i 0; i++) dprintf(1," %d",byes[p][i]); dprintf(1,"\n"); } }