play.cpp 52 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659
  1. #include "play.h"
  2. #include "db.h"
  3. #include "deck.h"
  4. #include "utils.h"
  5. #include "version.h"
  6. #include <ctime>
  7. #include <iomanip> // put_time
  8. #include <sstream>
  9. /**
  10. * @brief Allow any card to be played on any other card.
  11. *
  12. * This is for testing BONUS and scoring, etc.
  13. *
  14. */
  15. #define CHEATER "_CHEAT_YOUR_ASS_OFF"
  16. // static std::function<std::ofstream &(void)> get_logger;
  17. /*
  18. static int press_a_key(door::Door &door) {
  19. door << door::reset << "Press a key to continue...";
  20. int r = door.sleep_key(door.inactivity);
  21. door << door::nl;
  22. return r;
  23. }
  24. */
  25. /*
  26. In the future, this will probably check to see if they can play today or not, as
  27. well as displaying a calendar to show what days are available to be played.
  28. For now, it will play today.
  29. */
  30. PlayCards::PlayCards(door::Door &d, DBData &dbd, std::mt19937 &r)
  31. : door{d}, db{dbd}, rng{r} {
  32. get_logger = [this]() -> ofstream & { return door.log(); };
  33. init_values();
  34. play_day = std::chrono::system_clock::now();
  35. normalizeDate(play_day);
  36. time_t play_day_t = std::chrono::system_clock::to_time_t(play_day);
  37. // check last_played
  38. time_t last_played;
  39. {
  40. std::string last_played_str = dbd.getSetting("last_played", "0");
  41. last_played = std::stoi(last_played_str);
  42. std::string days_played_str = dbd.getSetting("days_played", "0");
  43. days_played = std::stoi(days_played_str);
  44. }
  45. if (last_played != play_day_t) {
  46. // Ok, they haven't played today, so:
  47. get_logger() << "reset days_played = 0" << std::endl;
  48. dbd.setSetting("last_played", std::to_string(play_day_t));
  49. dbd.setSetting("days_played", std::to_string(0));
  50. days_played = 0;
  51. }
  52. /*
  53. * TODO: Check the date with the db. Have they already played up today? If
  54. * so, display calendar and find a day they can play. Or, play missed hands
  55. * for today.
  56. */
  57. hand = 0;
  58. total_hands = 0;
  59. spaceAceTriPeaks = make_tripeaks();
  60. score_panel = make_score_panel();
  61. streak_panel = make_streak_panel();
  62. left_panel = make_left_panel();
  63. cmd_panel = make_command_panel();
  64. next_quit_panel = make_next_panel();
  65. calendar = make_calendar();
  66. /*
  67. int mx = door.width;
  68. int my = door.height;
  69. */
  70. }
  71. PlayCards::~PlayCards() { get_logger = nullptr; }
  72. void PlayCards::init_values(void) {
  73. // beware of hand=1 ! We might not be playing the first hand here!
  74. // hand = 1;
  75. if (config["hands_per_day"]) {
  76. total_hands = config["hands_per_day"].as<int>();
  77. } else
  78. total_hands = 3;
  79. play_card = 28;
  80. current_streak = 0;
  81. best_streak = 0;
  82. {
  83. std::string best;
  84. best = db.getSetting("best_streak", "0");
  85. best_streak = std::stoi(best);
  86. }
  87. select_card = 23;
  88. score = 0;
  89. }
  90. /**
  91. * @brief Display the bonus text, when you remove a peak.
  92. *
  93. */
  94. void PlayCards::bonus(void) {
  95. door << door::ANSIColor(door::COLOR::YELLOW, door::ATTR::BOLD) << "BONUS";
  96. }
  97. int PlayCards::press_a_key(void) {
  98. door << door::reset << "Press a key to continue...";
  99. int r = door.sleep_key(door.inactivity);
  100. door << door::nl;
  101. return r;
  102. }
  103. /**
  104. * @brief PLay
  105. *
  106. * This will display the calendar (if needed), otherwise takes player into
  107. * play_cards using now() as play_day.
  108. * \sa PlayCards::play_cards()
  109. *
  110. * @return int
  111. */
  112. int PlayCards::play(void) {
  113. play_day = std::chrono::system_clock::now();
  114. normalizeDate(play_day);
  115. int total_hands;
  116. if (config["hands_per_day"]) {
  117. total_hands = config["hands_per_day"].as<int>();
  118. } else
  119. total_hands = 3;
  120. // check to see if played already today
  121. time_t play_day_t = std::chrono::system_clock::to_time_t(play_day);
  122. int played = db.handsPlayedOnDay(play_day_t);
  123. if (get_logger) {
  124. get_logger() << "played today (" << play_day_t << ")= " << played
  125. << std::endl;
  126. }
  127. if (played == 0) {
  128. // playing today
  129. hand = 1;
  130. return play_cards();
  131. } else {
  132. if (played < total_hands) {
  133. hand = played + 1;
  134. return play_cards();
  135. }
  136. }
  137. // Ok, we need to select a day.
  138. std::unique_ptr<door::Screen> calendar = make_calendar();
  139. if (cls_display_starfield)
  140. cls_display_starfield();
  141. else
  142. door << door::reset << door::cls;
  143. door << *calendar;
  144. door << door::nl;
  145. door << "Choose, eh? ";
  146. std::string toplay = door.input_string(3);
  147. int number;
  148. try {
  149. number = std::stoi(toplay);
  150. } catch (std::exception &e) {
  151. number = 0;
  152. }
  153. if (number == 0)
  154. return ' ';
  155. int status;
  156. if (get_logger)
  157. get_logger() << "number " << number;
  158. if (number <= 31) {
  159. status = calendar_day_status[number - 1];
  160. if (get_logger)
  161. get_logger() << "status " << status << std::endl;
  162. if (status == 0) {
  163. // play full day -- how do I figure out the date for this?
  164. hand = 1;
  165. play_day_t = calendar_day_t[number - 1];
  166. play_day = std::chrono::system_clock::from_time_t(play_day_t);
  167. return play_cards();
  168. }
  169. if (status == 1) {
  170. // play half day
  171. play_day_t = calendar_day_t[number - 1];
  172. play_day = std::chrono::system_clock::from_time_t(play_day_t);
  173. played = db.handsPlayedOnDay(play_day_t);
  174. if (played < total_hands) {
  175. hand = played + 1;
  176. return play_cards();
  177. }
  178. }
  179. };
  180. int r = press_a_key();
  181. if (r < 0) // timeout! exit!
  182. return r;
  183. // return 0;
  184. /*
  185. hand = 1;
  186. // possibly init_values()
  187. play_day = std::chrono::system_clock::now();
  188. normalizeDate(play_day);
  189. return play_cards();
  190. */
  191. return r;
  192. }
  193. /**
  194. * @brief play_cards
  195. *
  196. * Play cards for the given play_day and hand.
  197. *
  198. * This should be called by play with the correct #play_day set.
  199. * \sa PlayCards::play()
  200. *
  201. * @return int
  202. */
  203. int PlayCards::play_cards(void) {
  204. init_values();
  205. // Calculate the game size
  206. int game_width;
  207. int game_height = 20;
  208. {
  209. int cx, cy;
  210. cardPos(27, cx, cy);
  211. game_width = cx + 5;
  212. }
  213. int mx = door.width;
  214. int my = door.height;
  215. off_x = (mx - game_width) / 2;
  216. off_y = (my - game_height) / 2;
  217. // int true_off_y = off_y;
  218. // we can now position things properly centered
  219. int tp_off_x = (mx - spaceAceTriPeaks->getWidth()) / 2;
  220. spaceAceTriPeaks->set(tp_off_x, off_y);
  221. off_y += 3; // adjust for tripeaks panel
  222. next_hand:
  223. // Make sure we pick the deck color here. We want it to (possibly) change
  224. // between hands.
  225. std::string currentDefault = db.getSetting("DeckColor", "ALL");
  226. get_logger() << "DeckColor shows as " << currentDefault << std::endl;
  227. deck_color = stringToANSIColor(currentDefault);
  228. dp = Deck(deck_color);
  229. play_card = 28;
  230. select_card = 23;
  231. score = 0;
  232. current_streak = 0;
  233. // Use play_day to seed the rng
  234. {
  235. time_t tt = std::chrono::system_clock::to_time_t(play_day);
  236. tm local_tm = *localtime(&tt);
  237. std::seed_seq seq{local_tm.tm_year + 1900, local_tm.tm_mon + 1,
  238. local_tm.tm_mday, hand};
  239. deck = shuffleCards(seq, 1);
  240. state = makeCardStates();
  241. }
  242. /*
  243. door::Panel score_panel = make_score_panel();
  244. door::Panel streak_panel = make_streak_panel();
  245. door::Panel left_panel = make_left_panel();
  246. door::Panel cmd_panel = make_command_panel();
  247. */
  248. {
  249. int off_yp = off_y + 11;
  250. int cx, cy;
  251. int left_panel_x, right_panel_x;
  252. // find position of card, to position the panels
  253. cardPos(18, cx, cy);
  254. left_panel_x = cx;
  255. cardPos(15, cx, cy);
  256. right_panel_x = cx;
  257. score_panel->set(left_panel_x + off_x, off_yp);
  258. streak_panel->set(right_panel_x + off_x, off_yp);
  259. cmd_panel->set(left_panel_x + off_x, off_yp + 5);
  260. // next panel position (top = card 10, centered)
  261. cardPos(10, cx, cy);
  262. int next_off_x = (mx - next_quit_panel->getWidth()) / 2;
  263. next_quit_panel->set(next_off_x, cy + off_y);
  264. }
  265. bool dealing = true;
  266. int r = 0;
  267. redraw(dealing);
  268. dealing = false;
  269. left_panel->update(door);
  270. door << door::reset;
  271. shared_panel c;
  272. bool in_game = true;
  273. bool save_streak = false;
  274. while (in_game) {
  275. // time might have updated, so update score panel too.
  276. score_panel->update(door);
  277. // do the save here -- try to hide the database lag
  278. if (save_streak) {
  279. save_streak = false;
  280. std::string best = std::to_string(best_streak);
  281. db.setSetting("best_streak", best);
  282. }
  283. r = door.sleep_key(door.inactivity);
  284. if (r > 0) {
  285. // not a timeout or expire.
  286. if (r < 0x1000) // not a function key
  287. r = std::toupper(r);
  288. switch (r) {
  289. case '\x0d':
  290. if (current_streak > best_streak) {
  291. best_streak = current_streak;
  292. if (!config[CHEATER]) {
  293. save_streak = true;
  294. }
  295. streak_panel->update(door);
  296. }
  297. if (play_card < 51) {
  298. play_card++;
  299. current_streak = 0;
  300. streak_panel->update(door);
  301. // update the cards left_panel
  302. left_panel->update(door);
  303. // Ok, deal next card from the pile.
  304. int cx, cy, level;
  305. if (play_card == 51) {
  306. cardPosLevel(29, cx, cy, level);
  307. level = 0; // out of cards
  308. c = dp.back(level);
  309. c->set(cx + off_x, cy + off_y);
  310. door << *c;
  311. }
  312. cardPos(28, cx, cy);
  313. c = dp.card(deck.at(play_card));
  314. c->set(cx + off_x, cy + off_y);
  315. door << *c;
  316. }
  317. break;
  318. case 'R':
  319. redraw(false);
  320. break;
  321. case 'Q':
  322. // possibly prompt here for [N]ext hand or [Q]uit ?
  323. if (current_streak > best_streak) {
  324. best_streak = current_streak;
  325. if (!config[CHEATER]) {
  326. save_streak = true;
  327. }
  328. streak_panel->update(door);
  329. }
  330. next_quit_panel->update();
  331. door << *next_quit_panel;
  332. if (hand < total_hands) {
  333. r = door.get_one_of("CQN");
  334. } else {
  335. r = door.get_one_of("CQ");
  336. }
  337. if (r == 0) {
  338. // continue
  339. redraw(false);
  340. break;
  341. }
  342. if (r == 1) {
  343. // Ok, we are calling it quits.
  344. // save score if > 0
  345. if (score >= 50) {
  346. time_t right_now = std::chrono::system_clock::to_time_t(
  347. std::chrono::system_clock::now());
  348. db.saveScore(right_now,
  349. std::chrono::system_clock::to_time_t(play_day), hand,
  350. 0, score);
  351. }
  352. in_game = false;
  353. r = 'Q';
  354. }
  355. if (r == 2) {
  356. // no. If you want to play the next hand, we have to save this.
  357. get_logger() << "SCORE: " << score << std::endl;
  358. time_t right_now = std::chrono::system_clock::to_time_t(
  359. std::chrono::system_clock::now());
  360. db.saveScore(right_now,
  361. std::chrono::system_clock::to_time_t(play_day), hand, 0,
  362. score);
  363. hand++;
  364. goto next_hand;
  365. }
  366. // in_game = false;
  367. break;
  368. case ' ':
  369. case '5':
  370. // can we play this card?
  371. /*
  372. get_logger() << "canPlay( " << select_card << ":"
  373. << deck1.at(select_card) << "/"
  374. << d.getRank(deck1.at(select_card)) << " , "
  375. << play_card << "/" <<
  376. d.getRank(deck1.at(play_card))
  377. << ") = "
  378. << d.canPlay(deck1.at(select_card),
  379. deck1.at(play_card))
  380. << std::endl;
  381. */
  382. if (dp.canPlay(deck.at(select_card), deck.at(play_card)) or
  383. config[CHEATER]) {
  384. // if (true) {
  385. // yes we can.
  386. ++current_streak;
  387. // update best_streak when they draw the next card.
  388. /*
  389. if (current_streak > best_streak) {
  390. best_streak = current_streak;
  391. if (!config[CHEATER]) {
  392. save_streak = true;
  393. }
  394. }
  395. */
  396. streak_panel->update(door);
  397. score += 10;
  398. if (current_streak > 1)
  399. score += current_streak * 5;
  400. score_panel->update(door);
  401. /*
  402. if (get_logger)
  403. get_logger() << "score_panel update : " << score << std::endl;
  404. */
  405. // play card!
  406. state.at(select_card) = 2;
  407. {
  408. // swap the select card with play_card
  409. int temp = deck.at(select_card);
  410. deck.at(select_card) = deck.at(play_card);
  411. deck.at(play_card) = temp;
  412. // select_card is -- invalidated here! find "new" card.
  413. int cx, cy;
  414. // erase/clear select_card
  415. std::vector<int> check = dp.unblocks(select_card);
  416. bool left = false, right = false;
  417. for (const int c : check) {
  418. std::pair<int, int> blockers = dp.blocks[c];
  419. if (blockers.first == select_card)
  420. right = true;
  421. if (blockers.second == select_card)
  422. left = true;
  423. }
  424. dp.removeCard(door, select_card, off_x, off_y, left, right);
  425. /* // old way of doing this that leaves holes.
  426. cardPosLevel(select_card, cx, cy, level);
  427. c = d.back(0);
  428. c->set(cx + off_x, cy + off_y);
  429. door << *c;
  430. */
  431. // redraw play card #28. (Which is the "old" select_card)
  432. cardPos(28, cx, cy);
  433. c = dp.card(deck.at(play_card));
  434. c->set(cx + off_x, cy + off_y);
  435. door << *c;
  436. // Did we unhide a card here?
  437. // std::vector<int> check = d.unblocks(select_card);
  438. /*
  439. get_logger() << "select_card = " << select_card
  440. << " unblocks: " << check.size() << std::endl;
  441. */
  442. int new_card_shown = -1;
  443. if (!check.empty()) {
  444. for (const int to_check : check) {
  445. std::pair<int, int> blockers = dp.blocks[to_check];
  446. /*
  447. get_logger()
  448. << "Check: " << to_check << " " << blockers.first << ","
  449. << blockers.second << " " << state.at(blockers.first)
  450. << "," << state.at(blockers.second) << std::endl;
  451. */
  452. if ((state.at(blockers.first) == 2) and
  453. (state.at(blockers.second) == 2)) {
  454. // WOOT! Card uncovered.
  455. /*
  456. get_logger() << "showing: " << to_check << std::endl;
  457. */
  458. state.at(to_check) = 1;
  459. cardPos(to_check, cx, cy);
  460. c = dp.card(deck.at(to_check));
  461. c->set(cx + off_x, cy + off_y);
  462. door << *c;
  463. new_card_shown = to_check;
  464. }
  465. }
  466. } else {
  467. // top card cleared
  468. // get_logger() << "top card cleared?" << std::endl;
  469. // display something at select_card position
  470. int cx, cy;
  471. cardPos(select_card, cx, cy);
  472. door << door::Goto(cx + off_x, cy + off_y);
  473. bonus();
  474. score += 100;
  475. state.at(select_card) = 3; // handle this in the "redraw"
  476. score_panel->update(door);
  477. }
  478. // Find new "number" for select_card to be.
  479. if (new_card_shown != -1) {
  480. select_card = new_card_shown;
  481. } else {
  482. // select_card++;
  483. int new_select = findClosestActiveCard(state, select_card);
  484. if (new_select != -1) {
  485. select_card = new_select;
  486. } else {
  487. get_logger() << "Winner!" << std::endl;
  488. select_card = -1; // winner marker?
  489. // bonus for cards left
  490. int bonus = 15 * (51 - play_card);
  491. score += 15 * (51 - play_card);
  492. score_panel->update(door);
  493. // maybe display this somewhere?
  494. // door << " BONUS: " << bonus << door::nl;
  495. get_logger()
  496. << "SCORE: " << score << ", " << bonus << std::endl;
  497. // if (!config[CHEATER]) {
  498. time_t right_now = std::chrono::system_clock::to_time_t(
  499. std::chrono::system_clock::now());
  500. db.saveScore(right_now,
  501. std::chrono::system_clock::to_time_t(play_day),
  502. hand, 1, score);
  503. //}
  504. next_quit_panel->update();
  505. door << *next_quit_panel;
  506. if (hand < total_hands) {
  507. r = door.get_one_of("QN");
  508. } else {
  509. r = door.get_one_of("Q");
  510. }
  511. if (r == 1) {
  512. hand++;
  513. goto next_hand;
  514. }
  515. if (r == 0) {
  516. r = 'Q';
  517. }
  518. /*
  519. press_a_key(door);
  520. if (hand < total_hands) {
  521. hand++;
  522. // current_streak = 0;
  523. door << door::reset << door::cls;
  524. goto next_hand;
  525. }
  526. r = 'Q';
  527. */
  528. in_game = false;
  529. }
  530. }
  531. // update the select_card marker!
  532. cardPos(select_card, cx, cy);
  533. c = dp.marker(1);
  534. c->set(cx + off_x + 2, cy + off_y + 2);
  535. door << *c;
  536. }
  537. }
  538. break;
  539. case XKEY_LEFT_ARROW:
  540. case '4': {
  541. int new_select = findNextActiveCard(true, state, select_card);
  542. /*
  543. int new_active = active_card - 1;
  544. while (new_active >= 0) {
  545. if (state.at(new_active) == 1)
  546. break;
  547. --new_active;
  548. }*/
  549. if (new_select >= 0) {
  550. int cx, cy;
  551. cardPos(select_card, cx, cy);
  552. c = dp.marker(0);
  553. c->set(cx + off_x + 2, cy + off_y + 2);
  554. door << *c;
  555. select_card = new_select;
  556. cardPos(select_card, cx, cy);
  557. c = dp.marker(1);
  558. c->set(cx + off_x + 2, cy + off_y + 2);
  559. door << *c;
  560. }
  561. } break;
  562. case XKEY_RIGHT_ARROW:
  563. case '6': {
  564. int new_select = findNextActiveCard(false, state, select_card);
  565. /*
  566. int new_active = active_card + 1;
  567. while (new_active < 28) {
  568. if (state.at(new_active) == 1)
  569. break;
  570. ++new_active;
  571. }
  572. */
  573. if (new_select >= 0) { //(new_active < 28) {
  574. int cx, cy;
  575. cardPos(select_card, cx, cy);
  576. c = dp.marker(0);
  577. c->set(cx + off_x + 2, cy + off_y + 2);
  578. door << *c;
  579. select_card = new_select;
  580. cardPos(select_card, cx, cy);
  581. c = dp.marker(1);
  582. c->set(cx + off_x + 2, cy + off_y + 2);
  583. door << *c;
  584. }
  585. }
  586. break;
  587. }
  588. } else
  589. in_game = false;
  590. }
  591. if (r == 'Q') {
  592. // continue, play next hand (if applicable), or quit?
  593. // if score < 50, don't bother saving.
  594. // continue -- eat r & redraw.
  595. // quit, save score and exit (unless score is zero).
  596. }
  597. return r;
  598. }
  599. /**
  600. * @brief Redraw the entire play cards screen.
  601. *
  602. * If dealing then show delays when displaying cards, or turning them over.
  603. *
  604. * Otherwise, the user has requested a redraw -- and don't delay.
  605. *
  606. * @param dealing
  607. */
  608. void PlayCards::redraw(bool dealing) {
  609. shared_panel c;
  610. display_starfield(door, rng);
  611. // door << door::reset << door::cls;
  612. door << *spaceAceTriPeaks;
  613. {
  614. // step 1:
  615. // draw the deck "source"
  616. int cx, cy, level;
  617. cardPosLevel(29, cx, cy, level);
  618. if (play_card == 51)
  619. level = 0; // out of cards!
  620. c = dp.back(level);
  621. c->set(cx + off_x, cy + off_y);
  622. // p3 is heigh below
  623. left_panel->set(cx + off_x, cy + off_y + height);
  624. // how do I update these? (hand >1)
  625. score_panel->update();
  626. left_panel->update();
  627. streak_panel->update();
  628. cmd_panel->update();
  629. door << *score_panel << *left_panel << *streak_panel << *cmd_panel;
  630. door << *c;
  631. if (dealing)
  632. std::this_thread::sleep_for(std::chrono::seconds(1));
  633. }
  634. for (int x = 0; x < (dealing ? 28 : 29); x++) {
  635. int cx, cy, level;
  636. cardPosLevel(x, cx, cy, level);
  637. // This is hardly visible.
  638. // door << door::Goto(cx + off_x - 1, cy + off_y + 1);
  639. if (dealing)
  640. std::this_thread::sleep_for(std::chrono::milliseconds(75));
  641. if (dealing) {
  642. c = dp.back(level);
  643. c->set(cx + off_x, cy + off_y);
  644. door << *c;
  645. } else {
  646. // redrawing -- draw the cards with their correct "state"
  647. int s = state.at(x);
  648. switch (s) {
  649. case 0:
  650. c = dp.back(level);
  651. c->set(cx + off_x, cy + off_y);
  652. door << *c;
  653. break;
  654. case 1:
  655. // cardPosLevel(x, space, height, cx, cy, level);
  656. if (x == 28)
  657. c = dp.card(deck.at(play_card));
  658. else
  659. c = dp.card(deck.at(x));
  660. c->set(cx + off_x, cy + off_y);
  661. door << *c;
  662. break;
  663. case 2:
  664. // no card to draw. :)
  665. break;
  666. case 3:
  667. // peak cleared, draw bonus
  668. door << door::Goto(cx + off_x, cy + off_y);
  669. bonus();
  670. break;
  671. }
  672. }
  673. }
  674. if (dealing)
  675. for (int x = 18; x < 29; x++) {
  676. int cx, cy;
  677. state.at(x) = 1;
  678. cardPos(x, cx, cy);
  679. // door << door::Goto(cx + off_x - 1, cy + off_y + 1);
  680. std::this_thread::sleep_for(std::chrono::milliseconds(200));
  681. c = dp.card(deck.at(x));
  682. c->set(cx + off_x, cy + off_y);
  683. door << *c;
  684. }
  685. {
  686. int cx, cy;
  687. cardPos(select_card, cx, cy);
  688. c = dp.marker(1);
  689. c->set(cx + off_x + 2, cy + off_y + 2);
  690. door << *c;
  691. }
  692. }
  693. door::renderFunction PlayCards::statusValue(door::ANSIColor status,
  694. door::ANSIColor value) {
  695. door::renderFunction rf = [status,
  696. value](const std::string &txt) -> door::Render {
  697. door::Render r(txt);
  698. size_t pos = txt.find(':');
  699. if (pos == std::string::npos) {
  700. for (char const &c : txt) {
  701. if (std::isdigit(c))
  702. r.append(value);
  703. else
  704. r.append(status);
  705. }
  706. } else {
  707. pos++;
  708. r.append(status, pos);
  709. r.append(value, txt.length() - pos);
  710. }
  711. return r;
  712. };
  713. return rf;
  714. }
  715. /**
  716. * @brief make the score panel
  717. * This displays: Name, Score, Time Used, Hands Played.
  718. *
  719. * @return std::unique_ptr<door::Panel>
  720. */
  721. std::unique_ptr<door::Panel> PlayCards::make_score_panel() {
  722. const int W = 25;
  723. std::unique_ptr<door::Panel> p = std::make_unique<door::Panel>(W);
  724. p->setStyle(door::BorderStyle::NONE);
  725. door::ANSIColor statusColor(door::COLOR::WHITE, door::COLOR::BLUE,
  726. door::ATTR::BOLD);
  727. door::ANSIColor valueColor(door::COLOR::YELLOW, door::COLOR::BLUE,
  728. door::ATTR::BOLD);
  729. door::renderFunction svRender = statusValue(statusColor, valueColor);
  730. // or use renderStatus as defined above.
  731. // We'll stick with these for now.
  732. {
  733. std::string userString = "Name: ";
  734. userString += door.username;
  735. door::Line username(userString, W);
  736. username.setRender(svRender);
  737. p->addLine(std::make_unique<door::Line>(username));
  738. }
  739. {
  740. door::updateFunction scoreUpdate = [this](void) -> std::string {
  741. std::string text = "Score: ";
  742. text.append(std::to_string(score));
  743. return text;
  744. };
  745. std::string scoreString = scoreUpdate();
  746. door::Line scoreline(scoreString, W);
  747. scoreline.setRender(svRender);
  748. scoreline.setUpdater(scoreUpdate);
  749. p->addLine(std::make_unique<door::Line>(scoreline));
  750. }
  751. {
  752. door::updateFunction timeUpdate = [this](void) -> std::string {
  753. std::stringstream ss;
  754. std::string text;
  755. ss << "Time used: " << setw(3) << door.time_used << " / " << setw(3)
  756. << door.time_left;
  757. text = ss.str();
  758. return text;
  759. };
  760. std::string timeString = timeUpdate();
  761. door::Line time(timeString, W);
  762. time.setRender(svRender);
  763. time.setUpdater(timeUpdate);
  764. p->addLine(std::make_unique<door::Line>(time));
  765. }
  766. {
  767. door::updateFunction handUpdate = [this](void) -> std::string {
  768. std::string text = "Playing Hand ";
  769. text.append(std::to_string(hand));
  770. text.append(" of ");
  771. text.append(std::to_string(total_hands));
  772. return text;
  773. };
  774. std::string handString = handUpdate();
  775. door::Line hands(handString, W);
  776. hands.setRender(svRender);
  777. hands.setUpdater(handUpdate);
  778. p->addLine(std::make_unique<door::Line>(hands));
  779. }
  780. return p;
  781. }
  782. std::unique_ptr<door::Panel> PlayCards::make_streak_panel(void) {
  783. const int W = 20;
  784. std::unique_ptr<door::Panel> p = std::make_unique<door::Panel>(W);
  785. p->setStyle(door::BorderStyle::NONE);
  786. door::ANSIColor statusColor(door::COLOR::WHITE, door::COLOR::BLUE,
  787. door::ATTR::BOLD);
  788. door::ANSIColor valueColor(door::COLOR::YELLOW, door::COLOR::BLUE,
  789. door::ATTR::BOLD);
  790. door::renderFunction svRender = statusValue(statusColor, valueColor);
  791. {
  792. door::updateFunction dateUpdate = [this](void) -> std::string {
  793. std::string text = "Playing: ";
  794. auto in_time_t = std::chrono::system_clock::to_time_t(play_day);
  795. std::stringstream ss;
  796. if (config["date_format"]) {
  797. std::string fmt = config["date_format"].as<std::string>();
  798. ss << std::put_time(std::localtime(&in_time_t), fmt.c_str());
  799. } else
  800. ss << std::put_time(std::localtime(&in_time_t), "%B %d");
  801. text.append(ss.str());
  802. return text;
  803. };
  804. std::string text = dateUpdate();
  805. door::Line current(text, W);
  806. current.setRender(svRender);
  807. current.setUpdater(dateUpdate);
  808. p->addLine(std::make_unique<door::Line>(current));
  809. }
  810. {
  811. door::updateFunction currentUpdate = [this](void) -> std::string {
  812. std::string text = "Current Streak: ";
  813. text.append(std::to_string(current_streak));
  814. return text;
  815. };
  816. std::string currentString = currentUpdate();
  817. door::Line current(currentString, W);
  818. current.setRender(svRender);
  819. current.setUpdater(currentUpdate);
  820. p->addLine(std::make_unique<door::Line>(current));
  821. }
  822. {
  823. door::updateFunction currentUpdate = [this](void) -> std::string {
  824. std::string text = "Longest Streak: ";
  825. text.append(std::to_string(best_streak));
  826. return text;
  827. };
  828. std::string currentString = currentUpdate();
  829. door::Line current(currentString, W);
  830. current.setRender(svRender);
  831. current.setUpdater(currentUpdate);
  832. p->addLine(std::make_unique<door::Line>(current));
  833. }
  834. return p;
  835. }
  836. std::unique_ptr<door::Panel> PlayCards::make_left_panel(void) {
  837. const int W = 13;
  838. std::unique_ptr<door::Panel> p = std::make_unique<door::Panel>(W);
  839. p->setStyle(door::BorderStyle::NONE);
  840. door::ANSIColor statusColor(door::COLOR::WHITE, door::COLOR::BLUE,
  841. door::ATTR::BOLD);
  842. door::ANSIColor valueColor(door::COLOR::YELLOW, door::COLOR::BLUE,
  843. door::ATTR::BOLD);
  844. door::renderFunction svRender = statusValue(statusColor, valueColor);
  845. {
  846. door::updateFunction cardsleftUpdate = [this](void) -> std::string {
  847. std::string text = "Cards left:";
  848. text.append(std::to_string(51 - play_card));
  849. return text;
  850. };
  851. std::string cardsleftString = "Cards left:--";
  852. door::Line cardsleft(cardsleftString, W);
  853. cardsleft.setRender(svRender);
  854. cardsleft.setUpdater(cardsleftUpdate);
  855. p->addLine(std::make_unique<door::Line>(cardsleft));
  856. }
  857. return p;
  858. }
  859. door::renderFunction PlayCards::commandLineRender(door::ANSIColor bracket,
  860. door::ANSIColor inner,
  861. door::ANSIColor outer) {
  862. door::renderFunction rf = [bracket, inner,
  863. outer](const std::string &txt) -> door::Render {
  864. door::Render r(txt);
  865. bool inOuter = true;
  866. for (char const &c : txt) {
  867. if (c == '[') {
  868. inOuter = false;
  869. r.append(bracket);
  870. continue;
  871. }
  872. if (c == ']') {
  873. inOuter = true;
  874. r.append(bracket);
  875. continue;
  876. }
  877. if (inOuter)
  878. r.append(outer);
  879. else
  880. r.append(inner);
  881. }
  882. return r;
  883. };
  884. return rf;
  885. }
  886. std::unique_ptr<door::Panel> PlayCards::make_command_panel(void) {
  887. const int W = 76;
  888. std::unique_ptr<door::Panel> p = make_unique<door::Panel>(W);
  889. p->setStyle(door::BorderStyle::NONE);
  890. std::string commands;
  891. if (door::unicode) {
  892. commands = "[4/\u25c4] Left [6/\u25ba] Right [Space] Play Card [Enter] "
  893. "Draw [Q]uit "
  894. "[R]edraw [H]elp";
  895. } else {
  896. commands =
  897. "[4/\x11] Left [6/\x10] Right [Space] Play Card [Enter] Draw [Q]uit "
  898. "[R]edraw [H]elp";
  899. }
  900. door::ANSIColor bracketColor(door::COLOR::YELLOW, door::COLOR::BLUE,
  901. door::ATTR::BOLD);
  902. door::ANSIColor outerColor(door::COLOR::CYAN, door::COLOR::BLUE,
  903. door::ATTR::BOLD);
  904. door::ANSIColor innerColor(door::COLOR::GREEN, door::COLOR::BLUE,
  905. door::ATTR::BOLD);
  906. door::renderFunction cmdRender =
  907. commandLineRender(bracketColor, innerColor, outerColor);
  908. door::Line cmd(commands, W);
  909. cmd.setRender(cmdRender);
  910. p->addLine(std::make_unique<door::Line>(cmd));
  911. return p;
  912. }
  913. std::unique_ptr<door::Panel> PlayCards::make_next_panel(void) {
  914. const int W = 50;
  915. std::unique_ptr<door::Panel> p = make_unique<door::Panel>(W);
  916. door::ANSIColor panelColor(door::COLOR::YELLOW, door::COLOR::GREEN,
  917. door::ATTR::BOLD);
  918. p->setStyle(door::BorderStyle::DOUBLE);
  919. p->setColor(panelColor);
  920. door::updateFunction nextUpdate = [this](void) -> std::string {
  921. std::string text;
  922. if (select_card == -1) {
  923. // winner
  924. if (hand < total_hands) {
  925. text = " [N]ext Hand - [Q]uit";
  926. } else
  927. text = " All hands played - [Q]uit";
  928. } else if (hand < total_hands) {
  929. text = " [C]ontinue this hand - [N]ext Hand - [Q]uit";
  930. } else {
  931. text = " [C]ontinue this hand - [Q]uit";
  932. }
  933. return text;
  934. };
  935. std::string text;
  936. text = nextUpdate();
  937. door::ANSIColor bracketColor(door::COLOR::YELLOW, door::COLOR::BLUE,
  938. door::ATTR::BOLD);
  939. door::ANSIColor innerColor(door::COLOR::CYAN, door::COLOR::BLUE,
  940. door::ATTR::BOLD);
  941. door::ANSIColor outerColor(door::COLOR::GREEN, door::COLOR::BLUE,
  942. door::ATTR::BOLD);
  943. door::renderFunction textRender =
  944. commandLineRender(bracketColor, innerColor, outerColor);
  945. door::Line nextLine(text, W);
  946. nextLine.setRender(textRender);
  947. nextLine.setPadding(" ", panelColor);
  948. nextLine.setUpdater(nextUpdate);
  949. nextLine.fit();
  950. p->addLine(std::make_unique<door::Line>(nextLine));
  951. return p;
  952. }
  953. std::unique_ptr<door::Panel> PlayCards::make_tripeaks(void) {
  954. std::string tripeaksText(" " SPACEACE
  955. " - Tri-Peaks Solitaire v" SPACEACE_VERSION " ");
  956. std::unique_ptr<door::Panel> spaceAceTriPeaks =
  957. std::make_unique<door::Panel>(tripeaksText.size());
  958. spaceAceTriPeaks->setStyle(door::BorderStyle::SINGLE);
  959. spaceAceTriPeaks->setColor(
  960. door::ANSIColor(door::COLOR::CYAN, door::COLOR::BLACK));
  961. spaceAceTriPeaks->addLine(
  962. std::make_unique<door::Line>(tripeaksText, tripeaksText.size()));
  963. return spaceAceTriPeaks;
  964. }
  965. std::unique_ptr<door::Panel> PlayCards::make_month(std::string month) {
  966. const int W = 3;
  967. std::unique_ptr<door::Panel> p = make_unique<door::Panel>(W);
  968. door::ANSIColor panelColor(door::COLOR::YELLOW, door::COLOR::BLACK,
  969. door::ATTR::BOLD);
  970. p->setStyle(door::BorderStyle::DOUBLE);
  971. p->setColor(panelColor);
  972. for (auto c : month) {
  973. std::string text = " ";
  974. text += c;
  975. text += " ";
  976. door::Line line(text);
  977. p->addLine(std::make_unique<door::Line>(line));
  978. }
  979. return p;
  980. }
  981. std::unique_ptr<door::Panel> PlayCards::make_weekdays(void) {
  982. const int W = 41;
  983. std::string text = " SUN MON TUE WED THU FRI SAT ";
  984. std::unique_ptr<door::Panel> p = make_unique<door::Panel>(W);
  985. door::ANSIColor panelColor(door::COLOR::CYAN, door::COLOR::BLACK,
  986. door::ATTR::BOLD);
  987. p->setStyle(door::BorderStyle::DOUBLE);
  988. p->setColor(panelColor);
  989. door::Line line(text);
  990. p->addLine(std::make_unique<door::Line>(line));
  991. return p;
  992. }
  993. std::unique_ptr<door::Panel> PlayCards::make_calendar_panel(void) {
  994. const int W = 41;
  995. std::unique_ptr<door::Panel> p = make_unique<door::Panel>(W);
  996. p->setStyle(door::BorderStyle::DOUBLE);
  997. p->setColor(door::ANSIColor(door::COLOR::CYAN, door::COLOR::BLACK));
  998. door::renderFunction calendarRender =
  999. [](const std::string &txt) -> door::Render {
  1000. door::Render r(txt);
  1001. // normal digits color
  1002. door::ANSIColor digits(door::COLOR::CYAN, door::COLOR::BLACK);
  1003. // digits/days that can be played
  1004. door::ANSIColor digits_play(door::COLOR::CYAN, door::COLOR::BLACK,
  1005. door::ATTR::BOLD);
  1006. // spaces color
  1007. door::ANSIColor spaces(door::COLOR::WHITE, door::COLOR::BLACK);
  1008. // o - open days
  1009. door::ANSIColor open(door::COLOR::GREEN, door::COLOR::BLACK,
  1010. door::ATTR::BOLD);
  1011. // h - hands can be played
  1012. door::ANSIColor hands(door::COLOR::YELLOW, door::COLOR::BLACK,
  1013. door::ATTR::BOLD);
  1014. // x - already played
  1015. door::ANSIColor full(door::COLOR::RED, door::COLOR::BLACK);
  1016. // u - No, Not Yet! unavailable
  1017. door::ANSIColor nny(door::COLOR::BLACK, door::COLOR::BLACK,
  1018. door::ATTR::BOLD);
  1019. int days;
  1020. /*
  1021. if (get_logger) {
  1022. get_logger() << "Line [" << txt << "]" << std::endl;
  1023. }
  1024. */
  1025. // B _ BBBB _ BBBB _ BBBB _ BBBB _ BBBB _ BBBB _ B
  1026. for (days = 0; days < 7; ++days) {
  1027. std::string dayText;
  1028. if (days == 0) {
  1029. r.append(spaces);
  1030. // dayText = txt.substr(1, 3);
  1031. } // else {
  1032. /*
  1033. if (get_logger)
  1034. get_logger() << days << " " << 1 + (days * 6) << std::endl;
  1035. */
  1036. dayText = txt.substr(1 + (days * 6), 3);
  1037. // }
  1038. /*
  1039. if (get_logger) {
  1040. get_logger() << days << " "
  1041. << "[" << dayText << "] " << std::endl;
  1042. }
  1043. */
  1044. if (dayText[1] == ' ') {
  1045. // Ok, nothing there!
  1046. r.append(spaces, 3);
  1047. } else {
  1048. // Something is there
  1049. char cday = dayText[2];
  1050. if ((cday == 'o') or (cday == 'h'))
  1051. r.append(digits_play, 2);
  1052. else
  1053. r.append(digits, 2);
  1054. switch (dayText[2]) {
  1055. case 'o':
  1056. r.append(open);
  1057. break;
  1058. case 'h':
  1059. r.append(hands);
  1060. break;
  1061. case 'x':
  1062. r.append(full);
  1063. break;
  1064. case 'u':
  1065. r.append(nny);
  1066. break;
  1067. }
  1068. }
  1069. if (days == 6)
  1070. r.append(spaces);
  1071. else
  1072. r.append(spaces, 3);
  1073. }
  1074. return r;
  1075. };
  1076. int row;
  1077. for (row = 0; row < 6; ++row) {
  1078. door::updateFunction calendarUpdate = [this, row]() -> std::string {
  1079. std::string text;
  1080. for (int d = 0; d < 7; ++d) {
  1081. text += " ";
  1082. int v = this->calendar_panel_days[(row * 7) + d];
  1083. if (v == 0)
  1084. text += " ";
  1085. else {
  1086. std::string number = std::to_string(v);
  1087. if (number.length() == 1)
  1088. text += " ";
  1089. text += number;
  1090. // Now, translate the status
  1091. int status = this->calendar_day_status[v - 1];
  1092. switch (status) {
  1093. case 0:
  1094. text += "o";
  1095. break;
  1096. case 1:
  1097. text += "h";
  1098. break;
  1099. case 2:
  1100. text += "x";
  1101. break;
  1102. case 3:
  1103. text += "u";
  1104. break;
  1105. }
  1106. }
  1107. if (d == 6)
  1108. text += " ";
  1109. else
  1110. text += " ";
  1111. }
  1112. return text;
  1113. };
  1114. std::string text;
  1115. text = calendarUpdate();
  1116. door::Line line(text, W);
  1117. line.setRender(calendarRender);
  1118. line.setUpdater(calendarUpdate);
  1119. p->addLine(std::make_unique<door::Line>(line));
  1120. }
  1121. return p;
  1122. }
  1123. /**
  1124. * @brief make calendar
  1125. * We assume the calendar is for this month now()
  1126. * Jaunary
  1127. * February
  1128. * March
  1129. * April
  1130. * May
  1131. * June
  1132. * July
  1133. * August
  1134. * September
  1135. * October
  1136. * November
  1137. * December
  1138. * 123456789012345678901234567890123456789012345678901234567890
  1139. * ▒▒░▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀░▒▒
  1140. * N ▒▒▌SUN MON TUE WED THU FRI SAT▐▒▒
  1141. * O ▒▒░───▄───▄───▄───▄───▄───▄───░▒▒
  1142. * V ▒▒▌ 1x│ 2x│ 3o│ 4o│ 5o│ 6o│ 7o▐▒▒ X = Day Completed
  1143. * E ▒▒▌ 8o│ 9x│10o│11o│12x│13x│14o▐▒▒ O = Day Playable
  1144. * M ▒▒▌15x│16u│17u│18u│19u│20u│21u▐▒▒ U = Day Unavailable
  1145. * B ▒▒▌22u│23u│24u│25u│26u│27u│28u▐▒▒ H = Day Uncompleted
  1146. * E ▒▒▌29u│30u│ │ │ │ │ ▐▒▒
  1147. * R ▒▒▌ │ │ │ │ │ │ ▐▒▒
  1148. * ▒▒░▄▄▄█▄▄▄█▄▄▄█▄▄▄█▄▄▄█▄▄▄█▄▄▄░▒▒
  1149. *
  1150. * 123456789012345678901234567890123456789012345678901234567890123456789012345
  1151. *
  1152. * ▒▒░▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀░▒▒
  1153. * N ▒▒▌ SUN MON TUE WED THU FRI SAT ▐▒▒
  1154. * O ▒▒░─────▄─────▄─────▄─────▄─────▄─────▄─────░▒▒
  1155. * V ▒▒▌ 1x │ 2x │ 3o │ 4o │ 5o │ 6o │ 7o ▐▒▒ X = Day Completed
  1156. * E ▒▒▌ 8o │ 9x │ 10o │ 11o │ 12x │ 13x │ 14o ▐▒▒ O = Day Playable
  1157. * M ▒▒▌ 15x │ 16u │ 17u │ 18u │ 19u │ 20u │ 21u ▐▒▒ U = Day Unavailable
  1158. * B ▒▒▌ 22u │ 23u │ 24u │ 25u │ 26u │ 27u │ 28u ▐▒▒ H = Day Uncompleted
  1159. * E ▒▒▌ 29u │ 30u │ │ │ │ │ ▐▒▒
  1160. * R ▒▒▌ │ │ │ │ │ │ ▐▒▒
  1161. * ▒▒░▄▄▄▄▄█▄▄▄▄▄█▄▄▄▄▄█▄▄▄▄▄█▄▄▄▄▄█▄▄▄▄▄█▄▄▄▄▄░▒▒
  1162. * X Extra Days Allowed Per Day
  1163. *
  1164. * 123456789012345678901234567890123456789012345678901234567890123456789012345
  1165. * ╔═══╗ ╔═════════════════════════════════════════╗
  1166. * ║ N ║ ║ SUN MON TUE WED THU FRI SAT ║
  1167. * ║ O ║ ╚═════════════════════════════════════════╝
  1168. * ║ V ║ ╔═════════════════════════════════════════╗
  1169. * ║ E ║ ║ 1x 2x 3o 4o 5o 6o 7o ║
  1170. * ║ M ║ ║ 8x 9x 10o 11o 12o 13o 14o ║
  1171. * ║ B ║ ║ 15h 16h 17h 18h 19u 20u 21u ║
  1172. * ║ E ║ ║ 22u 23u 24u 25u 26u 27u 28u ║
  1173. * ║ R ║ ║ 29u 30u 31u ║
  1174. * ╚═══╝ ║ ║
  1175. * ╚═════════════════════════════════════════╝
  1176. *
  1177. *
  1178. * Sunday
  1179. * Monday
  1180. * Tuesday
  1181. * Wednesday
  1182. * Thursday
  1183. * Friday
  1184. * Saturday
  1185. *
  1186. * 12345678901234567890123456789012345678901234567890123456789012345678901234567890
  1187. *
  1188. *╔═══╗╔═══════════════════════════════════════════════════════════════════════╗
  1189. *║ N ║║ Sunday Monday Tuesday Wednesday Thursday Friday Saturday
  1190. *║ ║ O
  1191. *║╚═══════════════════════════════════════════════════════════════════════╝
  1192. * 1 ╔══════╗ ╔══════╗ ╔══════╗ ╔══════╗ ╔══════╗ ╔══════╗ ╔══════╗
  1193. * ║ 1 x ║ ║ 2 x ║ ║ 3 o ║ ║ 4 o ║ ║ 5 o ║ ║ 6 o ║ ║ 7 o ║
  1194. * ╚══════╝ ╚══════╝ ╚══════╝ ╚══════╝ ╚══════╝ ╚══════╝ ╚══════╝
  1195. * 4 ╔══════╗ ╔══════╗ ╔══════╗ ╔══════╗ ╔══════╗ ╔══════╗ ╔══════╗
  1196. * ║ 8 x ║ ║ 9 x ║ ║ 10 o ║ ║ 11 o ║ ║ 12 o ║ ║ 13 o ║ ║ 14 o ║
  1197. * ╚══════╝ ╚══════╝ ╚══════╝ ╚══════╝ ╚══════╝ ╚══════╝ ╚══════╝
  1198. * 7 ╔══════╗ ╔══════╗ ╔══════╗ ╔══════╗ ╔══════╗ ╔══════╗ ╔══════╗
  1199. * ║ 15 x ║ ║ 16 x ║ ║ 17 o ║ ║ 18 o ║ ║ 19 o ║ ║ 20 o ║ ║ 21 o ║
  1200. * ╚══════╝ ╚══════╝ ╚══════╝ ╚══════╝ ╚══════╝ ╚══════╝ ╚══════╝
  1201. * 10 ╔══════╗ ╔══════╗ ╔══════╗ ╔══════╗ ╔══════╗ ╔══════╗ ╔══════╗
  1202. * ║ 22 x ║ ║ 23 x ║ ║ 24 o ║ ║ 25 o ║ ║ 26 o ║ ║ 27 o ║ ║ 28 o ║
  1203. * ╚══════╝ ╚══════╝ ╚══════╝ ╚══════╝ ╚══════╝ ╚══════╝ ╚══════╝
  1204. * 13 ╔══════╗ ╔══════╗ ╔══════╗
  1205. * ║ 29 x ║ ║ 30 u ║ ║ 31 u ║
  1206. * ╚══════╝ ╚══════╝ ╚══════╝
  1207. *
  1208. * +2 +12 +22 +32 +42 +52 +62
  1209. *
  1210. *
  1211. *
  1212. *
  1213. *
  1214. *
  1215. *
  1216. *
  1217. *
  1218. *
  1219. * 123456789012345678901234567890123456789012345678901234567890123456789012345
  1220. * ╔═══╗ ╔═════════════════════════════════════════════════════╗
  1221. * ║ N ║ ║ SUN MON TUE WED THU FRI SAT ║
  1222. * ║ O ║ ╚═════════════════════════════════════════════════════╝
  1223. * ║ V ║ ╔═════╗ ╔═════╗ ╔═════╗ ╔═════╗ ╔═════╗ ╔═════╗ ╔═════╗
  1224. * ║ E ║ ║ 1x ║ ║ 2x ║ ║ 3o ║ ║ 4o ║ ║ 5o ║ ║ 6o ║ ║ 7o ║
  1225. * ╚═════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚═════╝
  1226. * ╔═════╗ ╔═════╗ ╔═════╗ ╔═════╗ ╔═════╗ ╔═════╗ ╔═════╗
  1227. * ║ 8x ║ ║ 9x ║ ║ 10o ║ ║ 11o ║ ║ 12o ║ ║ 13o ║ ║ 14o ║
  1228. * ╚═════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚═════╝
  1229. * ╔═════╗ ╔═════╗ ╔═════╗ ╔═════╗ ╔═════╗ ╔═════╗ ╔═════╗
  1230. * ║ 15x ║ ║ 16x ║ ║ 17o ║ ║ 18o ║ ║ 19o ║ ║ 20o ║ ║ 21o ║
  1231. * ╚═════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚═════╝
  1232. * ╔═════╗ ╔═════╗ ╔═════╗ ╔═════╗ ╔═════╗ ╔═════╗ ╔═════╗
  1233. * ║ 22x ║ ║ 23x ║ ║ 24o ║ ║ 25o ║ ║ 26o ║ ║ 27o ║ ║ 28o ║
  1234. * ╚═════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚═════╝
  1235. * ╔═════╗ ╔═════╗ ╔═════╗
  1236. * ║ 29x ║ ║ 30u ║ ║ 31u ║
  1237. * ╚═════╝ ╚═════╝ ╚═════╝
  1238. *
  1239. * ^ Actually single lines, not double lines here ^
  1240. *
  1241. * ║ M ║ ║ 8x 9x 10o 11o 12o 13o 14o ║
  1242. * ║ B ║ ║ 15h 16h 17h 18h 19u 20u 21u ║
  1243. * ║ E ║ ║ 22u 23u 24u 25u 26u 27u 28u ║
  1244. * ║ R ║ ║ 29u 30u 31u ║
  1245. * ╚═══╝ ║ ║
  1246. * ╚═════════════════════════════════════════╝
  1247. *
  1248. *
  1249. *
  1250. * @return std::unique_ptr<door::Panel>
  1251. */
  1252. std::string
  1253. PlayCards::current_month(std::chrono::_V2::system_clock::time_point now) {
  1254. time_t now_t = std::chrono::system_clock::to_time_t(now);
  1255. std::tm *now_tm = localtime(&now_t);
  1256. ostringstream os;
  1257. std::string text;
  1258. os << std::put_time(now_tm, "%B");
  1259. text = os.str();
  1260. return text;
  1261. }
  1262. /**
  1263. * make_calendar
  1264. *
  1265. * This needs the screen size information so it can place the
  1266. * panels in the correct location.
  1267. */
  1268. std::unique_ptr<door::Screen> PlayCards::make_calendar() {
  1269. std::unique_ptr<door::Screen> s = std::make_unique<door::Screen>();
  1270. /*
  1271. * This has potential of jumping ahead if player is on after midnight.
  1272. * I'd rather this be stable. When they start the game, is where
  1273. * we count things. (Also last day of month + midnight -- means
  1274. * we need maint ran! No!)
  1275. */
  1276. auto month = std::chrono::system_clock::now();
  1277. time_t month_t = std::chrono::system_clock::to_time_t(month);
  1278. // adjust to first day of the month
  1279. std::tm month_lt;
  1280. localtime_r(&month_t, &month_lt);
  1281. int this_month = month_lt.tm_mon;
  1282. int this_day = month_lt.tm_mday;
  1283. int this_year = month_lt.tm_year + 1900;
  1284. if (month_lt.tm_mday > 1) {
  1285. month -= 24h * (month_lt.tm_mday - 1);
  1286. }
  1287. normalizeDate(month);
  1288. month_t = std::chrono::system_clock::to_time_t(month);
  1289. calendar_day_t.fill(0);
  1290. calendar_day_t[0] = month_t;
  1291. get_logger() << "1st of Month is "
  1292. << std::put_time(std::localtime(&month_t), "%c %Z") << std::endl;
  1293. localtime_r(&month_t, &month_lt);
  1294. const int FIRST_WEEKDAY = month_lt.tm_wday; // 0-6
  1295. get_logger() << "1st of the Month starts on " << FIRST_WEEKDAY << std::endl;
  1296. // find the last day of this month.
  1297. auto month_l = month;
  1298. time_t month_last_day_t = month_t;
  1299. std::tm mld_tm;
  1300. do {
  1301. month_l += 24h;
  1302. normalizeDate(month_l);
  1303. month_last_day_t = std::chrono::system_clock::to_time_t(month_l);
  1304. localtime_r(&month_last_day_t, &mld_tm);
  1305. if (mld_tm.tm_mday != 1) {
  1306. month_last_day = mld_tm.tm_mday;
  1307. calendar_day_t[month_last_day - 1] = month_last_day_t;
  1308. }
  1309. } while (mld_tm.tm_mday != 1);
  1310. /*
  1311. // increment the month, if > 11 (we've entered a new year)
  1312. mld_tm.tm_mon += 1;
  1313. if (mld_tm.tm_mon > 11) {
  1314. mld_tm.tm_mon = 0;
  1315. mld_tm.tm_year++;
  1316. }
  1317. month_last_day_t = std::mktime(&mld_tm);
  1318. // Ok, this should be the 1st of next month.
  1319. month_last_day_t -= (60 * 60 * 24);
  1320. localtime_r(&month_last_day_t, &mld_tm);
  1321. month_last_day = mld_tm.tm_mday;
  1322. */
  1323. get_logger() << "Last day is " << month_last_day << std::endl;
  1324. calendar_panel_days.fill(0);
  1325. int row = 0;
  1326. for (int x = 0; x < month_last_day; x++) {
  1327. int dow = (x + FIRST_WEEKDAY) % 7;
  1328. if ((x != 0) and (dow == 0))
  1329. row++;
  1330. /*
  1331. get_logger() << "x = " << x << " dow = " << dow << " row = " << row
  1332. << std::endl;
  1333. */
  1334. // we actually want x+1 (1- month_last_day)
  1335. // get_logger() << row * 7 + dow << " = " << x + 1 << std::endl;
  1336. calendar_panel_days[row * 7 + dow] = x + 1;
  1337. }
  1338. calendar_day_status.fill(0);
  1339. auto last_played = db.whenPlayed();
  1340. int play_days_ahead = 0;
  1341. if (config["play_days_ahead"]) {
  1342. play_days_ahead = config["play_days_ahead"].as<int>();
  1343. }
  1344. // until maint is setup, we need to verify that the month and year is
  1345. // correct.
  1346. for (auto played : last_played) {
  1347. get_logger() << "played " << played.first << " hands: " << played.second
  1348. << std::endl;
  1349. time_t play_t = played.first;
  1350. std::tm played_tm;
  1351. localtime_r(&play_t, &played_tm);
  1352. if (get_logger) {
  1353. get_logger() << played_tm.tm_mon + 1 << "/" << played_tm.tm_mday << "/"
  1354. << played_tm.tm_year + 1900 << " : " << played.second << " "
  1355. << play_t << std::endl;
  1356. }
  1357. if ((played_tm.tm_mon == this_month) &&
  1358. (played_tm.tm_year + 1900 == this_year)) {
  1359. // Ok!
  1360. int hands = played.second;
  1361. if (hands < total_hands) {
  1362. calendar_day_status[played_tm.tm_mday - 1] = 1;
  1363. } else {
  1364. if (hands >= total_hands) {
  1365. calendar_day_status[played_tm.tm_mday - 1] = 2;
  1366. }
  1367. }
  1368. }
  1369. }
  1370. if (get_logger) {
  1371. get_logger() << "last day " << month_last_day << " today " << this_day
  1372. << " plays ahead " << play_days_ahead << std::endl;
  1373. }
  1374. // mark days ahead as NNY.
  1375. for (int d = 0; d < month_last_day; ++d) {
  1376. if (this_day + play_days_ahead - 1 < d) {
  1377. calendar_day_status[d] = 3;
  1378. }
  1379. }
  1380. {
  1381. ofstream &of = get_logger();
  1382. of << "Calendar_panel_days:" << std::endl;
  1383. for (int x = 0; x < (int)calendar_panel_days.size(); ++x) {
  1384. of << std::setw(2) << calendar_panel_days[x] << ":";
  1385. int c = calendar_panel_days[x];
  1386. if (c == 0)
  1387. of << " ";
  1388. else {
  1389. of << calendar_day_status[c - 1];
  1390. };
  1391. of << " ";
  1392. if ((x != 0) and (((x + 1) % 7) == 0)) {
  1393. of << std::endl;
  1394. }
  1395. }
  1396. of << std::endl;
  1397. }
  1398. std::string current = current_month(month);
  1399. string_toupper(current);
  1400. if (current.length() < 6) {
  1401. current.insert(0, " ");
  1402. current += " ";
  1403. }
  1404. std::unique_ptr<door::Panel> p = make_month(current);
  1405. p->set(3, 3);
  1406. s->addPanel(std::move(p));
  1407. p = make_weekdays();
  1408. p->set(8, 3);
  1409. s->addPanel(std::move(p));
  1410. p = make_calendar_panel();
  1411. p->set(8, 6);
  1412. s->addPanel(std::move(p));
  1413. /*
  1414. const int W = 72;
  1415. p->setStyle(door::BorderStyle::NONE);
  1416. // Ok, that is working. I'm getting the first day of the month. So...
  1417. store the time_t for the date.
  1418. store the day in the column it needs to be in.
  1419. store any hands played (pull data from the db).
  1420. seems like this should be its own class, there's a lot of data that is
  1421. specific just to it.
  1422. */
  1423. /*
  1424. door::ANSIColor statusColor(door::COLOR::WHITE, door::COLOR::BLUE,
  1425. door::ATTR::BOLD);
  1426. door::ANSIColor valueColor(door::COLOR::YELLOW, door::COLOR::BLUE,
  1427. door::ATTR::BOLD);
  1428. door::renderFunction svRender = statusValue(statusColor, valueColor);
  1429. // or use renderStatus as defined above.
  1430. // We'll stick with these for now.
  1431. {
  1432. std::string userString = "Name: ";
  1433. userString += door.username;
  1434. door::Line username(userString, W);
  1435. username.setRender(svRender);
  1436. p->addLine(std::make_unique<door::Line>(username));
  1437. }
  1438. {
  1439. door::updateFunction scoreUpdate = [this](void) -> std::string {
  1440. std::string text = "Score: ";
  1441. text.append(std::to_string(score));
  1442. return text;
  1443. };
  1444. std::string scoreString = scoreUpdate();
  1445. door::Line scoreline(scoreString, W);
  1446. scoreline.setRender(svRender);
  1447. scoreline.setUpdater(scoreUpdate);
  1448. p->addLine(std::make_unique<door::Line>(scoreline));
  1449. }
  1450. {
  1451. door::updateFunction timeUpdate = [this](void) -> std::string {
  1452. std::stringstream ss;
  1453. std::string text;
  1454. ss << "Time used: " << setw(3) << door.time_used << " / " << setw(3)
  1455. << door.time_left;
  1456. text = ss.str();
  1457. return text;
  1458. };
  1459. std::string timeString = timeUpdate();
  1460. door::Line time(timeString, W);
  1461. time.setRender(svRender);
  1462. time.setUpdater(timeUpdate);
  1463. p->addLine(std::make_unique<door::Line>(time));
  1464. }
  1465. {
  1466. door::updateFunction handUpdate = [this](void) -> std::string {
  1467. std::string text = "Playing Hand ";
  1468. text.append(std::to_string(hand));
  1469. text.append(" of ");
  1470. text.append(std::to_string(total_hands));
  1471. return text;
  1472. };
  1473. std::string handString = handUpdate();
  1474. door::Line hands(handString, W);
  1475. hands.setRender(svRender);
  1476. hands.setUpdater(handUpdate);
  1477. p->addLine(std::make_unique<door::Line>(hands));
  1478. }
  1479. */
  1480. return s;
  1481. }