deck.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963
  1. #include "deck.h"
  2. #include <algorithm>
  3. #include <map>
  4. #include <sstream>
  5. Deck::Deck(door::ANSIColor backcolor) : card_back_color{backcolor} {
  6. for (int i = 0; i < 52; ++i) {
  7. cards.push_back(cardOf(i));
  8. }
  9. // 0 = BLANK, 1-4 levels
  10. for (int i = 0; i < 5; ++i) {
  11. backs.push_back(backOf(i));
  12. }
  13. mark.push_back(markOf(0));
  14. mark.push_back(markOf(1));
  15. }
  16. Deck::~Deck() {
  17. for (auto c : cards) {
  18. delete c;
  19. }
  20. cards.clear();
  21. for (auto b : backs) {
  22. delete b;
  23. }
  24. backs.clear();
  25. for (auto m : mark) {
  26. delete m;
  27. }
  28. mark.clear();
  29. }
  30. Deck::Deck(Deck &&ref) {
  31. card_back_color = ref.card_back_color;
  32. for (auto c : cards)
  33. delete c;
  34. cards.clear();
  35. cards = ref.cards;
  36. ref.cards.clear();
  37. for (auto b : backs)
  38. delete b;
  39. backs.clear();
  40. backs = ref.backs;
  41. ref.backs.clear();
  42. for (auto m : mark)
  43. delete m;
  44. mark.clear();
  45. mark = ref.mark;
  46. ref.mark.clear();
  47. // card_height = ref.card_height;
  48. };
  49. Deck &Deck::operator=(Deck &&ref) {
  50. card_back_color = ref.card_back_color;
  51. for (auto c : cards)
  52. delete c;
  53. cards.clear();
  54. cards = ref.cards;
  55. ref.cards.clear();
  56. for (auto b : backs)
  57. delete b;
  58. backs.clear();
  59. backs = ref.backs;
  60. ref.backs.clear();
  61. for (auto m : mark)
  62. delete m;
  63. mark.clear();
  64. mark = ref.mark;
  65. ref.mark.clear();
  66. // card_height = ref.card_height;
  67. return *this;
  68. }
  69. int Deck::getDeck(int c) { return c / 52; }
  70. int Deck::getSuit(int c) { return (c % 52) / 13; }
  71. int Deck::getRank(int c) { return (c % 52) % 13; }
  72. char Deck::rankSymbol(int c) {
  73. const char symbols[] = "A23456789TJQK";
  74. return symbols[c];
  75. }
  76. std::string Deck::suitSymbol(int c) {
  77. // unicode
  78. if (door::unicode) {
  79. switch (c) {
  80. case 0:
  81. return std::string("\u2665");
  82. case 1:
  83. return std::string("\u2666");
  84. case 2:
  85. return std::string("\u2663");
  86. case 3:
  87. return std::string("\u2660");
  88. }
  89. } else {
  90. if (door::full_cp437) {
  91. switch (c) {
  92. case 0:
  93. return std::string(1, '\x03');
  94. case 1:
  95. return std::string(1, '\x04');
  96. case 2:
  97. return std::string(1, '\x05');
  98. case 3:
  99. return std::string(1, '\x06');
  100. }
  101. } else {
  102. // These look horrible!
  103. switch (c) {
  104. case 0:
  105. return std::string(1, '*'); // H
  106. case 1:
  107. return std::string(1, '^'); // D
  108. case 2:
  109. return std::string(1, '%'); // C
  110. case 3:
  111. return std::string(1, '$'); // S
  112. }
  113. }
  114. }
  115. return std::string("!", 1);
  116. }
  117. door::Panel *Deck::cardOf(int c) {
  118. int suit = getSuit(c);
  119. int rank = getRank(c);
  120. bool is_red = (suit < 2);
  121. door::ANSIColor color;
  122. if (is_red) {
  123. color = door::ANSIColor(door::COLOR::RED, door::COLOR::WHITE);
  124. } else {
  125. color = door::ANSIColor(door::COLOR::BLACK, door::COLOR::WHITE);
  126. }
  127. door::Panel *p = new door::Panel(0, 0, 5);
  128. // setColor sets border_color. NOT WHAT I WANT.
  129. // p->setColor(color);
  130. char r = rankSymbol(rank);
  131. std::string s = suitSymbol(suit);
  132. // build lines
  133. std::ostringstream oss;
  134. oss << r << s << " ";
  135. std::string str = oss.str();
  136. p->addLine(std::make_unique<door::Line>(str, 5, color));
  137. oss.str(std::string());
  138. oss.clear();
  139. if (card_height == 5)
  140. p->addLine(std::make_unique<door::Line>(" ", 5, color));
  141. oss << " " << s << " ";
  142. str = oss.str();
  143. p->addLine(std::make_unique<door::Line>(str, 5, color));
  144. oss.str(std::string());
  145. oss.clear();
  146. if (card_height == 5)
  147. p->addLine(std::make_unique<door::Line>(" ", 5, color));
  148. oss << " " << s << r;
  149. str = oss.str();
  150. p->addLine(std::make_unique<door::Line>(str, 5, color));
  151. oss.str(std::string());
  152. oss.clear();
  153. return p;
  154. }
  155. std::string Deck::backSymbol(int level) {
  156. std::string c;
  157. if (level == 0) {
  158. c = ' ';
  159. return c;
  160. }
  161. if (door::unicode) {
  162. switch (level) {
  163. case 1:
  164. c = "\u2591";
  165. break;
  166. case 2:
  167. c = "\u2592";
  168. break;
  169. case 3:
  170. c = "\u2593";
  171. break;
  172. case 4:
  173. c = "\u2588";
  174. break;
  175. }
  176. } else {
  177. switch (level) {
  178. case 1:
  179. c = "\xb0";
  180. break;
  181. case 2:
  182. c = "\xb1";
  183. break;
  184. case 3:
  185. c = "\xb2";
  186. break;
  187. case 4:
  188. c = "\xdb";
  189. break;
  190. }
  191. }
  192. return c;
  193. }
  194. door::Panel *Deck::backOf(int level) {
  195. // using: \xb0, 0xb1, 0xb2, 0xdb
  196. // OR: \u2591, \u2592, \u2593, \u2588
  197. // door::ANSIColor color(door::COLOR::RED, door::COLOR::BLACK);
  198. door::Panel *p = new door::Panel(0, 0, 5);
  199. std::string c = backSymbol(level);
  200. std::string l = c + c + c + c + c;
  201. for (int x = 0; x < card_height; ++x) {
  202. p->addLine(std::make_unique<door::Line>(l, 5, card_back_color));
  203. };
  204. // p->addLine(std::make_unique<door::Line>(l, 5, card_back_color));
  205. // p->addLine(std::make_unique<door::Line>(l, 5, card_back_color));
  206. return p;
  207. }
  208. door::Panel *Deck::markOf(int c) {
  209. door::Panel *p = new door::Panel(1);
  210. door::ANSIColor color = door::ANSIColor(
  211. door::COLOR::BLUE, door::COLOR::WHITE); // , door::ATTR::BOLD);
  212. std::string m;
  213. if (c == 0)
  214. m = " ";
  215. else {
  216. if (door::unicode) {
  217. m = "\u25a0";
  218. } else {
  219. m = "\xfe";
  220. }
  221. }
  222. p->addLine(std::make_unique<door::Line>(m, 1, color));
  223. return p;
  224. }
  225. door::Panel *Deck::card(int c) { return cards[c]; }
  226. /**
  227. * @brief Return panel for back of card.
  228. *
  229. * 0 = Blank
  230. * 1 = level 1 (furthest/darkest)
  231. * 2 = level 2
  232. * 3 = level 3
  233. * 4 = level 4 (closest/lightest)
  234. *
  235. * 5 = left (fills with left corner in place)
  236. * 6 = right (fills right corner)
  237. * 7 = both (fills both corners)
  238. *
  239. * @param level
  240. * @return door::Panel*
  241. */
  242. door::Panel *Deck::back(int level) { return backs[level]; }
  243. const std::array<std::pair<int, int>, 18> Deck::blocks = {
  244. make_pair(3, 4), make_pair(5, 6), make_pair(7, 8), // end row 1
  245. make_pair(9, 10), make_pair(10, 11), make_pair(12, 13),
  246. make_pair(13, 14), make_pair(15, 16), make_pair(16, 17),
  247. make_pair(18, 19), // end row 2
  248. make_pair(19, 20), make_pair(20, 21), make_pair(21, 22),
  249. make_pair(22, 23), make_pair(23, 24), make_pair(24, 25),
  250. make_pair(25, 26), make_pair(26, 27) // 27
  251. };
  252. /**
  253. * @brief Which card (if any) is unblocked by this card
  254. *
  255. * @param card
  256. * @return * int
  257. */
  258. std::vector<int> Deck::unblocks(int card) {
  259. std::vector<int> result;
  260. for (size_t i = 0; i < blocks.size(); ++i) {
  261. if ((blocks.at(i).first == card) || (blocks.at(i).second == card)) {
  262. result.push_back(i);
  263. }
  264. }
  265. return result;
  266. }
  267. /**
  268. * @brief Can this card play on this other card?
  269. *
  270. * @param card1
  271. * @param card2
  272. * @return true
  273. * @return false
  274. */
  275. bool Deck::canPlay(int card1, int card2) {
  276. int rank1, rank2;
  277. rank1 = getRank(card1);
  278. rank2 = getRank(card2);
  279. // this works %13 handles wrap-around for us.
  280. if ((rank1 + 1) % 13 == rank2)
  281. return true;
  282. if (rank1 == 0) {
  283. rank1 += 13;
  284. }
  285. if (rank1 - 1 == rank2)
  286. return true;
  287. return false;
  288. }
  289. /**
  290. * @brief Returns marker
  291. *
  292. * 0 = blank
  293. * 1 = [] symbol thing \xfe ■
  294. *
  295. * @param c
  296. * @return door::Panel*
  297. */
  298. door::Panel *Deck::marker(int c) { return mark[c]; }
  299. /**
  300. * @brief removeCard
  301. *
  302. * This removes a card at a given position (c).
  303. * It needs to know if there are cards underneath
  304. * to the left or right. (If so, we restore those missing parts.)
  305. *
  306. * @param door
  307. * @param c
  308. * @param off_x
  309. * @param off_y
  310. * @param left
  311. * @param right
  312. */
  313. void Deck::removeCard(door::Door &door, int c, int off_x, int off_y, bool left,
  314. bool right) {
  315. int cx, cy, level;
  316. cardPosLevel(c, cx, cy, level);
  317. if (level > 1)
  318. --level;
  319. std::string cstr = backSymbol(level);
  320. door::Goto g(cx + off_x, cy + off_y);
  321. door << g << card_back_color;
  322. if (left)
  323. door << cstr;
  324. else
  325. door << " ";
  326. door << " ";
  327. if (right)
  328. door << cstr;
  329. else
  330. door << " ";
  331. g.set(cx + off_x, cy + off_y + 1);
  332. door << g << " ";
  333. g.set(cx + off_x, cy + off_y + 2);
  334. door << g << " ";
  335. }
  336. /*
  337. Layout spacing 1:
  338. 1 2 3 4 5 6
  339. 123456789012345678901234567890123456789012345678901234567890
  340. ░░░░░ ░░░░░ ░░░░░
  341. ░░░░░ ░░░░░ ░░░░░
  342. ▒▒▒▒▒░▒▒▒▒▒ #####░##### #####░#####
  343. ▒▒▒▒▒ ▒▒▒▒▒ ##### ##### ##### #####
  344. ▓▓▓▓▓▒▓▓▓▓▓▒▓▓▓▓▓ #####=#####=##### #####=#####=#####
  345. ▓▓▓▓▓ ▓▓▓▓▓ ▓▓▓▓▓ ##### ##### ##### ##### ##### #####
  346. █████▓█████▓█████▓#####=#####=#####=#####=#####=#####=#####
  347. █████ █████ █████ ##### ##### ##### ##### ##### ##### #####
  348. █████ █████ █████ ##### ##### ##### ##### ##### ##### #####
  349. width = 5 * 10 + (1*9) = 59 OK!
  350. Layout with spacing = 2:
  351. EEEEE
  352. ZZZZZ
  353. yyyyyZZZyyyyy
  354. yyyyy yyyyy
  355. XXXXXyyyXXXXXyyyXXXXX
  356. XXXXX XXXXX XXXXX
  357. width = 5 * 10 + (2 * 9) = 50+18 = 68 ! I could do that!
  358. */
  359. #ifdef NO
  360. /**
  361. * @brief Where does this card go in relation to everything else?
  362. *
  363. * This function is deprecated, see the other cardgo.
  364. *
  365. * @param pos
  366. * @param space
  367. * @param h
  368. * @param x
  369. * @param y
  370. * @param level
  371. */
  372. void cardgo(int pos, int space, int h, int &x, int &y, int &level) {
  373. // special cases here
  374. if (pos == 28) {
  375. // cardgo(23, space, h, x, y, level);
  376. cardgo(23, x, y, level);
  377. y += h + 1;
  378. --level;
  379. return;
  380. } else {
  381. if (pos == 29) {
  382. // cardgo(22, space, h, x, y, level);
  383. cardgo(22, x, y, level);
  384. y += h + 1;
  385. --level;
  386. return;
  387. }
  388. }
  389. const int CARD_WIDTH = 5;
  390. int HALF_WIDTH = 3;
  391. // space = 1 or 3
  392. // int space = 1;
  393. // space = 3;
  394. HALF_WIDTH += space / 2;
  395. /*
  396. int levels[4] = {3, 6, 9, 10};
  397. for (level = 0; level < 4; ++level) {
  398. if (pos < levels[level]) {
  399. level++;
  400. // we're here
  401. y = (level -1) * 2 + 1;
  402. } else {
  403. pos -= levels[level];
  404. }
  405. }
  406. */
  407. int between = CARD_WIDTH + space;
  408. if (pos < 3) {
  409. // top
  410. level = 1;
  411. y = (level - 1) * (h - 1) + 1;
  412. x = pos * (between * 3) + between + HALF_WIDTH + space; // 10
  413. return;
  414. } else {
  415. pos -= 3;
  416. }
  417. if (pos < 6) {
  418. level = 2;
  419. y = (level - 1) * (h - 1) + 1;
  420. int group = (pos) / 2;
  421. x = pos * between + (group * between) + CARD_WIDTH + space * 2;
  422. return;
  423. } else {
  424. pos -= 6;
  425. }
  426. if (pos < 9) {
  427. level = 3;
  428. y = (level - 1) * (h - 1) + 1;
  429. x = pos * between + HALF_WIDTH + space;
  430. return;
  431. } else {
  432. pos -= 9;
  433. }
  434. if (pos < 10) {
  435. level = 4;
  436. y = (level - 1) * (h - 1) + 1;
  437. x = (pos)*between + space;
  438. return;
  439. } else {
  440. // something is wrong.
  441. y = -1;
  442. x = -1;
  443. level = -1;
  444. }
  445. }
  446. #endif
  447. void cardPos(int pos, int &x, int &y) {
  448. const int space = 3;
  449. const int height = 3;
  450. // special cases here
  451. if (pos == 28) {
  452. cardPos(23, x, y);
  453. y += height + 1;
  454. return;
  455. } else {
  456. if (pos == 29) {
  457. cardPos(22, x, y);
  458. y += height + 1;
  459. return;
  460. }
  461. }
  462. const int CARD_WIDTH = 5;
  463. int HALF_WIDTH = 3;
  464. HALF_WIDTH += space / 2;
  465. int between = CARD_WIDTH + space;
  466. int level; // I still need level in my calculations
  467. if (pos < 3) {
  468. // top
  469. level = 1;
  470. y = (level - 1) * (height - 1) + 1;
  471. x = pos * (between * 3) + between + HALF_WIDTH + space; // 10
  472. return;
  473. } else {
  474. pos -= 3;
  475. }
  476. if (pos < 6) {
  477. level = 2;
  478. y = (level - 1) * (height - 1) + 1;
  479. int group = (pos) / 2;
  480. x = pos * between + (group * between) + CARD_WIDTH + space * 2;
  481. return;
  482. } else {
  483. pos -= 6;
  484. }
  485. if (pos < 9) {
  486. level = 3;
  487. y = (level - 1) * (height - 1) + 1;
  488. x = pos * between + HALF_WIDTH + space;
  489. return;
  490. } else {
  491. pos -= 9;
  492. }
  493. if (pos < 10) {
  494. level = 4;
  495. y = (level - 1) * (height - 1) + 1;
  496. x = (pos)*between + space;
  497. return;
  498. } else {
  499. // something is wrong.
  500. y = -1;
  501. x = -1;
  502. level = -1;
  503. }
  504. }
  505. void cardLevel(int pos, int &level) {
  506. /*
  507. const int space = 3;
  508. const int height = 3;
  509. */
  510. // special cases here
  511. if (pos == 28) {
  512. cardLevel(23, level);
  513. --level;
  514. return;
  515. } else {
  516. if (pos == 29) {
  517. cardLevel(22, level);
  518. --level;
  519. return;
  520. }
  521. }
  522. /*
  523. const int CARD_WIDTH = 5;
  524. int HALF_WIDTH = 3;
  525. HALF_WIDTH += space / 2;
  526. int between = CARD_WIDTH + space;
  527. */
  528. if (pos < 3) {
  529. // top
  530. level = 1;
  531. return;
  532. } else {
  533. pos -= 3;
  534. }
  535. if (pos < 6) {
  536. level = 2;
  537. return;
  538. } else {
  539. pos -= 6;
  540. }
  541. if (pos < 9) {
  542. level = 3;
  543. return;
  544. } else {
  545. pos -= 9;
  546. }
  547. if (pos < 10) {
  548. level = 4;
  549. return;
  550. } else {
  551. // something is wrong.
  552. level = -1;
  553. }
  554. }
  555. /**
  556. * @brief Given card pos, calculate x, y, and level values.
  557. *
  558. * level is used to determine the card background gradient.
  559. *
  560. * @param pos
  561. * @param x
  562. * @param y
  563. * @param level
  564. */
  565. void cardPosLevel(int pos, int &x, int &y, int &level) {
  566. const int space = 3;
  567. const int height = 3;
  568. // special cases here
  569. if (pos == 28) {
  570. cardPosLevel(23, x, y, level);
  571. y += height + 1;
  572. --level;
  573. return;
  574. } else {
  575. if (pos == 29) {
  576. cardPosLevel(22, x, y, level);
  577. y += height + 1;
  578. --level;
  579. return;
  580. }
  581. }
  582. const int CARD_WIDTH = 5;
  583. int HALF_WIDTH = 3;
  584. HALF_WIDTH += space / 2;
  585. int between = CARD_WIDTH + space;
  586. if (pos < 3) {
  587. // top
  588. level = 1;
  589. y = (level - 1) * (height - 1) + 1;
  590. x = pos * (between * 3) + between + HALF_WIDTH + space; // 10
  591. return;
  592. } else {
  593. pos -= 3;
  594. }
  595. if (pos < 6) {
  596. level = 2;
  597. y = (level - 1) * (height - 1) + 1;
  598. int group = (pos) / 2;
  599. x = pos * between + (group * between) + CARD_WIDTH + space * 2;
  600. return;
  601. } else {
  602. pos -= 6;
  603. }
  604. if (pos < 9) {
  605. level = 3;
  606. y = (level - 1) * (height - 1) + 1;
  607. x = pos * between + HALF_WIDTH + space;
  608. return;
  609. } else {
  610. pos -= 9;
  611. }
  612. if (pos < 10) {
  613. level = 4;
  614. y = (level - 1) * (height - 1) + 1;
  615. x = (pos)*between + space;
  616. return;
  617. } else {
  618. // something is wrong.
  619. y = -1;
  620. x = -1;
  621. level = -1;
  622. }
  623. }
  624. /**
  625. * @brief shuffle deck of cards
  626. *
  627. * example of seeding the deck for a given date 2/27/2021 game 1
  628. * std::seed_seq s1{2021, 2, 27, 1};
  629. * vector<int> deck1 = shuffleCards(s1, 1);
  630. * @param seed
  631. * @param decks
  632. * @return vector<int>
  633. */
  634. cards shuffleCards(std::seed_seq &seed, int decks) {
  635. std::mt19937 gen;
  636. // build deck of cards
  637. int size = decks * 52;
  638. std::vector<int> deck;
  639. deck.reserve(size);
  640. for (int x = 0; x < size; ++x) {
  641. deck.push_back(x);
  642. }
  643. // repeatable, but random
  644. gen.seed(seed);
  645. std::shuffle(deck.begin(), deck.end(), gen);
  646. return deck;
  647. }
  648. /**
  649. * @brief generate a vector of ints to track card states.
  650. *
  651. * This initializes everything to 0.
  652. *
  653. * @param decks
  654. * @return cards
  655. */
  656. cards makeCardStates(int decks) {
  657. // auto states = std::unique_ptr<std::vector<int>>(); // (decks * 52, 0)>;
  658. std::vector<int> states;
  659. states.assign(decks * 52, 0);
  660. return states;
  661. }
  662. /**
  663. * @brief Find the next card we can move the marker to.
  664. *
  665. * if left, look in the left - direction, otherwise the right + direction.
  666. * current is the current active card.
  667. * states is the card states (0 = down, 1 = in play, 2 = removed)
  668. *
  669. * updated: If we can't go any further left (or right), then
  670. * roll around to the other side.
  671. *
  672. * @param left
  673. * @param states
  674. * @param current
  675. * @return int
  676. */
  677. int findNextActiveCard(bool left, const cards &states, int current) {
  678. int cx, cy;
  679. int current_x;
  680. cardPos(current, cx, cy);
  681. current_x = cx;
  682. int x;
  683. int pos = -1;
  684. int pos_x;
  685. int max_pos = -1;
  686. int max_x = -1;
  687. int min_pos = -1;
  688. int min_x = 100;
  689. if (left)
  690. pos_x = 0;
  691. else
  692. pos_x = 100;
  693. for (x = 0; x < 28; x++) {
  694. if (states.at(x) == 1) {
  695. // possible location
  696. if (x == current)
  697. continue;
  698. cardPos(x, cx, cy);
  699. // find max and min while we're iterating here
  700. if (cx < min_x) {
  701. min_pos = x;
  702. min_x = cx;
  703. }
  704. if (cx > max_x) {
  705. max_pos = x;
  706. max_x = cx;
  707. }
  708. if (left) {
  709. if ((cx < current_x) and (cx > pos_x)) {
  710. pos_x = cx;
  711. pos = x;
  712. }
  713. } else {
  714. if ((cx > current_x) and (cx < pos_x)) {
  715. pos_x = cx;
  716. pos = x;
  717. }
  718. }
  719. }
  720. }
  721. if (pos == -1) {
  722. // we couldn't find one
  723. if (left) {
  724. // use max -- roll around to the right
  725. pos = max_pos;
  726. } else {
  727. // use min -- roll around to the left
  728. pos = min_pos;
  729. }
  730. }
  731. return pos;
  732. }
  733. /**
  734. * @brief Find the next closest card to move to.
  735. *
  736. * Given the card states, this finds the next closest card.
  737. * Uses current.
  738. *
  739. * return -1 there's no options to go to. (END OF GAME)
  740. * @param states
  741. * @param current
  742. * @return int
  743. */
  744. int findClosestActiveCard(const cards &states, int current) {
  745. int cx, cy;
  746. int current_x;
  747. cardPos(current, cx, cy);
  748. current_x = cx;
  749. int x;
  750. int pos = -1;
  751. int pos_x = -1;
  752. for (x = 0; x < 28; x++) {
  753. if (states.at(x) == 1) {
  754. // possible location
  755. if (x == current)
  756. continue;
  757. cardPos(x, cx, cy);
  758. if (pos == -1) {
  759. pos = x;
  760. pos_x = cx;
  761. } else {
  762. if (abs(current_x - cx) < abs(current_x - pos_x)) {
  763. pos = x;
  764. pos_x = cx;
  765. }
  766. }
  767. }
  768. }
  769. return pos;
  770. }
  771. vector<std::string> deck_colors = {std::string("All"), std::string("Blue"),
  772. std::string("Cyan"), std::string("Green"),
  773. std::string("Magenta"), std::string("Red")};
  774. /**
  775. * @brief menu render that sets the text color based on the color found in the
  776. * text itself.
  777. *
  778. * @param c1 [] brackets
  779. * @param c2 text within brackets
  780. * @param c3 base color give (we set the FG, we use the BG)
  781. * @return door::renderFunction
  782. */
  783. door::renderFunction makeColorRender(door::ANSIColor c1, door::ANSIColor c2,
  784. door::ANSIColor c3) {
  785. door::renderFunction render = [c1, c2,
  786. c3](const std::string &txt) -> door::Render {
  787. door::Render r(txt);
  788. bool option = true;
  789. door::ColorOutput co;
  790. // I need this mutable
  791. door::ANSIColor textColor = c3;
  792. // Color update:
  793. {
  794. std::string found;
  795. for (auto &dc : deck_colors) {
  796. if (txt.find(dc) != string::npos) {
  797. found = dc;
  798. break;
  799. }
  800. }
  801. if (!found.empty()) {
  802. if (found == "All") {
  803. // handle this some other way.
  804. textColor.setFg(door::COLOR::WHITE);
  805. } else {
  806. door::ANSIColor c = stringToANSIColor(found);
  807. textColor.setFg(c.getFg());
  808. }
  809. }
  810. }
  811. co.pos = 0;
  812. co.len = 0;
  813. co.c = c1;
  814. int tpos = 0;
  815. for (char const &c : txt) {
  816. if (option) {
  817. if (c == '[' or c == ']') {
  818. if (co.c != c1)
  819. if (co.len != 0) {
  820. r.outputs.push_back(co);
  821. co.reset();
  822. co.pos = tpos;
  823. }
  824. co.c = c1;
  825. if (c == ']')
  826. option = false;
  827. } else {
  828. if (co.c != c2)
  829. if (co.len != 0) {
  830. r.outputs.push_back(co);
  831. co.reset();
  832. co.pos = tpos;
  833. }
  834. co.c = c2;
  835. }
  836. } else {
  837. if (co.c != textColor)
  838. if (co.len != 0) {
  839. r.outputs.push_back(co);
  840. co.reset();
  841. co.pos = tpos;
  842. }
  843. co.c = textColor;
  844. }
  845. co.len++;
  846. tpos++;
  847. }
  848. if (co.len != 0) {
  849. r.outputs.push_back(co);
  850. }
  851. return r;
  852. };
  853. return render;
  854. }
  855. // convert a string to an option
  856. // an option to the string to store
  857. // This needs to be updated to use deck_colors.
  858. door::ANSIColor stringToANSIColor(std::string colorCode) {
  859. std::map<std::string, door::ANSIColor> codeMap = {
  860. {std::string("BLUE"), door::ANSIColor(door::COLOR::BLUE)},
  861. {std::string("RED"), door::ANSIColor(door::COLOR::RED)},
  862. {std::string("CYAN"), door::ANSIColor(door::COLOR::CYAN)},
  863. {std::string("GREEN"), door::ANSIColor(door::COLOR::GREEN)},
  864. {std::string("MAGENTA"), door::ANSIColor(door::COLOR::MAGENTA)}};
  865. std::string code = colorCode;
  866. string_toupper(code);
  867. auto iter = codeMap.find(code);
  868. if (iter != codeMap.end()) {
  869. return iter->second;
  870. }
  871. // And if it doesn't match, and isn't ALL ... ?
  872. // if (code.compare("ALL") == 0) {
  873. std::random_device dev;
  874. std::mt19937_64 rng(dev());
  875. std::uniform_int_distribution<size_t> idDist(0, codeMap.size() - 1);
  876. iter = codeMap.begin();
  877. std::advance(iter, idDist(rng));
  878. return iter->second;
  879. // }
  880. }
  881. std::string stringFromColorOptions(int opt) { return deck_colors[opt]; }
  882. void string_toupper(std::string &str) {
  883. std::transform(str.begin(), str.end(), str.begin(), ::toupper);
  884. }