main.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644
  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', "Drop to DOS");
  141. m.addSelection('4', "Chat with BUGZ");
  142. m.addSelection('H', "Help");
  143. m.addSelection('A', "About this game");
  144. m.addSelection('Q', "Quit");
  145. return m;
  146. }
  147. door::renderFunction statusValue(door::ANSIColor status,
  148. door::ANSIColor value) {
  149. door::renderFunction rf = [status,
  150. value](const std::string &txt) -> door::Render {
  151. door::Render r(txt);
  152. door::ColorOutput co;
  153. co.pos = 0;
  154. co.len = 0;
  155. co.c = status;
  156. size_t pos = txt.find(':');
  157. if (pos == std::string::npos) {
  158. // failed to find - use entire string as status color.
  159. co.len = txt.length();
  160. r.outputs.push_back(co);
  161. } else {
  162. pos++; // Have : in status color
  163. co.len = pos;
  164. r.outputs.push_back(co);
  165. co.reset();
  166. co.pos = pos;
  167. co.c = value;
  168. co.len = txt.length() - pos;
  169. r.outputs.push_back(co);
  170. }
  171. return r;
  172. };
  173. return rf;
  174. }
  175. door::renderFunction rStatus = [](const std::string &txt) -> door::Render {
  176. door::Render r(txt);
  177. door::ColorOutput co;
  178. // default colors STATUS: value
  179. door::ANSIColor status(door::COLOR::BLUE, door::ATTR::BOLD);
  180. door::ANSIColor value(door::COLOR::YELLOW, door::ATTR::BOLD);
  181. co.pos = 0;
  182. co.len = 0;
  183. co.c = status;
  184. size_t pos = txt.find(':');
  185. if (pos == std::string::npos) {
  186. // failed to find - use entire string as status color.
  187. co.len = txt.length();
  188. r.outputs.push_back(co);
  189. } else {
  190. pos++; // Have : in status color
  191. co.len = pos;
  192. r.outputs.push_back(co);
  193. co.reset();
  194. co.pos = pos;
  195. co.c = value;
  196. co.len = txt.length() - pos;
  197. r.outputs.push_back(co);
  198. }
  199. return r;
  200. };
  201. std::string return_current_time_and_date() {
  202. auto now = std::chrono::system_clock::now();
  203. auto in_time_t = std::chrono::system_clock::to_time_t(now);
  204. std::stringstream ss;
  205. // ss << std::put_time(std::localtime(&in_time_t), "%Y-%m-%d %X");
  206. ss << std::put_time(std::localtime(&in_time_t), "%Y-%m-%d %r");
  207. return ss.str();
  208. }
  209. door::Panel make_about(void) {
  210. door::Panel about(2, 2, 60);
  211. about.setStyle(door::BorderStyle::DOUBLE_SINGLE);
  212. about.setColor(door::ANSIColor(door::COLOR::YELLOW, door::COLOR::BLUE,
  213. door::ATTR::BOLD));
  214. about.addLine(std::make_unique<door::Line>("About This Door", 60));
  215. /*
  216. door::Line magic("---------------------------------", 60);
  217. magic.setColor(door::ANSIColor(door::COLOR::YELLOW, door::COLOR::BLACK,
  218. door::ATTR::BOLD));
  219. */
  220. about.addLine(std::make_unique<door::Line>(
  221. "---------------------------------", 60,
  222. door::ANSIColor(door::COLOR::CYAN, door::COLOR::BLUE, door::ATTR::BOLD)));
  223. /*
  224. 123456789012345678901234567890123456789012345678901234567890
  225. This door was written by Bugz.
  226. It is written in c++, only supports Linux, and replaces
  227. opendoors.
  228. It's written in c++, and replaces the outdated opendoors
  229. library.
  230. */
  231. about.addLine(
  232. std::make_unique<door::Line>("This door was written by Bugz.", 60));
  233. about.addLine(std::make_unique<door::Line>("", 60));
  234. about.addLine(std::make_unique<door::Line>(
  235. "It is written in c++, only support Linux, and replaces", 60));
  236. about.addLine(std::make_unique<door::Line>("opendoors.", 60));
  237. door::updateFunction updater = [](void) -> std::string {
  238. std::string text = "Currently: ";
  239. text.append(return_current_time_and_date());
  240. return text;
  241. };
  242. std::string current = updater();
  243. door::Line active(current, 60);
  244. active.setUpdater(updater);
  245. active.setRender(renderStatusValue(
  246. door::ANSIColor(door::COLOR::WHITE, door::COLOR::BLUE, door::ATTR::BOLD),
  247. door::ANSIColor(door::COLOR::YELLOW, door::COLOR::BLUE,
  248. door::ATTR::BOLD)));
  249. about.addLine(std::make_unique<door::Line>(active));
  250. /*
  251. about.addLine(std::make_unique<door::Line>(
  252. "Status: blue", 60,
  253. statusValue(door::ANSIColor(door::COLOR::GREEN, door::ATTR::BOLD),
  254. door::ANSIColor(door::COLOR::MAGENTA, door::ATTR::BLINK))));
  255. about.addLine(std::make_unique<door::Line>("Name: BUGZ", 60, rStatus));
  256. about.addLine(std::make_unique<door::Line>(
  257. "Size: 10240", 60,
  258. statusValue(door::ANSIColor(door::COLOR::GREEN, door::COLOR::BLUE,
  259. door::ATTR::BOLD),
  260. door::ANSIColor(door::COLOR::YELLOW, door::COLOR::BLUE,
  261. door::ATTR::BOLD, door::ATTR::BLINK))));
  262. about.addLine(std::make_unique<door::Line>("Bugz is here.", 60, rStatus));
  263. */
  264. return about;
  265. }
  266. void display_starfield(int mx, int my, door::Door &door, std::mt19937 &rng) {
  267. door << door::reset << door::cls;
  268. // display starfield
  269. const char *stars[2];
  270. stars[0] = ".";
  271. if (door::unicode) {
  272. stars[1] = "\u2219"; // "\u00b7";
  273. } else {
  274. stars[1] = "\xf9"; // "\xfa";
  275. };
  276. {
  277. // Make uniform random distribution between 1 and MAX screen size X/Y
  278. std::uniform_int_distribution<int> uni_x(1, mx);
  279. std::uniform_int_distribution<int> uni_y(1, my);
  280. door::ANSIColor white(door::COLOR::WHITE);
  281. door::ANSIColor dark(door::COLOR::BLACK, door::ATTR::BRIGHT);
  282. for (int x = 0; x < (mx * my / 100); x++) {
  283. door::Goto star_at(uni_x(rng), uni_y(rng));
  284. door << star_at;
  285. if (x % 5 < 2)
  286. door << dark;
  287. else
  288. door << white;
  289. if (x % 2 == 0)
  290. door << stars[0];
  291. else
  292. door << stars[1];
  293. }
  294. }
  295. }
  296. void display_space_ace(int mx, int my, door::Door &door) {
  297. // space_ace is 72 chars wide, 6 high
  298. int sa_x = (mx - 72) / 2;
  299. int sa_y = (my - 6) / 2;
  300. // output the SpaceAce logo -- centered!
  301. for (const auto s : space) {
  302. door::Goto sa_at(sa_x, sa_y);
  303. door << sa_at;
  304. if (door::unicode) {
  305. std::string unicode;
  306. door::cp437toUnicode(s, unicode);
  307. door << unicode; // << door::nl;
  308. } else
  309. door << s; // << door::nl;
  310. sa_y++;
  311. }
  312. // pause 5 seconds so they can enjoy our awesome logo
  313. door.sleep_key(5);
  314. }
  315. void display_starfield_space_ace(int mx, int my, door::Door &door,
  316. std::mt19937 &rng) {
  317. display_starfield(mx, my, door, rng);
  318. display_space_ace(mx, my, door);
  319. door << door::reset;
  320. }
  321. int main(int argc, char *argv[]) {
  322. /*
  323. google::InitGoogleLogging(argv[0]);
  324. LOG(INFO) << "Welcome!";
  325. */
  326. door::Door door("space-ace", argc, argv);
  327. // door << door::reset << door::cls << door::nl;
  328. // door::ANSIColor ac(door::COLOR::YELLOW, door::ATTR::BOLD);
  329. // door::ANSIColor mb(door::COLOR::MAGENTA, door::ATTR::BLINK);
  330. // door << mb << "Does this work?" << door::reset << door::nl;
  331. DBData spacedb;
  332. // spacedb.init();
  333. std::string setting = "last_play";
  334. std::string user = door.username;
  335. std::string value;
  336. std::string blank = "<blank>";
  337. value = spacedb.getSetting(user, setting, blank);
  338. door << door::reset << "last_play: " << value << door::nl;
  339. std::this_thread::sleep_for(std::chrono::seconds(2));
  340. value = return_current_time_and_date();
  341. spacedb.setSetting(user, setting, value);
  342. // https://stackoverflow.com/questions/5008804/generating-random-integer-from-a-range
  343. std::random_device rd; // only used once to initialise (seed) engine
  344. std::mt19937 rng(
  345. rd()); // random-number engine used (Mersenne-Twister in this case)
  346. // std::uniform_int_distribution<int> uni(min, max); // guaranteed unbiased
  347. int mx, my; // Max screen width/height
  348. if (door.width == 0) {
  349. // screen detect failed, use sensible defaults
  350. mx = 80;
  351. my = 23;
  352. } else {
  353. mx = door.width;
  354. my = door.height;
  355. }
  356. // We assume here that the width and height are something crazy like 10x15. :P
  357. display_starfield_space_ace(mx, my, door, rng);
  358. // for testing inactivity timeout
  359. // door.inactivity = 10;
  360. door::Panel timeout = make_timeout(mx, my);
  361. door::Menu m = make_main_menu();
  362. int r = m.choose(door);
  363. // need to reset the colors. (whoops!)
  364. door << door::reset << door::nl;
  365. if (r < 0) {
  366. TIMEOUT:
  367. if (r == -1) {
  368. door.log("TIMEOUT");
  369. door << timeout << door::reset << door::nl << door::nl;
  370. } else {
  371. if (r == -3) {
  372. door.log("OUTTA TIME");
  373. door::Panel notime = make_notime(mx, my);
  374. door << notime << door::reset << door::nl;
  375. }
  376. }
  377. return 0;
  378. }
  379. /*
  380. display_starfield(mx, my, door, rng);
  381. // WARNING: After starfield, cursor position is random!
  382. door << door::Goto(1, 9); // door::nl << door::nl; // door::cls;
  383. door << "Your name: "
  384. << door::ANSIColor(door::COLOR::WHITE, door::COLOR::GREEN,
  385. door::ATTR::BOLD);
  386. std::string istring;
  387. istring = door.input_string(25);
  388. door << door::reset << door::nl << "You typed in [" << istring << "]"
  389. << door::nl;
  390. door << "Hello, " << door.username << ", you chose option " << r << "!"
  391. << door::nl;
  392. door << "Press a key...";
  393. r = door.sleep_key(door.inactivity);
  394. if (r < 0)
  395. goto TIMEOUT;
  396. */
  397. door << door::nl;
  398. door::Panel about = make_about();
  399. about.set((mx - 60) / 2, (my - 5) / 2);
  400. door << about;
  401. // magic time!
  402. door << door::reset << door::nl << "Press another key...";
  403. int x;
  404. for (x = 0; x < 60; ++x) {
  405. r = door.sleep_key(1);
  406. if (r == -1) {
  407. // ok! Expected timeout!
  408. // PROBLEM: regular "local" terminal loses current attributes
  409. // when cursor is save / restored.
  410. door << door::SaveCursor;
  411. if (about.update(door)) {
  412. // ok I need to "fix" the cursor position.
  413. // it has moved.
  414. }
  415. door << door::RestoreCursor << door::reset;
  416. } else {
  417. if (r < 0)
  418. goto TIMEOUT;
  419. if (r >= 0)
  420. break;
  421. }
  422. }
  423. if (x == 60)
  424. goto TIMEOUT;
  425. door << door::nl;
  426. door::ANSIColor deck_color;
  427. // RED, BLUE, GREEN, MAGENTA, CYAN
  428. std::uniform_int_distribution<int> rand_color(0, 4);
  429. switch (rand_color(rng)) {
  430. case 0:
  431. deck_color = door::ANSIColor(door::COLOR::RED);
  432. break;
  433. case 1:
  434. deck_color = door::ANSIColor(door::COLOR::BLUE);
  435. break;
  436. case 2:
  437. deck_color = door::ANSIColor(door::COLOR::GREEN);
  438. break;
  439. case 3:
  440. deck_color = door::ANSIColor(door::COLOR::MAGENTA);
  441. break;
  442. case 4:
  443. deck_color = door::ANSIColor(door::COLOR::CYAN);
  444. break;
  445. default:
  446. deck_color = door::ANSIColor(door::COLOR::BLUE, door::ATTR::BLINK);
  447. break;
  448. }
  449. int height = 3;
  450. Deck d(deck_color, height);
  451. door::Panel *c;
  452. door << door::reset << door::cls;
  453. // This displays the cards in the upper left corner.
  454. // We want them center, and down some.
  455. int space = 3;
  456. // int cards_dealt_width = 59; int cards_dealt_height = 9;
  457. int game_width;
  458. {
  459. int cx, cy, level;
  460. cardgo(27, space, height, cx, cy, level);
  461. game_width = cx + 5; // card width
  462. }
  463. int off_x = (mx - game_width) / 2;
  464. int off_y = (my - 9) / 2;
  465. std::seed_seq s1{2021, 2, 27, 1};
  466. cards deck1 = card_shuffle(s1, 1);
  467. cards state = card_states();
  468. // I tried setting the cursor before the delay and before displaying the card.
  469. // It is very hard to see / just about useless. Not worth the effort.
  470. for (int x = 0; x < 28; x++) {
  471. int cx, cy, level;
  472. cardgo(x, space, height, cx, cy, level);
  473. // This is hardly visible.
  474. // door << door::Goto(cx + off_x - 1, cy + off_y + 1);
  475. std::this_thread::sleep_for(std::chrono::milliseconds(75));
  476. c = d.back(level);
  477. c->set(cx + off_x, cy + off_y);
  478. door << *c;
  479. }
  480. /*
  481. std::this_thread::sleep_for(
  482. std::chrono::seconds(1)); // 3 secs seemed too long!
  483. */
  484. for (int x = 18; x < 28; x++) {
  485. int cx, cy, level;
  486. // usleep(1000 * 20);
  487. state.at(x) = 1;
  488. cardgo(x, space, height, cx, cy, level);
  489. // door << door::Goto(cx + off_x - 1, cy + off_y + 1);
  490. std::this_thread::sleep_for(std::chrono::milliseconds(200));
  491. c = d.card(deck1.at(x));
  492. c->set(cx + off_x, cy + off_y);
  493. door << *c;
  494. }
  495. door << door::reset;
  496. door << door::nl << door::nl;
  497. r = door.sleep_key(door.inactivity);
  498. if (r < 0)
  499. goto TIMEOUT;
  500. /*
  501. door::Panel *p = d.back(2);
  502. p->set(10, 10);
  503. door << *p;
  504. door::Panel *d8 = d.card(8);
  505. d8->set(20, 8);
  506. door << *d8;
  507. r = door.sleep_key(door.inactivity);
  508. if (r < 0)
  509. goto TIMEOUT;
  510. */
  511. // door << door::reset << door::cls;
  512. display_starfield(mx, my, door, rng);
  513. door << m << door::reset << door::nl << "This is what the menu looked liked!"
  514. << door::nl;
  515. // Normal DOOR exit goes here...
  516. door << door::nl << "Returning you to the BBS, please wait..." << door::nl;
  517. return 0;
  518. }