Parcourir la source

Option for optional rune sets. Option to enable broken sets. Optimizer options are now a structure.

Iñigo Valentin il y a 4 ans
Parent
commit
590c8fc6ef

+ 6 - 0
src/RuneOptimizer/error/error.h

@@ -150,6 +150,12 @@
  */
  */
 #define ERROR_INPUT_OPTIMIZE_NO_TEAMS -119
 #define ERROR_INPUT_OPTIMIZE_NO_TEAMS -119
 
 
+/**
+ * Error indicating that no values have been passed to the exclude units
+ * parameter for the optimizer command.
+ */
+#define ERROR_INPUT_OPTIMIZE_NO_UNITS -119
+
 /**
 /**
  * Error indicating that not enough rune sets have been passed to the sets
  * Error indicating that not enough rune sets have been passed to the sets
  * option for the optimizer command.
  * option for the optimizer command.

+ 3 - 1
src/RuneOptimizer/help/help.c

@@ -113,7 +113,9 @@ void cmd_help(){
     printf("                                     'hpflat', 'atkflat', 'defflat', 'spd', 'crr', 'crd',\n");
     printf("                                     'hpflat', 'atkflat', 'defflat', 'spd', 'crr', 'crd',\n");
     printf("                                     'res' and 'acc'. Values must be comma-separated, and up\n");
     printf("                                     'res' and 'acc'. Values must be comma-separated, and up\n");
     printf("                                     to 12 can be included.\n");
     printf("                                     to 12 can be included.\n");
-    printf("        -e | --sets <S1>,<S2>...     Rune sets that than can be considered during the optimization.\n");
+    printf("        -e | --sets <S1>,<S2>...     Rune sets that than the unit being optimized must have.\n");
+    printf("        -i | --opt-sets <S1>,<S2>... Other rune sets that can be used to complement the required ones.\n");
+    printf("        -k | --broken                Allow the unit to have broken sets, other than the required ones.\n");
     printf("        -o | --storage               Use only runes not assigned to units.\n");
     printf("        -o | --storage               Use only runes not assigned to units.\n");
     printf("        -x | --no-teams <T1>,<T2>... Exclude runes assigned to units in selected teams.\n");
     printf("        -x | --no-teams <T1>,<T2>... Exclude runes assigned to units in selected teams.\n");
     printf("                                     Team IDs can be supplied, comma-separated.\n");
     printf("                                     Team IDs can be supplied, comma-separated.\n");

+ 105 - 84
src/RuneOptimizer/optimize/optimize.c

@@ -64,7 +64,54 @@ unsigned short optimize_calculate_dmg(
       ((float)atk * ((100 - crr_capped) / 100)) +              // Non-crit
       ((float)atk * ((100 - crr_capped) / 100)) +              // Non-crit
       ((float)atk * (crr_capped / 100) * ((float)crd / 100))   // Crit
       ((float)atk * (crr_capped / 100) * ((float)crd / 100))   // Crit
     );
     );
-    return dmg;
+    return(dmg);
+}
+
+unsigned char optimize_contains_broken(Rune_Set_Count *set_count){
+    if (set_count->energy        % 2 != 0) return(TRUE);
+    if (set_count->guard         % 2 != 0) return(TRUE);
+    if (set_count->swift         % 4 != 0) return(TRUE);
+    if (set_count->blade         % 2 != 0) return(TRUE);
+    if (set_count->rage          % 4 != 0) return(TRUE);
+    if (set_count->focus         % 2 != 0) return(TRUE);
+    if (set_count->endure        % 2 != 0) return(TRUE);
+    if (set_count->fatal         % 4 != 0) return(TRUE);
+    if (set_count->despair       % 4 != 0) return(TRUE);
+    if (set_count->vampire       % 4 != 0) return(TRUE);
+    if (set_count->violent       % 4 != 0) return(TRUE);
+    if (set_count->nemesis       % 2 != 0) return(TRUE);
+    if (set_count->will          % 2 != 0) return(TRUE);
+    if (set_count->shield        % 2 != 0) return(TRUE);
+    if (set_count->revenge       % 2 != 0) return(TRUE);
+    if (set_count->destroy       % 2 != 0) return(TRUE);
+    if (set_count->fight         % 2 != 0) return(TRUE);
+    if (set_count->determination % 2 != 0) return(TRUE);
+    if (set_count->enhance       % 2 != 0) return(TRUE);
+    if (set_count->accuracy      % 2 != 0) return(TRUE);
+    if (set_count->tolerance     % 2 != 0) return(TRUE);
+    return(FALSE);
+}
+
+void optimize_set_default_options(Optimizer_Options *options){
+    strcpy(options->id, "");
+    options->level = 0;
+    options->sets[0] = 0;
+    options->sets[1] = 0;
+    options->sets[2] = 0;
+    options->full_set = FALSE;
+    for (int i = 0; i < 13; i ++){
+        options->stats[i] = FALSE;
+    }
+    for (int i = 0; i < 25; i ++){
+        options->optional_sets[i] = FALSE;
+    }
+    Stats min = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
+    options->min_stats = &min;
+    options->gui = FALSE;
+    options->storage = FALSE;
+    options->total_excluded_teams = 0,
+    options->total_excluded_units = 0;
+    options->broken_sets = FALSE;
 }
 }
 
 
 int cmd_optimize(int argc, char *argv[]){
 int cmd_optimize(int argc, char *argv[]){
@@ -80,79 +127,46 @@ int cmd_optimize(int argc, char *argv[]){
         strcpy(args[i], argv[i]);
         strcpy(args[i], argv[i]);
     }
     }
 
 
-    // Initialize values for the data to be read form arguments.
-    char id[64];
-    char requested_level = 0;
-    char gui = 0;
-    char storage = 0;
-    int sets[3] = {0, 0, 0};
-    int requested_stats[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
-    struct Stats min_stats;
-    // Unsigned, so I cant use -1 as placeholder to check if its modified from
-    // arguments. I'm using just 1 (I don't think anybody will input values of
-    // 1), and later, if they are still 1, they are changed to 0.
-    min_stats.hp = 1;
-    min_stats.atk = 1;
-    min_stats.def = 1;
-    min_stats.spd = 1;
-    min_stats.crr = 1;
-    min_stats.crd = 1;
-    min_stats.res = 1;
-    min_stats.acc = 1;
-    min_stats.ehp = 1;
-    min_stats.dmg = 1;
-
-    char excluded_teams[128][8];
-    char excluded_units[128][8];
-    for (int i = 0; i < 128; i ++){
-        for (int j = 0; j < 8; j ++){
-            excluded_teams[i][j] = '\0';
-            excluded_units[i][j] = '\0';
-            //(char volatile) excluded_units[i][j] = excluded_teams[i][j];
-        }
-    }
-    // Parse arguments from the command line.
-    status = optimize_parse_arguments(
-      argc, args, id, &requested_level,
-      sets, requested_stats, &min_stats, &gui,
-      &storage, excluded_teams, excluded_units
-    );
+    // Read command line arguments.
+    Optimizer_Options options;
+    optimize_set_default_options(&options);
+    status = optimize_parse_arguments(argc, args, &options);
     if (SUCCESS != status) return(status);
     if (SUCCESS != status) return(status);
-
     // Get unit data from database
     // Get unit data from database
-    struct Unit unit;
-    status = optimize_fetch_unit(id, &unit);
+    Unit unit;
+    status = optimize_fetch_unit(options.id, &unit);
     if (SUCCESS != status) return(status);
     if (SUCCESS != status) return(status);
 
 
     // Min stats that have the value 1 get overriden by the current stats.
     // Min stats that have the value 1 get overriden by the current stats.
-    if (min_stats.hp == 1) min_stats.hp = unit.current_hp;
-    if (min_stats.atk == 1) min_stats.atk = unit.current_atk;
-    if (min_stats.def == 1) min_stats.def = unit.current_def;
-    if (min_stats.spd == 1) min_stats.spd = unit.current_spd;
-    if (min_stats.crr == 1) min_stats.crr = unit.current_crr;
-    if (min_stats.crd == 1) min_stats.crd = unit.current_crd;
-    if (min_stats.res == 1) min_stats.res = unit.current_res;
-    if (min_stats.acc == 1) min_stats.acc = unit.current_acc;
-    if (min_stats.ehp == 1) min_stats.ehp = 0;
-    if (min_stats.dmg == 1) min_stats.dmg = 0;
+    if (options.min_stats->hp == 1) options.min_stats->hp = unit.current_hp;
+    if (options.min_stats->atk == 1) options.min_stats->atk = unit.current_atk;
+    if (options.min_stats->def == 1) options.min_stats->def = unit.current_def;
+    if (options.min_stats->spd == 1) options.min_stats->spd = unit.current_spd;
+    if (options.min_stats->crr == 1) options.min_stats->crr = unit.current_crr;
+    if (options.min_stats->crd == 1) options.min_stats->crd = unit.current_crd;
+    if (options.min_stats->res == 1) options.min_stats->res = unit.current_res;
+    if (options.min_stats->acc == 1) options.min_stats->acc = unit.current_acc;
+    if (options.min_stats->ehp == 1) options.min_stats->ehp = 0;
+    if (options.min_stats->dmg == 1) options.min_stats->dmg = 0;
 
 
     // Calculate required rune count.
     // Calculate required rune count.
-    struct Rune_Set_Count requested_set_count;
-    if (6 != optimize_count_runes_for_sets(sets, &requested_set_count)){
+    Rune_Set_Count requested_set_count;
+    unsigned char total_runes_in_requested_sets =
+      optimize_count_runes_for_sets(options.sets, &requested_set_count);
+    if (6 < total_runes_in_requested_sets){
         fprintf(stderr, "Invalid rune set combination.\n");
         fprintf(stderr, "Invalid rune set combination.\n");
         return ERROR_INPUT_OPTIMIZE_INCOMPLETE_SETS;
         return ERROR_INPUT_OPTIMIZE_INCOMPLETE_SETS;
     }
     }
+    else if (6 == total_runes_in_requested_sets){
+        options.full_set = TRUE;
+    }
     // Create queries for the database.
     // Create queries for the database.
     char query_even[2500];
     char query_even[2500];
     char query_odd[2500];
     char query_odd[2500];
-    optimize_query_for_even_slots(
-      requested_level, sets, requested_stats, excluded_teams,
-      excluded_units, storage, id, query_even
-    );
-    optimize_query_for_odd_slots(
-      requested_level, sets, excluded_teams,
-      excluded_units, storage, id, query_odd
-    );
+    optimize_query_for_even_slots(&options, query_even);
+    optimize_query_for_odd_slots(&options, query_odd);
+    //printf("\nQ ODD:\n\n%s\n\n\n\n", query_odd);
+    //printf("\nQ EVEN:\n\n%s\n\n\n\n", query_even);
 
 
     // Get runes from database.
     // Get runes from database.
     struct Rune runes[7][600];
     struct Rune runes[7][600];
@@ -161,7 +175,7 @@ int cmd_optimize(int argc, char *argv[]){
     // If any slot doesn't have matching runes, we can stop now.
     // If any slot doesn't have matching runes, we can stop now.
     for (int i = 1; i < 7; i ++){
     for (int i = 1; i < 7; i ++){
         if (rune_count[i] == 0){
         if (rune_count[i] == 0){
-            if (gui != 1){
+            if (options.gui == FALSE){
                 printf("No availbale runes for slot %d\n", i);
                 printf("No availbale runes for slot %d\n", i);
             }
             }
             else{
             else{
@@ -172,7 +186,7 @@ int cmd_optimize(int argc, char *argv[]){
     }
     }
 
 
     // Precalculate how many combinations will have to be tested.
     // Precalculate how many combinations will have to be tested.
-    unsigned long max_combinations =
+    unsigned long long max_combinations =
       rune_count[1] *
       rune_count[1] *
       rune_count[2] *
       rune_count[2] *
       rune_count[3] *
       rune_count[3] *
@@ -181,19 +195,18 @@ int cmd_optimize(int argc, char *argv[]){
       rune_count[6];
       rune_count[6];
 
 
     // Print summary with data collected so far. Skip for GUI.
     // Print summary with data collected so far. Skip for GUI.
-    if (gui != 1){
+    if (options.gui == FALSE){
         optimize_print_summary(
         optimize_print_summary(
-          &unit, sets, requested_stats, &min_stats,
-          requested_level, rune_count, max_combinations
+          &unit, &options, rune_count, max_combinations
         );
         );
     }
     }
 
 
     // Now its time to loop all 6 'reels' of runes and try to match combos
     // Now its time to loop all 6 'reels' of runes and try to match combos
-    if (gui != 1){
+    if (options.gui == FALSE){
         printf("\n\n__Optimization progress___________________________\n");
         printf("\n\n__Optimization progress___________________________\n");
     }
     }
     else{
     else{
-        printf("Total combinations: %d\n", max_combinations);
+        printf("Total combinations: %llu\n", max_combinations);
         fflush(stdout);
         fflush(stdout);
     }
     }
 
 
@@ -202,7 +215,7 @@ int cmd_optimize(int argc, char *argv[]){
     unsigned long tested_combinations = 0;
     unsigned long tested_combinations = 0;
     unsigned long valid_sets = 0;
     unsigned long valid_sets = 0;
     unsigned long result_count = 0;
     unsigned long result_count = 0;
-    struct Rune_Set_Count set_count;
+    Rune_Set_Count set_count;
     Result results[MAX_RESULTS];
     Result results[MAX_RESULTS];
     while(
     while(
         index[1] < rune_count[1] &&
         index[1] < rune_count[1] &&
@@ -219,7 +232,7 @@ int cmd_optimize(int argc, char *argv[]){
           (unsigned long)(max_combinations / 50)
           (unsigned long)(max_combinations / 50)
           == 0
           == 0
         ){
         ){
-            if (gui != 1){
+            if (options.gui == FALSE){
                 printf("#");
                 printf("#");
             }
             }
             else{
             else{
@@ -282,7 +295,15 @@ int cmd_optimize(int argc, char *argv[]){
         }
         }
 
 
         // Compare with requested sets
         // Compare with requested sets
+        // TODO: Here, validate broken sets too
+        if (options.broken_sets == FALSE){
+
+        }
         if (
         if (
+            (
+              options.broken_sets == TRUE ||
+              optimize_contains_broken(&set_count) == FALSE
+            ) &&
             set_count.energy >= requested_set_count.energy &&
             set_count.energy >= requested_set_count.energy &&
             set_count.guard >= requested_set_count.guard &&
             set_count.guard >= requested_set_count.guard &&
             set_count.swift >= requested_set_count.swift &&
             set_count.swift >= requested_set_count.swift &&
@@ -374,16 +395,16 @@ int cmd_optimize(int argc, char *argv[]){
 
 
             // Compare with minimum requeriments
             // Compare with minimum requeriments
             if (
             if (
-                stats.hp >= min_stats.hp &&
-                stats.atk >= min_stats.atk &&
-                stats.def >= min_stats.def &&
-                stats.spd >= min_stats.spd &&
-                stats.crr >= min_stats.crr &&
-                stats.crd >= min_stats.crd &&
-                stats.res >= min_stats.res &&
-                stats.acc >= min_stats.acc &&
-                stats.ehp >= min_stats.ehp &&
-                stats.dmg >= min_stats.dmg
+                stats.hp >= options.min_stats->hp &&
+                stats.atk >= options.min_stats->atk &&
+                stats.def >= options.min_stats->def &&
+                stats.spd >= options.min_stats->spd &&
+                stats.crr >= options.min_stats->crr &&
+                stats.crd >= options.min_stats->crd &&
+                stats.res >= options.min_stats->res &&
+                stats.acc >= options.min_stats->acc &&
+                stats.ehp >= options.min_stats->ehp &&
+                stats.dmg >= options.min_stats->dmg
             ){
             ){
                 // This is a valid sets and all stats are above the minimum.
                 // This is a valid sets and all stats are above the minimum.
                 // Create a result with rune indexes, stats, and rating.
                 // Create a result with rune indexes, stats, and rating.
@@ -470,18 +491,18 @@ int cmd_optimize(int argc, char *argv[]){
         //    break;
         //    break;
         //}
         //}
     }
     }
-    if (gui != 1){
+    if (options.gui == FALSE){
         printf("\n");
         printf("\n");
     }
     }
     if (result_count > 0){
     if (result_count > 0){
         // Yay! Some combinations matched te criteria.
         // Yay! Some combinations matched te criteria.
-        if (gui != 1){
+        if (options.gui == FALSE){
             printf("\n\n%d results found\n", result_count);
             printf("\n\n%d results found\n", result_count);
         }
         }
 
 
         // Sort results
         // Sort results
         optimize_sort_results(results, result_count);
         optimize_sort_results(results, result_count);
-        if (gui != 1){
+        if (options.gui == FALSE){
             // Preview the best option
             // Preview the best option
             optimize_print_result(&unit, &results[0]);
             optimize_print_result(&unit, &results[0]);
         }
         }
@@ -491,7 +512,7 @@ int cmd_optimize(int argc, char *argv[]){
         }
         }
     }
     }
     else{
     else{
-        if (gui != 1){
+        if (options.gui == FALSE){
             printf("No results found\n");
             printf("No results found\n");
         }
         }
         else{
         else{

+ 65 - 41
src/RuneOptimizer/optimize/optimize.h

@@ -25,6 +25,12 @@
  */
  */
 #define MAX_RESULTS 5000
 #define MAX_RESULTS 5000
 
 
