deck.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555
  1. #include "deck.h"
  2. #include <algorithm>
  3. #include <sstream>
  4. Deck::Deck(int size) {
  5. cardback = door::ANSIColor(door::COLOR::RED);
  6. card_height = size;
  7. init();
  8. }
  9. Deck::Deck(door::ANSIColor backcolor, int size) : cardback{backcolor} {
  10. card_height = size;
  11. init();
  12. }
  13. void Deck::init(void) {
  14. if (card_height != 3) {
  15. if (card_height != 5) {
  16. card_height = 3;
  17. }
  18. }
  19. for (int i = 0; i < 52; ++i) {
  20. cards.push_back(card_of(i));
  21. }
  22. // 0 = BLANK, 1-4 levels
  23. for (int i = 0; i < 5; ++i) {
  24. backs.push_back(back_of(i));
  25. }
  26. mark.push_back(mark_of(0));
  27. mark.push_back(mark_of(1));
  28. }
  29. Deck::~Deck() {
  30. for (auto c : cards) {
  31. delete c;
  32. }
  33. cards.clear();
  34. for (auto b : backs) {
  35. delete b;
  36. }
  37. backs.clear();
  38. for (auto m : mark) {
  39. delete m;
  40. }
  41. mark.clear();
  42. }
  43. int Deck::is_deck(int c) { return c / 52; }
  44. int Deck::is_suit(int c) { return (c % 52) / 13; }
  45. int Deck::is_rank(int c) { return (c % 52) % 13; }
  46. char Deck::rank_symbol(int c) {
  47. const char symbols[] = "A23456789TJQK";
  48. return symbols[c];
  49. }
  50. std::string Deck::suit_symbol(int c) {
  51. // unicode
  52. if (door::unicode) {
  53. switch (c) {
  54. case 0:
  55. return std::string("\u2665");
  56. case 1:
  57. return std::string("\u2666");
  58. case 2:
  59. return std::string("\u2663");
  60. case 3:
  61. return std::string("\u2660");
  62. }
  63. } else {
  64. if (door::full_cp437) {
  65. switch (c) {
  66. case 0:
  67. return std::string(1, '\x03');
  68. case 1:
  69. return std::string(1, '\x04');
  70. case 2:
  71. return std::string(1, '\x05');
  72. case 3:
  73. return std::string(1, '\x06');
  74. }
  75. } else {
  76. // These look horrible!
  77. switch (c) {
  78. case 0:
  79. return std::string(1, '*'); // H
  80. case 1:
  81. return std::string(1, '^'); // D
  82. case 2:
  83. return std::string(1, '%'); // C
  84. case 3:
  85. return std::string(1, '$'); // S
  86. }
  87. }
  88. }
  89. return std::string("!", 1);
  90. }
  91. door::Panel *Deck::card_of(int c) {
  92. int suit = is_suit(c);
  93. int rank = is_rank(c);
  94. bool is_red = (suit < 2);
  95. door::ANSIColor color;
  96. if (is_red) {
  97. color = door::ANSIColor(door::COLOR::RED, door::COLOR::WHITE);
  98. } else {
  99. color = door::ANSIColor(door::COLOR::BLACK, door::COLOR::WHITE);
  100. }
  101. door::Panel *p = new door::Panel(0, 0, 5);
  102. // setColor sets border_color. NOT WHAT I WANT.
  103. // p->setColor(color);
  104. char r = rank_symbol(rank);
  105. std::string s = suit_symbol(suit);
  106. // build lines
  107. std::ostringstream oss;
  108. oss << r << s << " ";
  109. std::string str = oss.str();
  110. p->addLine(std::make_unique<door::Line>(str, 5, color));
  111. oss.str(std::string());
  112. oss.clear();
  113. if (card_height == 5)
  114. p->addLine(std::make_unique<door::Line>(" ", 5, color));
  115. oss << " " << s << " ";
  116. str = oss.str();
  117. p->addLine(std::make_unique<door::Line>(str, 5, color));
  118. oss.str(std::string());
  119. oss.clear();
  120. if (card_height == 5)
  121. p->addLine(std::make_unique<door::Line>(" ", 5, color));
  122. oss << " " << s << r;
  123. str = oss.str();
  124. p->addLine(std::make_unique<door::Line>(str, 5, color));
  125. oss.str(std::string());
  126. oss.clear();
  127. return p;
  128. }
  129. std::string Deck::back_char(int level) {
  130. std::string c;
  131. if (level == 0) {
  132. c = ' ';
  133. return c;
  134. }
  135. if (door::unicode) {
  136. switch (level) {
  137. case 1:
  138. c = "\u2591";
  139. break;
  140. case 2:
  141. c = "\u2592";
  142. break;
  143. case 3:
  144. c = "\u2593";
  145. break;
  146. case 4:
  147. c = "\u2588";
  148. break;
  149. }
  150. } else {
  151. switch (level) {
  152. case 1:
  153. c = "\xb0";
  154. break;
  155. case 2:
  156. c = "\xb1";
  157. break;
  158. case 3:
  159. c = "\xb2";
  160. break;
  161. case 4:
  162. c = "\xdb";
  163. break;
  164. }
  165. }
  166. return c;
  167. }
  168. door::Panel *Deck::back_of(int level) {
  169. // using: \xb0, 0xb1, 0xb2, 0xdb
  170. // OR: \u2591, \u2592, \u2593, \u2588
  171. // door::ANSIColor color(door::COLOR::RED, door::COLOR::BLACK);
  172. door::Panel *p = new door::Panel(0, 0, 5);
  173. std::string c = back_char(level);
  174. std::string l = c + c + c + c + c;
  175. for (int x = 0; x < card_height; ++x) {
  176. p->addLine(std::make_unique<door::Line>(l, 5, cardback));
  177. };
  178. // p->addLine(std::make_unique<door::Line>(l, 5, cardback));
  179. // p->addLine(std::make_unique<door::Line>(l, 5, cardback));
  180. return p;
  181. }
  182. door::Panel *Deck::mark_of(int c) {
  183. door::Panel *p = new door::Panel(1);
  184. door::ANSIColor color = door::ANSIColor(
  185. door::COLOR::BLUE, door::COLOR::WHITE); // , door::ATTR::BOLD);
  186. std::string m;
  187. if (c == 0)
  188. m = " ";
  189. else {
  190. if (door::unicode) {
  191. m = "\u25a0";
  192. } else {
  193. m = "\xfe";
  194. }
  195. }
  196. p->addLine(std::make_unique<door::Line>(m, 1, color));
  197. return p;
  198. }
  199. void Deck::part(int x, int y, door::Door &d, int level, bool left) {
  200. // Render part of the back of a card.
  201. y += 2;
  202. if (!left) {
  203. x += 2;
  204. }
  205. std::string c = back_char(level);
  206. std::string l = c + c + c;
  207. door::Goto g(x, y);
  208. d << g << cardback << l;
  209. }
  210. door::Panel *Deck::card(int c) { return cards[c]; }
  211. door::Panel *Deck::back(int level) { return backs[level]; }
  212. const std::array<std::pair<int, int>, 18> Deck::blocks = {
  213. make_pair(3, 4), make_pair(5, 6), make_pair(7, 8), // end row 1
  214. make_pair(9, 10), make_pair(10, 11), make_pair(12, 13),
  215. make_pair(13, 14), make_pair(15, 16), make_pair(16, 17),
  216. make_pair(18, 19), // end row 2
  217. make_pair(19, 20), make_pair(20, 21), make_pair(21, 22),
  218. make_pair(22, 23), make_pair(23, 24), make_pair(24, 25),
  219. make_pair(25, 26), make_pair(26, 27) // 27
  220. };
  221. /**
  222. * @brief Which card (if any) is unblocked by this card
  223. *
  224. * @param c
  225. * @return * int
  226. */
  227. std::vector<int> Deck::unblocks(int c) {
  228. std::vector<int> result;
  229. for (size_t i = 0; i < blocks.size(); ++i) {
  230. if ((blocks.at(i).first == c) || (blocks.at(i).second == c)) {
  231. result.push_back(i);
  232. }
  233. }
  234. return result;
  235. }
  236. bool Deck::can_play(int c1, int c2) {
  237. int s1, s2;
  238. s1 = is_rank(c1);
  239. s2 = is_rank(c2);
  240. // this works %13 handles wrap-around for us.
  241. if ((s1 + 1) % 13 == s2)
  242. return true;
  243. if (s1 == 0) {
  244. s1 += 13;
  245. }
  246. if (s1 - 1 == s2)
  247. return true;
  248. return false;
  249. }
  250. door::Panel *Deck::marker(int c) { return mark[c]; }
  251. void Deck::remove_card(door::Door &door, int c, int off_x, int off_y, bool left,
  252. bool right) {
  253. int cx, cy, level;
  254. cardgo(c, cx, cy, level);
  255. if (level > 1)
  256. --level;
  257. std::string cstr = back_char(level);
  258. door::Goto g(cx + off_x, cy + off_y);
  259. door << g << cardback;
  260. if (left)
  261. door << cstr;
  262. else
  263. door << " ";
  264. door << " ";
  265. if (right)
  266. door << cstr;
  267. else
  268. door << " ";
  269. g.set(cx + off_x, cy + off_y + 1);
  270. door << g << " ";
  271. g.set(cx + off_x, cy + off_y + 2);
  272. door << g << " ";
  273. }
  274. /*
  275. Layout spacing 1:
  276. 1 2 3 4 5 6
  277. 123456789012345678901234567890123456789012345678901234567890
  278. ░░░░░ ░░░░░ ░░░░░
  279. ░░░░░ ░░░░░ ░░░░░
  280. ▒▒▒▒▒░▒▒▒▒▒ #####░##### #####░#####
  281. ▒▒▒▒▒ ▒▒▒▒▒ ##### ##### ##### #####
  282. ▓▓▓▓▓▒▓▓▓▓▓▒▓▓▓▓▓ #####=#####=##### #####=#####=#####
  283. ▓▓▓▓▓ ▓▓▓▓▓ ▓▓▓▓▓ ##### ##### ##### ##### ##### #####
  284. █████▓█████▓█████▓#####=#####=#####=#####=#####=#####=#####
  285. █████ █████ █████ ##### ##### ##### ##### ##### ##### #####
  286. █████ █████ █████ ##### ##### ##### ##### ##### ##### #####
  287. width = 5 * 10 + (1*9) = 59 OK!
  288. Layout with spacing = 2:
  289. EEEEE
  290. ZZZZZ
  291. yyyyyZZZyyyyy
  292. yyyyy yyyyy
  293. XXXXXyyyXXXXXyyyXXXXX
  294. XXXXX XXXXX XXXXX
  295. width = 5 * 10 + (2 * 9) = 50+18 = 68 ! I could do that!
  296. */
  297. void cardgo(int pos, int space, int h, int &x, int &y, int &level) {
  298. // special cases here
  299. if (pos == 28) {
  300. // cardgo(23, space, h, x, y, level);
  301. cardgo(23, x, y, level);
  302. y += h + 1;
  303. --level;
  304. return;
  305. } else {
  306. if (pos == 29) {
  307. // cardgo(22, space, h, x, y, level);
  308. cardgo(22, x, y, level);
  309. y += h + 1;
  310. --level;
  311. return;
  312. }
  313. }
  314. const int CARD_WIDTH = 5;
  315. int HALF_WIDTH = 3;
  316. // space = 1 or 3
  317. // int space = 1;
  318. // space = 3;
  319. HALF_WIDTH += space / 2;
  320. /*
  321. int levels[4] = {3, 6, 9, 10};
  322. for (level = 0; level < 4; ++level) {
  323. if (pos < levels[level]) {
  324. level++;
  325. // we're here
  326. y = (level -1) * 2 + 1;
  327. } else {
  328. pos -= levels[level];
  329. }
  330. }
  331. */
  332. int between = CARD_WIDTH + space;
  333. if (pos < 3) {
  334. // top
  335. level = 1;
  336. y = (level - 1) * (h - 1) + 1;
  337. x = pos * (between * 3) + between + HALF_WIDTH + space; // 10
  338. return;
  339. } else {
  340. pos -= 3;
  341. }
  342. if (pos < 6) {
  343. level = 2;
  344. y = (level - 1) * (h - 1) + 1;
  345. int group = (pos) / 2;
  346. x = pos * between + (group * between) + CARD_WIDTH + space * 2;
  347. return;
  348. } else {
  349. pos -= 6;
  350. }
  351. if (pos < 9) {
  352. level = 3;
  353. y = (level - 1) * (h - 1) + 1;
  354. x = pos * between + HALF_WIDTH + space;
  355. return;
  356. } else {
  357. pos -= 9;
  358. }
  359. if (pos < 10) {
  360. level = 4;
  361. y = (level - 1) * (h - 1) + 1;
  362. x = (pos)*between + space;
  363. return;
  364. } else {
  365. // something is wrong.
  366. y = -1;
  367. x = -1;
  368. level = -1;
  369. }
  370. }
  371. /**
  372. * @brief Given card pos, calculate x, y, and level values.
  373. *
  374. * level is used to determine the card background gradient.
  375. *
  376. * @param pos
  377. * @param x
  378. * @param y
  379. * @param level
  380. */
  381. void cardgo(int pos, int &x, int &y, int &level) {
  382. const int space = 3;
  383. const int h = 3;
  384. // special cases here
  385. if (pos == 28) {
  386. cardgo(23, x, y, level);
  387. y += h + 1;
  388. --level;
  389. return;
  390. } else {
  391. if (pos == 29) {
  392. cardgo(22, x, y, level);
  393. y += h + 1;
  394. --level;
  395. return;
  396. }
  397. }
  398. const int CARD_WIDTH = 5;
  399. int HALF_WIDTH = 3;
  400. HALF_WIDTH += space / 2;
  401. int between = CARD_WIDTH + space;
  402. if (pos < 3) {
  403. // top
  404. level = 1;
  405. y = (level - 1) * (h - 1) + 1;
  406. x = pos * (between * 3) + between + HALF_WIDTH + space; // 10
  407. return;
  408. } else {
  409. pos -= 3;
  410. }
  411. if (pos < 6) {
  412. level = 2;
  413. y = (level - 1) * (h - 1) + 1;
  414. int group = (pos) / 2;
  415. x = pos * between + (group * between) + CARD_WIDTH + space * 2;
  416. return;
  417. } else {
  418. pos -= 6;
  419. }
  420. if (pos < 9) {
  421. level = 3;
  422. y = (level - 1) * (h - 1) + 1;
  423. x = pos * between + HALF_WIDTH + space;
  424. return;
  425. } else {
  426. pos -= 9;
  427. }
  428. if (pos < 10) {
  429. level = 4;
  430. y = (level - 1) * (h - 1) + 1;
  431. x = (pos)*between + space;
  432. return;
  433. } else {
  434. // something is wrong.
  435. y = -1;
  436. x = -1;
  437. level = -1;
  438. }
  439. }
  440. cards card_shuffle(std::seed_seq &seed, int decks) {
  441. std::mt19937 gen;
  442. // build deck of cards
  443. int size = decks * 52;
  444. std::vector<int> deck;
  445. deck.reserve(size);
  446. for (int x = 0; x < size; ++x) {
  447. deck.push_back(x);
  448. }
  449. // repeatable, but random
  450. gen.seed(seed);
  451. std::shuffle(deck.begin(), deck.end(), gen);
  452. return deck;
  453. }
  454. cards card_states(int decks) {
  455. // auto states = std::unique_ptr<std::vector<int>>(); // (decks * 52, 0)>;
  456. std::vector<int> states;
  457. states.assign(decks * 52, 0);
  458. return states;
  459. }
  460. /**
  461. * @brief Find the next card we can move the marker to.
  462. *
  463. * if left, look in the left - direction, otherwise the right + direction.
  464. * current is the current active card.
  465. * states is the card states (0 = down, 1 = in play, 2 = removed)
  466. *
  467. * @param left
  468. * @param states
  469. * @param current
  470. * @return int
  471. */
  472. int find_next(bool left, const cards &states, int current) {
  473. int cx, cy, level;
  474. int current_x;
  475. cardgo(current, cx, cy, level);
  476. current_x = cx;
  477. int x;
  478. int pos = -1;
  479. int pos_x;
  480. if (left)
  481. pos_x = 0;
  482. else
  483. pos_x = 100;
  484. for (x = 0; x < 28; x++) {
  485. if (states.at(x) == 1) {
  486. // possible location
  487. if (x == current)
  488. continue;
  489. cardgo(x, cx, cy, level);
  490. if (left) {
  491. if ((cx < current_x) and (cx > pos_x)) {
  492. pos_x = cx;
  493. pos = x;
  494. }
  495. } else {
  496. if ((cx > current_x) and (cx < pos_x)) {
  497. pos_x = cx;
  498. pos = x;
  499. }
  500. }
  501. }
  502. }
  503. return pos;
  504. }