yatzee.c 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827
  1. #include "yatzee_icons.h"
  2. #include <furi.h>
  3. #include <furi_hal.h>
  4. #include <gui/gui.h>
  5. #include <gui/elements.h>
  6. #include <input/input.h>
  7. #include <stdlib.h>
  8. #include <string.h>
  9. #define BASE_X 18
  10. #define BASE_Y 44
  11. #define DICE_OFFSET 12
  12. #define HOLD "*"
  13. #define MAX_DICE 5
  14. #define NUM_SCORES 13
  15. bool new_game = true;
  16. bool game_over = false;
  17. bool bonus_added = false;
  18. int8_t num_bonus_yatzees = 0;
  19. // struct to hold image posistion for dice
  20. typedef struct {
  21. // +-----x
  22. // |
  23. // |
  24. // y
  25. uint8_t x;
  26. uint8_t y;
  27. } ImagePosition;
  28. typedef struct {
  29. char* name;
  30. uint32_t value;
  31. bool used;
  32. int8_t row;
  33. int8_t col;
  34. uint8_t (*fn)(); // pointer to function that calculates score
  35. } Score;
  36. typedef struct {
  37. uint8_t index;
  38. uint8_t value;
  39. bool isHeld;
  40. } Die;
  41. typedef struct {
  42. int index;
  43. char* symbol;
  44. } Cursor;
  45. // locations for the dice images
  46. ImagePosition position[5] = {
  47. {.x = BASE_X - DICE_OFFSET, .y = BASE_Y},
  48. {.x = BASE_X * 2 - DICE_OFFSET, .y = BASE_Y},
  49. {.x = BASE_X * 3 - DICE_OFFSET, .y = BASE_Y},
  50. {.x = BASE_X * 4 - DICE_OFFSET, .y = BASE_Y},
  51. {.x = BASE_X * 5 - DICE_OFFSET, .y = BASE_Y},
  52. };
  53. // these are the positions that the score cursor will cycle through
  54. ImagePosition score_positions[13] = {
  55. {.x = 15, .y = 0},
  56. {.x = 15, .y = 9},
  57. {.x = 15, .y = 18},
  58. {.x = 15, .y = 27},
  59. {.x = 44, .y = 0},
  60. {.x = 44, .y = 9},
  61. {.x = 44, .y = 18},
  62. {.x = 44, .y = 27},
  63. {.x = 77, .y = 0},
  64. {.x = 77, .y = 9},
  65. {.x = 77, .y = 18},
  66. {.x = 77, .y = 27},
  67. {.x = 91, .y = 21},
  68. };
  69. // cursor to select dice
  70. Cursor cursor = {.index = 0, .symbol = "^"};
  71. // cursor to select score
  72. Cursor scoreCursor = {.index = -1, .symbol = "_"};
  73. // setup array to store dice info
  74. Die die[5] = {
  75. {.index = 0, .value = 1, .isHeld = false},
  76. {.index = 1, .value = 1, .isHeld = false},
  77. {.index = 2, .value = 1, .isHeld = false},
  78. {.index = 3, .value = 1, .isHeld = false},
  79. {.index = 4, .value = 1, .isHeld = false},
  80. };
  81. uint8_t upperScore = 0;
  82. int32_t lowerScore = 0;
  83. int32_t totalScore = 0;
  84. uint8_t roll = 0;
  85. uint8_t totalrolls = 0;
  86. // #############################################
  87. // # The following methods add the score for #
  88. // # whichever number is mentioned. #
  89. // #############################################
  90. static uint8_t ones() {
  91. uint8_t sum = 0;
  92. for(uint8_t i = 0; i < 5; i++) {
  93. if(die[i].value == 1) {
  94. sum++;
  95. }
  96. }
  97. return sum;
  98. }
  99. static uint8_t twos() {
  100. uint8_t sum = 0;
  101. for(uint8_t i = 0; i < 5; i++) {
  102. if(die[i].value == 2) {
  103. sum = sum + 2;
  104. }
  105. }
  106. return sum;
  107. }
  108. static uint8_t threes() {
  109. uint8_t sum = 0;
  110. for(uint8_t i = 0; i < 5; i++) {
  111. if(die[i].value == 3) {
  112. sum = sum + 3;
  113. }
  114. }
  115. return sum;
  116. }
  117. static uint8_t fours() {
  118. uint8_t sum = 0;
  119. for(uint8_t i = 0; i < 5; i++) {
  120. if(die[i].value == 4) {
  121. sum = sum + 4;
  122. }
  123. }
  124. return sum;
  125. }
  126. static uint8_t fives() {
  127. uint8_t sum = 0;
  128. for(uint8_t i = 0; i < 5; i++) {
  129. if(die[i].value == 5) {
  130. sum = sum + 5;
  131. }
  132. }
  133. return sum;
  134. }
  135. static uint8_t sixes() {
  136. uint8_t sum = 0;
  137. for(uint8_t i = 0; i < 5; i++) {
  138. if(die[i].value == 6) {
  139. sum = sum + 6;
  140. }
  141. }
  142. return sum;
  143. }
  144. // ####################################################
  145. // # Helper methods for the special score types #
  146. // # defined before them so they can be used #
  147. // # since this whole thing is a linear mess #
  148. // # lol. #
  149. // # add_dice: #
  150. // # inputs: none #
  151. // # output: int8_t value of roll #
  152. // # check_if_score_used:
  153. // # inputs: Score
  154. // # output: true if score.used = true
  155. // # # # # # # # # # # # # # # # # # # # # # # # # # #
  156. int8_t add_dice() {
  157. int8_t sum = 0;
  158. for(int8_t i = 0; i < MAX_DICE; i++) {
  159. sum += die[i].value;
  160. }
  161. return sum;
  162. }
  163. bool check_if_score_used(Score score) {
  164. if(score.used == true) {
  165. return true;
  166. } else {
  167. return false;
  168. }
  169. }
  170. // #############################################
  171. // # Methods to calculate scores for the fancy #
  172. // # scoring types: 3 of a kind, 4 of a kind, #
  173. // # Full house, small straight, large straight#
  174. // # chance & yatzee. #
  175. // #############################################
  176. static uint8_t threekind() {
  177. int8_t score = 0;
  178. for(int8_t num = 1; num < 7; num++) {
  179. int8_t sum = 0;
  180. for(int8_t i = 0; i < MAX_DICE; i++) {
  181. if(die[i].value == num) {
  182. sum++;
  183. }
  184. if(sum > 2) {
  185. score = add_dice();
  186. }
  187. }
  188. }
  189. return score;
  190. }
  191. static uint8_t fourkind() {
  192. int8_t score = 0;
  193. for(int8_t num = 1; num < 7; num++) {
  194. int8_t sum = 0;
  195. for(int8_t i = 0; i < MAX_DICE; i++) {
  196. if(die[i].value == num) {
  197. sum++;
  198. }
  199. if(sum > 3) {
  200. score = add_dice();
  201. }
  202. }
  203. }
  204. return score;
  205. }
  206. static uint8_t fullhouse() {
  207. bool check1 = false;
  208. bool check2 = false;
  209. int8_t val1 = 0;
  210. int8_t val2 = 0;
  211. UNUSED(val2);
  212. UNUSED(val1);
  213. //check 1 for three of a kind
  214. for(int8_t num = 1; num < 7; num++) {
  215. int8_t sum = 0;
  216. for(int8_t i = 0; i < MAX_DICE; i++) {
  217. if(die[i].value == num) {
  218. sum++;
  219. }
  220. if(sum > 2) {
  221. val1 = die[i].value;
  222. check1 = true;
  223. }
  224. }
  225. }
  226. // return if check 1 failed
  227. if(check1 == false) {
  228. return 0;
  229. }
  230. // check 2 for two of a kind.
  231. for(int8_t num = 1; num < 7; num++) {
  232. if(num == val1) {
  233. continue;
  234. }
  235. int8_t sum = 0;
  236. for(int8_t i = 0; i < MAX_DICE; i++) {
  237. if(die[i].value == num) {
  238. sum++;
  239. }
  240. if(sum > 1) {
  241. val2 = die[i].value;
  242. check2 = true;
  243. }
  244. }
  245. if(check1 && check2) {
  246. return 25;
  247. }
  248. }
  249. return 0;
  250. }
  251. // # # # # # # # # # # # # # # # # # # # # # # # # # # #
  252. // # I'm dumb so I asked ChatGPT to write the #
  253. // # smallstraight function for me. Then I adapted it #
  254. // # fo the largestraight function. #
  255. // # # # # # # # # # # # # # # # # # # # # # # # # # # #
  256. static uint8_t smallstraight() {
  257. // Create a new array with the frequencies of the different die faces
  258. int8_t frequencies[6] = {0};
  259. for(int8_t i = 0; i < 5; i++) {
  260. int8_t face = die[i].value;
  261. frequencies[face - 1]++;
  262. }
  263. // Check if there is a sequence of 4 consecutive die faces with at least one die
  264. bool found_small_straight = false;
  265. for(int i = 0; i < 3 && !found_small_straight; i++) {
  266. if(frequencies[i] > 0 && frequencies[i + 1] > 0 && frequencies[i + 2] > 0 &&
  267. frequencies[i + 3] > 0) {
  268. found_small_straight = true;
  269. }
  270. }
  271. if(found_small_straight) {
  272. return 30;
  273. } else {
  274. return 0;
  275. }
  276. }
  277. static uint8_t largestraight() {
  278. // Create a new array with the frequencies of the different die faces
  279. int8_t frequencies[6] = {0};
  280. for(int8_t i = 0; i < 5; i++) {
  281. int8_t face = die[i].value;
  282. frequencies[face - 1]++;
  283. }
  284. // Check if there is a sequence of 4 consecutive die faces with at least one die
  285. bool found_large_straight = false;
  286. for(int i = 0; i < 3 && !found_large_straight; i++) {
  287. if(frequencies[i] > 0 && frequencies[i + 1] > 0 && frequencies[i + 2] > 0 &&
  288. frequencies[i + 3] > 0 && frequencies[i + 4] > 0) {
  289. found_large_straight = true;
  290. }
  291. }
  292. if(found_large_straight) {
  293. return 40;
  294. } else {
  295. return 0;
  296. }
  297. }
  298. static uint8_t chance() {
  299. // chance allows your roll to count for the raw number of pips showing
  300. int8_t sum = 0;
  301. for(int8_t i = 0; i < MAX_DICE; i++) {
  302. sum += die[i].value;
  303. }
  304. return sum;
  305. }
  306. static uint8_t yatzee() {
  307. // checks if all die.values are equal to the first die
  308. int8_t val = die[0].value;
  309. for(int8_t i = 1; i < MAX_DICE; i++) {
  310. // if value is the same as the first die, continue to next
  311. if(die[i].value == val) {
  312. continue;
  313. } else {
  314. // if any value is not equal to the first die,
  315. // this is not a yatzee and we return 0
  316. return 0;
  317. }
  318. }
  319. return 50;
  320. }
  321. // # # # # # # # # # # # # # # # # # # # # # # # # # #
  322. // # Method to return true if yatzee returns 50 #
  323. // # #
  324. // # # # # # # # # # # # # # # # # # # # # # # # # # #
  325. // static bool check_for_bonus_yatzee() {
  326. // if (yatzee()==50){
  327. // return true;
  328. // } else {
  329. // return false;
  330. // }
  331. // }
  332. // Scorecard defined here so that we can use pointers to the functions
  333. // defined above
  334. Score scorecard[13] = {
  335. {.name = "1", .value = 0, .used = false, .row = 0, .col = 0, .fn = &ones},
  336. {.name = "2", .value = 0, .used = false, .row = 1, .col = 0, .fn = &twos},
  337. {.name = "3", .value = 0, .used = false, .row = 2, .col = 0, .fn = &threes},
  338. {.name = "4", .value = 0, .used = false, .row = 3, .col = 0, .fn = &fours},
  339. {.name = "5", .value = 0, .used = false, .row = 0, .col = 1, .fn = &fives},
  340. {.name = "6", .value = 0, .used = false, .row = 1, .col = 1, .fn = &sixes},
  341. {.name = "3k", .value = 0, .used = false, .row = 2, .col = 1, .fn = &threekind},
  342. {.name = "4k", .value = 0, .used = false, .row = 3, .col = 1, .fn = &fourkind},
  343. {.name = "Fh", .value = 0, .used = false, .row = 0, .col = 2, .fn = &fullhouse},
  344. {.name = "Sm", .value = 0, .used = false, .row = 1, .col = 2, .fn = &smallstraight},
  345. {.name = "Lg", .value = 0, .used = false, .row = 2, .col = 2, .fn = &largestraight},
  346. {.name = "Ch", .value = 0, .used = false, .row = 3, .col = 2, .fn = &chance},
  347. {.name = "Yz", .value = 0, .used = false, .row = 2, .col = 3, .fn = &yatzee},
  348. };
  349. // #############################################
  350. // # begin draw callback #
  351. // # #
  352. // #############################################
  353. // define the callback for telling ViewPort how to update the screen
  354. // not sure what ctx is but it seems important
  355. static void app_draw_callback(Canvas* canvas, void* ctx) {
  356. UNUSED(ctx);
  357. canvas_set_font(canvas, FontSecondary); //define a font so we can put letters on the screen
  358. int8_t selectorOffsetX = 8;
  359. int8_t selectorOffsetY = 16;
  360. char buffer[36];
  361. char bigbuffer[256];
  362. canvas_clear(canvas);
  363. // if new_game, show user instructions
  364. if(new_game) {
  365. canvas_set_font(canvas, FontPrimary);
  366. elements_multiline_text_aligned(canvas, 64, 0, AlignCenter, AlignTop, "Yatzee!");
  367. canvas_set_font(canvas, FontSecondary);
  368. snprintf(
  369. bigbuffer,
  370. sizeof(bigbuffer),
  371. "Up: Roll\nLeft/Right: Move cursor\nOK: Hold Die\nDown: Score");
  372. elements_multiline_text_aligned(canvas, 0, 8, AlignLeft, AlignTop, bigbuffer);
  373. elements_button_center(canvas, "Start!");
  374. return;
  375. } else {
  376. // draw border lines
  377. canvas_draw_line(canvas, 0, 37, 104, 37);
  378. canvas_draw_line(canvas, 104, 0, 104, 64);
  379. // iterate through dice and draw icon that correlates to die[n].value, and the x,y position indicated by position[die[i].index]
  380. for(int8_t i = 0; i < 5; i++) {
  381. if(die[i].value == 1) {
  382. canvas_draw_icon(
  383. canvas,
  384. position[die[i].index].x % 128,
  385. position[die[i].index].y % 64,
  386. &I_die_1);
  387. } else if(die[i].value == 2) {
  388. canvas_draw_icon(
  389. canvas,
  390. position[die[i].index].x % 128,
  391. position[die[i].index].y % 64,
  392. &I_die_2);
  393. } else if(die[i].value == 3) {
  394. canvas_draw_icon(
  395. canvas,
  396. position[die[i].index].x % 128,
  397. position[die[i].index].y % 64,
  398. &I_die_3);
  399. } else if(die[i].value == 4) {
  400. canvas_draw_icon(
  401. canvas,
  402. position[die[i].index].x % 128,
  403. position[die[i].index].y % 64,
  404. &I_die_4);
  405. } else if(die[i].value == 5) {
  406. canvas_draw_icon(
  407. canvas,
  408. position[die[i].index].x % 128,
  409. position[die[i].index].y % 64,
  410. &I_die_5);
  411. } else if(die[i].value == 6) {
  412. canvas_draw_icon(
  413. canvas,
  414. position[die[i].index].x % 128,
  415. position[die[i].index].y % 64,
  416. &I_die_6);
  417. }
  418. }
  419. // Puts an '*' above the die if hold is selected.
  420. int8_t holdOffsetX = 8;
  421. int8_t holdOffsetY = -5;
  422. for(int8_t i = 0; i < 5; i++) {
  423. if(die[i].isHeld == 1) {
  424. elements_multiline_text_aligned(
  425. canvas,
  426. position[die[i].index].x + holdOffsetX,
  427. position[die[i].index].y + holdOffsetY,
  428. AlignCenter,
  429. AlignTop,
  430. HOLD);
  431. }
  432. }
  433. // Update die cursor location
  434. if(cursor.index != -1) {
  435. elements_multiline_text_aligned(
  436. canvas,
  437. position[cursor.index].x + selectorOffsetX,
  438. position[cursor.index].y + selectorOffsetY,
  439. AlignCenter,
  440. AlignTop,
  441. cursor.symbol);
  442. }
  443. // Update score cursor location
  444. if(scoreCursor.index != -1) {
  445. elements_multiline_text_aligned(
  446. canvas,
  447. score_positions[scoreCursor.index].x,
  448. score_positions[scoreCursor.index].y + 1,
  449. AlignLeft,
  450. AlignTop,
  451. scoreCursor.symbol);
  452. }
  453. // Update Roll
  454. // Scores are updated in groups on screen to help with formatting
  455. // first group is scorecard[0:7], second group is [8:12]
  456. // Cycle through first 8 scores, if cursor at score, update to show possible score
  457. // otherwise, show current scores value.
  458. for(int8_t i = 0; i < 8; i++) {
  459. if(scoreCursor.index == i && scorecard[i].used == false) {
  460. int possiblescore = (int)(*scorecard[i].fn)();
  461. snprintf(buffer, sizeof(buffer), "%s: %3u ", scorecard[i].name, possiblescore);
  462. canvas_draw_str_aligned(
  463. canvas,
  464. 23 + 29 * scorecard[i].col,
  465. 9 * scorecard[i].row,
  466. AlignRight,
  467. AlignTop,
  468. buffer);
  469. } else {
  470. uint8_t currentscore = scorecard[i].value;
  471. snprintf(buffer, sizeof(buffer), "%s: %3u ", scorecard[i].name, currentscore);
  472. canvas_draw_str_aligned(
  473. canvas,
  474. 23 + 29 * scorecard[i].col,
  475. 9 * scorecard[i].row,
  476. AlignRight,
  477. AlignTop,
  478. buffer);
  479. }
  480. if(scorecard[i].used) {
  481. canvas_draw_dot(canvas, 23 + 29 * scorecard[i].col, 3 + 9 * scorecard[i].row);
  482. }
  483. }
  484. // cycle through lower scores
  485. // NUM_SCORES minus one because the yatzee is 12 and is handled separately
  486. for(int8_t i = 8; i < NUM_SCORES - 1; i++) {
  487. if(scoreCursor.index == i && scorecard[i].used == false) {
  488. int possiblescore = (int)(*scorecard[i].fn)();
  489. snprintf(buffer, sizeof(buffer), " %s: %3u ", scorecard[i].name, possiblescore);
  490. canvas_draw_str_aligned(
  491. canvas,
  492. 31 + 27 * scorecard[i].col,
  493. 9 * scorecard[i].row,
  494. AlignRight,
  495. AlignTop,
  496. buffer);
  497. } else {
  498. uint8_t currentscore = scorecard[i].value;
  499. snprintf(buffer, sizeof(buffer), " %s: %3u ", scorecard[i].name, currentscore);
  500. canvas_draw_str_aligned(
  501. canvas,
  502. 31 + 27 * scorecard[i].col,
  503. 9 * scorecard[i].row,
  504. AlignRight,
  505. AlignTop,
  506. buffer);
  507. }
  508. if(scorecard[i].used) {
  509. canvas_draw_dot(canvas, 31 + 27 * scorecard[i].col, 3 + 9 * scorecard[i].row);
  510. }
  511. }
  512. // update yatzee score
  513. if(scoreCursor.index == 12 && scorecard[12].used == false) {
  514. int possiblescore = (int)(*scorecard[12].fn)();
  515. snprintf(buffer, sizeof(buffer), "Yz\n%u", possiblescore);
  516. elements_multiline_text_aligned(canvas, 93, 10, AlignCenter, AlignTop, buffer);
  517. } else {
  518. snprintf(buffer, sizeof(buffer), "Yz\n%ld", scorecard[12].value);
  519. elements_multiline_text_aligned(canvas, 93, 10, AlignCenter, AlignTop, buffer);
  520. }
  521. // Scores and roll number updated
  522. // sub score shows the 1-6 scores only. If this is >63 at the end of the game,
  523. // a 35 point bonus is added to the total score
  524. snprintf(buffer, sizeof(buffer), "Sub\n%u", upperScore);
  525. elements_multiline_text_aligned(canvas, 117, 0, AlignCenter, AlignTop, buffer);
  526. snprintf(buffer, sizeof(buffer), "Total\n%ld", totalScore);
  527. elements_multiline_text_aligned(canvas, 117, 22, AlignCenter, AlignTop, buffer);
  528. if(totalrolls == 0) {
  529. snprintf(buffer, sizeof(buffer), "Roll\n%s", " ");
  530. elements_multiline_text_aligned(canvas, 117, 64, AlignCenter, AlignBottom, buffer);
  531. } else {
  532. snprintf(buffer, sizeof(buffer), "Roll\n%u", totalrolls);
  533. elements_multiline_text_aligned(canvas, 117, 64, AlignCenter, AlignBottom, buffer);
  534. }
  535. // Check for then handle end of game
  536. // add num_bonus_yatzees to total rounds so that multiple
  537. // yatzees can be scored without impacting the number of rounds before
  538. // the game is over
  539. int8_t total_rounds = num_bonus_yatzees;
  540. // add up number of scores counted so far
  541. for(int8_t i = 0; i < NUM_SCORES; i++) {
  542. if(scorecard[i].used) {
  543. total_rounds++;
  544. }
  545. }
  546. // if total rounds is 13 + the number of bonus rounds,
  547. // thats it, game over.
  548. if(total_rounds == NUM_SCORES + num_bonus_yatzees) {
  549. // if scores of 1-6 add up to 63, a 35 point bonus is bonus_added
  550. // bonus_added = true keeps the game loop from
  551. // adding bonuses indefinetly
  552. if(upperScore >= 63 && bonus_added == false) {
  553. totalScore += 35;
  554. bonus_added = true;
  555. }
  556. // set game over to true and tell the user the game is over
  557. game_over = true;
  558. elements_button_center(canvas, "Game Over");
  559. }
  560. }
  561. }
  562. // define the callback for helping ViewPort get InputEvent and place it in the event_queue defined in the main method
  563. static void app_input_callback(InputEvent* input_event, void* ctx) {
  564. furi_assert(ctx);
  565. FuriMessageQueue* event_queue = ctx;
  566. furi_message_queue_put(event_queue, input_event, FuriWaitForever);
  567. }
  568. // roll them diiiiceeee
  569. static void roll_dice() {
  570. // increment roll count
  571. totalrolls++;
  572. for(uint8_t i = 0; i < MAX_DICE; i++) {
  573. // dont reroll if the dice is being held
  574. if(die[i].isHeld == false) {
  575. die[i].value = 1 + rand() % 6;
  576. }
  577. }
  578. // if 3 rolls have been used, force user to select a score.
  579. if(totalrolls == 3) {
  580. scoreCursor.index = 0;
  581. }
  582. }
  583. static void clear_board() {
  584. // reset board after adding score
  585. totalrolls = 0;
  586. for(int8_t i = 0; i < MAX_DICE; i++) {
  587. die[i].isHeld = false;
  588. }
  589. scoreCursor.index = -1;
  590. cursor.index = 0;
  591. }
  592. static void add_score() {
  593. // return when scoring is not possible
  594. if(cursor.index != -1 || totalrolls == 0 ||
  595. (scorecard[scoreCursor.index].used &&
  596. strcmp(scorecard[scoreCursor.index].name, "Yz") != 0)) {
  597. return;
  598. }
  599. // extra yatzee scores
  600. if(scoreCursor.index == 12 && scorecard[scoreCursor.index].used) {
  601. uint8_t yatzee_score = (*scorecard[12].fn)();
  602. scorecard[12].value += 2 * yatzee_score;
  603. lowerScore += 100;
  604. num_bonus_yatzees++;
  605. }
  606. // upper score
  607. for(int8_t i = 0; i < 6; i++) {
  608. if(scoreCursor.index == i && scorecard[scoreCursor.index].used == false) {
  609. scorecard[i].value = (*scorecard[i].fn)();
  610. upperScore += scorecard[i].value;
  611. scorecard[i].used = true;
  612. }
  613. }
  614. // lower score
  615. for(int8_t i = 6; i < 13; i++) {
  616. if(scoreCursor.index == i && scorecard[scoreCursor.index].used == false) {
  617. scorecard[i].value = (*scorecard[i].fn)();
  618. lowerScore += scorecard[i].value;
  619. scorecard[i].used = true;
  620. }
  621. }
  622. // recalculate total score
  623. totalScore = lowerScore + upperScore;
  624. clear_board();
  625. }
  626. // Entry Point
  627. int32_t yatzee_main(void* p) {
  628. UNUSED(p);
  629. // Initialize event queue to handle incoming events like button presses
  630. // Use FuriMessageQueue as type as defined in furi api
  631. // InputEvents are supported by app_input_callback
  632. FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
  633. // Initialize viewport
  634. ViewPort* view_port = view_port_alloc();
  635. // Set system callbacks
  636. view_port_draw_callback_set(view_port, app_draw_callback, view_port);
  637. view_port_input_callback_set(view_port, app_input_callback, event_queue);
  638. // Open GUI & register viewport
  639. Gui* gui = furi_record_open(RECORD_GUI);
  640. gui_add_view_port(gui, view_port, GuiLayerFullscreen);
  641. // hold input event
  642. InputEvent event;
  643. // Create a loop for the app to run in and handle InputEvents
  644. bool isRunning = true;
  645. while(isRunning) {
  646. if(totalrolls == 3) {
  647. cursor.index = -1;
  648. }
  649. if(furi_message_queue_get(event_queue, &event, 100) == FuriStatusOk) {
  650. if((event.type == InputTypePress) || event.type == InputTypeRepeat) {
  651. switch(event.key) {
  652. case InputKeyLeft:
  653. if(cursor.index == -1) {
  654. if(scoreCursor.index == 0 && totalrolls == 3) {
  655. scoreCursor.index = NUM_SCORES - 1;
  656. } else if(scoreCursor.index == 0) {
  657. scoreCursor.index = -1;
  658. cursor.index = 4;
  659. } else {
  660. scoreCursor.index--;
  661. }
  662. } else {
  663. if(cursor.index == 0) {
  664. cursor.index = -1;
  665. scoreCursor.index = NUM_SCORES - 1;
  666. } else {
  667. cursor.index--;
  668. }
  669. }
  670. break;
  671. case InputKeyRight:
  672. // cursor.index == -1 means that scoreCursor is active
  673. if(cursor.index == -1) {
  674. if(scoreCursor.index == NUM_SCORES - 1 && totalrolls == 3) {
  675. scoreCursor.index = 0;
  676. } else if(scoreCursor.index == NUM_SCORES - 1) {
  677. scoreCursor.index = -1;
  678. cursor.index = 0;
  679. } else {
  680. scoreCursor.index++;
  681. }
  682. // if cursor.index is not -1, then dice cursor is active
  683. } else {
  684. if(cursor.index == 4) {
  685. cursor.index = -1;
  686. scoreCursor.index = 0;
  687. } else {
  688. cursor.index++;
  689. }
  690. }
  691. break;
  692. case InputKeyUp:
  693. if(totalrolls < 3) {
  694. roll_dice();
  695. }
  696. // if (check_for_bonus_yatzee() && scorecard[13].used) {
  697. // num_bonus_yatzees++;
  698. // totalScore+=100;
  699. //
  700. // clear_board();
  701. // }
  702. break;
  703. case InputKeyDown:
  704. add_score();
  705. break;
  706. case InputKeyOk:
  707. if(new_game) {
  708. new_game = false;
  709. break;
  710. }
  711. if(game_over) {
  712. isRunning = false;
  713. }
  714. if(cursor.index == -1 || totalrolls == 0) {
  715. break;
  716. }
  717. if(die[cursor.index].isHeld == false) {
  718. die[cursor.index].isHeld = true;
  719. } else {
  720. die[cursor.index].isHeld = false;
  721. }
  722. break;
  723. default:
  724. isRunning = false;
  725. break;
  726. }
  727. }
  728. }
  729. // after every event, update view_port
  730. // uses app_draw_callback which is set before the game loop begins.
  731. view_port_update(view_port);
  732. }
  733. // cleanup
  734. view_port_enabled_set(view_port, false);
  735. gui_remove_view_port(gui, view_port);
  736. view_port_free(view_port);
  737. furi_message_queue_free(event_queue);
  738. furi_record_close(RECORD_GUI);
  739. return 0;
  740. }