+/**
+ * Hard limit on the number of teams or units that can be excluded from
+ * optimization.
+ */
+#define MAX_EXCLUSIONS 128
+
 /**
 /**
  * Structure representing a unit. Represents an entity from the table 'unit'.
  * Structure representing a unit. Represents an entity from the table 'unit'.
  */
  */
@@ -90,6 +96,16 @@ typedef struct Stats {
     unsigned short dmg;
     unsigned short dmg;
 } Stats;
 } Stats;
 
 
+typedef struct Stat_Ponderation {
+    float hp;
+    float atk;
+    float def;
+    float spd;
+    float crr;
+    float crd;
+    float acc;
+    float res;
+} Stat_Ponderation;
 
 
 /**
 /**
  * A result, composed of 6 runes.
  * A result, composed of 6 runes.
@@ -100,6 +116,29 @@ typedef struct Result {
     struct Stats stats;
     struct Stats stats;
 } Result;
 } Result;
 
 
+/**
+ * Options for the optimizer.
+ */
+typedef struct Optimizer_Options {
+    unsigned char id[64];
+    unsigned char level;
+    unsigned char sets[3];
+    unsigned char full_set;
+    unsigned char optional_sets[25];
+    unsigned char stats[13];
+    Stats *min_stats;
+    unsigned char gui;
+    unsigned char storage;
+    unsigned char excluded_teams[MAX_EXCLUSIONS][8];
+    unsigned char total_excluded_teams;
+    unsigned char excluded_units[MAX_EXCLUSIONS][16];
+    unsigned char total_excluded_units;
+    unsigned char broken_sets;
+    // TODO: unimplemented options;
+    Stat_Ponderation ponderation;
+
+} Optimizer_Options;
+
 /**
 /**
  * Names for run stats.
  * Names for run stats.
  * 
  * 
@@ -131,8 +170,8 @@ const char set_names[][25] = {
  * number of runes of each type needed to form the sets.
  * number of runes of each type needed to form the sets.
  * @return Number of requested runes. In ost cases, it should be 6.
  * @return Number of requested runes. In ost cases, it should be 6.
  */
  */
