Переглянути джерело

Several fixes and improvements:
Database location is now fixed for the program and the GUI.
Fixed memory leak.
Result rating are now more consistent and accurate.
Output generation for the GUI is now faster and more efficient.
Result view implemented in GUI.

Iñigo Valentin 4 роки тому
батько
коміт
a36b06d03f

+ 3 - 1
src/Makefile

@@ -1,9 +1,11 @@
 CC=gcc
 CFLAGS=
+OPTS_DEBUG=-Wall -g -fsanitize=address
+OPTS=
 LIBS=-lsqlite3 -lm
 OUT=../RuneOptimizer
 compile :
-	@$(CC) RuneOptimizer/RuneOptimizer.c $(LIBS) -o $(OUT)
+	@$(CC) RuneOptimizer/RuneOptimizer.c $(LIBS) $(OPTS) -o $(OUT)
 	@echo Compiled executable $(OUT)
 #RuneOptimizer: RuneOptimizer.c
 #	$(CC) -o makeRO RuneOptimizer.c

+ 15 - 0
src/RuneOptimizer/RuneOptimizer.c

@@ -49,6 +49,21 @@ int main(int argc, char *argv[]){
         return ERROR_INPUT_NO_COMMAND;
     }
 
+    // Set the global database location
+    int last_path_separator = -1;
+    for(int i = 0; i < strlen(argv[0]); i++){
+        if(argv[0][i] == '/'){
+            last_path_separator = i + 1;
+        }
+    }
+    if (last_path_separator != -1){
+        strncpy(db_location, argv[0], last_path_separator);
+    }
+    else{
+        strcpy(db_location, "");
+    }
+    strcat(db_location, DB_NAME);
+
     // Update command, TODO
     if (strcmp(argv[1], "update") == 0){
         fprintf(stderr, "Updating is still unimplemented\n");

+ 3 - 1
src/RuneOptimizer/RuneOptimizer.h

@@ -15,7 +15,9 @@
  * RuneOptimizer. If not, see <https://www.gnu.org/licenses/>.
  */
 
-#define DB_PATH "../data.sqlite"
+#define DB_NAME "data.sqlite"
+
+char db_location[256];
 
 /**
  * Starts the program.

+ 75 - 47
src/RuneOptimizer/optimize/optimize.c

@@ -526,7 +526,7 @@ int parse_optimization_arguments(
  */
 int retrieveUnit(char id[64], struct Unit *unit){
     sqlite3 *db;
-    if (SUCCESS != sqlite3_open_v2(DB_PATH, &db, SQLITE_OPEN_READWRITE, NULL)){
+    if (SUCCESS != sqlite3_open_v2(db_location, &db, SQLITE_OPEN_READWRITE, NULL)){
         fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
         sqlite3_close(db);
         return ERROR_DB_CANT_OPEN;
@@ -735,7 +735,7 @@ void createQueryForEvenSlots(
     if (level == 12) strcpy(column, "lv12_");
     else if (level == 15) strcpy(column, "lv15_");
     else strcpy(column, "current_");
-    strcpy(query, "SELECT id, unit, type, ");
+    strcpy(query, "SELECT id, unit, slot, type, ");
     strcat(query, column);
     strcat(query, "hp_flat, ");
     strcat(query, column);
@@ -837,7 +837,7 @@ void createQueryForOddSlots(
     if (level == 12) strcpy(column, "lv12_");
     else if (level == 15) strcpy(column, "lv15_");
     else strcpy(column, "current_");
-    strcpy(query, "SELECT id, unit, type, ");
+    strcpy(query, "SELECT id, unit, slot, type, ");
     strcat(query, column);
     strcat(query, "hp_flat, ");
     strcat(query, column);
@@ -929,7 +929,7 @@ int get_runes(
     char **query;
     sqlite3 *db;
     sqlite3_stmt *stmt_runes[7];
-    if (SUCCESS != sqlite3_open_v2(DB_PATH, &db, SQLITE_OPEN_READWRITE, NULL)){
+    if (SUCCESS != sqlite3_open_v2(db_location, &db, SQLITE_OPEN_READWRITE, NULL)){
         fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
         sqlite3_close(db);
         return ERROR_DB_CANT_OPEN;
@@ -957,18 +957,19 @@ int get_runes(
             if (status == SQLITE_ROW){
                 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);
-                runes[i][j].hp_flat = sqlite3_column_int(stmt_runes[i], 3);
-                runes[i][j].atk_flat = sqlite3_column_int(stmt_runes[i], 4);
-                runes[i][j].def_flat = sqlite3_column_int(stmt_runes[i], 5);
-                runes[i][j].hp_percent = sqlite3_column_int(stmt_runes[i], 6);
-                runes[i][j].atk_percent = sqlite3_column_int(stmt_runes[i], 7);
-                runes[i][j].def_percent = sqlite3_column_int(stmt_runes[i], 8);
-                runes[i][j].spd = sqlite3_column_int(stmt_runes[i], 9);
-                runes[i][j].crr = sqlite3_column_int(stmt_runes[i], 10);
-                runes[i][j].crd = sqlite3_column_int(stmt_runes[i], 11);
-                runes[i][j].res = sqlite3_column_int(stmt_runes[i], 12);
-                runes[i][j].acc = sqlite3_column_int(stmt_runes[i], 13);
+                runes[i][j].slot = sqlite3_column_int(stmt_runes[i], 2);
+                runes[i][j].set = sqlite3_column_int(stmt_runes[i], 3);
+                runes[i][j].hp_flat = sqlite3_column_int(stmt_runes[i], 4);
+                runes[i][j].atk_flat = sqlite3_column_int(stmt_runes[i], 5);
+                runes[i][j].def_flat = sqlite3_column_int(stmt_runes[i], 6);
+                runes[i][j].hp_percent = sqlite3_column_int(stmt_runes[i], 7);
+                runes[i][j].atk_percent = sqlite3_column_int(stmt_runes[i], 8);
+                runes[i][j].def_percent = sqlite3_column_int(stmt_runes[i], 9);
+                runes[i][j].spd = sqlite3_column_int(stmt_runes[i], 10);
+                runes[i][j].crr = sqlite3_column_int(stmt_runes[i], 11);
+                runes[i][j].crd = sqlite3_column_int(stmt_runes[i], 12);
+                runes[i][j].res = sqlite3_column_int(stmt_runes[i], 13);
+                runes[i][j].acc = sqlite3_column_int(stmt_runes[i], 14);
                 j ++;
             }
             else{
@@ -1138,7 +1139,7 @@ int present_option(struct Unit *unit, Result *result){
 
     // Retrieve the rune stats from the database
     sqlite3 *db;
-    if (SUCCESS != sqlite3_open_v2(DB_PATH, &db, SQLITE_OPEN_READWRITE, NULL)){
+    if (SUCCESS != sqlite3_open_v2(db_location, &db, SQLITE_OPEN_READWRITE, NULL)){
         fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
         sqlite3_close(db);
         return ERROR_DB_CANT_OPEN;
@@ -1319,13 +1320,15 @@ int present_option(struct Unit *unit, Result *result){
  */
 int gui_output(Result results[5000], int result_count){
     //Print for GUI
-    char tmp[1000000];
+    char tmp[300];
     strcpy(tmp, "");
-    char json[1000000] = "{\"results\":[";
-    for (int i = 0; i < result_count; i++){
+    char json[300];
+    printf("{\"result_count\":%d,\"results\":[\n", result_count);
+    int result_limit = 100;
+    for (int i = 0; i < result_count && i < result_limit; i++){
         //printf("RES LOOP %d\n", i);
-        sprintf(tmp, "{\"rating\":%d,", results[i].rating);
-        strcat(json, tmp);
+        sprintf(tmp, "{\"id\":%d,\"rating\":%d,", i, results[i].rating);
+        strcpy(json, tmp);
         sprintf(tmp, "\"hp\":%d,", results[i].stats.hp);
         strcat(json, tmp);
         sprintf(tmp, "\"atk\":%d,", results[i].stats.atk);
@@ -1358,13 +1361,26 @@ int gui_output(Result results[5000], int result_count){
         sprintf(tmp, "\"%s\"", results[i].rune_ids[5]);
         strcat(json, tmp);
         strcat(json, "]},");
+
+        // If last result, remove last comma
+        if (i == result_count - 1 || i == result_limit - 1){
+            json[strlen(json) - 1] = 0;
+        }
+
+        printf(json);
+        printf("\n");
     }
     // Remove last comma
-    json[strlen(json) - 1] = '\0';
+    //json[strlen(json) - 1] = '\0';
+    //free(*tmp);
 
     // End and print
-    strcat(json, "]}\0");
-    printf("%s", json);
+    printf("]}\n");
+    //printf("STRLEN JSON %d\n", strlen(json));
+    //printf("%s", json);
+    //free(*json);
+    //printf("\n\nEND\n\n");
+    return SUCCESS;
 }
 
 /**
@@ -1410,7 +1426,7 @@ int optimize(int argc, char *argv[]){
     min_stats.dmg = 1;
     char excluded_teams[64][64];
     char excluded_units[64][64];
-    for (int i = 0; i < 128; i++){
+    for (int i = 0; i < 64; i++){
         excluded_teams[i][0] = '\0';
         excluded_units[i][0] = '\0';
     }
@@ -1496,16 +1512,15 @@ int optimize(int argc, char *argv[]){
 
     // Now its time to loop all 6 'reels' of runes and try to match combos
     if (gui != 1){
-        printf(
-          "\n\n__Optimization progress___________________________\n",
-          max_combinations
-        );
+        printf("\n\n__Optimization progress___________________________\n");
     }
+
     // Initialize arrys and some counters
     int index[7] = {0, 0, 0, 0, 0, 0};
     unsigned long tested_combinations = 0;
     unsigned long valid_sets = 0;
     unsigned long result_count = 0;
+    struct Rune_Set_Count set_count;
     Result results[5000];
     while(
         index[1] < rune_count[1] &&
@@ -1529,7 +1544,6 @@ int optimize(int argc, char *argv[]){
         }
 
         // Calculate rune sets at current indexes.
-        struct Rune_Set_Count set_count;
         set_count.energy = 0;
         set_count.guard = 0;
         set_count.swift = 0;
@@ -1618,6 +1632,7 @@ int optimize(int argc, char *argv[]){
                     break;
             }
         }
+
         // Compare with requested sets
         if (
             set_count.energy >= requested_set_count.energy &&
@@ -1661,9 +1676,9 @@ int optimize(int argc, char *argv[]){
                 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;
+                  unit.base_atk * runes[i][index[i]].atk_percent / 100;
                 stats.def +=
-                  unit.base_hp * runes[i][index[i]].def_percent / 100;
+                  unit.base_def * 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;
@@ -1703,7 +1718,7 @@ int optimize(int argc, char *argv[]){
             // Cap cappable stats
             if (stats.crr > 100) stats.crr = 100;
             if (stats.res > 100) stats.res = 100;
-            if (stats.crr > 85) stats.acc = 100;
+            if (stats.crr > 85) stats.acc = 85;
 
             // Calculated stats
             stats.ehp = calculate_ehp(stats.hp, stats.def);
@@ -1741,18 +1756,31 @@ int optimize(int argc, char *argv[]){
                 results[result_count].stats.ehp = stats.ehp;
                 results[result_count].stats.dmg = stats.dmg;
 
-                // 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);
-                results[result_count].rating += (stats.def - unit.current_def);
-                results[result_count].rating += (stats.spd - unit.current_spd);
-                results[result_count].rating += (stats.crr - unit.current_crr);
-                results[result_count].rating += (stats.crd - unit.current_crd);
-                results[result_count].rating += (stats.res - unit.current_res);
-                results[result_count].rating += (stats.acc - unit.current_acc);
+                // Calculate rating, based on the difference between the
+                // stats with this set and the current stats.
+                // This is calculated accounting for the proportions between
+                // the (flat) stats for each main stat in a 6 star, level 15
+                // rune:
+                // HP: 1 rating point per 58.29 points
+                // ATK: 1 rating point per 1.50 points
+                // DEF: 1 rating point per 1.50 points
+                // SPD: 1 rating point per 1.00 points
+                // CRR: 1 rating point per 1.38 points
+                // CRD: 1 rating point per 1.90 points
+                // RES: 1 rating point per 1.52 points
+                // ACC: 1 rating point per 1.52 points
+                float rating = 0.0f;
+                rating += (float) ((int) stats.hp  - (int) unit.current_hp ) / 58.29f;
+                rating += (float) ((int) stats.atk - (int) unit.current_atk) /  1.50f;
+                rating += (float) ((int) stats.def - (int) unit.current_def) /  1.50f;
+                rating += (float) ((int) stats.spd - (int) unit.current_spd) /  1.00f;
+                rating += (float) ((int) stats.crr - (int) unit.current_crr) /  1.38f;
+                rating += (float) ((int) stats.crd - (int) unit.current_crd) /  1.90f;
+                rating += (float) ((int) stats.res - (int) unit.current_res) /  1.52f;
+                rating += (float) ((int) stats.acc - (int) unit.current_acc) /  1.52f;
+                results[result_count].rating = (int) rating;
+
+                // Account for found result
                 result_count ++;
             }
         }
@@ -1794,9 +1822,9 @@ int optimize(int argc, char *argv[]){
         if (gui != 1){
             printf("\n\n%d results found\n", result_count);
         }
+
         // Sort results
         sort_results(results, result_count);
-
         if (gui != 1){
             // Preview the best option
             present_option(&unit, &results[0]);

+ 1 - 1
src/RuneOptimizer/team/team.c

@@ -34,7 +34,7 @@ int list_teams(int argc, char *argv[]){
     }
     strcat(query, " ORDER BY priority DESC");
     sqlite3 *db;
-    if (SUCCESS != sqlite3_open_v2(DB_PATH, &db, SQLITE_OPEN_READWRITE, NULL)){
+    if (SUCCESS != sqlite3_open_v2(db_location, &db, SQLITE_OPEN_READWRITE, NULL)){
         fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
         sqlite3_close(db);
         return ERROR_DB_CANT_OPEN;

+ 59 - 40
src/RuneOptimizerGUI/classes/PanelOptimizer.py

@@ -169,48 +169,54 @@ class PanelOptimizer(wx.Panel):
         #Rune set list
         runeListBox = [
           wx.StaticBox(
-            self.unitInfo, label="Slot1:", id=wx.ID_ANY,
+            parent=self.unitInfo, label="Slot1:", id=wx.ID_ANY,
             pos=(265, 30), size=(80, 115)
           ),
           wx.StaticBox(
-            self.unitInfo, label="Slot2:", id=wx.ID_ANY,
+            parent=self.unitInfo, label="Slot2:", id=wx.ID_ANY,
             pos=(350, 30), size=(80, 115)
           ),
           wx.StaticBox(
-            self.unitInfo, label="Slot3:", id=wx.ID_ANY,
+            parent=self.unitInfo, label="Slot3:", id=wx.ID_ANY,
             pos=(350, 150), size=(80, 115)
           ),
           wx.StaticBox(
-            self.unitInfo, label="Slot4:", id=wx.ID_ANY,
+            parent=self.unitInfo, label="Slot4:", id=wx.ID_ANY,
             pos=(265, 150), size=(80, 115)
           ),
           wx.StaticBox(
-            self.unitInfo, label="Slot5:", id=wx.ID_ANY,
+            parent=self.unitInfo, label="Slot5:", id=wx.ID_ANY,
             pos=(180, 150), size=(80, 115)
           ),
           wx.StaticBox(
-            self.unitInfo, label="Slot6:", id=wx.ID_ANY,
+            parent=self.unitInfo, label="Slot6:", id=wx.ID_ANY,
             pos=(180, 30), size=(80, 115)
           )
         ]
         self.runeList = [
           wx.StaticText(
-            runeListBox[0], label="", id=wx.ID_ANY, pos=(0, 0), size=(80, 115)
+            parent=runeListBox[0],  id=wx.ID_ANY, label="",
+            pos=(0, 0), size=(80, 115)
           ),
           wx.StaticText(
-            runeListBox[1], label="", id=wx.ID_ANY, pos=(0, 0), size=(80, 115)
+            parent=runeListBox[1],  id=wx.ID_ANY, label="",
+            pos=(0, 0), size=(80, 115)
           ),
           wx.StaticText(
-            runeListBox[2], label="", id=wx.ID_ANY, pos=(0, 0), size=(80, 115)
+            parent=runeListBox[2],  id=wx.ID_ANY, label="",
+            pos=(0, 0), size=(80, 115)
           ),
           wx.StaticText(
-            runeListBox[3], label="", id=wx.ID_ANY, pos=(0, 0), size=(80, 115)
+            parent=runeListBox[3],  id=wx.ID_ANY, label="",
+            pos=(0, 0), size=(80, 115)
           ),
           wx.StaticText(
-            runeListBox[4], label="", id=wx.ID_ANY, pos=(0, 0), size=(80, 115)
+            parent=runeListBox[4],  id=wx.ID_ANY, label="",
+            pos=(0, 0), size=(80, 115)
           ),
           wx.StaticText(
-            runeListBox[5], label="", id=wx.ID_ANY, pos=(0, 0), size=(80, 115)
+            parent=runeListBox[5],  id=wx.ID_ANY, label="",
+            pos=(0, 0), size=(80, 115)
           )
         ]
         monospaceFont.PointSize -= 2
@@ -264,37 +270,47 @@ class PanelOptimizer(wx.Panel):
         )
         self.minStatSlid = [
             wx.Slider(
-              parent=minStatBox, id=wx.ID_ANY, pos=(30, 0), size=(120, 25)
+              parent=minStatBox, id=wx.ID_ANY, pos=(30, 0),
+              size=(120, 25), name="slid0"
             ),
             wx.Slider(
-              parent=minStatBox, id=wx.ID_ANY, pos=(30, 25), size=(120, 25)
+              parent=minStatBox, id=wx.ID_ANY, pos=(30, 25),
+              size=(120, 25), name="slid1"
             ),
             wx.Slider(
-              parent=minStatBox, id=wx.ID_ANY, pos=(30, 50), size=(120, 25)
+              parent=minStatBox, id=wx.ID_ANY, pos=(30, 50),
+              size=(120, 25), name="slid2"
             ),
             wx.Slider(
-              parent=minStatBox, id=wx.ID_ANY, pos=(30, 75), size=(120, 25)
+              parent=minStatBox, id=wx.ID_ANY, pos=(30, 75),
+              size=(120, 25), name="slid3"
             ),
             wx.Slider(
-              parent=minStatBox, id=wx.ID_ANY, pos=(30, 100), size=(120, 25)
+              parent=minStatBox, id=wx.ID_ANY, pos=(30, 100),
+              size=(120, 25), name="slid4"
             ),
             wx.Slider(
-              parent=minStatBox, id=wx.ID_ANY, pos=(30, 125), size=(120, 25)
+              parent=minStatBox, id=wx.ID_ANY, pos=(30, 125),
+              size=(120, 25), name="slid5"
             ),
             wx.Slider(
-              parent=minStatBox, id=wx.ID_ANY, pos=(30, 150), size=(120, 25)
+              parent=minStatBox, id=wx.ID_ANY, pos=(30, 150),
+              size=(120, 25), name="slid6"
             ),
             wx.Slider(
-              parent=minStatBox, id=wx.ID_ANY, pos=(30, 175), size=(120, 25)
+              parent=minStatBox, id=wx.ID_ANY, pos=(30, 175),
+              size=(120, 25), name="slid7"
             ),
             wx.Slider(
-              parent=minStatBox, id=wx.ID_ANY, pos=(30, 200), size=(120, 25)
+              parent=minStatBox, id=wx.ID_ANY, pos=(30, 200),
+              size=(120, 25), name="slid8"
             ),
             wx.Slider(
-              parent=minStatBox, id=wx.ID_ANY, pos=(30, 225), size=(120, 25)
+              parent=minStatBox, id=wx.ID_ANY, pos=(30, 225),
+              size=(120, 25), name="slid9"
             )
         ]
-        for i in range(0, 9):
+        for i in range(0, 10):
             self.minStatSlid[i].SetMin(0)
             self.minStatSlid[i].SetMax(0)
             self.minStatSlid[i].SetValue(0)
@@ -687,8 +703,7 @@ class PanelOptimizer(wx.Panel):
         # ../RuneOptimizer optimize 7223811472 -l 15
         #-e rage,blade --stats atk,crr,crd -h 10000 -f 10
         command = "RuneOptimizer optimize "
-        unitId = \
-          str(self.unitList.GetItemData(self.unitList.GetFirstSelected()))
+        unitId = self.unitId
         command += unitId
         level = self.level.GetSelection()
         if level == 1:
@@ -760,24 +775,28 @@ class PanelOptimizer(wx.Panel):
 
 
         command += (" --gui ")
+        command = os.path.dirname(os.path.realpath(__file__)) + "/../../" + command
         print("Command: " + command)
 
-        command = "../../" + command
         out = subprocess.check_output(command.split())
-        #print ("---- OUTPUT ------------------------------------------------------------------------------------------")
-        #print(out)
-        #print ("------------------------------------------------------------------------------------------------------")
-
-        # TODO: Do this propperly
-        resultsFrame = ResultsFrame(
-          parent=None, title='Options for Lushen (DEBUG)',
-          pos=(100, 50), size=(900, 680)
-        )
-        resultsFrame.unitId = unitId
-        resultsFrame.unitName = self.unitNameValue
-        resultsFrame.currentStats = self.currentStats
-        resultsFrame.processResults(bytes.decode(out))
-        resultsFrame.Show()
+        json = bytes.decode(out)
+        # DEBUG: Sample data.
+        #self.unitId = "7223811472"
+        #json = """{"result_count":5000,"results":[
+        #    {"id":0,"rating":215,"hp":20461,"atk":2521,"dfc":845,"spd":137,"crr":63,"crd":167,"res":29,"acc":15,"ehp":83839,"dmg":5174,"runes":["22677809846","22920616295","21755980400","25633344349","26207902579","28512560500"]},
+        #    {"id":1,"rating":195,"hp":18876,"atk":2521,"dfc":853,"spd":137,"crr":60,"crd":174,"res":37,"acc":8,"ehp":77873,"dmg":5153,"runes":["22677809846","22920616295","27654723287","25633344349","26207902579","28512560500"]},
+        #    {"id":2,"rating":185,"hp":17730,"atk":2521,"dfc":862,"spd":147,"crr":60,"crd":164,"res":28,"acc":15,"ehp":73704,"dmg":5002,"runes":["21564691276","22920616295","27654723287","25633344349","26207902579","22348038576"]},
+        #    {"id":3,"rating":179,"hp":15810,"atk":2431,"dfc":1011,"spd":137,"crr":61,"crd":160,"res":27,"acc":15,"ehp":73968,"dmg":4804,"runes":["21564691276","22920616295","27444728625","22750814840","26670411873","22348038576"]},
+        #    {"id":4,"rating":176,"hp":15319,"atk":2530,"dfc":909,"spd":142,"crr":60,"crd":165,"res":28,"acc":15,"ehp":66202,"dmg":5035,"runes":["21564691276","16759622995","27654723287","22750814840","26207902579","22348038576"]}]}
+        #"""
+
+
+        print ("---- OUTPUT ------------------------------------------------------------------------------------------")
+        print(json)
+        print ("------------------------------------------------------------------------------------------------------")
+
+        self.GetParent().frameResults.processResults(self.unitId, json)
+
 
     def minStatChangeBySlider(self, event):
         """Changes text when a slider is changed.

+ 104 - 54
src/RuneOptimizerGUI/classes/PanelResults.py

@@ -26,9 +26,11 @@ class PanelResults(wx.Panel):
         ID of the unit being optimized (default "").
     unitName : str
         Name of the unit being optimized (default "").
+    unitNameLabel : wx.StaticText
+        Labels for the unit name.
     data : Python Object
         Results from RuneOptimizer (default None).
-    currentStats : int[10]
+    unitStats : int[10]
         The current stats of the unit being optimized. (default is
         [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]).
     page : int
@@ -87,8 +89,9 @@ class PanelResults(wx.Panel):
 
     unitId = ""
     unitName = ""
+    unitNameLabel = None
     data = None
-    currentStats = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
+    unitStats = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
     page = 0
     totalPages = 0
     linesPerPage = 10
@@ -130,9 +133,20 @@ class PanelResults(wx.Panel):
           style=wx.FONTSTYLE_ITALIC, weight=wx.FONTWEIGHT_NORMAL
         )
 
+        # Unit name and id
+        self.unitNameLabel =wx.StaticText(
+            parent=self, id=wx.ID_ANY, label="Nothing yet. Optimize something!",
+            pos=(0, 0), size=(400, 30)
+        )
+
+        # Contains the results table. Hidden until they are loaded.
+        self.resultContainer = wx.BoxSizer(wx.VERTICAL)
+        # Contains all thigs to be shown once a result is selected
+        self.resultContent = wx.BoxSizer(wx.VERTICAL)
+
         # The result list table
         self.resultGrid = wx.grid.Grid(
-          parent=self, id=wx.ID_ANY, pos=(50, 30), size=(544, 220)
+          parent=self, id=wx.ID_ANY, pos=(0, 30), size=(525, 170)
         )
         self.resultGrid.CreateGrid(
           numRows=10, numCols=11,
@@ -142,7 +156,9 @@ class PanelResults(wx.Panel):
         self.resultGrid.SetDefaultCellAlignment(
           horiz=wx.ALIGN_RIGHT, vert=wx.ALIGN_CENTRE
         )
+        monospaceFont.PointSize -= 1
         self.resultGrid.SetDefaultCellFont(monospaceFont)
+        monospaceFont.PointSize += 1
         self.resultGrid.SetRowLabelSize(width=35)
         self.resultGrid.SetColLabelValue(col=0, value="Rating")
         self.resultGrid.SetColSize(col=0, width=50)
@@ -167,34 +183,35 @@ class PanelResults(wx.Panel):
         self.resultGrid.SetColLabelValue(col=10, value="DMG")
         self.resultGrid.SetColSize(col=10, width=50)
         self.resultGrid.SetColLabelSize(height=20)
-        self.resultGrid.SetDefaultRowSize(height=20)
+        self.resultGrid.SetDefaultRowSize(height=15)
         self.Bind(
           wx.grid.EVT_GRID_SELECT_CELL, self.resultSelected, self.resultGrid
         )
+        self.resultContainer.Add(self.resultGrid)
 
         # Paginator
         self.pgPrevBt = wx.Button(
-          parent=self, id=wx.ID_ANY, pos=(580, 30), size=(40, 50),
+          parent=self, id=wx.ID_ANY, pos=(530, 30), size=(40, 50),
           style=wx.LC_REPORT, label="Prev\npage"
         )
+        self.resultContainer.Add(self.pgPrevBt)
         self.resultsPageIndicator = wx.StaticText(
-          parent=self,id=wx.ID_ANY, pos=(580, 80), size=(40, 15),
+          parent=self,id=wx.ID_ANY, pos=(530, 80), size=(40, 15),
           style=wx.ALIGN_CENTRE_HORIZONTAL, label="1/1"
         )
+        self.resultContainer.Add(self.resultsPageIndicator)
         self.pgNextBt = wx.Button(
-          parent=self, id=wx.ID_ANY, pos=(580, 100), size=(40, 50),
+          parent=self, id=wx.ID_ANY, pos=(530, 100), size=(40, 50),
           style=wx.LC_REPORT, label="Next\npage"
         )
+        self.resultContainer.Add(self.pgNextBt)
         self.Bind(wx.EVT_BUTTON, self.pgPrev, self.pgPrevBt)
         self.Bind(wx.EVT_BUTTON, self.pgNext, self.pgNextBt)
 
-        # Contains all thigs to be shown once a result is selected
-        self.resultContent = wx.BoxSizer(wx.VERTICAL)
-
         # Stats table
         self.statGrid = wx.grid.Grid(
-          parent=self, id=wx.ID_ANY, pos=(650, 30),
-          size=(205, 220), style=wx.LC_REPORT
+          parent=self, id=wx.ID_ANY, pos=(0, 210),
+          size=(185, 220), style=wx.LC_REPORT
         )
         self.statGrid.CreateGrid(
           numRows=10, numCols=2,
@@ -237,27 +254,27 @@ class PanelResults(wx.Panel):
         runeListBox = [
           wx.StaticBox(
             parent=self, label="Slot1:",id=wx.ID_ANY,
-            pos=(200, 255), size=(140, 160)
+            pos=(350, 210), size=(140, 160)
           ),
           wx.StaticBox(
             parent=self, label="Slot2:",id=wx.ID_ANY,
-            pos=(350, 255), size=(140, 160)
+            pos=(500, 210), size=(140, 160)
           ),
           wx.StaticBox(
             parent=self, label="Slot3:",id=wx.ID_ANY,
-            pos=(350, 420), size=(140, 160)
+            pos=(500, 375), size=(140, 160)
           ),
           wx.StaticBox(
             parent=self, label="Slot4:",id=wx.ID_ANY,
-            pos=(200, 420), size=(140, 160)
+            pos=(350, 375), size=(140, 160)
           ),
           wx.StaticBox(
             parent=self, label="Slot5:",id=wx.ID_ANY,
-            pos=(50, 420), size=(140, 160)
+            pos=(200, 375), size=(140, 160)
           ),
           wx.StaticBox(
             parent=self, label="Slot6:",id=wx.ID_ANY,
-            pos=(50, 255), size=(140, 160)
+            pos=(200, 210), size=(140, 160)
           )
         ]
         for i in range(0, 6):
@@ -523,16 +540,17 @@ class PanelResults(wx.Panel):
 
         # Action buttons
         applyBt = wx.Button(
-          parent=self, id=wx.ID_ANY, pos=(650, 330), size=(165, 60),
+          parent=self, id=wx.ID_ANY, pos=(10, 450), size=(165, 60),
           style=wx.LC_REPORT, label="Apply runes"
         )
         self.Bind(wx.EVT_BUTTON, self.applyRunes, applyBt)
         self.resultContent.Add(applyBt)
 
-        # By default, hide everything
+        # By default, hide everything TODO
+        self.resultContainer.ShowItems(False)
         self.resultContent.ShowItems(False)
 
-    def processResults(self, jsonData):
+    def processResults(self, unitId=None, jsonData=""):
         """Processes data obtained from RuneOptimizer.
 
         Reads the JSON data and initializes the property data.
@@ -545,6 +563,41 @@ class PanelResults(wx.Panel):
 
         """
 
+        if unitId != None:
+            self.unitId = unitId
+            cursor = conn.execute("""
+              SELECT
+                current_hp,
+                current_atk,
+                current_def,
+                current_spd,
+                current_crr,
+                current_crd,
+                current_res,
+                current_acc,
+                name
+              FROM units
+              WHERE
+              id = '""" + unitId + """';
+            """)
+            row = cursor.fetchone()
+            self.unitName = row[8]
+            for i in range(0, 8):
+                self.unitStats[i] = row[i]
+            # Calculate EHP and DMG
+            hp = row[0]
+            dfc = row[2]
+            ehp = math.ceil((((dfc * 3.5) + 1140) * hp) / 1000)
+            self.unitStats[8] = ehp
+            atk = row[1]
+            crr = row[4]
+            crd = row[5]
+            dmg = math.ceil(
+              (atk * (100 - crr) / 100) +
+              ((atk + (atk * crd / 100)) * crr / 100)
+            )
+            self.unitStats[9] = dmg
+
         self.data = json.loads(
           jsonData,
           object_hook=lambda d: SimpleNamespace(**d)
@@ -586,18 +639,6 @@ class PanelResults(wx.Panel):
             self.page += 1
             self.printResults()
 
-    def closeWindow(self, event):
-        """Closes the window.
-
-        Parameters
-        ----------
-        event : wxEvent, optional
-            The event that triggered the call (default is None).
-
-        """
-
-        self.Close(True)
-
     def applyRunes(self, event):
         """Applies the selected results and saves data to the database.
 
@@ -614,8 +655,8 @@ class PanelResults(wx.Panel):
             # TODO: Show error
             return;
         print("self.selectedResultIndex: " + str(self.selectedResultIndex))
-        for i in range(0, 6):
-            print(self.data.results[self.selectedResultIndex].runes[i])
+        #for i in range(0, 6):
+        #    print(self.data.results[self.selectedResultIndex].runes[i])
         # First, unassign all runes currently assigned to the unit
         cursor = conn.execute(
           """
@@ -668,7 +709,7 @@ class PanelResults(wx.Panel):
         recalculteStatsOfModifiedUnits()
         print("All recalculated!")
 
-        # Fetch the new values for self.currentStats
+        # Fetch the new values for self.unitStats
         cursor = conn.execute(
           """
             SELECT
@@ -689,18 +730,18 @@ class PanelResults(wx.Panel):
         )
         row = cursor.fetchone()
         for i in range(0, 8):
-            self.currentStats[i] = int(row[i])
-        self.currentStats[9] = math.ceil(
-          (((self.currentStats[2] * 3.5) + 1140) * self.currentStats[0]) / 1000
+            self.unitStats[i] = int(row[i])
+        self.unitStats[9] = math.ceil(
+          (((self.unitStats[2] * 3.5) + 1140) * self.unitStats[0]) / 1000
         )
-        self.currentStats[10] = math.ceil(
-          (self.currentStats[1] * (100 - self.currentStats[4]) / 100) +
+        self.unitStats[10] = math.ceil(
+          (self.unitStats[1] * (100 - self.unitStats[4]) / 100) +
           (
             (
-              self.currentStats[1] +
-              (self.currentStats[1] * self.currentStats[5] / 100)
+              self.unitStats[1] +
+              (self.unitStats[1] * self.unitStats[5] / 100)
             ) *
-            self.currentStats[4] / 100
+            self.unitStats[4] / 100
           )
         )
         self.resultSelected(None)
@@ -746,6 +787,12 @@ class PanelResults(wx.Panel):
                 for j in range(0, 11):
                     self.resultGrid.SetCellValue(row=i, col=j, s="")
 
+        # Display the unit name
+        self.unitNameLabel.SetLabel(self.unitName + "    #" + self.unitId)
+
+        # Make the table visible
+        self.resultContainer.ShowItems(True)
+
     def resultSelected(self, event):
         """Populates and shows the runes and effective stats with the
         currently seleced result.
@@ -766,7 +813,7 @@ class PanelResults(wx.Panel):
           s=str(self.data.results[self.selectedResultIndex].hp) + " "
         )
         diff = \
-          self.data.results[self.selectedResultIndex].hp - self.currentStats[0]
+          self.data.results[self.selectedResultIndex].hp - self.unitStats[0]
         if (diff < 0):
             self.statGrid.SetCellValue(
               row=0, col=1, s="- " + str(abs(diff)) + " "
@@ -789,7 +836,7 @@ class PanelResults(wx.Panel):
           s=str(self.data.results[self.selectedResultIndex].atk) + " "
         )
         diff = \
-          self.data.results[self.selectedResultIndex].atk - self.currentStats[1]
+          self.data.results[self.selectedResultIndex].atk - self.unitStats[1]
         if (diff < 0):
             self.statGrid.SetCellValue(
               row=1, col=1, s="- " + str(abs(diff)) + " "
@@ -810,7 +857,7 @@ class PanelResults(wx.Panel):
           s=str(self.data.results[self.selectedResultIndex].dfc) + " "
         )
         diff = \
-          self.data.results[self.selectedResultIndex].atk - self.currentStats[2]
+          self.data.results[self.selectedResultIndex].atk - self.unitStats[2]
         if (diff < 0):
             self.statGrid.SetCellValue(
               row=2, col=1, s="- " + str(abs(diff)) + " "
@@ -833,7 +880,7 @@ class PanelResults(wx.Panel):
           s=str(self.data.results[self.selectedResultIndex].spd) + " "
         )
         diff = \
-          self.data.results[self.selectedResultIndex].spd - self.currentStats[3]
+          self.data.results[self.selectedResultIndex].spd - self.unitStats[3]
         if (diff < 0):
             self.statGrid.SetCellValue(
               row=3, col=1, s="- " + str(abs(diff)) + " "
@@ -857,7 +904,7 @@ class PanelResults(wx.Panel):
           s=str(self.data.results[self.selectedResultIndex].crr) + "%"
         )
         diff = \
-          self.data.results[self.selectedResultIndex].crr - self.currentStats[4]
+          self.data.results[self.selectedResultIndex].crr - self.unitStats[4]
         if (diff < 0):
             self.statGrid.SetCellValue(
               row=4, col=1, s="- " + str(abs(diff)) + "%"
@@ -878,7 +925,7 @@ class PanelResults(wx.Panel):
           s=str(self.data.results[self.selectedResultIndex].crd) + "%"
         )
         diff = \
-          self.data.results[self.selectedResultIndex].crd - self.currentStats[5]
+          self.data.results[self.selectedResultIndex].crd - self.unitStats[5]
         if (diff < 0):
             self.statGrid.SetCellValue(
               row=5, col=1, s="- " + str(abs(diff)) + "%"
@@ -901,7 +948,7 @@ class PanelResults(wx.Panel):
           s=str(self.data.results[self.selectedResultIndex].res) + "%"
         )
         diff = \
-          self.data.results[self.selectedResultIndex].res - self.currentStats[6]
+          self.data.results[self.selectedResultIndex].res - self.unitStats[6]
         if (diff < 0):
             self.statGrid.SetCellValue(
               row=6, col=1, s="- " + str(abs(diff)) + "%"
@@ -921,7 +968,7 @@ class PanelResults(wx.Panel):
           row=7, col=0, s=str(self.data.results[self.selectedResultIndex].acc) + "%"
         )
         diff = \
-          self.data.results[self.selectedResultIndex].acc - self.currentStats[7]
+          self.data.results[self.selectedResultIndex].acc - self.unitStats[7]
         if (diff < 0):
             self.statGrid.SetCellValue(
               row=7, col=1, s="- " + str(abs(diff)) + "%"
@@ -944,7 +991,7 @@ class PanelResults(wx.Panel):
           s=str(self.data.results[self.selectedResultIndex].ehp) + " "
         )
         diff = \
-          self.data.results[self.selectedResultIndex].ehp - self.currentStats[8]
+          self.data.results[self.selectedResultIndex].ehp - self.unitStats[8]
         if (diff < 0):
             self.statGrid.SetCellValue(
               row=8, col=1, s="- " + str(abs(diff)) + " "
@@ -966,7 +1013,7 @@ class PanelResults(wx.Panel):
           row=9, col=0,
           s=str(self.data.results[self.selectedResultIndex].dmg) + " "
         )
-        diff = self.data.results[self.selectedResultIndex].dmg - self.currentStats[9]
+        diff = self.data.results[self.selectedResultIndex].dmg - self.unitStats[9]
         if (diff < 0):
             self.statGrid.SetCellValue(
               row=9, col=1, s="- " + str(abs(diff)) + " "
@@ -1074,3 +1121,6 @@ class PanelResults(wx.Panel):
                 else: # normal stats
                     self.runeStats[i][slot - 1].SetLabel(line)
             i += 1
+
+        # Make the info visible
+        self.resultContent.ShowItems(True)

+ 39 - 1
src/RuneOptimizerGUI/classes/PanelTeams.py

@@ -1 +1,39 @@
- 
+"""
+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/>.
+
+"""
+
+class PanelTeams(wx.Panel):
+    """
+    The team management Panel.
+
+    Parameters
+    ----------
+
+    Methods
+    -------
+
+    """
+
+    def __init__(self, parent, id=wx.ID_ANY):
+        """Initializes the panel.
+
+        Sets upt all the widgets.
+
+        """
+
+        # Parent constructor
+        wx.Panel.__init__(self, parent=parent, id=id)

+ 4 - 4
src/RuneOptimizerGUI/classes/PanelUnits.py

@@ -68,7 +68,7 @@ class PanelUnits(wx.Panel):
     runeInnates = None
     runeStats = None
 
-    def __init__(self, parent):
+    def __init__(self, parent, id=wx.ID_ANY):
         """Initializes the panel.
 
         Sets upt all the widgets.
@@ -76,7 +76,7 @@ class PanelUnits(wx.Panel):
         """
 
         # Parent constructor
-        wx.Panel.__init__(self, parent=parent, id=wx.ID_ANY)
+        wx.Panel.__init__(self, parent=parent, id=id)
 
         # Prepare some fonts
         monospaceFont = wx.Font(
@@ -598,7 +598,7 @@ class PanelUnits(wx.Panel):
         baseDmg = math.ceil(
           (baseAtk * (100 - baseCrr) / 100) +
           ((baseAtk + (baseAtk * baseCrd / 100)) * baseCrr / 100)
-        );
+        )
         self.statGrid.SetCellValue(row=9, col=0, s=str(baseDmg))
         #self.minStatSlid[9].SetMin(baseDmg)
         currentAtk = int(self.statGrid.GetCellValue(row=1, col=1))
@@ -612,7 +612,7 @@ class PanelUnits(wx.Panel):
         currentDmg = math.ceil(
           (currentAtk * (100 - currentCrr) / 100) +
           ((currentAtk + (currentAtk * currentCrd / 100)) * currentCrr / 100)
-        );
+        )
         self.statGrid.SetCellValue(row=9, col=1, s=str(currentDmg))
         #self.minStatSlid[9].SetValue(currentDmg)
         #self.minStatText[9].SetValue(str(currentDmg))

+ 13 - 4
src/RuneOptimizerGUI/classes/TabList.py

@@ -29,6 +29,11 @@ class TabList(wx.Listbook):
 
     """
 
+    frameUnits = None
+    frameTeams = None
+    frameOptimizer = None
+    frameResults = None
+
     def __init__(self, parent, id=wx.ID_ANY, pos=(0, 0), size=(800, 600), style=wx.LC_REPORT):
         """
         Initializes the tablist.
@@ -72,11 +77,15 @@ class TabList(wx.Listbook):
         self.AssignImageList(il)
 
         # Create the entries
+        self.frameUnits = PanelUnits(self)
+        self.frameTeams = PanelTeams(self)
+        self.frameOptimizer = PanelOptimizer(self)
+        self.frameResults = PanelResults(self)
         pages = [
-          (PanelUnits(self), "Units"),
-          (PanelUnits(self), "Teams"),
-          (PanelUnits(self), "Optimize"),
-          (PanelUnits(self), "Results")
+          (self.frameUnits, "Units"),
+          (self.frameTeams, "Teams"),
+          (self.frameOptimizer, "Optimize"),
+          (self.frameResults, "Results")
         ]
 
         # Add icons to the entries