#include "galaxy.h" #include // sort #include #include #include #include #include #include #include #include "config.h" #include "logging.h" #include "yaml-cpp/yaml.h" // 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% 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 = YAML::Node(); config = YAML::Node(); 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["basename"]) { basename = CONFIG["basename"].as(); } std::string filename = str(boost::format("%1%-%2%-%3%.yaml") % basename % game % username); // reset ? meta = YAML::Node(); config = YAML::Node(); ports.clear(); warps.clear(); if (file_exists(filename)) { YAML::Node data = YAML::LoadFile(filename); if (config["meta"]) meta = config["meta"]; meta["load_from"] = filename; std::chrono::_V2::system_clock::time_point now = std::chrono::system_clock::now(); meta["load_time"] = std::chrono::system_clock::to_time_t(now); // time_t if (data["config"]) { config = data["config"]; } else { BUGZ_LOG(fatal) << "YAML Missing config section."; } if (data["ports"]) { const YAML::Node ports = data["ports"]; for (auto const &port_iter : ports) { port p; p.sector = port_iter.first.as(); p.type = port_iter.second["class"].as(); 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.second["amount"]) { p.amount[x] = amount.as(); ++x; } x = 0; for (auto const &pct : port_iter.second["pct"]) { p.percent[x] = pct.as(); ++x; } } add_port(p); } } else { BUGZ_LOG(fatal) << "YAML Missing ports section."; } if (data["warps"]) { const YAML::Node &warps = data["warps"]; // if (warps.IsMap()) { for (auto const warp_iter : warps) { sector_warps sw; sw.sector = warp_iter.first.as(); for (auto const sector_iter : warp_iter.second) { sw.add(sector_iter.as()); } // BUGZ_LOG(trace) << "YAML warp: " << sw; add_warp(sw); } // } } else { BUGZ_LOG(fatal) << "YAML Missing warps section."; } BUGZ_LOG(fatal) << "YAML: config keys: " << config.size(); BUGZ_LOG(fatal) << "YAML: warp keys: " << warps.size(); BUGZ_LOG(fatal) << "YAML: port keys: " << ports.size(); } else { BUGZ_LOG(fatal) << "Missing YAML: " << filename; } } void Galaxy::save(void) { std::string basename = "galaxy"; if (CONFIG["basename"]) { basename = CONFIG["basename"].as(); } std::string filename = str(boost::format("%1%-%2%-%3%.yaml") % basename % game % username); std::ofstream fout(filename); fout << "%YAML 1.2" << std::endl << "---" << std::endl; BUGZ_LOG(fatal) << "YAML: " << filename; int depth = 0; std::string depth_spacer; meta["save_to"] = filename; std::chrono::_V2::system_clock::time_point now = std::chrono::system_clock::now(); meta["save_time"] = std::chrono::system_clock::to_time_t(now); // time_t /* // 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); */ // meta: fout << "meta:" << std::endl; ++depth; depth_spacer.assign(depth * 2, ' '); std::function yaml_out; yaml_out = [&yaml_out](std::ofstream &of, int yaml_depth, const YAML::Node &n) { std::string yaml_spacer; yaml_spacer.assign(yaml_depth * 2, ' '); for (auto const &data : n) { if (data.second.Type() == YAML::NodeType::Scalar) { of << yaml_spacer << data.first << ": " << data.second << std::endl; } else if (data.second.Type() == YAML::NodeType::Map) { // nested of << yaml_spacer << data.first << ":" << std::endl; yaml_out(of, yaml_depth + 1, data.second); } else if (data.second.Type() == YAML::NodeType::Sequence) { // sequence BUGZ_LOG(fatal) << "Outputting Sequence... " << data.first; of << yaml_spacer << data.first << ": ["; bool first = true; for (auto const &seq : data.second) { if (!first) of << ", "; else first = false; of << seq; } of << "]" << std::endl; } else { BUGZ_LOG(fatal) << "Unsupported NodeType: " << data.second.Type(); } } }; yaml_out(fout, depth, meta); BUGZ_LOG(fatal) << "YAML config: " << config.size(); fout << "config:" << std::endl; // in config, I usually switch to doing flow instead. I'll keep this like // this for now. yaml_out(fout, depth, config); BUGZ_LOG(fatal) << "YAML warps: " << warps.size(); fout << "warps:" << std::endl; for (auto const &warp : warps) { fout << depth_spacer << warp.first << ": ["; bool first = true; for (auto const §or : warp.second.warps) { if (!first) { fout << ", "; } else first = false; fout << sector; } fout << "]" << std::endl; } BUGZ_LOG(fatal) << "YAML ports: " << ports.size(); fout << "ports:" << std::endl; /* When saving to yaml, my sector_type is like char. So, it wants to save the values as a character. Cast to int. */ for (auto const &port : ports) { fout << depth_spacer << port.second.sector << ": {class: " << (int)port.second.type; if (port.second.type == 0) { fout << "}" << std::endl; } else { // write out the rest of the information fout << ", amount: ["; for (int x = 0; x < 3; x++) { fout << port.second.amount[x]; if (x != 2) fout << ", "; } fout << "], pct: ["; for (int x = 0; x < 3; x++) { fout << (int)port.second.percent[x]; if (x != 2) fout << ", "; } fout << "]}" << std::endl; } } // the big data structures later on are the ones I really need to optimize // here. Writing out 20 lines isn't what is killing us! /* for (auto const & meta_data : meta ) { std::cerr << "key: " << meta_data.first << std::endl; if (meta_data.second.Type() == YAML::NodeType::Scalar) { fout << depth_spacer << meta_data.first << ": " << meta_data.second << std::endl; } else { // nested structure std::cerr << "Nested: " << meta_data.first << std::endl; fout << depth_spacer << meta_data.first << ":" << std::endl; ++depth; depth_spacer.assign(depth *2, ' '); for (auto const &nested : meta_data.second) { if (nested.second.Type() == YAML::NodeType::Scalar) { fout << depth_spacer << nested.first << ": " << nested.second << std::endl; } else { std::cerr << "double-Nested meta structure under key: " << nested.first << std::endl; } } --depth; depth_spacer.assign(depth * 2, ' '); } } */ } 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 = config["burnt_percent"].as(); 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) { /* 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(); // 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"]["total"]) { max_holds = meta["ship"]["holds"]["total"].as(); } // 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}; }