play.cpp 36 KB

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