main.cpp 40 KB

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