Quellcode durchsuchen

Code optimization. The optimizer is now three or four times faster!
In the gui, some team edition functionalities and the button to apply a result are working again.

Iñigo Valentin vor 4 Jahren
Ursprung
Commit
215410ea11

+ 2 - 1
src/cli/runeoptimizer/db/db.c

@@ -306,7 +306,8 @@ static int db_create_tables(){
         "  current_crr INT,"
         "  current_crd INT,"
         "  current_res INT,"
-        "  current_acc INT"
+        "  current_acc INT,"
+        "  modified INT"
         ");",
         NULL
       )

+ 221 - 226
src/cli/runeoptimizer/optimize/optimize.c

@@ -32,8 +32,10 @@
 #include <math.h>
 #include <stdint.h>
 #include <getopt.h>
-#include <pthread.h>
+#include <threads.h>
 #include <signal.h>
+#include <stdbool.h>
+#include <stdint.h>
 #include <time.h>
 #include "../runeoptimizer.h"
 #include "../error/error.h"
@@ -41,6 +43,9 @@
 #include "optimize.h"
 #include "output/output.h"
 
+#define likely(x) __builtin_expect(!!(x), 1)
+#define unlikely(x) __builtin_expect(!!(x), 0)
+
 /**
  * Maximum arguments.
  *
@@ -144,7 +149,7 @@ static int initialize_data(Optimizer_Data *data);
  * @return {@link SUCCESS} if the unit info is loaded, or
  * {@link ERROR_DB_SELECT_UNIT} if the unit doesn't exist in the database.
  */
-static int read_unit(unsigned char id[UNIT_NAME_LEN], Unit *unit);
+static int read_unit(char *id, Unit *unit);
 
 /**
  * Sets the default values for stat value filters.
@@ -268,7 +273,7 @@ static int select_runes(
  * @param[in,out] vargp Must be a Optimizer_Data pointer casted to void, where
  * the info will be read from and wrtitten to.
  */
-static void *optimize_thread(void *vargp);
+static thrd_start_t optimize_thread(void *vargp);
 
 /**
  * Sorts the result array.
@@ -293,19 +298,14 @@ static void sort_results(Result results[MAX_RESULTS], int total);
  * @param set_count The set to check.
  * @return {@link TRUE} if the set has any broken sets, {@link FALSE} otherwise.
  */
-static unsigned char contains_broken(Rune_Set_Count *set_count);
+__inline static bool contains_broken(Rune_Set_Count *set_count);
 
 /**
  * Optimizer data package.
  *
  * Written by the threads.
  */
