Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- //
- // FairStrikeSim: Simulate matches of elimination strike play.
- //
- // End user provides # players, # strikes, and # simulations to run
- //
- // By Keith P. Johnson (twitter @pinballkeefer)
- //
- // This code is public domain. Do your worst. Attribution would be nice.
- //
- // ---------------------------------------------------------------------------
- // architecture
- // ---------------------------------------------------------------------------
- // currently supported are windows (_WIN64) and linux (__linux__)
- // you're on your own for anything else. tried to leave some placeholders
- // for __APPLE__ which looks like is a thing.
- // ---------------------------------------------------------------------------
- #if defined(_WIN64)
- #include <SDKDDKVer.h>
- #include <stdio.h>
- #include <tchar.h>
- #define _CRT_RAND_S
- #include <stdlib.h>
- // lol windows
- #define strtol _tcstol
- #endif
- // ---------------------------------------------------------------------------
- #if defined(__linux__)
- #include <stdio.h>
- #include <limits.h>
- #include <string.h>
- #include <stdlib.h>
- #include <sys/random.h>
- #endif
- // ---------------------------------------------------------------------------
- #if defined(__APPLE__)
- // I dunno lol
- #endif
- // ---------------------------------------------------------------------------
- // controls how verbose program is
- // range is 1-5, 1 least verbose (0 is absolutely nothing)
- static int debug_level = 0;
- // debug printf macro, will print message if debug_level above is set to at
- // least the number specified in the dpf statement.
- #define dpf(level, ...) if (debug_level >= level) { printf(__VA_ARGS__); }
- // ---------------------------------------------------------------------------
- // app config
- // ---------------------------------------------------------------------------
- // how big/small player groups can be. I don't guarantee all the logic works
- // properly if you change these, though.
- const int player_match_size_min = 2;
- const int player_match_size_max = 4;
- // size of results tables
- const int result_table_size_2p = 2 * 1;
- const int result_table_size_3p = 3 * 2 * 1;
- const int result_table_size_4p = 4 * 3 * 2 * 1;
- // range of virtual players to simulate, arbitrary top limit
- const int players_min = 2;
- const int players_max = 1024;
- // range of strikes to simulate against, arbitrary top limit
- const int strikes_min = 1;
- const int strikes_max = 100;
- // range of number of runs, arbitrary top limit
- const int runs_min = 1;
- const int runs_max = 1000000000;
- // gotta stop somewhere, arbitrary limit
- const int rounds_max = 1024;
- // ---------------------------------------------------------------------------
- // globals for analysis
- // ---------------------------------------------------------------------------
- // just used for the end so we know how many rounds to print out
- int max_rounds_actually_played = 0;
- // current strikes for each player
- int player_strikes[players_max] = { 0 };
- // pointer to each player record sorted by # strikes they have
- // this is how new strikes get assigned into player_strikes[] by being sorted
- // by current # strikes into this array
- int* sorted_player_strikes[players_max] = { NULL };
- // how many players still alive, printed out at end of each round
- int active_players_per_round[rounds_max] = { 0 };
- // running tally of players per round, gets printed at end of run
- int total_tally_players_per_round[rounds_max] = { 0 };
- // running tally of which round a match ended, gets printed at end of run
- int total_rounds_ended_on_round[rounds_max] = { 0 };
- // running tally of how many rounds a match lasted, basically a sum of round
- // numbers (same as above). gets printed at end, just easier than summing
- // and multiplying everything up
- int total_tally_rounds_match_lasted = 0;
- // running tally of x-player games played. use this to figure out WPPRs!
- int total_group_size_tally[5] = { 0 };
- // ---------------------------------------------------------------------------
- // strike tables
- // ---------------------------------------------------------------------------
- // these are brute-force possible results of each round. generating these
- // on the fly is left as an exercise to the reader.
- // #if 1 here gets you keefer (fair) strikes
- // #if 0 gets you progressive (01, 012, 0123) strikes)
- // of course you can make it friendlier than this, add more, etc.
- // ---------------------------------------------------------------------------
- // new in version 2! allow people to pick what strikes they want from
- // command line. default to fair strikes
- int strike_gen_table[player_match_size_max + 1][player_match_size_max] =
- {
- { 0 }, // 0-player games
- { 0 }, // 1-player games
- { 0, 2 }, // 2-player games
- { 0, 1, 2 }, // 3-player games
- { 0, 1, 1, 2 }, // 4-player games
- };
- int two_player_match_results[result_table_size_2p][player_match_size_max];
- int three_player_match_results[result_table_size_3p][player_match_size_max];
- int four_player_match_results[result_table_size_4p][player_match_size_max];
- #if 0
- // keefer strikes (fair strikes)
- int two_player_match_results[][player_match_size_max] =
- {
- {0,2,0,0},
- {2,0,0,0},
- };
- int three_player_match_results[][player_match_size_max] =
- {
- {0,1,2,0},
- {0,2,1,0},
- {1,0,2,0},
- {1,2,0,0},
- {2,0,1,0},
- {2,1,0,0},
- };
- int four_player_match_results[][player_match_size_max] =
- {
- {0,1,1,2},
- {0,1,2,1},
- {0,2,1,1},
- {1,0,1,2},
- {1,0,2,1},
- {1,1,0,2},
- {1,1,2,0},
- {1,2,0,1},
- {1,2,1,0},
- {2,0,1,1},
- {2,1,0,1},
- {2,1,1,0},
- };
- #endif
- #if 0
- // progressive strikes
- int two_player_match_results[][player_match_size_max] =
- {
- { 0,1,0,0 },
- { 1,0,0,0 },
- };
- int three_player_match_results[][player_match_size_max] =
- {
- { 0,1,2,0 },
- { 0,2,1,0 },
- { 1,0,2,0 },
- { 1,2,0,0 },
- { 2,0,1,0 },
- { 2,1,0,0 },
- };
- int four_player_match_results[][player_match_size_max] =
- {
- { 0,1,2,3 },
- { 0,1,3,2 },
- { 0,2,1,3 },
- { 0,2,3,1 },
- { 0,3,1,2 },
- { 0,3,2,1 },
- { 1,0,2,3 },
- { 1,0,3,2 },
- { 1,2,0,3 },
- { 1,2,3,0 },
- { 1,3,0,2 },
- { 1,3,2,0 },
- { 2,0,1,3 },
- { 2,0,3,1 },
- { 2,1,0,3 },
- { 2,1,3,0 },
- { 2,3,1,0 },
- { 2,3,0,1 },
- { 3,0,1,2 },
- { 3,0,2,1 },
- { 3,1,0,2 },
- { 3,1,2,0 },
- { 3,2,0,1 },
- { 3,2,1,0 },
- };
- #endif
- #if 0
- // since I'm making this nice, here's how you'd do the giant number of
- // ultra-fair strikes. someone called it an endurance battle. why not
- // pinball battle royale? whatever.
- // I haven't tested this but it should work.
- int two_player_match_results[][player_match_size_max] =
- {
- { 0,6,0,0 },
- { 6,0,0,0 },
- };
- int three_player_match_results[][player_match_size_max] =
- {
- { 0,3,6,0 },
- { 0,6,3,0 },
- { 3,0,6,0 },
- { 3,6,0,0 },
- { 6,0,3,0 },
- { 6,3,0,0 },
- };
- int four_player_match_results[][player_match_size_max] =
- {
- { 0,2,4,6 },
- { 0,2,6,4 },
- { 0,4,2,6 },
- { 0,4,6,2 },
- { 0,6,2,4 },
- { 0,6,4,2 },
- { 2,0,4,6 },
- { 2,0,6,4 },
- { 2,4,0,6 },
- { 2,4,6,0 },
- { 2,6,0,4 },
- { 2,6,4,0 },
- { 4,0,2,6 },
- { 4,0,6,2 },
- { 4,2,0,6 },
- { 4,2,6,0 },
- { 4,6,2,0 },
- { 4,6,0,2 },
- { 6,0,2,4 },
- { 6,0,4,2 },
- { 6,2,0,4 },
- { 6,2,4,0 },
- { 6,4,0,2 },
- { 6,4,2,0 },
- };
- #endif
- // array of ints of player_match_size_max size
- typedef int result_array_t[player_match_size_max];
- // define table of all possible round results
- typedef struct _player_strike_table_s
- {
- int num_results; // # results for this group of players
- result_array_t* results_tab; // pointer to results table
- } player_strike_table_t;
- // for ease-of-reading, we use literally the number of players to index the
- // array. efficiency is overrated.
- player_strike_table_t strike_result_tab[player_match_size_max + 1] =
- {
- {0, NULL},
- {0, NULL},
- { sizeof(two_player_match_results) / sizeof(two_player_match_results[0]), two_player_match_results },
- { sizeof(three_player_match_results) / sizeof(three_player_match_results[0]), three_player_match_results },
- { sizeof(four_player_match_results) / sizeof(four_player_match_results[0]), four_player_match_results },
- };
- // ---------------------------------------------------------------------------
- // main monolith brute force function. you get what you pay for
- // ---------------------------------------------------------------------------
- #if defined(_WIN64)
- int _tmain(int argc, _TCHAR* argv[])
- #else
- int main(int argc, char* argv[])
- #endif
- {
- bool need_usage = false; // bro do u even run
- int num_players = 0; // current number of players for run
- int num_players_start = 0; // for a range, start with # players
- int num_players_end = 0; // for a range, end with # players
- int num_strikes = 0; // how many strikes each player gets
- int num_runs = 0; // how many times to run simulation each count
- int excel = 0; // whether we're dumping for excel or not
- int player_group_limit = 4; // try not to go over this # players/group
- // parse the command line ------------------------------------------------
- int arg_index = 1;
- // new 29-nov-2018: everything is with -switches now
- while (arg_index < argc)
- {
- // every option begins with a "-"
- if (argv[arg_index][0] == '-')
- {
- switch (argv[arg_index][1])
- {
- case 'h':
- case '?':
- // standard force help display
- // even though help will show up anytime you fuck up
- need_usage = true;
- break;
- case 'p':
- // "# players" option
- switch (argv[arg_index][2])
- {
- case 'l':
- // low players
- num_players_start = strtol(&argv[arg_index][3], NULL, 10);
- break;
- case 'h':
- // high players
- num_players_end = strtol(&argv[arg_index][3], NULL, 10);
- break;
- default:
- // only do this # of players
- num_players = strtol(&argv[arg_index][2], NULL, 10);
- break;
- }
- break;
- case 's':
- // number of strikes in match for each player
- num_strikes = strtol(&argv[arg_index][2], NULL, 10);
- break;
- case 'r':
- // number of times to run sim for each player count
- num_runs = strtol(&argv[arg_index][2], NULL, 10);
- break;
- case 'g':
- // player group size limit (except to avoid byes)
- player_group_limit = strtol(&argv[arg_index][2], NULL, 10);
- break;
- case 'x':
- // present only a summary suitable for excel
- // this results in data that's tab-delimited which excel
- // happily accepts, but only if you redirect to a file
- // like "fairstrikestim > data.txt"
- // you won't be able to just copy the screen output, because
- // that will result in spaces, not tabs.
- excel = 1;
- break;
- case 'c':
- // strike configuration
- if (argc <= arg_index + 9)
- {
- need_usage = true;
- }
- else
- {
- // a Good Thing to do at some point would be to make sure
- // none of these starts with a '-' or other letter.
- // at some point.
- strike_gen_table[2][0] = strtol(argv[++arg_index], NULL, 10);
- strike_gen_table[2][1] = strtol(argv[++arg_index], NULL, 10);
- strike_gen_table[3][0] = strtol(argv[++arg_index], NULL, 10);
- strike_gen_table[3][1] = strtol(argv[++arg_index], NULL, 10);
- strike_gen_table[3][2] = strtol(argv[++arg_index], NULL, 10);
- strike_gen_table[4][0] = strtol(argv[++arg_index], NULL, 10);
- strike_gen_table[4][1] = strtol(argv[++arg_index], NULL, 10);
- strike_gen_table[4][2] = strtol(argv[++arg_index], NULL, 10);
- strike_gen_table[4][3] = strtol(argv[++arg_index], NULL, 10);
- }
- break;
- default:
- // unrecognized option/switch
- need_usage = true;
- break;
- }
- }
- else
- {
- need_usage = true;
- }
- ++arg_index;
- }
- // arg range check -------------------------------------------------------
- if (num_players)
- {
- // we specified only one player count to simulate
- if ((num_players < players_min) || (num_players > players_max))
- {
- // not in range.
- need_usage = true;
- }
- if (num_players_start || num_players_end)
- {
- // we also specified a start and/or end. that's a no-no
- need_usage = true;
- printf("Specified both a single run and a start/end range.\n");
- }
- }
- else
- {
- // assume range if no specific player count
- if ((num_players_start < players_min) || (num_players_start > players_max))
- {
- need_usage = true;
- }
- if ((num_players_end < players_min) || (num_players_end > players_max))
- {
- need_usage = true;
- }
- }
- if ((num_strikes < strikes_min) || (num_strikes > strikes_max))
- {
- need_usage = true;
- }
- if ((num_runs < runs_min) || (num_runs> runs_max))
- {
- need_usage = true;
- }
- if ((player_group_limit < player_match_size_min)
- || (player_group_limit > player_match_size_max))
- {
- need_usage = true;
- }
- if (need_usage)
- {
- // what do you say, skull?
- #if defined(_WIN64)
- printf("usage: %ws [options]\n\n", argv[0]);
- #else
- printf("usage: %s [options]\n\n", argv[0]);
- #endif
- printf("Option list (* = required)\n\n");
- printf("\t-h: this\n");
- printf("\t-?: this\n");
- printf("\t-p<num>: run sim only for <num> players\n");
- printf("\t-pl<num>: starting player count to run\n");
- printf("\t-ph<num>: ending player count to run\n");
- printf("\t-s<num>: # of strikes each player gets before losing\n");
- printf("\t-r<num>: # of times to run the sim for each player count\n");
- printf("\t-g<num>: maximum size of player groups (default: 4)\n");
- printf("\t-x: excel-friendly summary only\n");
- printf("\t-c: strike configuration (default: fair strikes)\n");
- printf("\t provide space-delimited strikes thusly:\n");
- printf("Specify the number of strikes to give in order of 2p, 3p, 4p games.\n");
- printf("For example, to specify fair strikes, you would provide:\n");
- printf("\n");
- printf("-c 0 2 0 1 2 0 1 1 2\n");
- printf(" -v- --v-- ---v---\n");
- printf(" | | \\- 4 player game strikes\n");
- printf(" | \\- 3 player game strikes\n");
- printf(" \\- 2 player game strikes\n");
- printf("\n");
- printf("\t group range: %i-%i\n", player_match_size_min, player_match_size_max);
- printf("\tplayers range: %i-%i\n", players_min, players_max);
- printf("\tstrikes range: %i-%i\n", strikes_min, strikes_max);
- printf("\t runs range: %i-%i\n", runs_min, runs_max);
- printf("\n");
- exit(-1);
- }
- // create strike result possibilities tables -----------------------------
- // new nov-2018: just generate the tables instead of hardcoding
- // chances are you'll screw it up anyway.
- // goal here is to iterate every possibility of arrangement of strikes
- // build tables so we can just assign the results later quickly
- // 2-player generation
- int cur_tab_row = 0;
- for (int i = 0; i < 2; i++)
- {
- for (int j = 0; j < 2; j++)
- {
- if (j != i)
- {
- two_player_match_results[cur_tab_row][0] = strike_gen_table[2][i];
- two_player_match_results[cur_tab_row][1] = strike_gen_table[2][j];
- cur_tab_row++;
- }
- }
- }
- dpf(3, "Generated %i rows of 2-player results\n", cur_tab_row); // sanity
- // 3-player generation
- cur_tab_row = 0;
- for (int i = 0; i < 3; i++)
- {
- for (int j = 0; j < 3; j++)
- {
- if (j != i)
- {
- for (int k = 0; k < 3; k++)
- {
- if ((k != i) && (k != j))
- {
- three_player_match_results[cur_tab_row][0] = strike_gen_table[3][i];
- three_player_match_results[cur_tab_row][1] = strike_gen_table[3][j];
- three_player_match_results[cur_tab_row][2] = strike_gen_table[3][k];
- cur_tab_row++;
- }
- }
- }
- }
- }
- dpf(3, "Generated %i rows of 3-player results\n", cur_tab_row); // sanity
- // 4-player generation
- cur_tab_row = 0;
- for (int i = 0; i < 4; i++)
- {
- for (int j = 0; j < 4; j++)
- {
- if (j != i)
- {
- for (int k = 0; k < 4; k++)
- {
- if ((k != i) && (k != j))
- {
- for (int l = 0; l < 4; l++)
- {
- if ((l != i) && (l != j) && (l != k))
- {
- four_player_match_results[cur_tab_row][0] = strike_gen_table[4][i];
- four_player_match_results[cur_tab_row][1] = strike_gen_table[4][j];
- four_player_match_results[cur_tab_row][2] = strike_gen_table[4][k];
- four_player_match_results[cur_tab_row][3] = strike_gen_table[4][l];
- cur_tab_row++;
- }
- }
- }
- }
- }
- }
- }
- dpf(3, "Generated %i rows of 4-player results\n", cur_tab_row); // sanity
- int runs; // keep track number of simulations
- int cur_active_players; // how many players are still in it
- int cur_round; // which round are we in?
- // debug output of generate strike tables --------------------------------
- // don't worry; it seems to work
- if (debug_level >= 4)
- {
- int i;
- dpf(4, "2 player table:\n\n");
- for (i = 0; i < result_table_size_2p; i++)
- {
- dpf(4, "%i %i\n",
- two_player_match_results[i][0],
- two_player_match_results[i][1]);
- }
- dpf(4, "\n\n3 player table:\n\n");
- for (i = 0; i < result_table_size_3p; i++)
- {
- dpf(4, "%i %i %i\n",
- three_player_match_results[i][0],
- three_player_match_results[i][1],
- three_player_match_results[i][2]);
- }
- dpf(4, "\n\n4 player table:\n\n");
- for (i = 0; i < result_table_size_4p; i++)
- {
- dpf(4, "%i %i %i %i\n",
- four_player_match_results[i][0],
- four_player_match_results[i][1],
- four_player_match_results[i][2],
- four_player_match_results[i][3]);
- }
- dpf(4, "\n\n");
- }
- // setup for simulation running - either once or a range -----------------
- // this is admittedly getting a little hacky, BUT:
- // a - it sure looks like it works
- // b - didn't really intend all this functionality at the beginning
- if (!num_players)
- {
- // running multiple
- num_players = num_players_start;
- }
- else
- {
- // running once
- num_players_end = num_players;
- }
- // simulation loop for all player counts ---------------------------------
- while (num_players <= num_players_end)
- {
- // stuff that needs to init each run of a player count
- // these inits used to be elsewhere, but now that we're running
- // different numbers of players each time, they had to be moved
- max_rounds_actually_played = 0;
- memset(total_tally_players_per_round, 0, sizeof(total_tally_players_per_round));
- memset(total_rounds_ended_on_round, 0, sizeof(total_rounds_ended_on_round));
- total_tally_rounds_match_lasted = 0;
- memset(total_group_size_tally, 0, sizeof(total_group_size_tally));
- runs = 0;
- // main sim loop, does all runs of a player count --------------------
- while (runs < num_runs)
- {
- dpf(1, "starting run #%i / %i\n", runs + 1, num_runs);
- // prime match loop
- cur_active_players = num_players;
- cur_round = 0;
- // everyone starts a new match with 0 strikes obviously
- memset(player_strikes, 0, sizeof(player_strikes));
- // no one's played any rounds yet.
- memset(active_players_per_round, 0, sizeof(active_players_per_round));
- dpf(2, "setup matches\n");
- // run one match per loop (eliminate all but 1 player)
- while (cur_active_players > 1)
- {
- dpf(2, "starting round # %i\n", cur_round + 1);
- dpf(3, "*** CURRENT STRIKES ***\n");
- for (int pi = 0; pi < num_players; pi++)
- {
- dpf(3, "%3i: %i\n", pi, player_strikes[pi]);
- }
- // setup player matches, matching by # strikes
- // first, clear it out
- memset(sorted_player_strikes, 0, sizeof(sorted_player_strikes));
- // gets update as we populate "groups"
- cur_active_players = 0;
- // order players from lowest to highest, forcing like-struck
- // people to play against each other, basically.
- // we start with the lowest number of strikes and move up
- for (int strike_check = 0; strike_check < num_strikes; strike_check++)
- {
- // find everyone that has the current number of strikes and
- // put them in the fight list
- dpf(4, "check for strikes: %i\n", strike_check);
- dpf(5, "players: ");
- for (int i = 0; i < num_players; i++)
- {
- // it's all random so we don't care if everyone is
- // populated in the same order every time
- if (player_strikes[i] == strike_check)
- {
- dpf(5, "%i ", i);
- sorted_player_strikes[cur_active_players] = &player_strikes[i];
- ++cur_active_players;
- }
- }
- dpf(5, "\n");
- }
- dpf(2, "cur active_players: %i\n", cur_active_players);
- // at this point, cur active players should be the number of
- // people playing.
- // if it's only one, we're done.
- // otherwise, we fight
- // keep track of number of each size group we have
- // clear it out first.
- int group_size_counts[player_match_size_max + 1] = { 0 };
- // there can be only one.
- if (cur_active_players > 1)
- {
- // figure out player groups ------------------------------
- // new 28-nov-2018
- // now we support keeping groups to 2 and 3p when possible
- switch (player_group_limit)
- {
- case 2:
- // here, the logic is:
- // if odd number of players, there needs to be 1 3p
- // group. the rest are 2p
- if (cur_active_players % player_group_limit)
- {
- group_size_counts[3] = 1;
- }
- group_size_counts[2] =
- (cur_active_players - (3 * group_size_counts[3]))
- / player_group_limit;
- break;
- case 3:
- // here, the logic is:
- // if remainder of players / 3 is 1, we need 2 2p
- // if remainder is 2, we need 1 2p group
- // 02 = 2
- // 03 = 3
- // 04 = 2 2
- // 05 = 3 2
- // 06 = 3 3
- // 07 = 3 2 2
- // 08 = 3 3 2
- // 09 = 3 3 3
- // ...
- switch (cur_active_players % player_group_limit)
- {
- case 1:
- group_size_counts[2] = 2;
- break;
- case 2:
- group_size_counts[2] = 1;
- break;
- default:
- break;
- }
- group_size_counts[3] =
- (cur_active_players - (2 * group_size_counts[2]))
- / player_group_limit;
- break;
- case 4:
- // need to find out how many groups and how many players
- // in each group
- // 02 = 2
- // 03 = 3
- // 04 = 4
- // 05 = 3 2
- // 06 = 3 3
- // 07 = 4 3
- // 08 = 4 4
- // 09 = 3 3 3
- // 10 = 4 3 3
- // 11 = 4 4 3
- // 12 = 4 4 4
- // 13 = 4 3 3 3
- // 14 = 4 4 3 3
- // 15 = 4 4 4 3
- // 16 = 4 4 4 4
- // 17 = 4 4 3 3 3
- // ...
- // special cases up to 5, after that, algorithmically
- switch (cur_active_players)
- {
- case 2:
- group_size_counts[2] = 1;
- break;
- case 3:
- group_size_counts[3] = 1;
- break;
- case 4:
- group_size_counts[4] = 1;
- break;
- case 5:
- group_size_counts[2] = 1;
- group_size_counts[3] = 1;
- break;
- default:
- // 6+
- int groups3p = player_match_size_max
- - (cur_active_players % player_match_size_max);
- if (groups3p == player_match_size_max)
- {
- groups3p = 0;
- }
- // now that we know how many 3p groups there need to be,
- // we can figure out how many 4p groups we need.
- group_size_counts[3] = groups3p;
- group_size_counts[4] =
- (cur_active_players - (3 * groups3p))
- / player_match_size_max;
- break;
- } // 4-player groups special case switch
- break;
- default:
- // lol wut
- printf("Something bad happened.\n");
- printf("player_group_limit (%i) is bad somehow.\n",
- player_group_limit);
- exit(-1);
- break;
- } // player group size switch
- dpf(4, "groups: 2p = %i ... 3p = %i ... 4p = %i\n",
- group_size_counts[2],
- group_size_counts[3],
- group_size_counts[4]);
- // tally up the total tally for all games played
- // this figures into games toward TGP later
- total_group_size_tally[2] += group_size_counts[2];
- total_group_size_tally[3] += group_size_counts[3];
- total_group_size_tally[4] += group_size_counts[4];
- // this keeps track of how far down the active player list
- // we are. update after each group's result
- int sorted_index = 0;
- // this loop packs the higher numbered groups with the lowest
- // strikes. in theory, that gives out more strikes to people
- // with fewer I guess?
- //
- // if you want smaller groups in the lower strikes, you can
- // obviously invert the logic.
- // hmm, could make this a command line option one day
- #if 1
- // smaller groups to more-struck players
- for (int group_size = player_match_size_max;
- group_size >= player_match_size_min; --group_size)
- #else
- // smaller groups to least-struck players
- for (int group_size = player_match_size_min;
- group_size <= player_match_size_max; ++group_size)
- #endif
- {
- // are there any more groups of this size?
- while (group_size_counts[group_size])
- {
- // pick a random result from the strike_result_tab
- unsigned int result;
- #if defined(_WIN64)
- rand_s(&result);
- #endif
- #if defined(__linux__)
- getrandom(&result, sizeof(result), 0);
- #endif
- #if defined(__APPLE__)
- result = 123456789; // you probably want to fix this
- #endif
- // normalize our random num to the number of entries
- unsigned int result_index = (unsigned int)
- ((double)result
- / ((double)UINT_MAX + 1)
- * (double)strike_result_tab[group_size].num_results);
- // assign the strikes for this "group"
- for (int p = 0; p < group_size; p++)
- {
- (*sorted_player_strikes[sorted_index + p])
- += strike_result_tab[group_size].results_tab[result_index][p];
- }
- // advance to next "group"
- sorted_index += group_size;
- // one less group to do in this size now
- group_size_counts[group_size]--;
- }
- }
- // this step really isn't needed, it's the same as next round
- //dpf(3, "*** NEW STRIKES ***\n");
- //for (int pi = 0; pi < num_players; pi++)
- //{
- // dpf(3, "%3i: %i\n", pi, player_strikes[pi]);
- //}
- // keep track of players this round for printing at end
- active_players_per_round[cur_round] = cur_active_players;
- // keep track of players this round for all time
- total_tally_players_per_round[cur_round] += cur_active_players;
- // round successfully completed.
- ++cur_round;
- } // if after seeding there are still players around
- else
- {
- // only one player was remaining after figuring out matchups
- // chalk for average completion round
- total_tally_rounds_match_lasted += cur_round;
- // chalk for list of ending rounds
- ++total_rounds_ended_on_round[cur_round];
- // keep track of highest round #
- if (cur_round > max_rounds_actually_played)
- {
- max_rounds_actually_played = cur_round;
- }
- // print result of this match (groups/round)
- if (!excel)
- {
- printf("**** PLAYERS PER ROUND, RUN #%i ****\n", runs + 1);
- for (int round = 0; round < cur_round; round++)
- {
- printf("round %3i:\t%i\n",
- round + 1,
- active_players_per_round[round]);
- }
- printf("\n\n");
- }
- }
- } // while players still active
- // completed this run
- ++runs;
- } // runs loop
- // time for non-excel summary!
- if (!excel)
- {
- printf("\n\n\n");
- printf("**** PLAYERS PER ROUND, SUMMARY AFTER RUN %i ****\n", runs);
- printf("\n");
- for (int round = 0; round < max_rounds_actually_played; round++)
- {
- printf("round %3i:\t%f\n",
- round + 1,
- (float)total_tally_players_per_round[round]
- / (float)num_runs);
- }
- printf("\n\n\n");
- printf("**** ROUNDS MATCH LASTED SUMMARY AFTER RUN %i ****\n", runs);
- printf("\n");
- // keep track if we are printing yet
- int print_end_round = 0;
- for (int round = 1; round <= max_rounds_actually_played; round++)
- {
- if (total_rounds_ended_on_round[round] || print_end_round)
- {
- printf("round %3i:\t%i\n",
- round,
- total_rounds_ended_on_round[round]);
- print_end_round = 1;
- }
- }
- printf("\n\n\n");
- printf("**** AVERAGE ROUNDS LASTED, SUMMARY AFTER RUN %i ****\n", runs);
- printf("\n");
- printf("average match lasted %f\n",
- (float)total_tally_rounds_match_lasted / (float)num_runs);
- printf("\n\n\n");
- printf("**** TOTAL GAMES PLAYED COUNTS ****\n");
- printf("\n");
- printf("2-player games: %i\n", total_group_size_tally[2]);
- printf("3-player games: %i\n", total_group_size_tally[3]);
- printf("4-player games: %i\n", total_group_size_tally[4]);
- }
- float tgp_total =
- (float)(total_group_size_tally[2]) +
- ((float)total_group_size_tally[3] * 1.5f) +
- ((float)total_group_size_tally[4] * 2.0f);
- if (!excel)
- {
- printf("\n\nTGP total: %.1f\n\n", tgp_total);
- }
- float tgp_count =
- (float)total_group_size_tally[2] +
- (float)total_group_size_tally[3] +
- (float)total_group_size_tally[4];
- if (!excel)
- {
- printf("TGP average per round: %.3f\n\n", (tgp_total / tgp_count));
- printf("TGP match average: %.3f\n\n",
- ((float)total_tally_rounds_match_lasted * tgp_total / tgp_count / (float)num_runs));
- }
- // tab-delimited output which excel helpfully interprets as
- // column separators.
- // the output order is:
- // player count<tab>average rounds<tab>average game weight<tab>games to TGP
- if (excel)
- {
- printf("%i\t%.3f\t%.3f\t%.3f\n",
- num_players,
- ((float)total_tally_rounds_match_lasted / (float)num_runs),
- (tgp_total / tgp_count),
- ((float)total_tally_rounds_match_lasted * tgp_total / tgp_count / (float)num_runs)
- );
- }
- // move on to the next player count if applicable.
- num_players++;
- } // main player count loop
- return 0;
- }
Advertisement
Add Comment
Please, Sign In to add comment