play.cpp 38 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283
  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. card_number = 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. active_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. card_number = 28;
  96. active_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 (card_number < 51) {
  153. card_number++;
  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 (card_number == 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(card_number));
  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( " << active_card << ":"
  185. << deck1.at(active_card) << "/"
  186. << d.is_rank(deck1.at(active_card)) << " , "
  187. << card_number << "/" <<
  188. d.is_rank(deck1.at(card_number))
  189. << ") = "
  190. << d.can_play(deck1.at(active_card),
  191. deck1.at(card_number))
  192. << std::endl;
  193. */
  194. if (dp.can_play(deck.at(active_card), deck.at(card_number)) 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(active_card) = 2;
  216. {
  217. // swap the active card with card_number (play card)
  218. int temp = deck.at(active_card);
  219. deck.at(active_card) = deck.at(card_number);
  220. deck.at(card_number) = temp;
  221. // active_card is -- invalidated here! find "new" card.
  222. int cx, cy, level;
  223. // erase/clear active_card
  224. std::vector<int> check = dp.unblocks(active_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 == active_card)
  229. right = true;
  230. if (blockers.second == active_card)
  231. left = true;
  232. }
  233. dp.remove_card(door, active_card, off_x, off_y, left, right);
  234. /* // old way of doing this that leaves holes.
  235. cardgo(active_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" active_card)
  241. cardgo(28, cx, cy, level);
  242. c = dp.card(deck.at(card_number));
  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(active_card);
  247. /*
  248. get_logger() << "active_card = " << active_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 active_card position
  279. int cx, cy, level;
  280. cardgo(active_card, cx, cy, level);
  281. door << door::Goto(cx + off_x, cy + off_y);
  282. bonus();
  283. score += 100;
  284. state.at(active_card) = 3; // handle this in the "redraw"
  285. score_panel->update(door);
  286. }
  287. // Find new "number" for active_card to be.
  288. if (new_card_shown != -1) {
  289. active_card = new_card_shown;
  290. } else {
  291. // active_card++;
  292. int new_active = find_next_closest(state, active_card);
  293. if (new_active != -1) {
  294. active_card = new_active;
  295. } else {
  296. get_logger() << "Winner!" << std::endl;
  297. // bonus for cards left
  298. int bonus = 15 * (51 - card_number);
  299. score += 15 * (51 - card_number);
  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 active_card marker!
  322. cardgo(active_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_active = find_next(true, state, active_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_active >= 0) {
  340. int cx, cy, level;
  341. cardgo(active_card, cx, cy, level);
  342. c = dp.marker(0);
  343. c->set(cx + off_x + 2, cy + off_y + 2);
  344. door << *c;
  345. active_card = new_active;
  346. cardgo(active_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_active = find_next(false, state, active_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_active >= 0) { //(new_active < 28) {
  364. int cx, cy, level;
  365. cardgo(active_card, cx, cy, level);
  366. c = dp.marker(0);
  367. c->set(cx + off_x + 2, cy + off_y + 2);
  368. door << *c;
  369. active_card = new_active;
  370. cardgo(active_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. }
  384. return r;
  385. }
  386. void PlayCards::redraw(bool dealing) {
  387. door::Panel *c;
  388. door << door::reset << door::cls;
  389. door << *spaceAceTriPeaks;
  390. {
  391. // step 1:
  392. // draw the deck "source"
  393. int cx, cy, level;
  394. cardgo(29, cx, cy, level);
  395. if (card_number == 51)
  396. level = 0; // out of cards!
  397. c = dp.back(level);
  398. c->set(cx + off_x, cy + off_y);
  399. // p3 is heigh below
  400. left_panel->set(cx + off_x, cy + off_y + height);
  401. // how do I update these? (hand >1)
  402. score_panel->update();
  403. left_panel->update();
  404. streak_panel->update();
  405. cmd_panel->update();
  406. door << *score_panel << *left_panel << *streak_panel << *cmd_panel;
  407. door << *c;
  408. if (dealing)
  409. std::this_thread::sleep_for(std::chrono::seconds(1));
  410. }
  411. for (int x = 0; x < (dealing ? 28 : 29); x++) {
  412. int cx, cy, level;
  413. cardgo(x, cx, cy, level);
  414. // This is hardly visible.
  415. // door << door::Goto(cx + off_x - 1, cy + off_y + 1);
  416. if (dealing)
  417. std::this_thread::sleep_for(std::chrono::milliseconds(75));
  418. if (dealing) {
  419. c = dp.back(level);
  420. c->set(cx + off_x, cy + off_y);
  421. door << *c;
  422. } else {
  423. // redrawing -- draw the cards with their correct "state"
  424. int s = state.at(x);
  425. switch (s) {
  426. case 0:
  427. c = dp.back(level);
  428. c->set(cx + off_x, cy + off_y);
  429. door << *c;
  430. break;
  431. case 1:
  432. // cardgo(x, space, height, cx, cy, level);
  433. if (x == 28)
  434. c = dp.card(deck.at(card_number));
  435. else
  436. c = dp.card(deck.at(x));
  437. c->set(cx + off_x, cy + off_y);
  438. door << *c;
  439. break;
  440. case 2:
  441. // no card to draw. :)
  442. break;
  443. case 3:
  444. // peak cleared, draw bonus
  445. door << door::Goto(cx + off_x, cy + off_y);
  446. bonus();
  447. break;
  448. }
  449. }
  450. }
  451. if (dealing)
  452. for (int x = 18; x < 29; x++) {
  453. int cx, cy, level;
  454. state.at(x) = 1;
  455. cardgo(x, cx, cy, level);
  456. // door << door::Goto(cx + off_x - 1, cy + off_y + 1);
  457. std::this_thread::sleep_for(std::chrono::milliseconds(200));
  458. c = dp.card(deck.at(x));
  459. c->set(cx + off_x, cy + off_y);
  460. door << *c;
  461. }
  462. {
  463. int cx, cy, level;
  464. cardgo(active_card, cx, cy, level);
  465. c = dp.marker(1);
  466. c->set(cx + off_x + 2, cy + off_y + 2);
  467. door << *c;
  468. }
  469. }
  470. door::renderFunction PlayCards::statusValue(door::ANSIColor status,
  471. door::ANSIColor value) {
  472. door::renderFunction rf = [status,
  473. value](const std::string &txt) -> door::Render {
  474. door::Render r(txt);
  475. door::ColorOutput co;
  476. co.pos = 0;
  477. co.len = 0;
  478. co.c = status;
  479. size_t pos = txt.find(':');
  480. if (pos == std::string::npos) {
  481. // failed to find :, render digits/numbers in value color
  482. int tpos = 0;
  483. for (char const &c : txt) {
  484. if (std::isdigit(c)) {
  485. if (co.c != value) {
  486. r.outputs.push_back(co);
  487. co.reset();
  488. co.pos = tpos;
  489. co.c = value;
  490. }
  491. } else {
  492. if (co.c != status) {
  493. r.outputs.push_back(co);
  494. co.reset();
  495. co.pos = tpos;
  496. co.c = status;
  497. }
  498. }
  499. co.len++;
  500. tpos++;
  501. }
  502. if (co.len != 0)
  503. r.outputs.push_back(co);
  504. } else {
  505. pos++; // Have : in status color
  506. co.len = pos;
  507. r.outputs.push_back(co);
  508. co.reset();
  509. co.pos = pos;
  510. co.c = value;
  511. co.len = txt.length() - pos;
  512. r.outputs.push_back(co);
  513. }
  514. return r;
  515. };
  516. return rf;
  517. }
  518. std::unique_ptr<door::Panel> PlayCards::make_score_panel() {
  519. const int W = 25;
  520. std::unique_ptr<door::Panel> p = std::make_unique<door::Panel>(W);
  521. p->setStyle(door::BorderStyle::NONE);
  522. door::ANSIColor statusColor(door::COLOR::WHITE, door::COLOR::BLUE,
  523. door::ATTR::BOLD);
  524. door::ANSIColor valueColor(door::COLOR::YELLOW, door::COLOR::BLUE,
  525. door::ATTR::BOLD);
  526. door::renderFunction svRender = statusValue(statusColor, valueColor);
  527. // or use renderStatus as defined above.
  528. // We'll stick with these for now.
  529. {
  530. std::string userString = "Name: ";
  531. userString += door.username;
  532. door::Line username(userString, W);
  533. username.setRender(svRender);
  534. p->addLine(std::make_unique<door::Line>(username));
  535. }
  536. {
  537. door::updateFunction scoreUpdate = [this](void) -> std::string {
  538. std::string text = "Score: ";
  539. text.append(std::to_string(score));
  540. return text;
  541. };
  542. std::string scoreString = scoreUpdate();
  543. door::Line scoreline(scoreString, W);
  544. scoreline.setRender(svRender);
  545. scoreline.setUpdater(scoreUpdate);
  546. p->addLine(std::make_unique<door::Line>(scoreline));
  547. }
  548. {
  549. door::updateFunction timeUpdate = [this](void) -> std::string {
  550. std::stringstream ss;
  551. std::string text;
  552. ss << "Time used: " << setw(3) << door.time_used << " / " << setw(3)
  553. << door.time_left;
  554. text = ss.str();
  555. return text;
  556. };
  557. std::string timeString = timeUpdate();
  558. door::Line time(timeString, W);
  559. time.setRender(svRender);
  560. time.setUpdater(timeUpdate);
  561. p->addLine(std::make_unique<door::Line>(time));
  562. }
  563. {
  564. door::updateFunction handUpdate = [this](void) -> std::string {
  565. std::string text = "Playing Hand ";
  566. text.append(std::to_string(hand));
  567. text.append(" of ");
  568. text.append(std::to_string(total_hands));
  569. return text;
  570. };
  571. std::string handString = handUpdate();
  572. door::Line hands(handString, W);
  573. hands.setRender(svRender);
  574. hands.setUpdater(handUpdate);
  575. p->addLine(std::make_unique<door::Line>(hands));
  576. }
  577. return p;
  578. }
  579. std::unique_ptr<door::Panel> PlayCards::make_streak_panel(void) {
  580. const int W = 20;
  581. std::unique_ptr<door::Panel> p = std::make_unique<door::Panel>(W);
  582. p->setStyle(door::BorderStyle::NONE);
  583. door::ANSIColor statusColor(door::COLOR::WHITE, door::COLOR::BLUE,
  584. door::ATTR::BOLD);
  585. door::ANSIColor valueColor(door::COLOR::YELLOW, door::COLOR::BLUE,
  586. door::ATTR::BOLD);
  587. door::renderFunction svRender = statusValue(statusColor, valueColor);
  588. {
  589. std::string text = "Playing: ";
  590. auto in_time_t = std::chrono::system_clock::to_time_t(play_day);
  591. std::stringstream ss;
  592. if (config["date_format"]) {
  593. std::string fmt = config["date_format"].as<std::string>();
  594. ss << std::put_time(std::localtime(&in_time_t), fmt.c_str());
  595. } else
  596. ss << std::put_time(std::localtime(&in_time_t), "%B %d");
  597. text.append(ss.str());
  598. door::Line current(text, W);
  599. current.setRender(svRender);
  600. p->addLine(std::make_unique<door::Line>(current));
  601. }
  602. {
  603. door::updateFunction currentUpdate = [this](void) -> std::string {
  604. std::string text = "Current Streak: ";
  605. text.append(std::to_string(current_streak));
  606. return text;
  607. };
  608. std::string currentString = currentUpdate();
  609. door::Line current(currentString, W);
  610. current.setRender(svRender);
  611. current.setUpdater(currentUpdate);
  612. p->addLine(std::make_unique<door::Line>(current));
  613. }
  614. {
  615. door::updateFunction currentUpdate = [this](void) -> std::string {
  616. std::string text = "Longest Streak: ";
  617. text.append(std::to_string(best_streak));
  618. return text;
  619. };
  620. std::string currentString = currentUpdate();
  621. door::Line current(currentString, W);
  622. current.setRender(svRender);
  623. current.setUpdater(currentUpdate);
  624. p->addLine(std::make_unique<door::Line>(current));
  625. }
  626. return p;
  627. }
  628. std::unique_ptr<door::Panel> PlayCards::make_left_panel(void) {
  629. const int W = 13;
  630. std::unique_ptr<door::Panel> p = std::make_unique<door::Panel>(W);
  631. p->setStyle(door::BorderStyle::NONE);
  632. door::ANSIColor statusColor(door::COLOR::WHITE, door::COLOR::BLUE,
  633. door::ATTR::BOLD);
  634. door::ANSIColor valueColor(door::COLOR::YELLOW, door::COLOR::BLUE,
  635. door::ATTR::BOLD);
  636. door::renderFunction svRender = statusValue(statusColor, valueColor);
  637. {
  638. door::updateFunction cardsleftUpdate = [this](void) -> std::string {
  639. std::string text = "Cards left:";
  640. text.append(std::to_string(51 - card_number));
  641. return text;
  642. };
  643. std::string cardsleftString = "Cards left:--";
  644. door::Line cardsleft(cardsleftString, W);
  645. cardsleft.setRender(svRender);
  646. cardsleft.setUpdater(cardsleftUpdate);
  647. p->addLine(std::make_unique<door::Line>(cardsleft));
  648. }
  649. return p;
  650. }
  651. door::renderFunction PlayCards::commandLineRender(door::ANSIColor bracket,
  652. door::ANSIColor inner,
  653. door::ANSIColor outer) {
  654. door::renderFunction rf = [bracket, inner,
  655. outer](const std::string &txt) -> door::Render {
  656. door::Render r(txt);
  657. door::ColorOutput co;
  658. co.pos = 0;
  659. co.len = 0;
  660. co.c = outer;
  661. bool inOuter = true;
  662. int tpos = 0;
  663. for (char const &c : txt) {
  664. if (inOuter) {
  665. // we're in the outer text
  666. if (co.c != outer) {
  667. if (co.len != 0) {
  668. r.outputs.push_back(co);
  669. co.reset();
  670. co.pos = tpos;
  671. }
  672. co.c = outer;
  673. }
  674. // check for [
  675. if (c == '[') {
  676. if (co.len != 0) {
  677. r.outputs.push_back(co);
  678. co.reset();
  679. co.pos = tpos;
  680. }
  681. inOuter = false;
  682. co.c = bracket;
  683. }
  684. } else {
  685. // We're not in the outer.
  686. if (co.c != inner) {
  687. if (co.len != 0) {
  688. r.outputs.push_back(co);
  689. co.reset();
  690. co.pos = tpos;
  691. }
  692. co.c = inner;
  693. }
  694. if (c == ']') {
  695. if (co.len != 0) {
  696. r.outputs.push_back(co);
  697. co.reset();
  698. co.pos = tpos;
  699. }
  700. inOuter = true;
  701. co.c = bracket;
  702. }
  703. }
  704. co.len++;
  705. tpos++;
  706. }
  707. if (co.len != 0)
  708. r.outputs.push_back(co);
  709. return r;
  710. };
  711. return rf;
  712. }
  713. std::unique_ptr<door::Panel> PlayCards::make_command_panel(void) {
  714. const int W = 76;
  715. std::unique_ptr<door::Panel> p = make_unique<door::Panel>(W);
  716. p->setStyle(door::BorderStyle::NONE);
  717. std::string commands;
  718. if (door::unicode) {
  719. commands = "[4/\u25c4] Left [6/\u25ba] Right [Space] Play Card [Enter] "
  720. "Draw [Q]uit "
  721. "[R]edraw [H]elp";
  722. } else {
  723. commands =
  724. "[4/\x11] Left [6/\x10] Right [Space] Play Card [Enter] Draw [Q]uit "
  725. "[R]edraw [H]elp";
  726. }
  727. door::ANSIColor bracketColor(door::COLOR::YELLOW, door::COLOR::BLUE,
  728. door::ATTR::BOLD);
  729. door::ANSIColor innerColor(door::COLOR::CYAN, door::COLOR::BLUE,
  730. door::ATTR::BOLD);
  731. door::ANSIColor outerColor(door::COLOR::GREEN, door::COLOR::BLUE,
  732. door::ATTR::BOLD);
  733. door::renderFunction cmdRender =
  734. commandLineRender(bracketColor, innerColor, outerColor);
  735. door::Line cmd(commands, W);
  736. cmd.setRender(cmdRender);
  737. p->addLine(std::make_unique<door::Line>(cmd));
  738. return p;
  739. }
  740. std::unique_ptr<door::Panel> PlayCards::make_tripeaks(void) {
  741. std::string tripeaksText(" " SPACEACE
  742. " - Tri-Peaks Solitaire v" SPACEACE_VERSION " ");
  743. std::unique_ptr<door::Panel> spaceAceTriPeaks =
  744. std::make_unique<door::Panel>(tripeaksText.size());
  745. spaceAceTriPeaks->setStyle(door::BorderStyle::SINGLE);
  746. spaceAceTriPeaks->setColor(
  747. door::ANSIColor(door::COLOR::CYAN, door::COLOR::BLACK));
  748. spaceAceTriPeaks->addLine(
  749. std::make_unique<door::Line>(tripeaksText, tripeaksText.size()));
  750. return spaceAceTriPeaks;
  751. }
  752. #ifdef OLD_WAY
  753. int play_cards(door::Door &door, DBData &db, std::mt19937 &rng) {
  754. int mx = door.width;
  755. int my = door.height;
  756. // init these values:
  757. card_number = 28;
  758. active_card = 23;
  759. hand = 1;
  760. score = 0;
  761. play_day = std::chrono::system_clock::now();
  762. // cards color --
  763. // configured by the player.
  764. door::ANSIColor deck_color;
  765. // std::string key("DeckColor");
  766. const char *key = "DeckColor";
  767. std::string currentDefault = db.getSetting(key, "ALL");
  768. door.log() << key << " shows as " << currentDefault << std::endl;
  769. deck_color = from_string(currentDefault);
  770. const int height = 3;
  771. Deck d(deck_color); // , height);
  772. door::Panel *c;
  773. // This displays the cards in the upper left corner.
  774. // We want them center, and down some.
  775. // const int space = 3;
  776. // int cards_dealt_width = 59; int cards_dealt_height = 9;
  777. int game_width;
  778. int game_height = 20; // 13; // 9;
  779. {
  780. int cx, cy, level;
  781. cardgo(27, cx, cy, level);
  782. game_width = cx + 5; // card width
  783. }
  784. int off_x = (mx - game_width) / 2;
  785. int off_y = (my - game_height) / 2;
  786. int true_off_y = off_y;
  787. // The idea is to see the cards with <<Something Unique to the card game>>,
  788. // Year, Month, Day, and game (like 1 of 3).
  789. // This will make the games the same/fair for everyone.
  790. next_hand:
  791. card_number = 28;
  792. active_card = 23;
  793. score = 0;
  794. off_y = true_off_y;
  795. door::Panel spaceAceTriPeaks = make_tripeaks();
  796. int tp_off_x = (mx - spaceAceTriPeaks.getWidth()) / 2;
  797. spaceAceTriPeaks.set(tp_off_x, off_y);
  798. off_y += 3;
  799. // figure out what date we're playing / what game we're playing
  800. time_t tt = std::chrono::system_clock::to_time_t(play_day);
  801. tm local_tm = *localtime(&tt);
  802. /*
  803. std::cout << utc_tm.tm_year + 1900 << '-';
  804. std::cout << utc_tm.tm_mon + 1 << '-';
  805. std::cout << utc_tm.tm_mday << ' ';
  806. std::cout << utc_tm.tm_hour << ':';
  807. std::cout << utc_tm.tm_min << ':';
  808. std::cout << utc_tm.tm_sec << '\n';
  809. */
  810. std::seed_seq s1{local_tm.tm_year + 1900, local_tm.tm_mon + 1,
  811. local_tm.tm_mday, hand};
  812. cards deck1 = card_shuffle(s1, 1);
  813. cards state = card_states();
  814. door::Panel score_panel = make_score_panel(door);
  815. door::Panel streak_panel = make_streak_panel();
  816. door::Panel left_panel = make_left_panel();
  817. door::Panel cmd_panel = make_command_panel();
  818. {
  819. int off_yp = off_y + 11;
  820. int cxp, cyp, levelp;
  821. int left_panel_x, right_panel_x;
  822. // find position of card, to position the panels
  823. cardgo(18, cxp, cyp, levelp);
  824. left_panel_x = cxp;
  825. cardgo(15, cxp, cyp, levelp);
  826. right_panel_x = cxp;
  827. score_panel.set(left_panel_x + off_x, off_yp);
  828. streak_panel.set(right_panel_x + off_x, off_yp);
  829. cmd_panel.set(left_panel_x + off_x, off_yp + 5);
  830. }
  831. bool dealing = true;
  832. int r = 0;
  833. while ((r >= 0) and (r != 'Q')) {
  834. // REDRAW everything
  835. door << door::reset << door::cls;
  836. door << spaceAceTriPeaks;
  837. {
  838. // step 1:
  839. // draw the deck "source"
  840. int cx, cy, level;
  841. cardgo(29, cx, cy, level);
  842. if (card_number == 51)
  843. level = 0; // out of cards!
  844. c = d.back(level);
  845. c->set(cx + off_x, cy + off_y);
  846. // p3 is heigh below
  847. left_panel.set(cx + off_x, cy + off_y + height);
  848. door << score_panel << left_panel << streak_panel << cmd_panel;
  849. door << *c;
  850. if (dealing)
  851. std::this_thread::sleep_for(std::chrono::seconds(1));
  852. }
  853. // I tried setting the cursor before the delay and before displaying the
  854. // card. It is very hard to see / just about useless. Not worth the effort.
  855. for (int x = 0; x < (dealing ? 28 : 29); x++) {
  856. int cx, cy, level;
  857. cardgo(x, cx, cy, level);
  858. // This is hardly visible.
  859. // door << door::Goto(cx + off_x - 1, cy + off_y + 1);
  860. if (dealing)
  861. std::this_thread::sleep_for(std::chrono::milliseconds(75));
  862. if (dealing) {
  863. c = d.back(level);
  864. c->set(cx + off_x, cy + off_y);
  865. door << *c;
  866. } else {
  867. // redrawing -- draw the cards with their correct "state"
  868. int s = state.at(x);
  869. switch (s) {
  870. case 0:
  871. c = d.back(level);
  872. c->set(cx + off_x, cy + off_y);
  873. door << *c;
  874. break;
  875. case 1:
  876. // cardgo(x, space, height, cx, cy, level);
  877. if (x == 28)
  878. c = d.card(deck1.at(card_number));
  879. else
  880. c = d.card(deck1.at(x));
  881. c->set(cx + off_x, cy + off_y);
  882. door << *c;
  883. break;
  884. case 2:
  885. // no card to draw. :)
  886. break;
  887. }
  888. }
  889. }
  890. if (dealing)
  891. for (int x = 18; x < 29; x++) {
  892. int cx, cy, level;
  893. state.at(x) = 1;
  894. cardgo(x, cx, cy, level);
  895. // door << door::Goto(cx + off_x - 1, cy + off_y + 1);
  896. std::this_thread::sleep_for(std::chrono::milliseconds(200));
  897. c = d.card(deck1.at(x));
  898. c->set(cx + off_x, cy + off_y);
  899. door << *c;
  900. }
  901. {
  902. int cx, cy, level;
  903. cardgo(active_card, cx, cy, level);
  904. c = d.marker(1);
  905. c->set(cx + off_x + 2, cy + off_y + 2);
  906. door << *c;
  907. }
  908. dealing = false;
  909. left_panel.update(door);
  910. door << door::reset;
  911. bool now_what = true;
  912. while (now_what) {
  913. // time might have updated, so update score panel too.
  914. score_panel.update(door);
  915. r = door.sleep_key(door.inactivity);
  916. if (r > 0) {
  917. // They didn't timeout/expire. They didn't press a function key.
  918. if (r < 0x1000)
  919. r = std::toupper(r);
  920. switch (r) {
  921. case '\x0d':
  922. if (card_number < 51) {
  923. card_number++;
  924. current_streak = 0;
  925. streak_panel.update(door);
  926. // Ok, deal next card from the pile.
  927. int cx, cy, level;
  928. if (card_number == 51) {
  929. cardgo(29, cx, cy, level);
  930. level = 0; // out of cards
  931. c = d.back(level);
  932. c->set(cx + off_x, cy + off_y);
  933. door << *c;
  934. }
  935. cardgo(28, cx, cy, level);
  936. c = d.card(deck1.at(card_number));
  937. c->set(cx + off_x, cy + off_y);
  938. door << *c;
  939. // update the cards left_panel
  940. left_panel.update(door);
  941. }
  942. break;
  943. case 'R':
  944. now_what = false;
  945. break;
  946. case 'Q':
  947. now_what = false;
  948. break;
  949. case ' ':
  950. case '5':
  951. // can we play this card?
  952. /*
  953. get_logger() << "can_play( " << active_card << ":"
  954. << deck1.at(active_card) << "/"
  955. << d.is_rank(deck1.at(active_card)) << " , "
  956. << card_number << "/" << d.is_rank(deck1.at(card_number))
  957. << ") = "
  958. << d.can_play(deck1.at(active_card),
  959. deck1.at(card_number))
  960. << std::endl;
  961. */
  962. if (d.can_play(deck1.at(active_card), deck1.at(card_number))) {
  963. // if (true) {
  964. // yes we can.
  965. ++current_streak;
  966. if (current_streak > best_streak)
  967. best_streak = current_streak;
  968. streak_panel.update(door);
  969. score += 10;
  970. if (current_streak > 2)
  971. score += 5;
  972. // play card!
  973. state.at(active_card) = 2;
  974. {
  975. // swap the active card with card_number (play card)
  976. int temp = deck1.at(active_card);
  977. deck1.at(active_card) = deck1.at(card_number);
  978. deck1.at(card_number) = temp;
  979. // active_card is -- invalidated here! find "new" card.
  980. int cx, cy, level;
  981. // erase/clear active_card
  982. std::vector<int> check = d.unblocks(active_card);
  983. bool left = false, right = false;
  984. for (const int c : check) {
  985. std::pair<int, int> blockers = d.blocks[c];
  986. if (blockers.first == active_card)
  987. right = true;
  988. if (blockers.second == active_card)
  989. left = true;
  990. }
  991. d.remove_card(door, active_card, off_x, off_y, left, right);
  992. /* // old way of doing this that leaves holes.
  993. cardgo(active_card, cx, cy, level);
  994. c = d.back(0);
  995. c->set(cx + off_x, cy + off_y);
  996. door << *c;
  997. */
  998. // redraw play card #28. (Which is the "old" active_card)
  999. cardgo(28, cx, cy, level);
  1000. c = d.card(deck1.at(card_number));
  1001. c->set(cx + off_x, cy + off_y);
  1002. door << *c;
  1003. // Did we unhide a card here?
  1004. // std::vector<int> check = d.unblocks(active_card);
  1005. /*
  1006. get_logger() << "active_card = " << active_card
  1007. << " unblocks: " << check.size() << std::endl;
  1008. */
  1009. int new_card_shown = -1;
  1010. if (!check.empty()) {
  1011. for (const int to_check : check) {
  1012. std::pair<int, int> blockers = d.blocks[to_check];
  1013. /*
  1014. get_logger()
  1015. << "Check: " << to_check << " " << blockers.first << ","
  1016. << blockers.second << " " << state.at(blockers.first)
  1017. << "," << state.at(blockers.second) << std::endl;
  1018. */
  1019. if ((state.at(blockers.first) == 2) and
  1020. (state.at(blockers.second) == 2)) {
  1021. // WOOT! Card uncovered.
  1022. /*
  1023. get_logger() << "showing: " << to_check << std::endl;
  1024. */
  1025. state.at(to_check) = 1;
  1026. cardgo(to_check, cx, cy, level);
  1027. c = d.card(deck1.at(to_check));
  1028. c->set(cx + off_x, cy + off_y);
  1029. door << *c;
  1030. new_card_shown = to_check;
  1031. }
  1032. }
  1033. } else {
  1034. // this would be a "top" card. Should set status = 4 and
  1035. // display something here?
  1036. get_logger() << "top card cleared?" << std::endl;
  1037. // display something at active_card position
  1038. int cx, cy, level;
  1039. cardgo(active_card, cx, cy, level);
  1040. door << door::Goto(cx + off_x, cy + off_y) << door::reset
  1041. << "BONUS";
  1042. score += 100;
  1043. state.at(active_card) = 3; // handle this in the "redraw"
  1044. }
  1045. // Find new "number" for active_card to be.
  1046. if (new_card_shown != -1) {
  1047. active_card = new_card_shown;
  1048. } else {
  1049. // active_card++;
  1050. int new_active = find_next_closest(state, active_card);
  1051. if (new_active != -1) {
  1052. active_card = new_active;
  1053. } else {
  1054. get_logger() << "This looks like END OF GAME." << std::endl;
  1055. // bonus for cards left
  1056. press_a_key(door);
  1057. if (hand < total_hands) {
  1058. hand++;
  1059. door << door::reset << door::cls;
  1060. goto next_hand;
  1061. }
  1062. r = 'Q';
  1063. now_what = false;
  1064. }
  1065. }
  1066. // update the active_card marker!
  1067. cardgo(active_card, cx, cy, level);
  1068. c = d.marker(1);
  1069. c->set(cx + off_x + 2, cy + off_y + 2);
  1070. door << *c;
  1071. }
  1072. }
  1073. break;
  1074. case XKEY_LEFT_ARROW:
  1075. case '4': {
  1076. int new_active = find_next(true, state, active_card);
  1077. /*
  1078. int new_active = active_card - 1;
  1079. while (new_active >= 0) {
  1080. if (state.at(new_active) == 1)
  1081. break;
  1082. --new_active;
  1083. }*/
  1084. if (new_active >= 0) {
  1085. int cx, cy, level;
  1086. cardgo(active_card, cx, cy, level);
  1087. c = d.marker(0);
  1088. c->set(cx + off_x + 2, cy + off_y + 2);
  1089. door << *c;
  1090. active_card = new_active;
  1091. cardgo(active_card, cx, cy, level);
  1092. c = d.marker(1);
  1093. c->set(cx + off_x + 2, cy + off_y + 2);
  1094. door << *c;
  1095. }
  1096. } break;
  1097. case XKEY_RIGHT_ARROW:
  1098. case '6': {
  1099. int new_active = find_next(false, state, active_card);
  1100. /*
  1101. int new_active = active_card + 1;
  1102. while (new_active < 28) {
  1103. if (state.at(new_active) == 1)
  1104. break;
  1105. ++new_active;
  1106. }
  1107. */
  1108. if (new_active >= 0) { //(new_active < 28) {
  1109. int cx, cy, level;
  1110. cardgo(active_card, cx, cy, level);
  1111. c = d.marker(0);
  1112. c->set(cx + off_x + 2, cy + off_y + 2);
  1113. door << *c;
  1114. active_card = new_active;
  1115. cardgo(active_card, cx, cy, level);
  1116. c = d.marker(1);
  1117. c->set(cx + off_x + 2, cy + off_y + 2);
  1118. door << *c;
  1119. }
  1120. }
  1121. break;
  1122. }
  1123. } else
  1124. now_what = false;
  1125. }
  1126. }
  1127. if (r == 'Q') {
  1128. // continue with hand or quit?
  1129. if (hand < total_hands) {
  1130. press_a_key(door);
  1131. hand++;
  1132. door << door::reset << door::cls;
  1133. goto next_hand;
  1134. }
  1135. }
  1136. return r;
  1137. }
  1138. #endif