main.cpp 41 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457
  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 = 28;
  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(51 - 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. // This displays the cards in the upper left corner.
  738. // We want them center, and down some.
  739. const int space = 3;
  740. // int cards_dealt_width = 59; int cards_dealt_height = 9;
  741. int game_width;
  742. int game_height = 20; // 13; // 9;
  743. {
  744. int cx, cy, level;
  745. cardgo(27, space, height, cx, cy, level);
  746. game_width = cx + 5; // card width
  747. }
  748. int off_x = (mx - game_width) / 2;
  749. int off_y = (my - game_height) / 2;
  750. // The idea is to see the cards with <<Something Unique to the card game>>,
  751. // Year, Month, Day, and game (like 1 of 3).
  752. // This will make the games the same/fair for everyone.
  753. door::Panel spaceAceTriPeaks = make_tripeaks();
  754. int tp_off_x = (mx - spaceAceTriPeaks.getWidth()) / 2;
  755. spaceAceTriPeaks.set(tp_off_x, off_y);
  756. off_y += 3;
  757. // figure out what date we're playing / what game we're playing
  758. std::seed_seq s1{2021, 2, 27, 1};
  759. cards deck1 = card_shuffle(s1, 1);
  760. cards state = card_states();
  761. door::Panel p1 = make_panel1(door);
  762. door::Panel p2 = make_panel2();
  763. door::Panel p3 = make_panel3();
  764. door::Panel pc = make_commandline();
  765. {
  766. int off_yp = off_y + 11;
  767. int cxp, cyp, levelp;
  768. int left_panel, right_panel;
  769. // find position of card, to position the panels
  770. cardgo(18, space, height, cxp, cyp, levelp);
  771. left_panel = cxp;
  772. cardgo(15, space, height, cxp, cyp, levelp);
  773. right_panel = cxp;
  774. p1.set(left_panel + off_x, off_yp);
  775. p2.set(right_panel + off_x, off_yp);
  776. pc.set(left_panel + off_x, off_yp + 5);
  777. }
  778. bool dealing = true;
  779. int r = 0;
  780. while ((r >= 0) and (r != 'Q')) {
  781. door << door::reset << door::cls;
  782. door << spaceAceTriPeaks;
  783. {
  784. // step 1:
  785. // draw the deck "source"
  786. int cx, cy, level;
  787. cardgo(29, space, height, cx, cy, level);
  788. if (card_number == 51)
  789. level = 0; // out of cards!
  790. c = d.back(level);
  791. c->set(cx + off_x, cy + off_y);
  792. // p3 is heigh below
  793. p3.set(cx + off_x, cy + off_y + height);
  794. door << p1 << p3 << p2 << pc;
  795. door << *c;
  796. if (dealing)
  797. std::this_thread::sleep_for(std::chrono::seconds(1));
  798. }
  799. // I tried setting the cursor before the delay and before displaying the
  800. // card. It is very hard to see / just about useless. Not worth the effort.
  801. for (int x = 0; x < (dealing ? 28 : 29); x++) {
  802. int cx, cy, level;
  803. cardgo(x, space, height, cx, cy, level);
  804. // This is hardly visible.
  805. // door << door::Goto(cx + off_x - 1, cy + off_y + 1);
  806. if (dealing)
  807. std::this_thread::sleep_for(std::chrono::milliseconds(75));
  808. if (dealing) {
  809. c = d.back(level);
  810. c->set(cx + off_x, cy + off_y);
  811. door << *c;
  812. } else {
  813. // redrawing -- draw the cards with their correct "state"
  814. int s = state.at(x);
  815. switch (s) {
  816. case 0:
  817. c = d.back(level);
  818. c->set(cx + off_x, cy + off_y);
  819. door << *c;
  820. break;
  821. case 1:
  822. // cardgo(x, space, height, cx, cy, level);
  823. if (x == 28)
  824. c = d.card(deck1.at(card_number));
  825. else
  826. c = d.card(deck1.at(x));
  827. c->set(cx + off_x, cy + off_y);
  828. door << *c;
  829. break;
  830. case 2:
  831. // no card to draw. :)
  832. break;
  833. }
  834. }
  835. }
  836. if (dealing)
  837. for (int x = 18; x < 29; x++) {
  838. int cx, cy, level;
  839. state.at(x) = 1;
  840. cardgo(x, space, height, cx, cy, level);
  841. // door << door::Goto(cx + off_x - 1, cy + off_y + 1);
  842. std::this_thread::sleep_for(std::chrono::milliseconds(200));
  843. c = d.card(deck1.at(x));
  844. c->set(cx + off_x, cy + off_y);
  845. door << *c;
  846. }
  847. else {
  848. // setup the "next card" @ 28
  849. }
  850. dealing = false;
  851. p3.update(door);
  852. door << door::reset;
  853. r = door.sleep_key(door.inactivity);
  854. if ((r < 0x1000) and (r > 0)) {
  855. r = std::toupper(r);
  856. if (r == ' ') {
  857. if (card_number < 51) {
  858. card_number++;
  859. // ok, we would redraw just that card here...
  860. // only "exit" and redo the drawing loop when "R" redraw is used!
  861. }
  862. }
  863. }
  864. }
  865. return r;
  866. }
  867. door::Panel make_about(void) {
  868. const int W = 60;
  869. door::Panel about(W);
  870. about.setStyle(door::BorderStyle::DOUBLE_SINGLE);
  871. about.setColor(door::ANSIColor(door::COLOR::YELLOW, door::COLOR::BLUE,
  872. door::ATTR::BOLD));
  873. about.addLine(std::make_unique<door::Line>("About This Door", W));
  874. about.addLine(std::make_unique<door::Line>(
  875. "---------------------------------", W,
  876. door::ANSIColor(door::COLOR::CYAN, door::COLOR::BLUE, door::ATTR::BOLD)));
  877. /*
  878. 123456789012345678901234567890123456789012345678901234567890-60
  879. This door was written by Bugz.
  880. It is written in c++, only supports Linux, and replaces
  881. opendoors.
  882. It's written in c++, and replaces the outdated opendoors
  883. library.
  884. */
  885. about.addLine(
  886. std::make_unique<door::Line>(SPACEACE " v" SPACEACE_VERSION, W));
  887. std::string copyright = SPACEACE_COPYRIGHT;
  888. if (door::unicode) {
  889. replace(copyright, "(C)", "\u00a9");
  890. }
  891. about.addLine(std::make_unique<door::Line>(copyright, W));
  892. about.addLine(std::make_unique<door::Line>("", W));
  893. about.addLine(
  894. std::make_unique<door::Line>("This door was written by Bugz.", W));
  895. about.addLine(std::make_unique<door::Line>("", W));
  896. about.addLine(std::make_unique<door::Line>(
  897. "It is written in c++, only support Linux, and replaces", W));
  898. about.addLine(std::make_unique<door::Line>("opendoors.", W));
  899. /*
  900. door::updateFunction updater = [](void) -> std::string {
  901. std::string text = "Currently: ";
  902. text.append(return_current_time_and_date());
  903. return text;
  904. };
  905. std::string current = updater();
  906. door::Line active(current, 60);
  907. active.setUpdater(updater);
  908. active.setRender(statusValue(
  909. door::ANSIColor(door::COLOR::WHITE, door::COLOR::BLUE,
  910. door::ATTR::BOLD), door::ANSIColor(door::COLOR::YELLOW, door::COLOR::BLUE,
  911. door::ATTR::BOLD)));
  912. about.addLine(std::make_unique<door::Line>(active));
  913. */
  914. return about;
  915. }
  916. void display_starfield(door::Door &door, std::mt19937 &rng) {
  917. door << door::reset << door::cls;
  918. int mx = door.width;
  919. int my = door.height;
  920. // display starfield
  921. const char *stars[2];
  922. stars[0] = ".";
  923. if (door::unicode) {
  924. stars[1] = "\u2219"; // "\u00b7";
  925. } else {
  926. stars[1] = "\xf9"; // "\xfa";
  927. };
  928. {
  929. // Make uniform random distribution between 1 and MAX screen size X/Y
  930. std::uniform_int_distribution<int> uni_x(1, mx);
  931. std::uniform_int_distribution<int> uni_y(1, my);
  932. door::ANSIColor white(door::COLOR::WHITE);
  933. door::ANSIColor dark(door::COLOR::BLACK, door::ATTR::BRIGHT);
  934. // 10 is too many, 100 is too few. 40 looks ok.
  935. int MAX_STARS = ((mx * my) / 40);
  936. // door.log() << "Generating starmap using " << mx << "," << my << " : "
  937. // << MAX_STARS << " stars." << std::endl;
  938. for (int x = 0; x < MAX_STARS; x++) {
  939. door::Goto star_at(uni_x(rng), uni_y(rng));
  940. door << star_at;
  941. if (x % 5 < 2)
  942. door << dark;
  943. else
  944. door << white;
  945. if (x % 2 == 0)
  946. door << stars[0];
  947. else
  948. door << stars[1];
  949. }
  950. }
  951. }
  952. void display_space_ace(door::Door &door) {
  953. int mx = door.width;
  954. int my = door.height;
  955. // space_ace is 72 chars wide, 6 high
  956. int sa_x = (mx - 72) / 2;
  957. int sa_y = (my - 6) / 2;
  958. // output the SpaceAce logo -- centered!
  959. for (const auto s : space) {
  960. door::Goto sa_at(sa_x, sa_y);
  961. door << sa_at;
  962. if (door::unicode) {
  963. std::string unicode;
  964. door::cp437toUnicode(s, unicode);
  965. door << unicode; // << door::nl;
  966. } else
  967. door << s; // << door::nl;
  968. sa_y++;
  969. }
  970. // pause 5 seconds so they can enjoy our awesome logo -- if they want.
  971. door.sleep_key(5);
  972. }
  973. void display_starfield_space_ace(door::Door &door, std::mt19937 &rng) {
  974. // mx = door.width;
  975. // my = door.height;
  976. display_starfield(door, rng);
  977. display_space_ace(door);
  978. door << door::reset;
  979. }
  980. int main(int argc, char *argv[]) {
  981. door::Door door("space-ace", argc, argv);
  982. // store the door log so we can easily access it.
  983. get_logger = [&door]() -> ofstream & { return door.log(); };
  984. DBData spacedb;
  985. spacedb.setUser(door.username);
  986. // retrieve lastcall
  987. time_t last_call = std::stol(spacedb.getSetting("LastCall", "0"));
  988. // store now as lastcall
  989. time_t now =
  990. std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
  991. spacedb.setSetting("LastCall", std::to_string(now));
  992. // Have they used this door before?
  993. if (last_call != 0) {
  994. door << "Welcome Back!" << door::nl;
  995. auto nowClock = std::chrono::system_clock::from_time_t(now);
  996. auto lastClock = std::chrono::system_clock::from_time_t(last_call);
  997. auto delta = nowClock - lastClock;
  998. // int days = chrono::duration_cast<chrono::days>(delta).count(); // c++
  999. // 20
  1000. int hours = chrono::duration_cast<chrono::hours>(delta).count();
  1001. int days = hours / 24;
  1002. int minutes = chrono::duration_cast<chrono::minutes>(delta).count();
  1003. int secs = chrono::duration_cast<chrono::seconds>(delta).count();
  1004. if (days > 1) {
  1005. door << "It's been " << days << " days since you last played."
  1006. << door::nl;
  1007. } else {
  1008. if (hours > 1) {
  1009. door << "It's been " << hours << " hours since you last played."
  1010. << door::nl;
  1011. } else {
  1012. if (minutes > 1) {
  1013. door << "It's been " << minutes << " minutes since you last played."
  1014. << door::nl;
  1015. } else {
  1016. door << "It's been " << secs << " seconds since you last played."
  1017. << door::nl;
  1018. door << "It's like you never left." << door::nl;
  1019. }
  1020. }
  1021. }
  1022. press_a_key(door);
  1023. }
  1024. /*
  1025. // example: saving the door.log() for global use.
  1026. std::function<std::ofstream &(void)> get_logger;
  1027. get_logger = [&door]() -> ofstream & { return door.log(); };
  1028. if (get_logger) {
  1029. get_logger() << "MEOW" << std::endl;
  1030. get_logger() << "hey! It works!" << std::endl;
  1031. }
  1032. get_logger = nullptr; // before door destruction
  1033. */
  1034. // https://stackoverflow.com/questions/5008804/generating-random-integer-from-a-range
  1035. std::random_device rd; // only used once to initialise (seed) engine
  1036. std::mt19937 rng(rd());
  1037. // random-number engine used (Mersenne-Twister in this case)
  1038. // std::uniform_int_distribution<int> uni(min, max); // guaranteed unbiased
  1039. int mx, my; // Max screen width/height
  1040. if (door.width == 0) {
  1041. // screen detect failed, use sensible defaults
  1042. door.width = mx = 80;
  1043. door.height = my = 23;
  1044. } else {
  1045. mx = door.width;
  1046. my = door.height;
  1047. }
  1048. // We assume here that the width and height aren't something crazy like
  1049. // 10x15. :P (or 24x923!)
  1050. display_starfield_space_ace(door, rng);
  1051. // for testing inactivity timeout
  1052. // door.inactivity = 10;
  1053. door::Panel timeout = make_timeout(mx, my);
  1054. door::Menu m = make_main_menu();
  1055. door::Panel about = make_about(); // 8 lines
  1056. // center the about box
  1057. about.set((mx - 60) / 2, (my - 9) / 2);
  1058. int r = 0;
  1059. while ((r >= 0) and (r != 6)) {
  1060. // starfield + menu ?
  1061. display_starfield(door, rng);
  1062. r = m.choose(door);
  1063. // need to reset the colors. (whoops!)
  1064. door << door::reset << door::cls; // door::nl;
  1065. // OK! The screen is blank at this point!
  1066. switch (r) {
  1067. case 1: // play game
  1068. r = play_cards(door, spacedb, rng);
  1069. break;
  1070. case 2: // view scores
  1071. door << "Show scores goes here!" << door::nl;
  1072. r = press_a_key(door);
  1073. break;
  1074. case 3: // configure
  1075. r = configure(door, spacedb);
  1076. // r = press_a_key(door);
  1077. break;
  1078. case 4: // help
  1079. door << "Help! Need some help here..." << door::nl;
  1080. r = press_a_key(door);
  1081. break;
  1082. case 5: // about
  1083. display_starfield(door, rng);
  1084. door << about << door::nl;
  1085. r = press_a_key(door);
  1086. break;
  1087. case 6: // quit
  1088. break;
  1089. }
  1090. }
  1091. if (r < 0) {
  1092. TIMEOUT:
  1093. if (r == -1) {
  1094. door.log() << "TIMEOUT" << std::endl;
  1095. door << timeout << door::reset << door::nl << door::nl;
  1096. } else {
  1097. if (r == -3) {
  1098. door.log() << "OUTTA TIME" << std::endl;
  1099. door::Panel notime = make_notime(mx, my);
  1100. door << notime << door::reset << door::nl;
  1101. }
  1102. }
  1103. return 0;
  1104. }
  1105. door << door::nl;
  1106. /*
  1107. // magic time!
  1108. door << door::reset << door::nl << "Press another key...";
  1109. int x;
  1110. for (x = 0; x < 60; ++x) {
  1111. r = door.sleep_key(1);
  1112. if (r == -1) {
  1113. // ok! Expected timeout!
  1114. // PROBLEM: regular "local" terminal loses current attributes
  1115. // when cursor is save / restored.
  1116. door << door::SaveCursor;
  1117. if (about.update(door)) {
  1118. // ok I need to "fix" the cursor position.
  1119. // it has moved.
  1120. }
  1121. door << door::RestoreCursor << door::reset;
  1122. } else {
  1123. if (r < 0)
  1124. goto TIMEOUT;
  1125. if (r >= 0)
  1126. break;
  1127. }
  1128. }
  1129. if (x == 60)
  1130. goto TIMEOUT;
  1131. */
  1132. #ifdef NNY
  1133. // configured by the player.
  1134. door::ANSIColor deck_color;
  1135. // RED, BLUE, GREEN, MAGENTA, CYAN
  1136. std::uniform_int_distribution<int> rand_color(0, 4);
  1137. switch (rand_color(rng)) {
  1138. case 0:
  1139. deck_color = door::ANSIColor(door::COLOR::RED);
  1140. break;
  1141. case 1:
  1142. deck_color = door::ANSIColor(door::COLOR::BLUE);
  1143. break;
  1144. case 2:
  1145. deck_color = door::ANSIColor(door::COLOR::GREEN);
  1146. break;
  1147. case 3:
  1148. deck_color = door::ANSIColor(door::COLOR::MAGENTA);
  1149. break;
  1150. case 4:
  1151. deck_color = door::ANSIColor(door::COLOR::CYAN);
  1152. break;
  1153. default:
  1154. deck_color = door::ANSIColor(door::COLOR::BLUE, door::ATTR::BLINK);
  1155. break;
  1156. }
  1157. int height = 3;
  1158. Deck d(deck_color, height);
  1159. door::Panel *c;
  1160. door << door::reset << door::cls;
  1161. // This displays the cards in the upper left corner.
  1162. // We want them center, and down some.
  1163. int space = 3;
  1164. // int cards_dealt_width = 59; int cards_dealt_height = 9;
  1165. int game_width;
  1166. {
  1167. int cx, cy, level;
  1168. cardgo(27, space, height, cx, cy, level);
  1169. game_width = cx + 5; // card width
  1170. }
  1171. int off_x = (mx - game_width) / 2;
  1172. int off_y = (my - 9) / 2;
  1173. // The idea is to see the cards with <<Something Unique to the card game>>,
  1174. // Year, Month, Day, and game (like 1 of 3).
  1175. // This will make the games the same/fair for everyone.
  1176. std::seed_seq s1{2021, 2, 27, 1};
  1177. cards deck1 = card_shuffle(s1, 1);
  1178. cards state = card_states();
  1179. // I tried setting the cursor before the delay and before displaying the
  1180. // card. It is very hard to see / just about useless. Not worth the effort.
  1181. for (int x = 0; x < 28; x++) {
  1182. int cx, cy, level;
  1183. cardgo(x, space, height, cx, cy, level);
  1184. // This is hardly visible.
  1185. // door << door::Goto(cx + off_x - 1, cy + off_y + 1);
  1186. std::this_thread::sleep_for(std::chrono::milliseconds(75));
  1187. c = d.back(level);
  1188. c->set(cx + off_x, cy + off_y);
  1189. door << *c;
  1190. }
  1191. /*
  1192. std::this_thread::sleep_for(
  1193. std::chrono::seconds(1)); // 3 secs seemed too long!
  1194. */
  1195. for (int x = 18; x < 28; x++) {
  1196. int cx, cy, level;
  1197. // usleep(1000 * 20);
  1198. state.at(x) = 1;
  1199. cardgo(x, space, height, cx, cy, level);
  1200. // door << door::Goto(cx + off_x - 1, cy + off_y + 1);
  1201. std::this_thread::sleep_for(std::chrono::milliseconds(200));
  1202. c = d.card(deck1.at(x));
  1203. c->set(cx + off_x, cy + off_y);
  1204. door << *c;
  1205. }
  1206. door << door::reset;
  1207. door << door::nl << door::nl;
  1208. r = door.sleep_key(door.inactivity);
  1209. if (r < 0)
  1210. goto TIMEOUT;
  1211. #endif
  1212. /*
  1213. door::Panel *p = d.back(2);
  1214. p->set(10, 10);
  1215. door << *p;
  1216. door::Panel *d8 = d.card(8);
  1217. d8->set(20, 8);
  1218. door << *d8;
  1219. r = door.sleep_key(door.inactivity);
  1220. if (r < 0)
  1221. goto TIMEOUT;
  1222. */
  1223. // door << door::reset << door::cls;
  1224. display_starfield(door, rng);
  1225. door << m << door::reset << door::nl;
  1226. // Normal DOOR exit goes here...
  1227. door << door::nl << "Returning you to the BBS, please wait..." << door::nl;
  1228. get_logger = nullptr;
  1229. return 0;
  1230. }