RuneOptimizer.c 58 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439
  1. /*
  2. * This file is part of RuneOptimizer.
  3. *
  4. * RuneOptimizer is free software: you can redistribute it and/or modify it
  5. * under the terms of the GNU General Public License as published by the Free
  6. * Software Foundation, either version 3 of the License, or (at your option)
  7. * any later version.
  8. *
  9. * RuneOptimizer is distributed in the hope that it will be useful, but WITHOUT
  10. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11. * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  12. * more details.
  13. *
  14. * You should have received a copy of the GNU General Public License along with
  15. * RuneOptimizer. If not, see <https://www.gnu.org/licenses/>.
  16. */
  17. #include <stdio.h>
  18. #include <stdlib.h>
  19. #include <string.h>
  20. #include <sqlite3.h>
  21. #include "RuneOptimizer.h"
  22. /**
  23. * Starts the program.
  24. *
  25. * Reads parameters and runs the appropiate functions.
  26. *
  27. * @param argc Argument count.
  28. * @param argv Argument list.
  29. * @return SUCCESS on success, other on error.
  30. */
  31. int main(int argc, char *argv[]){
  32. // Parse the arguments to find the command (index 1)
  33. // If no arguments, end here
  34. if (argc < 2){
  35. fprintf(stderr, "No command specified\n");
  36. return ERROR_INPUT_NO_COMMAND;
  37. }
  38. // Update command, TODO
  39. if (strcmp(argv[1], "update") == 0){
  40. fprintf(stderr, "Updating is still unimplemented\n");
  41. return UNIMPLEMENTED;
  42. }
  43. // Optimize command, call the function with all the arguments to be
  44. // processed there.
  45. else if (strcmp(argv[1], "optimize") == 0){
  46. int result = optimize(argc, argv);
  47. return result;
  48. }
  49. // Help command. Call and return
  50. else if (strcmp(argv[1], "help") == 0){
  51. show_help();
  52. return SUCCESS;
  53. }
  54. // Any other command is an error
  55. else{
  56. fprintf(stderr, "Invalid command specified: %s\n", argv[1]);
  57. return ERROR_INPUT_INVALID_COMMAND;
  58. }
  59. }
  60. int open_database(){
  61. // The database location and name is hardcoded in the same directory.
  62. int db_status = sqlite3_open("../data.sqlite", &db);
  63. if (db_status != SQLITE_OK) {
  64. fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
  65. sqlite3_close(db);
  66. return ERROR_DB_CANT_OPEN;
  67. }
  68. else{
  69. return SUCCESS;
  70. }
  71. }
  72. void show_help(){
  73. printf("\nRune Optimizer v0.1\n");
  74. printf("\n Usage:\n");
  75. printf(" RuneOptimizer [command] [options]\n");
  76. printf("\n\n Command: helps\n");
  77. printf("\n Display this help text and exists. It has no options.\n");
  78. printf("\n\n Command: update\n");
  79. printf("\n Updates the information and builds a database. Currently unimplemented.\n");
  80. printf("\n\n Command: optimize\n");
  81. printf("\n Calculates an optimization for a unit.\n");
  82. printf("\n Usage\n");
  83. printf(" RuneOptimizer optmize [unit] [options]\n");
  84. printf("\n [unit] can be a unit ID or a unit name (case sensitive)\n");
  85. printf("\n Options: \n\n");
  86. printf(" -h | --min_hp <NUM> Minumum HP to consider in the optimization.\n");
  87. printf(" It defaults to the unit's current value.\n");
  88. printf(" -a | --min_atk <NUM> Minumum ATK to consider in the optimization.\n");
  89. printf(" It defaults to the unit's current value.\n");
  90. printf(" -d | --min_def <NUM> Minumum DEF to consider in the optimization.\n");
  91. printf(" It defaults to the unit's current value.\n");
  92. printf(" -s | --min_spd <NUM> Minumum SPD to consider in the optimization.\n");
  93. printf(" It defaults to the unit's current value.\n");
  94. printf(" -c | --min_crr <NUM> Minumum CRIT RATE to consider in the optimization.\n");
  95. printf(" It defaults to the unit's current value.\n");
  96. printf(" -d | --min_crd <NUM> Minumum CRIT DAMAGE to consider in the optimization.\n");
  97. printf(" It defaults to the unit's current value.\n");
  98. printf(" -r | --min_res <NUM> Minumum RES to consider in the optimization.\n");
  99. printf(" It defaults to the unit's current value.\n");
  100. printf(" -f | --min_acc <NUM> Minumum ACC to consider in the optimization.\n");
  101. printf(" It defaults to the unit's current value.\n");
  102. printf(" -l | --level <LV> Level to consider the runes during the optimization.\n");
  103. printf(" It only affects the rune main stats. Valid values are\n");
  104. printf(" 'current', '12' and '15'. Default is 'current'\n");
  105. printf(" -t | --stats <ST1>,<ST2>... Stats than can be selected as mains for slots 2, 4 and 6.\n");
  106. printf(" Only the selected stats will be included, so this option\n");
  107. printf(" is mandatory. Accepted values are 'hp', 'atk', 'def',\n");
  108. printf(" 'hpflat', 'atkflat', 'defflat', 'spd', 'crr', 'crd',\n");
  109. printf(" 'res' and 'acc'. Values must be comma-separated, and up\n");
  110. printf(" to 12 can be included.\n");
  111. printf(" -e | --sets <S1>,<S2>... Rune sets that than can be considered during the optimization.\n");
  112. printf(" Only the selected sets will be included, so this option is\n");
  113. printf(" mandatory. Accepted values the rune net names, lowercase.\n");
  114. printf(" Values must be comma-separated, and up to 3 can be included.\n");
  115. return;
  116. }
  117. int optimize(int argc, char *argv[]){
  118. // Get unit identifier
  119. if (argc < 3){
  120. fprintf(stderr, "No unit specified for optimization\n");
  121. return ERROR_INPUT_NO_UNIT;
  122. }
  123. // Initialize values for the data to be read form arguments.
  124. int requested_level = 0;
  125. char requested_level_column[9] = "current_";
  126. int sets[3] = {0, 0, 0};
  127. int requested_stats[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
  128. struct Stats min_stats;
  129. // Unsigned, so I cant use -1 as placeholder to check if its modified from
  130. // arguments. I'm using just 1 (I don't think anybody will input values of
  131. // 1), and later, if they are still 1, they are changed to 0.
  132. min_stats.hp = 1;
  133. min_stats.atk = 1;
  134. min_stats.def = 1;
  135. min_stats.spd = 1;
  136. min_stats.crr = 1;
  137. min_stats.crd = 1;
  138. min_stats.res = 1;
  139. min_stats.acc = 1;
  140. // Loop command line arguments
  141. for (int i = 3; i < argc; i ++){
  142. // Rune level arguments
  143. if (strcmp("--level", argv[i]) == 0 || strcmp("-l", argv[i]) == 0){
  144. if (i < argc - 1){
  145. if (strcmp("current", argv[i + 1]) == 0){
  146. requested_level = 0;
  147. strcpy(requested_level_column, "current_");
  148. }
  149. else if (strcmp("12", argv[i + 1]) == 0){
  150. requested_level = 12;
  151. strcpy(requested_level_column, "lv12_");
  152. }
  153. else if (strcmp("15", argv[i + 1]) == 0){
  154. requested_level = 15;
  155. strcpy(requested_level_column, "lv15_");
  156. }
  157. else{
  158. fprintf(
  159. stderr,
  160. "Invalid option for argument %s: %s\n"
  161. "Valid options are 'current', '12' or '15'\n",
  162. argv[i], argv[i + 1]
  163. );
  164. return ERROR_INPUT_INVALID_LEVEL;
  165. }
  166. // Advance one position in argument reading
  167. i ++;
  168. }
  169. else{
  170. fprintf(
  171. stderr,
  172. "Argument %s requires a value.\n"
  173. "Valid options are 'current', '12' or '15'\n", argv[i]
  174. );
  175. return ERROR_INPUT_NO_LEVEL;
  176. }
  177. }
  178. // Rune sets argument
  179. else if (strcmp("--sets", argv[i]) == 0 || strcmp("-e", argv[i]) == 0){
  180. if (i < argc - 1){
  181. // Separate string by commas
  182. int j = 0;
  183. // Returns first token
  184. char *token = strtok(argv[i + 1], ",");
  185. // Keep printing tokens while one of the
  186. // delimiters present in the list, or until its complete
  187. while (token != NULL && j < 3){
  188. if (strcmp(token, "energy") == 0){
  189. sets[j] = ENERGY;
  190. }
  191. else if (strcmp(token, "guard") == 0){
  192. sets[j] = GUARD;
  193. }
  194. else if (strcmp(token, "swift") == 0){
  195. sets[j] = SWIFT;
  196. }
  197. else if (strcmp(token, "blade") == 0){
  198. sets[j] = BLADE;
  199. }
  200. else if (strcmp(token, "rage") == 0){
  201. sets[j] = RAGE;
  202. }
  203. else if (strcmp(token, "focus") == 0){
  204. sets[j] = FOCUS;
  205. }
  206. else if (strcmp(token, "endure") == 0){
  207. sets[j] = ENDURE;
  208. }
  209. else if (strcmp(token, "fatal") == 0){
  210. sets[j] = FATAL;
  211. }
  212. else if (strcmp(token, "despair") == 0){
  213. sets[j] = DESPAIR;
  214. }
  215. else if (strcmp(token, "vampire") == 0){
  216. sets[j] = VAMPIRE;
  217. }
  218. else if (strcmp(token, "violent") == 0){
  219. sets[j] = VIOLENT;
  220. }
  221. else if (strcmp(token, "nemesis") == 0){
  222. sets[j] = NEMESIS;
  223. }
  224. else if (strcmp(token, "will") == 0){
  225. sets[j] = WILL;
  226. }
  227. else if (strcmp(token, "shield") == 0){
  228. sets[j] = SHIELD;
  229. }
  230. else if (strcmp(token, "revenge") == 0){
  231. sets[j] = REVENGE;
  232. }
  233. else if (strcmp(token, "destroy") == 0){
  234. sets[j] = DESTROY;
  235. }
  236. else if (strcmp(token, "fight") == 0){
  237. sets[j] = FIGHT;
  238. }
  239. else if (strcmp(token, "determination") == 0){
  240. sets[j] = DETERMINATION;
  241. }
  242. else if (strcmp(token, "enhance") == 0){
  243. sets[j] = ENHANCE;
  244. }
  245. else if (strcmp(token, "accuracy") == 0){
  246. sets[j] = ACCURACY;
  247. }
  248. else if (strcmp(token, "tolerance") == 0){
  249. sets[j] = TOLERANCE;
  250. }
  251. else{
  252. fprintf(stderr, "Unknown rune set %s.\n", token);
  253. return ERROR_INPUT_INVALID_SET;
  254. }
  255. token = strtok(NULL, ",");
  256. j ++;
  257. }
  258. // Advance one position in argument reading
  259. i ++;
  260. }
  261. else{
  262. fprintf(
  263. stderr,
  264. "Argument %s requires a list of values separated by commas.\n",
  265. argv[i]
  266. );
  267. return ERROR_INPUT_NO_SET;
  268. }
  269. }
  270. // Accepted stats arguments
  271. else if (strcmp("--stats", argv[i]) == 0 || strcmp("-t", argv[i]) == 0){
  272. if (i < argc - 1){
  273. // Separate string by commas
  274. int j = 0;
  275. // Returns first token
  276. char *token = strtok(argv[i + 1], ",");
  277. // Keep printing tokens while one of the
  278. // delimiters present in the list, or until its complete
  279. while (token != NULL && j < 3){
  280. if (strcmp(token, "hp") == 0){
  281. requested_stats[j] = HP_PERCENT;
  282. }
  283. else if (strcmp(token, "atk") == 0){
  284. requested_stats[j] = ATK_PERCENT;
  285. }
  286. else if (strcmp(token, "def") == 0){
  287. requested_stats[j] = DEF_PERCENT;
  288. }
  289. else if (strcmp(token, "hpflat") == 0){
  290. requested_stats[j] = HP_FLAT;
  291. }
  292. else if (strcmp(token, "atkflat") == 0){
  293. requested_stats[j] = ATK_FLAT;
  294. }
  295. else if (strcmp(token, "defflat") == 0){
  296. requested_stats[j] = DEF_FLAT;
  297. }
  298. else if (strcmp(token, "spd") == 0){
  299. requested_stats[j] = SPD;
  300. }
  301. else if (strcmp(token, "crr") == 0){
  302. requested_stats[j] = CRR;
  303. }
  304. else if (strcmp(token, "crd") == 0){
  305. requested_stats[j] = CRD;
  306. }
  307. else if (strcmp(token, "res") == 0){
  308. requested_stats[j] = RES;
  309. }
  310. else if (strcmp(token, "acc") == 0){
  311. requested_stats[j] = ACC;
  312. }
  313. else{
  314. fprintf(stderr, "Unknown rune stat %s.\n", token);
  315. return ERROR_INPUT_INVALID_STAT;
  316. }
  317. token = strtok(NULL, ",");
  318. j ++;
  319. }
  320. // Advance one position in argument reading
  321. i ++;
  322. }
  323. else{
  324. fprintf(stderr, "Argument %s requires a list of values separated by commas.\n", argv[i]);
  325. return ERROR_INPUT_NO_STAT;
  326. }
  327. }
  328. // Minimum stats arguments.
  329. else if (strcmp("--min-hp", argv[i]) == 0 || strcmp("-h", argv[i]) == 0){
  330. if (i < argc - 1){
  331. unsigned short stat_tmp = atoi(argv[i + 1]);
  332. if (stat_tmp == 1){
  333. stat_tmp = 0;
  334. }
  335. min_stats.hp = stat_tmp;
  336. // Advance one position in argument reading
  337. i ++;
  338. }
  339. else{
  340. fprintf(stderr, "Argument %s requires a numeric value.\n", argv[i]);
  341. return ERROR_INPUT_NO_HP;
  342. }
  343. }
  344. else if (strcmp("--min-atk", argv[i]) == 0 || strcmp("-a", argv[i]) == 0){
  345. if (i < argc - 1){
  346. unsigned short stat_tmp = atoi(argv[i + 1]);
  347. if (stat_tmp == 1){
  348. stat_tmp = 0;
  349. }
  350. min_stats.atk = stat_tmp;
  351. // Advance one position in argument reading
  352. i ++;
  353. }
  354. else{
  355. fprintf(stderr, "Argument %s requires a numeric value.\n", argv[i]);
  356. return ERROR_INPUT_NO_ATK;
  357. }
  358. }
  359. else if (strcmp("--min-def", argv[i]) == 0 || strcmp("-d", argv[i]) == 0){
  360. if (i < argc - 1){
  361. unsigned short stat_tmp = atoi(argv[i + 1]);
  362. if (stat_tmp == 1){
  363. stat_tmp = 0;
  364. }
  365. min_stats.def = stat_tmp;
  366. // Advance one position in argument reading
  367. i ++;
  368. }
  369. else{
  370. fprintf(stderr, "Argument %s requires a numeric value.\n", argv[i]);
  371. return ERROR_INPUT_NO_DEF;
  372. }
  373. }
  374. else if (strcmp("--min-spd", argv[i]) == 0 || strcmp("-s", argv[i]) == 0){
  375. if (i < argc - 1){
  376. unsigned short stat_tmp = atoi(argv[i + 1]);
  377. if (stat_tmp == 1){
  378. stat_tmp = 0;
  379. }
  380. min_stats.spd = stat_tmp;
  381. // Advance one position in argument reading
  382. i ++;
  383. }
  384. else{
  385. fprintf(stderr, "Argument %s requires a numeric value.\n", argv[i]);
  386. return ERROR_INPUT_NO_SPD;
  387. }
  388. }
  389. else if (strcmp("--min-crr", argv[i]) == 0 || strcmp("-c", argv[i]) == 0){
  390. if (i < argc - 1){
  391. unsigned short stat_tmp = atoi(argv[i + 1]);
  392. if (stat_tmp == 1){
  393. stat_tmp = 0;
  394. }
  395. else if (stat_tmp > 100){
  396. stat_tmp = 100;
  397. }
  398. min_stats.crr = stat_tmp;
  399. // Advance one position in argument reading
  400. i ++;
  401. }
  402. else{
  403. fprintf(stderr, "Argument %s requires a numeric value.\n", argv[i]);
  404. return ERROR_INPUT_NO_CRR;
  405. }
  406. }
  407. else if (strcmp("--min-crd", argv[i]) == 0 || strcmp("-d", argv[i]) == 0){
  408. if (i < argc - 1){
  409. unsigned short stat_tmp = atoi(argv[i + 1]);
  410. if (stat_tmp == 1){
  411. stat_tmp = 0;
  412. }
  413. min_stats.crd = stat_tmp;
  414. // Advance one position in argument reading
  415. i ++;
  416. }
  417. else{
  418. fprintf(stderr, "Argument %s requires a numeric value.\n", argv[i]);
  419. return ERROR_INPUT_NO_CRD;
  420. }
  421. }
  422. else if (strcmp("--min-res", argv[i]) == 0 || strcmp("-r", argv[i]) == 0){
  423. if (i < argc - 1){
  424. unsigned short stat_tmp = atoi(argv[i + 1]);
  425. if (stat_tmp == 1){
  426. stat_tmp = 0;
  427. }
  428. else if (stat_tmp > 100){
  429. stat_tmp = 100;
  430. }
  431. min_stats.atk = stat_tmp;
  432. // Advance one position in argument reading
  433. i ++;
  434. }
  435. else{
  436. fprintf(stderr, "Argument %s requires a numeric value.\n", argv[i]);
  437. return ERROR_INPUT_NO_RES;
  438. }
  439. }
  440. else if (strcmp("--min-acc", argv[i]) == 0 || strcmp("-f", argv[i]) == 0){
  441. if (i < argc - 1){
  442. unsigned short stat_tmp = atoi(argv[i + 1]);
  443. if (stat_tmp == 1){
  444. stat_tmp = 0;
  445. }
  446. else if (stat_tmp > 85){
  447. stat_tmp = 85;
  448. }
  449. min_stats.acc = stat_tmp;
  450. // Advance one position in argument reading
  451. i ++;
  452. }
  453. else{
  454. fprintf(stderr, "Argument %s requires a numeric value.\n", argv[i]);
  455. return ERROR_INPUT_NO_ACC;
  456. }
  457. }
  458. }
  459. // Now we can open the database.
  460. if (SUCCESS != open_database()){
  461. return ERROR_DB_CANT_OPEN;
  462. }
  463. // Read the unit from the databse
  464. sqlite3_stmt *res;
  465. char *sql = "SELECT "
  466. "id, name, base_hp, base_atk, base_def, base_spd, base_crr, base_crd, "
  467. "base_res, base_acc, current_hp, current_atk, current_def, current_spd, "
  468. "current_crr, current_crd, current_res, current_acc "
  469. "FROM units WHERE id = ? OR name = ?";
  470. db_status = sqlite3_prepare_v2(db, sql, -1, &res, 0);
  471. if (db_status != SQLITE_OK) {
  472. fprintf(
  473. stderr,
  474. "Failed to execute statement to select unit: %s\n",
  475. sqlite3_errmsg(db)
  476. );
  477. return ERROR_DB_UNIT;
  478. }
  479. sqlite3_bind_text(res, 1, argv[2], strlen(argv[2]), NULL);
  480. sqlite3_bind_text(res, 2, argv[2], strlen(argv[2]), NULL);
  481. // Fetch just one line
  482. int step = sqlite3_step(res);
  483. // Create a unit structure with the red data.
  484. struct Unit unit;
  485. if (step != SQLITE_ROW) {
  486. fprintf(stderr, "Unit not found: %s\n", sqlite3_errmsg(db));
  487. return ERROR_DB_UNIT_NOF_FOUND;
  488. }
  489. strcpy(unit.id, sqlite3_column_text(res, 0));
  490. strcpy(unit.name, sqlite3_column_text(res, 1));
  491. unit.base_hp = sqlite3_column_int(res, 2);
  492. unit.base_atk = sqlite3_column_int(res, 3);
  493. unit.base_def = sqlite3_column_int(res, 4);
  494. unit.base_spd = sqlite3_column_int(res, 5);
  495. unit.base_crr = sqlite3_column_int(res, 6);
  496. unit.base_crd = sqlite3_column_int(res, 7);
  497. unit.base_res = sqlite3_column_int(res, 8);
  498. unit.base_acc = sqlite3_column_int(res, 9);
  499. unit.current_hp = sqlite3_column_int(res, 10);
  500. unit.current_atk = sqlite3_column_int(res, 11);
  501. unit.current_def = sqlite3_column_int(res, 12);
  502. unit.current_spd = sqlite3_column_int(res, 13);
  503. unit.current_crr = sqlite3_column_int(res, 14);
  504. unit.current_crd = sqlite3_column_int(res, 15);
  505. unit.current_res = sqlite3_column_int(res, 16);
  506. unit.current_acc = sqlite3_column_int(res, 17);
  507. // Min stats that have the value 1 get overriden by the current stats.
  508. if (min_stats.hp == 1){
  509. min_stats.hp = unit.current_hp;
  510. }
  511. if (min_stats.atk == 1){
  512. min_stats.atk = unit.current_atk;
  513. }
  514. if (min_stats.def == 1){
  515. min_stats.def = unit.current_def;
  516. }
  517. if (min_stats.spd == 1){
  518. min_stats.spd = unit.current_spd;
  519. }
  520. if (min_stats.crr == 1){
  521. min_stats.crr = unit.current_crr;
  522. }
  523. if (min_stats.crd == 1){
  524. min_stats.crd = unit.current_crd;
  525. }
  526. if (min_stats.res == 1){
  527. min_stats.res = unit.current_res;
  528. }
  529. if (min_stats.acc == 1){
  530. min_stats.acc = unit.current_acc;
  531. }
  532. // Calculate required rune count
  533. struct Rune_Set_Count requested_set_count;
  534. requested_set_count.energy = 0;
  535. requested_set_count.guard = 0;
  536. requested_set_count.swift = 0;
  537. requested_set_count.blade = 0;
  538. requested_set_count.rage = 0;
  539. requested_set_count.focus = 0;
  540. requested_set_count.endure = 0;
  541. requested_set_count.fatal = 0;
  542. requested_set_count.despair = 0;
  543. requested_set_count.vampire = 0;
  544. requested_set_count.violent = 0;
  545. requested_set_count.nemesis = 0;
  546. requested_set_count.will = 0;
  547. requested_set_count.shield = 0;
  548. requested_set_count.revenge = 0;
  549. requested_set_count.destroy = 0;
  550. requested_set_count.fight = 0;
  551. requested_set_count.determination = 0;
  552. requested_set_count.enhance = 0;
  553. requested_set_count.accuracy = 0;
  554. requested_set_count.tolerance = 0;
  555. char total_requested_runes = 0;
  556. for (int i = 0; i < 3; i ++){
  557. switch (sets[i]){
  558. case ENERGY:
  559. requested_set_count.energy += 2;
  560. total_requested_runes += 2;
  561. break;
  562. case GUARD:
  563. requested_set_count.guard += 2;
  564. total_requested_runes += 2;
  565. break;
  566. case SWIFT:
  567. requested_set_count.swift += 4;
  568. total_requested_runes += 4;
  569. break;
  570. case BLADE:
  571. requested_set_count.blade += 2;
  572. total_requested_runes += 2;
  573. break;
  574. case RAGE:
  575. requested_set_count.rage += 4;
  576. total_requested_runes += 4;
  577. break;
  578. case FOCUS:
  579. requested_set_count.focus += 2;
  580. total_requested_runes += 2;
  581. break;
  582. case ENDURE:
  583. requested_set_count.endure += 2;
  584. total_requested_runes += 2;
  585. break;
  586. case FATAL:
  587. requested_set_count.fatal += 4;
  588. total_requested_runes += 4;
  589. break;
  590. case DESPAIR:
  591. requested_set_count.despair += 2;
  592. total_requested_runes += 2;
  593. break;
  594. case VAMPIRE:
  595. requested_set_count.vampire += 4;
  596. total_requested_runes += 4;
  597. break;
  598. case VIOLENT:
  599. requested_set_count.violent += 4;
  600. total_requested_runes += 4;
  601. break;
  602. case NEMESIS:
  603. requested_set_count.nemesis += 2;
  604. total_requested_runes += 2;
  605. break;
  606. case WILL:
  607. requested_set_count.will += 2;
  608. total_requested_runes += 2;
  609. break;
  610. case SHIELD:
  611. requested_set_count.shield += 2;
  612. total_requested_runes += 2;
  613. break;
  614. case REVENGE:
  615. requested_set_count.revenge += 2;
  616. total_requested_runes += 2;
  617. break;
  618. case DESTROY:
  619. requested_set_count.destroy += 2;
  620. total_requested_runes += 2;
  621. break;
  622. case FIGHT:
  623. requested_set_count.fight += 2;
  624. total_requested_runes += 2;
  625. break;
  626. case DETERMINATION:
  627. requested_set_count.determination += 2;
  628. total_requested_runes += 2;
  629. break;
  630. case ENHANCE:
  631. requested_set_count.enhance += 2;
  632. total_requested_runes += 2;
  633. break;
  634. case ACCURACY:
  635. requested_set_count.accuracy += 2;
  636. total_requested_runes += 2;
  637. break;
  638. case TOLERANCE:
  639. requested_set_count.tolerance += 2;
  640. total_requested_runes += 2;
  641. break;
  642. }
  643. }
  644. printf("\n");
  645. if (total_requested_runes != 6){
  646. fprintf(stderr, "Invalid rune set combination.\n");
  647. return ERROR_INPUT_INCOMPLETE_SETS;
  648. }
  649. // Create queries for each slot
  650. char query_odd[2500] = "SELECT id, unit, type, ";
  651. strcat(query_odd, requested_level_column);
  652. strcat(query_odd, "hp_flat, ");
  653. strcat(query_odd, requested_level_column);
  654. strcat(query_odd, "atk_flat, ");
  655. strcat(query_odd, requested_level_column);
  656. strcat(query_odd, "def_flat, ");
  657. strcat(query_odd, requested_level_column);
  658. strcat(query_odd, "hp_percent, ");
  659. strcat(query_odd, requested_level_column);
  660. strcat(query_odd, "atk_percent, ");
  661. strcat(query_odd, requested_level_column);
  662. strcat(query_odd, "def_percent, ");
  663. strcat(query_odd, requested_level_column);
  664. strcat(query_odd, "spd, ");
  665. strcat(query_odd, requested_level_column);
  666. strcat(query_odd, "crr, ");
  667. strcat(query_odd, requested_level_column);
  668. strcat(query_odd, "crd, ");
  669. strcat(query_odd, requested_level_column);
  670. strcat(query_odd, "res, ");
  671. strcat(query_odd, requested_level_column);
  672. strcat(query_odd, "acc FROM runes WHERE slot = ? AND type IN (");
  673. char cur_set[2];
  674. char stat_set[2];
  675. if (sets[0] != 0){
  676. sprintf(cur_set, "%d", sets[0]);
  677. strcat(query_odd, cur_set);
  678. strcat(query_odd, ", ");
  679. }
  680. if (sets[1] != 0){
  681. sprintf(cur_set, "%d", sets[1]);
  682. strcat(query_odd, cur_set);
  683. strcat(query_odd, ", ");
  684. }
  685. if (sets[2] != 0){
  686. sprintf(cur_set, "%d", sets[2]);
  687. strcat(query_odd, cur_set);
  688. strcat(query_odd, ", ");
  689. }
  690. strcat(query_odd, "-1) ");
  691. // TODO: Add team stuff
  692. char query_even[1500] = "SELECT id, unit, type, ";
  693. strcat(query_even, requested_level_column);
  694. strcat(query_even, "hp_flat, ");
  695. strcat(query_even, requested_level_column);
  696. strcat(query_even, "atk_flat, ");
  697. strcat(query_even, requested_level_column);
  698. strcat(query_even, "def_flat, ");
  699. strcat(query_even, requested_level_column);
  700. strcat(query_even, "hp_percent, ");
  701. strcat(query_even, requested_level_column);
  702. strcat(query_even, "atk_percent, ");
  703. strcat(query_even, requested_level_column);
  704. strcat(query_even, "def_percent, ");
  705. strcat(query_even, requested_level_column);
  706. strcat(query_even, "spd, ");
  707. strcat(query_even, requested_level_column);
  708. strcat(query_even, "crr, ");
  709. strcat(query_even, requested_level_column);
  710. strcat(query_even, "crd, ");
  711. strcat(query_even, requested_level_column);
  712. strcat(query_even, "res, ");
  713. strcat(query_even, requested_level_column);
  714. strcat(query_even, "acc FROM runes WHERE slot = ? AND type IN (");
  715. if (sets[0] != 0){
  716. sprintf(cur_set, "%d", sets[0]);
  717. strcat(query_even, cur_set);
  718. strcat(query_even, ", ");
  719. }
  720. if (sets[1] != 0){
  721. sprintf(cur_set, "%d", sets[1]);
  722. strcat(query_even, cur_set);
  723. strcat(query_even, ", ");
  724. }
  725. if (sets[2] != 0){
  726. sprintf(cur_set, "%d", sets[2]);
  727. strcat(query_even, cur_set);
  728. strcat(query_even, ", ");
  729. }
  730. strcat(query_even, "-1) AND main_stat IN (");
  731. for (int i = 0; i < 12; i ++){
  732. if (requested_stats[i] != 0){
  733. sprintf(stat_set, "%d", requested_stats[i]);
  734. strcat(query_even, stat_set);
  735. strcat(query_even, ", ");
  736. }
  737. else{
  738. break;
  739. }
  740. }
  741. strcat(query_even, "-1) ");
  742. // TODO: Add team stuff
  743. // Get data for all the runes from the database.
  744. // Im using a 7 position array, and the index 0 is ignored. This is
  745. // is because I REALLY NEED to use 1-indexes to match rune slots.
  746. sqlite3_stmt *stmt_runes[7];
  747. // Im assumming 600 runes per slot is a safe estimate. I hope it doesn't
  748. // come back to bite me.
  749. struct Rune runes[7][600];
  750. db_status = sqlite3_prepare_v2(db, query_odd, -1, &stmt_runes[1], 0);
  751. if (db_status != SQLITE_OK) {
  752. printf("Error getting runes for slot 1: %s\n", sqlite3_errmsg(db));
  753. return ERROR_DB_RUNES_SLOT;
  754. }
  755. sqlite3_bind_int(stmt_runes[1], 1, 1);
  756. db_status = sqlite3_prepare_v2(db, query_odd, -1, &stmt_runes[3], 0);
  757. if (db_status != SQLITE_OK) {
  758. printf("Error getting runes for slot 3: %s\n", sqlite3_errmsg(db));
  759. return ERROR_DB_RUNES_SLOT;
  760. }
  761. sqlite3_bind_int(stmt_runes[3], 1, 3);
  762. db_status = sqlite3_prepare_v2(db, query_odd, -1, &stmt_runes[5], 0);
  763. if (db_status != SQLITE_OK) {
  764. printf("Error getting runes for slot 5: %s\n", sqlite3_errmsg(db));
  765. return ERROR_DB_RUNES_SLOT;
  766. }
  767. sqlite3_bind_int(stmt_runes[5], 1, 5);
  768. db_status = sqlite3_prepare_v2(db, query_odd, -1, &stmt_runes[2], 0);
  769. if (db_status != SQLITE_OK) {
  770. printf("Error getting runes for slot 2: %s\n", sqlite3_errmsg(db));
  771. return ERROR_DB_RUNES_SLOT;
  772. }
  773. sqlite3_bind_int(stmt_runes[2], 1, 2);
  774. db_status = sqlite3_prepare_v2(db, query_odd, -1, &stmt_runes[4], 0);
  775. if (db_status != SQLITE_OK) {
  776. printf("Error getting runes for slot 4: %s\n", sqlite3_errmsg(db));
  777. return ERROR_DB_RUNES_SLOT;
  778. }
  779. sqlite3_bind_int(stmt_runes[4], 1, 4);
  780. db_status = sqlite3_prepare_v2(db, query_odd, -1, &stmt_runes[6], 0);
  781. if (db_status != SQLITE_OK) {
  782. printf("Error getting runes for slot 6: %s\n", sqlite3_errmsg(db));
  783. return ERROR_DB_RUNES_SLOT;
  784. }
  785. sqlite3_bind_int(stmt_runes[6], 1, 6);
  786. // Get and populate all the runes
  787. int rune_count[7];
  788. for (int i = 1; i < 7; i ++){
  789. int j = 0;
  790. while (1 == 1){
  791. int status = sqlite3_step(stmt_runes[i]);
  792. if (status == SQLITE_ROW){
  793. strcpy(runes[i][j].id, sqlite3_column_text(stmt_runes[i], 0));
  794. strcpy(runes[i][j].unit, sqlite3_column_text(stmt_runes[i], 1));
  795. runes[i][j].set = sqlite3_column_int(stmt_runes[i], 2);
  796. runes[i][j].hp_flat = sqlite3_column_int(stmt_runes[i], 3);
  797. runes[i][j].atk_flat = sqlite3_column_int(stmt_runes[i], 4);
  798. runes[i][j].def_flat = sqlite3_column_int(stmt_runes[i], 5);
  799. runes[i][j].hp_percent = sqlite3_column_int(stmt_runes[i], 6);
  800. runes[i][j].atk_percent = sqlite3_column_int(stmt_runes[i], 7);
  801. runes[i][j].def_percent = sqlite3_column_int(stmt_runes[i], 8);
  802. runes[i][j].spd = sqlite3_column_int(stmt_runes[i], 9);
  803. runes[i][j].crr = sqlite3_column_int(stmt_runes[i], 10);
  804. runes[i][j].crd = sqlite3_column_int(stmt_runes[i], 11);
  805. runes[i][j].res = sqlite3_column_int(stmt_runes[i], 12);
  806. runes[i][j].acc = sqlite3_column_int(stmt_runes[i], 13);
  807. j ++;
  808. }
  809. else{
  810. break;
  811. }
  812. }
  813. rune_count[i] = j;
  814. }
  815. // If any slot doesn't have matching runes, we can stop now.
  816. for (int i = 1; i < 7; i ++){
  817. if (rune_count[i] == 0){
  818. printf("\n - No availbale runes for slot %d\n", i);
  819. return SUCCESS;
  820. }
  821. }
  822. printf("\n");
  823. unsigned long max_combinations =
  824. rune_count[1] *
  825. rune_count[2] *
  826. rune_count[3] *
  827. rune_count[4] *
  828. rune_count[5] *
  829. rune_count[6];
  830. // Print a nice summary before starting the long optimization.
  831. // This is an output example:
  832. //
  833. // ----------------------------------- Requested rune sets:
  834. // | Lushen | RAGE BLADE
  835. // | ID: 7223811472 |
  836. // ----------------------------------- Accepted stats:
  837. // | STAT | BASE | CURR. | MIN. | ATK CRR CRD
  838. // -----------------------------------
  839. // | HP: | 9225 | 12262 | 10000 | Considering runes at level 15
  840. // | ATK: | 900 | 2482 | 2482 |
  841. // | DEF: | 461 | 703 | 703 | Runes considered by slot:
  842. // | SPD: | 103 | 159 | 159 | 1: 27 2: 39 3: 25
  843. // | CRR: | 15% | 79% | 79% | 4: 35 5: 24 6: 32
  844. // | CRD: | 50% | 188% | 188% |
  845. // | RES: | 15% | 35% | 35% | Total combinations: 707616000
  846. // | ACC: | 0% | 11% | 10% |
  847. // -----------------------------------
  848. //
  849. // Im breaking the 80-characters-line rule here, but only 'cause I'd like
  850. // to remain sane.
  851. printf(" ----------------------------------- Requested rune sets:\n");
  852. printf(" | %*s | ", -31, unit.name);
  853. for (int i = 0; i < 3; i ++){
  854. if (sets[i] != 0){
  855. printf(" %s ", set_names[sets[i]]);
  856. }
  857. else{
  858. break;
  859. }
  860. }
  861. printf("\n");
  862. printf(" | ID: %*s |\n", -27, unit.id);
  863. printf(" ----------------------------------- Accepted stats:\n");
  864. printf(" | STAT | BASE | CURR. | MIN. | ");
  865. for (int i = 0; i < 12; i ++){
  866. if (requested_stats[i] != 0){
  867. printf(" %s ", stat_names[requested_stats[i]]);
  868. }
  869. else{
  870. break;
  871. }
  872. }
  873. printf("\n");
  874. printf(" -----------------------------------\n");
  875. printf(" | HP: | %*d | %*d | %*d | ", 5, unit.base_hp, 5, unit.current_hp, 5, min_stats.hp);
  876. if (requested_level == 0){
  877. printf("Using runes at their current level\n");
  878. }
  879. else{
  880. printf("Considering runes at level %d\n", requested_level);
  881. }
  882. printf(" | ATK: | %*d | %*d | %*d |\n", 5, unit.base_atk, 5, unit.current_atk, 5, min_stats.atk);
  883. printf(" | DEF: | %*d | %*d | %*d | Runes considered by slot:\n", 5, unit.base_def, 5, unit.current_def, 5, min_stats.def);
  884. printf(" | SPD: | %*d | %*d | %*d | 1:%*d 2:%*d 3:%*d\n", 5, unit.base_spd, 5, unit.current_spd, 5, min_stats.spd, 3, rune_count[1], 3, rune_count[2], 3, rune_count[3]);
  885. printf(" | CRR: | %*d\% | %*d\% | %*d\% | 4:%*d 5:%*d 6:%*d\n", 5, unit.base_crr, 5, unit.current_crr, 5, min_stats.crr, 3, rune_count[4], 3, rune_count[5], 3, rune_count[6]);
  886. printf(" | CRD: | %*d\% | %*d\% | %*d\% |\n", 5, unit.base_crd, 5, unit.current_crd, 5, min_stats.crd);
  887. printf(" | RES: | %*d\% | %*d\% | %*d\% | Total combinations: %d\n", 5, unit.base_res, 5, unit.current_res, 5, min_stats.res, max_combinations);
  888. printf(" | ACC: | %*d\% | %*d\% | %*d\% |\n", 5, unit.base_acc, 5, unit.current_acc, 5, min_stats.acc);
  889. printf(" -----------------------------------\n");
  890. // Now its time to loop all 6 'reels' of runes and try to match combos
  891. printf("\n\n__Optimization progress___________________________\n", max_combinations);
  892. // Initialize arrys and some counters
  893. int index[7] = {0, 0, 0, 0, 0, 0};
  894. unsigned long tested_combinations = 0;
  895. unsigned long valid_sets = 0;
  896. unsigned long result_count = 0;
  897. Result results[1000];
  898. while(
  899. index[1] < rune_count[1] &&
  900. index[2] < rune_count[2] &&
  901. index[3] < rune_count[3] &&
  902. index[4] < rune_count[4] &&
  903. index[5] < rune_count[5] &&
  904. index[6] < rune_count[6]
  905. ){
  906. // Progress bar, 50 characters to 100%
  907. if (
  908. (tested_combinations + 1) %
  909. (unsigned long)(max_combinations / 50)
  910. == 0
  911. ){
  912. printf("#");
  913. // Line buffered! need to flush after every char.
  914. fflush(stdout);
  915. }
  916. // Calculate rune sets at current indexes.
  917. struct Rune_Set_Count set_count;
  918. set_count.energy = 0;
  919. set_count.guard = 0;
  920. set_count.swift = 0;
  921. set_count.blade = 0;
  922. set_count.rage = 0;
  923. set_count.focus = 0;
  924. set_count.endure = 0;
  925. set_count.fatal = 0;
  926. set_count.despair = 0;
  927. set_count.vampire = 0;
  928. set_count.violent = 0;
  929. set_count.nemesis = 0;
  930. set_count.will = 0;
  931. set_count.shield = 0;
  932. set_count.revenge = 0;
  933. set_count.destroy = 0;
  934. set_count.fight = 0;
  935. set_count.determination = 0;
  936. set_count.enhance = 0;
  937. set_count.accuracy = 0;
  938. set_count.tolerance = 0;
  939. for (int i = 1; i < 7; i ++){
  940. switch (runes[i][index[i]].set){
  941. case ENERGY:
  942. set_count.energy ++;
  943. break;
  944. case GUARD:
  945. set_count.guard ++;
  946. break;
  947. case SWIFT:
  948. set_count.swift ++;
  949. break;
  950. case BLADE:
  951. set_count.blade ++;
  952. break;
  953. case RAGE:
  954. set_count.rage ++;
  955. break;
  956. case FOCUS:
  957. set_count.focus ++;
  958. break;
  959. case ENDURE:
  960. set_count.endure ++;
  961. break;
  962. case FATAL:
  963. set_count.fatal ++;
  964. break;
  965. case DESPAIR:
  966. set_count.despair ++;
  967. break;
  968. case VAMPIRE:
  969. set_count.vampire ++;
  970. break;
  971. case VIOLENT:
  972. set_count.violent ++;
  973. break;
  974. case NEMESIS:
  975. set_count.nemesis ++;
  976. break;
  977. case WILL:
  978. set_count.will ++;
  979. break;
  980. case SHIELD:
  981. set_count.shield ++;
  982. break;
  983. case REVENGE:
  984. set_count.revenge ++;
  985. break;
  986. case DESTROY:
  987. set_count.destroy ++;
  988. break;
  989. case FIGHT:
  990. set_count.fight ++;
  991. break;
  992. case DETERMINATION:
  993. set_count.determination ++;
  994. break;
  995. case ENHANCE:
  996. set_count.enhance ++;
  997. break;
  998. case ACCURACY:
  999. set_count.accuracy ++;
  1000. break;
  1001. case TOLERANCE:
  1002. set_count.tolerance ++;
  1003. break;
  1004. }
  1005. }
  1006. // Compare with requested sets
  1007. if (
  1008. set_count.energy >= requested_set_count.energy &&
  1009. set_count.guard >= requested_set_count.guard &&
  1010. set_count.swift >= requested_set_count.swift &&
  1011. set_count.blade >= requested_set_count.blade &&
  1012. set_count.rage >= requested_set_count.rage &&
  1013. set_count.focus >= requested_set_count.focus &&
  1014. set_count.endure >= requested_set_count.endure &&
  1015. set_count.fatal >= requested_set_count.fatal &&
  1016. set_count.despair >= requested_set_count.despair &&
  1017. set_count.vampire >= requested_set_count.vampire &&
  1018. set_count.violent >= requested_set_count.violent &&
  1019. set_count.nemesis >= requested_set_count.nemesis &&
  1020. set_count.will >= requested_set_count.will &&
  1021. set_count.shield >= requested_set_count.shield &&
  1022. set_count.revenge >= requested_set_count.revenge &&
  1023. set_count.destroy >= requested_set_count.destroy &&
  1024. set_count.fight >= requested_set_count.fight &&
  1025. set_count.determination >= requested_set_count.determination &&
  1026. set_count.enhance >= requested_set_count.enhance &&
  1027. set_count.accuracy >= requested_set_count.accuracy &&
  1028. set_count.tolerance >= requested_set_count.tolerance
  1029. ){
  1030. // The current runes form a valid set.
  1031. valid_sets ++;
  1032. // Calculate new stats
  1033. struct Stats stats;
  1034. stats.hp = unit.base_hp;
  1035. stats.atk = unit.base_atk;
  1036. stats.def = unit.base_def;
  1037. stats.spd = unit.base_spd;
  1038. stats.crr = unit.base_crr;
  1039. stats.crd = unit.base_crd;
  1040. stats.res = unit.base_res;
  1041. stats.acc = unit.base_acc;
  1042. for (int i = 1; i < 7; i ++){
  1043. stats.hp += runes[i][index[i]].hp_flat;
  1044. stats.atk += runes[i][index[i]].atk_flat;
  1045. stats.def += runes[i][index[i]].def_flat;
  1046. stats.hp += unit.base_hp * runes[i][index[i]].hp_percent / 100;
  1047. stats.atk +=
  1048. unit.base_hp * runes[i][index[i]].atk_percent / 100;
  1049. stats.def +=
  1050. unit.base_hp * runes[i][index[i]].def_percent / 100;
  1051. stats.spd += runes[i][index[i]].spd;
  1052. stats.crr += runes[i][index[i]].crr;
  1053. stats.crd += runes[i][index[i]].crd;
  1054. stats.res += runes[i][index[i]].res;
  1055. stats.acc += runes[i][index[i]].acc;
  1056. }
  1057. // Compare with minimum requeriments
  1058. if (
  1059. stats.hp >= min_stats.hp &&
  1060. stats.atk >= min_stats.atk &&
  1061. stats.def >= min_stats.def &&
  1062. stats.spd >= min_stats.spd &&
  1063. stats.crr >= min_stats.crr &&
  1064. stats.crd >= min_stats.crd &&
  1065. stats.res >= min_stats.res &&
  1066. stats.acc >= min_stats.acc
  1067. ){
  1068. // This is a valid sets and all stats are above the minimum.
  1069. // Create a result with rune indexes, stats, and rating.
  1070. for (int i = 1; i < 7; i ++){
  1071. strcpy(
  1072. results[result_count].rune_ids[i - 1],
  1073. runes[i][index[i]].id
  1074. );
  1075. }
  1076. results[result_count].stats.hp = stats.hp;
  1077. results[result_count].stats.atk = stats.atk;
  1078. results[result_count].stats.def = stats.def;
  1079. results[result_count].stats.spd = stats.spd;
  1080. results[result_count].stats.crr = stats.crr;
  1081. results[result_count].stats.crd = stats.crd;
  1082. results[result_count].stats.res = stats.res;
  1083. results[result_count].stats.acc = stats.acc;
  1084. // One rating point per stat increase over current stats.
  1085. // Save for HP, with takes 15 for a raing point
  1086. results[result_count].rating = 0;
  1087. results[result_count].rating += ((stats.hp - unit.current_hp) / 15);
  1088. results[result_count].rating += (stats.atk - unit.current_atk);
  1089. results[result_count].rating += (stats.def - unit.current_def);
  1090. results[result_count].rating += (stats.spd - unit.current_spd);
  1091. results[result_count].rating += (stats.crr - unit.current_crr);
  1092. results[result_count].rating += (stats.crd - unit.current_crd);
  1093. results[result_count].rating += (stats.res - unit.current_res);
  1094. results[result_count].rating += (stats.acc - unit.current_acc);
  1095. result_count ++;
  1096. }
  1097. }
  1098. // Loop control. Rotate the reels 'right to left'
  1099. tested_combinations ++;
  1100. index[6] ++;
  1101. if (index[6] == rune_count[6]){
  1102. index[6] = 0;
  1103. index[5] ++;
  1104. }
  1105. if (index[5] == rune_count[5]){
  1106. index[5] = 0;
  1107. index[4] ++;
  1108. }
  1109. if (index[4] == rune_count[4]){
  1110. index[4] = 0;
  1111. index[3] ++;
  1112. }
  1113. if (index[3] == rune_count[3]){
  1114. index[3] = 0;
  1115. index[2] ++;
  1116. }
  1117. if (index[2] == rune_count[2]){
  1118. index[2] = 0;
  1119. index[1] ++;
  1120. }
  1121. //DEBUG: force exit with some results TODO
  1122. //if (result_count > 1){
  1123. // break;
  1124. //}
  1125. }
  1126. printf("\n");
  1127. if (result_count > 0){
  1128. // Yay! Some combinations matched te criteria.
  1129. printf("\n\n%d results found\n", result_count);
  1130. // Sort results
  1131. sort_results(results, result_count);
  1132. // Preview the best option
  1133. printf("\nPresenting best option:\n\n");
  1134. printf(" --------------------------\n");
  1135. printf(" | %*s |\n", -22, unit.name);
  1136. printf(" | ID: %*s |\n", -18, unit.id);
  1137. printf(" --------------------------\n");
  1138. printf(" | STAT | CURR. | NEW |\n");
  1139. printf(" --------------------------\n");
  1140. printf(" | HP: | %*d | %*d |\n", 5, unit.current_hp, 5, results[0].stats.hp);
  1141. printf(" | ATK: | %*d | %*d |\n", 5, unit.current_atk, 5, results[0].stats.atk);
  1142. printf(" | DEF: | %*d | %*d |\n", 5, unit.current_def, 5, results[0].stats.def);
  1143. printf(" | SPD: | %*d | %*d |\n", 5, unit.current_spd, 5, results[0].stats.spd);
  1144. printf(" | CRR: | %*d\% | %*d\% |\n", 5, unit.current_crr, 5, results[0].stats.crr);
  1145. printf(" | CRD: | %*d\% | %*d\% |\n", 5, unit.current_crd, 5, results[0].stats.crd);
  1146. printf(" | RES: | %*d\% | %*d\% |\n", 5, unit.current_res, 5, results[0].stats.res);
  1147. printf(" | ACC: | %*d\% | %*d\% |\n", 5, unit.current_acc, 5, results[0].stats.acc);
  1148. printf(" --------------------------\n");
  1149. // This bit may be hard to follow.
  1150. // I'm populating 8 lines of text with data, to display the runes in
  1151. // a nice table format.
  1152. //
  1153. // This is an output exaple:
  1154. //
  1155. // ------------------------------ ------------------------------ ------------------------------
  1156. // |6|RAGE | 23285581330| |1|BLADE | 22677809846| |2|BLADE | 21432847749|
  1157. // ------------------------------ ------------------------------ ------------------------------
  1158. // | Storage | +12 | | Storage | +12 | | Perna | +15 |
  1159. // ------------------------------ ------------------------------ ------------------------------
  1160. // | ACC 48 | | ATK_FLAT 118 | | SPD 42 |
  1161. // | RES 6 | | | | |
  1162. // | ATK_FLAT 19 | | RES 14 | | CRD 16 |
  1163. // | CRR 12 | | ACC 8 | | CRR 10 |
  1164. // | HP 14 | | HP_FLAT 580 | | ATK 7 + 3 |
  1165. // | CRD 14 | | CRR 10 | | DEF 7 + 3 |
  1166. // ------------------------------ ------------------------------ ------------------------------
  1167. //
  1168. // ------------------------------ ------------------------------ ------------------------------
  1169. // |5|RAGE | 26260912967| |4|RAGE | 27947761086| |3|RAGE | 27654723287|
  1170. // ------------------------------ ------------------------------ ------------------------------
  1171. // | Lushen | +15 | | Lushen | +15 | | Covenant | +12 |
  1172. // ------------------------------ ------------------------------ ------------------------------
  1173. // | HP_FLAT 2448 | | CRD 80 | | DEF_FLAT 118 |
  1174. // | | | | | HP_FLAT 167 |
  1175. // | CRR 16 | | CRR 6 | | RES 8 |
  1176. // | SPD 6 + 2 | | RES 11 | | DEF 11 |
  1177. // | CRD 11 | | SPD 18 | | CRD 18 |
  1178. // | ATK 6 + 5 | | ATK 8 | | CRR 12 |
  1179. // ------------------------------ ------------------------------ ------------------------------
  1180. //
  1181. // Again, Im breaking the 80-characters-line rule.
  1182. char present[8][130];
  1183. strcpy(present[0], "");
  1184. strcpy(present[1], "");
  1185. strcpy(present[2], "");
  1186. strcpy(present[4], "");
  1187. strcpy(present[5], "");
  1188. strcpy(present[6], "");
  1189. strcpy(present[7], "");
  1190. // Retrieve the rune stats from the database
  1191. printf("\n");
  1192. for (int i = 6; i != 0;){
  1193. char *rune_id = results[0].rune_ids[i - 1];
  1194. sqlite3_stmt *rune_res;
  1195. char *sql =
  1196. "SELECT runes.id, runes.slot, runes.type, units.id, units.name, runes.level "
  1197. "FROM runes LEFT JOIN units ON runes.unit = units.id "
  1198. "WHERE runes.id = ?";
  1199. db_status = sqlite3_prepare_v2(db, sql, -1, &rune_res, 0);
  1200. if (db_status == SQLITE_OK) {
  1201. sqlite3_bind_text(rune_res, 1, results[0].rune_ids[i - 1], strlen(results[0].rune_ids[i - 1]), NULL);
  1202. }
  1203. else {
  1204. fprintf(stderr, "Failed to execute statement to select rune: %s\n", sqlite3_errmsg(db));
  1205. return ERROR_DB_RUNES_RESULT;
  1206. }
  1207. int step = sqlite3_step(rune_res);
  1208. if (step == SQLITE_ROW) {
  1209. char tmp[64];
  1210. strcpy(tmp, "");
  1211. strcat(present[0], "|");
  1212. sprintf(tmp, "%*d", 1, sqlite3_column_int(rune_res, 1));
  1213. strcat(present[0], tmp);
  1214. strcat(present[0], "|");
  1215. sprintf(tmp, "%*s|", -13, set_names[sqlite3_column_int(rune_res, 2)]);
  1216. strcat(present[0], tmp);
  1217. sprintf(tmp, "%*s| ", 12, sqlite3_column_text(rune_res, 0));
  1218. strcat(present[0], tmp);
  1219. strcat(present[1], "|");
  1220. if (sqlite3_column_type(rune_res, 3) == SQLITE_NULL){
  1221. sprintf(tmp, " %*s", -14, "Storage");
  1222. strcat(present[1], tmp);
  1223. strcat(present[1], "|");
  1224. }
  1225. else{
  1226. sprintf(tmp, " %*s", -14, sqlite3_column_text(rune_res, 4));
  1227. strcat(present[1], tmp);
  1228. strcat(present[1], "|");
  1229. }
  1230. // Rune level
  1231. sprintf(tmp, " +%*s | ", 2, sqlite3_column_text(rune_res, 5));
  1232. strcat(present[1], tmp);
  1233. sqlite3_stmt *stats_res;
  1234. char *sql_stats =
  1235. "SELECT rune, slot, stat, value, enchant, grind "
  1236. "FROM rune_stats "
  1237. "WHERE rune = ?";
  1238. db_status = sqlite3_prepare_v2(db, sql_stats, -1, &stats_res, 0);
  1239. if (db_status == SQLITE_OK) {
  1240. sqlite3_bind_text(stats_res, 1, sqlite3_column_text(rune_res, 0), strlen(sqlite3_column_text(rune_res, 0)), NULL);
  1241. }
  1242. else {
  1243. fprintf(stderr, "Failed to execute statement to select rune stats: %s\n", sqlite3_errmsg(db));
  1244. return ERROR_DB_STATS_RESULT;
  1245. }
  1246. int curr_slot = -1;
  1247. while (1 == 1){
  1248. int status = sqlite3_step(stats_res);
  1249. //printf("STATUS: %d ", status);
  1250. if (status == SQLITE_ROW){
  1251. // Print empty lines for no-stats
  1252. while (sqlite3_column_int(stats_res, 1) != curr_slot){
  1253. sprintf(tmp, "| | ");
  1254. strcat(present[curr_slot + 3], tmp);
  1255. curr_slot ++;
  1256. }
  1257. if (sqlite3_column_int(stats_res, 1) == curr_slot){
  1258. strcat(present[curr_slot + 3], "| ");
  1259. sprintf(tmp, "%*s", -9, stat_names[sqlite3_column_int(stats_res, 2)]);
  1260. strcat(present[curr_slot + 3], tmp);
  1261. sprintf(tmp, " %*d", 6, sqlite3_column_int(stats_res, 3));
  1262. strcat(present[curr_slot + 3], tmp);
  1263. // Display grinds
  1264. if (sqlite3_column_int(stats_res, 5) > 0){
  1265. sprintf(tmp, " + %*d", -8, sqlite3_column_int(stats_res, 5));
  1266. }
  1267. else{
  1268. sprintf(tmp, " ");
  1269. }
  1270. strcat(present[curr_slot + 3], tmp);
  1271. strcat(present[curr_slot + 3], "| ");
  1272. }
  1273. curr_slot ++;
  1274. }
  1275. else{
  1276. break;
  1277. }
  1278. }
  1279. sqlite3_finalize(stats_res);
  1280. }
  1281. else{
  1282. fprintf(stderr, "BAD ROW: %s\n", sqlite3_errmsg(db));
  1283. }
  1284. sqlite3_finalize(rune_res);
  1285. // Loop control
  1286. // It's weird, but I wanna present the runes in the same format the
  1287. // game does:
  1288. //
  1289. // 6 1 2
  1290. // 5 4 3
  1291. // After slots 2 and 3, the generated strings are written.
  1292. if (i == 6){
  1293. //printf("-6->1-");
  1294. i = 1;
  1295. }
  1296. else if (i == 1){
  1297. //printf("-1->2-");
  1298. i = 2;
  1299. }
  1300. else if (i == 2){
  1301. // Print and reinitialize the strings
  1302. printf("------------------------------ ------------------------------ ------------------------------\n");
  1303. printf(present[0]);
  1304. printf("\n------------------------------ ------------------------------ ------------------------------\n");
  1305. printf(present[1]);
  1306. printf("\n------------------------------ ------------------------------ ------------------------------\n");
  1307. printf(present[2]);
  1308. printf("\n");
  1309. printf(present[3]);
  1310. printf("\n");
  1311. printf(present[4]);
  1312. printf("\n");
  1313. printf(present[5]);
  1314. printf("\n");
  1315. printf(present[6]);
  1316. printf("\n");
  1317. printf(present[7]);
  1318. printf("\n------------------------------ ------------------------------ ------------------------------\n");
  1319. strcpy(present[0], "");
  1320. strcpy(present[1], "");
  1321. strcpy(present[2], "");
  1322. strcpy(present[3], "");
  1323. strcpy(present[4], "");
  1324. strcpy(present[5], "");
  1325. strcpy(present[6], "");
  1326. strcpy(present[7], "");
  1327. //printf("-2->5-");
  1328. i = 5;
  1329. }
  1330. else if (i == 5){
  1331. //printf("-5->4-");
  1332. i = 4;
  1333. }
  1334. else if (i == 4){
  1335. //printf("-4->3-");
  1336. i = 3;
  1337. }
  1338. else if (i == 3){
  1339. // Print and exit loop
  1340. printf("\n------------------------------ ------------------------------ ------------------------------\n");
  1341. printf(present[0]);
  1342. printf("\n------------------------------ ------------------------------ ------------------------------\n");
  1343. printf(present[1]);
  1344. printf("\n------------------------------ ------------------------------ ------------------------------\n");
  1345. printf(present[2]);
  1346. printf("\n");
  1347. printf(present[3]);
  1348. printf("\n");
  1349. printf(present[4]);
  1350. printf("\n");
  1351. printf(present[5]);
  1352. printf("\n");
  1353. printf(present[6]);
  1354. printf("\n");
  1355. printf(present[7]);
  1356. printf("\n------------------------------ ------------------------------ ------------------------------\n");
  1357. //printf("-3->0-");
  1358. i = 0;
  1359. }
  1360. }
  1361. }
  1362. else{
  1363. printf("No results found\n");
  1364. }
  1365. sqlite3_close(db);
  1366. return SUCCESS;
  1367. }
  1368. void sort_results(Result results[1000], int total){
  1369. // Bubble sort, by descending rating.
  1370. int i, j;
  1371. Result temp;
  1372. for (i = 0; i < total - 1; i++)
  1373. {
  1374. for (j = 0; j < (total - 1-i); j++)
  1375. {
  1376. if (results[j].rating < results[j + 1].rating)
  1377. {
  1378. temp = results[j];
  1379. results[j] = results[j + 1];
  1380. results[j + 1] = temp;
  1381. }
  1382. }
  1383. }
  1384. }