aaaaaa123456789 icon

Grid Wars battle calculator

aaaaaa123456789 | PRO | 03/30/15 07:42:21 AM UTC | 0 ⭐ | 713 👁️ | Never ⏰ | []
C |

11.26 KB

|

None

|

0 👍

/

0 👎

/*
   This code is hereby released to the public domain.
   ~aaaaaa123456789, 2015-03-30, last updated 2015-12-02
*/
 
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
 
#define DEFAULT_HP 10
#define DEFAULT_POWER 10
#define DEFAULT_BONUS 0
#define DEFAULT_MIN_RANGE 50
#define DEFAULT_MAX_RANGE 150
#define ATTACK_ROUNDS 10
#define DEFAULT_MASTERY 0
 
// NOTE: don't even try using a C++ compiler here (unless it has a C mode). It will cry. A lot.
 
struct player {
  char * name;
  unsigned power;
  int bonus;
  unsigned min;
  unsigned max;
  unsigned mastery;
};
 
struct battle {
  char * location;
  unsigned HP;
  struct player player1;
  struct player player2;
};
 
struct node {
  struct battle battle;
  struct node * next;
};
 
struct range {
  unsigned min;
  unsigned max;
};
 
unsigned randstate1 = 0, randstate2 = 0; // random number generator state
 
int main(void);
void init_RNG(void);
unsigned get_RNG(void);
char * get_line(void);
unsigned get_number(unsigned);
int get_signed_number(int);
struct node * get_battle_set(void);
struct node * get_battle(int *);
void delete_last_battle(struct node **, struct node **);
struct player get_player_info(const char *);
void make_battles(FILE *, struct node *);
void make_battle(FILE *, struct battle);
struct range effective_range(struct player);
unsigned roll(unsigned);
 
// output routines
void separator(FILE *);
void header(FILE *, const char *);
void player_info(FILE *, struct player, struct range);
void player_separator(FILE *);
void battle_header(FILE *, const char *, const char *);
void attack(FILE *, unsigned, unsigned, unsigned, unsigned, unsigned);
void battle_footer(FILE *);
void footer(FILE *, const char *, const char *, const char *, unsigned, unsigned);
 
 
int main (void) {
  srand(time(NULL));
  init_RNG();
  struct node * battles = get_battle_set();
  if (!battles) return 0;
  puts("");
  printf("%s", "File to write [default: output to console]: ");
  char * filename = get_line();
  FILE * file;
  if (*filename) 
    file = fopen(filename, "w");
  else
    file = stdout;
  if (!file) {
    fprintf(stderr, "Error opening %s for writing\n", filename);
    return 1;
  }
  make_battles(file, battles);
  return 0;
}
 
void init_RNG (void) {
  unsigned char count;
  unsigned num, result = 0;
  for (count = 0; count < 4; count ++) {
    num = (rand() >> 5) & 255;
    result |= num << (8 * count);
  }
  randstate1 = randstate2;
  randstate2 = result;
  if (!(randstate1 && randstate2) || (randstate1 == 0x464fffff) || (randstate2 == 0x9068ffff)) init_RNG();
}
 
unsigned get_RNG (void) {
  // George Marsaglia's MWC. Better than rand() for most cases.
  randstate2 = (36969 * (randstate2 & 65535) + (randstate2 >> 16)) & 0xffffffffu;
  randstate1 = (18000 * (randstate1 & 65535) + (randstate1 >> 16)) & 0xffffffffu;
  return ((randstate2 << 16) + randstate1) & 0xffffffffu;
}
 
char * get_line (void) {
  char * line = NULL;
  unsigned length = 0;
  int newchar;
  for (newchar = getchar(); (newchar != EOF) && (newchar != '\n'); newchar = getchar()) {
    line = realloc(line, ++ length);
    line[length - 1] = newchar;
  }
  line = realloc(line, length + 1);
  line[length] = 0;
  return line;
}
 
unsigned get_number (unsigned default_value) {
  char * line = NULL;
  char * end;
  unsigned number, attempted = 0;
  while (1) {
    if (attempted) {
      free(line);
      printf("%s", "That is not a valid number. Please enter a number: ");
    } else
      attempted = 1;
    line = get_line();
    if (!(line && *line)) return default_value;
    number = strtoul(line, &end, 10);
    if (*end) continue;
    free(line);
    return number;
  }
}
 
