play.cpp 37 KB

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