-char optimize_count_runes_for_sets(
-  int sets[3], Rune_Set_Count *requested_set_count
+unsigned char optimize_count_runes_for_sets(
+  unsigned char sets[3], Rune_Set_Count *requested_set_count
 );
 );
 
 
 /**
 /**
@@ -169,23 +208,13 @@ int optimize_get_runes(
  *
  *
  * @param[in] argc Number of argument passed to the optimizer command.
  * @param[in] argc Number of argument passed to the optimizer command.
  * @param[in] argv Arguments passed to the optimizer.
  * @param[in] argv Arguments passed to the optimizer.
- * @param[out] id Unit id or name.
- * @param[out] level Level to treat the runes as.
- * @param[out] sets List of requested sets.
- * @param[out] stats List of requested stats.
- * @param[out] min_stats Minimum stats for the optimization.
- * @param[out] storage 1 to use only runes from storage.
- * @param[in] excluded_teams List of teams whose unit's runes to exclude.
- * @param[in] excluded_units List of units whose runes to exclude.
- * @param[out] gui 1 to format the output for the GUI
+ * @param[out] options Options to set.
  * @return SUCCESS or an error code. See
  * @return SUCCESS or an error code. See
  * <a href="file:../error/error.h">../error/error.h</a> for a list of error
  * <a href="file:../error/error.h">../error/error.h</a> for a list of error
  * codes.
  * codes.
  */
  */
 int optimize_parse_arguments(
 int optimize_parse_arguments(
-  int argc, char argv[][128], char id[64], char *level,
-  int *sets, int *stats, struct Stats *min_stats, char *gui,
-  char *storage, char excluded_teams[128][8], char excluded_units[128][8]
+  int argc, char argv[][128], Optimizer_Options *options
 );
 );
 
 
 /**
 /**
@@ -194,16 +223,13 @@ int optimize_parse_arguments(
  * It is 17 lines high and un to 80 characters wide.
  * It is 17 lines high and un to 80 characters wide.
  *
  *
  * @param[in] unit Unit data.
  * @param[in] unit Unit data.
- * @param[in] sets Requested rune sets.
- * @param[in] stats Requested stats for even slots.
- * @param[in] min_stats Requested minimum stats sets.
- * @param[in] level Level to treat runes as.
+ * @param[in] options Options passed to the optimizer.
  * @param[in] rune_count Number of runes for each slot.
  * @param[in] rune_count Number of runes for each slot.
  * @param[in] max_combinations Total combinations to test during optimization.
  * @param[in] max_combinations Total combinations to test during optimization.
  */
  */
 void optimize_print_summary(
 void optimize_print_summary(
-  struct Unit *unit, int* sets, int* stats, struct Stats *min_stats,
-  char level, int rune_count[7], unsigned long max_combinations
+  struct Unit *unit, Optimizer_Options *options,
+  unsigned int rune_count[7], unsigned long max_combinations
 );
 );
 
 
 /**
 /**
@@ -231,37 +257,20 @@ int optimize_print_gui(Result results[5000], int result_count);
 /**
 /**
  * Generates the query to get the runes from the databas for even slots.
  * Generates the query to get the runes from the databas for even slots.
  *
  *
- * @param[in] level Level to consider runes at.
- * @param[in] sets Requested sets.
- * @param[in] stats Requested sets for even slots.
- * @param[in] excluded_teams List of teams whose unit's runes to exclude.
- * @param[in] excluded_units List of units whose runes to exclude.
- * @param[in] storage 1 to select only unassigned runes.
- * @param[in] unit_id ID of the unit being optimized
+ * @param[in] options Options passed to the optimizer.
  * @param[out] query String with the query.
  * @param[out] query String with the query.
  */
  */
 void optimize_query_for_even_slots(
 void optimize_query_for_even_slots(
-  char level, int* sets, int* stats,
-  char excluded_teams[128][8], char excluded_units[128][8],
-  char storage, char unit_id[64], char query[2500]
+    Optimizer_Options *options, char query[2500]
 );
 );
 
 
 /**
 /**
  * Generates the query to get the runes from the databas for odd slots.
  * Generates the query to get the runes from the databas for odd slots.
  *
  *
- * @param[in] level Level to consider runes at.
- * @param[in] sets Requested sets.
- * @param[in] excluded_teams List of teams whose unit's runes to exclude.
- * @param[in] excluded_units List of units whose runes to exclude.
- * @param[in] storage 1 to select only unassigned runes.
- * @param[in] unit_id ID of the unit being optimized
+ * @param[in] options Options passed to the optimizer.
  * @param[out] query String with the query.
  * @param[out] query String with the query.
  */
  */
