Procházet zdrojové kódy

v0.1.1 - Several fixes:
[CLI] Fixed an error when counting stat boost from rune sets.
[CLI] Fixed an error when importing RTA configurations that computed runes from normal mode.
[CLI] Fixed results ratings
[CLI] Non-stat-boosting rune sets now count a bit to the ratings
[CLI] New monster names added (from game versions v6.4.7 and v6.5.9)
[GUI] Fixed an error that locked the database when appliying runes.
[GUI] In the result listings, fixed the colors for posivtive and negative values on some stats.
[GUI] Fixed several team-related funcions.

Iñigo Valentin před 4 roky
rodič
revize
66b9d90ebc

+ 174 - 22
src/cli/runeoptimizer/optimize/optimize.c

@@ -84,6 +84,43 @@
  */
 #define RUNE_QUERY_LEN 3500
 
+/**
+ * Rating point given for a rune set of 2.
+ *
+ * Applies for Nemesis, Will, Shield, Revenge and Destroy. Loosely calculated
+ * to compensate for the stat loss for not equipping a stat boosting set of 2.
+ */
+#define RATING_SET_OF_2 21.586
+
+/**
+ * Rating point given for a rune set of 4.
+ *
+ * Applies for Despair, Vampire and Violent. Loosely calculated to compensate
+ * for the stat loss for not equipping a stat boosting set of 4.
+ */
+#define RATING_SET_OF_4 43.860
+
+/**
+ * Rating point giving for a Fight or Determination set.
+ *
+ * Loosely calculated to compensate for not equipping a individual set.
+ */
+#define RATING_SET_FIG_DET 14.828
+
+/**
+ * Rating point giving for a Enhance set.
+ *
+ * Loosely calculated to compensate for not equipping a individual set.
+ */
+#define RATING_SET_ENH 11.520
+
+/**
+ * Rating point giving for a Accuracy or Tolerance set.
+ *
+ * Loosely calculated to compensate for not equipping a individual set.
+ */
+#define RATING_SET_ACC_TOL 6.950
+
 /**
  * Limit on runes per slot for the optimizations.
  *
@@ -1034,6 +1071,74 @@ static int read_unit(char *id, Unit *unit){
     unit->current_dmg = calculate_dmg(
       unit->current_atk, unit->current_crr, unit->current_crd
     );
+
+    // Calculate the rating points that the current set would give.
+    // Non-stat-boosting sets and team stat sets add to this, to compensate
+    // the stat loss.
+    // Later, for each result, this must be substracted to the calculated
+    // rating
+    unit->set_rating_points = 0.0f;
+    Rune_Set_Count sets;
+    struct Stats stats;
+    sets.despair = 0;
+    sets.vampire = 0;
+    sets.violent = 0;
+    sets.nemesis = 0;
+    sets.will = 0;
+    sets.shield = 0;
+    sets.revenge = 0;
+    sets.destroy = 0;
+    sets.fight = 0;
+    sets.determination = 0;
+    sets.enhance = 0;
+    sets.accuracy = 0;
+    sets.tolerance = 0;
+    sqlite3_stmt *stmt_runes;
+    strcpy(parameters[0], (char *) id);
+    db_query(&stmt_runes, "SELECT type FROM runes WHERE unit = ?", parameters);
+    while (SQLITE_ROW == sqlite3_step(stmt_runes)){
+        int type = sqlite3_column_int(stmt_runes, 0);
+        switch (type){
+            case DESPAIR:       sets.despair ++;       break;
+            case VAMPIRE:       sets.vampire ++;       break;
+            case VIOLENT:       sets.violent ++;       break;
+            case NEMESIS:       sets.nemesis ++;       break;
+            case WILL:          sets.will ++;          break;
+            case SHIELD:        sets.shield ++;        break;
+            case REVENGE:       sets.revenge ++;       break;
+            case DESTROY:       sets.destroy ++;       break;
+            case FIGHT:         sets.fight ++;         break;
+            case DETERMINATION: sets.determination ++; break;
+            case ENHANCE:       sets.enhance ++;       break;
+            case ACCURACY:      sets.accuracy ++;      break;
+            case TOLERANCE:     sets.tolerance ++;     break;
+        }
+    }
+    if (sets.nemesis >= 2)
+        unit->set_rating_points += RATING_SET_OF_2 * floor(sets.nemesis / 2);
+    if (sets.will >= 2)
+        unit->set_rating_points += RATING_SET_OF_2 * floor(sets.will / 2);
+    if (sets.shield >= 2)
+        unit->set_rating_points += RATING_SET_OF_2 * floor(sets.shield / 2);
+    if (sets.revenge >= 2)
+        unit->set_rating_points += RATING_SET_OF_2 * floor(sets.revenge / 2);
+    if (sets.destroy >= 2)
+        unit->set_rating_points += RATING_SET_OF_2 * floor(sets.destroy / 2);
+    if (sets.despair >= 4 || sets.vampire >= 4 || sets.violent >= 4)
+        unit->set_rating_points += 43.86;
+    if (sets.fight >= 2)
+        unit->set_rating_points += RATING_SET_FIG_DET * floor(sets.fight / 2);
+    if (sets.determination >= 2)
+        unit->set_rating_points +=
+          RATING_SET_FIG_DET * floor(sets.determination / 2);
+    if (sets.enhance >= 2)
+        unit->set_rating_points += RATING_SET_ENH * floor(sets.enhance / 2);
+    if (sets.accuracy >= 2)
+        unit->set_rating_points +=
+          RATING_SET_ACC_TOL * floor(sets.accuracy / 2);
+    if (sets.tolerance >= 2)
+        unit->set_rating_points +=
+          RATING_SET_ACC_TOL * floor(sets.tolerance / 2);
     return(SUCCESS);
 }
 
@@ -1427,10 +1532,10 @@ static thrd_start_t optimize_thread(void *data_ptr){
             // Set stats
             if (set_count.energy >= 2) // +15% Base HP per set of 2
                 stats.hp +=
-                  data->unit->base_hp * 0.15 * (set_count.energy % 2);
+                  data->unit->base_hp * 0.15 * floor(set_count.energy / 2);
             if (set_count.guard >= 2) // +15% base DEF per set of 2
                 stats.def +=
-                  data->unit->base_def * 0.15 * (set_count.guard % 2);
+                  data->unit->base_def * 0.15 * floor(set_count.guard / 2);
             if (set_count.swift >= 4) // +25% base SPD per set of 4
                 stats.spd += data->unit->base_spd * 0.25;
             if (set_count.blade >= 2) // +12% CRR per set of 2
@@ -1445,14 +1550,14 @@ static thrd_start_t optimize_thread(void *data_ptr){
                 stats.atk += data->unit->base_atk * 0.35;
             if (set_count.fight >= 2) // +8% base ATK per set of 2
                 stats.atk +=
-                  data->unit->base_atk * 0.08 * (set_count.fight % 2);
+                  data->unit->base_atk * 0.08 * floor(set_count.fight / 2);
             if (set_count.determination >= 2) // +8% base DEF per set of 2
                 stats.def +=
                   data->unit->base_def * 0.08
-                  * (set_count.determination % 2);
+                  * floor(set_count.determination / 2);
             if (set_count.enhance >= 2) // +8% base HP per set of 2
                 stats.hp +=
-                  data->unit->base_hp * 0.08 * (set_count.enhance % 2);
+                  data->unit->base_hp * 0.08 * floor(set_count.enhance / 2);
             if (set_count.accuracy >= 2) // +10% ACC per set of 2
                 stats.acc += 10;
             if (set_count.tolerance >= 2) // +10% RES per set of 2
@@ -1483,41 +1588,88 @@ static thrd_start_t optimize_thread(void *data_ptr){
                 // 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
+                // the (flat) stats roll for a substat 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
+                // HP: 1 rating point per 62.500 points
+                // ATK: 1 rating point per 3.333 points
+                // DEF: 1 rating point per 3.333 points
+                // SPD: 1 rating point per 1.000 points
+                // CRR: 1 rating point per 1.000 points
+                // CRD: 1 rating point per 1.166 points
+                // RES: 1 rating point per 1.333 points
+                // ACC: 1 rating point per 1.333 points
                 float rating = 0.0f;
                 rating +=
                   (float) ((int) stats.hp  - (int) data->unit->current_hp )
-                  / 58.29f;
+                  / 62.500f;
                 rating +=
                   (float) ((int) stats.atk - (int) data->unit->current_atk)
-                  / 1.50f;
+                  / 3.333f;
                 rating +=
                   (float) ((int) stats.def - (int) data->unit->current_def)
-                  / 1.50f;
+                  / 3.333f;
                 rating +=
                   (float) ((int) stats.spd - (int) data->unit->current_spd)
-                  / 1.00f;
+                  / 1.000f;
                 rating +=
                   (float) ((int) stats.crr - (int) data->unit->current_crr)
-                  / 1.38f;
+                  / 1.000f;
                 rating +=
                   (float) ((int) stats.crd - (int) data->unit->current_crd)
-                  / 1.90f;
+                  / 1.166f;
                 rating +=
                   (float) ((int) stats.res - (int) data->unit->current_res)
-                  / 1.52f;
+                  / 1.333f;
                 rating +=
                   (float) ((int) stats.acc - (int) data->unit->current_acc)
-                  / 1.52f;
+                  / 1.333f;
+
+                // Non-stat sets also count for ratings.
+                // These ratings are loosely calculated to make up for the
+                // stats not boosted.
+                // Individual sets of 2 (Nenesis, Will, Shield, Revenge,
+                // Destroy) give RATING_SET_OF_2 points each.
+                // Sets of 4 (Despair, Violent, Vampire) give
+                // RATING_SET_OF_4 points
+                if (set_count.nemesis >= 2)
+                    rating += RATING_SET_OF_2 * floor(set_count.nemesis / 2);
+                if (set_count.will >= 2)
+                    rating += RATING_SET_OF_2 * floor(set_count.will / 2);
+                if (set_count.shield >= 2)
+                    rating += RATING_SET_OF_2 * floor(set_count.shield / 2);
+                if (set_count.revenge >= 2)
+                    rating += RATING_SET_OF_2 * floor(set_count.revenge / 2);
+                if (set_count.destroy >= 2)
+                    rating += RATING_SET_OF_2 * floor(set_count.destroy / 2);
+                if (
+                  set_count.despair >= 4
+                  || set_count.vampire >= 4
+                  || set_count.violent >= 4
+                ) rating += 43.86;
+
+                // Team stat bosting sets also boost ratings. These values are
+                // calculated from their equivalent individual sets,
+                // considering a team of 4.
+                // Fight, Determination: RATING_SET_FIG_DET points for each set.
+                // Enhance: +RATING_SET_ENH points for each set.
+                // Accuracy, Tolerance: RATING_SET_ACC_TOL points for each set
+                if (set_count.fight >= 2)
+                    rating += RATING_SET_FIG_DET * floor(set_count.fight / 2);
+                if (set_count.determination >= 2)
+                    rating +=
+                      RATING_SET_FIG_DET * floor(set_count.determination / 2);
+                if (set_count.enhance >= 2)
+                    rating += RATING_SET_ENH * floor(set_count.enhance / 2);
+                if (set_count.accuracy >= 2)
+                    rating +=
+                      RATING_SET_ACC_TOL * floor(set_count.accuracy / 2);
+                if (set_count.tolerance >= 2)
+                    rating +=
+                      RATING_SET_ACC_TOL * floor(set_count.tolerance / 2);
+
+                // Substract the rating for the currently equipped sets.
+
+                rating -= data->unit->set_rating_points;
 
                 // This is a valid sets and all stats are above the minimum.
                 // Create a result with rune indexes, stats, and rating.

+ 8 - 1
src/cli/runeoptimizer/runeoptimizer.h

@@ -34,7 +34,7 @@
  *
  * Metadata, only used in the help and version commands.
  */
