Advertisement
aaaaaa123456789

Grid Wars battle calculator

Mar 30th, 2015
446
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 11.26 KB | None | 0 0
  1. /*
  2.    This code is hereby released to the public domain.
  3.    ~aaaaaa123456789, 2015-03-30, last updated 2015-12-02
  4. */
  5.  
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include <time.h>
  9.  
  10. #define DEFAULT_HP 10
  11. #define DEFAULT_POWER 10
  12. #define DEFAULT_BONUS 0
  13. #define DEFAULT_MIN_RANGE 50
  14. #define DEFAULT_MAX_RANGE 150
  15. #define ATTACK_ROUNDS 10
  16. #define DEFAULT_MASTERY 0
  17.  
  18. // NOTE: don't even try using a C++ compiler here (unless it has a C mode). It will cry. A lot.
  19.  
  20. struct player {
  21.   char * name;
  22.   unsigned power;
  23.   int bonus;
  24.   unsigned min;
  25.   unsigned max;
  26.   unsigned mastery;
  27. };
  28.  
  29. struct battle {
  30.   char * location;
  31.   unsigned HP;
  32.   struct player player1;
  33.   struct player player2;
  34. };
  35.  
  36. struct node {
  37.   struct battle battle;
  38.   struct node * next;
  39. };
  40.  
  41. struct range {
  42.   unsigned min;
  43.   unsigned max;
  44. };
  45.  
  46. unsigned randstate1 = 0, randstate2 = 0; // random number generator state
  47.  
  48. int main(void);
  49. void init_RNG(void);
  50. unsigned get_RNG(void);
  51. char * get_line(void);
  52. unsigned get_number(unsigned);
  53. int get_signed_number(int);
  54. struct node * get_battle_set(void);
  55. struct node * get_battle(int *);
  56. void delete_last_battle(struct node **, struct node **);
  57. struct player get_player_info(const char *);
  58. void make_battles(FILE *, struct node *);
  59. void make_battle(FILE *, struct battle);
  60. struct range effective_range(struct player);
  61. unsigned roll(unsigned);
  62.  
  63. // output routines
  64. void separator(FILE *);
  65. void header(FILE *, const char *);
  66. void player_info(FILE *, struct player, struct range);
  67. void player_separator(FILE *);
  68. void battle_header(FILE *, const char *, const char *);
  69. void attack(FILE *, unsigned, unsigned, unsigned, unsigned, unsigned);
  70. void battle_footer(FILE *);
  71. void footer(FILE *, const char *, const char *, const char *, unsigned, unsigned);
  72.  
  73.  
  74. int main (void) {
  75.   srand(time(NULL));
  76.   init_RNG();
  77.   struct node * battles = get_battle_set();
  78.   if (!battles) return 0;
  79.   puts("");
  80.   printf("%s", "File to write [default: output to console]: ");
  81.   char * filename = get_line();
  82.   FILE * file;
  83.   if (*filename)
  84.     file = fopen(filename, "w");
  85.   else
  86.     file = stdout;
  87.   if (!file) {
  88.     fprintf(stderr, "Error opening %s for writing\n", filename);
  89.     return 1;
  90.   }
  91.   make_battles(file, battles);
  92.   return 0;
  93. }
  94.  
  95. void init_RNG (void) {
  96.   unsigned char count;
  97.   unsigned num, result = 0;
  98.   for (count = 0; count < 4; count ++) {
  99.     num = (rand() >> 5) & 255;
  100.     result |= num << (8 * count);
  101.   }
  102.   randstate1 = randstate2;
  103.   randstate2 = result;
  104.   if (!(randstate1 && randstate2) || (randstate1 == 0x464fffff) || (randstate2 == 0x9068ffff)) init_RNG();
  105. }
  106.  
  107. unsigned get_RNG (void) {
  108.   // George Marsaglia's MWC. Better than rand() for most cases.
  109.   randstate2 = (36969 * (randstate2 & 65535) + (randstate2 >> 16)) & 0xffffffffu;
  110.   randstate1 = (18000 * (randstate1 & 65535) + (randstate1 >> 16)) & 0xffffffffu;
  111.   return ((randstate2 << 16) + randstate1) & 0xffffffffu;
  112. }
  113.  
  114. char * get_line (void) {
  115.   char * line = NULL;
  116.   unsigned length = 0;
  117.   int newchar;
  118.   for (newchar = getchar(); (newchar != EOF) && (newchar != '\n'); newchar = getchar()) {
  119.     line = realloc(line, ++ length);
  120.     line[length - 1] = newchar;
  121.   }
  122.   line = realloc(line, length + 1);
  123.   line[length] = 0;
  124.   return line;
  125. }
  126.  
  127. unsigned get_number (unsigned default_value) {
  128.   char * line = NULL;
  129.   char * end;
  130.   unsigned number, attempted = 0;
  131.   while (1) {
  132.     if (attempted) {
  133.       free(line);
  134.       printf("%s", "That is not a valid number. Please enter a number: ");
  135.     } else
  136.       attempted = 1;
  137.     line = get_line();
  138.     if (!(line && *line)) return default_value;
  139.     number = strtoul(line, &end, 10);
  140.     if (*end) continue;
  141.     free(line);
  142.     return number;
  143.   }
  144. }
  145.  
  146. int get_signed_number (int default_value) {
  147.   char * line = NULL;
  148.   char * end;
  149.   int number, attempted = 0;
  150.   while (1) {
  151.     if (attempted) {
  152.       free(line);
  153.       printf("%s", "That is not a valid number. Please enter a number: ");
  154.     } else
  155.       attempted = 1;
  156.     line = get_line();
  157.     if (!(line && *line)) return default_value;
  158.     number = strtol(line, &end, 10);
  159.     if (*end) continue;
  160.     free(line);
  161.     return number;
  162.   }
  163. }
  164.  
  165. struct node * get_battle_set (void) {
  166.   int x = 0;
  167.   struct node * result = get_battle(&x);
  168.   struct node * current = result;
  169.   struct node * new_node;
  170.   while (1) {
  171.     x = result != NULL;
  172.     new_node = get_battle(&x);
  173.     if (x) {
  174.       if (!new_node) break;
  175.       if (result)
  176.         current = current -> next = new_node;
  177.       else
  178.         result = current = new_node;
  179.     } else
  180.       delete_last_battle(&result, &current);
  181.   }
  182.   return result;
  183. }
  184.  
  185. struct node * get_battle (int * status) {
  186.   if (*status)
  187.     printf("%s", "Enter coordinates [<empty input>: stop, delete: delete last]: ");
  188.   else
  189.     printf("%s", "Enter coordinates [empty input to stop]: ");
  190.   char * coords = get_line();
  191.   if (!(coords && *coords)) {
  192.     free(coords);
  193.     *status = 1;
  194.     return NULL;
  195.   }
  196.   if (!strcmp(coords, "delete")) {
  197.     free(coords);
  198.     if (!*status) {
  199.       puts("Nothing to delete.");
  200.       return get_battle(status);
  201.     }
  202.     *status = 0;
  203.     return NULL;
  204.   }
  205.   struct battle battle = {.location = coords};
  206.   printf("Enter the HP of cell %s [default: %u]: ", coords, DEFAULT_HP);
  207.   battle.HP = get_number(DEFAULT_HP);
  208.   battle.player1 = get_player_info("Attacker");
  209.   battle.player2 = get_player_info("Defender");
  210.   puts("");
  211.   struct node * new = malloc(sizeof(struct node));
  212.   new -> battle = battle;
  213.   new -> next = NULL;
  214.   *status = 1;
  215.   return new;
  216. }
  217.  
  218. void delete_last_battle (struct node ** list, struct node ** pointer) {
  219.   if (!*list) return; // you're drunk, go home
  220.   if (*list == *pointer) {
  221.     free(*list);
  222.     *list = *pointer = NULL;
  223.     return;
  224.   }
  225.   struct node * previous;
  226.   for (previous = *list; previous -> next != *pointer; previous = previous -> next);
  227.   *pointer = previous;
  228.   free(previous -> next);
  229.   previous -> next = NULL;
  230. }
  231.  
  232. struct player get_player_info (const char * player) {
  233.   printf("%s data:\n", player);
  234.   printf("%s", "Name: ");
  235.   char * name = get_line();
  236.   while (!(name && *name)) {
  237.     free(name);
  238.     printf("%s", "The name must not be blank. Name: ");
  239.     name = get_line();
  240.   }
  241.   struct player result = {.name = name};
  242.   printf("Base power [default: %u]: ", DEFAULT_POWER);
  243.   result.power = get_number(DEFAULT_POWER);
  244.   printf("Bonus %% [default: %d]: ", DEFAULT_BONUS);
  245.   result.bonus = get_signed_number(DEFAULT_BONUS);
  246.   printf("Min. range %% [default: %u]: ", DEFAULT_MIN_RANGE);
  247.   result.min = get_number(DEFAULT_MIN_RANGE);
  248.   printf("Max. range %% [default: %u]: ", DEFAULT_MAX_RANGE);
  249.   result.max = get_number(DEFAULT_MAX_RANGE);
  250.   printf("Mastery %% [default: %u]: ", DEFAULT_MASTERY);
  251.   result.mastery = get_number(DEFAULT_MASTERY);
  252.   return result;
  253. }
  254.  
  255. void make_battles (FILE * output, struct node * battles) {
  256.   make_battle(output, battles -> battle);
  257.   for (battles = battles -> next; battles; battles = battles -> next) {
  258.     separator(output);
  259.     make_battle(output, battles -> battle);
  260.   }
  261. }
  262.  
  263. void make_battle (FILE * output, struct battle battle) {
  264.   header(output, battle.location);
  265.   struct range range1 = effective_range(battle.player1), range2 = effective_range(battle.player2);
  266.   player_info(output, battle.player1, range1);
  267.   player_separator(output);
  268.   player_info(output, battle.player2, range2);
  269.   battle_header(output, battle.player1.name, battle.player2.name);
  270.   unsigned char attacks = 0, wins = 0;
  271.   while ((attacks < ATTACK_ROUNDS) && (wins < battle.HP)) {
  272.     unsigned roll1 = roll(range1.max - range1.min), roll2 = roll(range2.max - range2.min);
  273.     unsigned char result = ((range1.min + roll1) > (range2.min + roll2)) ? 1 : ((range2.min + roll2) > (range1.min + roll1)) ? 2 : 0;
  274.     attacks ++;
  275.     if (result == 1) wins ++;
  276.     attack(output, range1.min, roll1, range2.min, roll2, result);
  277.   }
  278.   battle_footer(output);
  279.   footer(output, battle.player1.name, battle.player2.name, battle.location, wins, battle.HP - wins);
  280. }
  281.  
  282. struct range effective_range (struct player player) {
  283.   unsigned long long base = player.power * (100 + player.bonus);
  284.   unsigned long long min = base * player.min, max = base * player.max;
  285.   return (struct range) {.min = (min * 100 + (max - min) * player.mastery) / 1000000, .max = max / 10000};
  286. }
  287.  
  288. unsigned roll (unsigned max_roll) {
  289.   if (!max_roll) return 0;
  290.   unsigned limit = max_roll + 1;
  291.   unsigned result = -1;
  292.   while ((result + limit - (result % limit)) < result) result = get_RNG();
  293.   return result % limit;
  294. }
  295.  
  296.  
  297. // Output routines begin here. If the output format is to be changed, everything that should be changed starts here.
  298.  
  299. void separator (FILE * output) {
  300.   fputs("\n[hr]\n\n", output);
  301. }
  302.  
  303. void header (FILE * output, const char * coordinate) {
  304.   fprintf(output, "[b]Battle at %s[/b]\n", coordinate);
  305.   fputs("[spoiler][center][b]", output);
  306. }
  307.  
  308. void player_info (FILE * output, struct player player, struct range range) {
  309.   fprintf(output, "%s's Power: %u\n", player.name, player.power);
  310.   fprintf(output, "%s's Bonuses: %+d%%\n", player.name, player.bonus);
  311.   fprintf(output, "%s's Mastery: %u%%\n", player.name, player.mastery);
  312.   unsigned range_integer = player.min * 100 + (player.max - player.min) * player.mastery;
  313.   unsigned char range_fraction = range_integer % 100;
  314.   range_integer /= 100;
  315.   if (range_fraction)
  316.     fprintf(output, "%s's Range: %u.%02hhu%% - %u%%\n", player.name, range_integer, range_fraction, player.max);
  317.   else
  318.     fprintf(output, "%s's Range: %u%% - %u%%\n", player.name, range_integer, player.max);
  319.   fprintf(output, "%s's Effective Range: %u - %u\n", player.name, range.min, range.max);
  320. }
  321.  
  322. void player_separator (FILE * output) {
  323.   fputs("\nVS\n\n", output);
  324. }
  325.  
  326. void battle_header (FILE * output, const char * attacker, const char * defender) {
  327.   fputs("\n[table=width: 400px; margin: auto]\n[tr]\n", output);
  328.   fprintf(output, "[td]%s[/td]\n", attacker);
  329.   fprintf(output, "[td]%s[/td]\n", defender);
  330.   fputs("[td]Result[/td]\n", output);
  331.   fputs("[/tr]\n", output);
  332. }
  333.  
  334. void attack (FILE * output, unsigned min1, unsigned roll1, unsigned min2, unsigned roll2, unsigned result) {
  335.   if (result > 2) return;
  336.   fputs("[tr]\n", output);
  337.   fprintf(output, "[td]%u + %u = %u[/td]\n", min1, roll1, min1 + roll1);
  338.   fprintf(output, "[td]%u + %u = %u[/td]\n", min2, roll2, min2 + roll2);
  339.   fprintf(output, "[td][color=%s]%s[/color][/td]\n",
  340.           result[(const char * []) {"#000000", "#008000", "#ff0000"}],
  341.           result[(const char * []) {"Draw", "Win", "Lose"}]
  342.          );
  343.   fputs("[/tr]\n", output);
  344. }
  345.  
  346. void battle_footer (FILE * output) {
  347.   fputs("[/table]\n[/b][/center][/spoiler]\n\n", output);
  348. }
  349.  
  350. void footer (FILE * output, const char * attacker, const char * defender, const char * coordinate, unsigned wins, unsigned remaining) {
  351.   fputs("[b]", output);
  352.   if (wins) {
  353.     fprintf(output, "[color=#008000]%s wins %u %s.[/color]\n\n", attacker, wins, (wins == 1) ? "fight" : "fights");
  354.     fprintf(output, "%s loses %uHP.\n", defender, wins);
  355.   } else
  356.     fprintf(output, "[color=#ff0000]%s wins no fights.[/color]\n\n", attacker);
  357.   if (remaining)
  358.     fprintf(output, "%s has %uHP remaining.", coordinate, remaining);
  359.   else
  360.     fprintf(output, "%s falls to %s.", coordinate, attacker);
  361.   fputs("[/b]\n", output);
  362. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement