Guest User

Untitled

a guest
Nov 30th, 2018
281
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 29.74 KB | None | 0 0
  1. //
  2. // FairStrikeSim:  Simulate matches of elimination strike play.
  3. //
  4. // End user provides # players, # strikes, and # simulations to run
  5. //
  6. // By Keith P. Johnson (twitter @pinballkeefer)
  7. //
  8. // This code is public domain.  Do your worst.  Attribution would be nice.
  9. //
  10.  
  11. // ---------------------------------------------------------------------------
  12. // architecture
  13. // ---------------------------------------------------------------------------
  14.  
  15. // currently supported are windows (_WIN64) and linux (__linux__)
  16.  
  17. // you're on your own for anything else.  tried to leave some placeholders
  18. // for __APPLE__ which looks like is a thing.
  19.  
  20. // ---------------------------------------------------------------------------
  21.  
  22. #if defined(_WIN64)
  23.  
  24. #include <SDKDDKVer.h>
  25.  
  26. #include <stdio.h>
  27. #include <tchar.h>
  28.  
  29. #define _CRT_RAND_S
  30. #include <stdlib.h>
  31.  
  32. // lol windows
  33. #define strtol _tcstol
  34.  
  35. #endif
  36.  
  37. // ---------------------------------------------------------------------------
  38.  
  39. #if defined(__linux__)
  40.  
  41. #include <stdio.h>
  42. #include <limits.h>
  43. #include <string.h>
  44. #include <stdlib.h>
  45. #include <sys/random.h>
  46.  
  47. #endif
  48.  
  49. // ---------------------------------------------------------------------------
  50.  
  51. #if defined(__APPLE__)
  52.  
  53. // I dunno lol
  54.  
  55. #endif
  56.  
  57. // ---------------------------------------------------------------------------
  58.  
  59. // controls how verbose program is
  60. // range is 1-5, 1 least verbose (0 is absolutely nothing)
  61. static int debug_level = 0;
  62.  
  63. // debug printf macro, will print message if debug_level above is set to at
  64. // least the number specified in the dpf statement.
  65. #define dpf(level, ...)     if (debug_level >= level) { printf(__VA_ARGS__); }
  66.  
  67. // ---------------------------------------------------------------------------
  68. // app config
  69. // ---------------------------------------------------------------------------
  70.  
  71. // how big/small player groups can be.  I don't guarantee all the logic works
  72. // properly if you change these, though.
  73. const int player_match_size_min = 2;
  74. const int player_match_size_max = 4;
  75.  
  76. // size of results tables
  77. const int result_table_size_2p = 2 * 1;
  78. const int result_table_size_3p = 3 * 2 * 1;
  79. const int result_table_size_4p = 4 * 3 * 2 * 1;
  80.  
  81. // range of virtual players to simulate, arbitrary top limit
  82. const int players_min = 2;
  83. const int players_max = 1024;
  84.  
  85. // range of strikes to simulate against, arbitrary top limit
  86. const int strikes_min = 1;
  87. const int strikes_max = 100;
  88.  
  89. // range of number of runs, arbitrary top limit
  90. const int runs_min = 1;
  91. const int runs_max = 1000000000;
  92.  
  93. // gotta stop somewhere, arbitrary limit
  94. const int rounds_max = 1024;
  95.  
  96. // ---------------------------------------------------------------------------
  97. // globals for analysis
  98. // ---------------------------------------------------------------------------
  99.  
  100. // just used for the end so we know how many rounds to print out
  101. int max_rounds_actually_played = 0;
  102.  
  103. // current strikes for each player
  104. int player_strikes[players_max] = { 0 };
  105.  
  106. // pointer to each player record sorted by # strikes they have
  107. // this is how new strikes get assigned into player_strikes[] by being sorted
  108. // by current # strikes into this array
  109. int* sorted_player_strikes[players_max] = { NULL };
  110.  
  111. // how many players still alive, printed out at end of each round
  112. int active_players_per_round[rounds_max] = { 0 };
  113.  
  114. // running tally of players per round, gets printed at end of run
  115. int total_tally_players_per_round[rounds_max] = { 0 };
  116.  
  117. // running tally of which round a match ended, gets printed at end of run
  118. int total_rounds_ended_on_round[rounds_max] = { 0 };
  119.  
  120. // running tally of how many rounds a match lasted, basically a sum of round
  121. // numbers (same as above).  gets printed at end, just easier than summing
  122. // and multiplying everything up
  123. int total_tally_rounds_match_lasted = 0;
  124.  
  125. // running tally of x-player games played.  use this to figure out WPPRs!
  126. int total_group_size_tally[5] = { 0 };
  127.  
  128. // ---------------------------------------------------------------------------
  129. // strike tables
  130. // ---------------------------------------------------------------------------
  131.  
  132. // these are brute-force possible results of each round.  generating these
  133. // on the fly is left as an exercise to the reader.
  134.  
  135.  
  136. // #if 1 here gets you keefer (fair) strikes
  137. // #if 0 gets you progressive (01, 012, 0123) strikes)
  138. // of course you can make it friendlier than this, add more, etc.
  139.  
  140. // ---------------------------------------------------------------------------
  141.  
  142. // new in version 2!  allow people to pick what strikes they want from
  143. // command line.  default to fair strikes
  144.  
  145. int strike_gen_table[player_match_size_max + 1][player_match_size_max] =
  146. {
  147.     { 0 },              // 0-player games
  148.     { 0 },              // 1-player games
  149.     { 0, 2 },           // 2-player games
  150.     { 0, 1, 2 },        // 3-player games
  151.     { 0, 1, 1, 2 },     // 4-player games
  152. };
  153.  
  154. int two_player_match_results[result_table_size_2p][player_match_size_max];
  155. int three_player_match_results[result_table_size_3p][player_match_size_max];
  156. int four_player_match_results[result_table_size_4p][player_match_size_max];
  157.  
  158. #if 0
  159.  
  160. // keefer strikes (fair strikes)
  161.  
  162. int two_player_match_results[][player_match_size_max] =
  163. {
  164.     {0,2,0,0},
  165.     {2,0,0,0},
  166. };
  167.  
  168. int three_player_match_results[][player_match_size_max] =
  169. {
  170.     {0,1,2,0},
  171.     {0,2,1,0},
  172.     {1,0,2,0},
  173.     {1,2,0,0},
  174.     {2,0,1,0},
  175.     {2,1,0,0},
  176. };
  177.  
  178. int four_player_match_results[][player_match_size_max] =
  179. {
  180.     {0,1,1,2},
  181.     {0,1,2,1},
  182.     {0,2,1,1},
  183.     {1,0,1,2},
  184.     {1,0,2,1},
  185.     {1,1,0,2},
  186.     {1,1,2,0},
  187.     {1,2,0,1},
  188.     {1,2,1,0},
  189.     {2,0,1,1},
  190.     {2,1,0,1},
  191.     {2,1,1,0},
  192. };
  193.  
  194. #endif
  195.  
  196. #if 0
  197.  
  198. // progressive strikes
  199.  
  200. int two_player_match_results[][player_match_size_max] =
  201. {
  202.     { 0,1,0,0 },
  203.     { 1,0,0,0 },
  204. };
  205.  
  206. int three_player_match_results[][player_match_size_max] =
  207. {
  208.     { 0,1,2,0 },
  209.     { 0,2,1,0 },
  210.     { 1,0,2,0 },
  211.     { 1,2,0,0 },
  212.     { 2,0,1,0 },
  213.     { 2,1,0,0 },
  214. };
  215.  
  216. int four_player_match_results[][player_match_size_max] =
  217. {
  218.     { 0,1,2,3 },
  219.     { 0,1,3,2 },
  220.     { 0,2,1,3 },
  221.     { 0,2,3,1 },
  222.     { 0,3,1,2 },
  223.     { 0,3,2,1 },
  224.     { 1,0,2,3 },
  225.     { 1,0,3,2 },
  226.     { 1,2,0,3 },
  227.     { 1,2,3,0 },
  228.     { 1,3,0,2 },
  229.     { 1,3,2,0 },
  230.     { 2,0,1,3 },
  231.     { 2,0,3,1 },
  232.     { 2,1,0,3 },
  233.     { 2,1,3,0 },
  234.     { 2,3,1,0 },
  235.     { 2,3,0,1 },
  236.     { 3,0,1,2 },
  237.     { 3,0,2,1 },
  238.     { 3,1,0,2 },
  239.     { 3,1,2,0 },
  240.     { 3,2,0,1 },
  241.     { 3,2,1,0 },
  242. };
  243. #endif
  244.  
  245. #if 0
  246.  
  247. // since I'm making this nice, here's how you'd do the giant number of
  248. // ultra-fair strikes.  someone called it an endurance battle.  why not
  249. // pinball battle royale?  whatever.
  250.  
  251. // I haven't tested this but it should work.
  252.  
  253. int two_player_match_results[][player_match_size_max] =
  254. {
  255.     { 0,6,0,0 },
  256.     { 6,0,0,0 },
  257. };
  258.  
  259. int three_player_match_results[][player_match_size_max] =
  260. {
  261.     { 0,3,6,0 },
  262.     { 0,6,3,0 },
  263.     { 3,0,6,0 },
  264.     { 3,6,0,0 },
  265.     { 6,0,3,0 },
  266.     { 6,3,0,0 },
  267. };
  268.  
  269. int four_player_match_results[][player_match_size_max] =
  270. {
  271.     { 0,2,4,6 },
  272.     { 0,2,6,4 },
  273.     { 0,4,2,6 },
  274.     { 0,4,6,2 },
  275.     { 0,6,2,4 },
  276.     { 0,6,4,2 },
  277.     { 2,0,4,6 },
  278.     { 2,0,6,4 },
  279.     { 2,4,0,6 },
  280.     { 2,4,6,0 },
  281.     { 2,6,0,4 },
  282.     { 2,6,4,0 },
  283.     { 4,0,2,6 },
  284.     { 4,0,6,2 },
  285.     { 4,2,0,6 },
  286.     { 4,2,6,0 },
  287.     { 4,6,2,0 },
  288.     { 4,6,0,2 },
  289.     { 6,0,2,4 },
  290.     { 6,0,4,2 },
  291.     { 6,2,0,4 },
  292.     { 6,2,4,0 },
  293.     { 6,4,0,2 },
  294.     { 6,4,2,0 },
  295. };
  296.  
  297. #endif
  298.  
  299.  
  300.  
  301.  
  302. // array of ints of player_match_size_max size
  303. typedef int result_array_t[player_match_size_max];
  304.  
  305. // define table of all possible round results
  306. typedef struct _player_strike_table_s
  307. {
  308.     int num_results;                // # results for this group of players
  309.     result_array_t* results_tab;    // pointer to results table
  310. } player_strike_table_t;
  311.  
  312. // for ease-of-reading, we use literally the number of players to index the
  313. // array.  efficiency is overrated.
  314. player_strike_table_t strike_result_tab[player_match_size_max + 1] =
  315. {
  316.     {0, NULL},
  317.     {0, NULL},
  318.     { sizeof(two_player_match_results) / sizeof(two_player_match_results[0]), two_player_match_results },
  319.     { sizeof(three_player_match_results) / sizeof(three_player_match_results[0]), three_player_match_results },
  320.     { sizeof(four_player_match_results) / sizeof(four_player_match_results[0]), four_player_match_results },
  321. };
  322.  
  323. // ---------------------------------------------------------------------------
  324. // main monolith brute force function.  you get what you pay for
  325. // ---------------------------------------------------------------------------
  326.  
  327. #if defined(_WIN64)
  328. int _tmain(int argc, _TCHAR* argv[])
  329. #else
  330. int main(int argc, char* argv[])
  331. #endif
  332. {
  333.     bool need_usage = false;    // bro do u even run
  334.     int num_players = 0;        // current number of players for run
  335.     int num_players_start = 0;  // for a range, start with # players
  336.     int num_players_end = 0;    // for a range, end with # players
  337.     int num_strikes = 0;        // how many strikes each player gets
  338.     int num_runs = 0;           // how many times to run simulation each count
  339.     int excel = 0;              // whether we're dumping for excel or not
  340.     int player_group_limit = 4; // try not to go over this # players/group
  341.  
  342.     // parse the command line ------------------------------------------------
  343.  
  344.     int arg_index = 1;
  345.  
  346.     // new 29-nov-2018:  everything is with -switches now
  347.     while (arg_index < argc)
  348.     {
  349.         // every option begins with a "-"
  350.         if (argv[arg_index][0] == '-')
  351.         {
  352.             switch (argv[arg_index][1])
  353.             {
  354.             case 'h':
  355.             case '?':
  356.                 // standard force help display
  357.                 // even though help will show up anytime you fuck up
  358.                 need_usage = true;
  359.                 break;
  360.             case 'p':
  361.                 // "# players" option
  362.                 switch (argv[arg_index][2])
  363.                 {
  364.                 case 'l':
  365.                     // low players
  366.                     num_players_start = strtol(&argv[arg_index][3], NULL, 10);
  367.                     break;
  368.                 case 'h':
  369.                     // high players
  370.                     num_players_end = strtol(&argv[arg_index][3], NULL, 10);
  371.                     break;
  372.                 default:
  373.                     // only do this # of players
  374.                     num_players = strtol(&argv[arg_index][2], NULL, 10);
  375.                     break;
  376.                 }
  377.                 break;
  378.             case 's':
  379.                 // number of strikes in match for each player
  380.                 num_strikes = strtol(&argv[arg_index][2], NULL, 10);
  381.                 break;
  382.             case 'r':
  383.                 // number of times to run sim for each player count
  384.                 num_runs = strtol(&argv[arg_index][2], NULL, 10);
  385.                 break;
  386.             case 'g':
  387.                 // player group size limit (except to avoid byes)
  388.                 player_group_limit = strtol(&argv[arg_index][2], NULL, 10);
  389.                 break;
  390.             case 'x':
  391.                 // present only a summary suitable for excel
  392.                 // this results in data that's tab-delimited which excel
  393.                 // happily accepts, but only if you redirect to a file
  394.                 // like "fairstrikestim > data.txt"
  395.                 // you won't be able to just copy the screen output, because
  396.                 // that will result in spaces, not tabs.
  397.                 excel = 1;
  398.                 break;
  399.             case 'c':
  400.                 // strike configuration
  401.  
  402.                 if (argc <= arg_index + 9)
  403.                 {
  404.                     need_usage = true;
  405.                 }
  406.                 else
  407.                 {
  408.                     // a Good Thing to do at some point would be to make sure
  409.                     // none of these starts with a '-' or other letter.
  410.                     // at some point.
  411.  
  412.                     strike_gen_table[2][0] = strtol(argv[++arg_index], NULL, 10);
  413.                     strike_gen_table[2][1] = strtol(argv[++arg_index], NULL, 10);
  414.                     strike_gen_table[3][0] = strtol(argv[++arg_index], NULL, 10);
  415.                     strike_gen_table[3][1] = strtol(argv[++arg_index], NULL, 10);
  416.                     strike_gen_table[3][2] = strtol(argv[++arg_index], NULL, 10);
  417.                     strike_gen_table[4][0] = strtol(argv[++arg_index], NULL, 10);
  418.                     strike_gen_table[4][1] = strtol(argv[++arg_index], NULL, 10);
  419.                     strike_gen_table[4][2] = strtol(argv[++arg_index], NULL, 10);
  420.                     strike_gen_table[4][3] = strtol(argv[++arg_index], NULL, 10);
  421.                 }
  422.                 break;
  423.             default:
  424.                 // unrecognized option/switch
  425.                 need_usage = true;
  426.                 break;
  427.             }
  428.         }
  429.         else
  430.         {
  431.             need_usage = true;
  432.         }
  433.         ++arg_index;
  434.     }
  435.  
  436.     // arg range check -------------------------------------------------------
  437.  
  438.     if (num_players)
  439.     {
  440.         // we specified only one player count to simulate
  441.  
  442.         if ((num_players < players_min) || (num_players > players_max))
  443.         {
  444.             // not in range.
  445.             need_usage = true;
  446.         }
  447.         if (num_players_start || num_players_end)
  448.         {
  449.             // we also specified a start and/or end.  that's a no-no
  450.             need_usage = true;
  451.             printf("Specified both a single run and a start/end range.\n");
  452.         }
  453.     }
  454.     else
  455.     {
  456.         // assume range if no specific player count
  457.  
  458.         if ((num_players_start < players_min) || (num_players_start > players_max))
  459.         {
  460.             need_usage = true;
  461.         }
  462.  
  463.         if ((num_players_end < players_min) || (num_players_end > players_max))
  464.         {
  465.             need_usage = true;
  466.         }
  467.     }
  468.  
  469.     if ((num_strikes < strikes_min) || (num_strikes > strikes_max))
  470.     {
  471.         need_usage = true;
  472.     }
  473.  
  474.     if ((num_runs < runs_min) || (num_runs> runs_max))
  475.     {
  476.         need_usage = true;
  477.     }
  478.  
  479.     if ((player_group_limit < player_match_size_min)
  480.         || (player_group_limit > player_match_size_max))
  481.     {
  482.         need_usage = true;
  483.     }
  484.  
  485.     if (need_usage)
  486.     {
  487.         // what do you say, skull?
  488.  
  489. #if defined(_WIN64)
  490.         printf("usage:  %ws [options]\n\n", argv[0]);
  491. #else
  492.         printf("usage:  %s [options]\n\n", argv[0]);
  493. #endif
  494.         printf("Option list (* = required)\n\n");
  495.  
  496.         printf("\t-h:        this\n");
  497.         printf("\t-?:        this\n");
  498.         printf("\t-p<num>:   run sim only for <num> players\n");
  499.         printf("\t-pl<num>:  starting player count to run\n");
  500.         printf("\t-ph<num>:  ending player count to run\n");
  501.         printf("\t-s<num>:   # of strikes each player gets before losing\n");
  502.         printf("\t-r<num>:   # of times to run the sim for each player count\n");
  503.         printf("\t-g<num>:   maximum size of player groups (default:  4)\n");
  504.         printf("\t-x:        excel-friendly summary only\n");
  505.         printf("\t-c:        strike configuration (default:  fair strikes)\n");
  506.         printf("\t           provide space-delimited strikes thusly:\n");
  507.         printf("Specify the number of strikes to give in order of 2p, 3p, 4p games.\n");
  508.         printf("For example, to specify fair strikes, you would provide:\n");
  509.         printf("\n");
  510.         printf("-c 0 2 0 1 2 0 1 1 2\n");
  511.         printf("   -v- --v-- ---v---\n");
  512.         printf("    |    |      \\- 4 player game strikes\n");
  513.         printf("    |    \\- 3 player game strikes\n");
  514.         printf("    \\- 2 player game strikes\n");
  515.         printf("\n");
  516.  
  517.         printf("\t  group range:  %i-%i\n", player_match_size_min, player_match_size_max);
  518.         printf("\tplayers range:  %i-%i\n", players_min, players_max);
  519.         printf("\tstrikes range:  %i-%i\n", strikes_min, strikes_max);
  520.         printf("\t   runs range:  %i-%i\n", runs_min, runs_max);
  521.         printf("\n");
  522.         exit(-1);
  523.     }
  524.  
  525.     // create strike result possibilities tables -----------------------------
  526.     // new nov-2018:  just generate the tables instead of hardcoding
  527.     // chances are you'll screw it up anyway.
  528.  
  529.     // goal here is to iterate every possibility of arrangement of strikes
  530.     // build tables so we can just assign the results later quickly
  531.  
  532.     // 2-player generation
  533.     int cur_tab_row = 0;
  534.  
  535.     for (int i = 0; i < 2; i++)
  536.     {
  537.         for (int j = 0; j < 2; j++)
  538.         {
  539.             if (j != i)
  540.             {
  541.                 two_player_match_results[cur_tab_row][0] = strike_gen_table[2][i];
  542.                 two_player_match_results[cur_tab_row][1] = strike_gen_table[2][j];
  543.                 cur_tab_row++;
  544.             }
  545.         }
  546.     }
  547.     dpf(3, "Generated %i rows of 2-player results\n", cur_tab_row); // sanity
  548.  
  549.     // 3-player generation
  550.     cur_tab_row = 0;
  551.  
  552.     for (int i = 0; i < 3; i++)
  553.     {
  554.         for (int j = 0; j < 3; j++)
  555.         {
  556.             if (j != i)
  557.             {
  558.                 for (int k = 0; k < 3; k++)
  559.                 {
  560.                     if ((k != i) && (k != j))
  561.                     {
  562.                         three_player_match_results[cur_tab_row][0] = strike_gen_table[3][i];
  563.                         three_player_match_results[cur_tab_row][1] = strike_gen_table[3][j];
  564.                         three_player_match_results[cur_tab_row][2] = strike_gen_table[3][k];
  565.                         cur_tab_row++;
  566.                     }
  567.                 }
  568.             }
  569.         }
  570.     }
  571.     dpf(3, "Generated %i rows of 3-player results\n", cur_tab_row); // sanity
  572.  
  573.     // 4-player generation
  574.     cur_tab_row = 0;
  575.  
  576.     for (int i = 0; i < 4; i++)
  577.     {
  578.         for (int j = 0; j < 4; j++)
  579.         {
  580.             if (j != i)
  581.             {
  582.                 for (int k = 0; k < 4; k++)
  583.                 {
  584.                     if ((k != i) && (k != j))
  585.                     {
  586.                         for (int l = 0; l < 4; l++)
  587.                         {
  588.                             if ((l != i) && (l != j) && (l != k))
  589.                             {
  590.                                 four_player_match_results[cur_tab_row][0] = strike_gen_table[4][i];
  591.                                 four_player_match_results[cur_tab_row][1] = strike_gen_table[4][j];
  592.                                 four_player_match_results[cur_tab_row][2] = strike_gen_table[4][k];
  593.                                 four_player_match_results[cur_tab_row][3] = strike_gen_table[4][l];
  594.                                 cur_tab_row++;
  595.                             }
  596.                         }
  597.                     }
  598.                 }
  599.             }
  600.         }
  601.     }
  602.     dpf(3, "Generated %i rows of 4-player results\n", cur_tab_row); // sanity
  603.  
  604.     int runs;                   // keep track number of simulations
  605.     int cur_active_players;     // how many players are still in it
  606.     int cur_round;              // which round are we in?
  607.  
  608.     // debug output of generate strike tables --------------------------------
  609.     // don't worry; it seems to work
  610.  
  611.     if (debug_level >= 4)
  612.     {
  613.         int i;
  614.  
  615.         dpf(4, "2 player table:\n\n");
  616.         for (i = 0; i < result_table_size_2p; i++)
  617.         {
  618.             dpf(4, "%i %i\n",
  619.                 two_player_match_results[i][0],
  620.                 two_player_match_results[i][1]);
  621.         }
  622.  
  623.         dpf(4, "\n\n3 player table:\n\n");
  624.         for (i = 0; i < result_table_size_3p; i++)
  625.         {
  626.             dpf(4, "%i %i %i\n",
  627.                 three_player_match_results[i][0],
  628.                 three_player_match_results[i][1],
  629.                 three_player_match_results[i][2]);
  630.         }
  631.  
  632.         dpf(4, "\n\n4 player table:\n\n");
  633.         for (i = 0; i < result_table_size_4p; i++)
  634.         {
  635.             dpf(4, "%i %i %i %i\n",
  636.                 four_player_match_results[i][0],
  637.                 four_player_match_results[i][1],
  638.                 four_player_match_results[i][2],
  639.                 four_player_match_results[i][3]);
  640.         }
  641.  
  642.         dpf(4, "\n\n");
  643.     }
  644.  
  645.     // setup for simulation running - either once or a range -----------------
  646.     // this is admittedly getting a little hacky, BUT:
  647.     // a - it sure looks like it works
  648.     // b - didn't really intend all this functionality at the beginning
  649.  
  650.     if (!num_players)
  651.     {
  652.         // running multiple
  653.         num_players = num_players_start;
  654.     }
  655.     else
  656.     {
  657.         // running once
  658.         num_players_end = num_players;
  659.     }
  660.  
  661.     // simulation loop for all player counts ---------------------------------
  662.  
  663.     while (num_players <= num_players_end)
  664.     {
  665.         // stuff that needs to init each run of a player count
  666.         // these inits used to be elsewhere, but now that we're running
  667.         // different numbers of players each time, they had to be moved
  668.  
  669.         max_rounds_actually_played = 0;
  670.         memset(total_tally_players_per_round, 0, sizeof(total_tally_players_per_round));
  671.         memset(total_rounds_ended_on_round, 0, sizeof(total_rounds_ended_on_round));
  672.         total_tally_rounds_match_lasted = 0;
  673.         memset(total_group_size_tally, 0, sizeof(total_group_size_tally));
  674.         runs = 0;
  675.  
  676.  
  677.         // main sim loop, does all runs of a player count --------------------
  678.  
  679.         while (runs < num_runs)
  680.         {
  681.             dpf(1, "starting run #%i / %i\n", runs + 1, num_runs);
  682.  
  683.             // prime match loop
  684.             cur_active_players = num_players;
  685.             cur_round = 0;
  686.  
  687.             // everyone starts a new match with 0 strikes obviously
  688.             memset(player_strikes, 0, sizeof(player_strikes));
  689.  
  690.             // no one's played any rounds yet.
  691.             memset(active_players_per_round, 0, sizeof(active_players_per_round));
  692.  
  693.             dpf(2, "setup matches\n");
  694.  
  695.             // run one match per loop (eliminate all but 1 player)
  696.             while (cur_active_players > 1)
  697.             {
  698.                 dpf(2, "starting round # %i\n", cur_round + 1);
  699.  
  700.                 dpf(3, "*** CURRENT STRIKES ***\n");
  701.                 for (int pi = 0; pi < num_players; pi++)
  702.                 {
  703.                     dpf(3, "%3i:  %i\n", pi, player_strikes[pi]);
  704.                 }
  705.  
  706.                 // setup player matches, matching by # strikes
  707.  
  708.                 // first, clear it out
  709.                 memset(sorted_player_strikes, 0, sizeof(sorted_player_strikes));
  710.  
  711.                 // gets update as we populate "groups"
  712.                 cur_active_players = 0;
  713.  
  714.                 // order players from lowest to highest, forcing like-struck
  715.                 // people to play against each other, basically.
  716.                 // we start with the lowest number of strikes and move up
  717.                 for (int strike_check = 0; strike_check < num_strikes; strike_check++)
  718.                 {
  719.                     // find everyone that has the current number of strikes and
  720.                     // put them in the fight list
  721.  
  722.                     dpf(4, "check for strikes: %i\n", strike_check);
  723.                     dpf(5, "players:  ");
  724.  
  725.                     for (int i = 0; i < num_players; i++)
  726.                     {
  727.                         // it's all random so we don't care if everyone is
  728.                         // populated in the same order every time
  729.                         if (player_strikes[i] == strike_check)
  730.                         {
  731.                             dpf(5, "%i ", i);
  732.                             sorted_player_strikes[cur_active_players] = &player_strikes[i];
  733.                             ++cur_active_players;
  734.                         }
  735.                     }
  736.                     dpf(5, "\n");
  737.                 }
  738.  
  739.                 dpf(2, "cur active_players: %i\n", cur_active_players);
  740.  
  741.                 // at this point, cur active players should be the number of
  742.                 // people playing.
  743.  
  744.                 // if it's only one, we're done.
  745.                 // otherwise, we fight
  746.  
  747.                 // keep track of number of each size group we have
  748.                 // clear it out first.
  749.                 int group_size_counts[player_match_size_max + 1] = { 0 };
  750.  
  751.                 // there can be only one.
  752.                 if (cur_active_players > 1)
  753.                 {
  754.                     // figure out player groups ------------------------------
  755.                     // new 28-nov-2018
  756.                     // now we support keeping groups to 2 and 3p when possible
  757.  
  758.                     switch (player_group_limit)
  759.                     {
  760.                     case 2:
  761.                         // here, the logic is:
  762.                         // if odd number of players, there needs to be 1 3p
  763.                         // group.  the rest are 2p
  764.  
  765.                         if (cur_active_players % player_group_limit)
  766.                         {
  767.                             group_size_counts[3] = 1;
  768.                         }
  769.  
  770.                         group_size_counts[2] =
  771.                             (cur_active_players - (3 * group_size_counts[3]))
  772.                             / player_group_limit;
  773.  
  774.                         break;
  775.  
  776.                     case 3:
  777.                         // here, the logic is:
  778.                         // if remainder of players / 3 is 1, we need 2 2p
  779.                         // if remainder is 2, we need 1 2p group
  780.  
  781.                         // 02 = 2
  782.                         // 03 = 3
  783.                         // 04 = 2 2
  784.                         // 05 = 3 2
  785.                         // 06 = 3 3
  786.                         // 07 = 3 2 2
  787.                         // 08 = 3 3 2
  788.                         // 09 = 3 3 3
  789.                         // ...
  790.  
  791.                         switch (cur_active_players % player_group_limit)
  792.                         {
  793.                         case 1:
  794.                             group_size_counts[2] = 2;
  795.                             break;
  796.                         case 2:
  797.                             group_size_counts[2] = 1;
  798.                             break;
  799.                         default:
  800.                             break;
  801.                         }
  802.  
  803.                         group_size_counts[3] =
  804.                             (cur_active_players - (2 * group_size_counts[2]))
  805.                             / player_group_limit;
  806.  
  807.                         break;
  808.  
  809.                     case 4:
  810.                         // need to find out how many groups and how many players
  811.                         // in each group
  812.                         // 02 = 2
  813.                         // 03 = 3
  814.                         // 04 = 4
  815.                         // 05 = 3 2
  816.                         // 06 = 3 3
  817.                         // 07 = 4 3
  818.                         // 08 = 4 4
  819.                         // 09 = 3 3 3
  820.                         // 10 = 4 3 3
  821.                         // 11 = 4 4 3
  822.                         // 12 = 4 4 4
  823.                         // 13 = 4 3 3 3
  824.                         // 14 = 4 4 3 3
  825.                         // 15 = 4 4 4 3
  826.                         // 16 = 4 4 4 4
  827.                         // 17 = 4 4 3 3 3
  828.                         // ...
  829.  
  830.                         // special cases up to 5, after that, algorithmically
  831.  
  832.                         switch (cur_active_players)
  833.                         {
  834.                         case 2:
  835.                             group_size_counts[2] = 1;
  836.                             break;
  837.                         case 3:
  838.                             group_size_counts[3] = 1;
  839.                             break;
  840.                         case 4:
  841.                             group_size_counts[4] = 1;
  842.                             break;
  843.                         case 5:
  844.                             group_size_counts[2] = 1;
  845.                             group_size_counts[3] = 1;
  846.                             break;
  847.                         default:
  848.                             // 6+
  849.                             int groups3p = player_match_size_max
  850.                                 - (cur_active_players % player_match_size_max);
  851.                             if (groups3p == player_match_size_max)
  852.                             {
  853.                                 groups3p = 0;
  854.                             }
  855.  
  856.                             // now that we know how many 3p groups there need to be,
  857.                             // we can figure out how many 4p groups we need.
  858.  
  859.                             group_size_counts[3] = groups3p;
  860.                             group_size_counts[4] =
  861.                                 (cur_active_players - (3 * groups3p))
  862.                                 / player_match_size_max;
  863.                             break;
  864.                         }   // 4-player groups special case switch
  865.                         break;
  866.  
  867.                     default:
  868.                         // lol wut
  869.                         printf("Something bad happened.\n");
  870.                         printf("player_group_limit (%i) is bad somehow.\n",
  871.                                player_group_limit);
  872.                         exit(-1);
  873.                         break;
  874.                     }   // player group size switch
  875.  
  876.                     dpf(4, "groups:  2p = %i ... 3p = %i ... 4p = %i\n",
  877.                         group_size_counts[2],
  878.                         group_size_counts[3],
  879.                         group_size_counts[4]);
  880.  
  881.                     // tally up the total tally for all games played
  882.                     // this figures into games toward TGP later
  883.                     total_group_size_tally[2] += group_size_counts[2];
  884.                     total_group_size_tally[3] += group_size_counts[3];
  885.                     total_group_size_tally[4] += group_size_counts[4];
  886.  
  887.                     // this keeps track of how far down the active player list
  888.                     // we are.  update after each group's result
  889.                     int sorted_index = 0;
  890.  
  891.                     // this loop packs the higher numbered groups with the lowest
  892.                     // strikes.  in theory, that gives out more strikes to people
  893.                     // with fewer I guess?
  894.                     //
  895.                     // if you want smaller groups in the lower strikes, you can
  896.                     // obviously invert the logic.
  897.  
  898.                     // hmm, could make this a command line option one day
  899.  
  900. #if 1
  901.                     // smaller groups to more-struck players
  902.                     for (int group_size = player_match_size_max;
  903.                          group_size >= player_match_size_min; --group_size)
  904. #else
  905.                     // smaller groups to least-struck players
  906.                     for (int group_size = player_match_size_min;
  907.                          group_size <= player_match_size_max; ++group_size)
  908. #endif
  909.                     {
  910.                         // are there any more groups of this size?
  911.                         while (group_size_counts[group_size])
  912.                         {
  913.                             // pick a random result from the strike_result_tab
  914.                             unsigned int result;
  915.  
  916. #if defined(_WIN64)
  917.                                 rand_s(&result);
  918. #endif
  919. #if defined(__linux__)
  920.                             getrandom(&result, sizeof(result), 0);
  921. #endif
  922. #if defined(__APPLE__)
  923.                             result = 123456789; // you probably want to fix this
  924. #endif
  925.                             // normalize our random num to the number of entries
  926.                             unsigned int result_index = (unsigned int)
  927.                                 ((double)result
  928.                                  / ((double)UINT_MAX + 1)
  929.                                  * (double)strike_result_tab[group_size].num_results);
  930.  
  931.                             // assign the strikes for this "group"
  932.                             for (int p = 0; p < group_size; p++)
  933.                             {
  934.                                 (*sorted_player_strikes[sorted_index + p])
  935.                                     += strike_result_tab[group_size].results_tab[result_index][p];
  936.                             }
  937.  
  938.                             // advance to next "group"
  939.                             sorted_index += group_size;
  940.  
  941.                             // one less group to do in this size now
  942.                             group_size_counts[group_size]--;
  943.                         }
  944.                     }
  945.  
  946.                     // this step really isn't needed, it's the same as next round
  947.                     //dpf(3, "*** NEW STRIKES ***\n");
  948.                     //for (int pi = 0; pi < num_players; pi++)
  949.                     //{
  950.                     //  dpf(3, "%3i:  %i\n", pi, player_strikes[pi]);
  951.                     //}
  952.  
  953.                     // keep track of players this round for printing at end
  954.                     active_players_per_round[cur_round] = cur_active_players;
  955.  
  956.                     // keep track of players this round for all time
  957.                     total_tally_players_per_round[cur_round] += cur_active_players;
  958.  
  959.                     // round successfully completed.
  960.                     ++cur_round;
  961.  
  962.                 }       // if after seeding there are still players around
  963.                 else
  964.                 {
  965.                     // only one player was remaining after figuring out matchups
  966.  
  967.                     // chalk for average completion round
  968.                     total_tally_rounds_match_lasted += cur_round;
  969.  
  970.                     // chalk for list of ending rounds
  971.                     ++total_rounds_ended_on_round[cur_round];
  972.  
  973.                     // keep track of highest round #
  974.                     if (cur_round > max_rounds_actually_played)
  975.                     {
  976.                         max_rounds_actually_played = cur_round;
  977.                     }
  978.  
  979.                     // print result of this match (groups/round)
  980.  
  981.                     if (!excel)
  982.                     {
  983.                         printf("**** PLAYERS PER ROUND, RUN #%i ****\n", runs + 1);
  984.  
  985.                         for (int round = 0; round < cur_round; round++)
  986.                         {
  987.                             printf("round %3i:\t%i\n",
  988.                                    round + 1,
  989.                                    active_players_per_round[round]);
  990.                         }
  991.  
  992.                         printf("\n\n");
  993.                     }
  994.                 }
  995.             }       // while players still active
  996.  
  997.             // completed this run
  998.             ++runs;
  999.         }       // runs loop
  1000.  
  1001.         // time for non-excel summary!
  1002.  
  1003.         if (!excel)
  1004.         {
  1005.             printf("\n\n\n");
  1006.             printf("**** PLAYERS PER ROUND, SUMMARY AFTER RUN %i ****\n", runs);
  1007.             printf("\n");
  1008.  
  1009.             for (int round = 0; round < max_rounds_actually_played; round++)
  1010.             {
  1011.                 printf("round %3i:\t%f\n",
  1012.                        round + 1,
  1013.                        (float)total_tally_players_per_round[round]
  1014.                        / (float)num_runs);
  1015.             }
  1016.  
  1017.             printf("\n\n\n");
  1018.             printf("**** ROUNDS MATCH LASTED SUMMARY AFTER RUN %i ****\n", runs);
  1019.             printf("\n");
  1020.  
  1021.             // keep track if we are printing yet
  1022.             int print_end_round = 0;
  1023.  
  1024.             for (int round = 1; round <= max_rounds_actually_played; round++)
  1025.             {
  1026.                 if (total_rounds_ended_on_round[round] || print_end_round)
  1027.                 {
  1028.                     printf("round %3i:\t%i\n",
  1029.                            round,
  1030.                            total_rounds_ended_on_round[round]);
  1031.                     print_end_round = 1;
  1032.                 }
  1033.             }
  1034.  
  1035.             printf("\n\n\n");
  1036.             printf("**** AVERAGE ROUNDS LASTED, SUMMARY AFTER RUN %i ****\n", runs);
  1037.             printf("\n");
  1038.  
  1039.             printf("average match lasted %f\n",
  1040.                 (float)total_tally_rounds_match_lasted / (float)num_runs);
  1041.  
  1042.             printf("\n\n\n");
  1043.             printf("**** TOTAL GAMES PLAYED COUNTS ****\n");
  1044.             printf("\n");
  1045.  
  1046.             printf("2-player games:  %i\n", total_group_size_tally[2]);
  1047.             printf("3-player games:  %i\n", total_group_size_tally[3]);
  1048.             printf("4-player games:  %i\n", total_group_size_tally[4]);
  1049.         }
  1050.  
  1051.         float tgp_total =
  1052.             (float)(total_group_size_tally[2]) +
  1053.             ((float)total_group_size_tally[3] * 1.5f) +
  1054.             ((float)total_group_size_tally[4] * 2.0f);
  1055.  
  1056.         if (!excel)
  1057.         {
  1058.             printf("\n\nTGP total:  %.1f\n\n", tgp_total);
  1059.         }
  1060.  
  1061.         float tgp_count =
  1062.             (float)total_group_size_tally[2] +
  1063.             (float)total_group_size_tally[3] +
  1064.             (float)total_group_size_tally[4];
  1065.  
  1066.         if (!excel)
  1067.         {
  1068.             printf("TGP average per round:  %.3f\n\n", (tgp_total / tgp_count));
  1069.  
  1070.             printf("TGP match average:  %.3f\n\n",
  1071.                 ((float)total_tally_rounds_match_lasted * tgp_total / tgp_count / (float)num_runs));
  1072.         }
  1073.  
  1074.         // tab-delimited output which excel helpfully interprets as
  1075.         // column separators.
  1076.         // the output order is:
  1077.         // player count<tab>average rounds<tab>average game weight<tab>games to TGP
  1078.         if (excel)
  1079.         {
  1080.             printf("%i\t%.3f\t%.3f\t%.3f\n",
  1081.                    num_players,
  1082.                    ((float)total_tally_rounds_match_lasted / (float)num_runs),
  1083.                    (tgp_total / tgp_count),
  1084.                    ((float)total_tally_rounds_match_lasted * tgp_total / tgp_count / (float)num_runs)
  1085.             );
  1086.         }
  1087.  
  1088.         // move on to the next player count if applicable.
  1089.         num_players++;
  1090.     }       // main player count loop
  1091.  
  1092.  
  1093.     return 0;
  1094. }
Advertisement
Add Comment
Please, Sign In to add comment