galaxy.cpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989
  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. YAML::Node data = YAML::LoadFile(filename);
  266. if (config["meta"]) meta = config["meta"];
  267. meta["load_from"] = filename;
  268. std::chrono::_V2::system_clock::time_point now =
  269. std::chrono::system_clock::now();
  270. meta["load_time"] = std::chrono::system_clock::to_time_t(now); // time_t
  271. if (data["config"]) {
  272. config = data["config"];
  273. } else {
  274. BUGZ_LOG(fatal) << "YAML Missing config section.";
  275. }
  276. if (data["ports"]) {
  277. const YAML::Node ports = data["ports"];
  278. for (auto const &port_iter : ports) {
  279. port p;
  280. p.sector = port_iter.first.as<int>();
  281. p.type = port_iter.second["class"].as<int>();
  282. int x;
  283. if (p.type == 0) {
  284. // nothing to load for type = 0, set defaults.
  285. for (x = 0; x < 3; ++x) {
  286. p.amount[x] = 0;
  287. p.percent[x] = 0;
  288. }
  289. } else {
  290. x = 0;
  291. for (auto const &amount : port_iter.second["amount"]) {
  292. p.amount[x] = amount.as<int>();
  293. ++x;
  294. }
  295. x = 0;
  296. for (auto const &pct : port_iter.second["pct"]) {
  297. p.percent[x] = pct.as<int>();
  298. ++x;
  299. }
  300. }
  301. add_port(p);
  302. }
  303. } else {
  304. BUGZ_LOG(fatal) << "YAML Missing ports section.";
  305. }
  306. if (data["warps"]) {
  307. const YAML::Node &warps = data["warps"];
  308. // if (warps.IsMap()) {
  309. for (auto const warp_iter : warps) {
  310. sector_warps sw;
  311. sw.sector = warp_iter.first.as<int>();
  312. for (auto const sector_iter : warp_iter.second) {
  313. sw.add(sector_iter.as<int>());
  314. }
  315. // BUGZ_LOG(trace) << "YAML warp: " << sw;
  316. add_warp(sw);
  317. }
  318. // }
  319. } else {
  320. BUGZ_LOG(fatal) << "YAML Missing warps section.";
  321. }
  322. BUGZ_LOG(fatal) << "YAML: config keys: " << config.size();
  323. BUGZ_LOG(fatal) << "YAML: warp keys: " << warps.size();
  324. BUGZ_LOG(fatal) << "YAML: port keys: " << ports.size();
  325. } else {
  326. BUGZ_LOG(fatal) << "Missing YAML: " << filename;
  327. }
  328. }
  329. void Galaxy::save(void) {
  330. std::string basename = "galaxy";
  331. if (CONFIG["basename"]) {
  332. basename = CONFIG["basename"].as<std::string>();
  333. }
  334. std::string filename =
  335. str(boost::format("%1%-%2%-%3%.yaml") % basename % game % username);
  336. std::ofstream fout(filename);
  337. fout << "%YAML 1.2" << std::endl << "---" << std::endl;
  338. BUGZ_LOG(fatal) << "YAML: " << filename;
  339. int depth = 0;
  340. std::string depth_spacer;
  341. meta["save_to"] = filename;
  342. std::chrono::_V2::system_clock::time_point now =
  343. std::chrono::system_clock::now();
  344. meta["save_time"] = std::chrono::system_clock::to_time_t(now); // time_t
  345. /* // testing sequence code
  346. meta["sequence"]["part"].push_back(1);
  347. meta["sequence"]["part"].push_back(2);
  348. meta["sequence"]["part"].push_back(3);
  349. meta["sequence"]["smeg"].push_back(0);
  350. */
  351. // meta:
  352. fout << "meta:" << std::endl;
  353. ++depth;
  354. depth_spacer.assign(depth * 2, ' ');
  355. std::function<void(std::ofstream & of, int depth, const YAML::Node &n)>
  356. yaml_out;
  357. yaml_out = [&yaml_out](std::ofstream &of, int yaml_depth,
  358. const YAML::Node &n) {
  359. std::string yaml_spacer;
  360. yaml_spacer.assign(yaml_depth * 2, ' ');
  361. for (auto const &data : n) {
  362. if (data.second.Type() == YAML::NodeType::Scalar) {
  363. of << yaml_spacer << data.first << ": " << data.second << std::endl;
  364. } else if (data.second.Type() == YAML::NodeType::Map) {
  365. // nested
  366. of << yaml_spacer << data.first << ":" << std::endl;
  367. yaml_out(of, yaml_depth + 1, data.second);
  368. } else if (data.second.Type() == YAML::NodeType::Sequence) {
  369. // sequence
  370. BUGZ_LOG(fatal) << "Outputting Sequence... " << data.first;
  371. of << yaml_spacer << data.first << ": [";
  372. bool first = true;
  373. for (auto const &seq : data.second) {
  374. if (!first)
  375. of << ", ";
  376. else
  377. first = false;
  378. of << seq;
  379. }
  380. of << "]" << std::endl;
  381. } else {
  382. BUGZ_LOG(fatal) << "Unsupported NodeType: " << data.second.Type();
  383. }
  384. }
  385. };
  386. yaml_out(fout, depth, meta);
  387. BUGZ_LOG(fatal) << "YAML config: " << config.size();
  388. fout << "config:" << std::endl;
  389. // in config, I usually switch to doing flow instead. I'll keep this like
  390. // this for now.
  391. yaml_out(fout, depth, config);
  392. BUGZ_LOG(fatal) << "YAML warps: " << warps.size();
  393. fout << "warps:" << std::endl;
  394. for (auto const &warp : warps) {
  395. fout << depth_spacer << warp.first << ": [";
  396. bool first = true;
  397. for (auto const &sector : warp.second.warps) {
  398. if (!first) {
  399. fout << ", ";
  400. } else
  401. first = false;
  402. fout << sector;
  403. }
  404. fout << "]" << std::endl;
  405. }
  406. BUGZ_LOG(fatal) << "YAML ports: " << ports.size();
  407. fout << "ports:" << std::endl;
  408. /*
  409. When saving to yaml, my sector_type is like char. So, it wants
  410. to save the values as a character. Cast to int.
  411. */
  412. for (auto const &port : ports) {
  413. fout << depth_spacer << port.second.sector
  414. << ": {class: " << (int)port.second.type;
  415. if (port.second.type == 0) {
  416. fout << "}" << std::endl;
  417. } else {
  418. // write out the rest of the information
  419. fout << ", amount: [";
  420. for (int x = 0; x < 3; x++) {
  421. fout << port.second.amount[x];
  422. if (x != 2) fout << ", ";
  423. }
  424. fout << "], pct: [";
  425. for (int x = 0; x < 3; x++) {
  426. fout << (int)port.second.percent[x];
  427. if (x != 2) fout << ", ";
  428. }
  429. fout << "]}" << std::endl;
  430. }
  431. }
  432. // the big data structures later on are the ones I really need to optimize
  433. // here. Writing out 20 lines isn't what is killing us!
  434. /*
  435. for (auto const & meta_data : meta ) {
  436. std::cerr << "key: " << meta_data.first << std::endl;
  437. if (meta_data.second.Type() == YAML::NodeType::Scalar) {
  438. fout << depth_spacer << meta_data.first << ": " << meta_data.second <<
  439. std::endl; } else {
  440. // nested structure
  441. std::cerr << "Nested: " << meta_data.first << std::endl;
  442. fout << depth_spacer << meta_data.first << ":" << std::endl;
  443. ++depth;
  444. depth_spacer.assign(depth *2, ' ');
  445. for (auto const &nested : meta_data.second) {
  446. if (nested.second.Type() == YAML::NodeType::Scalar) {
  447. fout << depth_spacer << nested.first << ": " << nested.second <<
  448. std::endl; } else { std::cerr << "double-Nested meta structure under key: " <<
  449. nested.first << std::endl;
  450. }
  451. }
  452. --depth;
  453. depth_spacer.assign(depth * 2, ' ');
  454. }
  455. }
  456. */
  457. }
  458. std::vector<port_pair_type> Galaxy::find_trades(sector_type sector,
  459. bool highest) {
  460. std::vector<port_pair_type> pptv;
  461. // Does this sector have a port?
  462. auto port = ports.find(sector);
  463. if (port == ports.end()) return pptv;
  464. auto port_warps = warps.find(sector);
  465. // warps not found for port sector?
  466. if (port_warps == warps.end()) return pptv;
  467. for (auto const &s : port_warps->second.warps) {
  468. // only count the ports > our sector number -- so we don't have dups
  469. if (highest && (s < sector)) continue;
  470. // verify we have a way back
  471. {
  472. auto warpback = warps.find(s);
  473. // can we find that warp?
  474. if (warpback == warps.end()) continue;
  475. // does it link back to the port's sector?
  476. if (warpback->second.warps.find(sector) == warpback->second.warps.end())
  477. continue;
  478. }
  479. // Does this sector have a port?
  480. auto possible_port = ports.find(s);
  481. if (possible_port == ports.end()) continue;
  482. if (possible_port->second.type == 0) continue;
  483. // calculate trade type
  484. // int t = trade_type(port->second.type, possible_port->second.type);
  485. BUGZ_LOG(trace) << "find_trades: Port " << sector << ","
  486. << (int)port->second.type << " " << s
  487. << (int)possible_port->second.type;
  488. trade_type_result ttr = trade_type_info(
  489. sector, s); // port->second.type, possible_port->second.type);
  490. if ((ttr.type == NONE) || (ttr.type == FAIR_F)) continue;
  491. pptv.push_back(port_pair_type{ttr.type, ttr.trades, sector, s});
  492. BUGZ_LOG(trace) << "sector: " << sector << " and " << s
  493. << " tt:" << ttr.type;
  494. }
  495. return pptv;
  496. }
  497. bool compare_port_pair(const port_pair_type &ppt1, const port_pair_type &ppt2) {
  498. if (ppt1.type == ppt2.type) {
  499. return ppt1.s1 < ppt2.s1;
  500. } else {
  501. return (ppt1.type < ppt2.type);
  502. }
  503. }
  504. void Galaxy::sort_port_pair_type(std::vector<port_pair_type> &pptv) {
  505. sort(pptv.begin(), pptv.end(), compare_port_pair);
  506. }
  507. std::vector<port_pair_type> Galaxy::find_best_trades(void) {
  508. std::vector<port_pair_type> pptv;
  509. burnt_percent = config["burnt_percent"].as<int>();
  510. if (burnt_percent > 90) {
  511. burnt_percent = 90;
  512. config["burnt_percent"] = 90;
  513. }
  514. if (burnt_percent < 10) {
  515. burnt_percent = 10;
  516. config["burnt_percent"] = 10;
  517. }
  518. for (auto const &pi : ports) {
  519. if (pi.second.type == 0) continue;
  520. sector_type sector = pi.second.sector;
  521. auto port_warps = warps.find(sector);
  522. if (port_warps == warps.end()) continue;
  523. std::vector<port_pair_type> ppt_sector = find_trades(sector, true);
  524. if (ppt_sector.empty()) continue;
  525. pptv.insert(pptv.end(), ppt_sector.begin(), ppt_sector.end());
  526. }
  527. return pptv;
  528. }
  529. /**
  530. * Find_closest trade
  531. *
  532. * This works by getting all of the ports we know of.
  533. * Then, we find the nearest.
  534. *
  535. * @param sector
  536. * @return port_pair_type
  537. */
  538. port_pair_type Galaxy::find_closest(int sector) {
  539. auto trades = find_best_trades();
  540. // int type, sector_type s1, s2;
  541. std::set<sector_type> seen;
  542. std::set<sector_type> current;
  543. current.insert(sector);
  544. bool found = false;
  545. bool added_new = false;
  546. int depth = 0;
  547. while (!found) {
  548. // is current list in any of the trades ?
  549. for (auto const &t : trades) {
  550. if (t.type > 2) continue;
  551. if (current.find(t.s1) != current.end()) {
  552. // found one!
  553. return t;
  554. }
  555. if (current.find(t.s2) != current.end()) {
  556. return t;
  557. }
  558. }
  559. ++depth;
  560. BUGZ_LOG(info) << "depth: " << depth << " current:" << current.size()
  561. << " seen: " << seen.size();
  562. added_new = false;
  563. if (!found) {
  564. // update the seen
  565. seen.insert(current.begin(), current.end());
  566. auto look_in = current;
  567. current.clear();
  568. for (auto const &li : look_in) {
  569. auto wi = warps.find(li);
  570. if (wi == warps.end()) continue;
  571. for (auto const &w : wi->second.warps) {
  572. // have we already seen this sector?
  573. if (seen.find(w) != seen.end()) continue;
  574. // does it have a warp back to the original sector?
  575. auto warp_back = warps.find(w);
  576. if (warp_back != warps.end()) {
  577. if (warp_back->second.warps.find(li) !=
  578. warp_back->second.warps.end()) {
  579. // Ok, this links back to the original sector...
  580. current.insert(w);
  581. added_new = true;
  582. }
  583. }
  584. }
  585. }
  586. if (!added_new) {
  587. BUGZ_LOG(warning) << "No new sectors added. We're done!";
  588. port_pair_type ppt;
  589. ppt.type = 0;
  590. return ppt;
  591. }
  592. }
  593. }
  594. port_pair_type ppt;
  595. ppt.type = 0;
  596. return ppt;
  597. }
  598. /**
  599. * Find closest and best trade
  600. *
  601. * @param sector
  602. * @param lowest_trade_type
  603. * @return port_pair_type
  604. */
  605. port_pair_type Galaxy::find_closest_trade(int sector, int lowest_trade_type,
  606. int burnt_percent) {
  607. // int type, sector_type s1, s2;
  608. BUGZ_LOG(fatal) << "find_closest_trade(" << sector << ")";
  609. std::vector<port_pair_type> vppt;
  610. std::set<sector_type> seen;
  611. std::set<sector_type> current;
  612. current.insert(sector);
  613. bool found = false;
  614. bool added_new = false;
  615. int depth = 0;
  616. while (!found) {
  617. /*
  618. for (auto const &t : trades) {
  619. if (t.type > 2) continue;
  620. if (current.find(t.s1) != current.end()) {
  621. // found one!
  622. return t;
  623. }
  624. if (current.find(t.s2) != current.end()) {
  625. return t;
  626. }
  627. }
  628. */
  629. ++depth;
  630. BUGZ_LOG(info) << "depth: " << depth << " current:" << current.size()
  631. << " seen: " << seen.size();
  632. // search current for trades
  633. for (auto const &c : current) {
  634. auto port = ports.find(c);
  635. if (port == ports.end()) continue;
  636. if (port->second.type == 0) continue;
  637. auto port_warps = warps.find(c);
  638. if (port_warps == warps.end()) continue;
  639. for (auto const &s : port_warps->second.warps) {
  640. // verify we have a way back
  641. {
  642. auto warpback = warps.find(s);
  643. if (warpback == warps.end()) continue;
  644. if (warpback->second.warps.find(c) == warpback->second.warps.end())
  645. continue;
  646. }
  647. auto possible_port = ports.find(s);
  648. if (possible_port == ports.end()) continue;
  649. if (possible_port->second.type == 0) continue;
  650. trade_type_result ttr = trade_type_info(c, s, burnt_percent);
  651. if ((ttr.type == NONE) || (ttr.type > lowest_trade_type)) continue;
  652. // Ok! we found a trade that fits the criteria!
  653. vppt.push_back(port_pair_type{ttr.type, ttr.trades, c, s});
  654. found = true;
  655. }
  656. }
  657. added_new = false;
  658. if (found) {
  659. // ok, we've found some trades, sort and return the best
  660. sort_port_pair_type(vppt);
  661. return port_pair_type{vppt[0]};
  662. } else {
  663. // update the seen
  664. seen.insert(current.begin(), current.end());
  665. auto look_in = current;
  666. current.clear();
  667. for (auto const &li : look_in) {
  668. auto wi = warps.find(li);
  669. if (wi == warps.end()) continue;
  670. for (auto const &w : wi->second.warps) {
  671. // have we already seen this sector?
  672. if (seen.find(w) != seen.end()) continue;
  673. // does it have a warp back to the original sector?
  674. auto warp_back = warps.find(w);
  675. if (warp_back != warps.end()) {
  676. if (warp_back->second.warps.find(li) !=
  677. warp_back->second.warps.end()) {
  678. // Ok, this links back to the original sector...
  679. current.insert(w);
  680. added_new = true;
  681. }
  682. }
  683. }
  684. }
  685. if (!added_new) {
  686. BUGZ_LOG(warning) << "No new sectors added. We're done!";
  687. port_pair_type ppt;
  688. ppt.type = 0;
  689. return ppt;
  690. }
  691. }
  692. }
  693. port_pair_type ppt;
  694. ppt.type = 0;
  695. return ppt;
  696. }
  697. sector_type Galaxy::find_nearest_unexplored(sector_type sector) {
  698. // search the galaxy for unexplored
  699. std::set<sector_type> seen;
  700. std::set<sector_type> current;
  701. current.insert(sector);
  702. bool found = false;
  703. bool added_new = false;
  704. int depth = 0;
  705. while (!found) {
  706. ++depth;
  707. BUGZ_LOG(info) << "depth: " << depth << " current:" << current.size()
  708. << " seen: " << seen.size();
  709. // search for warps
  710. for (auto const &c : current) {
  711. auto port_warps = warps.find(c);
  712. if (port_warps == warps.end()) return c;
  713. }
  714. // update the seen
  715. seen.insert(current.begin(), current.end());
  716. auto look_in = current;
  717. current.clear();
  718. for (auto const &li : look_in) {
  719. auto wi = warps.find(li);
  720. if (wi == warps.end()) return li;
  721. for (auto const &w : wi->second.warps) {
  722. // have we already seen this sector?
  723. if (seen.find(w) != seen.end()) continue;
  724. current.insert(w);
  725. added_new = true;
  726. }
  727. }
  728. if (!added_new) {
  729. BUGZ_LOG(warning) << "No new sectors added. We're done!";
  730. found = false;
  731. }
  732. }
  733. return 0;
  734. }
  735. trade_type_result Galaxy::trade_type_info(sector_type port1, sector_type port2,
  736. int burnt_percent) {
  737. BUGZ_LOG(fatal) << "Trade_type_info(" << port1 << "," << port2 << ")";
  738. // This only gives us one trade_type per pair. There actually
  739. // should be multiple values returned here!
  740. // Like in case of BBB/SSS: return 3, 4 and 5.
  741. // NONE = 0
  742. // GOOD = 1 = OE PAIR
  743. // OK = 2 = ?? Pair
  744. // FAIR = 3 = B/S E
  745. // 4 = B/S O
  746. // 5 = B/S F
  747. buysell inv2;
  748. auto p1 = ports.find(port1);
  749. if (p1 == ports.end()) {
  750. BUGZ_LOG(fatal) << "Can't find port 1: " << (int)port1;
  751. return {NONE, inv2};
  752. }
  753. BUGZ_LOG(fatal) << "port 1: " << p1->first << " " << p1->second.sector << ", "
  754. << (int)p1->second.type;
  755. auto p2 = ports.find(port2);
  756. if (p2 == ports.end()) {
  757. BUGZ_LOG(fatal) << "Can't find port 2: " << (int)port2;
  758. return {NONE, inv2};
  759. }
  760. BUGZ_LOG(fatal) << "port 2: " << p2->first << " " << p2->second.sector << ", "
  761. << (int)p2->second.type;
  762. buysell bsp1 = get_buysell(p1->second.type);
  763. buysell bsp2 = get_buysell(p2->second.type);
  764. inv2 = invert_buysell(bsp2);
  765. int matches = 0; // or pos.size();
  766. std::vector<int> pos;
  767. // If we don't know how many holds the ship has, default to 300.
  768. int max_holds = 300;
  769. if (meta["ship"]["holds"]["total"]) {
  770. max_holds = meta["ship"]["holds"]["total"].as<int>();
  771. }
  772. // find which FOE are flipped. Save index pos.
  773. for (int x = 0; x < 3; ++x) {
  774. inv2.foe[x] = (bsp1.foe[x] == inv2.foe[x]);
  775. // Ok, these are possible trades (B->S or S->B)
  776. // If known, check for burnt
  777. if (!p1->second.unknown()) {
  778. if (p1->second.percent[x] < burnt_percent) {
  779. BUGZ_LOG(fatal) << "Marking Port 1: " << x << " (burnt)";
  780. inv2.foe[x] = false;
  781. }
  782. if (p1->second.amount[x] < max_holds) {
  783. BUGZ_LOG(fatal) << "Marking Port 1: " << x << " (burnt/amount)";
  784. inv2.foe[x] = false;
  785. }
  786. } else {
  787. BUGZ_LOG(fatal) << "Port 1 : unknown / skip burnt checks";
  788. }
  789. if (!p2->second.unknown()) {
  790. if (p2->second.percent[x] < burnt_percent) {
  791. BUGZ_LOG(fatal) << "Marking Port 2: " << x << " (burnt)";
  792. inv2.foe[x] = false;
  793. }
  794. if (p2->second.amount[x] < max_holds) {
  795. BUGZ_LOG(fatal) << "Marking Port 2: " << x << " (burnt/amount)";
  796. inv2.foe[x] = false;
  797. }
  798. } else {
  799. BUGZ_LOG(fatal) << "Port 2 : unknown / skip burnt checks";
  800. }
  801. if (inv2.foe[x]) {
  802. matches++;
  803. pos.push_back(x);
  804. }
  805. }
  806. BUGZ_LOG(fatal) << "Matches: " << matches << " inv: " << inv2;
  807. if (matches > 1) {
  808. // Check for BEST
  809. // O != E for both ports, and O != O, and ORG/EQU in inv2 list
  810. if (inv2.foe[ORG] && inv2.foe[EQU] && (bsp1.foe[ORG] != bsp1.foe[EQU]) &&
  811. (bsp2.foe[ORG] != bsp2.foe[EQU]) && (bsp1.foe[ORG] != bsp2.foe[ORG])) {
  812. // verify that fuel isn't set.
  813. inv2.foe[FUEL] = false;
  814. BUGZ_LOG(fatal) << "result: " << BEST << " " << inv2;
  815. return trade_type_result{BEST, inv2};
  816. }
  817. if (matches == 3) {
  818. // This could be SBB / BSS (it's a pair, but not BEST)
  819. // Is it FO or FE ?
  820. if (bsp1.foe[FUEL] != bsp2.foe[EQU]) {
  821. // OK, FE
  822. inv2.foe[ORG] = false;
  823. BUGZ_LOG(fatal) << "result: " << OK << " " << inv2;
  824. return trade_type_result{OK, inv2};
  825. }
  826. if (bsp1.foe[FUEL] != bsp2.foe[ORG]) {
  827. // OK, FO
  828. inv2.foe[EQU] = false;
  829. BUGZ_LOG(fatal) << "result: " << OK << " " << inv2;
  830. return trade_type_result{OK, inv2};
  831. }
  832. // Ok, take the highest (EQU)
  833. inv2.foe[FUEL] = false;
  834. inv2.foe[ORG] = false;
  835. BUGZ_LOG(fatal) << "result: " << FAIR_E << " " << inv2;
  836. return trade_type_result{FAIR_E, inv2};
  837. }
  838. // 2 matches. but are they trade pairs?
  839. if (bsp1.foe[pos[matches - 1]] != bsp1.foe[pos[matches - 2]]) {
  840. // yes!
  841. BUGZ_LOG(fatal) << "result: " << OK << " " << inv2;
  842. return trade_type_result{OK, inv2};
  843. } else {
  844. // they are NOT. Use highest one. clear the lower flag
  845. inv2.foe[pos[0]] = false;
  846. switch (pos[1]) {
  847. case 0:
  848. BUGZ_LOG(fatal) << "result: " << FAIR_F << " " << inv2;
  849. return trade_type_result{FAIR_F, inv2};
  850. break;
  851. case 1:
  852. BUGZ_LOG(fatal) << "result: " << FAIR_O << " " << inv2;
  853. return trade_type_result{FAIR_O, inv2};
  854. break;
  855. case 2:
  856. BUGZ_LOG(fatal) << "result: " << FAIR_E << " " << inv2;
  857. return trade_type_result{FAIR_E, inv2};
  858. break;
  859. }
  860. }
  861. }
  862. if (matches == 1) {
  863. switch (pos[0]) {
  864. case 0:
  865. BUGZ_LOG(fatal) << "result: " << FAIR_F << " " << inv2;
  866. return trade_type_result{FAIR_F, inv2};
  867. break;
  868. case 1:
  869. BUGZ_LOG(fatal) << "result: " << FAIR_O << " " << inv2;
  870. return trade_type_result{FAIR_O, inv2};
  871. break;
  872. case 2:
  873. BUGZ_LOG(fatal) << "result: " << FAIR_E << " " << inv2;
  874. return trade_type_result{FAIR_E, inv2};
  875. break;
  876. }
  877. }
  878. // no matches.
  879. BUGZ_LOG(fatal) << "result: " << NONE << " " << inv2;
  880. return trade_type_result{NONE, inv2};
  881. }