main.cpp 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110
  1. #include "door.h"
  2. #include "space.h"
  3. #include <chrono> // chrono::system_clock
  4. #include <ctime> // localtime
  5. #include <iomanip> // put_time
  6. #include <iostream>
  7. #include <random>
  8. #include <string>
  9. #include "db.h"
  10. #include "deck.h"
  11. #include <algorithm> // transform
  12. void string_toupper(std::string &str) {
  13. std::transform(str.begin(), str.end(), str.begin(), ::toupper);
  14. }
  15. door::ANSIColor from_string(std::string colorCode);
  16. std::function<std::ofstream &(void)> get_logger;
  17. /*
  18. Cards:
  19. 4 layers deep.
  20. https://en.wikipedia.org/wiki/Code_page_437
  21. using: \xb0, 0xb1, 0xb2, 0xdb
  22. OR: \u2591, \u2592, \u2593, \u2588
  23. Like so:
  24. ##### #####
  25. ##### #####
  26. ##### #####
  27. Cards: (Black on White, or Red on White)
  28. 8D### TH###
  29. ##D## ##H##
  30. ###D8 ###HT
  31. D, H = Red, Clubs, Spades = Black.
  32. ^ Where D = Diamonds, H = Hearts
  33. ♥, ♦, ♣, ♠
  34. \x03, \x04, \x05, \x06
  35. \u2665, \u2666, \u2663, \u2660
  36. Card layout. Actual cards are 3 lines thick.
  37. ░░░░░ ░░░░░ ░░░░░
  38. ░░░░░ ░░░░░ ░░░░░
  39. ▒▒▒▒▒░▒▒▒▒▒ #####░##### #####░#####
  40. ▒▒▒▒▒ ▒▒▒▒▒ ##### ##### ##### #####
  41. ▓▓▓▓▓▒▓▓▓▓▓▒▓▓▓▓▓ #####=#####=##### #####=#####=#####
  42. ▓▓▓▓▓ ▓▓▓▓▓ ▓▓▓▓▓ ##### ##### ##### ##### ##### #####
  43. █████▓█████▓█████▓#####=#####=#####=#####=#####=#####=#####
  44. █████ █████ █████ ##### ##### ##### ##### ##### ##### #####
  45. █████ █████ █████ ##### ##### ##### ##### ##### ##### #####
  46. #####
  47. Player Information ##### Time in: xx Time out: xx
  48. Name: ##### Playing Day: November 3rd
  49. Hand Score : Current Streak: N
  50. Todays Score : XX Cards Remaining Longest Streak: NN
  51. Monthly Score: Playing Hand X of X Most Won: xxx Lost: xxx
  52. [4] Lf [6] Rt [Space] Play Card [Enter] Draw [D]one [H]elp [R]edraw
  53. ►, ◄, ▲, ▼
  54. \x10, \x11, \x1e, \x1f
  55. \u25ba, \u25c4, \u25b2, \u25bc
  56. The Name is <- 6 to the left.
  57. # is back of card. = is upper card showing between.
  58. There's no fancy anything here. Cards overlap the last
  59. line of the previous line/card.
  60. */
  61. // The idea is that this would be defined elsewhere (maybe)
  62. int user_score = 0;
  63. // NOTE: When run as a door, we always detect CP437 (because that's what Enigma
  64. // is defaulting to)
  65. void adjust_score(int by) { user_score += by; }
  66. door::Panel make_timeout(int mx, int my) {
  67. door::ANSIColor yellowred =
  68. door::ANSIColor(door::COLOR::YELLOW, door::COLOR::RED, door::ATTR::BOLD);
  69. std::string line_text("Sorry, you've been inactive for too long.");
  70. int msgWidth = line_text.length() + (2 * 3); // + padding * 2
  71. door::Panel timeout((mx - (msgWidth)) / 2, my / 2 + 4, msgWidth);
  72. // place.setTitle(std::make_unique<door::Line>(title), 1);
  73. timeout.setStyle(door::BorderStyle::DOUBLE);
  74. timeout.setColor(yellowred);
  75. door::Line base(line_text);
  76. base.setColor(yellowred);
  77. std::string pad1(3, ' ');
  78. /*
  79. std::string pad1(3, '\xb0');
  80. if (door::unicode) {
  81. std::string unicode;
  82. door::cp437toUnicode(pad1.c_str(), unicode);
  83. pad1 = unicode;
  84. }
  85. */
  86. base.setPadding(pad1, yellowred);
  87. // base.setColor(door::ANSIColor(door::COLOR::GREEN, door::COLOR::BLACK));
  88. std::unique_ptr<door::Line> stuff = std::make_unique<door::Line>(base);
  89. timeout.addLine(std::make_unique<door::Line>(base));
  90. return timeout;
  91. }
  92. door::Panel make_notime(int mx, int my) {
  93. door::ANSIColor yellowred =
  94. door::ANSIColor(door::COLOR::YELLOW, door::COLOR::RED, door::ATTR::BOLD);
  95. std::string line_text("Sorry, you've used up all your time for today.");
  96. int msgWidth = line_text.length() + (2 * 3); // + padding * 2
  97. door::Panel timeout((mx - (msgWidth)) / 2, my / 2 + 4, msgWidth);
  98. // place.setTitle(std::make_unique<door::Line>(title), 1);
  99. timeout.setStyle(door::BorderStyle::DOUBLE);
  100. timeout.setColor(yellowred);
  101. door::Line base(line_text);
  102. base.setColor(yellowred);
  103. std::string pad1(3, ' ');
  104. /*
  105. std::string pad1(3, '\xb0');
  106. if (door::unicode) {
  107. std::string unicode;
  108. door::cp437toUnicode(pad1.c_str(), unicode);
  109. pad1 = unicode;
  110. }
  111. */
  112. base.setPadding(pad1, yellowred);
  113. // base.setColor(door::ANSIColor(door::COLOR::GREEN, door::COLOR::BLACK));
  114. std::unique_ptr<door::Line> stuff = std::make_unique<door::Line>(base);
  115. timeout.addLine(std::make_unique<door::Line>(base));
  116. return timeout;
  117. }
  118. door::Menu make_main_menu(void) {
  119. door::Menu m(5, 5, 25);
  120. door::Line mtitle("Space-Ace Main Menu");
  121. door::ANSIColor border_color(door::COLOR::CYAN, door::COLOR::BLUE);
  122. door::ANSIColor title_color(door::COLOR::CYAN, door::COLOR::BLUE,
  123. door::ATTR::BOLD);
  124. m.setColor(border_color);
  125. mtitle.setColor(title_color);
  126. mtitle.setPadding(" ", title_color);
  127. m.setTitle(std::make_unique<door::Line>(mtitle), 1);
  128. // m.setColorizer(true,
  129. m.setRender(true, door::Menu::makeRender(
  130. door::ANSIColor(door::COLOR::CYAN, door::ATTR::BOLD),
  131. door::ANSIColor(door::COLOR::BLUE, door::ATTR::BOLD),
  132. door::ANSIColor(door::COLOR::CYAN, door::ATTR::BOLD),
  133. door::ANSIColor(door::COLOR::BLUE, door::ATTR::BOLD)));
  134. // m.setColorizer(false,
  135. m.setRender(false, door::Menu::makeRender(
  136. door::ANSIColor(door::COLOR::YELLOW, door::COLOR::BLUE,
  137. door::ATTR::BOLD),
  138. door::ANSIColor(door::COLOR::WHITE, door::COLOR::BLUE,
  139. door::ATTR::BOLD),
  140. door::ANSIColor(door::COLOR::YELLOW, door::COLOR::BLUE,
  141. door::ATTR::BOLD),
  142. door::ANSIColor(door::COLOR::CYAN, door::COLOR::BLUE,
  143. door::ATTR::BOLD)));
  144. m.addSelection('1', "Play Cards");
  145. m.addSelection('2', "View Scores");
  146. m.addSelection('3', "Configure");
  147. m.addSelection('H', "Help");
  148. m.addSelection('A', "About this game");
  149. m.addSelection('Q', "Quit");
  150. return m;
  151. }
  152. door::renderFunction statusValue(door::ANSIColor status,
  153. door::ANSIColor value) {
  154. door::renderFunction rf = [status,
  155. value](const std::string &txt) -> door::Render {
  156. door::Render r(txt);
  157. door::ColorOutput co;
  158. co.pos = 0;
  159. co.len = 0;
  160. co.c = status;
  161. size_t pos = txt.find(':');
  162. if (pos == std::string::npos) {
  163. // failed to find - use entire string as status color.
  164. co.len = txt.length();
  165. r.outputs.push_back(co);
  166. } else {
  167. pos++; // Have : in status color
  168. co.len = pos;
  169. r.outputs.push_back(co);
  170. co.reset();
  171. co.pos = pos;
  172. co.c = value;
  173. co.len = txt.length() - pos;
  174. r.outputs.push_back(co);
  175. }
  176. return r;
  177. };
  178. return rf;
  179. }
  180. door::renderFunction rStatus = [](const std::string &txt) -> door::Render {
  181. door::Render r(txt);
  182. door::ColorOutput co;
  183. // default colors STATUS: value
  184. door::ANSIColor status(door::COLOR::BLUE, door::ATTR::BOLD);
  185. door::ANSIColor value(door::COLOR::YELLOW, door::ATTR::BOLD);
  186. co.pos = 0;
  187. co.len = 0;
  188. co.c = status;
  189. size_t pos = txt.find(':');
  190. if (pos == std::string::npos) {
  191. // failed to find - use entire string as status color.
  192. co.len = txt.length();
  193. r.outputs.push_back(co);
  194. } else {
  195. pos++; // Have : in status color
  196. co.len = pos;
  197. r.outputs.push_back(co);
  198. co.reset();
  199. co.pos = pos;
  200. co.c = value;
  201. co.len = txt.length() - pos;
  202. r.outputs.push_back(co);
  203. }
  204. return r;
  205. };
  206. std::string return_current_time_and_date() {
  207. auto now = std::chrono::system_clock::now();
  208. auto in_time_t = std::chrono::system_clock::to_time_t(now);
  209. std::stringstream ss;
  210. // ss << std::put_time(std::localtime(&in_time_t), "%Y-%m-%d %X");
  211. ss << std::put_time(std::localtime(&in_time_t), "%Y-%m-%d %r");
  212. return ss.str();
  213. }
  214. int press_a_key(door::Door &door) {
  215. door << door::reset << "Press a key to continue...";
  216. int r = door.sleep_key(door.inactivity);
  217. door << door::nl;
  218. return r;
  219. }
  220. door::Menu make_config_menu(void) {
  221. door::Menu m(5, 5, 31);
  222. door::Line mtitle("Space-Ace Configuration Menu");
  223. door::ANSIColor border_color(door::COLOR::CYAN, door::COLOR::BLUE);
  224. door::ANSIColor title_color(door::COLOR::CYAN, door::COLOR::BLUE,
  225. door::ATTR::BOLD);
  226. m.setColor(border_color);
  227. mtitle.setColor(title_color);
  228. mtitle.setPadding(" ", title_color);
  229. m.setTitle(std::make_unique<door::Line>(mtitle), 1);
  230. // m.setColorizer(true,
  231. m.setRender(true, door::Menu::makeRender(
  232. door::ANSIColor(door::COLOR::CYAN, door::ATTR::BOLD),
  233. door::ANSIColor(door::COLOR::BLUE, door::ATTR::BOLD),
  234. door::ANSIColor(door::COLOR::CYAN, door::ATTR::BOLD),
  235. door::ANSIColor(door::COLOR::BLUE, door::ATTR::BOLD)));
  236. // m.setColorizer(false,
  237. m.setRender(false, door::Menu::makeRender(
  238. door::ANSIColor(door::COLOR::YELLOW, door::COLOR::BLUE,
  239. door::ATTR::BOLD),
  240. door::ANSIColor(door::COLOR::WHITE, door::COLOR::BLUE,
  241. door::ATTR::BOLD),
  242. door::ANSIColor(door::COLOR::YELLOW, door::COLOR::BLUE,
  243. door::ATTR::BOLD),
  244. door::ANSIColor(door::COLOR::CYAN, door::COLOR::BLUE,
  245. door::ATTR::BOLD)));
  246. m.addSelection('D', "Deck Colors");
  247. m.addSelection('Q', "Quit");
  248. return m;
  249. }
  250. vector<std::string> deck_colors = {std::string("All"), std::string("Blue"),
  251. std::string("Cyan"), std::string("Green"),
  252. std::string("Magenta"), std::string("Red")};
  253. /**
  254. * @brief menu render that sets the text color based on the color found in the
  255. * text itself.
  256. *
  257. * @param c1
  258. * @param c2
  259. * @param c3
  260. * @return door::renderFunction
  261. */
  262. door::renderFunction makeColorRender(door::ANSIColor c1, door::ANSIColor c2,
  263. door::ANSIColor c3) {
  264. door::renderFunction render = [c1, c2,
  265. c3](const std::string &txt) -> door::Render {
  266. door::Render r(txt);
  267. bool option = true;
  268. door::ColorOutput co;
  269. // I need this mutable
  270. door::ANSIColor textColor = c3;
  271. // Color update:
  272. {
  273. std::string found;
  274. for (auto &dc : deck_colors) {
  275. if (txt.find(dc) != string::npos) {
  276. found = dc;
  277. break;
  278. }
  279. }
  280. if (!found.empty()) {
  281. if (found == "All") {
  282. // handle this some other way.
  283. textColor.setFg(door::COLOR::WHITE);
  284. } else {
  285. door::ANSIColor c = from_string(found);
  286. textColor.setFg(c.getFg());
  287. }
  288. }
  289. }
  290. co.pos = 0;
  291. co.len = 0;
  292. co.c = c1;
  293. // d << blue;
  294. int tpos = 0;
  295. for (char const &c : txt) {
  296. if (option) {
  297. if (c == '[' or c == ']') {
  298. if (co.c != c1)
  299. if (co.len != 0) {
  300. r.outputs.push_back(co);
  301. co.reset();
  302. co.pos = tpos;
  303. }
  304. co.c = c1;
  305. if (c == ']')
  306. option = false;
  307. } else {
  308. if (co.c != c2)
  309. if (co.len != 0) {
  310. r.outputs.push_back(co);
  311. co.reset();
  312. co.pos = tpos;
  313. }
  314. co.c = c2;
  315. }
  316. } else {
  317. if (co.c != textColor)
  318. if (co.len != 0) {
  319. r.outputs.push_back(co);
  320. co.reset();
  321. co.pos = tpos;
  322. }
  323. co.c = textColor;
  324. }
  325. co.len++;
  326. tpos++;
  327. }
  328. if (co.len != 0) {
  329. r.outputs.push_back(co);
  330. }
  331. return r;
  332. };
  333. return render;
  334. }
  335. door::Menu make_deck_menu(void) {
  336. door::Menu m(5, 5, 31);
  337. door::Line mtitle("Space-Ace Deck Menu");
  338. door::ANSIColor border_color(door::COLOR::CYAN, door::COLOR::BLUE);
  339. door::ANSIColor title_color(door::COLOR::CYAN, door::COLOR::BLUE,
  340. door::ATTR::BOLD);
  341. m.setColor(border_color);
  342. mtitle.setColor(title_color);
  343. mtitle.setPadding(" ", title_color);
  344. m.setTitle(std::make_unique<door::Line>(mtitle), 1);
  345. /*
  346. // m.setColorizer(true,
  347. m.setRender(true, door::Menu::makeRender(
  348. door::ANSIColor(door::COLOR::CYAN, door::ATTR::BOLD),
  349. door::ANSIColor(door::COLOR::BLUE, door::ATTR::BOLD),
  350. door::ANSIColor(door::COLOR::CYAN, door::ATTR::BOLD),
  351. door::ANSIColor(door::COLOR::BLUE,
  352. door::ATTR::BOLD)));
  353. // m.setColorizer(false,
  354. m.setRender(false, door::Menu::makeRender(
  355. door::ANSIColor(door::COLOR::YELLOW,
  356. door::COLOR::BLUE, door::ATTR::BOLD), door::ANSIColor(door::COLOR::WHITE,
  357. door::COLOR::BLUE, door::ATTR::BOLD), door::ANSIColor(door::COLOR::YELLOW,
  358. door::COLOR::BLUE, door::ATTR::BOLD), door::ANSIColor(door::COLOR::CYAN,
  359. door::COLOR::BLUE, door::ATTR::BOLD)));
  360. */
  361. m.setRender(true, makeColorRender(
  362. door::ANSIColor(door::COLOR::CYAN, door::ATTR::BOLD),
  363. door::ANSIColor(door::COLOR::BLUE, door::ATTR::BOLD),
  364. door::ANSIColor(door::COLOR::CYAN, door::ATTR::BOLD)));
  365. m.setRender(false, makeColorRender(
  366. door::ANSIColor(door::COLOR::YELLOW, door::COLOR::BLUE,
  367. door::ATTR::BOLD),
  368. door::ANSIColor(door::COLOR::WHITE, door::COLOR::BLUE,
  369. door::ATTR::BOLD),
  370. door::ANSIColor(door::COLOR::YELLOW, door::COLOR::BLUE,
  371. door::ATTR::BOLD)));
  372. for (auto iter = deck_colors.begin(); iter != deck_colors.end(); ++iter) {
  373. char c = (*iter)[0];
  374. m.addSelection(c, (*iter).c_str());
  375. }
  376. /*
  377. m.addSelection('A', "All");
  378. m.addSelection('B', "Blue");
  379. m.addSelection('C', "Cyan");
  380. m.addSelection('G', "Green");
  381. m.addSelection('M', "Magenta");
  382. m.addSelection('R', "Red");
  383. */
  384. return m;
  385. }
  386. bool iequals(const string &a, const string &b) {
  387. unsigned int sz = a.size();
  388. if (b.size() != sz)
  389. return false;
  390. for (unsigned int i = 0; i < sz; ++i)
  391. if (tolower(a[i]) != tolower(b[i]))
  392. return false;
  393. return true;
  394. }
  395. // convert a string to an option
  396. // an option to the string to store
  397. door::ANSIColor from_string(std::string colorCode) {
  398. std::map<std::string, door::ANSIColor> codeMap = {
  399. {std::string("BLUE"), door::ANSIColor(door::COLOR::BLUE)},
  400. {std::string("RED"), door::ANSIColor(door::COLOR::RED)},
  401. {std::string("CYAN"), door::ANSIColor(door::COLOR::CYAN)},
  402. {std::string("GREEN"), door::ANSIColor(door::COLOR::GREEN)},
  403. {std::string("MAGENTA"), door::ANSIColor(door::COLOR::MAGENTA)}};
  404. std::string code = colorCode;
  405. string_toupper(code);
  406. auto iter = codeMap.find(code);
  407. if (iter != codeMap.end()) {
  408. return iter->second;
  409. }
  410. // And if it doesn't match, and isn't ALL ... ?
  411. // if (code.compare("ALL") == 0) {
  412. std::random_device dev;
  413. std::mt19937_64 rng(dev());
  414. std::uniform_int_distribution<size_t> idDist(0, codeMap.size() - 1);
  415. iter = codeMap.begin();
  416. std::advance(iter, idDist(rng));
  417. return iter->second;
  418. // }
  419. }
  420. // This does not seem to be working. I keep getting zero.
  421. int opt_from_string(std::string colorCode) {
  422. for (std::size_t pos = 0; pos != deck_colors.size(); ++pos) {
  423. // if (caseInsensitiveStringCompare(colorCode, deck_colors[pos]) == 0) {
  424. if (iequals(colorCode, deck_colors[pos])) {
  425. return pos;
  426. }
  427. }
  428. return 0;
  429. }
  430. std::string from_color_option(int opt) { return deck_colors[opt]; }
  431. int configure(door::Door &door, DBData &db) {
  432. auto menu = make_config_menu();
  433. int r = 0;
  434. while (r >= 0) {
  435. r = menu.choose(door);
  436. if (r > 0) {
  437. door << door::reset << door::cls;
  438. char c = menu.which(r - 1);
  439. if (c == 'D') {
  440. // Ok, deck colors
  441. // get default
  442. std::string key("DeckColor");
  443. std::string currentDefault =
  444. db.getSetting(door.username, key, std::string("ALL"));
  445. int currentOpt = opt_from_string(currentDefault);
  446. door << door::reset << door::cls;
  447. auto deck = make_deck_menu();
  448. deck.defaultSelection(currentOpt);
  449. int newOpt = deck.choose(door);
  450. door << door::reset << door::cls;
  451. if (newOpt >= 0) {
  452. newOpt--;
  453. std::string newColor = from_color_option(newOpt);
  454. if (newOpt != currentOpt) {
  455. door.log() << key << " was " << currentDefault << ", " << currentOpt
  456. << ". Now " << newColor << ", " << newOpt << std::endl;
  457. db.setSetting(door.username, key, newColor);
  458. }
  459. }
  460. }
  461. if (c == 'Q') {
  462. return r;
  463. }
  464. }
  465. }
  466. return r;
  467. }
  468. int play_cards(door::Door &door, DBData &db, std::mt19937 &rng) {
  469. int mx = door.width;
  470. int my = door.height;
  471. // cards color --
  472. // configured by the player.
  473. door::ANSIColor deck_color;
  474. std::string key("DeckColor");
  475. std::string currentDefault =
  476. db.getSetting(door.username, key, std::string("ALL"));
  477. door.log() << key << " shows as " << currentDefault << std::endl;
  478. deck_color = from_string(currentDefault);
  479. /*
  480. // RED, BLUE, GREEN, MAGENTA, CYAN
  481. std::uniform_int_distribution<int> rand_color(0, 4);
  482. switch (rand_color(rng)) {
  483. case 0:
  484. deck_color = door::ANSIColor(door::COLOR::RED);
  485. break;
  486. case 1:
  487. deck_color = door::ANSIColor(door::COLOR::BLUE);
  488. break;
  489. case 2:
  490. deck_color = door::ANSIColor(door::COLOR::GREEN);
  491. break;
  492. case 3:
  493. deck_color = door::ANSIColor(door::COLOR::MAGENTA);
  494. break;
  495. case 4:
  496. deck_color = door::ANSIColor(door::COLOR::CYAN);
  497. break;
  498. default:
  499. deck_color = door::ANSIColor(door::COLOR::BLUE, door::ATTR::BLINK);
  500. break;
  501. }
  502. */
  503. int height = 3;
  504. Deck d(deck_color, height);
  505. door::Panel *c;
  506. door << door::reset << door::cls;
  507. // This displays the cards in the upper left corner.
  508. // We want them center, and down some.
  509. const int space = 3;
  510. // int cards_dealt_width = 59; int cards_dealt_height = 9;
  511. int game_width;
  512. int game_height = 13; // 9;
  513. {
  514. int cx, cy, level;
  515. cardgo(27, space, height, cx, cy, level);
  516. game_width = cx + 5; // card width
  517. }
  518. int off_x = (mx - game_width) / 2;
  519. int off_y = (my - game_height) / 2;
  520. // The idea is to see the cards with <<Something Unique to the card game>>,
  521. // Year, Month, Day, and game (like 1 of 3).
  522. // This will make the games the same/fair for everyone.
  523. std::string tripeaks("Space Ace - Tri-Peaks Solitaire");
  524. int tp_off_x = (mx - tripeaks.size()) / 2;
  525. door::Panel spaceAceTriPeaks(tp_off_x, off_y, tripeaks.size() + 2);
  526. spaceAceTriPeaks.setStyle(door::BorderStyle::SINGLE);
  527. spaceAceTriPeaks.setColor(
  528. door::ANSIColor(door::COLOR::CYAN, door::COLOR::BLACK));
  529. spaceAceTriPeaks.addLine(
  530. std::make_unique<door::Line>(tripeaks, tripeaks.size() + 2));
  531. door << spaceAceTriPeaks;
  532. off_y += 3;
  533. std::seed_seq s1{2021, 2, 27, 1};
  534. cards deck1 = card_shuffle(s1, 1);
  535. cards state = card_states();
  536. // I tried setting the cursor before the delay and before displaying the
  537. // card. It is very hard to see / just about useless. Not worth the effort.
  538. for (int x = 0; x < 28; x++) {
  539. int cx, cy, level;
  540. cardgo(x, space, height, cx, cy, level);
  541. // This is hardly visible.
  542. // door << door::Goto(cx + off_x - 1, cy + off_y + 1);
  543. std::this_thread::sleep_for(std::chrono::milliseconds(75));
  544. c = d.back(level);
  545. c->set(cx + off_x, cy + off_y);
  546. door << *c;
  547. }
  548. /*
  549. std::this_thread::sleep_for(
  550. std::chrono::seconds(1)); // 3 secs seemed too long!
  551. */
  552. for (int x = 18; x < 28; x++) {
  553. int cx, cy, level;
  554. // usleep(1000 * 20);
  555. state.at(x) = 1;
  556. cardgo(x, space, height, cx, cy, level);
  557. // door << door::Goto(cx + off_x - 1, cy + off_y + 1);
  558. std::this_thread::sleep_for(std::chrono::milliseconds(200));
  559. c = d.card(deck1.at(x));
  560. c->set(cx + off_x, cy + off_y);
  561. door << *c;
  562. }
  563. door << door::reset;
  564. door << door::nl << door::nl;
  565. int r = door.sleep_key(door.inactivity);
  566. return r;
  567. }
  568. door::Panel make_about(void) {
  569. door::Panel about(2, 2, 60);
  570. about.setStyle(door::BorderStyle::DOUBLE_SINGLE);
  571. about.setColor(door::ANSIColor(door::COLOR::YELLOW, door::COLOR::BLUE,
  572. door::ATTR::BOLD));
  573. about.addLine(std::make_unique<door::Line>("About This Door", 60));
  574. /*
  575. door::Line magic("---------------------------------", 60);
  576. magic.setColor(door::ANSIColor(door::COLOR::YELLOW, door::COLOR::BLACK,
  577. door::ATTR::BOLD));
  578. */
  579. about.addLine(std::make_unique<door::Line>(
  580. "---------------------------------", 60,
  581. door::ANSIColor(door::COLOR::CYAN, door::COLOR::BLUE, door::ATTR::BOLD)));
  582. /*
  583. 123456789012345678901234567890123456789012345678901234567890
  584. This door was written by Bugz.
  585. It is written in c++, only supports Linux, and replaces
  586. opendoors.
  587. It's written in c++, and replaces the outdated opendoors
  588. library.
  589. */
  590. about.addLine(
  591. std::make_unique<door::Line>("This door was written by Bugz.", 60));
  592. about.addLine(std::make_unique<door::Line>("", 60));
  593. about.addLine(std::make_unique<door::Line>(
  594. "It is written in c++, only support Linux, and replaces", 60));
  595. about.addLine(std::make_unique<door::Line>("opendoors.", 60));
  596. /*
  597. door::updateFunction updater = [](void) -> std::string {
  598. std::string text = "Currently: ";
  599. text.append(return_current_time_and_date());
  600. return text;
  601. };
  602. std::string current = updater();
  603. door::Line active(current, 60);
  604. active.setUpdater(updater);
  605. active.setRender(renderStatusValue(
  606. door::ANSIColor(door::COLOR::WHITE, door::COLOR::BLUE,
  607. door::ATTR::BOLD), door::ANSIColor(door::COLOR::YELLOW, door::COLOR::BLUE,
  608. door::ATTR::BOLD)));
  609. about.addLine(std::make_unique<door::Line>(active));
  610. */
  611. /*
  612. about.addLine(std::make_unique<door::Line>(
  613. "Status: blue", 60,
  614. statusValue(door::ANSIColor(door::COLOR::GREEN, door::ATTR::BOLD),
  615. door::ANSIColor(door::COLOR::MAGENTA,
  616. door::ATTR::BLINK)))); about.addLine(std::make_unique<door::Line>("Name:
  617. BUGZ", 60, rStatus)); about.addLine(std::make_unique<door::Line>( "Size:
  618. 10240", 60, statusValue(door::ANSIColor(door::COLOR::GREEN,
  619. door::COLOR::BLUE, door::ATTR::BOLD), door::ANSIColor(door::COLOR::YELLOW,
  620. door::COLOR::BLUE, door::ATTR::BOLD, door::ATTR::BLINK))));
  621. about.addLine(std::make_unique<door::Line>("Bugz is here.", 60, rStatus));
  622. */
  623. return about;
  624. }
  625. void display_starfield(door::Door &door, std::mt19937 &rng) {
  626. door << door::reset << door::cls;
  627. int mx = door.width;
  628. int my = door.height;
  629. // display starfield
  630. const char *stars[2];
  631. stars[0] = ".";
  632. if (door::unicode) {
  633. stars[1] = "\u2219"; // "\u00b7";
  634. } else {
  635. stars[1] = "\xf9"; // "\xfa";
  636. };
  637. {
  638. // Make uniform random distribution between 1 and MAX screen size X/Y
  639. std::uniform_int_distribution<int> uni_x(1, mx);
  640. std::uniform_int_distribution<int> uni_y(1, my);
  641. door::ANSIColor white(door::COLOR::WHITE);
  642. door::ANSIColor dark(door::COLOR::BLACK, door::ATTR::BRIGHT);
  643. // 10 is too many, 100 is too few. 40 looks ok.
  644. int MAX_STARS = ((mx * my) / 40);
  645. // door.log() << "Generating starmap using " << mx << "," << my << " : "
  646. // << MAX_STARS << " stars." << std::endl;
  647. for (int x = 0; x < MAX_STARS; x++) {
  648. door::Goto star_at(uni_x(rng), uni_y(rng));
  649. door << star_at;
  650. if (x % 5 < 2)
  651. door << dark;
  652. else
  653. door << white;
  654. if (x % 2 == 0)
  655. door << stars[0];
  656. else
  657. door << stars[1];
  658. }
  659. }
  660. }
  661. void display_space_ace(door::Door &door) {
  662. int mx = door.width;
  663. int my = door.height;
  664. // space_ace is 72 chars wide, 6 high
  665. int sa_x = (mx - 72) / 2;
  666. int sa_y = (my - 6) / 2;
  667. // output the SpaceAce logo -- centered!
  668. for (const auto s : space) {
  669. door::Goto sa_at(sa_x, sa_y);
  670. door << sa_at;
  671. if (door::unicode) {
  672. std::string unicode;
  673. door::cp437toUnicode(s, unicode);
  674. door << unicode; // << door::nl;
  675. } else
  676. door << s; // << door::nl;
  677. sa_y++;
  678. }
  679. // pause 5 seconds so they can enjoy our awesome logo
  680. door.sleep_key(5);
  681. }
  682. void display_starfield_space_ace(door::Door &door, std::mt19937 &rng) {
  683. // mx = door.width;
  684. // my = door.height;
  685. display_starfield(door, rng);
  686. display_space_ace(door);
  687. door << door::reset;
  688. }
  689. int main(int argc, char *argv[]) {
  690. door::Door door("space-ace", argc, argv);
  691. // door << door::reset << door::cls << door::nl;
  692. get_logger = [&door]() -> ofstream & { return door.log(); };
  693. DBData spacedb;
  694. /*
  695. std::function<std::ofstream &(void)> get_logger;
  696. get_logger = [&door]() -> ofstream & { return door.log(); };
  697. if (get_logger) {
  698. get_logger() << "MEOW" << std::endl;
  699. get_logger() << "hey! It works!" << std::endl;
  700. }
  701. */
  702. // spacedb.init();
  703. /*
  704. // Example: How to read/set values in spacedb settings.
  705. std::string setting = "last_play";
  706. std::string user = door.username;
  707. std::string value;
  708. std::string blank = "<blank>";
  709. value = spacedb.getSetting(user, setting, blank);
  710. door << door::reset << "last_play: " << value << door::nl;
  711. std::this_thread::sleep_for(std::chrono::seconds(2));
  712. value = return_current_time_and_date();
  713. spacedb.setSetting(user, setting, value);
  714. */
  715. // https://stackoverflow.com/questions/5008804/generating-random-integer-from-a-range
  716. std::random_device rd; // only used once to initialise (seed) engine
  717. std::mt19937 rng(rd());
  718. // random-number engine used (Mersenne-Twister in this case)
  719. // std::uniform_int_distribution<int> uni(min, max); // guaranteed unbiased
  720. int mx, my; // Max screen width/height
  721. if (door.width == 0) {
  722. // screen detect failed, use sensible defaults
  723. door.width = mx = 80;
  724. door.height = my = 23;
  725. } else {
  726. mx = door.width;
  727. my = door.height;
  728. }
  729. // We assume here that the width and height are something crazy like 10x15.
  730. // :P
  731. display_starfield_space_ace(door, rng);
  732. // for testing inactivity timeout
  733. // door.inactivity = 10;
  734. door::Panel timeout = make_timeout(mx, my);
  735. door::Menu m = make_main_menu();
  736. door::Panel about = make_about();
  737. // center the about box
  738. about.set((mx - 60) / 2, (my - 5) / 2);
  739. int r = 0;
  740. while ((r >= 0) and (r != 6)) {
  741. // starfield + menu ?
  742. display_starfield(door, rng);
  743. r = m.choose(door);
  744. // need to reset the colors. (whoops!)
  745. door << door::reset << door::cls; // door::nl;
  746. // OK! The screen is blank at this point!
  747. switch (r) {
  748. case 1: // play game
  749. r = play_cards(door, spacedb, rng);
  750. break;
  751. case 2: // view scores
  752. door << "Show scores goes here!" << door::nl;
  753. r = press_a_key(door);
  754. break;
  755. case 3: // configure
  756. r = configure(door, spacedb);
  757. // r = press_a_key(door);
  758. break;
  759. case 4: // help
  760. door << "Help! Need some help here..." << door::nl;
  761. r = press_a_key(door);
  762. break;
  763. case 5: // about
  764. display_starfield(door, rng);
  765. door << about << door::nl;
  766. r = press_a_key(door);
  767. break;
  768. case 6: // quit
  769. break;
  770. }
  771. }
  772. if (r < 0) {
  773. TIMEOUT:
  774. if (r == -1) {
  775. door.log() << "TIMEOUT" << std::endl;
  776. door << timeout << door::reset << door::nl << door::nl;
  777. } else {
  778. if (r == -3) {
  779. door.log() << "OUTTA TIME" << std::endl;
  780. door::Panel notime = make_notime(mx, my);
  781. door << notime << door::reset << door::nl;
  782. }
  783. }
  784. return 0;
  785. }
  786. door << door::nl;
  787. /*
  788. // magic time!
  789. door << door::reset << door::nl << "Press another key...";
  790. int x;
  791. for (x = 0; x < 60; ++x) {
  792. r = door.sleep_key(1);
  793. if (r == -1) {
  794. // ok! Expected timeout!
  795. // PROBLEM: regular "local" terminal loses current attributes
  796. // when cursor is save / restored.
  797. door << door::SaveCursor;
  798. if (about.update(door)) {
  799. // ok I need to "fix" the cursor position.
  800. // it has moved.
  801. }
  802. door << door::RestoreCursor << door::reset;
  803. } else {
  804. if (r < 0)
  805. goto TIMEOUT;
  806. if (r >= 0)
  807. break;
  808. }
  809. }
  810. if (x == 60)
  811. goto TIMEOUT;
  812. */
  813. door << door::nl;
  814. #ifdef NNY
  815. // configured by the player.
  816. door::ANSIColor deck_color;
  817. // RED, BLUE, GREEN, MAGENTA, CYAN
  818. std::uniform_int_distribution<int> rand_color(0, 4);
  819. switch (rand_color(rng)) {
  820. case 0:
  821. deck_color = door::ANSIColor(door::COLOR::RED);
  822. break;
  823. case 1:
  824. deck_color = door::ANSIColor(door::COLOR::BLUE);
  825. break;
  826. case 2:
  827. deck_color = door::ANSIColor(door::COLOR::GREEN);
  828. break;
  829. case 3:
  830. deck_color = door::ANSIColor(door::COLOR::MAGENTA);
  831. break;
  832. case 4:
  833. deck_color = door::ANSIColor(door::COLOR::CYAN);
  834. break;
  835. default:
  836. deck_color = door::ANSIColor(door::COLOR::BLUE, door::ATTR::BLINK);
  837. break;
  838. }
  839. int height = 3;
  840. Deck d(deck_color, height);
  841. door::Panel *c;
  842. door << door::reset << door::cls;
  843. // This displays the cards in the upper left corner.
  844. // We want them center, and down some.
  845. int space = 3;
  846. // int cards_dealt_width = 59; int cards_dealt_height = 9;
  847. int game_width;
  848. {
  849. int cx, cy, level;
  850. cardgo(27, space, height, cx, cy, level);
  851. game_width = cx + 5; // card width
  852. }
  853. int off_x = (mx - game_width) / 2;
  854. int off_y = (my - 9) / 2;
  855. // The idea is to see the cards with <<Something Unique to the card game>>,
  856. // Year, Month, Day, and game (like 1 of 3).
  857. // This will make the games the same/fair for everyone.
  858. std::seed_seq s1{2021, 2, 27, 1};
  859. cards deck1 = card_shuffle(s1, 1);
  860. cards state = card_states();
  861. // I tried setting the cursor before the delay and before displaying the
  862. // card. It is very hard to see / just about useless. Not worth the effort.
  863. for (int x = 0; x < 28; x++) {
  864. int cx, cy, level;
  865. cardgo(x, space, height, cx, cy, level);
  866. // This is hardly visible.
  867. // door << door::Goto(cx + off_x - 1, cy + off_y + 1);
  868. std::this_thread::sleep_for(std::chrono::milliseconds(75));
  869. c = d.back(level);
  870. c->set(cx + off_x, cy + off_y);
  871. door << *c;
  872. }
  873. /*
  874. std::this_thread::sleep_for(
  875. std::chrono::seconds(1)); // 3 secs seemed too long!
  876. */
  877. for (int x = 18; x < 28; x++) {
  878. int cx, cy, level;
  879. // usleep(1000 * 20);
  880. state.at(x) = 1;
  881. cardgo(x, space, height, cx, cy, level);
  882. // door << door::Goto(cx + off_x - 1, cy + off_y + 1);
  883. std::this_thread::sleep_for(std::chrono::milliseconds(200));
  884. c = d.card(deck1.at(x));
  885. c->set(cx + off_x, cy + off_y);
  886. door << *c;
  887. }
  888. door << door::reset;
  889. door << door::nl << door::nl;
  890. r = door.sleep_key(door.inactivity);
  891. if (r < 0)
  892. goto TIMEOUT;
  893. #endif
  894. /*
  895. door::Panel *p = d.back(2);
  896. p->set(10, 10);
  897. door << *p;
  898. door::Panel *d8 = d.card(8);
  899. d8->set(20, 8);
  900. door << *d8;
  901. r = door.sleep_key(door.inactivity);
  902. if (r < 0)
  903. goto TIMEOUT;
  904. */
  905. // door << door::reset << door::cls;
  906. display_starfield(door, rng);
  907. door << m << door::reset << door::nl;
  908. // Normal DOOR exit goes here...
  909. door << door::nl << "Returning you to the BBS, please wait..." << door::nl;
  910. get_logger = nullptr;
  911. return 0;
  912. }