play.cpp 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285
  1. #include "play.h"
  2. #include "db.h"
  3. #include "deck.h"
  4. #include "version.h"
  5. #include <iomanip> // put_time
  6. #include <sstream>
  7. // configuration settings access
  8. #include "yaml-cpp/yaml.h"
  9. extern YAML::Node config;
  10. #define CHEATER "CHEAT_YOUR_ASS_OFF"
  11. static std::function<std::ofstream &(void)> get_logger;
  12. static int press_a_key(door::Door &door) {
  13. door << door::reset << "Press a key to continue...";
  14. int r = door.sleep_key(door.inactivity);
  15. door << door::nl;
  16. return r;
  17. }
  18. /*
  19. In the future, this will probably check to see if they can play today or not, as
  20. well as displaying a calendar to show what days are available to be played.
  21. For now, it will play today.
  22. */
  23. PlayCards::PlayCards(door::Door &d, DBData &dbd, std::mt19937 &r)
  24. : door{d}, db{dbd}, rng{r} {
  25. get_logger = [this]() -> ofstream & { return door.log(); };
  26. init_values();
  27. play_day = std::chrono::system_clock::now();
  28. // adjustment
  29. time_t time_play_day = std::chrono::system_clock::to_time_t(play_day);
  30. if (get_logger) {
  31. get_logger() << "before: "
  32. << std::put_time(std::localtime(&time_play_day), "%F %R")
  33. << std::endl;
  34. };
  35. standard_date(time_play_day);
  36. play_day = std::chrono::system_clock::from_time_t(time_play_day);
  37. if (get_logger) {
  38. get_logger() << "after: "
  39. << std::put_time(std::localtime(&time_play_day), "%F %R")
  40. << std::endl;
  41. };
  42. spaceAceTriPeaks = make_tripeaks();
  43. score_panel = make_score_panel();
  44. streak_panel = make_streak_panel();
  45. left_panel = make_left_panel();
  46. cmd_panel = make_command_panel();
  47. /*
  48. int mx = door.width;
  49. int my = door.height;
  50. */
  51. }
  52. PlayCards::~PlayCards() { get_logger = nullptr; }
  53. void PlayCards::init_values(void) {
  54. hand = 1;
  55. if (config["hands_per_day"]) {
  56. total_hands = config["hands_per_day"].as<int>();
  57. } else
  58. total_hands = 3;
  59. play_card = 28;
  60. current_streak = 0;
  61. best_streak = 0;
  62. std::string best;
  63. best = db.getSetting("best_streak", "0");
  64. best_streak = std::stoi(best);
  65. select_card = 23;
  66. score = 0;
  67. }
  68. void PlayCards::bonus(void) {
  69. door << door::ANSIColor(door::COLOR::YELLOW, door::ATTR::BOLD) << "BONUS";
  70. }
  71. int PlayCards::play_cards(void) {
  72. init_values();
  73. std::string currentDefault = db.getSetting("DeckColor", "ALL");
  74. get_logger() << "DeckColor shows as " << currentDefault << std::endl;
  75. deck_color = from_string(currentDefault);
  76. dp = Deck(deck_color);
  77. // Calculate the game size
  78. int game_width;
  79. int game_height = 20;
  80. {
  81. int cx, cy, level;
  82. cardgo(27, cx, cy, level);
  83. game_width = cx + 5;
  84. }
  85. int mx = door.width;
  86. int my = door.height;
  87. off_x = (mx - game_width) / 2;
  88. off_y = (my - game_height) / 2;
  89. // int true_off_y = off_y;
  90. // we can now position things properly centered
  91. int tp_off_x = (mx - spaceAceTriPeaks->getWidth()) / 2;
  92. spaceAceTriPeaks->set(tp_off_x, off_y);
  93. off_y += 3; // adjust for tripeaks panel
  94. next_hand:
  95. play_card = 28;
  96. select_card = 23;
  97. score = 0;
  98. current_streak = 0;
  99. // Use play_day to seed the rng
  100. {
  101. time_t tt = std::chrono::system_clock::to_time_t(play_day);
  102. tm local_tm = *localtime(&tt);
  103. std::seed_seq seq{local_tm.tm_year + 1900, local_tm.tm_mon + 1,
  104. local_tm.tm_mday, hand};
  105. deck = card_shuffle(seq, 1);
  106. state = card_states();
  107. }
  108. /*
  109. door::Panel score_panel = make_score_panel();
  110. door::Panel streak_panel = make_streak_panel();
  111. door::Panel left_panel = make_left_panel();
  112. door::Panel cmd_panel = make_command_panel();
  113. */
  114. {
  115. int off_yp = off_y + 11;
  116. int cxp, cyp, levelp;
  117. int left_panel_x, right_panel_x;
  118. // find position of card, to position the panels
  119. cardgo(18, cxp, cyp, levelp);
  120. left_panel_x = cxp;
  121. cardgo(15, cxp, cyp, levelp);
  122. right_panel_x = cxp;
  123. score_panel->set(left_panel_x + off_x, off_yp);
  124. streak_panel->set(right_panel_x + off_x, off_yp);
  125. cmd_panel->set(left_panel_x + off_x, off_yp + 5);
  126. }
  127. bool dealing = true;
  128. int r = 0;
  129. redraw(dealing);
  130. dealing = false;
  131. left_panel->update(door);
  132. door << door::reset;
  133. door::Panel *c;
  134. bool in_game = true;
  135. bool save_streak = false;
  136. while (in_game) {
  137. // time might have updated, so update score panel too.
  138. score_panel->update(door);
  139. // do the save here -- try to hide the database lag
  140. if (save_streak) {
  141. save_streak = false;
  142. std::string best = std::to_string(best_streak);
  143. db.setSetting("best_streak", best);
  144. }
  145. r = door.sleep_key(door.inactivity);
  146. if (r > 0) {
  147. // not a timeout or expire.
  148. if (r < 0x1000) // not a function key
  149. r = std::toupper(r);
  150. switch (r) {
  151. case '\x0d':
  152. if (play_card < 51) {
  153. play_card++;
  154. current_streak = 0;
  155. streak_panel->update(door);
  156. // update the cards left_panel
  157. left_panel->update(door);
  158. // Ok, deal next card from the pile.
  159. int cx, cy, level;
  160. if (play_card == 51) {
  161. cardgo(29, cx, cy, level);
  162. level = 0; // out of cards
  163. c = dp.back(level);
  164. c->set(cx + off_x, cy + off_y);
  165. door << *c;
  166. }
  167. cardgo(28, cx, cy, level);
  168. c = dp.card(deck.at(play_card));
  169. c->set(cx + off_x, cy + off_y);
  170. door << *c;
  171. }
  172. break;
  173. case 'R':
  174. redraw(false);
  175. break;
  176. case 'Q':
  177. // possibly prompt here for [N]ext hand or [Q]uit ?
  178. in_game = false;
  179. break;
  180. case ' ':
  181. case '5':
  182. // can we play this card?
  183. /*
  184. get_logger() << "can_play( " << select_card << ":"
  185. << deck1.at(select_card) << "/"
  186. << d.is_rank(deck1.at(select_card)) << " , "
  187. << play_card << "/" <<
  188. d.is_rank(deck1.at(play_card))
  189. << ") = "
  190. << d.can_play(deck1.at(select_card),
  191. deck1.at(play_card))
  192. << std::endl;
  193. */
  194. if (dp.can_play(deck.at(select_card), deck.at(play_card)) or
  195. config[CHEATER]) {
  196. // if (true) {
  197. // yes we can.
  198. ++current_streak;
  199. if (current_streak > best_streak) {
  200. best_streak = current_streak;
  201. if (!config[CHEATER]) {
  202. save_streak = true;
  203. }
  204. }
  205. streak_panel->update(door);
  206. score += 10;
  207. if (current_streak > 1)
  208. score += current_streak * 5;
  209. score_panel->update(door);
  210. /*
  211. if (get_logger)
  212. get_logger() << "score_panel update : " << score << std::endl;
  213. */
  214. // play card!
  215. state.at(select_card) = 2;
  216. {
  217. // swap the select card with play_card
  218. int temp = deck.at(select_card);
  219. deck.at(select_card) = deck.at(play_card);
  220. deck.at(play_card) = temp;
  221. // select_card is -- invalidated here! find "new" card.
  222. int cx, cy, level;
  223. // erase/clear select_card
  224. std::vector<int> check = dp.unblocks(select_card);
  225. bool left = false, right = false;
  226. for (const int c : check) {
  227. std::pair<int, int> blockers = dp.blocks[c];
  228. if (blockers.first == select_card)
  229. right = true;
  230. if (blockers.second == select_card)
  231. left = true;
  232. }
  233. dp.remove_card(door, select_card, off_x, off_y, left, right);
  234. /* // old way of doing this that leaves holes.
  235. cardgo(select_card, cx, cy, level);
  236. c = d.back(0);
  237. c->set(cx + off_x, cy + off_y);
  238. door << *c;
  239. */
  240. // redraw play card #28. (Which is the "old" select_card)
  241. cardgo(28, cx, cy, level);
  242. c = dp.card(deck.at(play_card));
  243. c->set(cx + off_x, cy + off_y);
  244. door << *c;
  245. // Did we unhide a card here?
  246. // std::vector<int> check = d.unblocks(select_card);
  247. /*
  248. get_logger() << "select_card = " << select_card
  249. << " unblocks: " << check.size() << std::endl;
  250. */
  251. int new_card_shown = -1;
  252. if (!check.empty()) {
  253. for (const int to_check : check) {
  254. std::pair<int, int> blockers = dp.blocks[to_check];
  255. /*
  256. get_logger()
  257. << "Check: " << to_check << " " << blockers.first << ","
  258. << blockers.second << " " << state.at(blockers.first)
  259. << "," << state.at(blockers.second) << std::endl;
  260. */
  261. if ((state.at(blockers.first) == 2) and
  262. (state.at(blockers.second) == 2)) {
  263. // WOOT! Card uncovered.
  264. /*
  265. get_logger() << "showing: " << to_check << std::endl;
  266. */
  267. state.at(to_check) = 1;
  268. cardgo(to_check, cx, cy, level);
  269. c = dp.card(deck.at(to_check));
  270. c->set(cx + off_x, cy + off_y);
  271. door << *c;
  272. new_card_shown = to_check;
  273. }
  274. }
  275. } else {
  276. // top card cleared
  277. // get_logger() << "top card cleared?" << std::endl;
  278. // display something at select_card position
  279. int cx, cy, level;
  280. cardgo(select_card, cx, cy, level);
  281. door << door::Goto(cx + off_x, cy + off_y);
  282. bonus();
  283. score += 100;
  284. state.at(select_card) = 3; // handle this in the "redraw"
  285. score_panel->update(door);
  286. }
  287. // Find new "number" for select_card to be.
  288. if (new_card_shown != -1) {
  289. select_card = new_card_shown;
  290. } else {
  291. // select_card++;
  292. int new_select = find_next_closest(state, select_card);
  293. if (new_select != -1) {
  294. select_card = new_select;
  295. } else {
  296. get_logger() << "Winner!" << std::endl;
  297. // bonus for cards left
  298. int bonus = 15 * (51 - play_card);
  299. score += 15 * (51 - play_card);
  300. score_panel->update(door);
  301. door << " BONUS: " << bonus << door::nl;
  302. get_logger() << "SCORE: " << score << std::endl;
  303. // if (!config[CHEATER]) {
  304. time_t right_now = std::chrono::system_clock::to_time_t(
  305. std::chrono::system_clock::now());
  306. db.save_score(right_now,
  307. std::chrono::system_clock::to_time_t(play_day),
  308. hand, score);
  309. //}
  310. press_a_key(door);
  311. if (hand < total_hands) {
  312. hand++;
  313. // current_streak = 0;
  314. door << door::reset << door::cls;
  315. goto next_hand;
  316. }
  317. r = 'Q';
  318. in_game = false;
  319. }
  320. }
  321. // update the select_card marker!
  322. cardgo(select_card, cx, cy, level);
  323. c = dp.marker(1);
  324. c->set(cx + off_x + 2, cy + off_y + 2);
  325. door << *c;
  326. }
  327. }
  328. break;
  329. case XKEY_LEFT_ARROW:
  330. case '4': {
  331. int new_select = find_next(true, state, select_card);
  332. /*
  333. int new_active = active_card - 1;
  334. while (new_active >= 0) {
  335. if (state.at(new_active) == 1)
  336. break;
  337. --new_active;
  338. }*/
  339. if (new_select >= 0) {
  340. int cx, cy, level;
  341. cardgo(select_card, cx, cy, level);
  342. c = dp.marker(0);
  343. c->set(cx + off_x + 2, cy + off_y + 2);
  344. door << *c;
  345. select_card = new_select;
  346. cardgo(select_card, cx, cy, level);
  347. c = dp.marker(1);
  348. c->set(cx + off_x + 2, cy + off_y + 2);
  349. door << *c;
  350. }
  351. } break;
  352. case XKEY_RIGHT_ARROW:
  353. case '6': {
  354. int new_select = find_next(false, state, select_card);
  355. /*
  356. int new_active = active_card + 1;
  357. while (new_active < 28) {
  358. if (state.at(new_active) == 1)
  359. break;
  360. ++new_active;
  361. }
  362. */
  363. if (new_select >= 0) { //(new_active < 28) {
  364. int cx, cy, level;
  365. cardgo(select_card, cx, cy, level);
  366. c = dp.marker(0);
  367. c->set(cx + off_x + 2, cy + off_y + 2);
  368. door << *c;
  369. select_card = new_select;
  370. cardgo(select_card, cx, cy, level);
  371. c = dp.marker(1);
  372. c->set(cx + off_x + 2, cy + off_y + 2);
  373. door << *c;
  374. }
  375. }
  376. break;
  377. }
  378. } else
  379. in_game = false;
  380. }
  381. if (r == 'Q') {
  382. // continue with hand, or quit?
  383. // continue -- eat r & redraw.
  384. // quit, save score and exit (unless score is zero).
  385. }
  386. return r;
  387. }
  388. void PlayCards::redraw(bool dealing) {
  389. door::Panel *c;
  390. door << door::reset << door::cls;
  391. door << *spaceAceTriPeaks;
  392. {
  393. // step 1:
  394. // draw the deck "source"
  395. int cx, cy, level;
  396. cardgo(29, cx, cy, level);
  397. if (play_card == 51)
  398. level = 0; // out of cards!
  399. c = dp.back(level);
  400. c->set(cx + off_x, cy + off_y);
  401. // p3 is heigh below
  402. left_panel->set(cx + off_x, cy + off_y + height);
  403. // how do I update these? (hand >1)
  404. score_panel->update();
  405. left_panel->update();
  406. streak_panel->update();
  407. cmd_panel->update();
  408. door << *score_panel << *left_panel << *streak_panel << *cmd_panel;
  409. door << *c;
  410. if (dealing)
  411. std::this_thread::sleep_for(std::chrono::seconds(1));
  412. }
  413. for (int x = 0; x < (dealing ? 28 : 29); x++) {
  414. int cx, cy, level;
  415. cardgo(x, cx, cy, level);
  416. // This is hardly visible.
  417. // door << door::Goto(cx + off_x - 1, cy + off_y + 1);
  418. if (dealing)
  419. std::this_thread::sleep_for(std::chrono::milliseconds(75));
  420. if (dealing) {
  421. c = dp.back(level);
  422. c->set(cx + off_x, cy + off_y);
  423. door << *c;
  424. } else {
  425. // redrawing -- draw the cards with their correct "state"
  426. int s = state.at(x);
  427. switch (s) {
  428. case 0:
  429. c = dp.back(level);
  430. c->set(cx + off_x, cy + off_y);
  431. door << *c;
  432. break;
  433. case 1:
  434. // cardgo(x, space, height, cx, cy, level);
  435. if (x == 28)
  436. c = dp.card(deck.at(play_card));
  437. else
  438. c = dp.card(deck.at(x));
  439. c->set(cx + off_x, cy + off_y);
  440. door << *c;
  441. break;
  442. case 2:
  443. // no card to draw. :)
  444. break;
  445. case 3:
  446. // peak cleared, draw bonus
  447. door << door::Goto(cx + off_x, cy + off_y);
  448. bonus();
  449. break;
  450. }
  451. }
  452. }
  453. if (dealing)
  454. for (int x = 18; x < 29; x++) {
  455. int cx, cy, level;
  456. state.at(x) = 1;
  457. cardgo(x, cx, cy, level);
  458. // door << door::Goto(cx + off_x - 1, cy + off_y + 1);
  459. std::this_thread::sleep_for(std::chrono::milliseconds(200));
  460. c = dp.card(deck.at(x));
  461. c->set(cx + off_x, cy + off_y);
  462. door << *c;
  463. }
  464. {
  465. int cx, cy, level;
  466. cardgo(select_card, cx, cy, level);
  467. c = dp.marker(1);
  468. c->set(cx + off_x + 2, cy + off_y + 2);
  469. door << *c;
  470. }
  471. }
  472. door::renderFunction PlayCards::statusValue(door::ANSIColor status,
  473. door::ANSIColor value) {
  474. door::renderFunction rf = [status,
  475. value](const std::string &txt) -> door::Render {
  476. door::Render r(txt);
  477. door::ColorOutput co;
  478. co.pos = 0;
  479. co.len = 0;
  480. co.c = status;
  481. size_t pos = txt.find(':');
  482. if (pos == std::string::npos) {
  483. // failed to find :, render digits/numbers in value color
  484. int tpos = 0;
  485. for (char const &c : txt) {
  486. if (std::isdigit(c)) {
  487. if (co.c != value) {
  488. r.outputs.push_back(co);
  489. co.reset();
  490. co.pos = tpos;
  491. co.c = value;
  492. }
  493. } else {
  494. if (co.c != status) {
  495. r.outputs.push_back(co);
  496. co.reset();
  497. co.pos = tpos;
  498. co.c = status;
  499. }
  500. }
  501. co.len++;
  502. tpos++;
  503. }
  504. if (co.len != 0)
  505. r.outputs.push_back(co);
  506. } else {
  507. pos++; // Have : in status color
  508. co.len = pos;
  509. r.outputs.push_back(co);
  510. co.reset();
  511. co.pos = pos;
  512. co.c = value;
  513. co.len = txt.length() - pos;
  514. r.outputs.push_back(co);
  515. }
  516. return r;
  517. };
  518. return rf;
  519. }
  520. std::unique_ptr<door::Panel> PlayCards::make_score_panel() {
  521. const int W = 25;
  522. std::unique_ptr<door::Panel> p = std::make_unique<door::Panel>(W);
  523. p->setStyle(door::BorderStyle::NONE);
  524. door::ANSIColor statusColor(door::COLOR::WHITE, door::COLOR::BLUE,
  525. door::ATTR::BOLD);
  526. door::ANSIColor valueColor(door::COLOR::YELLOW, door::COLOR::BLUE,
  527. door::ATTR::BOLD);
  528. door::renderFunction svRender = statusValue(statusColor, valueColor);
  529. // or use renderStatus as defined above.
  530. // We'll stick with these for now.
  531. {
  532. std::string userString = "Name: ";
  533. userString += door.username;
  534. door::Line username(userString, W);
  535. username.setRender(svRender);
  536. p->addLine(std::make_unique<door::Line>(username));
  537. }
  538. {
  539. door::updateFunction scoreUpdate = [this](void) -> std::string {
  540. std::string text = "Score: ";
  541. text.append(std::to_string(score));
  542. return text;
  543. };
  544. std::string scoreString = scoreUpdate();
  545. door::Line scoreline(scoreString, W);
  546. scoreline.setRender(svRender);
  547. scoreline.setUpdater(scoreUpdate);
  548. p->addLine(std::make_unique<door::Line>(scoreline));
  549. }
  550. {
  551. door::updateFunction timeUpdate = [this](void) -> std::string {
  552. std::stringstream ss;
  553. std::string text;
  554. ss << "Time used: " << setw(3) << door.time_used << " / " << setw(3)
  555. << door.time_left;
  556. text = ss.str();
  557. return text;
  558. };
  559. std::string timeString = timeUpdate();
  560. door::Line time(timeString, W);
  561. time.setRender(svRender);
  562. time.setUpdater(timeUpdate);
  563. p->addLine(std::make_unique<door::Line>(time));
  564. }
  565. {
  566. door::updateFunction handUpdate = [this](void) -> std::string {
  567. std::string text = "Playing Hand ";
  568. text.append(std::to_string(hand));
  569. text.append(" of ");
  570. text.append(std::to_string(total_hands));
  571. return text;
  572. };
  573. std::string handString = handUpdate();
  574. door::Line hands(handString, W);
  575. hands.setRender(svRender);
  576. hands.setUpdater(handUpdate);
  577. p->addLine(std::make_unique<door::Line>(hands));
  578. }
  579. return p;
  580. }
  581. std::unique_ptr<door::Panel> PlayCards::make_streak_panel(void) {
  582. const int W = 20;
  583. std::unique_ptr<door::Panel> p = std::make_unique<door::Panel>(W);
  584. p->setStyle(door::BorderStyle::NONE);
  585. door::ANSIColor statusColor(door::COLOR::WHITE, door::COLOR::BLUE,
  586. door::ATTR::BOLD);
  587. door::ANSIColor valueColor(door::COLOR::YELLOW, door::COLOR::BLUE,
  588. door::ATTR::BOLD);
  589. door::renderFunction svRender = statusValue(statusColor, valueColor);
  590. {
  591. std::string text = "Playing: ";
  592. auto in_time_t = std::chrono::system_clock::to_time_t(play_day);
  593. std::stringstream ss;
  594. if (config["date_format"]) {
  595. std::string fmt = config["date_format"].as<std::string>();
  596. ss << std::put_time(std::localtime(&in_time_t), fmt.c_str());
  597. } else
  598. ss << std::put_time(std::localtime(&in_time_t), "%B %d");
  599. text.append(ss.str());
  600. door::Line current(text, W);
  601. current.setRender(svRender);
  602. p->addLine(std::make_unique<door::Line>(current));
  603. }
  604. {
  605. door::updateFunction currentUpdate = [this](void) -> std::string {
  606. std::string text = "Current Streak: ";
  607. text.append(std::to_string(current_streak));
  608. return text;
  609. };
  610. std::string currentString = currentUpdate();
  611. door::Line current(currentString, W);
  612. current.setRender(svRender);
  613. current.setUpdater(currentUpdate);
  614. p->addLine(std::make_unique<door::Line>(current));
  615. }
  616. {
  617. door::updateFunction currentUpdate = [this](void) -> std::string {
  618. std::string text = "Longest Streak: ";
  619. text.append(std::to_string(best_streak));
  620. return text;
  621. };
  622. std::string currentString = currentUpdate();
  623. door::Line current(currentString, W);
  624. current.setRender(svRender);
  625. current.setUpdater(currentUpdate);
  626. p->addLine(std::make_unique<door::Line>(current));
  627. }
  628. return p;
  629. }
  630. std::unique_ptr<door::Panel> PlayCards::make_left_panel(void) {
  631. const int W = 13;
  632. std::unique_ptr<door::Panel> p = std::make_unique<door::Panel>(W);
  633. p->setStyle(door::BorderStyle::NONE);
  634. door::ANSIColor statusColor(door::COLOR::WHITE, door::COLOR::BLUE,
  635. door::ATTR::BOLD);
  636. door::ANSIColor valueColor(door::COLOR::YELLOW, door::COLOR::BLUE,
  637. door::ATTR::BOLD);
  638. door::renderFunction svRender = statusValue(statusColor, valueColor);
  639. {
  640. door::updateFunction cardsleftUpdate = [this](void) -> std::string {
  641. std::string text = "Cards left:";
  642. text.append(std::to_string(51 - play_card));
  643. return text;
  644. };
  645. std::string cardsleftString = "Cards left:--";
  646. door::Line cardsleft(cardsleftString, W);
  647. cardsleft.setRender(svRender);
  648. cardsleft.setUpdater(cardsleftUpdate);
  649. p->addLine(std::make_unique<door::Line>(cardsleft));
  650. }
  651. return p;
  652. }
  653. door::renderFunction PlayCards::commandLineRender(door::ANSIColor bracket,
  654. door::ANSIColor inner,
  655. door::ANSIColor outer) {
  656. door::renderFunction rf = [bracket, inner,
  657. outer](const std::string &txt) -> door::Render {
  658. door::Render r(txt);
  659. door::ColorOutput co;
  660. co.pos = 0;
  661. co.len = 0;
  662. co.c = outer;
  663. bool inOuter = true;
  664. int tpos = 0;
  665. for (char const &c : txt) {
  666. if (inOuter) {
  667. // we're in the outer text
  668. if (co.c != outer) {
  669. if (co.len != 0) {
  670. r.outputs.push_back(co);
  671. co.reset();
  672. co.pos = tpos;
  673. }
  674. co.c = outer;
  675. }
  676. // check for [
  677. if (c == '[') {
  678. if (co.len != 0) {
  679. r.outputs.push_back(co);
  680. co.reset();
  681. co.pos = tpos;
  682. }
  683. inOuter = false;
  684. co.c = bracket;
  685. }
  686. } else {
  687. // We're not in the outer.
  688. if (co.c != inner) {
  689. if (co.len != 0) {
  690. r.outputs.push_back(co);
  691. co.reset();
  692. co.pos = tpos;
  693. }
  694. co.c = inner;
  695. }
  696. if (c == ']') {
  697. if (co.len != 0) {
  698. r.outputs.push_back(co);
  699. co.reset();
  700. co.pos = tpos;
  701. }
  702. inOuter = true;
  703. co.c = bracket;
  704. }
  705. }
  706. co.len++;
  707. tpos++;
  708. }
  709. if (co.len != 0)
  710. r.outputs.push_back(co);
  711. return r;
  712. };
  713. return rf;
  714. }
  715. std::unique_ptr<door::Panel> PlayCards::make_command_panel(void) {
  716. const int W = 76;
  717. std::unique_ptr<door::Panel> p = make_unique<door::Panel>(W);
  718. p->setStyle(door::BorderStyle::NONE);
  719. std::string commands;
  720. if (door::unicode) {
  721. commands = "[4/\u25c4] Left [6/\u25ba] Right [Space] Play Card [Enter] "
  722. "Draw [Q]uit "
  723. "[R]edraw [H]elp";
  724. } else {
  725. commands =
  726. "[4/\x11] Left [6/\x10] Right [Space] Play Card [Enter] Draw [Q]uit "
  727. "[R]edraw [H]elp";
  728. }
  729. door::ANSIColor bracketColor(door::COLOR::YELLOW, door::COLOR::BLUE,
  730. door::ATTR::BOLD);
  731. door::ANSIColor innerColor(door::COLOR::CYAN, door::COLOR::BLUE,
  732. door::ATTR::BOLD);
  733. door::ANSIColor outerColor(door::COLOR::GREEN, door::COLOR::BLUE,
  734. door::ATTR::BOLD);
  735. door::renderFunction cmdRender =
  736. commandLineRender(bracketColor, innerColor, outerColor);
  737. door::Line cmd(commands, W);
  738. cmd.setRender(cmdRender);
  739. p->addLine(std::make_unique<door::Line>(cmd));
  740. return p;
  741. }
  742. std::unique_ptr<door::Panel> PlayCards::make_tripeaks(void) {
  743. std::string tripeaksText(" " SPACEACE
  744. " - Tri-Peaks Solitaire v" SPACEACE_VERSION " ");
  745. std::unique_ptr<door::Panel> spaceAceTriPeaks =
  746. std::make_unique<door::Panel>(tripeaksText.size());
  747. spaceAceTriPeaks->setStyle(door::BorderStyle::SINGLE);
  748. spaceAceTriPeaks->setColor(
  749. door::ANSIColor(door::COLOR::CYAN, door::COLOR::BLACK));
  750. spaceAceTriPeaks->addLine(
  751. std::make_unique<door::Line>(tripeaksText, tripeaksText.size()));
  752. return spaceAceTriPeaks;
  753. }
  754. #ifdef OLD_WAY
  755. int play_cards(door::Door &door, DBData &db, std::mt19937 &rng) {
  756. int mx = door.width;
  757. int my = door.height;
  758. // init these values:
  759. card_number = 28;
  760. active_card = 23;
  761. hand = 1;
  762. score = 0;
  763. play_day = std::chrono::system_clock::now();
  764. // cards color --
  765. // configured by the player.
  766. door::ANSIColor deck_color;
  767. // std::string key("DeckColor");
  768. const char *key = "DeckColor";
  769. std::string currentDefault = db.getSetting(key, "ALL");
  770. door.log() << key << " shows as " << currentDefault << std::endl;
  771. deck_color = from_string(currentDefault);
  772. const int height = 3;
  773. Deck d(deck_color); // , height);
  774. door::Panel *c;
  775. // This displays the cards in the upper left corner.
  776. // We want them center, and down some.
  777. // const int space = 3;
  778. // int cards_dealt_width = 59; int cards_dealt_height = 9;
  779. int game_width;
  780. int game_height = 20; // 13; // 9;
  781. {
  782. int cx, cy, level;
  783. cardgo(27, cx, cy, level);
  784. game_width = cx + 5; // card width
  785. }
  786. int off_x = (mx - game_width) / 2;
  787. int off_y = (my - game_height) / 2;
  788. int true_off_y = off_y;
  789. // The idea is to see the cards with <<Something Unique to the card game>>,
  790. // Year, Month, Day, and game (like 1 of 3).
  791. // This will make the games the same/fair for everyone.
  792. next_hand:
  793. card_number = 28;
  794. active_card = 23;
  795. score = 0;
  796. off_y = true_off_y;
  797. door::Panel spaceAceTriPeaks = make_tripeaks();
  798. int tp_off_x = (mx - spaceAceTriPeaks.getWidth()) / 2;
  799. spaceAceTriPeaks.set(tp_off_x, off_y);
  800. off_y += 3;
  801. // figure out what date we're playing / what game we're playing
  802. time_t tt = std::chrono::system_clock::to_time_t(play_day);
  803. tm local_tm = *localtime(&tt);
  804. /*
  805. std::cout << utc_tm.tm_year + 1900 << '-';
  806. std::cout << utc_tm.tm_mon + 1 << '-';
  807. std::cout << utc_tm.tm_mday << ' ';
  808. std::cout << utc_tm.tm_hour << ':';
  809. std::cout << utc_tm.tm_min << ':';
  810. std::cout << utc_tm.tm_sec << '\n';
  811. */
  812. std::seed_seq s1{local_tm.tm_year + 1900, local_tm.tm_mon + 1,
  813. local_tm.tm_mday, hand};
  814. cards deck1 = card_shuffle(s1, 1);
  815. cards state = card_states();
  816. door::Panel score_panel = make_score_panel(door);
  817. door::Panel streak_panel = make_streak_panel();
  818. door::Panel left_panel = make_left_panel();
  819. door::Panel cmd_panel = make_command_panel();
  820. {
  821. int off_yp = off_y + 11;
  822. int cxp, cyp, levelp;
  823. int left_panel_x, right_panel_x;
  824. // find position of card, to position the panels
  825. cardgo(18, cxp, cyp, levelp);
  826. left_panel_x = cxp;
  827. cardgo(15, cxp, cyp, levelp);
  828. right_panel_x = cxp;
  829. score_panel.set(left_panel_x + off_x, off_yp);
  830. streak_panel.set(right_panel_x + off_x, off_yp);
  831. cmd_panel.set(left_panel_x + off_x, off_yp + 5);
  832. }
  833. bool dealing = true;
  834. int r = 0;
  835. while ((r >= 0) and (r != 'Q')) {
  836. // REDRAW everything
  837. door << door::reset << door::cls;
  838. door << spaceAceTriPeaks;
  839. {
  840. // step 1:
  841. // draw the deck "source"
  842. int cx, cy, level;
  843. cardgo(29, cx, cy, level);
  844. if (card_number == 51)
  845. level = 0; // out of cards!
  846. c = d.back(level);
  847. c->set(cx + off_x, cy + off_y);
  848. // p3 is heigh below
  849. left_panel.set(cx + off_x, cy + off_y + height);
  850. door << score_panel << left_panel << streak_panel << cmd_panel;
  851. door << *c;
  852. if (dealing)
  853. std::this_thread::sleep_for(std::chrono::seconds(1));
  854. }
  855. // I tried setting the cursor before the delay and before displaying the
  856. // card. It is very hard to see / just about useless. Not worth the effort.
  857. for (int x = 0; x < (dealing ? 28 : 29); x++) {
  858. int cx, cy, level;
  859. cardgo(x, cx, cy, level);
  860. // This is hardly visible.
  861. // door << door::Goto(cx + off_x - 1, cy + off_y + 1);
  862. if (dealing)
  863. std::this_thread::sleep_for(std::chrono::milliseconds(75));
  864. if (dealing) {
  865. c = d.back(level);
  866. c->set(cx + off_x, cy + off_y);
  867. door << *c;
  868. } else {
  869. // redrawing -- draw the cards with their correct "state"
  870. int s = state.at(x);
  871. switch (s) {
  872. case 0:
  873. c = d.back(level);
  874. c->set(cx + off_x, cy + off_y);
  875. door << *c;
  876. break;
  877. case 1:
  878. // cardgo(x, space, height, cx, cy, level);
  879. if (x == 28)
  880. c = d.card(deck1.at(card_number));
  881. else
  882. c = d.card(deck1.at(x));
  883. c->set(cx + off_x, cy + off_y);
  884. door << *c;
  885. break;
  886. case 2:
  887. // no card to draw. :)
  888. break;
  889. }
  890. }
  891. }
  892. if (dealing)
  893. for (int x = 18; x < 29; x++) {
  894. int cx, cy, level;
  895. state.at(x) = 1;
  896. cardgo(x, cx, cy, level);
  897. // door << door::Goto(cx + off_x - 1, cy + off_y + 1);
  898. std::this_thread::sleep_for(std::chrono::milliseconds(200));
  899. c = d.card(deck1.at(x));
  900. c->set(cx + off_x, cy + off_y);
  901. door << *c;
  902. }
  903. {
  904. int cx, cy, level;
  905. cardgo(active_card, cx, cy, level);
  906. c = d.marker(1);
  907. c->set(cx + off_x + 2, cy + off_y + 2);
  908. door << *c;
  909. }
  910. dealing = false;
  911. left_panel.update(door);
  912. door << door::reset;
  913. bool now_what = true;
  914. while (now_what) {
  915. // time might have updated, so update score panel too.
  916. score_panel.update(door);
  917. r = door.sleep_key(door.inactivity);
  918. if (r > 0) {
  919. // They didn't timeout/expire. They didn't press a function key.
  920. if (r < 0x1000)
  921. r = std::toupper(r);
  922. switch (r) {
  923. case '\x0d':
  924. if (card_number < 51) {
  925. card_number++;
  926. current_streak = 0;
  927. streak_panel.update(door);
  928. // Ok, deal next card from the pile.
  929. int cx, cy, level;
  930. if (card_number == 51) {
  931. cardgo(29, cx, cy, level);
  932. level = 0; // out of cards
  933. c = d.back(level);
  934. c->set(cx + off_x, cy + off_y);
  935. door << *c;
  936. }
  937. cardgo(28, cx, cy, level);
  938. c = d.card(deck1.at(card_number));
  939. c->set(cx + off_x, cy + off_y);
  940. door << *c;
  941. // update the cards left_panel
  942. left_panel.update(door);
  943. }
  944. break;
  945. case 'R':
  946. now_what = false;
  947. break;
  948. case 'Q':
  949. now_what = false;
  950. break;
  951. case ' ':
  952. case '5':
  953. // can we play this card?
  954. /*
  955. get_logger() << "can_play( " << active_card << ":"
  956. << deck1.at(active_card) << "/"
  957. << d.is_rank(deck1.at(active_card)) << " , "
  958. << card_number << "/" << d.is_rank(deck1.at(card_number))
  959. << ") = "
  960. << d.can_play(deck1.at(active_card),
  961. deck1.at(card_number))
  962. << std::endl;
  963. */
  964. if (d.can_play(deck1.at(active_card), deck1.at(card_number))) {
  965. // if (true) {
  966. // yes we can.
  967. ++current_streak;
  968. if (current_streak > best_streak)
  969. best_streak = current_streak;
  970. streak_panel.update(door);
  971. score += 10;
  972. if (current_streak > 2)
  973. score += 5;
  974. // play card!
  975. state.at(active_card) = 2;
  976. {
  977. // swap the active card with card_number (play card)
  978. int temp = deck1.at(active_card);
  979. deck1.at(active_card) = deck1.at(card_number);
  980. deck1.at(card_number) = temp;
  981. // active_card is -- invalidated here! find "new" card.
  982. int cx, cy, level;
  983. // erase/clear active_card
  984. std::vector<int> check = d.unblocks(active_card);
  985. bool left = false, right = false;
  986. for (const int c : check) {
  987. std::pair<int, int> blockers = d.blocks[c];
  988. if (blockers.first == active_card)
  989. right = true;
  990. if (blockers.second == active_card)
  991. left = true;
  992. }
  993. d.remove_card(door, active_card, off_x, off_y, left, right);
  994. /* // old way of doing this that leaves holes.
  995. cardgo(active_card, cx, cy, level);
  996. c = d.back(0);
  997. c->set(cx + off_x, cy + off_y);
  998. door << *c;
  999. */
  1000. // redraw play card #28. (Which is the "old" active_card)
  1001. cardgo(28, cx, cy, level);
  1002. c = d.card(deck1.at(card_number));
  1003. c->set(cx + off_x, cy + off_y);
  1004. door << *c;
  1005. // Did we unhide a card here?
  1006. // std::vector<int> check = d.unblocks(active_card);
  1007. /*
  1008. get_logger() << "active_card = " << active_card
  1009. << " unblocks: " << check.size() << std::endl;
  1010. */
  1011. int new_card_shown = -1;
  1012. if (!check.empty()) {
  1013. for (const int to_check : check) {
  1014. std::pair<int, int> blockers = d.blocks[to_check];
  1015. /*
  1016. get_logger()
  1017. << "Check: " << to_check << " " << blockers.first << ","
  1018. << blockers.second << " " << state.at(blockers.first)
  1019. << "," << state.at(blockers.second) << std::endl;
  1020. */
  1021. if ((state.at(blockers.first) == 2) and
  1022. (state.at(blockers.second) == 2)) {
  1023. // WOOT! Card uncovered.
  1024. /*
  1025. get_logger() << "showing: " << to_check << std::endl;
  1026. */
  1027. state.at(to_check) = 1;
  1028. cardgo(to_check, cx, cy, level);
  1029. c = d.card(deck1.at(to_check));
  1030. c->set(cx + off_x, cy + off_y);
  1031. door << *c;
  1032. new_card_shown = to_check;
  1033. }
  1034. }
  1035. } else {
  1036. // this would be a "top" card. Should set status = 4 and
  1037. // display something here?
  1038. get_logger() << "top card cleared?" << std::endl;
  1039. // display something at active_card position
  1040. int cx, cy, level;
  1041. cardgo(active_card, cx, cy, level);
  1042. door << door::Goto(cx + off_x, cy + off_y) << door::reset
  1043. << "BONUS";
  1044. score += 100;
  1045. state.at(active_card) = 3; // handle this in the "redraw"
  1046. }
  1047. // Find new "number" for active_card to be.
  1048. if (new_card_shown != -1) {
  1049. active_card = new_card_shown;
  1050. } else {
  1051. // active_card++;
  1052. int new_active = find_next_closest(state, active_card);
  1053. if (new_active != -1) {
  1054. active_card = new_active;
  1055. } else {
  1056. get_logger() << "This looks like END OF GAME." << std::endl;
  1057. // bonus for cards left
  1058. press_a_key(door);
  1059. if (hand < total_hands) {
  1060. hand++;
  1061. door << door::reset << door::cls;
  1062. goto next_hand;
  1063. }
  1064. r = 'Q';
  1065. now_what = false;
  1066. }
  1067. }
  1068. // update the active_card marker!
  1069. cardgo(active_card, cx, cy, level);
  1070. c = d.marker(1);
  1071. c->set(cx + off_x + 2, cy + off_y + 2);
  1072. door << *c;
  1073. }
  1074. }
  1075. break;
  1076. case XKEY_LEFT_ARROW:
  1077. case '4': {
  1078. int new_active = find_next(true, state, active_card);
  1079. /*
  1080. int new_active = active_card - 1;
  1081. while (new_active >= 0) {
  1082. if (state.at(new_active) == 1)
  1083. break;
  1084. --new_active;
  1085. }*/
  1086. if (new_active >= 0) {
  1087. int cx, cy, level;
  1088. cardgo(active_card, cx, cy, level);
  1089. c = d.marker(0);
  1090. c->set(cx + off_x + 2, cy + off_y + 2);
  1091. door << *c;
  1092. active_card = new_active;
  1093. cardgo(active_card, cx, cy, level);
  1094. c = d.marker(1);
  1095. c->set(cx + off_x + 2, cy + off_y + 2);
  1096. door << *c;
  1097. }
  1098. } break;
  1099. case XKEY_RIGHT_ARROW:
  1100. case '6': {
  1101. int new_active = find_next(false, state, active_card);
  1102. /*
  1103. int new_active = active_card + 1;
  1104. while (new_active < 28) {
  1105. if (state.at(new_active) == 1)
  1106. break;
  1107. ++new_active;
  1108. }
  1109. */
  1110. if (new_active >= 0) { //(new_active < 28) {
  1111. int cx, cy, level;
  1112. cardgo(active_card, cx, cy, level);
  1113. c = d.marker(0);
  1114. c->set(cx + off_x + 2, cy + off_y + 2);
  1115. door << *c;
  1116. active_card = new_active;
  1117. cardgo(active_card, cx, cy, level);
  1118. c = d.marker(1);
  1119. c->set(cx + off_x + 2, cy + off_y + 2);
  1120. door << *c;
  1121. }
  1122. }
  1123. break;
  1124. }
  1125. } else
  1126. now_what = false;
  1127. }
  1128. }
  1129. if (r == 'Q') {
  1130. // continue with hand or quit?
  1131. if (hand < total_hands) {
  1132. press_a_key(door);
  1133. hand++;
  1134. door << door::reset << door::cls;
  1135. goto next_hand;
  1136. }
  1137. }
  1138. return r;
  1139. }
  1140. #endif