play.cpp 35 KB

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