slotmachine.c 8.7 KB

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