yatzee.c 24 KB

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