Sfoglia il codice sorgente

Header file and Makefile. Code optimization and cleanup. Error codes.

Iñigo Valentin 4 anni fa
parent
commit
5e08453ad0
4 ha cambiato i file con 441 aggiunte e 325 eliminazioni
  1. 2 0
      .gitignore
  2. 11 0
      src/Makefile
  3. 216 325
      src/RuneOptimizer.c
  4. 212 0
      src/RuneOptimizer.h

+ 2 - 0
.gitignore

@@ -1 +1,3 @@
 data.sqlite
+RuneOptimizer
+src/util

+ 11 - 0
src/Makefile

@@ -0,0 +1,11 @@
+CC=gcc
+CFLAGS=
+LIBS=-lsqlite3
+OUT=../RuneOptimizer
+compile :
+	@$(CC) RuneOptimizer.c $(LIBS) -o $(OUT)
+	@echo Compiled executable $(OUT)
+#RuneOptimizer: RuneOptimizer.c
+#	$(CC) -o makeRO RuneOptimizer.c
+#install :
+#	gcc RuneOptimizer.c -lsqlite3 -o makeRO

+ 216 - 325
src/RuneOptimizer.c

@@ -19,174 +19,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sqlite3.h>
-
-// Rune set definitions
-#define ENERGY 1
-#define GUARD 2
-#define SWIFT 3
-#define BLADE 4
-#define RAGE 5
-#define FOCUS 6
-#define ENDURE 7
-#define FATAL 8
-#define DESPAIR 10
-#define VAMPIRE 11
-#define VIOLENT 13
-#define NEMESIS 14
-#define WILL 15
-#define SHIELD 16
-#define REVENGE 17
-#define DESTROY 18
-#define FIGHT 19
-#define DETERMINATION 20
-#define ENHANCE 21
-#define ACCURACY 22
-#define TOLERANCE 23
-
-// Rune stat definitions
-#define HP_FLAT 1
-#define HP_PERCENT 2
-#define ATK_FLAT 3
-#define ATK_PERCENT 4
-#define DEF_FLAT 5
-#define DEF_PERCENT 6
-#define SPD 8
-#define CRR 9
-#define CRD 10
-#define RES 11
-#define ACC 12
-
-struct Unit {
-    unsigned char id[12];
-    unsigned char name[50];
-    unsigned short base_hp;
-    unsigned short base_atk;
-    unsigned short base_def;
-    unsigned short base_spd;
-    unsigned short base_crr;
-    unsigned short base_crd;
-    unsigned short base_acc;
-    unsigned short base_res;
-    unsigned short current_hp;
-    unsigned short current_atk;
-    unsigned short current_def;
-    unsigned short current_spd;
-    unsigned short current_crr;
-    unsigned short current_crd;
-    unsigned short current_acc;
-    unsigned short current_res;
-};
-
-struct Rune {
-    unsigned char id[12];
-    unsigned char slot;
-    unsigned char set;
-    unsigned char unit[12];
-    unsigned char hp_percent;
-    unsigned char atk_percent;
-    unsigned char def_percent;
-    unsigned short hp_flat;
-    unsigned char atk_flat;
-    unsigned char def_flat;
-    unsigned char spd;
-    unsigned char crr;
-    unsigned char crd;
-    unsigned char acc;
-    unsigned char res;
-};
-
-struct Stats {
-    unsigned short hp;
-    unsigned short atk;
-    unsigned short def;
-    unsigned short spd;
-    unsigned short crr;
-    unsigned short crd;
-    unsigned short acc;
-    unsigned short res;
-};
-
-struct Rune_Set_Count {
-    unsigned short energy;
-    unsigned short guard;
-    unsigned short swift;
-    unsigned short blade;
-    unsigned short rage;
-    unsigned short focus;
-    unsigned short endure;
-    unsigned short fatal;
-    unsigned short despair;
-    unsigned short vampire;
-    unsigned short violent;
-    unsigned short nemesis;
-    unsigned short will;
-    unsigned short shield;
-    unsigned short revenge;
-    unsigned short destroy;
-    unsigned short fight;
-    unsigned short determination;
-    unsigned short enhance;
-    unsigned short accuracy;
-    unsigned short tolerance;
-};
-typedef struct Result {
-    unsigned char rune_ids[6][12];
-    signed int rating;
-    struct Stats stats;
-} Result;
-
-char stat_names[][13] = {
-  "NULL", "HP_FLAT", "HP",  "ATK_FLAT", "ATK", "DEF_FLAT",
-  "DEF",  "NULL",    "SPD", "CRR",      "CRD", "RES",      "ACC"
-};
-char set_names[][24] = {
-  "NULL",    "ENERGY",        "GUARD",   "SWIFT",    "BLADE",   "RAGE",
-  "FOCUS",   "ENDURE",        "FATAL",   "NULL",     "DESPAIR", "VAMPIRE",
-  "VIOLENT", "NEMESIS",       "WILL",    "SHIELD",   "REVENGE", "DESTROY",
-  "FIGHT",   "DETERMINATION", "ENHANCE", "ACCURACY", "TOLERANCE"
-};
-/**
- * Displays the help text.
- */
-void show_help();
-
-/**
- * Starts the optimization process.
- *
- * @param argc Argument count.
- * @param argv Argument list.
- * @return 0 on success, other on error.
- */
-int optimize(int argc, char *argv[]);
-
-/**
- * Connects to the database.
- *
- * Initializes the global db.
- *
- * @return 0 on success, other on error.
- */
-int open_databse();
-
-/**
- * Sorts the result array.
- *
- * Sorts them by rating.
- *
- * @param results List of results to sort.
- * @param total Number of results.
- */
-void sort_results(Result results[1000], int total);
-
-/**
- * @var Stores the database error statuses.
- */
-int db_status;
-
-/**
- * @var Database connection.
- */
-sqlite3 *db;
+#include "RuneOptimizer.h"
 
 /**
  * Starts the program.
@@ -195,51 +28,53 @@ sqlite3 *db;
  *
  * @param argc Argument count.
  * @param argv Argument list.
- * @return 0 on success, other on error.
+ * @return SUCCESS on success, other on error.
  */
 int main(int argc, char *argv[]){
-    // Parse the arguments
-   if (argc < 2){
-        fprintf(stderr, "No options specified\n");
-        return 0;
+    // Parse the arguments to find the command (index 1)
+
+    // If no arguments, end here
+    if (argc < 2){
+        fprintf(stderr, "No command specified\n");
+        return ERROR_INPUT_NO_COMMAND;
     }
-    int command = -1;
+
+    // Update command, TODO
     if (strcmp(argv[1], "update") == 0){
         fprintf(stderr, "Updating is still unimplemented\n");
-        return 0;
+        return UNIMPLEMENTED;
     }
+
+    // Optimize command, call the function with all the arguments to be
+    // processed there.
     else if (strcmp(argv[1], "optimize") == 0){
-        //printf("Starting optimization...\n");
         int result = optimize(argc, argv);
-        if (result != 0){
-            //fprintf(stderr, "Errors in optimization: %d\n", result);
-            return result;
-        }
+        return result;
     }
+
+    // Help command. Call and return
     else if (strcmp(argv[1], "help") == 0){
         show_help();
-        return 0;
+        return SUCCESS;
     }
+
+    // Any other command is an error
     else{
-        fprintf(stderr, "Invalid option specified: %s\n", argv[1]);
-        return 0;
+        fprintf(stderr, "Invalid command specified: %s\n", argv[1]);
+        return ERROR_INPUT_INVALID_COMMAND;
     }
-
-   return 0;
 }
 
 int open_database(){
+    // The database location and name is hardcoded in the same directory.
     int db_status = sqlite3_open("../data.sqlite", &db);
-
     if (db_status != SQLITE_OK) {
-
         fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
         sqlite3_close(db);
-
-        return 1;
+        return ERROR_DB_CANT_OPEN;
     }
     else{
-        return 0;
+        return SUCCESS;
     }
 }
 
@@ -286,16 +121,17 @@ void show_help(){
     printf("                                    Only the selected sets will be included, so this option is\n");
     printf("                                    mandatory. Accepted values  the rune net names, lowercase.\n");
     printf("                                    Values must be comma-separated, and up to 3 can be included.\n");
+    return;
 }
 
 int optimize(int argc, char *argv[]){
     // Get unit identifier
     if (argc < 3){
         fprintf(stderr, "No unit specified for optimization\n");
-        return -1;
+        return ERROR_INPUT_NO_UNIT;
     }
 
-    // Get other data
+    // Initialize values for the data to be read form arguments.
     int requested_level = 0;
     char requested_level_column[9] = "current_";
     int sets[3] = {0, 0, 0};
@@ -313,9 +149,10 @@ int optimize(int argc, char *argv[]){
     min_stats.res = 1;
     min_stats.acc = 1;
 
-    // Loop argumens
+    // Loop command line arguments
     for (int i = 3; i < argc; i ++){
-        //printf("ARGUMENT %d: %s\n", i, argv[i]);
+
+        // Rune level arguments
         if (strcmp("--level", argv[i]) == 0 || strcmp("-l", argv[i]) == 0){
             if (i < argc - 1){
                 if (strcmp("current", argv[i + 1]) == 0){
@@ -331,17 +168,28 @@ int optimize(int argc, char *argv[]){
                     strcpy(requested_level_column, "lv15_");
                 }
                 else{
-                    fprintf(stderr, "Invalid option for argument %s: %s\nValid options are 'current', '12' or '15'\n", argv[i], argv[i + 1]);
-                    return -9;
+                    fprintf(
+                      stderr,
+                      "Invalid option for argument %s: %s\n"
+                      "Valid options are 'current', '12' or '15'\n",
+                      argv[i], argv[i + 1]
+                    );
+                    return ERROR_INPUT_INVALID_LEVEL;
                 }
                 // Advance one position in argument reading
                 i ++;
             }
             else{
-                fprintf(stderr, "Argument %s requires a value.\nValid options are 'current', '12' or '15'\n", argv[i]);
-                return -9;
+                fprintf(
+                  stderr,
+                  "Argument %s requires a value.\n"
+                  "Valid options are 'current', '12' or '15'\n", argv[i]
+                );
+                return ERROR_INPUT_NO_LEVEL;
             }
         }
+
+        // Rune sets argument
         else if (strcmp("--sets", argv[i]) == 0 || strcmp("-e", argv[i]) == 0){
             if (i < argc - 1){
                 // Separate string by commas
@@ -417,7 +265,7 @@ int optimize(int argc, char *argv[]){
                     }
                     else{
                         fprintf(stderr, "Unknown rune set %s.\n", token);
-                        return -10;
+                        return ERROR_INPUT_INVALID_SET;
                     }
                     token = strtok(NULL, ",");
                     j ++;
@@ -426,10 +274,16 @@ int optimize(int argc, char *argv[]){
                 i ++;
             }
             else{
-                fprintf(stderr, "Argument %s requires a list of values separated by commas.\n", argv[i]);
-                return -9;
+                fprintf(
+                  stderr,
+                  "Argument %s requires a list of values separated by commas.\n",
+                  argv[i]
+                );
+                return ERROR_INPUT_NO_SET;
             }
         }
+
+        // Accepted stats arguments
         else if (strcmp("--stats", argv[i]) == 0 || strcmp("-t", argv[i]) == 0){
             if (i < argc - 1){
                 // Separate string by commas
@@ -475,7 +329,7 @@ int optimize(int argc, char *argv[]){
                     }
                     else{
                         fprintf(stderr, "Unknown rune stat %s.\n", token);
-                        return -10;
+                        return ERROR_INPUT_INVALID_STAT;
                     }
                     token = strtok(NULL, ",");
                     j ++;
@@ -485,9 +339,11 @@ int optimize(int argc, char *argv[]){
             }
             else{
                 fprintf(stderr, "Argument %s requires a list of values separated by commas.\n", argv[i]);
-                return -9;
+                return ERROR_INPUT_NO_STAT;
             }
         }
+
+        // Minimum stats arguments.
         else if (strcmp("--min-hp", argv[i]) == 0 || strcmp("-h", argv[i]) == 0){
             if (i < argc - 1){
                 unsigned short stat_tmp = atoi(argv[i + 1]);
@@ -500,7 +356,7 @@ int optimize(int argc, char *argv[]){
             }
             else{
                 fprintf(stderr, "Argument %s requires a numeric value.\n", argv[i]);
-                return -9;
+                return ERROR_INPUT_NO_HP;
             }
         }
         else if (strcmp("--min-atk", argv[i]) == 0 || strcmp("-a", argv[i]) == 0){
@@ -515,7 +371,7 @@ int optimize(int argc, char *argv[]){
             }
             else{
                 fprintf(stderr, "Argument %s requires a numeric value.\n", argv[i]);
-                return -9;
+                return ERROR_INPUT_NO_ATK;
             }
         }
         else if (strcmp("--min-def", argv[i]) == 0 || strcmp("-d", argv[i]) == 0){
@@ -530,7 +386,7 @@ int optimize(int argc, char *argv[]){
             }
             else{
                 fprintf(stderr, "Argument %s requires a numeric value.\n", argv[i]);
-                return -9;
+                return ERROR_INPUT_NO_DEF;
             }
         }
         else if (strcmp("--min-spd", argv[i]) == 0 || strcmp("-s", argv[i]) == 0){
@@ -545,7 +401,7 @@ int optimize(int argc, char *argv[]){
             }
             else{
                 fprintf(stderr, "Argument %s requires a numeric value.\n", argv[i]);
-                return -9;
+                return ERROR_INPUT_NO_SPD;
             }
         }
         else if (strcmp("--min-crr", argv[i]) == 0 || strcmp("-c", argv[i]) == 0){
@@ -563,7 +419,7 @@ int optimize(int argc, char *argv[]){
             }
             else{
                 fprintf(stderr, "Argument %s requires a numeric value.\n", argv[i]);
-                return -9;
+                return ERROR_INPUT_NO_CRR;
             }
         }
         else if (strcmp("--min-crd", argv[i]) == 0 || strcmp("-d", argv[i]) == 0){
@@ -578,7 +434,7 @@ int optimize(int argc, char *argv[]){
             }
             else{
                 fprintf(stderr, "Argument %s requires a numeric value.\n", argv[i]);
-                return -9;
+                return ERROR_INPUT_NO_CRD;
             }
         }
         else if (strcmp("--min-res", argv[i]) == 0 || strcmp("-r", argv[i]) == 0){
@@ -596,7 +452,7 @@ int optimize(int argc, char *argv[]){
             }
             else{
                 fprintf(stderr, "Argument %s requires a numeric value.\n", argv[i]);
-                return -9;
+                return ERROR_INPUT_NO_RES;
             }
         }
         else if (strcmp("--min-acc", argv[i]) == 0 || strcmp("-f", argv[i]) == 0){
@@ -614,63 +470,65 @@ int optimize(int argc, char *argv[]){
             }
             else{
                 fprintf(stderr, "Argument %s requires a numeric value.\n", argv[i]);
-                return -9;
+                return ERROR_INPUT_NO_ACC;
             }
         }
 
     }
 
-    // Read the unit from the databse
-    open_database();
+    // Now we can open the database.
+    if (SUCCESS != open_database()){
+        return ERROR_DB_CANT_OPEN;
+    }
 
+    // Read the unit from the databse
     sqlite3_stmt *res;
     char *sql = "SELECT "
-      "id, name, "
-      "base_hp, base_atk, base_def, base_spd, base_crr, base_crd, base_res, base_acc, "
-      "current_hp, current_atk, current_def, current_spd, current_crr, current_crd, current_res, current_acc "
+      "id, name, base_hp, base_atk, base_def, base_spd, base_crr, base_crd, "
+      "base_res, base_acc, current_hp, current_atk, current_def, current_spd, "
+      "current_crr, current_crd, current_res, current_acc "
       "FROM units WHERE id = ? OR name = ?";
     db_status = sqlite3_prepare_v2(db, sql, -1, &res, 0);
-
-    if (db_status == SQLITE_OK) {
-        sqlite3_bind_text(res, 1, argv[2], strlen(argv[2]), NULL);
-        sqlite3_bind_text(res, 2, argv[2], strlen(argv[2]), NULL);
-    }
-    else {
-        fprintf(stderr, "Failed to execute statement to select unit: %s\n", sqlite3_errmsg(db));
-        return -2;
+    if (db_status != SQLITE_OK) {
+        fprintf(
+          stderr,
+          "Failed to execute statement to select unit: %s\n",
+          sqlite3_errmsg(db)
+        );
+        return ERROR_DB_UNIT;
     }
+    sqlite3_bind_text(res, 1, argv[2], strlen(argv[2]), NULL);
+    sqlite3_bind_text(res, 2, argv[2], strlen(argv[2]), NULL);
 
+    // Fetch just one line
     int step = sqlite3_step(res);
 
+    // Create a unit structure with the red data.
     struct Unit unit;
-
-
-    if (step == SQLITE_ROW) {
-        strcpy(unit.id, sqlite3_column_text(res, 0));
-        strcpy(unit.name, sqlite3_column_text(res, 1));
-        unit.base_hp = sqlite3_column_int(res, 2);
-        unit.base_atk = sqlite3_column_int(res, 3);
-        unit.base_def = sqlite3_column_int(res, 4);
-        unit.base_spd = sqlite3_column_int(res, 5);
-        unit.base_crr = sqlite3_column_int(res, 6);
-        unit.base_crd = sqlite3_column_int(res, 7);
-        unit.base_res = sqlite3_column_int(res, 8);
-        unit.base_acc = sqlite3_column_int(res, 9);
-        unit.current_hp = sqlite3_column_int(res, 10);
-        unit.current_atk = sqlite3_column_int(res, 11);
-        unit.current_def = sqlite3_column_int(res, 12);
-        unit.current_spd = sqlite3_column_int(res, 13);
-        unit.current_crr = sqlite3_column_int(res, 14);
-        unit.current_crd = sqlite3_column_int(res, 15);
-        unit.current_res = sqlite3_column_int(res, 16);
-        unit.current_acc = sqlite3_column_int(res, 17);
-
-    }
-    else{
+    if (step != SQLITE_ROW) {
         fprintf(stderr, "Unit not found: %s\n", sqlite3_errmsg(db));
-        return -3;
+        return ERROR_DB_UNIT_NOF_FOUND;
     }
-
+    strcpy(unit.id, sqlite3_column_text(res, 0));
+    strcpy(unit.name, sqlite3_column_text(res, 1));
+    unit.base_hp = sqlite3_column_int(res, 2);
+    unit.base_atk = sqlite3_column_int(res, 3);
+    unit.base_def = sqlite3_column_int(res, 4);
+    unit.base_spd = sqlite3_column_int(res, 5);
+    unit.base_crr = sqlite3_column_int(res, 6);
+    unit.base_crd = sqlite3_column_int(res, 7);
+    unit.base_res = sqlite3_column_int(res, 8);
+    unit.base_acc = sqlite3_column_int(res, 9);
+    unit.current_hp = sqlite3_column_int(res, 10);
+    unit.current_atk = sqlite3_column_int(res, 11);
+    unit.current_def = sqlite3_column_int(res, 12);
+    unit.current_spd = sqlite3_column_int(res, 13);
+    unit.current_crr = sqlite3_column_int(res, 14);
+    unit.current_crd = sqlite3_column_int(res, 15);
+    unit.current_res = sqlite3_column_int(res, 16);
+    unit.current_acc = sqlite3_column_int(res, 17);
+
+    // Min stats that have the value 1 get overriden by the current stats.
     if (min_stats.hp == 1){
         min_stats.hp = unit.current_hp;
     }
@@ -809,15 +667,11 @@ int optimize(int argc, char *argv[]){
         }
     }
     printf("\n");
-
     if (total_requested_runes != 6){
         fprintf(stderr, "Invalid rune set combination.\n");
-        return -3;
+        return ERROR_INPUT_INCOMPLETE_SETS;
     }
 
-    // TODO
-    //return 0;
-
     // Create queries for each slot
     char query_odd[2500] = "SELECT id, unit, type, ";
     strcat(query_odd, requested_level_column);
@@ -862,12 +716,6 @@ int optimize(int argc, char *argv[]){
     strcat(query_odd, "-1) ");
     // TODO: Add team stuff
 
-
-    //printf("- QUERY ODD: %s\n", query_odd);
-
-
-
-
     char query_even[1500] = "SELECT id, unit, type, ";
     strcat(query_even, requested_level_column);
     strcat(query_even, "hp_flat, ");
@@ -907,7 +755,6 @@ int optimize(int argc, char *argv[]){
         strcat(query_even, ", ");
     }
     strcat(query_even, "-1) AND main_stat IN (");
-
     for (int i = 0; i < 12; i ++){
         if (requested_stats[i] != 0){
             sprintf(stat_set, "%d", requested_stats[i]);
@@ -918,59 +765,60 @@ int optimize(int argc, char *argv[]){
             break;
         }
     }
-
     strcat(query_even, "-1) ");
     // TODO: Add team stuff
 
+    // Get data for all the runes from the database.
+    // Im using a 7 position array, and the index 0 is ignored. This is
+    // is because I REALLY NEED to use 1-indexes to match rune slots.
     sqlite3_stmt *stmt_runes[7];
+    // Im assumming 600 runes per slot is a safe estimate. I hope it doesn't
+    // come back to bite me.
     struct Rune runes[7][600];
     db_status = sqlite3_prepare_v2(db, query_odd, -1, &stmt_runes[1], 0);
     if (db_status != SQLITE_OK) {
         printf("Error getting runes for slot 1: %s\n", sqlite3_errmsg(db));
-        return -7;
+        return ERROR_DB_RUNES_SLOT;
     }
     sqlite3_bind_int(stmt_runes[1], 1, 1);
     db_status = sqlite3_prepare_v2(db, query_odd, -1, &stmt_runes[3], 0);
     if (db_status != SQLITE_OK) {
         printf("Error getting runes for slot 3: %s\n", sqlite3_errmsg(db));
-        return -7;
+        return ERROR_DB_RUNES_SLOT;
     }
     sqlite3_bind_int(stmt_runes[3], 1, 3);
     db_status = sqlite3_prepare_v2(db, query_odd, -1, &stmt_runes[5], 0);
     if (db_status != SQLITE_OK) {
         printf("Error getting runes for slot 5: %s\n", sqlite3_errmsg(db));
-        return -7;
+        return ERROR_DB_RUNES_SLOT;
     }
     sqlite3_bind_int(stmt_runes[5], 1, 5);
     db_status = sqlite3_prepare_v2(db, query_odd, -1, &stmt_runes[2], 0);
     if (db_status != SQLITE_OK) {
         printf("Error getting runes for slot 2: %s\n", sqlite3_errmsg(db));
-        return -7;
+        return ERROR_DB_RUNES_SLOT;
     }
     sqlite3_bind_int(stmt_runes[2], 1, 2);
     db_status = sqlite3_prepare_v2(db, query_odd, -1, &stmt_runes[4], 0);
     if (db_status != SQLITE_OK) {
         printf("Error getting runes for slot 4: %s\n", sqlite3_errmsg(db));
-        return -7;
+        return ERROR_DB_RUNES_SLOT;
     }
     sqlite3_bind_int(stmt_runes[4], 1, 4);
     db_status = sqlite3_prepare_v2(db, query_odd, -1, &stmt_runes[6], 0);
     if (db_status != SQLITE_OK) {
         printf("Error getting runes for slot 6: %s\n", sqlite3_errmsg(db));
-        return -7;
+        return ERROR_DB_RUNES_SLOT;
     }
     sqlite3_bind_int(stmt_runes[6], 1, 6);
 
-
     // Get and populate all the runes
     int rune_count[7];
     for (int i = 1; i < 7; i ++){
         int j = 0;
         while (1 == 1){
             int status = sqlite3_step(stmt_runes[i]);
-            //printf("STATUS:  %d   ", status);
             if (status == SQLITE_ROW){
-                //printf("  - (%d) Slot %d, Rune ID %s\n", j, i, sqlite3_column_text(stmt_runes[i], 0));
                 strcpy(runes[i][j].id, sqlite3_column_text(stmt_runes[i], 0));
                 strcpy(runes[i][j].unit, sqlite3_column_text(stmt_runes[i], 1));
                 runes[i][j].set = sqlite3_column_int(stmt_runes[i], 2);
@@ -994,10 +842,11 @@ int optimize(int argc, char *argv[]){
         rune_count[i] = j;
     }
 
+    // If any slot doesn't have matching runes, we can stop now.
     for (int i = 1; i < 7; i ++){
         if (rune_count[i] == 0){
-            fprintf(stderr, "\n  - No availbale runes for slot %d\n", i);
-            return 1;
+            printf("\n  - No availbale runes for slot %d\n", i);
+            return SUCCESS;
         }
     }
     printf("\n");
@@ -1009,7 +858,27 @@ int optimize(int argc, char *argv[]){
       rune_count[5] *
       rune_count[6];
 
-    // Print summary
+    // Print a nice summary before starting the long optimization.
+    // This is an output example:
+    //
+    // -----------------------------------    Requested rune sets:
+    // | Lushen                          |       RAGE  BLADE
+    // | ID: 7223811472                  |
+    // -----------------------------------    Accepted stats:
+    // | STAT | BASE   | CURR.  | MIN.   |       ATK  CRR  CRD
+    // -----------------------------------
+    // | HP:  |  9225  | 12262  | 10000  |    Considering runes at level 15
+    // | ATK: |   900  |  2482  |  2482  |
+    // | DEF: |   461  |   703  |   703  |    Runes considered by slot:
+    // | SPD: |   103  |   159  |   159  |      1: 27    2: 39    3: 25
+    // | CRR: |    15% |    79% |    79% |      4: 35    5: 24    6: 32
+    // | CRD: |    50% |   188% |   188% |
+    // | RES: |    15% |    35% |    35% |    Total combinations: 707616000
+    // | ACC: |     0% |    11% |    10% |
+    // -----------------------------------
+    //
+    // Im breaking the 80-characters-line rule here, but only 'cause I'd like
+    // to remain sane.
     printf(" -----------------------------------    Requested rune sets:\n");
     printf(" | %*s |      ", -31, unit.name);
     for (int i = 0; i < 3; i ++){
@@ -1050,8 +919,9 @@ int optimize(int argc, char *argv[]){
     printf(" | ACC: | %*d\% | %*d\% | %*d\% |\n", 5, unit.base_acc, 5, unit.current_acc, 5, min_stats.acc);
     printf(" -----------------------------------\n");
 
-    // 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
     printf("\n\n__Optimization progress___________________________\n", max_combinations);
+    // Initialize arrys and some counters
     int index[7] = {0, 0, 0, 0, 0, 0};
     unsigned long tested_combinations = 0;
     unsigned long valid_sets = 0;
@@ -1066,8 +936,13 @@ int optimize(int argc, char *argv[]){
         index[6] < rune_count[6]
     ){
         // Progress bar, 50 characters to 100%
-        if ((tested_combinations + 1) % (unsigned long)(max_combinations / 50) == 0){
+        if (
+          (tested_combinations + 1) %
+          (unsigned long)(max_combinations / 50)
+          == 0
+        ){
             printf("#");
+            // Line buffered! need to flush after every char.
             fflush(stdout);
         }
 
@@ -1203,8 +1078,10 @@ int optimize(int argc, char *argv[]){
                 stats.atk += runes[i][index[i]].atk_flat;
                 stats.def += runes[i][index[i]].def_flat;
                 stats.hp += unit.base_hp * runes[i][index[i]].hp_percent / 100;
-                stats.atk += unit.base_hp * runes[i][index[i]].atk_percent / 100;
-                stats.def += unit.base_hp * runes[i][index[i]].def_percent / 100;
+                stats.atk +=
+                  unit.base_hp * runes[i][index[i]].atk_percent / 100;
+                stats.def +=
+                  unit.base_hp * runes[i][index[i]].def_percent / 100;
                 stats.spd += runes[i][index[i]].spd;
                 stats.crr += runes[i][index[i]].crr;
                 stats.crd += runes[i][index[i]].crd;
@@ -1223,11 +1100,13 @@ int optimize(int argc, char *argv[]){
                 stats.acc >= min_stats.acc
             ){
                 // This is a valid sets and all stats are above the minimum.
-                // Create a result.
+                // Create a result with rune indexes, stats, and rating.
                 for (int i = 1; i < 7; i ++){
-                    strcpy(results[result_count].rune_ids[i - 1], runes[i][index[i]].id);
+                    strcpy(
+                      results[result_count].rune_ids[i - 1],
+                      runes[i][index[i]].id
+                    );
                 }
-
                 results[result_count].stats.hp = stats.hp;
                 results[result_count].stats.atk = stats.atk;
                 results[result_count].stats.def = stats.def;
@@ -1237,6 +1116,8 @@ int optimize(int argc, char *argv[]){
                 results[result_count].stats.res = stats.res;
                 results[result_count].stats.acc = stats.acc;
 
+                // One rating point per stat increase over current stats.
+                // Save for HP, with takes 15 for a raing point
                 results[result_count].rating = 0;
                 results[result_count].rating += ((stats.hp - unit.current_hp) / 15);
                 results[result_count].rating += (stats.atk - unit.current_atk);
@@ -1250,7 +1131,7 @@ int optimize(int argc, char *argv[]){
             }
         }
 
-        // Loop control
+        // Loop control. Rotate the reels 'right to left'
         tested_combinations ++;
         index[6] ++;
         if (index[6] == rune_count[6]){
@@ -1274,23 +1155,21 @@ int optimize(int argc, char *argv[]){
             index[1] ++;
         }
 
-        // TODO: DEBUG force exit with some results
-        if (result_count > 0){
-            break;
-        }
+        //DEBUG: force exit with some results TODO
+        //if (result_count > 1){
+        //    break;
+        //}
     }
     printf("\n");
 
     if (result_count > 0){
+        // Yay! Some combinations matched te criteria.
         printf("\n\n%d results found\n", result_count);
         // Sort results
         sort_results(results, result_count);
-        //for (int i = 0; i < result_count; i ++){
-        //    printf("  - Sorted option %d rating: %d\n", i, results[i].rating);
-        //}
-        printf("\nPresenting best option:\n\n");
 
         // Preview the best option
+        printf("\nPresenting best option:\n\n");
         printf(" --------------------------\n");
         printf(" | %*s |\n", -22, unit.name);
         printf(" | ID: %*s |\n", -18, unit.id);
@@ -1307,6 +1186,40 @@ int optimize(int argc, char *argv[]){
         printf(" | ACC: | %*d\% | %*d\% |\n", 5, unit.current_acc, 5, results[0].stats.acc);
         printf(" --------------------------\n");
 
+        // This bit may be hard to follow.
+        // I'm populating  8 lines of text with data, to display the runes in
+        // a nice table format.
+        //
+        // This is an output exaple:
+        //
+        // ------------------------------  ------------------------------  ------------------------------
+        // |6|RAGE         | 23285581330|  |1|BLADE        | 22677809846|  |2|BLADE        | 21432847749|
+        // ------------------------------  ------------------------------  ------------------------------
+        // | Storage       |        +12 |  | Storage       |        +12 |  | Perna         |        +15 |
+        // ------------------------------  ------------------------------  ------------------------------
+        // | ACC           48           |  | ATK_FLAT     118           |  | SPD           42           |
+        // | RES            6           |  |                            |  |                            |
+        // | ATK_FLAT      19           |  | RES           14           |  | CRD           16           |
+        // | CRR           12           |  | ACC            8           |  | CRR           10           |
+        // | HP            14           |  | HP_FLAT      580           |  | ATK            7 + 3       |
+        // | CRD           14           |  | CRR           10           |  | DEF            7 + 3       |
+        // ------------------------------  ------------------------------  ------------------------------
+        //
+        // ------------------------------  ------------------------------  ------------------------------
+        // |5|RAGE         | 26260912967|  |4|RAGE         | 27947761086|  |3|RAGE         | 27654723287|
+        // ------------------------------  ------------------------------  ------------------------------
+        // | Lushen        |        +15 |  | Lushen        |        +15 |  | Covenant      |        +12 |
+        // ------------------------------  ------------------------------  ------------------------------
+        // | HP_FLAT     2448           |  | CRD           80           |  | DEF_FLAT     118           |
+        // |                            |  |                            |  | HP_FLAT      167           |
+        // | CRR           16           |  | CRR            6           |  | RES            8           |
+        // | SPD            6 + 2       |  | RES           11           |  | DEF           11           |
+        // | CRD           11           |  | SPD           18           |  | CRD           18           |
+        // | ATK            6 + 5       |  | ATK            8           |  | CRR           12           |
+        // ------------------------------  ------------------------------  ------------------------------
+        //
+        // Again, Im breaking the 80-characters-line rule.
+
         char present[8][130];
         strcpy(present[0], "");
         strcpy(present[1], "");
@@ -1332,15 +1245,13 @@ int optimize(int argc, char *argv[]){
             }
             else {
                 fprintf(stderr, "Failed to execute statement to select rune: %s\n", sqlite3_errmsg(db));
-                return -2;
+                return ERROR_DB_RUNES_RESULT;
             }
 
             int step = sqlite3_step(rune_res);
             if (step == SQLITE_ROW) {
                 char tmp[64];
                 strcpy(tmp, "");
-                //strcpy(unit.id, sqlite3_column_text(rune_res, 0));
-                //printf("Rune %d (%s)\n", sqlite3_column_int(rune_res, 1), sqlite3_column_text(rune_res, 0));
                 strcat(present[0], "|");
                 sprintf(tmp, "%*d", 1, sqlite3_column_int(rune_res, 1));
                 strcat(present[0], tmp);
@@ -1378,7 +1289,7 @@ int optimize(int argc, char *argv[]){
                 }
                 else {
                     fprintf(stderr, "Failed to execute statement to select rune stats: %s\n", sqlite3_errmsg(db));
-                    return -2;
+                    return ERROR_DB_STATS_RESULT;
                 }
 
                 int curr_slot = -1;
@@ -1409,25 +1320,27 @@ int optimize(int argc, char *argv[]){
                             strcat(present[curr_slot + 3], tmp);
                             strcat(present[curr_slot + 3], "|  ");
                         }
-                        /*else{
-                            sprintf(tmp, "%d-EMPTY           ", curr_slot);
-                            strcat(present[curr_slot + 3], tmp);
-                            //curr_slot ++;
-                        }*/
+
                         curr_slot ++;
                     }
                     else{
                         break;
                     }
                 }
+                sqlite3_finalize(stats_res);
             }
             else{
                 fprintf(stderr, "BAD ROW: %s\n", sqlite3_errmsg(db));
             }
             sqlite3_finalize(rune_res);
-            //printf(present[0]);
 
             // Loop control
+            // It's weird, but I wanna present the runes in the same format the
+            // game does:
+            //
+            // 6 1 2
+            // 5 4 3
+            // After slots 2 and 3, the generated strings are written.
             if (i == 6){
                 //printf("-6->1-");
                 i = 1;
@@ -1502,34 +1415,12 @@ int optimize(int argc, char *argv[]){
         printf("No results found\n");
     }
 
-    printf("ENDING\n");
-
-    sqlite3_finalize(res);
     sqlite3_close(db);
-
-    /*
-
-    rc = sqlite3_exec(db, sql, callback, 0, &err_msg);
-
-    if (rc != SQLITE_OK ) {
-
-        ffprintf(stderr, "Failed to select data\n");
-        ffprintf(stderr, "SQL error: %s\n", err_msg);
-
-        sqlite3_free(err_msg);
-        sqlite3_close(db);
-
-        return 1;
-    }
-
-    sqlite3_close(db);
-
-    */
-    return 0;
+    return SUCCESS;
 }
 
 void sort_results(Result results[1000], int total){
-    // Bubble sort
+    // Bubble sort, by descending rating.
     int i, j;
     Result temp;
 

+ 212 - 0
src/RuneOptimizer.h

@@ -0,0 +1,212 @@
+/*
+ * This file is part of RuneOptimizer.
+ *
+ * RuneOptimizer is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * RuneOptimizer is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * RuneOptimizer. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+// Rune set definitions
+#define ENERGY 1
+#define GUARD 2
+#define SWIFT 3
+#define BLADE 4
+#define RAGE 5
+#define FOCUS 6
+#define ENDURE 7
+#define FATAL 8
+#define DESPAIR 10
+#define VAMPIRE 11
+#define VIOLENT 13
+#define NEMESIS 14
+#define WILL 15
+#define SHIELD 16
+#define REVENGE 17
+#define DESTROY 18
+#define FIGHT 19
+#define DETERMINATION 20
+#define ENHANCE 21
+#define ACCURACY 22
+#define TOLERANCE 23
+
+// Rune stat definitions
+#define HP_FLAT 1
+#define HP_PERCENT 2
+#define ATK_FLAT 3
+#define ATK_PERCENT 4
+#define DEF_FLAT 5
+#define DEF_PERCENT 6
+#define SPD 8
+#define CRR 9
+#define CRD 10
+#define RES 11
+#define ACC 12
+
+struct Unit {
+    unsigned char id[12];
+    unsigned char name[50];
+    unsigned short base_hp;
+    unsigned short base_atk;
+    unsigned short base_def;
+    unsigned short base_spd;
+    unsigned short base_crr;
+    unsigned short base_crd;
+    unsigned short base_acc;
+    unsigned short base_res;
+    unsigned short current_hp;
+    unsigned short current_atk;
+    unsigned short current_def;
+    unsigned short current_spd;
+    unsigned short current_crr;
+    unsigned short current_crd;
+    unsigned short current_acc;
+    unsigned short current_res;
+};
+
+struct Rune {
+    unsigned char id[12];
+    unsigned char slot;
+    unsigned char set;
+    unsigned char unit[12];
+    unsigned char hp_percent;
+    unsigned char atk_percent;
+    unsigned char def_percent;
+    unsigned short hp_flat;
+    unsigned char atk_flat;
+    unsigned char def_flat;
+    unsigned char spd;
+    unsigned char crr;
+    unsigned char crd;
+    unsigned char acc;
+    unsigned char res;
+};
+
+struct Stats {
+    unsigned short hp;
+    unsigned short atk;
+    unsigned short def;
+    unsigned short spd;
+    unsigned short crr;
+    unsigned short crd;
+    unsigned short acc;
+    unsigned short res;
+};
+
+struct Rune_Set_Count {
+    unsigned short energy;
+    unsigned short guard;
+    unsigned short swift;
+    unsigned short blade;
+    unsigned short rage;
+    unsigned short focus;
+    unsigned short endure;
+    unsigned short fatal;
+    unsigned short despair;
+    unsigned short vampire;
+    unsigned short violent;
+    unsigned short nemesis;
+    unsigned short will;
+    unsigned short shield;
+    unsigned short revenge;
+    unsigned short destroy;
+    unsigned short fight;
+    unsigned short determination;
+    unsigned short enhance;
+    unsigned short accuracy;
+    unsigned short tolerance;
+};
+typedef struct Result {
+    unsigned char rune_ids[6][12];
+    signed int rating;
+    struct Stats stats;
+} Result;
+
+char stat_names[][13] = {
+  "NULL", "HP_FLAT", "HP",  "ATK_FLAT", "ATK", "DEF_FLAT",
+  "DEF",  "NULL",    "SPD", "CRR",      "CRD", "RES",      "ACC"
+};
+char set_names[][24] = {
+  "NULL",    "ENERGY",        "GUARD",   "SWIFT",    "BLADE",   "RAGE",
+  "FOCUS",   "ENDURE",        "FATAL",   "NULL",     "DESPAIR", "VAMPIRE",
+  "VIOLENT", "NEMESIS",       "WILL",    "SHIELD",   "REVENGE", "DESTROY",
+  "FIGHT",   "DETERMINATION", "ENHANCE", "ACCURACY", "TOLERANCE"
+};
+/**
+ * Displays the help text.
+ */
+void show_help();
+
+/**
+ * Starts the optimization process.
+ *
+ * @param argc Argument count.
+ * @param argv Argument list.
+ * @return 0 on success, other on error.
+ */
+int optimize(int argc, char *argv[]);
+
+/**
+ * Connects to the database.
+ *
+ * Initializes the global db.
+ *
+ * @return 0 on success, other on error.
+ */
+int open_databse();
+
+/**
+ * Sorts the result array.
+ *
+ * Sorts them by rating.
+ *
+ * @param results List of results to sort.
+ * @param total Number of results.
+ */
+void sort_results(Result results[1000], int total);
+
+/**
+ * @var Stores the database error statuses.
+ */
+int db_status;
+
+/**
+ * @var Database connection.
+ */
+sqlite3 *db;
+
+// Error code definitions
+#define SUCCESS 0
+#define UNIMPLEMENTED 1
+#define ERROR_INPUT_NO_COMMAND 100
+#define ERROR_INPUT_INVALID_COMMAND 101
+#define ERROR_INPUT_NO_UNIT 102
+#define ERROR_INPUT_INVALID_LEVEL 103
+#define ERROR_INPUT_NO_LEVEL 104
+#define ERROR_INPUT_INVALID_SET 105
+#define ERROR_INPUT_NO_SET 106
+#define ERROR_INPUT_INVALID_STAT 107
+#define ERROR_INPUT_NO_STAT 108
+#define ERROR_INPUT_NO_HP 109
+#define ERROR_INPUT_NO_ATK 110
+#define ERROR_INPUT_NO_DEF 111
+#define ERROR_INPUT_NO_SPD 112
+#define ERROR_INPUT_NO_CRR 113
+#define ERROR_INPUT_NO_CRD 114
+#define ERROR_INPUT_NO_RES 115
+#define ERROR_INPUT_NO_ACC 116
+#define ERROR_INPUT_INCOMPLETE_SETS 117
+#define ERROR_DB_CANT_OPEN 200
+#define ERROR_DB_UNIT 201
+#define ERROR_DB_UNIT_NOF_FOUND 202
+#define ERROR_DB_RUNES_SLOT 203
+#define ERROR_DB_RUNES_RESULT 204
+#define ERROR_DB_STATS_RESULT 205