main.cpp 39 KB

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