galaxy.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  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 "logging.h"
  11. #include "yaml-cpp/yaml.h"
  12. // c++ default exceptions list
  13. // https://en.cppreference.com/w/cpp/error/exception
  14. bool buysell::operator==(const buysell &rhs) const {
  15. return ((foe[0] == rhs.foe[0]) && (foe[1] == rhs.foe[1]) &&
  16. (foe[2] == rhs.foe[2]));
  17. }
  18. std::ostream &operator<<(std::ostream &os, const buysell &bs) {
  19. os << bs.foe[0] << bs.foe[1] << bs.foe[2];
  20. return os;
  21. }
  22. bool buysell_text::operator==(const buysell_text &rhs) const {
  23. return ((txt[0] == rhs.txt[0]) && (txt[1] == rhs.txt[1]) &&
  24. (txt[2] == rhs.txt[2]));
  25. }
  26. std::ostream &operator<<(std::ostream &os, const buysell_text &bst) {
  27. os << '"' << bst.txt[0] << bst.txt[1] << bst.txt[2] << '"';
  28. return os;
  29. }
  30. std::ostream &operator<<(std::ostream &os, const port &p) {
  31. if (p.type == 0) {
  32. os << p.sector << ": " << (int)p.type;
  33. } else {
  34. os << p.sector << ": " << (int)p.type << " " << text_from_type(p.type)
  35. << " " << p.amount[0] << "," << p.amount[1] << "," << p.amount[2];
  36. }
  37. return os;
  38. }
  39. /*
  40. // adding this breaks test-galaxy's port = {2, 2, {1,2,3}, {1,2,3}} code.
  41. port::port() {
  42. sector = 0;
  43. type = 0;
  44. for (int x = 0; x < 3; x++) {
  45. amount[x] = 0;
  46. percent[x] = 0;
  47. }
  48. }
  49. */
  50. sector_warps::sector_warps() {
  51. sector = 0;
  52. // for (int x = 0; x < MAX_WARPS; ++x) warps[x] = 0;
  53. }
  54. void sector_warps::add(sector_type new_sector) {
  55. warps.insert(new_sector);
  56. /*
  57. for (int x = 0; x < MAX_WARPS; ++x) {
  58. if (warps[x] == new_sector) return;
  59. if (warps[x] == 0) {
  60. warps[x] = new_sector;
  61. return;
  62. }
  63. }
  64. std::string message = str(boost::format("More then MAX %1% sectors for %2%") %
  65. MAX_WARPS % (int)sector);
  66. throw std::out_of_range(message);
  67. */
  68. }
  69. std::ostream &operator<<(std::ostream &os, const sector_warps &warps) {
  70. os << "Sector: " << warps.sector << " ";
  71. bool comma = false;
  72. for (auto const &warp : warps.warps) {
  73. if (comma)
  74. os << ",";
  75. else
  76. comma = true;
  77. os << warp;
  78. }
  79. /*
  80. for (int x = 0; x < MAX_WARPS; ++x) {
  81. if (warps.warps[x] != 0) {
  82. if (x != 0) os << ",";
  83. os << warps.warps[x];
  84. }
  85. }
  86. */
  87. return os;
  88. }
  89. #ifdef NOT_SET
  90. bool sector_sort(sector_type st1, sector_type st2) {
  91. /* Sort sectors, put 0's at the end. */
  92. if (st1 == 0) return false;
  93. if (st2 == 0) return false;
  94. return (st1 < st2);
  95. }
  96. void sector_warps::sort(void) {
  97. std::sort(&warps[0], &warps[MAX_WARPS], sector_sort);
  98. }
  99. bool sector_warps::operator==(const sector_warps &rhs) const {
  100. /*
  101. Comapre if the sector_warps are the same.
  102. They do not need to be in the same order.
  103. */
  104. std::set<sector_type> contains;
  105. if (sector == rhs.sector) {
  106. int x;
  107. for (x = 0; x < MAX_WARPS; ++x) {
  108. if (warps[x] == 0) break;
  109. contains.insert(warps[x]);
  110. }
  111. for (x = 0; x < MAX_WARPS; ++x) {
  112. if (warps[0] == 0) break;
  113. auto pos = contains.find(rhs.warps[x]);
  114. if (pos == contains.end()) return false;
  115. contains.erase(pos);
  116. }
  117. return contains.empty();
  118. }
  119. // sector number doesn't match!
  120. return false;
  121. }
  122. #endif
  123. #define GTEST_COUT std::cerr << "[ ] [ INFO ]"
  124. #define GTEST_DEBUG
  125. struct port parse_portcim(const std::string line) {
  126. struct port p;
  127. p.sector = std::stoi(line);
  128. // 20 - 1708 97% - 710 56% 287 15%
  129. static std::regex portrx(
  130. "[ ]*([0-9]+) (.)[ ]+([0-9]+)[ ]+([0-9]+%) (.)[ "
  131. "]+([0-9]+)[ ]+([0-9]+%) (.)[ ]+([0-9]+)[ ]+([0-9]+%)[ ]*",
  132. std::regex_constants::ECMAScript);
  133. // does it not understand {3} ??
  134. // NO, it does not, from regex101.com:
  135. // A repeated capturing group will only capture the last iteration. Put a
  136. // capturing group around the repeated group to capture all iterations or use
  137. // a non-capturing group instead if you're not interested in the data
  138. //
  139. // static std::regex portrx("[ ]*([0-9]+)( (.)[ ]+([0-9]+)[ ]+([0-9]+%)){3}[
  140. // ]*",
  141. // std::regex_constants::ECMAScript);
  142. // sector + amount pct + amount pct + amount pct
  143. // 1 2 3 4 5 6 7 8 9 10
  144. #ifdef GTEST_DEBUG
  145. GTEST_COUT << "Sector: " << p.sector << std::endl;
  146. GTEST_COUT << "Line: [" << line << "]" << std::endl;
  147. #endif
  148. buysell port_buysell;
  149. std::smatch matches;
  150. if (std::regex_match(line, matches, portrx)) {
  151. #ifdef GTEST_DEBUG
  152. for (size_t x = 1; x < matches.size(); ++x) {
  153. GTEST_COUT << x << " : " << matches[x] << std::endl;
  154. }
  155. #endif
  156. if (matches.size() != 11) {
  157. #ifdef GTEST_DEBUG
  158. GTEST_COUT << "Now you have 101 problems." << std::endl;
  159. #endif
  160. p.sector = 0;
  161. p.type = 0;
  162. return p;
  163. }
  164. // GTEST_COUT << "matches: " << matches.size() << std::endl;
  165. p.sector = stoi(matches[1]);
  166. // GTEST_COUT << "sector: " << matches[1] << std::endl;
  167. // for (int x = 1; x < 11; ++x) {
  168. // GTEST_COUT << x << " : " << matches[x] << std::endl;
  169. // }
  170. for (int x = 0; x < 3; ++x) {
  171. int pos = x * 3;
  172. port_buysell.foe[x] = matches[pos + 2] == "-";
  173. p.amount[x] = stoi(matches[pos + 3]);
  174. p.percent[x] = stoi(matches[pos + 4]);
  175. }
  176. p.type = type_from_buysell(port_buysell);
  177. #ifdef GTEST_DEBUG
  178. GTEST_COUT << "port is type " << (int)p.type << std::endl;
  179. #endif
  180. return p;
  181. } else {
  182. #ifdef GTEST_DEBUG
  183. GTEST_COUT << "regex_match failed." << std::endl;
  184. #endif
  185. p.type = 0;
  186. p.sector = 0;
  187. return p;
  188. }
  189. }
  190. Galaxy::Galaxy() {}
  191. Galaxy::~Galaxy() { BUGZ_LOG(fatal) << "Galaxy::~Galaxy()"; }
  192. void Galaxy::add_warp(sector_warps sw) {
  193. auto pos = warps.find(sw.sector);
  194. if (pos == warps.end()) {
  195. // not found
  196. // sw.sort();
  197. warps[sw.sector] = sw;
  198. BUGZ_LOG(info) << "add_warp NEW " << sw.sector;
  199. } else {
  200. // found!
  201. if (pos->second.warps == sw.warps) {
  202. BUGZ_LOG(trace) << "add_warp: Yup, I already know about " << sw.sector;
  203. } else {
  204. BUGZ_LOG(info) << "add_warp: Warps don't match! Updating...";
  205. BUGZ_LOG(warning) << "Have: " << pos->second;
  206. BUGZ_LOG(warning) << "Got : " << sw;
  207. warps[sw.sector] = sw;
  208. }
  209. }
  210. }
  211. void Galaxy::add_port(sector_type sector, int port_type) {
  212. auto pos = ports.find(sector);
  213. if (pos == ports.end()) {
  214. // no such port.
  215. port p;
  216. p.sector = sector;
  217. p.type = port_type;
  218. for (int x = 0; x < 3; x++) {
  219. p.amount[x] = 0;
  220. p.percent[x] = 0;
  221. }
  222. BUGZ_LOG(trace) << "add_port: " << sector << ", " << port_type << " : "
  223. << p;
  224. ports[sector] = p;
  225. } else {
  226. // port was found, so:
  227. if (pos->second.type == port_type) {
  228. BUGZ_LOG(trace) << "add_port: Yup, port " << sector << " is class "
  229. << port_type;
  230. } else {
  231. BUGZ_LOG(fatal) << "add_port: " << sector << " shows " << pos->second.type
  232. << " >> set to " << port_type;
  233. pos->second.type = port_type;
  234. }
  235. }
  236. }
  237. void Galaxy::add_port(port p) {
  238. auto pos = ports.find(p.sector);
  239. if (pos == ports.end()) {
  240. BUGZ_LOG(trace) << "add_port: NEW " << p;
  241. ports[p.sector] = p;
  242. } else {
  243. if (pos->second.type != p.type) {
  244. if ((pos->second.type == 9) && (p.type == 8)) {
  245. BUGZ_LOG(info) << "add_port: StarDock " << p.sector;
  246. p.type = 9;
  247. ports[p.sector] = p;
  248. } else {
  249. BUGZ_LOG(fatal) << "add_port: " << pos->second << " NEW : " << p;
  250. ports[p.sector] = p;
  251. }
  252. } else {
  253. if (pos->second.amount != p.amount) {
  254. BUGZ_LOG(info) << "add_port: UPDATE " << p.sector;
  255. pos->second = p;
  256. } else {
  257. BUGZ_LOG(info) << "add_port: Yup " << p.sector;
  258. }
  259. }
  260. }
  261. }
  262. void Galaxy::load(void) {
  263. std::string filename =
  264. str(boost::format("galaxy-%1%-%2%.json") % game % username);
  265. // reset ?
  266. meta = YAML::Node();
  267. config = YAML::Node();
  268. ports.clear();
  269. warps.clear();
  270. if (file_exists(filename)) {
  271. YAML::Node data = YAML::LoadFile(filename);
  272. if (config["meta"]) meta = config["meta"];
  273. meta["load_from"] = filename;
  274. std::chrono::_V2::system_clock::time_point now =
  275. std::chrono::system_clock::now();
  276. meta["load_time"] = std::chrono::system_clock::to_time_t(now); // time_t
  277. if (data["config"]) {
  278. config = data["config"];
  279. } else {
  280. BUGZ_LOG(fatal) << "YAML Missing config section.";
  281. }
  282. if (data["ports"]) {
  283. const YAML::Node ports = data["ports"];
  284. for (auto const &port_iter : ports) {
  285. port p;
  286. p.sector = port_iter.first.as<int>();
  287. p.type = port_iter.second["class"].as<int>();
  288. int x = 0;
  289. for (auto const &amount : port_iter.second["amount"]) {
  290. p.amount[x] = amount.as<int>();
  291. ++x;
  292. }
  293. x = 0;
  294. for (auto const &pct : port_iter.second["pct"]) {
  295. p.percent[x] = pct.as<int>();
  296. ++x;
  297. }
  298. add_port(p);
  299. }
  300. } else {
  301. BUGZ_LOG(fatal) << "YAML Missing ports section.";
  302. }
  303. if (data["warps"]) {
  304. const YAML::Node &warps = data["warps"];
  305. // if (warps.IsMap()) {
  306. for (auto const warp_iter : warps) {
  307. sector_warps sw;
  308. sw.sector = warp_iter.first.as<int>();
  309. for (auto const sector_iter : warp_iter.second) {
  310. sw.add(sector_iter.as<int>());
  311. }
  312. // BUGZ_LOG(trace) << "YAML warp: " << sw;
  313. add_warp(sw);
  314. }
  315. // }
  316. } else {
  317. BUGZ_LOG(fatal) << "YAML Missing warps section.";
  318. }
  319. BUGZ_LOG(fatal) << "YAML: config keys: " << config.size();
  320. BUGZ_LOG(fatal) << "YAML: warp keys: " << warps.size();
  321. BUGZ_LOG(fatal) << "YAML: port keys: " << ports.size();
  322. } else {
  323. BUGZ_LOG(fatal) << "Missing YAML: " << filename;
  324. }
  325. }
  326. void Galaxy::save(void) {
  327. std::string filename =
  328. str(boost::format("galaxy-%1%-%2%.json") % game % username);
  329. YAML::Node data;
  330. // add some information to meta before saving.
  331. meta["save_to"] = filename;
  332. std::chrono::_V2::system_clock::time_point now =
  333. std::chrono::system_clock::now();
  334. meta["save_time"] =
  335. std::chrono::system_clock::to_time_t(now); // time_t
  336. data["meta"] = meta;
  337. BUGZ_LOG(fatal) << "YAML config: " << config.size();
  338. data["config"] = config;
  339. /*
  340. for (auto const &config_iter : config) {
  341. data["config"][config_iter.first] = config_iter.second;
  342. }
  343. */
  344. BUGZ_LOG(fatal) << "YAML warps: " << warps.size();
  345. for (auto const &warp : warps) {
  346. for (auto const &sector : warp.second.warps) {
  347. data["warps"][warp.first].push_back(sector);
  348. }
  349. /*
  350. for (int x = 0; x < MAX_WARPS; ++x) {
  351. if (warp.second.warps[x] == 0) break;
  352. data["warps"][warp.first].push_back(warp.second.warps[x]);
  353. }
  354. */
  355. }
  356. BUGZ_LOG(fatal) << "YAML ports: " << ports.size();
  357. /*
  358. When saving to yaml, my sector_type is like char. So, it wants
  359. to save the values as a character. Cast to int.
  360. */
  361. for (auto const &port : ports) {
  362. data["ports"][port.second.sector]["class"] = (int)port.second.type;
  363. for (int x = 0; x < 3; x++) {
  364. data["ports"][port.second.sector]["amount"].push_back(
  365. (int)port.second.amount[x]);
  366. data["ports"][port.second.sector]["pct"].push_back(
  367. (int)port.second.percent[x]);
  368. }
  369. }
  370. std::ofstream fout(filename);
  371. fout << data << std::endl;
  372. BUGZ_LOG(fatal) << "YAML: " << filename;
  373. }