int get_signed_number (int default_value) {
  char * line = NULL;
  char * end;
  int number, attempted = 0;
  while (1) {
    if (attempted) {
      free(line);
      printf("%s", "That is not a valid number. Please enter a number: ");
    } else
      attempted = 1;
    line = get_line();
    if (!(line && *line)) return default_value;
    number = strtol(line, &end, 10);
    if (*end) continue;
    free(line);
    return number;
  }
}
 
struct node * get_battle_set (void) {
  int x = 0;
  struct node * result = get_battle(&x);
  struct node * current = result;
  struct node * new_node;
  while (1) {
    x = result != NULL;
    new_node = get_battle(&x);
    if (x) {
      if (!new_node) break;
      if (result)
        current = current -> next = new_node;
      else
        result = current = new_node;
    } else
      delete_last_battle(&result, &current);
  }
  return result;
}
 
struct node * get_battle (int * status) {
  if (*status)
    printf("%s", "Enter coordinates [<empty input>: stop, delete: delete last]: ");
  else
    printf("%s", "Enter coordinates [empty input to stop]: ");
  char * coords = get_line();
  if (!(coords && *coords)) {
    free(coords);
    *status = 1;
    return NULL;
  }
  if (!strcmp(coords, "delete")) {
    free(coords);
    if (!*status) {
      puts("Nothing to delete.");
      return get_battle(status);
    }
    *status = 0;
    return NULL;
  }
  struct battle battle = {.location = coords};
  printf("Enter the HP of cell %s [default: %u]: ", coords, DEFAULT_HP);
  battle.HP = get_number(DEFAULT_HP);
  battle.player1 = get_player_info("Attacker");
  battle.player2 = get_player_info("Defender");
  puts("");
  struct node * new = malloc(sizeof(struct node));
  new -> battle = battle;
  new -> next = NULL;
  *status = 1;
  return new;
}
 
void delete_last_battle (struct node ** list, struct node ** pointer) {
  if (!*list) return; // you're drunk, go home
  if (*list == *pointer) {
    free(*list);
    *list = *pointer = NULL;
    return;
  }
  struct node * previous;
  for (previous = *list; previous -> next != *pointer; previous = previous -> next);
  *pointer = previous;
  free(previous -> next);
  previous -> next = NULL;
}
 
struct player get_player_info (const char * player) {
  printf("%s data:\n", player);
  printf("%s", "Name: ");
  char * name = get_line();
  while (!(name && *name)) {
    free(name);
    printf("%s", "The name must not be blank. Name: ");
    name = get_line();
  }
  struct player result = {.name = name};
  printf("Base power [default: %u]: ", DEFAULT_POWER);
  result.power = get_number(DEFAULT_POWER);
  printf("Bonus %% [default: %d]: ", DEFAULT_BONUS);
  result.bonus = get_signed_number(DEFAULT_BONUS);
  printf("Min. range %% [default: %u]: ", DEFAULT_MIN_RANGE);
  result.min = get_number(DEFAULT_MIN_RANGE);
  printf("Max. range %% [default: %u]: ", DEFAULT_MAX_RANGE);
  result.max = get_number(DEFAULT_MAX_RANGE);
  printf("Mastery %% [default: %u]: ", DEFAULT_MASTERY);
  result.mastery = get_number(DEFAULT_MASTERY);
  return result;
}
 
void make_battles (FILE * output, struct node * battles) {
  make_battle(output, battles -> battle);
  for (battles = battles -> next; battles; battles = battles -> next) {
    separator(output);
    make_battle(output, battles -> battle);
  }
}
 
void make_battle (FILE * output, struct battle battle) {
  header(output, battle.location);
  struct range range1 = effective_range(battle.player1), range2 = effective_range(battle.player2);
  player_info(output, battle.player1, range1);
  player_separator(output);
  player_info(output, battle.player2, range2);
  battle_header(output, battle.player1.name, battle.player2.name);
  unsigned char attacks = 0, wins = 0;
  while ((attacks < ATTACK_ROUNDS) && (wins < battle.HP)) {
    unsigned roll1 = roll(range1.max - range1.min), roll2 = roll(range2.max - range2.min);
    unsigned char result = ((range1.min + roll1) > (range2.min + roll2)) ? 1 : ((range2.min + roll2) > (range1.min + roll1)) ? 2 : 0;
    attacks ++;
    if (result == 1) wins ++;
    attack(output, range1.min, roll1, range2.min, roll2, result);
  }
  battle_footer(output);
  footer(output, battle.player1.name, battle.player2.name, battle.location, wins, battle.HP - wins);
}
 