-#define VERSION "0.1.0"
+#define VERSION "0.1.1"
 
 /**
  * Application author.
@@ -570,6 +570,13 @@ typedef struct Unit {
      * artifacts or towers. See {@link calculate_ehp} for more details bout EHP.
      */
     uint_fast16_t current_dmg;
+
+    /**
+     * Rating points for current non-stat-boosting rune sets and team sets.
+     *
+     * This is used during optimization, to compare with a result set.
+     */
+    double set_rating_points;
 } Unit;
 
 /**

+ 11 - 6
src/cli/runeoptimizer/update/update_current_stats.c

@@ -26,6 +26,7 @@
 
 #include <stdio.h>
 #include <string.h>
+#include <math.h>
 #include <stdlib.h>
 #include <sqlite3.h>
 #include "../runeoptimizer.h"
@@ -84,7 +85,7 @@ extern int update_current_stats(){
           "  current_hp_flat, current_atk_flat, current_def_flat,"
           "  current_hp_percent, current_atk_percent, current_def_percent,"
           "  current_spd, current_crr, current_crd, current_res, current_acc, "
-          "  slot"
+          "  slot, id"
           " FROM runes WHERE unit = ?",
           parameters
         );
@@ -134,9 +135,11 @@ extern int update_current_stats(){
 
         // Rune set bonuses
         if (set_count.energy >= 2) // +15% Base HP per set of 2
-            unit.current_hp += unit.base_hp * 0.15 * (set_count.energy % 2);
+            unit.current_hp +=
+              unit.base_hp * 0.15 * floor(set_count.energy / 2);
         if (set_count.guard >= 2) // +15% base DEF per set of 2
-            unit.current_def += unit.base_def * 0.15 * (set_count.guard % 2);
+            unit.current_def +=
+              unit.base_def * 0.15 * floor(set_count.guard / 2);
         if (set_count.swift >= 4) // +25% base SPD per set of 4
             unit.current_spd += unit.base_spd * 0.25;
         if (set_count.blade >= 2) // +12% CRR per set of 2
@@ -150,12 +153,14 @@ extern int update_current_stats(){
         if (set_count.fatal >= 4) // +35% base ATK per set of 4
             unit.current_atk += unit.base_atk * 0.35;
         if (set_count.fight >= 2) // +8% base ATK per set of 2
-            unit.current_atk += unit.base_atk * 0.08 * (set_count.fight % 2);
+            unit.current_atk +=
+              unit.base_atk * 0.08 * floor(set_count.fight / 2);
         if (set_count.determination >= 2) // +8% base DEF per set of 2
             unit.current_def +=
-              unit.base_def * 0.08 * (set_count.determination % 2);
+              unit.base_def * 0.08 * floor(set_count.determination / 2);
         if (set_count.enhance >= 2) // +8% base HP per set of 2
-            unit.current_hp += unit.base_hp * 0.08 * (set_count.enhance % 2);
+            unit.current_hp +=
+              unit.base_hp * 0.08 * floor(set_count.enhance / 2);
         if (set_count.accuracy >= 2) // +10% ACC per set of 2
             unit.current_acc += 20;
         if (set_count.tolerance >= 2) // +10% RES per set of 2

+ 81 - 0
src/cli/runeoptimizer/update/update_get_monster_name.c

@@ -1912,6 +1912,87 @@ extern void update_get_monster_name(int id, char name[UNIT_NAME_LEN]){
         case 25613: strcpy(name, "Riley"); break;
         case 25614: strcpy(name, "Ella"); break;
         case 25615: strcpy(name, "Maya"); break;
+        case 25701: strcpy(name, "Weapon Master (Water)"); break;
+        case 25702: strcpy(name, "Weapon Master (Fire)"); break;
+        case 25703: strcpy(name, "Weapon Master (Wind)"); break;
+        case 25704: strcpy(name, "Weapon Master (Light)"); break;
+        case 25705: strcpy(name, "Weapon Master (Dark)"); break;
+        case 25711: strcpy(name, "Liam"); break;
+        case 25712: strcpy(name, "Carlos"); break;
+        case 25713: strcpy(name, "Dominic"); break;
+        case 25714: strcpy(name, "Benedict"); break;
+        case 25715: strcpy(name, "Maximilian"); break;
+        case 25801: strcpy(name, "Rune Blacksmith (Water)"); break;
+        case 25802: strcpy(name, "Rune Blacksmith (Fire)"); break;
+        case 25803: strcpy(name, "Rune Blacksmith (Wind)"); break;
+        case 25804: strcpy(name, "Rune Blacksmith (Light)"); break;
+        case 25805: strcpy(name, "Rune Blacksmith (Dark)"); break;
+        case 25811: strcpy(name, "Susan"); break;
+        case 25812: strcpy(name, "Miriam"); break;
+        case 25813: strcpy(name, "Celine"); break;
+        case 25814: strcpy(name, "Madeleine"); break;
+        case 25815: strcpy(name, "Deborah"); break;
+        case 15231: strcpy(name, "Allen (2A)"); break;
+        case 15232: strcpy(name, "Kai'en (2A)"); break;
+        case 15233: strcpy(name, "Roid (2A)"); break;
+        case 15234: strcpy(name, "Darion (2A)"); break;
+        case 15235: strcpy(name, "Jubelle (2A)"); break;
+        case 15931: strcpy(name, "Megan (2A)"); break;
+        case 15932: strcpy(name, "Rebecca (2A)"); break;
+        case 15933: strcpy(name, "Silia (2A)"); break;
+        case 15934: strcpy(name, "Linda (2A)"); break;
+        case 15935: strcpy(name, "Gina (2A)"); break;
+        case 47005: strcpy(name, "Ancient Guardian (Light)"); break;
+        case 49001: strcpy(name, "Allen (Water)"); break;
+        case 49002: strcpy(name, "Kai'en (Fire)"); break;
+        case 49003: strcpy(name, "Roid (Wind)"); break;
+        case 49004: strcpy(name, "Darion (Light)"); break;
+        case 49005: strcpy(name, "Jubelle (Dark)"); break;
+        case 50001: strcpy(name, "Megan (Water)"); break;
+        case 50002: strcpy(name, "Rebecca (Fire)"); break;
+        case 50003: strcpy(name, "Silia (Wind)"); break;
+        case 50004: strcpy(name, "Linda (Light)"); break;
+        case 50005: strcpy(name, "Gina (Dark)"); break;
+        case 121101: strcpy(name, "2차각성 방랑 기사 수정_왼쪽(물) (Water)"); break;
+        case 121102: strcpy(name, "2차각성 방랑 기사 수정_왼쪽(불) (Fire)"); break;
+        case 121103: strcpy(name, "2차각성 방랑 기사 수정_왼쪽(바람) (Wind)"); break;
+        case 121104: strcpy(name, "2차각성 방랑 기사 수정_왼쪽(빛) (Light)"); break;
+        case 121105: strcpy(name, "2차각성 방랑 기사 수정_왼쪽(어둠) (Dark)"); break;
+        case 121111: strcpy(name, "2차각성 방랑 기사 수정_오른쪽(물) (Water)"); break;
+        case 121112: strcpy(name, "2차각성 방랑 기사 수정_오른쪽(불) (Fire)"); break;
+        case 121113: strcpy(name, "2차각성 방랑 기사 수정_오른쪽(바람) (Wind)"); break;
+        case 121114: strcpy(name, "2차각성 방랑 기사 수정_오른쪽(빛) (Light)"); break;
+        case 121115: strcpy(name, "2차각성 방랑 기사 수정_오른쪽(어둠) (Dark)"); break;
+        case 121201: strcpy(name, "2차각성 미스틱 위치 수정_왼쪽(물) (Water)"); break;
+        case 121202: strcpy(name, "2차각성 미스틱 위치 수정_왼쪽(불) (Fire)"); break;
+        case 121203: strcpy(name, "2차각성 미스틱 위치 수정_왼쪽(바람) (Wind)"); break;
+        case 121204: strcpy(name, "2차각성 미스틱 위치 수정_왼쪽(빛) (Light)"); break;
+        case 121205: strcpy(name, "2차각성 미스틱 위치 수정_왼쪽(어둠) (Dark)"); break;
+        case 121211: strcpy(name, "2차각성 미스틱 위치 수정_오른쪽(물) (Water)"); break;
+        case 121212: strcpy(name, "2차각성 미스틱 위치 수정_오른쪽(불) (Fire)"); break;
+        case 121213: strcpy(name, "2차각성 미스틱 위치 수정_오른쪽(바람) (Wind)"); break;
+        case 121214: strcpy(name, "2차각성 미스틱 위치 수정_오른쪽(빛) (Light)"); break;
+        case 121215: strcpy(name, "2차각성 미스틱 위치 수정_오른쪽(어둠) (Dark)"); break;
+        case 25901: strcpy(name, "Shadowcaster (Water)"); break;
+        case 25902: strcpy(name, "Shadowcaster (Fire)"); break;
+        case 25903: strcpy(name, "Shadowcaster (Wind)"); break;
+        case 25904: strcpy(name, "Shadowcaster (Light)"); break;
+        case 25905: strcpy(name, "Shadowcaster (Dark)"); break;
+        case 25911: strcpy(name, "Minato"); break;
+        case 25912: strcpy(name, "Ren"); break;
+        case 25913: strcpy(name, "Zen"); break;
+        case 25914: strcpy(name, "Shun"); break;
+        case 25915: strcpy(name, "Ritsu"); break;
+        case 26001: strcpy(name, "Hypnomeow (Water)"); break;
+        case 26002: strcpy(name, "Hypnomeow (Fire)"); break;
+        case 26003: strcpy(name, "Hypnomeow (Wind)"); break;
+        case 26004: strcpy(name, "Hypnomeow (Light)"); break;
+        case 26005: strcpy(name, "Hypnomeow (Dark)"); break;
+        case 26011: strcpy(name, "Birman"); break;
+        case 26012: strcpy(name, "Manx"); break;
+        case 26013: strcpy(name, "Nebelung"); break;
+        case 26014: strcpy(name, "Siamese"); break;
+        case 26015: strcpy(name, "Bombay"); break;
         default: strcpy(name, "UNKNOWN MONSTER");
     }
 }

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

@@ -133,9 +133,6 @@ extern int update_json(char file[], Update_Options *options){
         if (status == 1) total_units ++;
     }
 
-    status = update_current_stats();
-    if (SUCCESS != status) return(status);
-
     // If rta mode, update rta runes
     if (options->rta){
         json_object *rta_runes =
@@ -143,6 +140,9 @@ extern int update_json(char file[], Update_Options *options){
         save_rta_runes(rta_runes);
     }
 
+    status = update_current_stats();
+    if (SUCCESS != status) return(status);
+
     // Insert into table info
     status = update_info(
       json_object_get_string(wizard_id),

+ 6 - 6
src/gui/runeoptimizer_gui/gui/DialogNewTeam.py

@@ -18,6 +18,8 @@ RuneOptimizer. If not, see <https://www.gnu.org/licenses/>.
 
 import wx
 import database.database as database
+import runeoptimizer.runeoptimizer as runeoptimizer
+import data.data as data
 
 class DialogNewTeam(wx.Dialog):
     """
@@ -90,11 +92,9 @@ class DialogNewTeam(wx.Dialog):
             )
         if error == False:
             # TODO: Use a command
-            cursor = database.CONNECTION.execute("""
-              INSERT INTO teams (id, name, priority) VALUES (
-                (SELECT max(CAST(id AS INTEGER)) + 1 FROM teams), ?, ?)
-              """, (name, priority))
-            DB_CONNECTION.commit()
-            reload_teams()
+            ret = runeoptimizer.run(
+              ["team", "create", name, priority]
+            )
+            data.reload_teams()
             self.EndModal(wx.ID_OK)
 

+ 2 - 0
src/gui/runeoptimizer_gui/gui/PanelOptimizer.py

@@ -595,6 +595,8 @@ class PanelOptimizer(wx.Panel):
             # Read rune stats
             j = -1
             for stat in rune.stats:
+                if stat is None:
+                    continue
                 if (rune.slot == 2 and stat.slot == -1):
                     current_equiped_stats[0] = stat.stat
                 elif (rune.slot == 4 and stat.slot == -1):

+ 2 - 2
src/gui/runeoptimizer_gui/gui/PanelResults.py

@@ -508,7 +508,7 @@ class PanelResults(wx.Panel):
         print("Applied!")
         self._recalculte_stats_of_modified_units()
         data.reload_units()
-        self._unit = units[self._unit.id]
+        self._unit = data.UNITS[self._unit.id]
         self._update_base_and_current_stats()
         print("All recalculated!")
 
@@ -663,7 +663,7 @@ class PanelResults(wx.Panel):
               row=2, col=3, s="- " + str(abs(diff)) + " "
             )
             self._stat_grid.SetCellTextColour(
-              row=1, col=3, colour=wx.Colour(red=255, green=0, blue=0)
+              row=2, col=3, colour=wx.Colour(red=255, green=0, blue=0)
             )
         elif (diff > 0):
             self._stat_grid.SetCellValue(

+ 1 - 0
src/gui/runeoptimizer_gui/runeoptimizer_gui.py

@@ -66,6 +66,7 @@ if __name__ == '__main__':
     database.CONNECTION = sqlite3.connect(
       appdirs.user_data_dir("RuneOptimizer", "", roaming=True) + "/data.sqlite"
     )
+    database.CONNECTION.isolation_level = None
     
     # This makes sure that the executable has connection to the database.
     runeoptimizer.run(["init"])