/* Monte Carlo Simulation of Jai-Alai This program simulates the results of a large number of jai-alai games to see if the Spectacular Seven scoring system inherently favors certain starting positions. Steven Skiena -- January 18, 1987 Simulation of win, place, and show probabilities as a function of post position. Dario Vlah -- March 12, 1997 Enhance simulation to support players with non-uniform point win probabilities. Copyright: Steven Skiena 1987-2003. This program is distributed under a GNU public licence. The authors names and header information must be left intact in any distribution or modification of this program. The authors make no guarantees of correctness and will not be liable for any damages caused by the use of this program. In particular, we take no responsibility for any gambling losses by people foolish enough to bet on the results of this program. */ #include "stdio.h" #include "stdlib.h" #include "string.h" #include #define TOTALPLAYERS 8 /*number of players per game*/ #define TOTALWINPOINTS 7 /*number of points to win*/ #define MAXLENGTH 100 /*maximum length of a game*/ int queue[TOTALPLAYERS+1]; /*next player queue*/ int queuesize; /*number of items in the queue*/ int p1,p2; /*active players*/ int doublepoint; /*how many points until doubling?*/ int nplayers; /*number of players in the game*/ int nties [TOTALPLAYERS+1]; /*number of ties broken*/ int length[MAXLENGTH]; /*length of game record*/ int notgame; /*game not really a game*/ int debug; /*debug flag*/ int ngames; /*number of games in simulation*/ int nstatus; /*number of games between reports*/ int show_trifecta; /*show trifecta results*/ int show_quinela; /*show quinela results*/ int results[TOTALPLAYERS+1][TOTALPLAYERS+1][TOTALPLAYERS+1]; /*total outcomes*/ int scoreperpoint; /*single, double or triple points?*/ int winpoints; /*number of point needed to win game*/ int wins[TOTALPLAYERS+1], places[TOTALPLAYERS+1], shows[TOTALPLAYERS+1]; /*cummulative outcomes in simulation*/ int win,place,show; /*results for the last game*/ int points[TOTALPLAYERS+1]; /*scoring for a game*/ int i; /*counter*/ /* Added by Dario Vlah */ double winprob[TOTALPLAYERS][TOTALPLAYERS]; /* winprob[A][B] is the probability that A wins a point against B */ int points_won[TOTALPLAYERS+1]; /* won points per player */ int points_lost[TOTALPLAYERS+1]; /* lost points per player */ int win_points[2][TOTALPLAYERS+1]; /* won/lost points per winner */ int place_points[2][TOTALPLAYERS+1]; /* won/lost points per placer */ int show_points[2][TOTALPLAYERS+1]; /* won/lost points per shower */ int othr_points[2][TOTALPLAYERS+1]; /* won/lost points per others */ /*************************************************************************/ /* This procedure identifies the winner and loser of each point. Both players have an equal chance for each point. */ void playpoint(int p1, int p2, int *winner, int *loser) { double x; /*random number*/ x = (double) random_number(); /*random number function - range 0-1 */ *winner = p1; *loser = p2; /* The probability P that p1 will win over p2 is given in winprob[p1][p2]. Also, the probability of a random number in the range [0,1] being greater than P is (1-P)/1 = 1-P. This is actually the probability of p1 losing a game, in which case we have to swap the values in winner and loser. */ if (x > winprob[p1-1][p2-1]) { /* P1 has lost the game */ *winner = p2; *loser = p1; points_won[p2]++; points_lost[p1]++; } else { /* P2 has lost the game */ points_won[p1]++; points_lost[p2]++; } } /* Adds the given player to the bottom of the queue. */ void addtoqueue(int loser) { queuesize = queuesize + 1; queue[queuesize] = loser; } /* Fetch the next player off the top of the queue. */ int fetchfromqueue() { int i; /*counter*/ int ret; ret = queue[1]; for (i=1; i= doublepoint) scoreperpoint = 2; if (pointsplayed >= triplepoint) scoreperpoint = 3; addtoqueue(loser); p1 = winner; } while (!(points[winner] >= winpoints)); if (!notgame) length[pointsplayed] = length[pointsplayed] + 1; if (debug) { printf("%3d wins the match.\n", winner); printf("\n\n"); printf(" player points\n"); for (i=1; i<=TOTALPLAYERS; i++) printf(" %6d %6d\n", i, points[i]); } return winner; } /* This routine identifies who has the most points after top, the highest scorer in the game, with the tie breaking rules used in Florida. */ int next() { int i,j, /*counters*/ high, /*next highest score*/ quantity, /*number of people with this score*/ winner,loser; /*winner and loser of particular points*/ int next_ret; high = -1; for (i= 1; i<=queuesize; i++) { if (points[queue[i]] > high) { high = points[queue[i]]; quantity = 1; } else if (points[queue[i]] == high) { quantity = quantity + 1; } } nties[quantity] = nties[quantity] + 1; if (debug) printf(" high = %d, quantity = %d\n", high, quantity); if ((high+scoreperpoint >= winpoints) && (quantity >= 2)) { p1 = fetchnext(high); p2 = fetchnext(high); playpoint(p1,p2,&winner,&loser); next_ret = winner; addtoqueue(loser); } else switch(quantity) { case 1 : next_ret = fetchnext(high); break; case 2 : p1 = fetchnext(high); p2 = fetchnext(high); playpoint(p1,p2,&winner,&loser); next_ret = winner; addtoqueue(loser); break; case 3 : p1 = fetchnext(high); p2 = fetchnext(high); playpoint(p1,p2,&winner,&loser); points[winner] = points[winner] + scoreperpoint; addtoqueue(loser); p1 = winner; p2 = fetchnext(high); playpoint(p1,p2,&winner,&loser); next_ret = winner; addtoqueue(loser); break; case 4: case 6: for (i=1; i<=(quantity/2); i++) { p1 = fetchnext(high); p2 = fetchnext(high); playpoint(p1,p2,&winner,&loser); points[winner] = points[winner] + scoreperpoint; addtoqueue(winner); } next_ret = next(); /*recursive call*/ break; case 5: case 7: if (high == 0) winpoints = winpoints - 1; i = 1; /*remove dead players*/ while (i <= queuesize) if (points[queue[i]] == high) { i = i+1; } else { for (j=i+1; j<=queuesize; j++) queue[j-1] = queue[j]; queuesize = queuesize - 1; } nplayers = quantity; notgame = 1; /*turn off length counting*/ next_ret = playgame(); notgame = 0; break; } /* switch */ return next_ret; } /* Fetch the next player off the queue with this quantity of points, placing the others back in the queue when done. */ int fetchnext(int quantity) { int p; /*player from queue*/ int i; /*counter*/ p = fetchfromqueue(); i = 0; while ((points[p] != quantity) && (i <= queuesize)) { i = i+1; addtoqueue(p); p = fetchfromqueue(); } if (i > queuesize) printf(" no player with %d in the queue.\n", quantity); else return p; return -1; } int triplepoint; /*how many points until tripling*/ int triplelow,triplehigh; /*range of tripling values*/ int triplestart; /*double before tripling*/ int doublelow,doublehigh; /*range of doubling values*/ /* EXTRA FUNCTIONS & VARIABLES - Added by Dario Vlah, 3/12/97 */ /* Note: rand() is not very good at generating random numbers. */ /* 4/9/97 - used random() -> makes a significant difference */ /* random_number() returns a double between 0 and 1 */ double random_number() { int n = random() % 100000001; /* 1 greater to allow the range [0,1] */ return (float)n / 100000000.0; /* with 1 included */ } double sqr(double x) { return x*x; } /* This function initializes the player skill table to uniform skills, i.e. p(a,b) for all (a,b) and a<>b is equal to 0.5 and 0 otherwise. */ void init_player_skills() { int i,j; for (i=0; i<8; i++) { for (j=0; j<8; j++) winprob[i][j] = 0.5; winprob[i][i] = 0; } } void read_player_skills(const char *filename) { FILE *fp; int i,j; char line[512]; if (!(fp = fopen(filename, "r"))) { fprintf(stderr, "Can't open the given player skill file!\n"); exit(1); } for (i=0; i<8; i++) { fgets(line, 512, fp); sscanf(line, "%lf %lf %lf %lf %lf %lf %lf %lf", &winprob[i][0], &winprob[i][1], &winprob[i][2], &winprob[i][3], &winprob[i][4], &winprob[i][5], &winprob[i][6], &winprob[i][7]); } fclose(fp); } /* Initialize the various parameters of the simulation. This is the interogiation section of the program, which sets ranges on what gets done on this run. */ int show_points_won; void initsimulation(int argc, char **argv) { int i; ngames = 1000; /* DEFAULT - number of games*/ nstatus = -1; /* DEFAULT - number of games between status reports */ /* -1 means it wasn't given as arg, so set */ /* it to ngames */ debug = 0; /* DEFAULT - debug off */ show_points_won = 0; /* DEFAULT - don't show percentage of games won. */ char filename[512] = ""; /* name of the file where player skills are found. */ show_trifecta=0; show_quinela=0; init_player_skills(); /* Now we sift through the command line to set any of these */ for (i=1; i */ if (i+1 */ if (!strcmp(argv[i], "-d")) /* -d for debug info */ debug=1; if (!strcmp(argv[i], "-w")) show_points_won=1; if (!strcmp(argv[i], "-f")) if (i+1 */ read_player_skills(filename); } if (!strcmp(argv[i], "-t")) show_trifecta=1; if (!strcmp(argv[i], "-q")) show_quinela=1; } if (nstatus==-1) nstatus=ngames; if (argc==1) { printf("\nUsage: jsim [-g N] [-s N] [-w] [-d] [-f matrix] [-q|-t]\n"); printf(" -g: N is the number of games to simulate (1000 by default).\n"); printf(" -s: N sets the number of games to average per report.\n"); printf(" The default value equals the number of games.\n"); printf(" -w: Includes information about # of games won.\n"); printf(" -d: turns on debugging information.\n"); printf(" -f: 'matrix' is a file containing a player skill matrix.\n"); printf(" -t: output trifecta results.\n"); printf(" -q: output quinela results.\n"); } printf("Running: %d games, %d games between status reports. Debug info %s.\n", ngames, nstatus, debug?"on":"off"); if (filename[0]) printf("Using win probabilities read from '%s.'\n\n", filename); else printf("Using default (equal opportunity!) win probabilities.\n\n"); doublelow = TOTALPLAYERS - 1; doublehigh = doublelow; triplelow = 0x7fff; triplehigh = triplelow; } /* This routine initializes the statistical arrays used in the program. */ void inittotals() { int i,j,k; /*counters*/ notgame = 0; for (i=1; i<=MAXLENGTH; i++) length[i] = 0; for (i=1; i<=TOTALPLAYERS; i++) nties[i] = 0; for (i=1; i<=TOTALPLAYERS; i++) { wins[i] = 0; places[i] = 0; shows[i] = 0; points_won[i] = 0; points_lost[i] = 0; win_points[0][i] = 0; win_points[1][i] = 0; show_points[0][i] = 0; show_points[1][i] = 0; place_points[0][i] = 0; place_points[1][i] = 0; othr_points[0][i] = 0; othr_points[1][i] = 0; } for (i=1; i<=nplayers; i++) for (j=1; j<=nplayers; j++) for (k=1; k<=nplayers; k++) results[i][j][k] = 0; } /* This routine prints the results of the simulation. */ void outtotals(int count) { int i, j, k, points, gp; /*counter*/ printf(" Jai-alai Simulation Results\n\n"); printf(" Pos win %%wins place %%places show %%shows\n"); for (i=1; i<=nplayers; i++) printf("%3d %7d %6.2f %7d %6.2f %7d %6.2f\n", i, wins[i], (100.0*(float)wins[i]/(float)count), places[i],(100.0*(float)places[i]/(float)count), shows[i], (100.0*(float)shows[i]/(float)count) ); printf("\n"); printf(" total games = %6d\n",count); if (show_points_won){ printf("\nPos Winner Placer Shower Other\n"); printf(" %% won total %% won total %% won total %% won total\n"); for (i=1; i<=nplayers; i++) printf("%2d %3.2f %9d %3.2f %9d %3.2f %9d %3.2f %9d\n", i, (float)win_points[0][i]/(float)(win_points[0][i] + win_points[1][i])*100.0, win_points[0][i]+win_points[1][i], (float)place_points[0][i]/(float)(place_points[0][i] + place_points[1][i])*100.0, place_points[0][i]+place_points[1][i], (float)show_points[0][i]/(float)(show_points[0][i] + show_points[1][i])*100.0, show_points[0][i]+show_points[1][i], (float)othr_points[0][i]/(float)(othr_points[0][i] + othr_points[1][i])*100.0, othr_points[0][i]+othr_points[1][i]); } if (show_trifecta) { printf("\n Trifecta Count %%count\n"); points = 0; for (i=1; i<=nplayers; i++) for (j=1; j<=nplayers; j++) for (k=1; k<=nplayers; k++) points += results[i][j][k]; for (i=1; i<=nplayers; i++) for (j=1; j<=nplayers; j++) for (k=1; k<=nplayers; k++) if ((i!=k) && (j!=k) && (i!=j)) printf("%d-%d-%d: %4d %2.6f\n", i, j, k, results[i][j][k], 100.0 * (double)(results[i][j][k])/(double)points); } if (show_quinela){ printf("\n Quinela Count %%count\n"); points = 0; for (i=1; i<=nplayers; i++) for (j=1; j<=nplayers; j++) for (k=1; k<=nplayers; k++) points += results[i][j][k]; for (i=1; i<=nplayers; i++) for (j=1; j<=nplayers; j++) if (i!=j) { gp = 0; for (k=1; k<=nplayers; k++) gp += results[i][j][k]; printf("%d-%d: %4d %2.6f\n", i, j, gp, 100.0 * (double)(gp)/(double)points); } } } /* This procedure calculates the various betting statistics, such as quinellas, exactas, trifectas, etc. */ void betting() { int i,j,k; /*counters*/ int exacta[TOTALPLAYERS+1][TOTALPLAYERS+1]; int total; /*running sum*/ nplayers = TOTALPLAYERS; /*trifecta results*/ /* for i:= 1 to nplayers do begin printf; printf(' Trifecta results, Win=',i); printf; printf(' place show'); write(' '); for j:=1 to nplayers do write(j:7); printf; for j:=1 to nplayers do begin write(j,' '); for k:= 1 to nplayers do write(results[i,j,k]:7); printf; end; printf; printf; end; */ /*trifecta boxing*/ /* printf(' Trifecta Boxing'); printf; printf(' x y z payoffs %occurances'); for i := 1 to nplayers do for j := i+1 to nplayers do for k := j+1 to nplayers do begin total := results[i,j,k] + results [i,k,j] + results[j,i,k] + results[j,k,i] + results[k,i,j] + results[k,j,i]; printf(i:3,j:3,k:3,' ',total:7,' ', (100.0*total)/(ngames):6:3); end; printf; printf; */ /*Exacta or perfecta*/ /* printf(' Exacta Betting'); printf; printf(' i j payoffs %payoffs'); for i:=1 to nplayers do for j:=1 to nplayers do begin exacta[i,j] := 0; for k:=1 to nplayers do exacta[i,j] := exacta[i,j] + results[i,j,k]; printf(i:3,j:3,' ',exacta[i,j],' ', (100.0*exacta[i,j])/(ngames):6:3); end; printf; printf; */ /*quinella*/ /* printf(' quinella'); printf; printf(' i j payoffs %payoffs'); for i:=1 to nplayers do for j:=i to nplayers do begin total := exacta[i,j] + exacta[j,i]; printf(i:3,j:3,' ',total,' ', (100.0*total)/(ngames):6:3); end; printf; printf; */ /*length of game*/ /* printf; printf; printf(' length of game'); printf; printf(' i outcomes %outcome'); */ total = 0; for (i=1; i<=MAXLENGTH; i++) { /* printf(i:4,' ',length[i],' ',(100.0*length[i]/ngames):6:3); */ total = total + length[i]*i; } printf("\nThe expected length of a game sans ties is: %6.3f\n", (float)total/(float)ngames); printf("\n\n"); /*number of ties*/ /* printf; printf; printf(' number of ties'); printf; printf(' nplayers frequency %outcome'); for i:=1 to TOTALPLAYERS do printf(' ',i,' ',nties[i],' ', (100.0*nties[i])/(2*ngames):6:3); printf; printf; */ } /* This procedure analyzes the variances associated with the scoring system. */ void deviations() { int i,j,k; /*counters*/ float total; /*running total of variance*/ printf("deviations for doublepoint=%3d triplepoint=%3d\n", doublepoint, triplepoint); total = 0; for (i=1; i<=TOTALPLAYERS; i++) total = total + sqr( (float)wins[i] - (float)ngames/(float)TOTALPLAYERS ); total = sqrt( total / (float)(TOTALPLAYERS - 1) ); printf("deviation for wins = %11.2f\n",total); total = 0; for (i=1; i<=TOTALPLAYERS; i++) total = total + sqr( (float)places[i] - (float)ngames/(float)TOTALPLAYERS ); total = sqrt( total / (float)(TOTALPLAYERS - 1) ); printf("deviation for places = %11.2f\n",total); total = 0; for (i=1; i<=TOTALPLAYERS; i++) total = total + sqr( (float)shows[i] - (float)ngames/(float)TOTALPLAYERS ); total = sqrt( total / (float)(TOTALPLAYERS - 1) ); printf("deviation for shows = %11.2f\n",total); total = 0; for (i=1; i<=TOTALPLAYERS; i++) for (j=1; j<=TOTALPLAYERS; j++) for (k=1; k<=TOTALPLAYERS; k++) total = total + sqr( (float)results[i][j][k] - (float)ngames/(8.0*7.0*6.0) ); total = sqrt(total / (float)(8*7*6 - 1)); printf("deviation for trifectas = %11.2f\n\n",total); } void main(int argc, char **argv) { int jj; initsimulation(argc, argv); for (doublepoint= doublelow; doublepoint<=doublehigh; doublepoint++) { if (doublepoint >= triplelow) triplestart = doublepoint + 1; else triplestart = triplelow; for (triplepoint = triplestart; triplepoint<=triplehigh; triplepoint++) { inittotals(); printf(" Doublepoint = %3d, Triplepoint = %3d\n", doublepoint, triplepoint); for (i=1; i<=ngames; i++) { initgame(); win = playgame(); place = next(); show = next(); wins[win] = wins[win] + 1; places[place] = places[place] + 1; shows[show] = shows[show] + 1; win_points[0][win] += points_won[win]; win_points[1][win] += points_lost[win]; place_points[0][place] += points_won[place]; place_points[1][place] += points_lost[place]; show_points[0][show] += points_won[show]; show_points[1][show] += points_lost[show]; for (jj=1; jj<=nplayers; jj++) { if (jj!=win && jj!=place && jj!=show) { othr_points[0][jj] += points_won[jj]; othr_points[1][jj] += points_lost[jj]; } } results[win][place][show] = results[win][place][show] + 1; if (debug) printf(" Win Place Show %d, %d %d\n", win,place,show); if (i % nstatus == 0) outtotals(i); } /* betting(); deviations(); printf("\n----------------------------------------------------\n");*/ } } }