galaxy.cpp 28 KB

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