-void optimize_query_for_odd_slots(
-  char level, int* sets,
-  char excluded_teams[128][8], char excluded_units[128][8],
-  char storage, char unit_id[64], char query[2500]
-);
+void optimize_query_for_odd_slots(Optimizer_Options *options, char query[2500]);
 
 
 /**
 /**
  * Sorts the result array.
  * Sorts the result array.
@@ -294,6 +303,21 @@ unsigned short optimize_calculate_dmg(
   unsigned short atk, unsigned short crr, unsigned short crd
   unsigned short atk, unsigned short crr, unsigned short crd
 );
 );
 
 
+/**
+ * Sets the default options for the optimizer.
+ *
+ * @param[out] options Options to set defaults to.
+ */
+void optimize_set_default_options(Optimizer_Options *options);
+
+/**
+ * Verifies if a given set of runes contains any broken set.
+ *
+ * @param set_count The set to check.
+ * @return TRUE if the set has any broken sets, false otherwise.
+ */
+unsigned char optimize_contains_broken(Rune_Set_Count *set_count);
+
 /**
 /**
  * Starts the optimization process.
  * Starts the optimization process.
  *
  *

+ 2 - 2
src/RuneOptimizer/optimize/optimize_count_runes_for_sets.c

@@ -20,8 +20,8 @@
  * Implementation of optimize_count_runes_for_sets.
  * Implementation of optimize_count_runes_for_sets.
  */
  */
 
 