struct range effective_range (struct player player) {
  unsigned long long base = player.power * (100 + player.bonus);
  unsigned long long min = base * player.min, max = base * player.max;
  return (struct range) {.min = (min * 100 + (max - min) * player.mastery) / 1000000, .max = max / 10000};
}
 
unsigned roll (unsigned max_roll) {
  if (!max_roll) return 0;
  unsigned limit = max_roll + 1;
  unsigned result = -1;
  while ((result + limit - (result % limit)) < result) result = get_RNG();
  return result % limit;
}
 
 
// Output routines begin here. If the output format is to be changed, everything that should be changed starts here.
 
void separator (FILE * output) {
  fputs("\n[hr]\n\n", output);
}
 
void header (FILE * output, const char * coordinate) {
  fprintf(output, "[b]Battle at %s[/b]\n", coordinate);
  fputs("[spoiler][center][b]", output);
}
 
void player_info (FILE * output, struct player player, struct range range) {
  fprintf(output, "%s's Power: %u\n", player.name, player.power);
  fprintf(output, "%s's Bonuses: %+d%%\n", player.name, player.bonus);
  fprintf(output, "%s's Mastery: %u%%\n", player.name, player.mastery);
  unsigned range_integer = player.min * 100 + (player.max - player.min) * player.mastery;
  unsigned char range_fraction = range_integer % 100;
  range_integer /= 100;
  if (range_fraction)
    fprintf(output, "%s's Range: %u.%02hhu%% - %u%%\n", player.name, range_integer, range_fraction, player.max);
  else
    fprintf(output, "%s's Range: %u%% - %u%%\n", player.name, range_integer, player.max);
  fprintf(output, "%s's Effective Range: %u - %u\n", player.name, range.min, range.max);
}
 
void player_separator (FILE * output) {
  fputs("\nVS\n\n", output);
}
 
void battle_header (FILE * output, const char * attacker, const char * defender) {
  fputs("\n[table=width: 400px; margin: auto]\n[tr]\n", output);
  fprintf(output, "[td]%s[/td]\n", attacker);
  fprintf(output, "[td]%s[/td]\n", defender);
  fputs("[td]Result[/td]\n", output);
  fputs("[/tr]\n", output);
}
 
void attack (FILE * output, unsigned min1, unsigned roll1, unsigned min2, unsigned roll2, unsigned result) {
  if (result > 2) return;
  fputs("[tr]\n", output);
  fprintf(output, "[td]%u + %u = %u[/td]\n", min1, roll1, min1 + roll1);
  fprintf(output, "[td]%u + %u = %u[/td]\n", min2, roll2, min2 + roll2);
  fprintf(output, "[td][color=%s]%s[/color][/td]\n", 
          result[(const char * []) {"#000000", "#008000", "#ff0000"}],
          result[(const char * []) {"Draw", "Win", "Lose"}]
         );
  fputs("[/tr]\n", output);
}
 
void battle_footer (FILE * output) {
  fputs("[/table]\n[/b][/center][/spoiler]\n\n", output);
}
 
void footer (FILE * output, const char * attacker, const char * defender, const char * coordinate, unsigned wins, unsigned remaining) {
  fputs("[b]", output);
  if (wins) {
    fprintf(output, "[color=#008000]%s wins %u %s.[/color]\n\n", attacker, wins, (wins == 1) ? "fight" : "fights");
    fprintf(output, "%s loses %uHP.\n", defender, wins);
  } else
    fprintf(output, "[color=#ff0000]%s wins no fights.[/color]\n\n", attacker);
  if (remaining)
    fprintf(output, "%s has %uHP remaining.", coordinate, remaining);
  else
    fprintf(output, "%s falls to %s.", coordinate, attacker);
  fputs("[/b]\n", output);
}

Comments