#include "galaxy.h" #include // sort #include #include #include #include #include #include #include #include "config.h" #include "json.hpp" #include "logging.h" using json = nlohmann::json; // c++ default exceptions list // https://en.cppreference.com/w/cpp/error/exception std::ostream &operator<<(std::ostream &os, const port &p) { if (p.type == 0) { os << p.sector << ": " << (int)p.type; } else { os << p.sector << ": " << (int)p.type << " " << text_from_type(p.type) << " " << p.amount[0] << "," << p.amount[1] << "," << p.amount[2]; } return os; } /** * Is port unknown? * * As in we haven't visited it, we don't know what it has? * We were checking percent to 0, but we've seen valid ports with 0 percent. * We now check amount. If this becomes an issue, we'll change to an unknown * flag. * * @return true * @return false */ bool port::unknown(void) { for (int x = 0; x < 3; ++x) { if (amount[x] != 0) return false; } return true; } density_scan::density_scan() { reset(0); } void density_scan::reset(sector_type s) { sector = s; pos = 0; for (int x = 0; x < (int)d.size(); ++x) { d[x].sector = 0; } } void density_scan::add_scan(density den) { if (pos > (int)d.size()) { std::string message("density_scan::add_scan() exceeded max size "); message.append(std::to_string(d.size())); throw std::out_of_range(message); } d[pos] = den; ++pos; } density density_scan::find(sector_type sector) { for (int x = 0; x < pos; ++x) { if (d[x].sector == sector) return d[x]; } BUGZ_LOG(fatal) << "density_scan::find failed: " << sector << " we have " << pos << " scans."; density den; den.sector = 0; return den; } bool operator==(const density lhs, const density rhs) { return ((lhs.sector == rhs.sector) && (lhs.density == rhs.density) && (lhs.navhaz == rhs.navhaz) && (lhs.anomaly == rhs.anomaly) && (lhs.warps == rhs.warps) && (lhs.known == rhs.known)); } sector_warps::sector_warps() { sector = 0; } sector_warps::sector_warps(sector_type s, std::set w) { sector = s; warps = w; } void sector_warps::add(sector_type new_sector) { warps.insert(new_sector); /* for (int x = 0; x < MAX_WARPS; ++x) { if (warps[x] == new_sector) return; if (warps[x] == 0) { warps[x] = new_sector; return; } } std::string message = str(boost::format("More then MAX %1% sectors for %2%") % MAX_WARPS % (int)sector); throw std::out_of_range(message); */ } std::ostream &operator<<(std::ostream &os, const sector_warps &warps) { os << "Sector: " << warps.sector << " "; bool comma = false; for (auto const &warp : warps.warps) { if (comma) os << ","; else comma = true; os << warp; } return os; } #define GTEST_COUT std::cerr << "[ ] [ INFO ]" // #define GTEST_DEBUG // TODO: fix this. I want some trace output, but I don't want // my logs flooded ... struct port parse_portcim(const std::string line) { struct port p; p.sector = std::stoi(line); // 20 - 1708 97% - 710 56% 287 15% // [15954 - 1990 100% 20910 100% 32880 100% ] // SL: [ 4591 - 395 47% -15822 84% 19830 100% ] static std::regex portrx( "[ ]*([0-9]+) (.)[ ]*([0-9]+)[ ]+([0-9]+%) (.)[ " "]*([0-9]+)[ ]+([0-9]+%) (.)[ ]*([0-9]+)[ ]+([0-9]+%)[ ]*", std::regex_constants::ECMAScript); // does it not understand {3} ?? // NO, it does not, from regex101.com: // A repeated capturing group will only capture the last iteration. Put a // capturing group around the repeated group to capture all iterations or use // a non-capturing group instead if you're not interested in the data // // static std::regex portrx("[ ]*([0-9]+)( (.)[ ]+([0-9]+)[ ]+([0-9]+%)){3}[ // ]*", // std::regex_constants::ECMAScript); // sector + amount pct + amount pct + amount pct // 1 2 3 4 5 6 7 8 9 10 #ifdef GTEST_DEBUG GTEST_COUT << "Sector: " << p.sector << std::endl; GTEST_COUT << "Line: [" << line << "]" << std::endl; #endif buysell port_buysell; std::smatch matches; if (std::regex_match(line, matches, portrx)) { #ifdef GTEST_DEBUG for (size_t x = 1; x < matches.size(); ++x) { GTEST_COUT << x << " : " << matches[x] << std::endl; } #endif if (matches.size() != 11) { #ifdef GTEST_DEBUG GTEST_COUT << "Now you have 101 problems." << std::endl; #endif p.sector = 0; p.type = 0; return p; } // GTEST_COUT << "matches: " << matches.size() << std::endl; p.sector = stoi(matches[1]); // GTEST_COUT << "sector: " << matches[1] << std::endl; // for (int x = 1; x < 11; ++x) { // GTEST_COUT << x << " : " << matches[x] << std::endl; // } for (int x = 0; x < 3; ++x) { int pos = x * 3; port_buysell.foe[x] = matches[pos + 2] == "-"; p.amount[x] = stoi(matches[pos + 3]); p.percent[x] = stoi(matches[pos + 4]); } p.type = type_from_buysell(port_buysell); #ifdef GTEST_DEBUG GTEST_COUT << "port is type " << (int)p.type << std::endl; #endif return p; } else { #ifdef GTEST_DEBUG GTEST_COUT << "regex_match failed." << std::endl; #endif p.type = 0; p.sector = 0; return p; } } Galaxy::Galaxy() { burnt_percent = 40; } Galaxy::~Galaxy() { BUGZ_LOG(fatal) << "Galaxy::~Galaxy()"; } void Galaxy::reset(void) { meta = json(); config = json(); ports.clear(); warps.clear(); } void Galaxy::add_warp(sector_warps sw) { auto pos = warps.find(sw.sector); if (pos == warps.end()) { // not found // sw.sort(); warps[sw.sector] = sw; // BUGZ_LOG(info) << "add_warp NEW " << sw.sector; } else { // found! if (pos->second.warps == sw.warps) { // BUGZ_LOG(trace) << "add_warp: Yup, I already know about " << sw.sector; } else { BUGZ_LOG(info) << "add_warp: Warps don't match! Updating..."; BUGZ_LOG(warning) << "Have: " << pos->second; BUGZ_LOG(warning) << "Got : " << sw; warps[sw.sector] = sw; } } } void Galaxy::add_port(sector_type sector, int port_type) { auto pos = ports.find(sector); if (pos == ports.end()) { // no such port. port p; p.sector = sector; p.type = port_type; for (int x = 0; x < 3; x++) { p.amount[x] = 0; p.percent[x] = 0; } // BUGZ_LOG(trace) << "add_port: " << sector << ", " << port_type << " : " // << p; ports[sector] = p; } else { // port was found, so: if (pos->second.type == port_type) { // BUGZ_LOG(trace) << "add_port: Yup, port " << sector << " is class " << // port_type; } else { BUGZ_LOG(fatal) << "add_port: " << sector << " shows " << pos->second.type << " >> set to " << port_type; pos->second.type = port_type; } } } void Galaxy::add_port(port p) { auto pos = ports.find(p.sector); if (pos == ports.end()) { // BUGZ_LOG(trace) << "add_port: NEW " << p; ports[p.sector] = p; } else { if (pos->second.type != p.type) { if ((pos->second.type == 9) && (p.type == 8)) { BUGZ_LOG(trace) << "add_port: StarDock " << p.sector; p.type = 9; ports[p.sector] = p; } else { BUGZ_LOG(fatal) << "add_port: " << pos->second << " NEW : " << p; ports[p.sector] = p; } } else { if (pos->second.amount != p.amount) { // BUGZ_LOG(info) << "add_port: UPDATE " << p.sector; pos->second = p; } else { // BUGZ_LOG(info) << "add_port: Yup " << p.sector; } } } } void Galaxy::load(void) { std::string basename = "galaxy"; if (CONFIG.contains("basename")) { basename = json_str(CONFIG["basename"]); } std::string filename = str(boost::format("%1%-%2%-%3%.json") % basename % game % username); // reset ? meta = json(); config = json(); ports.clear(); warps.clear(); if (file_exists(filename)) { BUGZ_LOG(fatal) << "Galaxy::load( " << filename << " )"; json data; { std::ifstream fin(filename); data = json::parse(fin); } if (data.contains("meta")) { meta = data["meta"]; } else { BUGZ_LOG(fatal) << "Missing meta data."; } meta["load_from"] = filename; meta["load_time"] = time_t_now(); // time_t if (data.contains("config")) { config = data["config"]; } else { BUGZ_LOG(fatal) << "Missing config section."; } if (data.contains("ports")) { // const json &ports = data["ports"]; for (auto const &port_iter : data["ports"].items()) { port p; p.sector = sstoi(port_iter.key()); // first.as(); p.type = port_iter.value()["class"].get(); int x; if (p.type == 0) { // nothing to load for type = 0, set defaults. for (x = 0; x < 3; ++x) { p.amount[x] = 0; p.percent[x] = 0; } } else { x = 0; for (auto const &amount : port_iter.value()["amount"].items()) { p.amount[x] = amount.value().get(); ++x; } x = 0; for (auto const &pct : port_iter.value()["pct"].items()) { p.percent[x] = pct.value().get(); ++x; } } add_port(p); } } else { BUGZ_LOG(fatal) << "Missing ports section."; } if (data.contains("warps")) { // const json &warps = data["warps"]; // if (warps.IsMap()) { for (auto const &warp_iter : data["warps"].items()) { sector_warps sw; sw.sector = sstoi(warp_iter.key()); for (auto const §or_iter : warp_iter.value().items()) { sw.add(sector_iter.value().get()); } // BUGZ_LOG(trace) << "YAML warp: " << sw; add_warp(sw); } // } } else { BUGZ_LOG(fatal) << "Missing warps section."; } BUGZ_LOG(trace) << "JSON: meta keys: " << meta.size(); BUGZ_LOG(trace) << "JSON: config keys: " << config.size(); BUGZ_LOG(trace) << "JSON: warp keys: " << warps.size(); BUGZ_LOG(trace) << "JSON: port keys: " << ports.size(); } else { BUGZ_LOG(fatal) << "Missing: " << filename; } } void Galaxy::save(void) { std::string basename = "galaxy"; if (CONFIG.contains("basename")) { basename = CONFIG["basename"]; } std::string filename = str(boost::format("%1%-%2%-%3%.json") % basename % game % username); std::ofstream fout(filename); BUGZ_LOG(fatal) << "save: " << filename; // int depth = 0; // std::string depth_spacer; meta["save_to"] = filename; meta["save_time"] = time_t_now(); // time_t // For now, we'll delete this manually. // FUTURE: delete all keys starting with _. if (meta.contains("density")) { meta.erase("density"); } // clean meta data { // build vector of things to delete, don't invalidate your iterator. (UB) std::vector to_delete; for (auto m : meta.items()) { if (m.key()[0] == '_') { to_delete.push_back(m.key()); } } for (auto const &key : to_delete) { BUGZ_LOG(fatal) << "erasing meta key: [" << key << "]"; meta.erase(key); } } /* // testing sequence code meta["sequence"]["part"].push_back(1); meta["sequence"]["part"].push_back(2); meta["sequence"]["part"].push_back(3); meta["sequence"]["smeg"].push_back(0); */ json output; output["meta"] = meta; output["config"] = config; BUGZ_LOG(trace) << "JSON: meta: " << meta.size(); // yaml_out(fout, depth, meta); BUGZ_LOG(trace) << "JSON: config: " << config.size(); BUGZ_LOG(trace) << "JSON: warps: " << warps.size(); // in config, I usually switch to doing flow instead. I'll keep this like // this for now. // yaml_out(fout, depth, config); for (auto const &warp : warps) { // fout << depth_spacer << warp.first << ": ["; // bool first = true; std::string warp_text = std::to_string(warp.first); // output["warps"][warp.first] = json::array(); for (auto const §or : warp.second.warps) { output["warps"][warp_text].push_back(sector); /* if (!first) { fout << ", "; } else first = false; fout << sector; */ } // fout << "]" << std::endl; } BUGZ_LOG(trace) << "JSON: ports: " << ports.size(); // fout << "ports:" << std::endl; for (auto const &port : ports) { std::string port_text = std::to_string(port.first); output["ports"][port_text]["class"] = (int)port.second.type; if (port.second.type != 0) { // write out the rest of the information // output["ports"][port.first]["amount"] = // json::array{port.second.amount[0], port.second.amount[1], // port.second.aount[2]}; for (int x = 0; x < 3; x++) { // output["ports"][port.first]["amount"][x] = port.second.amount[x]; output["ports"][port_text]["amount"].push_back(port.second.amount[x]); } // output["ports"][port.first]["pct"] = // json::array{port.second.percent[0], port.second.percent[1], // port.second.percent[2]}; for (int x = 0; x < 3; x++) { // output["ports"][port.first]["pct"][x] = port.second.percent[x]; output["ports"][port_text]["pct"].push_back(port.second.percent[x]); } } } fout << output; } std::vector Galaxy::find_trades(sector_type sector, bool highest) { std::vector pptv; // Does this sector have a port? auto port = ports.find(sector); if (port == ports.end()) return pptv; auto port_warps = warps.find(sector); // warps not found for port sector? if (port_warps == warps.end()) return pptv; for (auto const &s : port_warps->second.warps) { // only count the ports > our sector number -- so we don't have dups if (highest && (s < sector)) continue; // verify we have a way back { auto warpback = warps.find(s); // can we find that warp? if (warpback == warps.end()) continue; // does it link back to the port's sector? if (warpback->second.warps.find(sector) == warpback->second.warps.end()) continue; } // Does this sector have a port? auto possible_port = ports.find(s); if (possible_port == ports.end()) continue; if (possible_port->second.type == 0) continue; // calculate trade type // int t = trade_type(port->second.type, possible_port->second.type); BUGZ_LOG(trace) << "find_trades: Port " << sector << "," << (int)port->second.type << " " << s << (int)possible_port->second.type; trade_type_result ttr = trade_type_info( sector, s); // port->second.type, possible_port->second.type); if ((ttr.type == NONE) || (ttr.type == FAIR_F)) continue; pptv.push_back(port_pair_type{ttr.type, ttr.trades, sector, s}); BUGZ_LOG(trace) << "sector: " << sector << " and " << s << " tt:" << ttr.type; } return pptv; } bool compare_port_pair(const port_pair_type &ppt1, const port_pair_type &ppt2) { if (ppt1.type == ppt2.type) { return ppt1.s1 < ppt2.s1; } else { return (ppt1.type < ppt2.type); } } void Galaxy::sort_port_pair_type(std::vector &pptv) { sort(pptv.begin(), pptv.end(), compare_port_pair); } std::vector Galaxy::find_best_trades(void) { std::vector pptv; burnt_percent = json_int(config["burnt_percent"]); if (burnt_percent > 90) { burnt_percent = 90; config["burnt_percent"] = 90; } if (burnt_percent < 10) { burnt_percent = 10; config["burnt_percent"] = 10; } for (auto const &pi : ports) { if (pi.second.type == 0) continue; sector_type sector = pi.second.sector; auto port_warps = warps.find(sector); if (port_warps == warps.end()) continue; std::vector ppt_sector = find_trades(sector, true); if (ppt_sector.empty()) continue; pptv.insert(pptv.end(), ppt_sector.begin(), ppt_sector.end()); } return pptv; } /** * Find_closest trade * * This works by getting all of the ports we know of. * Then, we find the nearest. * * @param sector * @return port_pair_type */ port_pair_type Galaxy::find_closest(int sector) { auto trades = find_best_trades(); // int type, sector_type s1, s2; std::set seen; std::set current; current.insert(sector); bool found = false; bool added_new = false; int depth = 0; while (!found) { // is current list in any of the trades ? for (auto const &t : trades) { if (t.type > 2) continue; if (current.find(t.s1) != current.end()) { // found one! return t; } if (current.find(t.s2) != current.end()) { return t; } } ++depth; BUGZ_LOG(info) << "depth: " << depth << " current:" << current.size() << " seen: " << seen.size(); added_new = false; if (!found) { // update the seen seen.insert(current.begin(), current.end()); auto look_in = current; current.clear(); for (auto const &li : look_in) { auto wi = warps.find(li); if (wi == warps.end()) continue; for (auto const &w : wi->second.warps) { // have we already seen this sector? if (seen.find(w) != seen.end()) continue; // does it have a warp back to the original sector? auto warp_back = warps.find(w); if (warp_back != warps.end()) { if (warp_back->second.warps.find(li) != warp_back->second.warps.end()) { // Ok, this links back to the original sector... current.insert(w); added_new = true; } } } } if (!added_new) { BUGZ_LOG(warning) << "No new sectors added. We're done!"; port_pair_type ppt; ppt.type = 0; return ppt; } } } port_pair_type ppt; ppt.type = 0; return ppt; } /** * Find closest and best trade * * @param sector * @param lowest_trade_type * @return port_pair_type */ port_pair_type Galaxy::find_closest_trade(int sector, int lowest_trade_type, int burnt_percent) { // int type, sector_type s1, s2; BUGZ_LOG(fatal) << "find_closest_trade(" << sector << ")"; std::vector vppt; std::set seen; std::set current; current.insert(sector); bool found = false; bool added_new = false; int depth = 0; while (!found) { ++depth; BUGZ_LOG(info) << "depth: " << depth << " current:" << current.size() << " seen: " << seen.size(); // search current for trades for (auto const &c : current) { auto port = ports.find(c); if (port == ports.end()) continue; if (port->second.type == 0) continue; auto port_warps = warps.find(c); if (port_warps == warps.end()) continue; for (auto const &s : port_warps->second.warps) { // verify we have a way back { auto warpback = warps.find(s); if (warpback == warps.end()) continue; if (warpback->second.warps.find(c) == warpback->second.warps.end()) continue; } auto possible_port = ports.find(s); if (possible_port == ports.end()) continue; if (possible_port->second.type == 0) continue; trade_type_result ttr = trade_type_info(c, s, burnt_percent); if ((ttr.type == NONE) || (ttr.type > lowest_trade_type)) continue; // Ok! we found a trade that fits the criteria! vppt.push_back(port_pair_type{ttr.type, ttr.trades, c, s}); found = true; } } added_new = false; if (found) { // ok, we've found some trades, sort and return the best sort_port_pair_type(vppt); return port_pair_type{vppt[0]}; } else { // update the seen seen.insert(current.begin(), current.end()); auto look_in = current; current.clear(); for (auto const &li : look_in) { auto wi = warps.find(li); if (wi == warps.end()) continue; for (auto const &w : wi->second.warps) { // have we already seen this sector? if (seen.find(w) != seen.end()) continue; // does it have a warp back to the original sector? auto warp_back = warps.find(w); if (warp_back != warps.end()) { if (warp_back->second.warps.find(li) != warp_back->second.warps.end()) { // Ok, this links back to the original sector... current.insert(w); added_new = true; } } } } if (!added_new) { BUGZ_LOG(warning) << "No new sectors added. We're done!"; port_pair_type ppt; ppt.type = 0; return ppt; } } } port_pair_type ppt; ppt.type = 0; return ppt; } sector_type Galaxy::find_nearest_unexplored(sector_type sector) { // search the galaxy for unexplored std::set seen; std::set current; current.insert(sector); bool found = false; bool added_new = false; int depth = 0; while (!found) { ++depth; BUGZ_LOG(info) << "depth: " << depth << " current:" << current.size() << " seen: " << seen.size(); // search for warps for (auto const &c : current) { auto port_warps = warps.find(c); if (port_warps == warps.end()) return c; } // update the seen seen.insert(current.begin(), current.end()); auto look_in = current; current.clear(); for (auto const &li : look_in) { auto wi = warps.find(li); if (wi == warps.end()) return li; for (auto const &w : wi->second.warps) { // have we already seen this sector? if (seen.find(w) != seen.end()) continue; current.insert(w); added_new = true; } } if (!added_new) { BUGZ_LOG(warning) << "No new sectors added. We're done!"; found = false; } } return 0; } trade_type_result Galaxy::trade_type_info(sector_type port1, sector_type port2, int burnt_percent) { BUGZ_LOG(fatal) << "Trade_type_info(" << port1 << "," << port2 << ")"; // This only gives us one trade_type per pair. There actually // should be multiple values returned here! // Like in case of BBB/SSS: return 3, 4 and 5. // NONE = 0 // GOOD = 1 = OE PAIR // OK = 2 = ?? Pair // FAIR = 3 = B/S E // 4 = B/S O // 5 = B/S F buysell inv2; auto p1 = ports.find(port1); if (p1 == ports.end()) { BUGZ_LOG(fatal) << "Can't find port 1: " << (int)port1; return {NONE, inv2}; } BUGZ_LOG(fatal) << "port 1: " << p1->first << " " << p1->second.sector << ", " << (int)p1->second.type; auto p2 = ports.find(port2); if (p2 == ports.end()) { BUGZ_LOG(fatal) << "Can't find port 2: " << (int)port2; return {NONE, inv2}; } BUGZ_LOG(fatal) << "port 2: " << p2->first << " " << p2->second.sector << ", " << (int)p2->second.type; buysell bsp1 = get_buysell(p1->second.type); buysell bsp2 = get_buysell(p2->second.type); inv2 = invert_buysell(bsp2); int matches = 0; // or pos.size(); std::vector pos; // If we don't know how many holds the ship has, default to 300. int max_holds = 300; if (meta["ship"]["holds"].contains("total")) { max_holds = json_int(meta["ship"]["holds"]["total"]); } // find which FOE are flipped. Save index pos. for (int x = 0; x < 3; ++x) { inv2.foe[x] = (bsp1.foe[x] == inv2.foe[x]); // Ok, these are possible trades (B->S or S->B) // If known, check for burnt if (!p1->second.unknown()) { if (p1->second.percent[x] < burnt_percent) { BUGZ_LOG(fatal) << "Marking Port 1: " << x << " (burnt)"; inv2.foe[x] = false; } if (p1->second.amount[x] < max_holds) { BUGZ_LOG(fatal) << "Marking Port 1: " << x << " (burnt/amount)"; inv2.foe[x] = false; } } else { BUGZ_LOG(fatal) << "Port 1 : unknown / skip burnt checks"; } if (!p2->second.unknown()) { if (p2->second.percent[x] < burnt_percent) { BUGZ_LOG(fatal) << "Marking Port 2: " << x << " (burnt)"; inv2.foe[x] = false; } if (p2->second.amount[x] < max_holds) { BUGZ_LOG(fatal) << "Marking Port 2: " << x << " (burnt/amount)"; inv2.foe[x] = false; } } else { BUGZ_LOG(fatal) << "Port 2 : unknown / skip burnt checks"; } if (inv2.foe[x]) { matches++; pos.push_back(x); } } BUGZ_LOG(fatal) << "Matches: " << matches << " inv: " << inv2; if (matches > 1) { // Check for BEST // O != E for both ports, and O != O, and ORG/EQU in inv2 list if (inv2.foe[ORG] && inv2.foe[EQU] && (bsp1.foe[ORG] != bsp1.foe[EQU]) && (bsp2.foe[ORG] != bsp2.foe[EQU]) && (bsp1.foe[ORG] != bsp2.foe[ORG])) { // verify that fuel isn't set. inv2.foe[FUEL] = false; BUGZ_LOG(fatal) << "result: " << BEST << " " << inv2; return trade_type_result{BEST, inv2}; } if (matches == 3) { // This could be SBB / BSS (it's a pair, but not BEST) // Is it FO or FE ? if (bsp1.foe[FUEL] != bsp2.foe[EQU]) { // OK, FE inv2.foe[ORG] = false; BUGZ_LOG(fatal) << "result: " << OK << " " << inv2; return trade_type_result{OK, inv2}; } if (bsp1.foe[FUEL] != bsp2.foe[ORG]) { // OK, FO inv2.foe[EQU] = false; BUGZ_LOG(fatal) << "result: " << OK << " " << inv2; return trade_type_result{OK, inv2}; } // Ok, take the highest (EQU) inv2.foe[FUEL] = false; inv2.foe[ORG] = false; BUGZ_LOG(fatal) << "result: " << FAIR_E << " " << inv2; return trade_type_result{FAIR_E, inv2}; } // 2 matches. but are they trade pairs? if (bsp1.foe[pos[matches - 1]] != bsp1.foe[pos[matches - 2]]) { // yes! BUGZ_LOG(fatal) << "result: " << OK << " " << inv2; return trade_type_result{OK, inv2}; } else { // they are NOT. Use highest one. clear the lower flag inv2.foe[pos[0]] = false; switch (pos[1]) { case 0: BUGZ_LOG(fatal) << "result: " << FAIR_F << " " << inv2; return trade_type_result{FAIR_F, inv2}; break; case 1: BUGZ_LOG(fatal) << "result: " << FAIR_O << " " << inv2; return trade_type_result{FAIR_O, inv2}; break; case 2: BUGZ_LOG(fatal) << "result: " << FAIR_E << " " << inv2; return trade_type_result{FAIR_E, inv2}; break; } } } if (matches == 1) { switch (pos[0]) { case 0: BUGZ_LOG(fatal) << "result: " << FAIR_F << " " << inv2; return trade_type_result{FAIR_F, inv2}; break; case 1: BUGZ_LOG(fatal) << "result: " << FAIR_O << " " << inv2; return trade_type_result{FAIR_O, inv2}; break; case 2: BUGZ_LOG(fatal) << "result: " << FAIR_E << " " << inv2; return trade_type_result{FAIR_E, inv2}; break; } } // no matches. BUGZ_LOG(fatal) << "result: " << NONE << " " << inv2; return trade_type_result{NONE, inv2}; } sector_type Galaxy::find_nearest_selling(int sector, int product, int selling) { BUGZ_LOG(fatal) << "find_nearest_selling(" << sector << ") " << product; std::set seen; std::set current; current.insert(sector); bool found = false; bool added_new = false; int depth = 0; // check current sector auto port = ports.find(sector); if (port != ports.end()) { // ok, port exists in starting sector // are they selling? int t = port->second.type; if (t != 0) { buysell bs = get_buysell(t); if (bs.foe[product] == false) { if (port->second.amount[product] >= selling) { BUGZ_LOG(fatal) << "Found port: " << port->first; return port->first; } } } } while (!found) { ++depth; BUGZ_LOG(info) << "depth: " << depth << " current:" << current.size() << " seen: " << seen.size(); // search current for trades for (auto const &c : current) { auto port_warps = warps.find(c); if (port_warps == warps.end()) continue; for (auto const &s : port_warps->second.warps) { // I don't care if there's a way back the way we came auto possible_port = ports.find(s); if (possible_port == ports.end()) continue; if (possible_port->second.type == 0) continue; // are they selling? int t = possible_port->second.type; buysell bs = get_buysell(t); if (bs.foe[product] == true) continue; // check this port out if (possible_port->second.amount[product] >= selling) { BUGZ_LOG(fatal) << "Found port: " << possible_port->first; return possible_port->first; } } } added_new = false; // update the seen seen.insert(current.begin(), current.end()); auto look_in = current; current.clear(); for (auto const &li : look_in) { auto wi = warps.find(li); if (wi == warps.end()) continue; for (auto const &w : wi->second.warps) { // have we already seen this sector? if (seen.find(w) != seen.end()) continue; // I don't care if it has a warp back or not. current.insert(w); added_new = true; } } if (!added_new) { BUGZ_LOG(warning) << "No new sectors added. We're done!"; return 0; } } return 0; }; std::vector Galaxy::find_safe(void) { std::vector vst; for (auto const &warp : warps) { if (warp.second.warps.size() == 1) { vst.push_back(warp.first); } } return vst; }