galaxy.cpp 30 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045
  1. #include "galaxy.h"
  2. #include <algorithm> // sort
  3. #include <boost/format.hpp>
  4. #include <chrono>
  5. #include <exception>
  6. #include <fstream>
  7. #include <ostream>
  8. #include <set>
  9. #include <string>
  10. #include "config.h"
  11. #include "json.hpp"
  12. #include "logging.h"
  13. using json = nlohmann::json;
  14. // c++ default exceptions list
  15. // https://en.cppreference.com/w/cpp/error/exception
  16. std::ostream &operator<<(std::ostream &os, const port &p) {
  17. if (p.type == 0) {
  18. os << p.sector << ": " << (int)p.type;
  19. } else {
  20. os << p.sector << ": " << (int)p.type << " " << text_from_type(p.type)
  21. << " " << p.amount[0] << "," << p.amount[1] << "," << p.amount[2];
  22. }
  23. return os;
  24. }
  25. /**
  26. * Is port unknown?
  27. *
  28. * As in we haven't visited it, we don't know what it has?
  29. * We were checking percent to 0, but we've seen valid ports with 0 percent.
  30. * We now check amount. If this becomes an issue, we'll change to an unknown
  31. * flag.
  32. *
  33. * @return true
  34. * @return false
  35. */
  36. bool port::unknown(void) {
  37. for (int x = 0; x < 3; ++x) {
  38. if (amount[x] != 0) return false;
  39. }
  40. return true;
  41. }
  42. density_scan::density_scan() { reset(0); }
  43. void density_scan::reset(sector_type s) {
  44. sector = s;
  45. pos = 0;
  46. for (int x = 0; x < (int)d.size(); ++x) {
  47. d[x].sector = 0;
  48. }
  49. }
  50. void density_scan::add_scan(density den) {
  51. if (pos > (int)d.size()) {
  52. std::string message("density_scan::add_scan() exceeded max size ");
  53. message.append(std::to_string(d.size()));
  54. throw std::out_of_range(message);
  55. }
  56. d[pos] = den;
  57. ++pos;
  58. }
  59. density density_scan::find(sector_type sector) {
  60. for (int x = 0; x < pos; ++x) {
  61. if (d[x].sector == sector) return d[x];
  62. }
  63. BUGZ_LOG(fatal) << "density_scan::find failed: " << sector << " we have "
  64. << pos << " scans.";
  65. density den;
  66. den.sector = 0;
  67. return den;
  68. }
  69. bool operator==(const density lhs, const density rhs) {
  70. return ((lhs.sector == rhs.sector) && (lhs.density == rhs.density) &&
  71. (lhs.navhaz == rhs.navhaz) && (lhs.anomaly == rhs.anomaly) &&
  72. (lhs.warps == rhs.warps) && (lhs.known == rhs.known));
  73. }
  74. sector_warps::sector_warps() { sector = 0; }
  75. sector_warps::sector_warps(sector_type s, std::set<sector_type> w) {
  76. sector = s;
  77. warps = w;
  78. }
  79. void sector_warps::add(sector_type new_sector) {
  80. warps.insert(new_sector);
  81. /*
  82. for (int x = 0; x < MAX_WARPS; ++x) {
  83. if (warps[x] == new_sector) return;
  84. if (warps[x] == 0) {
  85. warps[x] = new_sector;
  86. return;
  87. }
  88. }
  89. std::string message = str(boost::format("More then MAX %1% sectors for %2%") %
  90. MAX_WARPS % (int)sector);
  91. throw std::out_of_range(message);
  92. */
  93. }
  94. std::ostream &operator<<(std::ostream &os, const sector_warps &warps) {
  95. os << "Sector: " << warps.sector << " ";
  96. bool comma = false;
  97. for (auto const &warp : warps.warps) {
  98. if (comma)
  99. os << ",";
  100. else
  101. comma = true;
  102. os << warp;
  103. }
  104. return os;
  105. }
  106. #define GTEST_COUT std::cerr << "[ ] [ INFO ]"
  107. // #define GTEST_DEBUG
  108. // TODO: fix this. I want some trace output, but I don't want
  109. // my logs flooded ...
  110. struct port parse_portcim(const std::string line) {
  111. struct port p;
  112. p.sector = std::stoi(line);
  113. // 20 - 1708 97% - 710 56% 287 15%
  114. // [15954 - 1990 100% 20910 100% 32880 100% ]
  115. // SL: [ 4591 - 395 47% -15822 84% 19830 100% ]
  116. static std::regex portrx(
  117. "[ ]*([0-9]+) (.)[ ]*([0-9]+)[ ]+([0-9]+%) (.)[ "
  118. "]*([0-9]+)[ ]+([0-9]+%) (.)[ ]*([0-9]+)[ ]+([0-9]+%)[ ]*",
  119. std::regex_constants::ECMAScript);
  120. // does it not understand {3} ??
  121. // NO, it does not, from regex101.com:
  122. // A repeated capturing group will only capture the last iteration. Put a
  123. // capturing group around the repeated group to capture all iterations or use
  124. // a non-capturing group instead if you're not interested in the data
  125. //
  126. // static std::regex portrx("[ ]*([0-9]+)( (.)[ ]+([0-9]+)[ ]+([0-9]+%)){3}[
  127. // ]*",
  128. // std::regex_constants::ECMAScript);
  129. // sector + amount pct + amount pct + amount pct
  130. // 1 2 3 4 5 6 7 8 9 10
  131. #ifdef GTEST_DEBUG
  132. GTEST_COUT << "Sector: " << p.sector << std::endl;
  133. GTEST_COUT << "Line: [" << line << "]" << std::endl;
  134. #endif
  135. buysell port_buysell;
  136. std::smatch matches;
  137. if (std::regex_match(line, matches, portrx)) {
  138. #ifdef GTEST_DEBUG
  139. for (size_t x = 1; x < matches.size(); ++x) {
  140. GTEST_COUT << x << " : " << matches[x] << std::endl;
  141. }
  142. #endif
  143. if (matches.size() != 11) {
  144. #ifdef GTEST_DEBUG
  145. GTEST_COUT << "Now you have 101 problems." << std::endl;
  146. #endif
  147. p.sector = 0;
  148. p.type = 0;
  149. return p;
  150. }
  151. // GTEST_COUT << "matches: " << matches.size() << std::endl;
  152. p.sector = stoi(matches[1]);
  153. // GTEST_COUT << "sector: " << matches[1] << std::endl;
  154. // for (int x = 1; x < 11; ++x) {
  155. // GTEST_COUT << x << " : " << matches[x] << std::endl;
  156. // }
  157. for (int x = 0; x < 3; ++x) {
  158. int pos = x * 3;
  159. port_buysell.foe[x] = matches[pos + 2] == "-";
  160. p.amount[x] = stoi(matches[pos + 3]);
  161. p.percent[x] = stoi(matches[pos + 4]);
  162. }
  163. p.type = type_from_buysell(port_buysell);
  164. #ifdef GTEST_DEBUG
  165. GTEST_COUT << "port is type " << (int)p.type << std::endl;
  166. #endif
  167. return p;
  168. } else {
  169. #ifdef GTEST_DEBUG
  170. GTEST_COUT << "regex_match failed." << std::endl;
  171. #endif
  172. p.type = 0;
  173. p.sector = 0;
  174. return p;
  175. }
  176. }
  177. Galaxy::Galaxy() { burnt_percent = 40; }
  178. Galaxy::~Galaxy() { BUGZ_LOG(fatal) << "Galaxy::~Galaxy()"; }
  179. void Galaxy::reset(void) {
  180. meta = json();
  181. config = json();
  182. ports.clear();
  183. warps.clear();
  184. }
  185. void Galaxy::add_warp(sector_warps sw) {
  186. auto pos = warps.find(sw.sector);
  187. if (pos == warps.end()) {
  188. // not found
  189. // sw.sort();
  190. warps[sw.sector] = sw;
  191. // BUGZ_LOG(info) << "add_warp NEW " << sw.sector;
  192. } else {
  193. // found!
  194. if (pos->second.warps == sw.warps) {
  195. // BUGZ_LOG(trace) << "add_warp: Yup, I already know about " << sw.sector;
  196. } else {
  197. BUGZ_LOG(info) << "add_warp: Warps don't match! Updating...";
  198. BUGZ_LOG(warning) << "Have: " << pos->second;
  199. BUGZ_LOG(warning) << "Got : " << sw;
  200. warps[sw.sector] = sw;
  201. }
  202. }
  203. }
  204. void Galaxy::add_port(sector_type sector, int port_type) {
  205. auto pos = ports.find(sector);
  206. if (pos == ports.end()) {
  207. // no such port.
  208. port p;
  209. p.sector = sector;
  210. p.type = port_type;
  211. for (int x = 0; x < 3; x++) {
  212. p.amount[x] = 0;
  213. p.percent[x] = 0;
  214. }
  215. // BUGZ_LOG(trace) << "add_port: " << sector << ", " << port_type << " : "
  216. // << p;
  217. ports[sector] = p;
  218. } else {
  219. // port was found, so:
  220. if (pos->second.type == port_type) {
  221. // BUGZ_LOG(trace) << "add_port: Yup, port " << sector << " is class " <<
  222. // port_type;
  223. } else {
  224. BUGZ_LOG(fatal) << "add_port: " << sector << " shows " << pos->second.type
  225. << " >> set to " << port_type;
  226. pos->second.type = port_type;
  227. }
  228. }
  229. }
  230. void Galaxy::add_port(port p) {
  231. auto pos = ports.find(p.sector);
  232. if (pos == ports.end()) {
  233. // BUGZ_LOG(trace) << "add_port: NEW " << p;
  234. ports[p.sector] = p;
  235. } else {
  236. if (pos->second.type != p.type) {
  237. if ((pos->second.type == 9) && (p.type == 8)) {
  238. BUGZ_LOG(trace) << "add_port: StarDock " << p.sector;
  239. p.type = 9;
  240. ports[p.sector] = p;
  241. } else {
  242. BUGZ_LOG(fatal) << "add_port: " << pos->second << " NEW : " << p;
  243. ports[p.sector] = p;
  244. }
  245. } else {
  246. if (pos->second.amount != p.amount) {
  247. // BUGZ_LOG(info) << "add_port: UPDATE " << p.sector;
  248. pos->second = p;
  249. } else {
  250. // BUGZ_LOG(info) << "add_port: Yup " << p.sector;
  251. }
  252. }
  253. }
  254. }
  255. void Galaxy::load(void) {
  256. std::string basename = "galaxy";
  257. if (CONFIG.contains("basename")) {
  258. basename = json_str(CONFIG["basename"]);
  259. }
  260. std::string filename =
  261. str(boost::format("%1%-%2%-%3%.json") % basename % game % username);
  262. // reset ?
  263. meta = json();
  264. config = json();
  265. ports.clear();
  266. warps.clear();
  267. if (file_exists(filename)) {
  268. BUGZ_LOG(fatal) << "Galaxy::load( " << filename << " )";
  269. json data;
  270. {
  271. std::ifstream fin(filename);
  272. data = json::parse(fin);
  273. }
  274. if (data.contains("meta")) {
  275. meta = data["meta"];
  276. } else {
  277. BUGZ_LOG(fatal) << "Missing meta data.";
  278. }
  279. meta["load_from"] = filename;
  280. meta["load_time"] = time_t_now(); // time_t
  281. if (data.contains("config")) {
  282. config = data["config"];
  283. } else {
  284. BUGZ_LOG(fatal) << "Missing config section.";
  285. }
  286. if (data.contains("ports")) {
  287. // const json &ports = data["ports"];
  288. for (auto const &port_iter : data["ports"].items()) {
  289. port p;
  290. p.sector = sstoi(port_iter.key()); // first.as<int>();
  291. p.type = port_iter.value()["class"].get<int>();
  292. int x;
  293. if (p.type == 0) {
  294. // nothing to load for type = 0, set defaults.
  295. for (x = 0; x < 3; ++x) {
  296. p.amount[x] = 0;
  297. p.percent[x] = 0;
  298. }
  299. } else {
  300. x = 0;
  301. for (auto const &amount : port_iter.value()["amount"].items()) {
  302. p.amount[x] = amount.value().get<int>();
  303. ++x;
  304. }
  305. x = 0;
  306. for (auto const &pct : port_iter.value()["pct"].items()) {
  307. p.percent[x] = pct.value().get<int>();
  308. ++x;
  309. }
  310. }
  311. add_port(p);
  312. }
  313. } else {
  314. BUGZ_LOG(fatal) << "Missing ports section.";
  315. }
  316. if (data.contains("warps")) {
  317. // const json &warps = data["warps"];
  318. // if (warps.IsMap()) {
  319. for (auto const &warp_iter : data["warps"].items()) {
  320. sector_warps sw;
  321. sw.sector = sstoi(warp_iter.key());
  322. for (auto const &sector_iter : warp_iter.value().items()) {
  323. sw.add(sector_iter.value().get<int>());
  324. }
  325. // BUGZ_LOG(trace) << "YAML warp: " << sw;
  326. add_warp(sw);
  327. }
  328. // }
  329. } else {
  330. BUGZ_LOG(fatal) << "Missing warps section.";
  331. }
  332. BUGZ_LOG(trace) << "JSON: meta keys: " << meta.size();
  333. BUGZ_LOG(trace) << "JSON: config keys: " << config.size();
  334. BUGZ_LOG(trace) << "JSON: warp keys: " << warps.size();
  335. BUGZ_LOG(trace) << "JSON: port keys: " << ports.size();
  336. } else {
  337. BUGZ_LOG(fatal) << "Missing: " << filename;
  338. }
  339. }
  340. void Galaxy::save(void) {
  341. std::string basename = "galaxy";
  342. if (CONFIG.contains("basename")) {
  343. basename = CONFIG["basename"];
  344. }
  345. std::string filename =
  346. str(boost::format("%1%-%2%-%3%.json") % basename % game % username);
  347. std::ofstream fout(filename);
  348. BUGZ_LOG(fatal) << "save: " << filename;
  349. // int depth = 0;
  350. // std::string depth_spacer;
  351. meta["save_to"] = filename;
  352. meta["save_time"] = time_t_now(); // time_t
  353. // For now, we'll delete this manually.
  354. // FUTURE: delete all keys starting with _.
  355. if (meta.contains("density")) {
  356. meta.erase("density");
  357. }
  358. // clean meta data
  359. {
  360. // build vector of things to delete, don't invalidate your iterator. (UB)
  361. std::vector<std::string> to_delete;
  362. for (auto m : meta.items()) {
  363. if (m.key()[0] == '_') {
  364. to_delete.push_back(m.key());
  365. }
  366. }
  367. for (auto const &key : to_delete) {
  368. BUGZ_LOG(fatal) << "erasing meta key: [" << key << "]";
  369. meta.erase(key);
  370. }
  371. }
  372. /* // testing sequence code
  373. meta["sequence"]["part"].push_back(1);
  374. meta["sequence"]["part"].push_back(2);
  375. meta["sequence"]["part"].push_back(3);
  376. meta["sequence"]["smeg"].push_back(0);
  377. */
  378. json output;
  379. output["meta"] = meta;
  380. output["config"] = config;
  381. BUGZ_LOG(trace) << "JSON: meta: " << meta.size();
  382. // yaml_out(fout, depth, meta);
  383. BUGZ_LOG(trace) << "JSON: config: " << config.size();
  384. BUGZ_LOG(trace) << "JSON: warps: " << warps.size();
  385. // in config, I usually switch to doing flow instead. I'll keep this like
  386. // this for now.
  387. // yaml_out(fout, depth, config);
  388. for (auto const &warp : warps) {
  389. // fout << depth_spacer << warp.first << ": [";
  390. // bool first = true;
  391. std::string warp_text = std::to_string(warp.first);
  392. // output["warps"][warp.first] = json::array();
  393. for (auto const &sector : warp.second.warps) {
  394. output["warps"][warp_text].push_back(sector);
  395. /*
  396. if (!first) {
  397. fout << ", ";
  398. } else
  399. first = false;
  400. fout << sector;
  401. */
  402. }
  403. // fout << "]" << std::endl;
  404. }
  405. BUGZ_LOG(trace) << "JSON: ports: " << ports.size();
  406. // fout << "ports:" << std::endl;
  407. for (auto const &port : ports) {
  408. std::string port_text = std::to_string(port.first);
  409. output["ports"][port_text]["class"] = (int)port.second.type;
  410. if (port.second.type != 0) {
  411. // write out the rest of the information
  412. // output["ports"][port.first]["amount"] =
  413. // json::array{port.second.amount[0], port.second.amount[1],
  414. // port.second.aount[2]};
  415. for (int x = 0; x < 3; x++) {
  416. // output["ports"][port.first]["amount"][x] = port.second.amount[x];
  417. output["ports"][port_text]["amount"].push_back(port.second.amount[x]);
  418. }
  419. // output["ports"][port.first]["pct"] =
  420. // json::array{port.second.percent[0], port.second.percent[1],
  421. // port.second.percent[2]};
  422. for (int x = 0; x < 3; x++) {
  423. // output["ports"][port.first]["pct"][x] = port.second.percent[x];
  424. output["ports"][port_text]["pct"].push_back(port.second.percent[x]);
  425. }
  426. }
  427. }
  428. fout << output;
  429. }
  430. std::vector<port_pair_type> Galaxy::find_trades(sector_type sector,
  431. bool highest) {
  432. std::vector<port_pair_type> pptv;
  433. // Does this sector have a port?
  434. auto port = ports.find(sector);
  435. if (port == ports.end()) return pptv;
  436. auto port_warps = warps.find(sector);
  437. // warps not found for port sector?
  438. if (port_warps == warps.end()) return pptv;
  439. for (auto const &s : port_warps->second.warps) {
  440. // only count the ports > our sector number -- so we don't have dups
  441. if (highest && (s < sector)) continue;
  442. // verify we have a way back
  443. {
  444. auto warpback = warps.find(s);
  445. // can we find that warp?
  446. if (warpback == warps.end()) continue;
  447. // does it link back to the port's sector?
  448. if (warpback->second.warps.find(sector) == warpback->second.warps.end())
  449. continue;
  450. }
  451. // Does this sector have a port?
  452. auto possible_port = ports.find(s);
  453. if (possible_port == ports.end()) continue;
  454. if (possible_port->second.type == 0) continue;
  455. // calculate trade type
  456. // int t = trade_type(port->second.type, possible_port->second.type);
  457. BUGZ_LOG(trace) << "find_trades: Port " << sector << ","
  458. << (int)port->second.type << " " << s
  459. << (int)possible_port->second.type;
  460. trade_type_result ttr = trade_type_info(
  461. sector, s); // port->second.type, possible_port->second.type);
  462. if ((ttr.type == NONE) || (ttr.type == FAIR_F)) continue;
  463. pptv.push_back(port_pair_type{ttr.type, ttr.trades, sector, s});
  464. BUGZ_LOG(trace) << "sector: " << sector << " and " << s
  465. << " tt:" << ttr.type;
  466. }
  467. return pptv;
  468. }
  469. bool compare_port_pair(const port_pair_type &ppt1, const port_pair_type &ppt2) {
  470. if (ppt1.type == ppt2.type) {
  471. return ppt1.s1 < ppt2.s1;
  472. } else {
  473. return (ppt1.type < ppt2.type);
  474. }
  475. }
  476. void Galaxy::sort_port_pair_type(std::vector<port_pair_type> &pptv) {
  477. sort(pptv.begin(), pptv.end(), compare_port_pair);
  478. }
  479. std::vector<port_pair_type> Galaxy::find_best_trades(void) {
  480. std::vector<port_pair_type> pptv;
  481. burnt_percent = json_int(config["burnt_percent"]);
  482. if (burnt_percent > 90) {
  483. burnt_percent = 90;
  484. config["burnt_percent"] = 90;
  485. }
  486. if (burnt_percent < 10) {
  487. burnt_percent = 10;
  488. config["burnt_percent"] = 10;
  489. }
  490. for (auto const &pi : ports) {
  491. if (pi.second.type == 0) continue;
  492. sector_type sector = pi.second.sector;
  493. auto port_warps = warps.find(sector);
  494. if (port_warps == warps.end()) continue;
  495. std::vector<port_pair_type> ppt_sector = find_trades(sector, true);
  496. if (ppt_sector.empty()) continue;
  497. pptv.insert(pptv.end(), ppt_sector.begin(), ppt_sector.end());
  498. }
  499. return pptv;
  500. }
  501. /**
  502. * Find_closest trade
  503. *
  504. * This works by getting all of the ports we know of.
  505. * Then, we find the nearest.
  506. *
  507. * @param sector
  508. * @return port_pair_type
  509. */
  510. port_pair_type Galaxy::find_closest(int sector) {
  511. auto trades = find_best_trades();
  512. // int type, sector_type s1, s2;
  513. std::set<sector_type> seen;
  514. std::set<sector_type> current;
  515. current.insert(sector);
  516. bool found = false;
  517. bool added_new = false;
  518. int depth = 0;
  519. while (!found) {
  520. // is current list in any of the trades ?
  521. for (auto const &t : trades) {
  522. if (t.type > 2) continue;
  523. if (current.find(t.s1) != current.end()) {
  524. // found one!
  525. return t;
  526. }
  527. if (current.find(t.s2) != current.end()) {
  528. return t;
  529. }
  530. }
  531. ++depth;
  532. BUGZ_LOG(info) << "depth: " << depth << " current:" << current.size()
  533. << " seen: " << seen.size();
  534. added_new = false;
  535. if (!found) {
  536. // update the seen
  537. seen.insert(current.begin(), current.end());
  538. auto look_in = current;
  539. current.clear();
  540. for (auto const &li : look_in) {
  541. auto wi = warps.find(li);
  542. if (wi == warps.end()) continue;
  543. for (auto const &w : wi->second.warps) {
  544. // have we already seen this sector?
  545. if (seen.find(w) != seen.end()) continue;
  546. // does it have a warp back to the original sector?
  547. auto warp_back = warps.find(w);
  548. if (warp_back != warps.end()) {
  549. if (warp_back->second.warps.find(li) !=
  550. warp_back->second.warps.end()) {
  551. // Ok, this links back to the original sector...
  552. current.insert(w);
  553. added_new = true;
  554. }
  555. }
  556. }
  557. }
  558. if (!added_new) {
  559. BUGZ_LOG(warning) << "No new sectors added. We're done!";
  560. port_pair_type ppt;
  561. ppt.type = 0;
  562. return ppt;
  563. }
  564. }
  565. }
  566. port_pair_type ppt;
  567. ppt.type = 0;
  568. return ppt;
  569. }
  570. /**
  571. * Find closest and best trade
  572. *
  573. * @param sector
  574. * @param lowest_trade_type
  575. * @return port_pair_type
  576. */
  577. port_pair_type Galaxy::find_closest_trade(int sector, int lowest_trade_type,
  578. int burnt_percent) {
  579. // int type, sector_type s1, s2;
  580. BUGZ_LOG(fatal) << "find_closest_trade(" << sector << ")";
  581. std::vector<port_pair_type> vppt;
  582. std::set<sector_type> seen;
  583. std::set<sector_type> current;
  584. current.insert(sector);
  585. bool found = false;
  586. bool added_new = false;
  587. int depth = 0;
  588. while (!found) {
  589. ++depth;
  590. BUGZ_LOG(info) << "depth: " << depth << " current:" << current.size()
  591. << " seen: " << seen.size();
  592. // search current for trades
  593. for (auto const &c : current) {
  594. auto port = ports.find(c);
  595. if (port == ports.end()) continue;
  596. if (port->second.type == 0) continue;
  597. auto port_warps = warps.find(c);
  598. if (port_warps == warps.end()) continue;
  599. for (auto const &s : port_warps->second.warps) {
  600. // verify we have a way back
  601. {
  602. auto warpback = warps.find(s);
  603. if (warpback == warps.end()) continue;
  604. if (warpback->second.warps.find(c) == warpback->second.warps.end())
  605. continue;
  606. }
  607. auto possible_port = ports.find(s);
  608. if (possible_port == ports.end()) continue;
  609. if (possible_port->second.type == 0) continue;
  610. trade_type_result ttr = trade_type_info(c, s, burnt_percent);
  611. if ((ttr.type == NONE) || (ttr.type > lowest_trade_type)) continue;
  612. // Ok! we found a trade that fits the criteria!
  613. vppt.push_back(port_pair_type{ttr.type, ttr.trades, c, s});
  614. found = true;
  615. }
  616. }
  617. added_new = false;
  618. if (found) {
  619. // ok, we've found some trades, sort and return the best
  620. sort_port_pair_type(vppt);
  621. return port_pair_type{vppt[0]};
  622. } else {
  623. // update the seen
  624. seen.insert(current.begin(), current.end());
  625. auto look_in = current;
  626. current.clear();
  627. for (auto const &li : look_in) {
  628. auto wi = warps.find(li);
  629. if (wi == warps.end()) continue;
  630. for (auto const &w : wi->second.warps) {
  631. // have we already seen this sector?
  632. if (seen.find(w) != seen.end()) continue;
  633. // does it have a warp back to the original sector?
  634. auto warp_back = warps.find(w);
  635. if (warp_back != warps.end()) {
  636. if (warp_back->second.warps.find(li) !=
  637. warp_back->second.warps.end()) {
  638. // Ok, this links back to the original sector...
  639. current.insert(w);
  640. added_new = true;
  641. }
  642. }
  643. }
  644. }
  645. if (!added_new) {
  646. BUGZ_LOG(warning) << "No new sectors added. We're done!";
  647. port_pair_type ppt;
  648. ppt.type = 0;
  649. return ppt;
  650. }
  651. }
  652. }
  653. port_pair_type ppt;
  654. ppt.type = 0;
  655. return ppt;
  656. }
  657. sector_type Galaxy::find_nearest_unexplored(sector_type sector) {
  658. // search the galaxy for unexplored
  659. std::set<sector_type> seen;
  660. std::set<sector_type> current;
  661. current.insert(sector);
  662. bool found = false;
  663. bool added_new = false;
  664. int depth = 0;
  665. while (!found) {
  666. ++depth;
  667. BUGZ_LOG(info) << "depth: " << depth << " current:" << current.size()
  668. << " seen: " << seen.size();
  669. // search for warps
  670. for (auto const &c : current) {
  671. auto port_warps = warps.find(c);
  672. if (port_warps == warps.end()) return c;
  673. }
  674. // update the seen
  675. seen.insert(current.begin(), current.end());
  676. auto look_in = current;
  677. current.clear();
  678. for (auto const &li : look_in) {
  679. auto wi = warps.find(li);
  680. if (wi == warps.end()) return li;
  681. for (auto const &w : wi->second.warps) {
  682. // have we already seen this sector?
  683. if (seen.find(w) != seen.end()) continue;
  684. current.insert(w);
  685. added_new = true;
  686. }
  687. }
  688. if (!added_new) {
  689. BUGZ_LOG(warning) << "No new sectors added. We're done!";
  690. found = false;
  691. }
  692. }
  693. return 0;
  694. }
  695. trade_type_result Galaxy::trade_type_info(sector_type port1, sector_type port2,
  696. int burnt_percent) {
  697. BUGZ_LOG(fatal) << "Trade_type_info(" << port1 << "," << port2 << ")";
  698. // This only gives us one trade_type per pair. There actually
  699. // should be multiple values returned here!
  700. // Like in case of BBB/SSS: return 3, 4 and 5.
  701. // NONE = 0
  702. // GOOD = 1 = OE PAIR
  703. // OK = 2 = ?? Pair
  704. // FAIR = 3 = B/S E
  705. // 4 = B/S O
  706. // 5 = B/S F
  707. buysell inv2;
  708. auto p1 = ports.find(port1);
  709. if (p1 == ports.end()) {
  710. BUGZ_LOG(fatal) << "Can't find port 1: " << (int)port1;
  711. return {NONE, inv2};
  712. }
  713. BUGZ_LOG(fatal) << "port 1: " << p1->first << " " << p1->second.sector << ", "
  714. << (int)p1->second.type;
  715. auto p2 = ports.find(port2);
  716. if (p2 == ports.end()) {
  717. BUGZ_LOG(fatal) << "Can't find port 2: " << (int)port2;
  718. return {NONE, inv2};
  719. }
  720. BUGZ_LOG(fatal) << "port 2: " << p2->first << " " << p2->second.sector << ", "
  721. << (int)p2->second.type;
  722. buysell bsp1 = get_buysell(p1->second.type);
  723. buysell bsp2 = get_buysell(p2->second.type);
  724. inv2 = invert_buysell(bsp2);
  725. int matches = 0; // or pos.size();
  726. std::vector<int> pos;
  727. // If we don't know how many holds the ship has, default to 300.
  728. int max_holds = 300;
  729. if (meta["ship"]["holds"].contains("total")) {
  730. max_holds = json_int(meta["ship"]["holds"]["total"]);
  731. }
  732. // find which FOE are flipped. Save index pos.
  733. for (int x = 0; x < 3; ++x) {
  734. inv2.foe[x] = (bsp1.foe[x] == inv2.foe[x]);
  735. // Ok, these are possible trades (B->S or S->B)
  736. // If known, check for burnt
  737. if (!p1->second.unknown()) {
  738. if (p1->second.percent[x] < burnt_percent) {
  739. BUGZ_LOG(fatal) << "Marking Port 1: " << x << " (burnt)";
  740. inv2.foe[x] = false;
  741. }
  742. if (p1->second.amount[x] < max_holds) {
  743. BUGZ_LOG(fatal) << "Marking Port 1: " << x << " (burnt/amount)";
  744. inv2.foe[x] = false;
  745. }
  746. } else {
  747. BUGZ_LOG(fatal) << "Port 1 : unknown / skip burnt checks";
  748. }
  749. if (!p2->second.unknown()) {
  750. if (p2->second.percent[x] < burnt_percent) {
  751. BUGZ_LOG(fatal) << "Marking Port 2: " << x << " (burnt)";
  752. inv2.foe[x] = false;
  753. }
  754. if (p2->second.amount[x] < max_holds) {
  755. BUGZ_LOG(fatal) << "Marking Port 2: " << x << " (burnt/amount)";
  756. inv2.foe[x] = false;
  757. }
  758. } else {
  759. BUGZ_LOG(fatal) << "Port 2 : unknown / skip burnt checks";
  760. }
  761. if (inv2.foe[x]) {
  762. matches++;
  763. pos.push_back(x);
  764. }
  765. }
  766. BUGZ_LOG(fatal) << "Matches: " << matches << " inv: " << inv2;
  767. if (matches > 1) {
  768. // Check for BEST
  769. // O != E for both ports, and O != O, and ORG/EQU in inv2 list
  770. if (inv2.foe[ORG] && inv2.foe[EQU] && (bsp1.foe[ORG] != bsp1.foe[EQU]) &&
  771. (bsp2.foe[ORG] != bsp2.foe[EQU]) && (bsp1.foe[ORG] != bsp2.foe[ORG])) {
  772. // verify that fuel isn't set.
  773. inv2.foe[FUEL] = false;
  774. BUGZ_LOG(fatal) << "result: " << BEST << " " << inv2;
  775. return trade_type_result{BEST, inv2};
  776. }
  777. if (matches == 3) {
  778. // This could be SBB / BSS (it's a pair, but not BEST)
  779. // Is it FO or FE ?
  780. if (bsp1.foe[FUEL] != bsp2.foe[EQU]) {
  781. // OK, FE
  782. inv2.foe[ORG] = false;
  783. BUGZ_LOG(fatal) << "result: " << OK << " " << inv2;
  784. return trade_type_result{OK, inv2};
  785. }
  786. if (bsp1.foe[FUEL] != bsp2.foe[ORG]) {
  787. // OK, FO
  788. inv2.foe[EQU] = false;
  789. BUGZ_LOG(fatal) << "result: " << OK << " " << inv2;
  790. return trade_type_result{OK, inv2};
  791. }
  792. // Ok, take the highest (EQU)
  793. inv2.foe[FUEL] = false;
  794. inv2.foe[ORG] = false;
  795. BUGZ_LOG(fatal) << "result: " << FAIR_E << " " << inv2;
  796. return trade_type_result{FAIR_E, inv2};
  797. }
  798. // 2 matches. but are they trade pairs?
  799. if (bsp1.foe[pos[matches - 1]] != bsp1.foe[pos[matches - 2]]) {
  800. // yes!
  801. BUGZ_LOG(fatal) << "result: " << OK << " " << inv2;
  802. return trade_type_result{OK, inv2};
  803. } else {
  804. // they are NOT. Use highest one. clear the lower flag
  805. inv2.foe[pos[0]] = false;
  806. switch (pos[1]) {
  807. case 0:
  808. BUGZ_LOG(fatal) << "result: " << FAIR_F << " " << inv2;
  809. return trade_type_result{FAIR_F, inv2};
  810. break;
  811. case 1:
  812. BUGZ_LOG(fatal) << "result: " << FAIR_O << " " << inv2;
  813. return trade_type_result{FAIR_O, inv2};
  814. break;
  815. case 2:
  816. BUGZ_LOG(fatal) << "result: " << FAIR_E << " " << inv2;
  817. return trade_type_result{FAIR_E, inv2};
  818. break;
  819. }
  820. }
  821. }
  822. if (matches == 1) {
  823. switch (pos[0]) {
  824. case 0:
  825. BUGZ_LOG(fatal) << "result: " << FAIR_F << " " << inv2;
  826. return trade_type_result{FAIR_F, inv2};
  827. break;
  828. case 1:
  829. BUGZ_LOG(fatal) << "result: " << FAIR_O << " " << inv2;
  830. return trade_type_result{FAIR_O, inv2};
  831. break;
  832. case 2:
  833. BUGZ_LOG(fatal) << "result: " << FAIR_E << " " << inv2;
  834. return trade_type_result{FAIR_E, inv2};
  835. break;
  836. }
  837. }
  838. // no matches.
  839. BUGZ_LOG(fatal) << "result: " << NONE << " " << inv2;
  840. return trade_type_result{NONE, inv2};
  841. }
  842. sector_type Galaxy::find_nearest_selling(int sector, int product, int selling) {
  843. BUGZ_LOG(fatal) << "find_nearest_selling(" << sector << ") " << product;
  844. std::set<sector_type> seen;
  845. std::set<sector_type> current;
  846. current.insert(sector);
  847. bool found = false;
  848. bool added_new = false;
  849. int depth = 0;
  850. // check current sector
  851. auto port = ports.find(sector);
  852. if (port != ports.end()) {
  853. // ok, port exists in starting sector
  854. // are they selling?
  855. int t = port->second.type;
  856. if (t != 0) {
  857. buysell bs = get_buysell(t);
  858. if (bs.foe[product] == false) {
  859. if (port->second.amount[product] >= selling) {
  860. BUGZ_LOG(fatal) << "Found port: " << port->first;
  861. return port->first;
  862. }
  863. }
  864. }
  865. }
  866. while (!found) {
  867. ++depth;
  868. BUGZ_LOG(info) << "depth: " << depth << " current:" << current.size()
  869. << " seen: " << seen.size();
  870. // search current for trades
  871. for (auto const &c : current) {
  872. auto port_warps = warps.find(c);
  873. if (port_warps == warps.end()) continue;
  874. for (auto const &s : port_warps->second.warps) {
  875. // I don't care if there's a way back the way we came
  876. auto possible_port = ports.find(s);
  877. if (possible_port == ports.end()) continue;
  878. if (possible_port->second.type == 0) continue;
  879. // are they selling?
  880. int t = possible_port->second.type;
  881. buysell bs = get_buysell(t);
  882. if (bs.foe[product] == true) continue;
  883. // check this port out
  884. if (possible_port->second.amount[product] >= selling) {
  885. BUGZ_LOG(fatal) << "Found port: " << possible_port->first;
  886. return possible_port->first;
  887. }
  888. }
  889. }
  890. added_new = false;
  891. // update the seen
  892. seen.insert(current.begin(), current.end());
  893. auto look_in = current;
  894. current.clear();
  895. for (auto const &li : look_in) {
  896. auto wi = warps.find(li);
  897. if (wi == warps.end()) continue;
  898. for (auto const &w : wi->second.warps) {
  899. // have we already seen this sector?
  900. if (seen.find(w) != seen.end()) continue;
  901. // I don't care if it has a warp back or not.
  902. current.insert(w);
  903. added_new = true;
  904. }
  905. }
  906. if (!added_new) {
  907. BUGZ_LOG(warning) << "No new sectors added. We're done!";
  908. return 0;
  909. }
  910. }
  911. return 0;
  912. };
  913. std::vector<sector_type> Galaxy::find_safe(void) {
  914. std::vector<sector_type> vst;
  915. for (auto const &warp : warps) {
  916. if (warp.second.warps.size() == 1) {
  917. vst.push_back(warp.first);
  918. }
  919. }
  920. return vst;
  921. }