-char optimize_count_runes_for_sets(
-  int sets[3], Rune_Set_Count *requested_set_count
+unsigned char optimize_count_runes_for_sets(
+  unsigned char sets[3], Rune_Set_Count *requested_set_count
 ){
 ){
     requested_set_count->energy = 0;
     requested_set_count->energy = 0;
     requested_set_count->guard = 0;
     requested_set_count->guard = 0;

+ 172 - 69
src/RuneOptimizer/optimize/optimize_parse_arguments.c

@@ -21,37 +21,30 @@
  */
  */
 
 
 int optimize_parse_arguments(
 int optimize_parse_arguments(
-  int argc, char argv[][128], char id[64], char *level,
-  int *sets, int *stats, struct Stats *min_stats, char *gui,
-  char *storage, char excluded_teams[128][8], char excluded_units[128][8]
+  int argc, char argv[][128], Optimizer_Options *options
 ){
 ){
     // Get unit identifier
     // Get unit identifier
     if (argc < 1){
     if (argc < 1){
         fprintf(stderr, "No unit specified for optimization\n");
         fprintf(stderr, "No unit specified for optimization\n");
         return ERROR_INPUT_OPTIMIZE_NO_UNIT;
         return ERROR_INPUT_OPTIMIZE_NO_UNIT;
     }
     }
-    //strncpy(id, argv[0], 64);
     if (strlen(argv[0]) > 63){
     if (strlen(argv[0]) > 63){
-        strncpy(id, argv[0], 63);
-        id[63] = '\0';
+        strncpy(options->id, argv[0], 63);
+        options->id[63] = '\0';
     }
     }
     else{
     else{
-        strcpy(id, argv[0]);
+        strcpy(options->id, argv[0]);
     }
     }
 
 
-    // Assign default values for non-value arguments
-    *storage = 0;
-    *gui = 0;
-
     // Loop command line arguments
     // Loop command line arguments
     for (int i = 1; i < argc; i ++){
     for (int i = 1; i < argc; i ++){
 
 
         // Rune level arguments
         // Rune level arguments
         if (strcmp("--level", argv[i]) == 0 || strcmp("-l", argv[i]) == 0){
         if (strcmp("--level", argv[i]) == 0 || strcmp("-l", argv[i]) == 0){
             if (i < argc - 1){
             if (i < argc - 1){
-                if (strcmp("current", argv[i + 1]) == 0) *level = 0;
-                else if (strcmp("12", argv[i + 1]) == 0) *level = 12;
-                else if (strcmp("15", argv[i + 1]) == 0) *level = 15;
+                if (strcmp("current", argv[i + 1]) == 0) options->level = 0;
+                else if (strcmp("12", argv[i + 1]) == 0) options->level = 12;
+                else if (strcmp("15", argv[i + 1]) == 0) options->level = 15;
                 else{
                 else{
                     fprintf(
                     fprintf(
                       stderr,
                       stderr,
@@ -79,7 +72,6 @@ int optimize_parse_arguments(
         ){
         ){
             if (i < argc - 1){
             if (i < argc - 1){
                 // Separate string by commas
                 // Separate string by commas
-                int j = 0;
                 // Returns first token
                 // Returns first token
                 char opt_list[128];
                 char opt_list[128];
                 strcpy(opt_list, argv[i + 1]);
                 strcpy(opt_list, argv[i + 1]);
@@ -88,9 +80,12 @@ int optimize_parse_arguments(
                 // Keep printing tokens while one of the
                 // Keep printing tokens while one of the
                 // delimiters present in the list, or until its complete
                 // delimiters present in the list, or until its complete
                 while (token != NULL){
                 while (token != NULL){
-                    strcpy(excluded_teams[j], token);
+                    strcpy(
+                      options->excluded_teams[options->total_excluded_teams],
+                      token
+                    );
+                    options->total_excluded_teams ++;
                     token = strtok(NULL, ",");
                     token = strtok(NULL, ",");
-                    j ++;
                 }
                 }
                 i ++; // Advance one position in argument reading
                 i ++; // Advance one position in argument reading
             }
             }
@@ -110,7 +105,6 @@ int optimize_parse_arguments(
         ){
         ){
             if (i < argc - 1){
             if (i < argc - 1){
                 // Separate string by commas
                 // Separate string by commas
-                int j = 0;
                 // Returns first token
                 // Returns first token
                 char opt_list[128];
                 char opt_list[128];
                 strcpy(opt_list, argv[i + 1]);
                 strcpy(opt_list, argv[i + 1]);
@@ -119,9 +113,12 @@ int optimize_parse_arguments(
                 // Keep printing tokens while one of the
                 // Keep printing tokens while one of the
                 // delimiters present in the list, or until its complete
                 // delimiters present in the list, or until its complete
                 while (token != NULL){
                 while (token != NULL){
-                    strcpy(excluded_units[j], token);
+                    strcpy(
+                      options->excluded_units[options->total_excluded_units],
+                      token
+                    );
+                    options->total_excluded_units ++;
                     token = strtok(NULL, ",");
                     token = strtok(NULL, ",");
-                    j ++;
                 }
                 }
                 i ++; // Advance one position in argument reading
                 i ++; // Advance one position in argument reading
             }
             }
@@ -131,11 +128,11 @@ int optimize_parse_arguments(
                   "Argument %s requires a comma-separated list of values.\n",
                   "Argument %s requires a comma-separated list of values.\n",
                   argv[i]
                   argv[i]
                 );
                 );
-                return ERROR_INPUT_OPTIMIZE_NO_SET;
+                return ERROR_INPUT_OPTIMIZE_NO_UNITS;
             }
             }
         }
         }
 
 
-        // Rune sets argument
+        // Mandatory rune sets argument
         else if (strcmp("--sets", argv[i]) == 0 || strcmp("-e", argv[i]) == 0){
         else if (strcmp("--sets", argv[i]) == 0 || strcmp("-e", argv[i]) == 0){
             if (i < argc - 1){
             if (i < argc - 1){
                 // Separate string by commas
                 // Separate string by commas
@@ -146,29 +143,120 @@ int optimize_parse_arguments(
                 // Keep printing tokens while one of the
                 // Keep printing tokens while one of the
                 // delimiters present in the list, or until its complete
                 // delimiters present in the list, or until its complete
                 while (token != NULL && j < 3){
                 while (token != NULL && j < 3){
-                    if (strcmp(token, "energy") == 0) sets[j] = ENERGY;
-                    else if (strcmp(token, "guard") == 0) sets[j] = GUARD;
-                    else if (strcmp(token, "swift") == 0) sets[j] = SWIFT;
-                    else if (strcmp(token, "blade") == 0) sets[j] = BLADE;
-                    else if (strcmp(token, "rage") == 0) sets[j] = RAGE;
-                    else if (strcmp(token, "focus") == 0) sets[j] = FOCUS;
-                    else if (strcmp(token, "endure") == 0) sets[j] = ENDURE;
-                    else if (strcmp(token, "fatal") == 0) sets[j] = FATAL;
-                    else if (strcmp(token, "despair") == 0) sets[j] = DESPAIR;
-                    else if (strcmp(token, "vampire") == 0) sets[j] = VAMPIRE;
-                    else if (strcmp(token, "violent") == 0) sets[j] = VIOLENT;
-                    else if (strcmp(token, "nemesis") == 0) sets[j] = NEMESIS;
-                    else if (strcmp(token, "will") == 0) sets[j] = WILL;
-                    else if (strcmp(token, "shield") == 0) sets[j] = SHIELD;
-                    else if (strcmp(token, "revenge") == 0) sets[j] = REVENGE;
-                    else if (strcmp(token, "destroy") == 0) sets[j] = DESTROY;
-                    else if (strcmp(token, "fight") == 0) sets[j] = FIGHT;
+                    if (strcmp(token, "energy") == 0)
+                        options->sets[j] = ENERGY;
+                    else if (strcmp(token, "guard") == 0)
+                        options->sets[j] = GUARD;
+                    else if (strcmp(token, "swift") == 0)
+                        options->sets[j] = SWIFT;
+                    else if (strcmp(token, "blade") == 0)
+                        options->sets[j] = BLADE;
+                    else if (strcmp(token, "rage") == 0)
+                        options->sets[j] = RAGE;
+                    else if (strcmp(token, "focus") == 0)
+                        options->sets[j] = FOCUS;
+                    else if (strcmp(token, "endure") == 0)
+                        options->sets[j] = ENDURE;
+                    else if (strcmp(token, "fatal") == 0)
+                        options->sets[j] = FATAL;
+                    else if (strcmp(token, "despair") == 0)
+                        options->sets[j] = DESPAIR;
+                    else if (strcmp(token, "vampire") == 0)
+                        options->sets[j] = VAMPIRE;
+                    else if (strcmp(token, "violent") == 0)
+                        options->sets[j] = VIOLENT;
+                    else if (strcmp(token, "nemesis") == 0)
+                        options->sets[j] = NEMESIS;
+                    else if (strcmp(token, "will") == 0)
+                        options->sets[j] = WILL;
+                    else if (strcmp(token, "shield") == 0)
+                        options->sets[j] = SHIELD;
+                    else if (strcmp(token, "revenge") == 0)
+                        options->sets[j] = REVENGE;
+                    else if (strcmp(token, "destroy") == 0)
+                        options->sets[j] = DESTROY;
+                    else if (strcmp(token, "fight") == 0)
+                        options->sets[j] = FIGHT;
                     else if (strcmp(token, "determination") == 0)
                     else if (strcmp(token, "determination") == 0)
-                        sets[j] = DETERMINATION;
-                    else if (strcmp(token, "enhance") == 0) sets[j] = ENHANCE;
-                    else if (strcmp(token, "accuracy") == 0) sets[j] = ACCURACY;
+                        options->sets[j] = DETERMINATION;
+                    else if (strcmp(token, "enhance") == 0)
+                        options->sets[j] = ENHANCE;
+                    else if (strcmp(token, "accuracy") == 0)
+                        options->sets[j] = ACCURACY;
                     else if (strcmp(token, "tolerance") == 0)
                     else if (strcmp(token, "tolerance") == 0)
-                        sets[j] = TOLERANCE;
+                        options->sets[j] = TOLERANCE;
+                    else{
+                        fprintf(stderr, "Unknown rune set %s.\n", token);
+                        return ERROR_INPUT_OPTIMIZE_INVALID_SET;
+                    }
+                    token = strtok(NULL, ",");
+                    j ++;
+                }
+                i ++; // Advance one position in argument reading
+            }
+            else{
+                fprintf(
+                  stderr,
+                  "Argument %s requires a comma-separated list of values.\n",
+                  argv[i]
+                );
+                return ERROR_INPUT_OPTIMIZE_NO_SET;
+            }
+        }
+
+        // Optional rune sets argument
+        else if (strcmp("--opt-sets", argv[i]) == 0 || strcmp("-i", argv[i]) == 0){
+            if (i < argc - 1){
+                // Separate string by commas
+                int j = 0;
+                // Returns first token
+                char *token = strtok(argv[i + 1], ",");
+
+                // Keep printing tokens while one of the
+                // delimiters present in the list, or until its complete
+                while (token != NULL){
+                    if (strcmp(token, "energy") == 0)
+                        options->optional_sets[ENERGY] = TRUE;
+                    else if (strcmp(token, "guard") == 0)
+                        options->optional_sets[GUARD] = TRUE;
+                    else if (strcmp(token, "swift") == 0)
+                        options->optional_sets[SWIFT] = TRUE;
+                    else if (strcmp(token, "blade") == 0)
+                        options->optional_sets[BLADE] = TRUE;
+                    else if (strcmp(token, "rage") == 0)
+                        options->optional_sets[RAGE] = TRUE;
+                    else if (strcmp(token, "focus") == 0)
+                        options->optional_sets[FOCUS] = TRUE;
+                    else if (strcmp(token, "endure") == 0)
+                        options->optional_sets[ENDURE] = TRUE;
+                    else if (strcmp(token, "fatal") == 0)
+                        options->optional_sets[FATAL] = TRUE;
+                    else if (strcmp(token, "despair") == 0)
+                        options->optional_sets[DESPAIR] = TRUE;
+                    else if (strcmp(token, "vampire") == 0)
+                        options->optional_sets[VAMPIRE] = TRUE;
+                    else if (strcmp(token, "violent") == 0)
+                        options->optional_sets[VIOLENT] = TRUE;
+                    else if (strcmp(token, "nemesis") == 0)
+                        options->optional_sets[NEMESIS] = TRUE;
+                    else if (strcmp(token, "will") == 0)
+                        options->optional_sets[WILL] = TRUE;
+                    else if (strcmp(token, "shield") == 0)
+                        options->optional_sets[SHIELD] = TRUE;
+                    else if (strcmp(token, "revenge") == 0)
+                        options->optional_sets[REVENGE] = TRUE;
+                    else if (strcmp(token, "destroy") == 0)
+                        options->optional_sets[DESTROY] = TRUE;
+                    else if (strcmp(token, "fight") == 0)
+                        options->optional_sets[FIGHT] = TRUE;
+                    else if (strcmp(token, "determination") == 0)
+                        options->optional_sets[DETERMINATION] = TRUE;
+                    else if (strcmp(token, "enhance") == 0)
+                        options->optional_sets[ENHANCE] = TRUE;
+                    else if (strcmp(token, "accuracy") == 0)
+                        options->optional_sets[ACCURACY] = TRUE;
+                    else if (strcmp(token, "tolerance") == 0)
+                        options->optional_sets[TOLERANCE] = TRUE;
                     else{
                     else{
                         fprintf(stderr, "Unknown rune set %s.\n", token);
                         fprintf(stderr, "Unknown rune set %s.\n", token);
                         return ERROR_INPUT_OPTIMIZE_INVALID_SET;
                         return ERROR_INPUT_OPTIMIZE_INVALID_SET;
@@ -192,30 +280,39 @@ int optimize_parse_arguments(
         else if (strcmp("--stats", argv[i]) == 0 || strcmp("-t", argv[i]) == 0){
         else if (strcmp("--stats", argv[i]) == 0 || strcmp("-t", argv[i]) == 0){
             if (i < argc - 1){
             if (i < argc - 1){
                 // Separate string by commas
                 // Separate string by commas
-                int j = 0;
                 // Returns first token
                 // Returns first token
                 char *token = strtok(argv[i + 1], ",");
                 char *token = strtok(argv[i + 1], ",");
 
 
                 // Keep printing tokens while one of the
                 // Keep printing tokens while one of the
                 // delimiters present in the list, or until its complete
                 // delimiters present in the list, or until its complete
-                while (token != NULL && j < 3){
-                    if (strcmp(token, "hp") == 0) stats[j] = HP_PERCENT;
-                    else if (strcmp(token, "atk") == 0) stats[j] = ATK_PERCENT;
-                    else if (strcmp(token, "def") == 0) stats[j] = DEF_PERCENT;
-                    else if (strcmp(token, "hpflat") == 0) stats[j] = HP_FLAT;
-                    else if (strcmp(token, "atkflat") == 0) stats[j] = ATK_FLAT;
-                    else if (strcmp(token, "defflat") == 0) stats[j] = DEF_FLAT;
-                    else if (strcmp(token, "spd") == 0) stats[j] = SPD;
-                    else if (strcmp(token, "crr") == 0) stats[j] = CRR;
-                    else if (strcmp(token, "crd") == 0) stats[j] = CRD;
-                    else if (strcmp(token, "res") == 0) stats[j] = RES;
-                    else if (strcmp(token, "acc") == 0) stats[j] = ACC;
+                while (token != NULL){
+                    if (strcmp(token, "hp") == 0)
+                        options->stats[HP_PERCENT] = TRUE;
+                    else if (strcmp(token, "atk") == 0)
+                        options->stats[ATK_PERCENT] = TRUE;
+                    else if (strcmp(token, "def") == 0)
+                        options->stats[DEF_PERCENT] = TRUE;
+                    else if (strcmp(token, "hpflat") == 0)
+                        options->stats[HP_FLAT] = TRUE;
+                    else if (strcmp(token, "atkflat") == 0)
+                        options->stats[ATK_FLAT] = TRUE;
+                    else if (strcmp(token, "defflat") == 0)
+                        options->stats[DEF_FLAT] = TRUE;
+                    else if (strcmp(token, "spd") == 0)
+                        options->stats[SPD] = TRUE;
+                    else if (strcmp(token, "crr") == 0)
+                        options->stats[CRR] = TRUE;
+                    else if (strcmp(token, "crd") == 0)
+                        options->stats[CRD] = TRUE;
+                    else if (strcmp(token, "res") == 0)
+                        options->stats[RES] = TRUE;
+                    else if (strcmp(token, "acc") == 0)
+                        options->stats[ACC] = TRUE;
                     else{
                     else{
                         fprintf(stderr, "Unknown rune stat %s.\n", token);
                         fprintf(stderr, "Unknown rune stat %s.\n", token);
                         return ERROR_INPUT_OPTIMIZE_INVALID_STAT;
                         return ERROR_INPUT_OPTIMIZE_INVALID_STAT;
                     }
                     }
                     token = strtok(NULL, ",");
                     token = strtok(NULL, ",");
-                    j ++;
                 }
                 }
                 i ++; // Advance one position in argument reading
                 i ++; // Advance one position in argument reading
             }
             }
@@ -236,7 +333,7 @@ int optimize_parse_arguments(
             if (i < argc - 1){
             if (i < argc - 1){
                 unsigned short stat_tmp = atoi(argv[i + 1]);
                 unsigned short stat_tmp = atoi(argv[i + 1]);
                 if (stat_tmp == 1) stat_tmp = 0;
                 if (stat_tmp == 1) stat_tmp = 0;
-                min_stats->hp = stat_tmp;
+                options->min_stats->hp = stat_tmp;
                 i ++; // Advance one position in argument reading
                 i ++; // Advance one position in argument reading
             }
             }
             else{
             else{
@@ -252,7 +349,7 @@ int optimize_parse_arguments(
             if (i < argc - 1){
             if (i < argc - 1){
                 unsigned short stat_tmp = atoi(argv[i + 1]);
                 unsigned short stat_tmp = atoi(argv[i + 1]);
                 if (stat_tmp == 1) stat_tmp = 0;
                 if (stat_tmp == 1) stat_tmp = 0;
-                min_stats->atk = stat_tmp;
+                options->min_stats->atk = stat_tmp;
                 i ++; // Advance one position in argument reading
                 i ++; // Advance one position in argument reading
             }
             }
             else{
             else{
@@ -268,7 +365,7 @@ int optimize_parse_arguments(
             if (i < argc - 1){
             if (i < argc - 1){
                 unsigned short stat_tmp = atoi(argv[i + 1]);
                 unsigned short stat_tmp = atoi(argv[i + 1]);
                 if (stat_tmp == 1) stat_tmp = 0;
                 if (stat_tmp == 1) stat_tmp = 0;
-                min_stats->def = stat_tmp;
+                options->min_stats->def = stat_tmp;
                 i ++; // Advance one position in argument reading
                 i ++; // Advance one position in argument reading
             }
             }
             else{
             else{
@@ -284,7 +381,7 @@ int optimize_parse_arguments(
             if (i < argc - 1){
             if (i < argc - 1){
                 unsigned short stat_tmp = atoi(argv[i + 1]);
                 unsigned short stat_tmp = atoi(argv[i + 1]);
                 if (stat_tmp == 1) stat_tmp = 0;
                 if (stat_tmp == 1) stat_tmp = 0;
-                min_stats->spd = stat_tmp;
+                options->min_stats->spd = stat_tmp;
                 i ++; // Advance one position in argument reading
                 i ++; // Advance one position in argument reading
             }
             }
             else{
             else{
@@ -301,7 +398,7 @@ int optimize_parse_arguments(
                 unsigned short stat_tmp = atoi(argv[i + 1]);
                 unsigned short stat_tmp = atoi(argv[i + 1]);
                 if (stat_tmp == 1) stat_tmp = 0;
                 if (stat_tmp == 1) stat_tmp = 0;
                 else if (stat_tmp > 100) stat_tmp = 100;
                 else if (stat_tmp > 100) stat_tmp = 100;
-                min_stats->crr = stat_tmp;
+                options->min_stats->crr = stat_tmp;
                 // Advance one position in argument reading
                 // Advance one position in argument reading
                 i ++;
                 i ++;
             }
             }
@@ -318,7 +415,7 @@ int optimize_parse_arguments(
             if (i < argc - 1){
             if (i < argc - 1){
                 unsigned short stat_tmp = atoi(argv[i + 1]);
                 unsigned short stat_tmp = atoi(argv[i + 1]);
                 if (stat_tmp == 1) stat_tmp = 0;
                 if (stat_tmp == 1) stat_tmp = 0;
-                min_stats->crd = stat_tmp;
+                options->min_stats->crd = stat_tmp;
                 i ++; // Advance one position in argument reading
                 i ++; // Advance one position in argument reading
             }
             }
             else{
             else{
@@ -335,7 +432,7 @@ int optimize_parse_arguments(
                 unsigned short stat_tmp = atoi(argv[i + 1]);
                 unsigned short stat_tmp = atoi(argv[i + 1]);
                 if (stat_tmp == 1) stat_tmp = 0;
                 if (stat_tmp == 1) stat_tmp = 0;
                 else if (stat_tmp > 100) stat_tmp = 100;
                 else if (stat_tmp > 100) stat_tmp = 100;
-                min_stats->res = stat_tmp;
+                options->min_stats->res = stat_tmp;
                 i ++; // Advance one position in argument reading
                 i ++; // Advance one position in argument reading
             }
             }
             else{
             else{
@@ -352,7 +449,7 @@ int optimize_parse_arguments(
                 unsigned short stat_tmp = atoi(argv[i + 1]);
                 unsigned short stat_tmp = atoi(argv[i + 1]);
                 if (stat_tmp == 1) stat_tmp = 0;
                 if (stat_tmp == 1) stat_tmp = 0;
                 else if (stat_tmp > 85) stat_tmp = 85;
                 else if (stat_tmp > 85) stat_tmp = 85;
-                min_stats->acc = stat_tmp;
+                options->min_stats->acc = stat_tmp;
                 i ++; // Advance one position in argument reading
                 i ++; // Advance one position in argument reading
             }
             }
             else{
             else{
@@ -368,7 +465,7 @@ int optimize_parse_arguments(
             if (i < argc - 1){
             if (i < argc - 1){
                 unsigned int stat_tmp = atoi(argv[i + 1]);
                 unsigned int stat_tmp = atoi(argv[i + 1]);
                 if (stat_tmp == 1) stat_tmp = 0;
                 if (stat_tmp == 1) stat_tmp = 0;
-                min_stats->ehp = stat_tmp;
+                options->min_stats->ehp = stat_tmp;
                 i ++; // Advance one position in argument reading
                 i ++; // Advance one position in argument reading
             }
             }
             else{
             else{
@@ -384,7 +481,7 @@ int optimize_parse_arguments(
             if (i < argc - 1){
             if (i < argc - 1){
                 unsigned short stat_tmp = atoi(argv[i + 1]);
                 unsigned short stat_tmp = atoi(argv[i + 1]);
                 if (stat_tmp == 1) stat_tmp = 0;
                 if (stat_tmp == 1) stat_tmp = 0;
-                min_stats->dmg = stat_tmp;
+                options->min_stats->dmg = stat_tmp;
                 i ++; // Advance one position in argument reading
                 i ++; // Advance one position in argument reading
             }
             }
             else{
             else{
@@ -394,15 +491,21 @@ int optimize_parse_arguments(
                 return ERROR_INPUT_OPTIMIZE_NO_DMG;
                 return ERROR_INPUT_OPTIMIZE_NO_DMG;
             }
             }
         }
         }
+        // Allow broken sets?
+        else if (
+          strcmp("--broken", argv[i]) == 0 || strcmp("-k", argv[i]) == 0
+        ){
+            options->broken_sets = TRUE;
+        }
         // Storage option?
         // Storage option?
         else if (
         else if (
           strcmp("--storage", argv[i]) == 0 || strcmp("-o", argv[i]) == 0
           strcmp("--storage", argv[i]) == 0 || strcmp("-o", argv[i]) == 0
         ){
         ){
-            *storage = 1;
+            options->storage = TRUE;
         }
         }
         // GUI invoked?
         // GUI invoked?
         else if (strcmp("--gui", argv[i]) == 0 || strcmp("-g", argv[i]) == 0){
         else if (strcmp("--gui", argv[i]) == 0 || strcmp("-g", argv[i]) == 0){
-            *gui = 1;
+            options->gui = FALSE;
         }
         }
 
 
     }
     }

+ 19 - 18
src/RuneOptimizer/optimize/optimize_print.c

@@ -21,8 +21,8 @@
  */
  */
 
 
 void optimize_print_summary(
 void optimize_print_summary(
-  struct Unit *unit, int* sets, int* stats, struct Stats *min_stats,
-  char level, int rune_count[7], unsigned long max_combinations
+  struct Unit *unit, Optimizer_Options *options,
+  unsigned int rune_count[7], unsigned long max_combinations
 ){
 ){
     // This is an output example:
     // This is an output example:
     ////////////////////////////////
     ////////////////////////////////
@@ -48,63 +48,64 @@ void optimize_print_summary(
     );
     );
     printf(" | %*s |      ", -37, unit->name);
     printf(" | %*s |      ", -37, unit->name);
     for (int i = 0; i < 3; i ++){
     for (int i = 0; i < 3; i ++){
-        if (sets[i] != 0) printf(" %s ", set_names[sets[i]]);
+        if (options->sets[i] == TRUE) printf(" %s ", set_names[options->sets[i]]);
         else break;
         else break;
     }
     }
     printf("\n");
     printf("\n");
     printf(" | ID: %*s |\n", -33, unit->id);
     printf(" | ID: %*s |\n", -33, unit->id);
     printf(" -----------------------------------------    Accepted stats:\n");
     printf(" -----------------------------------------    Accepted stats:\n");
     printf(" | STAT | BASE     | CURR.    | MIN.     |      ");
     printf(" | STAT | BASE     | CURR.    | MIN.     |      ");
-    for (int i = 0; i < 12; i ++){
-        if (stats[i] != 0) printf(" %s ", stat_names[stats[i]]);
+    for (int i = 0; i < 13; i ++){
+        if (options->stats[i] == TRUE)
+            printf(" %s ", stat_names[options->stats[i]]);
         else break;
         else break;
     }
     }
     printf("\n");
     printf("\n");
     printf(" -----------------------------------------\n");
     printf(" -----------------------------------------\n");
     printf(
     printf(
       " | HP:  | %*d  | %*d  | %*d  |    ", 7,
       " | HP:  | %*d  | %*d  | %*d  |    ", 7,
-      unit->base_hp, 7, unit->current_hp, 7, min_stats->hp
+      unit->base_hp, 7, unit->current_hp, 7, options->min_stats->hp
     );
     );
-    if (level == 0) printf("Using runes at their current level\n");
-    else printf("Considering runes at level %d\n", level);
+    if (options->level == 0) printf("Using runes at their current level\n");
+    else printf("Considering runes at level %d\n", options->level);
     printf(
     printf(
       " | ATK: | %*d  | %*d  | %*d  |\n", 7,
       " | ATK: | %*d  | %*d  | %*d  |\n", 7,
-      unit->base_atk, 7, unit->current_atk, 7, min_stats->atk
+      unit->base_atk, 7, unit->current_atk, 7, options->min_stats->atk
     );
     );
     printf(
     printf(
       " | DEF: | %*d  | %*d  | %*d  |    Runes considered by slot:\n",
       " | DEF: | %*d  | %*d  | %*d  |    Runes considered by slot:\n",
-      7, unit->base_def, 7, unit->current_def, 7, min_stats->def
+      7, unit->base_def, 7, unit->current_def, 7, options->min_stats->def
     );
     );
     printf(
     printf(
       " | SPD: | %*d  | %*d  | %*d  |      1:%*d    2:%*d    3:%*d\n",
       " | SPD: | %*d  | %*d  | %*d  |      1:%*d    2:%*d    3:%*d\n",
-      7, unit->base_spd, 7, unit->current_spd, 7, min_stats->spd,
+      7, unit->base_spd, 7, unit->current_spd, 7, options->min_stats->spd,
       3, rune_count[1], 3, rune_count[2], 3, rune_count[3]
       3, rune_count[1], 3, rune_count[2], 3, rune_count[3]
     );
     );
     printf(
     printf(
       " | CRR: | %*d\% | %*d\% | %*d\% |      4:%*d    5:%*d    6:%*d\n",
       " | CRR: | %*d\% | %*d\% | %*d\% |      4:%*d    5:%*d    6:%*d\n",
-      7, unit->base_crr, 7, unit->current_crr, 7, min_stats->crr,
+      7, unit->base_crr, 7, unit->current_crr, 7, options->min_stats->crr,
       3, rune_count[4], 3, rune_count[5], 3, rune_count[6]
       3, rune_count[4], 3, rune_count[5], 3, rune_count[6]
     );
     );
     printf(
     printf(
       " | CRD: | %*d\% | %*d\% | %*d\% |\n",
       " | CRD: | %*d\% | %*d\% | %*d\% |\n",
-      7, unit->base_crd, 7, unit->current_crd, 7, min_stats->crd
+      7, unit->base_crd, 7, unit->current_crd, 7, options->min_stats->crd
     );
     );
     printf(
     printf(
-      " | RES: | %*d\% | %*d\% | %*d\% |    Total combinations: %d\n",
-      7, unit->base_res, 7, unit->current_res, 7, min_stats->res,
+      " | RES: | %*d\% | %*d\% | %*d\% |    Total combinations: %llu\n",
+      7, unit->base_res, 7, unit->current_res, 7, options->min_stats->res,
       max_combinations
       max_combinations
     );
     );
     printf(
     printf(
       " | ACC: | %*d\% | %*d\% | %*d\% |\n",
       " | ACC: | %*d\% | %*d\% | %*d\% |\n",
-      7, unit->base_acc, 7, unit->current_acc, 7, min_stats->acc
+      7, unit->base_acc, 7, unit->current_acc, 7, options->min_stats->acc
     );
     );
     printf(
     printf(
       " | EHP: | %*d  | %*d  | %*d  |\n",
       " | EHP: | %*d  | %*d  | %*d  |\n",
-      7, unit->base_ehp, 7, unit->current_ehp, 7, min_stats->ehp
+      7, unit->base_ehp, 7, unit->current_ehp, 7, options->min_stats->ehp
     );
     );
     printf(
     printf(
       " | DMG: | %*d  | %*d  | %*d  |\n",
       " | DMG: | %*d  | %*d  | %*d  |\n",
-      7, unit->base_dmg, 7, unit->current_dmg, 7, min_stats->dmg
+      7, unit->base_dmg, 7, unit->current_dmg, 7, options->min_stats->dmg
     );
     );
     printf(" -----------------------------------------\n");
     printf(" -----------------------------------------\n");
     return;
     return;

+ 61 - 130
src/RuneOptimizer/optimize/optimize_query.c

@@ -22,106 +22,30 @@
  */
  */
 
 
 void optimize_query_for_even_slots(
 void optimize_query_for_even_slots(
-  char level, int* sets, int* stats,
-  char excluded_teams[128][8], char excluded_units[128][8],
-  char storage, char unit_id[64], char query[2500]
+    Optimizer_Options *options, char query[2500]
 ){
 ){
-    char column[10];
-    if (level == 12) strcpy(column, "lv12_");
-    else if (level == 15) strcpy(column, "lv15_");
-    else strcpy(column, "current_");
-    strcpy(query, "SELECT id, unit, slot, type, ");
-    strcat(query, column);
-    strcat(query, "hp_flat, ");
-    strcat(query, column);
-    strcat(query, "atk_flat, ");
-    strcat(query, column);
-    strcat(query, "def_flat, ");
-    strcat(query, column);
-    strcat(query, "hp_percent, ");
-    strcat(query, column);
-    strcat(query, "atk_percent, ");
-    strcat(query, column);
-    strcat(query, "def_percent, ");
-    strcat(query, column);
-    strcat(query, "spd, ");
-    strcat(query, column);
-    strcat(query, "crr, ");
-    strcat(query, column);
-    strcat(query, "crd, ");
-    strcat(query, column);
-    strcat(query, "res, ");
-    strcat(query, column);
-    strcat(query, "acc FROM runes WHERE slot = ? AND type IN (");
-    char cur_set[2];
     char stat_set[2];
     char stat_set[2];
-    if (sets[0] != 0){
-        sprintf(cur_set, "%d", sets[0]);
-        strcat(query, cur_set);
-        strcat(query, ", ");
-    }
-    if (sets[1] != 0){
-        sprintf(cur_set, "%d", sets[1]);
-        strcat(query, cur_set);
-        strcat(query, ", ");
-    }
-    if (sets[2] != 0){
-        sprintf(cur_set, "%d", sets[2]);
-        strcat(query, cur_set);
-        strcat(query, ", ");
-    }
-    strcat(query, "-1) AND main_stat IN (");
-    for (int i = 0; i < 12; i ++){
-        if (stats[i] != 0){
-            sprintf(stat_set, "%d", stats[i]);
+    optimize_query_for_odd_slots(options, query);
+    strcat(query, "  AND main_stat IN (");
+    for (int i = 0; i < 13; i ++){
+        if (options->stats[i] == TRUE){
+            sprintf(stat_set, "%d", i);
             strcat(query, stat_set);
             strcat(query, stat_set);
             strcat(query, ", ");
             strcat(query, ", ");
         }
         }
-        else{
-            break;
-        }
-    }
-    strcat(query, "-1) ");
-    if (storage == 1){
-        // They are not null, are empty!
-        strcat(query, " AND (unit = '' OR unit = '");
-        strcat(query, unit_id);
-        strcat(query, "') ");
-    }
-    // Excluded teams
-    strcat(query, " AND (unit = '' OR unit = '");
-    strcat(query, unit_id);
-    strcat(
-      query, "' OR unit NOT IN (SELECT unit FROM units_teams WHERE team IN("
-    );
-    for (int i = 0; i < 128 && excluded_teams[i][0] != '\0'; i ++){
-        strcat(query, "'");
-        strcat(query, excluded_teams[i]);
-        strcat(query, "',");
     }
     }
-    strcat(query, "'-1'))) ");
-    // Excluded units
-    strcat(query, " AND (unit = '' OR unit = '");
-    strcat(query, unit_id);
-    strcat(query, "' OR unit NOT IN (");
-    for (int i = 0; i < 128 && excluded_units[i][0] != '\0'; i ++){
-        strcat(query, "'");
-        strcat(query, excluded_units[i]);
-        strcat(query, "',");
-    }
-    strcat(query, "'-1')) ");
+    query[strlen(query) - 2] = '\0'; // Remove last coma
+    strcat(query, ") -- Main stats\n");
     //printf("QUERY: %s\n", query);
     //printf("QUERY: %s\n", query);
     return;
     return;
 }
 }
 
 
-void optimize_query_for_odd_slots(
-  char level, int* sets,
-  char excluded_teams[128][8], char excluded_units[128][8],
-  char storage, char unit_id[64], char query[2500]
-){
+void optimize_query_for_odd_slots(Optimizer_Options *options, char query[2500]){
     char column[10];
     char column[10];
-    if (level == 12) strcpy(column, "lv12_");
-    else if (level == 15) strcpy(column, "lv15_");
+    char cur_set[2];
+    char stat_set[2];
+    if (options->level == 12) strcpy(column, "lv12_");
+    else if (options->level == 15) strcpy(column, "lv15_");
     else strcpy(column, "current_");
     else strcpy(column, "current_");
     strcpy(query, "SELECT id, unit, slot, type, ");
     strcpy(query, "SELECT id, unit, slot, type, ");
     strcat(query, column);
     strcat(query, column);
@@ -145,53 +69,60 @@ void optimize_query_for_odd_slots(
     strcat(query, column);
     strcat(query, column);
     strcat(query, "res, ");
     strcat(query, "res, ");
     strcat(query, column);
     strcat(query, column);
-    strcat(query, "acc FROM runes WHERE slot = ? AND type IN (");
-    char cur_set[2];
-    char stat_set[2];
-    if (sets[0] != 0){
-        sprintf(cur_set, "%d", sets[0]);
-        strcat(query, cur_set);
-        strcat(query, ", ");
-    }
-    if (sets[1] != 0){
-        sprintf(cur_set, "%d", sets[1]);
-        strcat(query, cur_set);
-        strcat(query, ", ");
+    strcat(query, "acc\nFROM runes\nWHERE\n  slot = ? -- Slot\n");
+    strcat(query, "  AND type IN (");
+    for (int i = 0; i < 3; i ++){
+        if (options->sets[i] != 0){
+            sprintf(cur_set, "%d", options->sets[i]);
+            strcat(query, cur_set);
+            strcat(query, ", ");
+        }
     }
     }
-    if (sets[2] != 0){
-        sprintf(cur_set, "%d", sets[2]);
-        strcat(query, cur_set);
-        strcat(query, ", ");
+    if (options->full_set == FALSE){
+        for (int i = 0; i < 25; i ++){
+            if (options->optional_sets[i] == TRUE){
+                sprintf(cur_set, "%d", i);
+                strcat(query, cur_set);
+                strcat(query, ", ");
+            }
+        }
     }
     }
-    strcat(query, "-1) ");
-    if (storage == 1){
+    query[strlen(query) - 2] = '\0'; // Remove last coma
+    strcat(query, ") -- Sets\n");
+    if (options->storage == TRUE){
         // They are not null, are empty!
         // They are not null, are empty!
-        strcat(query, " AND (unit = '' OR unit = '");
-        strcat(query, unit_id);
-        strcat(query, "') ");
+        strcat(query, "  AND (unit = '' OR unit = '");
+        strcat(query, options->id);
+        strcat(query, "') -- Only storage\n");
     }
     }
-    // Excluded teams
-    strcat(query, " AND (unit = '' OR unit = '");
-    strcat(query, unit_id);
-    strcat(
-      query, "' OR unit NOT IN (SELECT unit FROM units_teams WHERE team IN("
-    );
-    for (int i = 0; i < 64 && excluded_teams[i][0] != '\0'; i ++){
-        strcat(query, "'");
-        strcat(query, excluded_teams[i]);
-        strcat(query, "',");
+    // Excluded teams (overriden by storage option)
+    if (options->storage == FALSE && options->total_excluded_teams > 0){
+        strcat(query, "  AND (unit = '' OR unit = '");
+        strcat(query, options->id);
+        strcat(
+        query, "' OR unit NOT IN (SELECT unit FROM units_teams WHERE team IN("
+        );
+        for (int i = 0; i < options->total_excluded_teams; i ++){
+            strcat(query, "'");
+            strcat(query, options->excluded_teams[i]);
+            strcat(query, "', ");
+        }
+        query[strlen(query) - 2] = '\0'; // Remove last coma
+        strcat(query, "))) -- Excluded teams\n ");
     }
     }
-    strcat(query, "'-1'))) ");
-    // Excluded units
-    strcat(query, " AND (unit = '' OR unit = '");
-    strcat(query, unit_id);
-    strcat(query, "' OR unit NOT IN (");
-    for (int i = 0; i < 64 && excluded_units[i][0] != '\0'; i ++){
-        strcat(query, "'");
-        strcat(query, excluded_units[i]);
-        strcat(query, "',");
+    // Excluded units (overriden by storage option)
+    if (options->storage == FALSE && options->total_excluded_units > 0){
+        strcat(query, " AND (unit = '' OR unit = '");
+        strcat(query, options->id);
+        strcat(query, "' OR unit NOT IN (");
+        for (int i = 0; i < options->total_excluded_units; i ++){
+            strcat(query, "'");
+            strcat(query, options->excluded_units[i]);
+            strcat(query, "', ");
+        }
+        query[strlen(query) - 2] = '\0'; // Remove last coma
+        strcat(query, ")) -- Excluded units");
     }
     }
-    strcat(query, "'-1')) ");
     //printf("QUERY: %s\n", query);
     //printf("QUERY: %s\n", query);
     return;
     return;
 }
 }