-static Optimizer_Data opt_data;
-
-/**
- * Mutex for writing to {@link opt_data}.
- */
-static pthread_mutex_t mutex;
+static Progress progress;
 
 static int initialize_data(Optimizer_Data *data){
     data->filters = malloc(sizeof(Optimizer_Filters));
@@ -321,18 +321,20 @@ static int initialize_data(Optimizer_Data *data){
         return ERROR_MEMORY;
     }
     data->output->level = OUT_S;
-    data->output->quiet = FALSE;
-    data->output->verbose = FALSE;
-    data->output->debug = FALSE;
+    data->output->quiet = false;
+    data->output->verbose = false;
+    data->output->debug = false;
     data->output->format = OUTPUT_HUMAN;
     data->output->output = OUTPUT_SCREEN;
-    data->options->threads = FALSE;
-    data->options->help = FALSE;
+    data->options->threads = false;
+    data->options->next_thread_id = 0;
+    data->options->help = false;
     strcpy((char *) data->filters->id, "");
     data->filters->level = 0;
     for (int i = 0; i < MAX_SETS; i ++) data->filters->sets[i] = 0;
-    for (int i = 0; i < DIFFERENT_STATS; i ++) data->filters->stats[i] = FALSE;
-    for (int i = 0; i < DIFFERENT_SETS; i ++) data->filters->optional_sets[i] = FALSE;
+    for (int i = 0; i < DIFFERENT_STATS; i ++) data->filters->stats[i] = false;
+    for (int i = 0; i < DIFFERENT_SETS; i ++)
+        data->filters->optional_sets[i] = false;
     data->filters->min_stats = malloc(sizeof(Stats));
     data->filters->min_stats->hp = 1;
     data->filters->min_stats->atk = 1;
@@ -344,13 +346,12 @@ static int initialize_data(Optimizer_Data *data){
     data->filters->min_stats->acc = 1;
     data->filters->min_stats->ehp = 1;
     data->filters->min_stats->dmg = 1;
-    data->filters->gui = FALSE;
-    data->filters->storage = FALSE;
+    data->filters->storage = false;
     data->filters->total_excluded_teams = 0,
     data->filters->total_excluded_units = 0;
-    data->filters->broken_sets = FALSE;
-    data->options->threads = 2;
-    data->filters->full_set = FALSE;
+    data->filters->broken_sets = false;
+    data->options->threads = 1;
+    data->filters->full_set = false;
     data->requested_set_count->energy = 0;
     data->requested_set_count->guard = 0;
     data->requested_set_count->swift = 0;
@@ -373,7 +374,7 @@ static int initialize_data(Optimizer_Data *data){
     data->requested_set_count->accuracy = 0;
     data->requested_set_count->tolerance = 0;
     data->total_results = 0;
-    data->tested_combinations = 0;
+    //data->tested_combinations = 0;
     // Calculate start time
     time_t t = time(NULL);
     struct tm tm = *localtime(&t);
@@ -387,26 +388,26 @@ static int initialize_data(Optimizer_Data *data){
 }
 
 static void set_default_mins(Optimizer_Data *data){
-    if (opt_data.filters->min_stats->hp == 1)
-      opt_data.filters->min_stats->hp = opt_data.unit->current_hp;
-    if (opt_data.filters->min_stats->atk == 1)
-      opt_data.filters->min_stats->atk = opt_data.unit->current_atk;
-    if (opt_data.filters->min_stats->def == 1)
-      opt_data.filters->min_stats->def = opt_data.unit->current_def;
-    if (opt_data.filters->min_stats->spd == 1)
-      opt_data.filters->min_stats->spd = opt_data.unit->current_spd;
-    if (opt_data.filters->min_stats->crr == 1)
-      opt_data.filters->min_stats->crr = opt_data.unit->current_crr;
-    if (opt_data.filters->min_stats->crd == 1)
-      opt_data.filters->min_stats->crd = opt_data.unit->current_crd;
-    if (opt_data.filters->min_stats->res == 1)
-      opt_data.filters->min_stats->res = opt_data.unit->current_res;
-    if (opt_data.filters->min_stats->acc == 1)
-      opt_data.filters->min_stats->acc = opt_data.unit->current_acc;
-    if (opt_data.filters->min_stats->ehp == 1)
-      opt_data.filters->min_stats->ehp = 0;
-    if (opt_data.filters->min_stats->dmg == 1)
-      opt_data.filters->min_stats->dmg = 0;
+    if (data->filters->min_stats->hp == 1)
+      data->filters->min_stats->hp = data->unit->current_hp;
+    if (data->filters->min_stats->atk == 1)
+      data->filters->min_stats->atk = data->unit->current_atk;
+    if (data->filters->min_stats->def == 1)
+      data->filters->min_stats->def = data->unit->current_def;
+    if (data->filters->min_stats->spd == 1)
+      data->filters->min_stats->spd = data->unit->current_spd;
+    if (data->filters->min_stats->crr == 1)
+      data->filters->min_stats->crr = data->unit->current_crr;
+    if (data->filters->min_stats->crd == 1)
+      data->filters->min_stats->crd = data->unit->current_crd;
+    if (data->filters->min_stats->res == 1)
+      data->filters->min_stats->res = data->unit->current_res;
+    if (data->filters->min_stats->acc == 1)
+      data->filters->min_stats->acc = data->unit->current_acc;
+    if (data->filters->min_stats->ehp == 1)
+      data->filters->min_stats->ehp = 0;
+    if (data->filters->min_stats->dmg == 1)
+      data->filters->min_stats->dmg = 0;
 }
 
 static int calculate_rune_count(Optimizer_Data *data){
@@ -462,11 +463,12 @@ static int calculate_rune_count(Optimizer_Data *data){
         fprintf(stderr, "Invalid rune set combination.\n");
         return ERROR_INPUT_OPTIMIZE_INCOMPLETE_SETS;
     }
-    else if (RUNE_SLOTS == total) opt_data.filters->full_set = TRUE;
+    else if (RUNE_SLOTS == total) data->filters->full_set = TRUE;
     return total;
 }
 
 extern int optimize(int argc, char *argv[]){
+    Optimizer_Data opt_data;
     int status = SUCCESS;
     char query_even[RUNE_QUERY_LEN];
     char query_odd[RUNE_QUERY_LEN];
@@ -499,22 +501,28 @@ extern int optimize(int argc, char *argv[]){
         }
     }
 
+    // initialize progression values
+    progress.percent = 0;
+    progress.tested = 0;
+    progress.total = opt_data.max_combinations;
+    progress.results = 0;
+
     // Print summary with data collected so far.
     if (opt_data.output->level > OUT_Q) output_summary(&opt_data);
+    if (opt_data.output->quiet == FALSE) output_progress_human(&progress);
 
     // Now its time to loop all 6 'reels' of runes and try to match combos
-    pthread_t thread[opt_data.options->threads];
-    for (char t = 0; t < opt_data.options->threads; t++){
-        pthread_create(
-          &thread[t], NULL, optimize_thread, (void *)(uintptr_t) t
+    mtx_init(&opt_data.mutex, mtx_plain);
+    thrd_t thread[opt_data.options->threads];
+    for (int t = 0; t < opt_data.options->threads; t++)
+        thrd_create(
+          &thread[t], (thrd_start_t) optimize_thread, (void *) &opt_data
         );
-    }
     for (int t = 0; t < opt_data.options->threads; t++)
-        pthread_join(thread[t], NULL);
+        thrd_join(thread[t], NULL);
+    progress.percent = 100;
     if (opt_data.output->quiet == FALSE){
-        printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b");
-        printf("[%3d%%] Results found: %5u", 100, opt_data.total_results);
-        fflush(stdout);
+        output_progress_human(&progress);
         printf("\n");
     }
     sort_results(opt_data.results, opt_data.total_results);
@@ -972,7 +980,7 @@ static int parse_arguments(int argc, char *argv[], Optimizer_Data *data){
     return SUCCESS;
 }
 
-static int read_unit(unsigned char id[UNIT_NAME_LEN], Unit *unit){
+static int read_unit(char *id, Unit *unit){
     sqlite3_stmt *stmt_unit;
     char *parameters[2] = {(char *) id, (char *) id};
     db_query(
@@ -1284,27 +1292,24 @@ static int select_runes(
 
 
 
-static void *optimize_thread(void *tid){
-    int thread_id = (int) (uintptr_t) tid;
-    int start_at = thread_id * ceil((float) opt_data.count[1]
-      / (float) opt_data.options->threads);
-    int end_at = start_at + ceil((float) opt_data.count[1] / (float) opt_data.options->threads);
+static thrd_start_t optimize_thread(void *data_ptr){
+    Optimizer_Data *data = data_ptr;//(Optimizer_Data) (uintptr_t) data_ptr;
+    uint_fast64_t tested_combinations = 0;
+    mtx_lock(&data->mutex);
+    uint_fast8_t thread_id = data->options->next_thread_id;
+    data->options->next_thread_id ++;
+    mtx_unlock(&data->mutex);
+    uint_fast16_t start_at = thread_id * ceil((float) data->count[1]
+      / (float) data->options->threads);
+    uint_fast16_t end_at =
+      start_at
+      + ceil((float) data->count[1] / (float) data->options->threads);
 
     // Initialize arrys and some counters
-    unsigned int index[RUNE_SLOTS + 1] = {0, start_at, 0, 0, 0, 0};
-    unsigned long valid_sets = 0;
+    uint_fast16_t index[RUNE_SLOTS + 1] = {0, start_at, 0, 0, 0, 0};
     Rune_Set_Count set_count;
-    unsigned char progress_printed = FALSE;
-    while(
-      index[1] < end_at
-      && index[1] < opt_data.count[1]
-      && index[2] < opt_data.count[2]
-      && index[3] < opt_data.count[3]
-      && index[4] < opt_data.count[4]
-      && index[5] < opt_data.count[5]
-      && index[6] < opt_data.count[6]
-      && opt_data.total_results < MAX_RESULTS // Hard limit
-    ){
+    struct Stats stats;
+    while(index[1] < end_at){
         // Calculate rune sets at current indexes.
         set_count.energy = 0;
         set_count.guard = 0;
@@ -1327,8 +1332,8 @@ static void *optimize_thread(void *tid){
         set_count.enhance = 0;
         set_count.accuracy = 0;
         set_count.tolerance = 0;
-        for (int i = 1; i < RUNE_SLOTS + 1; i ++){
-            switch (opt_data.runes[i][index[i]].set){
+        for (uint_fast8_t i = 1; i < RUNE_SLOTS + 1; i ++){
+            switch (data->runes[i][index[i]].set){
                 case ENERGY:        set_count.energy ++;        break;
                 case GUARD:         set_count.guard ++;         break;
                 case SWIFT:         set_count.swift ++;         break;
@@ -1354,76 +1359,74 @@ static void *optimize_thread(void *tid){
         }
 
         // Compare with requested sets
-        if (
-            (
-              opt_data.filters->broken_sets == TRUE ||
-              contains_broken(&set_count) == FALSE
-            ) &&
-            set_count.energy >= opt_data.requested_set_count->energy &&
-            set_count.guard >= opt_data.requested_set_count->guard &&
-            set_count.swift >= opt_data.requested_set_count->swift &&
-            set_count.blade >= opt_data.requested_set_count->blade &&
-            set_count.rage >= opt_data.requested_set_count->rage &&
-            set_count.focus >= opt_data.requested_set_count->focus &&
-            set_count.endure >= opt_data.requested_set_count->endure &&
-            set_count.fatal >= opt_data.requested_set_count->fatal &&
-            set_count.despair >= opt_data.requested_set_count->despair &&
-            set_count.vampire >= opt_data.requested_set_count->vampire &&
-            set_count.violent >= opt_data.requested_set_count->violent &&
-            set_count.nemesis >= opt_data.requested_set_count->nemesis &&
-            set_count.will >= opt_data.requested_set_count->will &&
-            set_count.shield >= opt_data.requested_set_count->shield &&
-            set_count.revenge >= opt_data.requested_set_count->revenge &&
-            set_count.destroy >= opt_data.requested_set_count->destroy &&
-            set_count.fight >= opt_data.requested_set_count->fight &&
+        bool broken_state = true;
+        if (data->filters->broken_sets == false)
+            broken_state = !contains_broken(&set_count);
+        if (unlikely(
+            broken_state &&
+            set_count.energy >= data->requested_set_count->energy &&
+            set_count.guard >= data->requested_set_count->guard &&
+            set_count.swift >= data->requested_set_count->swift &&
+            set_count.blade >= data->requested_set_count->blade &&
+            set_count.rage >= data->requested_set_count->rage &&
+            set_count.focus >= data->requested_set_count->focus &&
+            set_count.endure >= data->requested_set_count->endure &&
+            set_count.fatal >= data->requested_set_count->fatal &&
+            set_count.despair >= data->requested_set_count->despair &&
+            set_count.vampire >= data->requested_set_count->vampire &&
+            set_count.violent >= data->requested_set_count->violent &&
+            set_count.nemesis >= data->requested_set_count->nemesis &&
+            set_count.will >= data->requested_set_count->will &&
+            set_count.shield >= data->requested_set_count->shield &&
+            set_count.revenge >= data->requested_set_count->revenge &&
+            set_count.destroy >= data->requested_set_count->destroy &&
+            set_count.fight >= data->requested_set_count->fight &&
             set_count.determination >=
-              opt_data.requested_set_count->determination &&
-            set_count.enhance >= opt_data.requested_set_count->enhance &&
-            set_count.accuracy >= opt_data.requested_set_count->accuracy &&
-            set_count.tolerance >= opt_data.requested_set_count->tolerance
-        ){
+              data->requested_set_count->determination &&
+            set_count.enhance >= data->requested_set_count->enhance &&
+            set_count.accuracy >= data->requested_set_count->accuracy &&
+            set_count.tolerance >= data->requested_set_count->tolerance
+        )){
             // The current runes form a valid set.
-            valid_sets ++;
 
             // Calculate new stats
-            struct Stats stats;
-            stats.hp = opt_data.unit->base_hp;
-            stats.atk = opt_data.unit->base_atk;
-            stats.def = opt_data.unit->base_def;
-            stats.spd = opt_data.unit->base_spd;
-            stats.crr = opt_data.unit->base_crr;
-            stats.crd = opt_data.unit->base_crd;
-            stats.res = opt_data.unit->base_res;
-            stats.acc = opt_data.unit->base_acc;
-            for (int i = 1; i < RUNE_SLOTS + 1; i ++){
-                stats.hp += opt_data.runes[i][index[i]].hp_flat;
-                stats.atk += opt_data.runes[i][index[i]].atk_flat;
-                stats.def += opt_data.runes[i][index[i]].def_flat;
+            stats.hp = data->unit->base_hp;
+            stats.atk = data->unit->base_atk;
+            stats.def = data->unit->base_def;
+            stats.spd = data->unit->base_spd;
+            stats.crr = data->unit->base_crr;
+            stats.crd = data->unit->base_crd;
+            stats.res = data->unit->base_res;
+            stats.acc = data->unit->base_acc;
+            for (uint_fast8_t i = 1; i < RUNE_SLOTS + 1; i ++){
+                stats.hp += data->runes[i][index[i]].hp_flat;
+                stats.atk += data->runes[i][index[i]].atk_flat;
+                stats.def += data->runes[i][index[i]].def_flat;
                 stats.hp +=
-                  opt_data.unit->base_hp
-                  * opt_data.runes[i][index[i]].hp_percent / 100;
+                  data->unit->base_hp
+                  * data->runes[i][index[i]].hp_percent / 100;
                 stats.atk +=
-                  opt_data.unit->base_atk
-                  * opt_data.runes[i][index[i]].atk_percent / 100;
+                  data->unit->base_atk
+                  * data->runes[i][index[i]].atk_percent / 100;
                 stats.def +=
-                  opt_data.unit->base_def
-                  * opt_data.runes[i][index[i]].def_percent / 100;
-                stats.spd += opt_data.runes[i][index[i]].spd;
-                stats.crr += opt_data.runes[i][index[i]].crr;
-                stats.crd += opt_data.runes[i][index[i]].crd;
-                stats.res += opt_data.runes[i][index[i]].res;
-                stats.acc += opt_data.runes[i][index[i]].acc;
+                  data->unit->base_def
+                  * data->runes[i][index[i]].def_percent / 100;
+                stats.spd += data->runes[i][index[i]].spd;
+                stats.crr += data->runes[i][index[i]].crr;
+                stats.crd += data->runes[i][index[i]].crd;
+                stats.res += data->runes[i][index[i]].res;
+                stats.acc += data->runes[i][index[i]].acc;
             }
 
             // Set stats
             if (set_count.energy >= 2) // +15% Base HP per set of 2
                 stats.hp +=
-                  opt_data.unit->base_hp * 0.15 * (set_count.energy % 2);
+                  data->unit->base_hp * 0.15 * (set_count.energy % 2);
             if (set_count.guard >= 2) // +15% base DEF per set of 2
                 stats.def +=
-                  opt_data.unit->base_def * 0.15 * (set_count.guard % 2);
+                  data->unit->base_def * 0.15 * (set_count.guard % 2);
             if (set_count.swift >= 4) // +25% base SPD per set of 4
-                stats.spd += opt_data.unit->base_spd * 0.25;
+                stats.spd += data->unit->base_spd * 0.25;
             if (set_count.blade >= 2) // +12% CRR per set of 2
                 stats.crr += 12;
             if (set_count.rage >= 4) // +40% CRD per set of 4
@@ -1433,17 +1436,17 @@ static void *optimize_thread(void *tid){
             if (set_count.endure >= 2) // +20% RES per set of 2
                 stats.res += 20;
             if (set_count.fatal >= 4) // +35% base ATK per set of 4
-                stats.atk += opt_data.unit->base_atk * 0.35;
+                stats.atk += data->unit->base_atk * 0.35;
             if (set_count.fight >= 2) // +8% base ATK per set of 2
                 stats.atk +=
-                  opt_data.unit->base_atk * 0.08 * (set_count.fight % 2);
+                  data->unit->base_atk * 0.08 * (set_count.fight % 2);
             if (set_count.determination >= 2) // +8% base DEF per set of 2
                 stats.def +=
-                  opt_data.unit->base_def * 0.08
+                  data->unit->base_def * 0.08
                   * (set_count.determination % 2);
             if (set_count.enhance >= 2) // +8% base HP per set of 2
                 stats.hp +=
-                  opt_data.unit->base_hp * 0.08 * (set_count.enhance % 2);
+                  data->unit->base_hp * 0.08 * (set_count.enhance % 2);
             if (set_count.accuracy >= 2) // +10% ACC per set of 2
                 stats.acc += 20;
             if (set_count.tolerance >= 2) // +10% RES per set of 2
@@ -1459,18 +1462,18 @@ static void *optimize_thread(void *tid){
             stats.dmg = calculate_dmg(stats.atk, stats.crr, stats.crd);
 
             // Compare with minimum requeriments
-            if (
-                stats.hp >= opt_data.filters->min_stats->hp &&
-                stats.atk >= opt_data.filters->min_stats->atk &&
-                stats.def >= opt_data.filters->min_stats->def &&
-                stats.spd >= opt_data.filters->min_stats->spd &&
-                stats.crr >= opt_data.filters->min_stats->crr &&
-                stats.crd >= opt_data.filters->min_stats->crd &&
-                stats.res >= opt_data.filters->min_stats->res &&
-                stats.acc >= opt_data.filters->min_stats->acc &&
-                stats.ehp >= opt_data.filters->min_stats->ehp &&
-                stats.dmg >= opt_data.filters->min_stats->dmg
-            ){
+            if (unlikely(
+                stats.hp >= data->filters->min_stats->hp &&
+                stats.atk >= data->filters->min_stats->atk &&
+                stats.def >= data->filters->min_stats->def &&
+                stats.spd >= data->filters->min_stats->spd &&
+                stats.crr >= data->filters->min_stats->crr &&
+                stats.crd >= data->filters->min_stats->crd &&
+                stats.res >= data->filters->min_stats->res &&
+                stats.acc >= data->filters->min_stats->acc &&
+                stats.ehp >= data->filters->min_stats->ehp &&
+                stats.dmg >= data->filters->min_stats->dmg
+            )){
                 // Calculate rating, based on the difference between the
                 // stats with this set and the current stats.
                 // This is calculated accounting for the proportions between
@@ -1486,97 +1489,92 @@ static void *optimize_thread(void *tid){
                 // ACC: 1 rating point per 1.52 points
                 float rating = 0.0f;
                 rating +=
-                  (float) ((int) stats.hp  - (int) opt_data.unit->current_hp )
+                  (float) ((int) stats.hp  - (int) data->unit->current_hp )
                   / 58.29f;
                 rating +=
-                  (float) ((int) stats.atk - (int) opt_data.unit->current_atk)
+                  (float) ((int) stats.atk - (int) data->unit->current_atk)
                   / 1.50f;
                 rating +=
-                  (float) ((int) stats.def - (int) opt_data.unit->current_def)
+                  (float) ((int) stats.def - (int) data->unit->current_def)
                   / 1.50f;
                 rating +=
-                  (float) ((int) stats.spd - (int) opt_data.unit->current_spd)
+                  (float) ((int) stats.spd - (int) data->unit->current_spd)
                   / 1.00f;
                 rating +=
-                  (float) ((int) stats.crr - (int) opt_data.unit->current_crr)
+                  (float) ((int) stats.crr - (int) data->unit->current_crr)
                   / 1.38f;
                 rating +=
-                  (float) ((int) stats.crd - (int) opt_data.unit->current_crd)
+                  (float) ((int) stats.crd - (int) data->unit->current_crd)
                   / 1.90f;
                 rating +=
-                  (float) ((int) stats.res - (int) opt_data.unit->current_res)
+                  (float) ((int) stats.res - (int) data->unit->current_res)
                   / 1.52f;
                 rating +=
-                  (float) ((int) stats.acc - (int) opt_data.unit->current_acc)
+                  (float) ((int) stats.acc - (int) data->unit->current_acc)
                   / 1.52f;
 
                 // This is a valid sets and all stats are above the minimum.
                 // Create a result with rune indexes, stats, and rating.
                 // Lock the mutex
-                pthread_mutex_lock(&mutex);
+                mtx_lock(&data->mutex);
                 for (int i = 1; i < RUNE_SLOTS + 1; i ++){
                     strcpy(
-                      (char *) opt_data.results[
-                        opt_data.total_results
+                      (char *) data->results[
+                        data->total_results
                       ].rune_ids[i - 1],
-                      (const char *) opt_data.runes[i][index[i]].id
+                      (const char *) data->runes[i][index[i]].id
                     );
                 }
-                opt_data.results[opt_data.total_results].stats.hp = stats.hp;
-                opt_data.results[opt_data.total_results].stats.atk = stats.atk;
-                opt_data.results[opt_data.total_results].stats.def = stats.def;
-                opt_data.results[opt_data.total_results].stats.spd = stats.spd;
-                opt_data.results[opt_data.total_results].stats.crr = stats.crr;
-                opt_data.results[opt_data.total_results].stats.crd = stats.crd;
-                opt_data.results[opt_data.total_results].stats.res = stats.res;
-                opt_data.results[opt_data.total_results].stats.acc = stats.acc;
-                opt_data.results[opt_data.total_results].stats.ehp = stats.ehp;
-                opt_data.results[opt_data.total_results].stats.dmg = stats.dmg;
-                opt_data.results[opt_data.total_results].rating = (int) rating;
-                opt_data.total_results ++;
-                pthread_mutex_unlock(&mutex);
+                data->results[data->total_results].stats.hp = stats.hp;
+                data->results[data->total_results].stats.atk = stats.atk;
+                data->results[data->total_results].stats.def = stats.def;
+                data->results[data->total_results].stats.spd = stats.spd;
+                data->results[data->total_results].stats.crr = stats.crr;
+                data->results[data->total_results].stats.crd = stats.crd;
+                data->results[data->total_results].stats.res = stats.res;
+                data->results[data->total_results].stats.acc = stats.acc;
+                data->results[data->total_results].stats.ehp = stats.ehp;
+                data->results[data->total_results].stats.dmg = stats.dmg;
+                data->results[data->total_results].rating = (int) rating;
+                data->total_results ++;
+                progress.results ++;
+                mtx_unlock(&data->mutex);
             }
         }
 
         // Loop control. Rotate the reels 'right to left'
-        pthread_mutex_lock(&mutex);
-        opt_data.tested_combinations ++;
+        tested_combinations ++;
+
 
         // Update progress bar.
-        if (
-          opt_data.output->quiet == FALSE
-          && opt_data.tested_combinations % 500000 == 0
-        ){
-            printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b");
-            printf(
-              "[%3d%%] Results found: %5u",
-              (int) (
-                (
-                  (double) opt_data.tested_combinations
-                  / (double) opt_data.max_combinations
-                )
-                * 100.0f
-              ),
-              opt_data.total_results
+        if (unlikely(tested_combinations == PROGRESS_TICK)){
+            mtx_lock(&data->mutex);
+            progress.tested += tested_combinations;
+            progress.percent = (int) (
+              ((double) progress.tested * 100.0f)
+              / (double) progress.total
             );
-            fflush(stdout);
+            if (data->output->quiet == FALSE){
+                output_progress_human(&progress);
+            }
+            mtx_unlock(&data->mutex);
+            tested_combinations = 0;
         }
 
-        pthread_mutex_unlock(&mutex);
         index[6] ++;
-        if (index[6] == opt_data.count[6]){
+        if (index[6] == data->count[6]){
             index[6] = 0; index[5] ++;
         }
-        if (index[5] == opt_data.count[5]){
+        if (index[5] == data->count[5]){
             index[5] = 0; index[4] ++;
         }
-        if (index[4] == opt_data.count[4]){
+        if (index[4] == data->count[4]){
             index[4] = 0; index[3] ++;
         }
-        if (index[3] == opt_data.count[3]){
+        if (index[3] == data->count[3]){
             index[3] = 0; index[2] ++;
         }
-        if (index[2] == opt_data.count[2]){
+        if (index[2] == data->count[2]){
             index[2] = 0; index[1] ++;
         }
 
@@ -1584,31 +1582,36 @@ static void *optimize_thread(void *tid){
         //if (result_count > 2)
         //    break;
     }
-    pthread_exit(NULL);
+    mtx_lock(&data->mutex);
+    // Update current test count before exiting
+    progress.tested += tested_combinations;
+    mtx_unlock(&data->mutex);
+    // Thread ending
+    thrd_exit(EXIT_SUCCESS);
 }
 
-static unsigned char 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);
+__inline static bool 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);
 }
 
@@ -1631,15 +1634,7 @@ static void sort_results(Result results[MAX_RESULTS], int total){
 
 static void handle_signal(int sig){
     if (sig == SIGUSR1){
-        unsigned int progress;
-        if (0 == opt_data.max_combinations) progress = 0;
-        else progress =
-          opt_data.tested_combinations * 100 / opt_data.max_combinations;
-        printf(
-          "  Progress: %u%% (%llu / %llu)\n",
-          progress, opt_data.tested_combinations, opt_data.max_combinations
-        );
-        fflush(stdout);
+        output_progress_json(&progress);
     }
 }
 

+ 112 - 79
src/cli/runeoptimizer/optimize/optimize.h

@@ -27,6 +27,9 @@
 #pragma once
 
 #include "../runeoptimizer.h"
+#include <stdint.h>
+#include <stdbool.h>
+#include <threads.h>
 
 /**
  * Hard limit on the number of results
@@ -58,6 +61,15 @@
 #define OUT_V 2
 #define OUT_D 3
 
+/**
+ * Number of tested combinations after which the progress is updated.
+ *
+ * Used to display the progress or to update {@link Progress} structures.
+ * Shorter values give more precise information, but can slow down the
+ * optimization process.
+ */
+#define PROGRESS_TICK 1500000
+
 /**
  * A rune.
  *
@@ -77,14 +89,14 @@ typedef struct Rune {
      *
      * Can be [1-6].
      */
-    unsigned char slot;
+    uint_fast8_t slot;
 
     /**
      * Rune set id.
      *
      * Identifier of the set the rune belongs to.
      */
-    unsigned char set;
+    uint_fast8_t set;
 
     /**
      * ID of the unit the rune is assigned to.
@@ -98,77 +110,77 @@ typedef struct Rune {
      *
      * HP% given by the rune, in any of it's stats.
      */
-    unsigned char hp_percent;
+    uint_fast8_t hp_percent;
 
     /**
      * ATK% stat.
      *
      * ATK% given by the rune, in any of it's stats.
      */
-    unsigned char atk_percent;
+    uint_fast8_t atk_percent;
 
     /**
      * DEF% stat.
      *
      * DEF% given by the rune, in any of it's stats.
      */
-    unsigned char def_percent;
+    uint_fast8_t def_percent;
 
     /**
      * HP stat.
      *
      * HP given by the rune, in any of it's stats.
      */
-    unsigned short hp_flat;
+    uint_fast16_t hp_flat;
 
     /**
      * ATK stat.
      *
      * ATK given by the rune, in any of it's stats.
      */
-    unsigned char atk_flat;
+    uint_fast8_t atk_flat;
 
     /**
      * DEF stat.
      *
      * DEF given by the rune, in any of it's stats.
      */
-    unsigned char def_flat;
+    uint_fast8_t def_flat;
 
     /**
      * SPD stat.
      *
      * SPD given by the rune, in any of it's stats.
      */
-    unsigned char spd;
+    uint_fast8_t spd;
 
     /**
      * CRR stat.
      *
      * CRR given by the rune, in any of it's stats.
      */
-    unsigned char crr;
+    uint_fast8_t crr;
 
     /**
      * CRD stat.
      *
      * CRD given by the rune, in any of it's stats.
      */
-    unsigned char crd;
+    uint_fast8_t crd;
 
     /**
      * RES stat.
      *
      * RES given by the rune, in any of it's stats.
      */
-    unsigned char res;
+    uint_fast8_t res;
 
     /**
      * ACC stat.
      *
      * ACC given by the rune, in any of it's stats.
      */
-    unsigned char acc;
+    uint_fast8_t acc;
 } Rune;
 
 
@@ -184,56 +196,56 @@ typedef struct Stats {
      *
      * Unit health. Unit gets defeated in battle when it reaches 0.
      */
-    unsigned int hp;
+    uint_fast16_t hp;
 
     /**
      * ATK stat.
      *
      * Unit attack power. Retermines how much damage it does.
      */
-    unsigned short atk;
+    uint_fast16_t atk;
 
     /**
      * DEF stat.
      *
      * Unit defense. Reduces incoming damage.
      */
-    unsigned short def;
+    uint_fast16_t def;
 
     /**
      * SPD stat.
      *
      * Unit speed. Determines turn order.
      */
-    unsigned short spd;
+    uint_fast16_t spd;
 
     /**
      * CRR stat.
      *
      * Chance of landing a critical hit. Capped at 100.
      */
-    unsigned short crr;
+    uint_fast8_t crr;
 
     /**
      * CRD stat.
      *
      * Extra damage done when landing a critical hit.
      */
-    unsigned short crd;
+    uint_fast16_t crd;
 
     /**
      * RES stat.
      *
      * Chance of resisting a harmful effect. Cant be lower than 15.
      */
-    unsigned short res;
+    uint_fast8_t res;
 
     /**
      * ACC stat.
      *
      * Chance of landing a harmfull effect. Effective cap is 85.
      */
-    unsigned short acc;
+    uint_fast8_t acc;
 
     /**
      * EHP stat.
@@ -241,7 +253,7 @@ typedef struct Stats {
      * EHP is a complex stat that depends on {@link hp} and {@link def}. See
      * {@link calculate_ehp} for more details.
      */
-    unsigned int ehp;
+    uint_fast32_t ehp;
 
     /**
      * DMG stat.
@@ -249,7 +261,7 @@ typedef struct Stats {
      * DMG is a complex stat that depends on {@link atk} and {@link crr} and
      * {@link crr}. See {@link calculate_ehp} for more details.
      */
-    unsigned short dmg;
+    uint_fast16_t dmg;
 } Stats;
 
 /**
@@ -340,7 +352,7 @@ typedef struct Result {
      * Calculated by comparing each stat (except EHP and DMG) with the unit
      * current stats.
      */
-    signed int rating;
+    int_fast16_t rating;
 
     /**
      * Result stats.
@@ -358,32 +370,32 @@ typedef struct Optimizer_Output{
     /**
      * Flag to indicate verbose output.
      */
-    unsigned char verbose;
+    bool verbose;
 
     /**
      * Flag to indicate quiet output.
      */
-    unsigned char quiet;
+    bool quiet;
 
     /**
      * Flag to indicate debug output.
      */
-    unsigned char debug;
+    bool debug;
 
     /**
      * Output format indicator.
      */
-    unsigned char format;
+    uint_fast8_t format;
 
     /**
      * Output target indicator.
      */
-    unsigned char output;
+    uint_fast8_t output;
 
     /**
      * Verbosity level.
      */
-    unsigned char level;
+    uint_fast8_t level;
 } Optimizer_Output;
 
 /**
@@ -396,14 +408,25 @@ typedef struct Optimizer_Options{
      *
      * Currently ignored.
      */
-    unsigned char help;
+    bool help;
+
+    /**
+     * Number of thread to be used during optimization.
+     *
+     * Calculation of rune combinations can be done in separateed threads. Each
+     * thread shares the same amount of work as the others, so the more threads,
+     * the faster the optimzation is! The default value is 1, and can be set
+     * passing the parameter -b or --threads, followed by a numeric value. No
+     * more than 8 threads can be used.
+     */
+    uint_fast8_t threads;
 
     /**
-     * Number of threads to use.
+     * Number of the next available thread ID.
      *
-     * Limited to {@link MAX_THREADS}.
+     * When read by a thread, it must be incremented.
      */
-    unsigned char threads;
+    uint_fast8_t next_thread_id;
 } Optimizer_Options;
 
 /**
@@ -437,7 +460,7 @@ typedef struct Optimizer_Filters {
      * the main stat will be considered to have the value it would have if the
      * rune was at that level.
      */
-    unsigned char level;
+    uint_fast8_t level;
 
     /**
      * Sets of runes that the unit must have.
@@ -465,7 +488,7 @@ typedef struct Optimizer_Filters {
      *                                  is ignored!)
      * @endcode
      */
-    unsigned char sets[MAX_SETS];
+    uint_fast8_t sets[MAX_SETS];
 
     /**
      * Indicates if the sets in {@link sets} amount for 6 runes.
@@ -473,7 +496,7 @@ typedef struct Optimizer_Filters {
      * This is not a configurable option, but a handy flag. The default value is
      * {@link FALSE} and will be checked in {@link optimize}.
      */
-    unsigned char full_set;
+    bool full_set;
 
     /**
      * Other sets of runes that can be used.
@@ -490,7 +513,7 @@ typedef struct Optimizer_Filters {
      * effect. It can take as many as rune sets are, but optimization times
      * increase exponencially for every set.
      */
-    unsigned char optional_sets[DIFFERENT_SETS];
+    uint_fast8_t optional_sets[DIFFERENT_SETS];
 
     /**
      * Stats allowed in the even slots.
@@ -505,7 +528,7 @@ typedef struct Optimizer_Filters {
      * and 'acc'. As many as the user want can be passed, and repeated ones will
      * be ignored.
      */
-    unsigned char stats[DIFFERENT_STATS];
+    uint_fast8_t stats[DIFFERENT_STATS];
 
     /**
      * Min stats for the unit.
@@ -532,15 +555,6 @@ typedef struct Optimizer_Filters {
      */
     Stats *min_stats;
 
-    /**
-     * Option to format the optput for the GUI.
-     *
-     * This is not intended to be used by the user. It changes the output format
-     * to be consumed by the GUI. To set it, the parameter -g or --gui must be
-     * passed (it takes no options).
-     */
-    unsigned char gui;
-
     /**
      * Flag to use only unassigned runes.
      *
@@ -552,7 +566,7 @@ typedef struct Optimizer_Filters {
      * The parameter to set this option is -o or --storage, and it takes no
      * options.
      */
-    unsigned char storage;
+    bool storage;
 
     /**
      * List of teams to ignore during optimization.
@@ -573,7 +587,7 @@ typedef struct Optimizer_Filters {
      * This is not a configurable option, but a handy counter. The default value
      * is 0, and will be set when {@link excluded_teams} is populated.
      */
-    unsigned char total_excluded_teams;
+    uint_fast8_t total_excluded_teams;
 
     /**
      * List of units to ignore during optimization.
@@ -596,7 +610,7 @@ typedef struct Optimizer_Filters {
      * This is not a configurable option, but a handy counter. The default value
      * is 0, and will be set when {@link excluded_units} is populated.
      */
-    unsigned char total_excluded_units;
+    uint_fast8_t total_excluded_units;
 
     /**
      * Option to enable broken sets.
@@ -606,18 +620,7 @@ typedef struct Optimizer_Filters {
      * {@link FALSE}. To set it, use the parameter -k or --broken (it takes no
      * options).
      */
-    unsigned char broken_sets;
-
-    /**
-     * Number of thread to be used during optimization.
-     *
-     * Calculation of rune combinations can be done in separateed threads. Each
-     * thread shares the same amount of work as the others, so the more threads,
-     * the faster the optimzation is! The default value is 1, and can be set
-     * passing the parameter -b or --threads, followed by a numeric value. No
-     * more than 8 threads can be used.
-     */
-    unsigned char threads;
+    bool broken_sets;
 
     /**
      * Weights for each stat.
@@ -633,11 +636,6 @@ typedef struct Optimizer_Filters {
      */
     Stat_Ponderation ponderation;
 
-    /**
-     * Output mode for the optimization data.
-     */
-    unsigned char output_mode;
-
 } Optimizer_Filters;
 
 /**
@@ -648,13 +646,6 @@ typedef struct Optimizer_Filters {
  */
 typedef struct Optimizer_Data {
 
-    /**
-     * Numeric ID of the thread.
-     *
-     * Usually, the spawing order.
-     */
-    unsigned int thread_id;
-
     /**
      * List of runes.
      *
@@ -668,7 +659,7 @@ typedef struct Optimizer_Data {
      *
      * Indicates the size of each {@link runes}. Index 0 is not used.
      */
-    unsigned int count[RUNE_SLOTS + 1];
+    uint_fast16_t count[RUNE_SLOTS + 1];
 
     /**
      * List of results.
@@ -682,7 +673,7 @@ typedef struct Optimizer_Data {
      *
      * The total number of results the thread has found.
      */
-    unsigned int total_results;
+    uint_fast16_t total_results;
 
     /**
      * The options passed to the optimizer.     *
@@ -709,14 +700,14 @@ typedef struct Optimizer_Data {
      * Not just for this thread, but the sum of combinations to test by every
      * thread.
      */
-    unsigned long long max_combinations;
+    uint_fast64_t max_combinations;
 
     /**
      * Tested combinations.
      *
      * Number of tested combinations at any given point in time.
      */
-    unsigned long long tested_combinations;
+    //uint_fast64_t tested_combinations;
 
     /**
      * Set count calculated from the options passed to the optimizer.
@@ -739,8 +730,50 @@ typedef struct Optimizer_Data {
      * YYYY-MM-DDTHH:MM:SS
      */
     char start_time[20];
+
+    /**
+     * Mutex for multithreading.
+     */
+    mtx_t mutex;
 } Optimizer_Data;
 
+/**
+ * Optimization progress indicator.
+ *
+ * Contains usefull information to display progress.
+ */
+typedef struct Progress{
+    /**
+     * Percentage indicator.
+     *
+     * Indicates the optimization progress, in a scale from 0 to 100 (rounded
+     * down).
+     */
+    uint_fast8_t percent;
+
+    /**
+     * Total combinations to check.
+     *
+     * Set before starting the optimization, it won't change during the whole
+     * process.
+     */
+    uint_fast64_t total;
+
+    /**
+     * Number of combinations tested at any given point. It's not exactly real
+     * time, the threads update it every {@link PROGRESS_TICK}, but it's a
+     * reliable indicator for a progress bar.
+     */
+    uint_fast64_t tested;
+
+    /**
+     * Number of results found at any given time.
+     *
+     * Updated in real time, the number is always exact.
+     */
+    uint_fast16_t results;
+} Progress;
+
 /**
  * Starts the optimization process.
  *

+ 30 - 10
src/cli/runeoptimizer/optimize/output/output.c

@@ -426,23 +426,23 @@ static int print_human(
             int quality = sqlite3_column_int(stmt_rune, 4);
             if (color == TRUE){
                 switch (quality){
-                    case 1: strcpy(tmp, "\033[30m"); break;
-                    case 2: strcpy(tmp, "\033[32m"); break;
-                    case 3: strcpy(tmp, "\033[36m"); break;
-                    case 4: strcpy(tmp, "\033[35m"); break;
-                    case 5: strcpy(tmp, "\033[33m"); break;
-                    default: strcpy(tmp, "\033[30m");
+                    case 1: strcpy(tmp2, "\033[30m"); break;
+                    case 2: strcpy(tmp2, "\033[32m"); break;
+                    case 3: strcpy(tmp2, "\033[36m"); break;
+                    case 4: strcpy(tmp2, "\033[35m"); break;
+                    case 5: strcpy(tmp2, "\033[33m"); break;
+                    default: strcpy(tmp2, "\033[30m");
                 }
             }
-            else strcpy(tmp, "");
+            else strcpy(tmp2, "");
 
             sprintf(
-              tmp, "%s%-9s", tmp, SET_NAMES[sqlite3_column_int(stmt_rune, 2)]
+              tmp, "%s%-9s", tmp2, SET_NAMES[sqlite3_column_int(stmt_rune, 2)]
             );
             if (color == TRUE) strcat(tmp, "\033[39m");
-            sprintf(tmp, "%s +%2d", tmp, sqlite3_column_int(stmt_rune, 3));
+            sprintf(tmp2, "%s +%2d", tmp, sqlite3_column_int(stmt_rune, 3));
             strcat(lines[0], "|");
-            strcat(lines[0], tmp);
+            strcat(lines[0], tmp2);
 
             // Row 1, rune ID
             strcat(lines[1], "| #");
@@ -641,3 +641,23 @@ static int print_json(Optimizer_Data *data, FILE *target, unsigned int max){
     fprintf(target, "]}\n");
     return total_printed;
 }
+
+extern void output_progress_human(Progress *progress){
+    printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b");
+    printf(
+      "[%3d%%] Results found: %5u",
+      progress->percent,
+      progress->results
+    );
+    fflush(stdout);
+    return;
+}
+
+extern void output_progress_json(Progress *progress){
+    printf(
+      "{\"progress\":%u,\"tested\":%llu,\"total\":%llu,\"results\":%u}\n",
+      progress->percent, progress->tested, progress->total, progress->results
+    );
+    fflush(stdout);
+    return;
+}

+ 4 - 0
src/cli/runeoptimizer/optimize/output/output.h

@@ -121,5 +121,9 @@ extern int output_screen(Optimizer_Data *data);
  */
 extern int output_json(Optimizer_Data *data);
 
+extern void output_progress_human(Progress *progress);
+
+extern void output_progress_json(Progress *progress);
+
 
 

+ 3 - 3
src/cli/runeoptimizer/runeoptimizer.c

@@ -214,7 +214,7 @@ const int STAT_ROLL_MAX[DIFFERENT_STATS][RUNE_MAX_STARS + 1] = {
   {-1, 2, 3, 4, 5, 7, 8} // ACC
 };
 
-extern unsigned int calculate_ehp(unsigned int hp, unsigned short def){
+extern uint_fast32_t calculate_ehp(uint_fast32_t hp, uint_fast16_t def){
     // Sorry, but this is the formula, and I don't understand it either.
     // Take it or leave it.
     unsigned int ehp = ceil(
@@ -224,8 +224,8 @@ extern unsigned int calculate_ehp(unsigned int hp, unsigned short def){
     return ehp;
 }
 
-extern unsigned short calculate_dmg(
-  unsigned short atk, unsigned short crr, unsigned short crd
+extern uint_fast16_t calculate_dmg(
+  uint_fast16_t atk, uint_fast8_t crr, uint_fast16_t crd
 ){
     float crr_capped = (float) crr;
     if (crr_capped > CRR_CAP){

+ 45 - 44
src/cli/runeoptimizer/runeoptimizer.h

@@ -27,6 +27,7 @@
 #pragma once
 
 #include <sqlite3.h>
+#include <stdint.h>
 
 /**
  * Application version.
@@ -416,7 +417,7 @@ typedef struct Unit {
      * The Unit HP, at it's current level, without counting runes, artifacts,
      * towers...
      */
-    unsigned int base_hp;
+    uint_fast32_t base_hp;
 
     /**
      * The unit base ATK.
@@ -424,7 +425,7 @@ typedef struct Unit {
      * The Unit ATK, at it's current level, without counting runes, artifacts,
      * towers...
      */
-    unsigned short base_atk;
+    uint_fast16_t base_atk;
 
     /**
      * The unit base DEF.
@@ -432,7 +433,7 @@ typedef struct Unit {
      * The Unit DEF, at it's current level, without counting runes, artifacts,
      * towers...
      */
-    unsigned short base_def;
+    uint_fast16_t base_def;
 
     /**
      * The unit base SPD.
@@ -440,7 +441,7 @@ typedef struct Unit {
      * The Unit SPD, at it's current level, without counting runes, artifacts,
      * towers...
      */
-    unsigned short base_spd;
+    uint_fast16_t base_spd;
 
     /**
      * The unit base CRR.
@@ -448,7 +449,7 @@ typedef struct Unit {
      * The Unit CRR, at it's current level, without counting runes, artifacts,
      * towers...
      */
-    unsigned short base_crr;
+    uint_fast8_t base_crr;
 
     /**
      * The unit base CRD.
@@ -456,7 +457,7 @@ typedef struct Unit {
      * The Unit CRD, at it's current level, without counting runes, artifacts,
      * towers...
      */
-    unsigned short base_crd;
+    uint_fast16_t base_crd;
 
     /**
      * The unit base RES.
@@ -464,7 +465,7 @@ typedef struct Unit {
      * The Unit RES, at it's current level, without counting runes, artifacts,
      * towers...
      */
-    unsigned short base_res;
+    uint_fast8_t base_res;
 
     /**
      * The unit base ACC.
@@ -472,7 +473,7 @@ typedef struct Unit {
      * The Unit ACC, at it's current level, without counting runes, artifacts,
      * towers...
      */
-    unsigned short base_acc;
+    uint_fast8_t base_acc;
 
     /**
      * The unit base EHP.
@@ -480,7 +481,7 @@ typedef struct Unit {
      * The Unit EHP, at it's current level, without counting runes, artifacts,
      * towers... See {@link calculate_ehp} for more details bout EHP.
      */
-    unsigned int base_ehp;
+    uint_fast32_t base_ehp;
 
     /**
      * The unit base DMG.
@@ -488,7 +489,7 @@ typedef struct Unit {
      * The Unit DMG, at it's current level, without counting runes, artifacts,
      * towers...See {@link calculate_ehp} for more details bout EHP.
      */
-    unsigned short base_dmg;
+    uint_fast16_t base_dmg;
 
     /**
      * The unit base HP.
@@ -496,7 +497,7 @@ typedef struct Unit {
      * The Unit HP, at it's current level, counting equiped runes, but not
      * artifacts or towers.
      */
-    unsigned int current_hp;
+    uint_fast32_t current_hp;
 
     /**
      * The unit base ATK.
@@ -504,7 +505,7 @@ typedef struct Unit {
      * The Unit ATK, at it's current level, counting equiped runes, but not
      * artifacts or towers.
      */
-    unsigned short current_atk;
+    uint_fast16_t current_atk;
 
     /**
      * The unit base DEF.
@@ -512,7 +513,7 @@ typedef struct Unit {
      * The Unit DEF, at it's current level, counting equiped runes, but not
      * artifacts or towers.
      */
-    unsigned short current_def;
+    uint_fast16_t current_def;
 
     /**
      * The unit base SPD.
@@ -520,7 +521,7 @@ typedef struct Unit {
      * The Unit SPD, at it's current level, counting equiped runes, but not
      * artifacts or towers.
      */
-    unsigned short current_spd;
+    uint_fast16_t current_spd;
 
     /**
      * The unit base CRR.
@@ -528,7 +529,7 @@ typedef struct Unit {
      * The Unit CRR, at it's current level, counting equiped runes, but not
      * artifacts or towers.
      */
-    unsigned short current_crr;
+    uint_fast8_t current_crr;
 
     /**
      * The unit base CRD.
@@ -536,7 +537,7 @@ typedef struct Unit {
      * The Unit CRD, at it's current level, counting equiped runes, but not
      * artifacts or towers.
      */
-    unsigned short current_crd;
+    uint_fast16_t current_crd;
 
     /**
      * The unit base RES.
@@ -544,7 +545,7 @@ typedef struct Unit {
      * The Unit RES, at it's current level, counting equiped runes, but not
      * artifacts or towers.
      */
-    unsigned short current_res;
+    uint_fast8_t current_res;
 
     /**
      * The unit base ACC.
@@ -552,7 +553,7 @@ typedef struct Unit {
      * The Unit ACC, at it's current level, counting equiped runes, but not
      * artifacts or towers.
      */
-    unsigned short current_acc;
+    uint_fast8_t current_acc;
 
     /**
      * The unit base EHP.
@@ -560,7 +561,7 @@ typedef struct Unit {
      * The Unit EHP, at it's current level, counting equiped runes, but not
      * artifacts or towers.See {@link calculate_ehp} for more details bout EHP.
      */
-    unsigned int current_ehp;
+    uint_fast32_t current_ehp;
 
     /**
      * The unit base DMG.
@@ -568,7 +569,7 @@ typedef struct Unit {
      * The Unit DMG, at it's current level, counting equiped runes, but not
      * artifacts or towers. See {@link calculate_ehp} for more details bout EHP.
      */
-    unsigned short current_dmg;
+    uint_fast16_t current_dmg;
 } Unit;
 
 /**
@@ -584,147 +585,147 @@ typedef struct Rune_Set_Count {
      *
      * Number of Energy runes.
      */
-    unsigned short energy;
+    uint_fast8_t energy;
 
     /**
      * Guard count.
      *
      * Number of Guard runes.
      */
-    unsigned short guard;
+    uint_fast8_t guard;
 
     /**
      * Swift count.
      *
      * Number of Swift runes.
      */
-    unsigned short swift;
+    uint_fast8_t swift;
 
     /**
      * Blade count.
      *
      * Number of Blade runes.
      */
-    unsigned short blade;
+    uint_fast8_t blade;
 
     /**
      * Rage count.
      *
      * Number of Rage runes.
      */
-    unsigned short rage;
+    uint_fast8_t rage;
 
     /**
      * Focus count.
      *
      * Number of Focus runes.
      */
-    unsigned short focus;
+    uint_fast8_t focus;
 
     /**
      * Endure count.
      *
      * Number of Endure runes.
      */
-    unsigned short endure;
+    uint_fast8_t endure;
 
     /**
      * Fatal count.
      *
      * Number of Fatal runes.
      */
-    unsigned short fatal;
+    uint_fast8_t fatal;
 
     /**
      * Despair count.
      *
      * Number of Despair runes.
      */
-    unsigned short despair;
+    uint_fast8_t despair;
 
     /**
      * Vampire count.
      *
      * Number of Vampire runes.
      */
-    unsigned short vampire;
+    uint_fast8_t vampire;
 
     /**
      * Violent count.
      *
      * Number of Violent runes.
      */
-    unsigned short violent;
+    uint_fast8_t violent;
 
     /**
      * Nemesis count.
      *
      * Number of Nemesis runes.
      */
-    unsigned short nemesis;
+    uint_fast8_t nemesis;
 
     /**
      * Will count.
      *
      * Number of Will runes.
      */
-    unsigned short will;
+    uint_fast8_t will;
 
     /**
      * Shield count.
      *
      * Number of Shield runes.
      */
-    unsigned short shield;
+    uint_fast8_t shield;
 
     /**
      * Revenge count.
      *
      * Number of Revenge runes.
      */
-    unsigned short revenge;
+    uint_fast8_t revenge;
 
     /**
      * Destroy count.
      *
      * Number of Destroy runes.
      */
-    unsigned short destroy;
+    uint_fast8_t destroy;
 
     /**
      * Fight count.
      *
      * Number of Fight runes.
      */
-    unsigned short fight;
+    uint_fast8_t fight;
 
     /**
      * Determination count.
      *
      * Number of Determination runes.
      */
-    unsigned short determination;
+    uint_fast8_t determination;
 
     /**
      * Enhance count.
      *
      * Number of Enhance runes.
      */
-    unsigned short enhance;
+    uint_fast8_t enhance;
 
     /**
      * Accuracy count.
      *
      * Number of Accuracy runes.
      */
-    unsigned short accuracy;
+    uint_fast8_t accuracy;
 
     /**
      * Tolerance count.
      *
      * Number of Tolerance runes.
      */
-    unsigned short tolerance;
+    uint_fast8_t tolerance;
 } Rune_Set_Count;
 
 /**
@@ -808,7 +809,7 @@ extern char db_location[DB_PATH_LEN];
  * @param[in] def DEF stat.
  * @return Calculated EHP.
  */
-extern unsigned int calculate_ehp(unsigned int hp, unsigned short def);
+extern uint_fast32_t calculate_ehp(uint_fast32_t hp, uint_fast16_t def);
 
 /**
  * Calculates damage.
@@ -822,8 +823,8 @@ extern unsigned int calculate_ehp(unsigned int hp, unsigned short def);
  * @param[in] crd CRD stat.
  * @return Calculated DMG.
  */
-extern unsigned short calculate_dmg(
-  unsigned short atk, unsigned short crr, unsigned short crd
+extern uint_fast16_t calculate_dmg(
+    uint_fast16_t atk, uint_fast8_t crr, uint_fast16_t crd
 );
 
 /**

+ 0 - 2
src/gui/runeoptimizer_gui/entity/StatSet.py

@@ -168,8 +168,6 @@ class StatSet():
     @acc.setter
     def acc(self, value):
         value = int(value)
-        if value < 15:
-            value = 15;
         if value > 85:
             value = 85
         self._acc = value

+ 36 - 11
src/gui/runeoptimizer_gui/gui/PanelOptimizer.py

@@ -175,7 +175,7 @@ class PanelOptimizer(wx.Panel):
           min_stats_button_sizer, 0, wx.ALIGN_CENTRE|wx.TOP|wx.BOTTOM, 5
         )
         self._control_left_sizer.Add(min_stats_sizer, 0, wx.EXPAND)
-        self._control_left_sizer.Add(teams_sizer, 1, wx.EXPAND|wx.ALIGN_CENTRE)
+        self._control_left_sizer.Add(teams_sizer, 1, wx.EXPAND)
         # After adding the unit selector
         #unit_sizer.Add(unit_info_sizer, 0)
         #unit_sizer.Add(unit_runes_sizer, 0)
@@ -433,15 +433,14 @@ class PanelOptimizer(wx.Panel):
             cores = 8;
         self._thread_slider.SetValue(cores)
         thread_sizer.Add(thread_label, 0, wx.ALIGN_CENTRE)
-        thread_sizer.Add(self._thread_slider, 1, wx.EXPAND|wx.ALIGN_CENTRE)
+        thread_sizer.Add(self._thread_slider, 1, wx.EXPAND)
 
         # Progress bar
         self._progress_gauge = wx.Gauge(
           parent=self, range=100, style=wx.GA_HORIZONTAL
         )
         self._progress_sizer.Add(
-          self._progress_gauge, 1,
-          wx.EXPAND|wx.ALIGN_CENTRE_VERTICAL|wx.BOTTOM, 20
+          self._progress_gauge, 1, wx.EXPAND|wx.BOTTOM, 20
         )
         self._progress_label = wx.StaticText(self, size=(300, 25))
         self._progress_sizer.Add(
@@ -893,13 +892,37 @@ class PanelOptimizer(wx.Panel):
                 # Get only the last line
                 if text.rfind("\n") != -1:
                     text = text[text.rfind("\n") + 1:]
+                data = json.loads(
+                  text, object_hook=lambda d: SimpleNamespace(**d)
+                )
+                current = data.tested
+                total = data.total
+                if current > 2000000000000: # Two trillions
+                    current = '{:.1f}T'.format(current / 1000000000000)
+                elif current > 2000000000: # Two billions
+                    current = '{:.1f}B'.format(current / 1000000000)
+                elif current > 2000000: # Two millions
+                    current = '{:.1f}M'.format(current / 1000000)
+                elif current > 2000: # Two tousands
+                    current = '{:.1f}k'.format(current / 1000)
+                else:
+                    current = str(current)
+                if total > 2000000000000: # Two trillions
+                    total = '{:.1f}T'.format(total / 1000000000000)
+                elif total > 2000000000: # Two billions
+                    total = '{:.1f}B'.format(total / 1000000000)
+                elif total > 2000000: # Two millions
+                    total = '{:.1f}M'.format(total / 1000000)
+                elif total > 2000: # Two thousands
+                    total = '{:.1f}k'.format(total / 1000)
+                else:
+                    total = str(total)
+                self._progress_label.SetLabel(
+                  str(data.progress) + "%   " + current + " / " + total
+                  + "\nResults found: " + str(data.results)
+                )
+                self._progress_gauge.SetValue(min(100, int(data.progress)))
 
-                self._progress_label.SetLabel(text)
-                for character in '%()/':
-                    text = text.replace(character, '')
-                text = text.strip().split()[1:]
-                if text[0].isnumeric() and int(text[0]) <= 100:
-                    self._progress_gauge.SetValue(int(text[0]))
         else:
             self._timer.Stop()
 
@@ -1020,7 +1043,9 @@ class PanelOptimizer(wx.Panel):
             The event that triggered the call (default is None).
         """
         for i in range(0, 10):
-           self._min_stat_slid_list[i].SetValue(self.unitStats[i])
+           self._min_stat_slid_list[i].SetValue(
+             self._selected_unit.stats.values()[i]
+           )
            self._min_stat_text_list[i].SetValue(
              str(self._min_stat_slid_list[i].GetValue())
         )

+ 11 - 6
src/gui/runeoptimizer_gui/gui/PanelResults.py

@@ -282,7 +282,7 @@ class PanelResults(wx.Panel):
         self._location_label_list = []
         for i in range(0, 6):
             rune_box_list.append(
-              wx.StaticBoxSizer(wx.VERTICAL, self, label="Slot " + str(i))
+              wx.StaticBoxSizer(wx.VERTICAL, self, label="Slot " + str(i + 1))
             )
             self._set_label_list.append(
               wx.StaticText(rune_box_list[i].GetStaticBox(), size=(130, 15))
@@ -335,7 +335,12 @@ class PanelResults(wx.Panel):
             self._main_label_list[i].SetFont(monospace_font_bold)
             self._eff_label_list[i].SetFont(monospace_font_italic)
             self._location_label_list[i].SetFont(monospace_font_bold)
-            runes_sizer.Add(rune_box_list[i], 0)
+        runes_sizer.Add(rune_box_list[5], 0)
+        runes_sizer.Add(rune_box_list[0], 0)
+        runes_sizer.Add(rune_box_list[1], 0)
+        runes_sizer.Add(rune_box_list[4], 0)
+        runes_sizer.Add(rune_box_list[3], 0)
+        runes_sizer.Add(rune_box_list[2], 0)
 
         # By default, hide everything TODO
         self._top_sizer.ShowItems(False)
@@ -459,7 +464,7 @@ class PanelResults(wx.Panel):
             WHERE unit = ?
           """,
           (
-            self.unitId,
+            self._unit.id,
           )
         )
 
@@ -472,7 +477,7 @@ class PanelResults(wx.Panel):
               id IN (SELECT unit FROM runes WHERE id IN (?, ?, ?, ?, ?, ?))
           """,
           (
-            self.unitId,
+            self._unit.id,
             self._results.results[self._selected_result_index].runes[0],
             self._results.results[self._selected_result_index].runes[1],
             self._results.results[self._selected_result_index].runes[2],
@@ -489,7 +494,7 @@ class PanelResults(wx.Panel):
             WHERE id IN (?, ?, ?, ?, ?, ?)
           """,
           (
-            self.unitId,
+            self._unit.id,
             self._results.results[self._selected_result_index].runes[0],
             self._results.results[self._selected_result_index].runes[1],
             self._results.results[self._selected_result_index].runes[2],
@@ -502,7 +507,7 @@ class PanelResults(wx.Panel):
         # TODO: Recalculate all modified units stats from the database
         print("Applied!")
         self._recalculte_stats_of_modified_units()
-        reload_units()
+        data.reload_units()
         self._unit = units[self._unit.id]
         self._update_base_and_current_stats()
         print("All recalculated!")

+ 6 - 3
src/gui/runeoptimizer_gui/gui/PanelTeams.py

@@ -369,7 +369,8 @@ class PanelTeams(wx.Panel):
               ["team", "add_unit", self._selected_team.id, unit_id]
             )
             sel_index = self._all_unit_list.GetNextSelected(sel_index)
-        data.reload_teams();
+        data.reload_units()
+        data.reload_teams()
         self._selected_team = data.TEAMS[self._selected_team.id]
         self._populate_team_unit_list()
         self._populate_unit_list()
@@ -389,12 +390,13 @@ class PanelTeams(wx.Panel):
         """
         sel_index = self._team_unit_list.GetFirstSelected()
         while (sel_index != -1):
-            unit_id = self._team_unit_list.GetItem(sel_index, 2).getText()
+            unit_id = self._team_unit_list.GetItem(sel_index, 1).GetText()
             ret = runeoptimizer.run(
               ["team", "remove_unit", self._selected_team.id, unit_id]
             )
             sel_index = self._team_unit_list.GetNextSelected(sel_index)
-        data.reload_teams();
+        data.reload_units()
+        data.reload_teams()
         self._selected_team = data.TEAMS[self._selected_team.id]
         self._populate_team_unit_list()
         self._populate_unit_list()
@@ -535,6 +537,7 @@ class PanelTeams(wx.Panel):
                 ret = runeoptimizer.run(
                   ["team", "update", self._selected_team.id, new_priority]
                 )
+                data.reload_units()
                 data.reload_teams()
                 self._populate_team_list(event=None)
                 self.GetParent().panel_units.populate_unit_list(event=None)