main.cpp 28 KB

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