slotmachine.c 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. #include <furi.h>
  2. #include <gui/gui.h>
  3. #include <gui/elements.h>
  4. #include <stdlib.h>
  5. #include <stdio.h>
  6. #include <input/input.h>
  7. #include <furi_hal.h>
  8. #include <slotmachine_icons.h>
  9. const Icon* slot_frames[] = {
  10. &I_x2,
  11. &I_x3,
  12. &I_x4,
  13. &I_x2_2
  14. };
  15. const uint8_t slot_coef[] = {
  16. 2,
  17. 3,
  18. 4,
  19. 2
  20. };
  21. typedef struct {
  22. uint8_t x, y, value, times, speed;
  23. bool spining;
  24. } SlotColumn;
  25. int COLUMNS_COUNT = 4;
  26. const MAX_COLUMNS_COUNT = 4;
  27. typedef struct {
  28. Gui* gui; // container gui
  29. ViewPort* view_port; // current viewport
  30. FuriMessageQueue* input_queue; // Input Events queue
  31. FuriMutex** model_mutex; // mutex for safe threads
  32. uint16_t bet;
  33. double money, winamount;
  34. SlotColumn* columns[4];
  35. bool winview;
  36. } SlotMachineApp;
  37. #define START_MONEY 1500;
  38. #define START_BET 100;
  39. #define RAND_MAX 4;
  40. #define DEFAULT_SPEED 16;
  41. uint8_t DEFAULT_SPINNING_TIMES = 10;
  42. void game_results(SlotMachineApp* app) {
  43. int matches[] = {
  44. 0,
  45. 0,
  46. 0,
  47. 0
  48. };
  49. double total = 0;
  50. for (int i = 0; i < COLUMNS_COUNT; i++) {
  51. matches[app->columns[i]->value]++;
  52. }
  53. for (int i = 0; i < 4; i++) {
  54. if (matches[i] >= 2) {
  55. total += app->bet * (slot_coef[i] / (double)(MAX_COLUMNS_COUNT + 1 - matches[i]));
  56. }
  57. }
  58. if (total > 0) {
  59. app->money += total;
  60. app->winamount = total;
  61. app->winview = true;
  62. }
  63. }
  64. void draw_container(Canvas* canvas) {
  65. canvas_draw_rframe(canvas, 2, 12, 120, 34, 3);
  66. canvas_draw_rframe(canvas, 2, 13, 120, 34, 3);
  67. canvas_draw_rframe(canvas, 2, 14, 120, 34, 3);
  68. canvas_draw_rframe(canvas, 2, 15, 120, 34, 3);
  69. canvas_draw_rframe(canvas, 2, 16, 120, 34, 3);
  70. canvas_draw_rframe(canvas, 2, 17, 120, 34, 3);
  71. canvas_draw_line(canvas, 31, 16, 31, 48);
  72. canvas_draw_line(canvas, 61, 16, 61, 48);
  73. canvas_draw_line(canvas, 91, 16, 91, 48);
  74. }
  75. // viewport callback
  76. void slotmachine_draw_callback(Canvas* canvas, void* ctx) {
  77. SlotMachineApp* slotmachine = (SlotMachineApp *)ctx;
  78. furi_check(furi_mutex_acquire(slotmachine->model_mutex, FuriWaitForever) == FuriStatusOk);
  79. canvas_set_font(canvas, FontPrimary);
  80. canvas_draw_str(canvas, 2, 10, "Slots");
  81. Icon* litl_icon = &I_little_coin;
  82. canvas_draw_icon(canvas, 30, 3, litl_icon);
  83. char moneyStr[15];
  84. snprintf(moneyStr, sizeof(moneyStr), "$%.0f", slotmachine->money);
  85. char betStr[7];
  86. snprintf(betStr, sizeof(betStr), "$%d", slotmachine->bet);
  87. canvas_set_font(canvas, FontSecondary);
  88. canvas_draw_str(canvas, 45, 10, moneyStr);
  89. canvas_draw_str(canvas, 2, canvas_height(canvas) - 3, "Bet:");
  90. canvas_draw_str(canvas, 18, canvas_height(canvas) - 3, betStr);
  91. if (slotmachine->winview) {
  92. char winamountStr[30];
  93. snprintf(winamountStr, sizeof(winamountStr), "You win: $%.2f!", slotmachine->winamount);
  94. canvas_set_font(canvas, FontPrimary);
  95. canvas_draw_str(canvas, 10, 30, winamountStr);
  96. elements_button_center(canvas, "Ok");
  97. furi_mutex_release(slotmachine->model_mutex);
  98. return;
  99. }
  100. draw_container(canvas);
  101. for (int i = 0; i < COLUMNS_COUNT; i++) {
  102. if (slotmachine->columns[i]->spining) {
  103. slotmachine->columns[i]->y += slotmachine->columns[i]->speed;
  104. if (slotmachine->columns[i]->y > 30) {
  105. slotmachine->columns[i]->y = 17;
  106. slotmachine->columns[i]->times--;
  107. slotmachine->columns[i]->speed--;
  108. slotmachine->columns[i]->value = rand() % RAND_MAX;
  109. if (slotmachine->columns[i]->times == 0) {
  110. slotmachine->columns[i]->y = 25;
  111. slotmachine->columns[i]->spining = false;
  112. if (i == COLUMNS_COUNT - 1) {
  113. game_results(slotmachine);
  114. }
  115. }
  116. if (i < COLUMNS_COUNT - 1 && slotmachine->columns[i]->times == (DEFAULT_SPINNING_TIMES - 2)) {
  117. slotmachine->columns[i + 1]->spining = true;
  118. }
  119. }
  120. }
  121. canvas_draw_icon(canvas, slotmachine->columns[i]->x, slotmachine->columns[i]->y, slot_frames[slotmachine->columns[i]->value]);
  122. }
  123. elements_button_center(canvas, "Spin");
  124. furi_mutex_release(slotmachine->model_mutex);
  125. }
  126. // коллбэк события ввода viewport
  127. static void slotmachine_input_callback(InputEvent* input_event, void* ctx) {
  128. SlotMachineApp* slotmachine = ctx;
  129. furi_message_queue_put(slotmachine->input_queue, input_event, FuriWaitForever);
  130. }
  131. // allocation memory and initialization
  132. SlotMachineApp* slotmachine_app_alloc() {
  133. SlotMachineApp* app = malloc(sizeof(SlotMachineApp));
  134. app->model_mutex = furi_mutex_alloc(FuriMutexTypeNormal);
  135. app->input_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
  136. app->view_port = view_port_alloc();
  137. view_port_draw_callback_set(app->view_port, slotmachine_draw_callback, app); // viewport callback register
  138. view_port_input_callback_set(app->view_port, slotmachine_input_callback, app);
  139. app->money = START_MONEY;
  140. app->bet = START_BET;
  141. app->winview = false;
  142. app->winamount = 0;
  143. int x = 10;
  144. for (int i = 0; i< COLUMNS_COUNT; i++) {
  145. app->columns[i] = malloc(sizeof(SlotColumn));
  146. app->columns[i]->x = x;
  147. app->columns[i]->y = 25;
  148. app->columns[i]->value = 0;
  149. app->columns[i]->spining = false;
  150. x += 30;
  151. }
  152. app->gui = furi_record_open("gui"); // start gui and adding viewport
  153. gui_add_view_port(app->gui, app->view_port, GuiLayerFullscreen);
  154. return app;
  155. }
  156. void slotmachine_app_free(SlotMachineApp* app) {
  157. gui_remove_view_port(app->gui, app->view_port);
  158. view_port_free(app->view_port);
  159. furi_record_close("gui"); // free memory
  160. furi_mutex_free(app->model_mutex);
  161. for (int i = 0; i< COLUMNS_COUNT; i++) {
  162. free(app->columns[i]);
  163. }
  164. free(app);
  165. }
  166. bool checkIsSpinning(SlotMachineApp* slotmachine) {
  167. for (int i = 0; i< COLUMNS_COUNT; i++) {
  168. if (slotmachine->columns[i]->spining) return true;
  169. }
  170. return false;
  171. }
  172. // entry point
  173. int32_t slotmachine_app(void* p) {
  174. UNUSED(p);
  175. SlotMachineApp* slotmachine = slotmachine_app_alloc();
  176. InputEvent input;
  177. // endless input cycle
  178. while(furi_message_queue_get(slotmachine->input_queue, &input, FuriWaitForever) == FuriStatusOk) {
  179. // if thread idle - take it
  180. furi_check(furi_mutex_acquire(slotmachine->model_mutex, FuriWaitForever) == FuriStatusOk);
  181. if (!checkIsSpinning(slotmachine)) {
  182. if (input.key == InputKeyBack) {
  183. // exit on back button
  184. furi_mutex_release(slotmachine->model_mutex);
  185. break;
  186. } else if (input.key == InputKeyOk && input.type == InputTypeShort && slotmachine->winview) {
  187. slotmachine->winview = false;
  188. } else if (input.key == InputKeyOk && input.type == InputTypeShort && slotmachine->bet <= slotmachine->money) {
  189. COLUMNS_COUNT = rand() % 3 + 2;
  190. slotmachine->money -= slotmachine->bet;
  191. slotmachine->columns[0]->spining = true;
  192. for (int i = 0; i< COLUMNS_COUNT; i++) {
  193. slotmachine->columns[i]->times = DEFAULT_SPINNING_TIMES;
  194. slotmachine->columns[i]->speed = DEFAULT_SPEED;
  195. }
  196. } else if (input.key == InputKeyUp) {
  197. if (slotmachine->bet + 10 < slotmachine->money) {
  198. slotmachine->bet += 10;
  199. }
  200. } else if (input.key == InputKeyDown) {
  201. if (slotmachine->bet - 10 > 0) {
  202. slotmachine->bet -= 10;
  203. }
  204. }
  205. }
  206. // release thread
  207. furi_mutex_release(slotmachine->model_mutex);
  208. // redraw viewport
  209. view_port_update(slotmachine->view_port);
  210. }
  211. slotmachine_app_free(slotmachine);
  212. return 0;
  213. }