ttt_multi_game_view.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  1. #include "ttt_multi_game_view.h"
  2. struct TttMultiGameView {
  3. View* view;
  4. };
  5. typedef struct {
  6. TttMultiGame* game;
  7. uint8_t position_x;
  8. uint8_t position_y;
  9. TttMultiGamePlayer local_player;
  10. bool is_local;
  11. TttMultiGameViewCallback callback;
  12. void* context;
  13. TttMultiGameMove* last_move;
  14. TttMultiGameResult result;
  15. } TttMultiGameViewModel;
  16. void ttt_multi_game_view_draw_callback(Canvas* canvas, void* _model) {
  17. TttMultiGameViewModel* model = _model;
  18. canvas_clear(canvas);
  19. const uint8_t top = 2;
  20. const uint8_t left = 4;
  21. const uint8_t cell_size = 20;
  22. const uint8_t padding = 4;
  23. canvas_draw_frame(canvas, left - 1, top - 1, 3 * cell_size + 2, 3 * cell_size + 2);
  24. for(uint8_t i = 0; i < 3; i++) {
  25. for(uint8_t j = 0; j < 3; j++) {
  26. canvas_draw_frame(
  27. canvas, left + i * cell_size, top + j * cell_size, cell_size, cell_size);
  28. }
  29. }
  30. for(uint8_t i = 0; i < 3; i++) {
  31. for(uint8_t j = 0; j < 3; j++) {
  32. bool is_selected = model->position_x == i && model->position_y == j &&
  33. ttt_multi_game_get_state(model->game) != TttMultiGameStateFinished;
  34. if(ttt_multi_game_player_at(model->game, i, j) == TttMultiGamePlayerX) {
  35. canvas_draw_line(
  36. canvas,
  37. left + i * cell_size + padding,
  38. top + j * cell_size + padding,
  39. left + i * cell_size + cell_size - padding,
  40. top + j * cell_size + cell_size - padding);
  41. canvas_draw_line(
  42. canvas,
  43. left + i * cell_size + cell_size - padding,
  44. top + j * cell_size + padding,
  45. left + i * cell_size + padding,
  46. top + j * cell_size + cell_size - padding);
  47. } else if(ttt_multi_game_player_at(model->game, i, j) == TttMultiGamePlayerO) {
  48. canvas_draw_circle(
  49. canvas,
  50. left + i * cell_size + cell_size / 2,
  51. top + j * cell_size + cell_size / 2,
  52. cell_size / 2 - padding);
  53. }
  54. if(is_selected) {
  55. canvas_draw_frame(
  56. canvas,
  57. left + i * cell_size + 1,
  58. top + j * cell_size + 1,
  59. cell_size - 2,
  60. cell_size - 2);
  61. }
  62. }
  63. }
  64. char* status_str = "";
  65. // TODO: Rewrite this
  66. if(model->is_local) {
  67. if(ttt_multi_game_get_state(model->game) == TttMultiGameStateFinished) {
  68. if(ttt_multi_game_get_result(model->game) == TttMultiGameResultXWin) {
  69. status_str = "X win";
  70. } else if(ttt_multi_game_get_result(model->game) == TttMultiGameResultOWin) {
  71. status_str = "O win";
  72. } else if(ttt_multi_game_get_result(model->game) == TttMultiGameResultDraw) {
  73. status_str = "Draw";
  74. }
  75. } else {
  76. if(ttt_multi_game_current_player(model->game) == TttMultiGamePlayerX) {
  77. status_str = "X turn";
  78. } else {
  79. status_str = "O turn";
  80. }
  81. }
  82. } else {
  83. if(ttt_multi_game_get_state(model->game) == TttMultiGameStateFinished) {
  84. if(model->local_player == TttMultiGamePlayerX) {
  85. if(ttt_multi_game_get_result(model->game) == TttMultiGameResultXWin) {
  86. status_str = "You win";
  87. } else if(ttt_multi_game_get_result(model->game) == TttMultiGameResultOWin) {
  88. status_str = "You lose";
  89. } else if(ttt_multi_game_get_result(model->game) == TttMultiGameResultDraw) {
  90. status_str = "Draw";
  91. }
  92. } else {
  93. if(ttt_multi_game_get_result(model->game) == TttMultiGameResultXWin) {
  94. status_str = "You lose";
  95. } else if(ttt_multi_game_get_result(model->game) == TttMultiGameResultOWin) {
  96. status_str = "You win";
  97. } else if(ttt_multi_game_get_result(model->game) == TttMultiGameResultDraw) {
  98. status_str = "Draw";
  99. }
  100. }
  101. } else {
  102. if(model->local_player == TttMultiGamePlayerX) {
  103. if(ttt_multi_game_get_state(model->game) == TttMultiGameStateTurnX) {
  104. status_str = "Your turn";
  105. } else {
  106. status_str = "Opponent turn";
  107. }
  108. } else {
  109. if(ttt_multi_game_get_state(model->game) == TttMultiGameStateTurnO) {
  110. status_str = "Your turn";
  111. } else {
  112. status_str = "Opponent turn";
  113. }
  114. }
  115. }
  116. }
  117. elements_text_box(canvas, 64, 16, 64, 16, AlignCenter, AlignCenter, status_str, false);
  118. if(ttt_multi_game_get_state(model->game) == TttMultiGameStateFinished) {
  119. elements_multiline_text_aligned(
  120. canvas, 100, 32, AlignCenter, AlignTop, "Press OK\nto restart");
  121. }
  122. }
  123. static void ttt_multi_game_view_process_left(TttMultiGameView* view);
  124. static void ttt_multi_game_view_process_right(TttMultiGameView* view);
  125. static void ttt_multi_game_view_process_up(TttMultiGameView* view);
  126. static void ttt_multi_game_view_process_down(TttMultiGameView* view);
  127. static void ttt_multi_game_view_process_ok(TttMultiGameView* view);
  128. static bool ttt_multi_game_view_input_callback(InputEvent* event, void* context) {
  129. furi_assert(context);
  130. TttMultiGameView* game_view = context;
  131. bool consumed = false;
  132. if(event->type == InputTypeShort) {
  133. switch(event->key) {
  134. case InputKeyLeft:
  135. ttt_multi_game_view_process_left(game_view);
  136. consumed = true;
  137. break;
  138. case InputKeyRight:
  139. ttt_multi_game_view_process_right(game_view);
  140. consumed = true;
  141. break;
  142. case InputKeyUp:
  143. ttt_multi_game_view_process_up(game_view);
  144. consumed = true;
  145. break;
  146. case InputKeyDown:
  147. ttt_multi_game_view_process_down(game_view);
  148. consumed = true;
  149. break;
  150. case InputKeyOk:
  151. ttt_multi_game_view_process_ok(game_view);
  152. consumed = true;
  153. break;
  154. default:
  155. break;
  156. }
  157. }
  158. return consumed;
  159. }
  160. void ttt_multi_game_view_model_reset(TttMultiGameViewModel* model) {
  161. furi_assert(model);
  162. furi_assert(model->game);
  163. model->position_x = 1;
  164. model->position_y = 1;
  165. ttt_multi_game_reset(model->game);
  166. }
  167. TttMultiGameView* ttt_multi_game_view_alloc() {
  168. TttMultiGameView* game_view = malloc(sizeof(TttMultiGameView));
  169. game_view->view = view_alloc();
  170. view_set_context(game_view->view, game_view);
  171. view_allocate_model(game_view->view, ViewModelTypeLocking, sizeof(TttMultiGameViewModel));
  172. view_set_draw_callback(game_view->view, (ViewDrawCallback)ttt_multi_game_view_draw_callback);
  173. view_set_input_callback(game_view->view, ttt_multi_game_view_input_callback);
  174. with_view_model(
  175. game_view->view,
  176. TttMultiGameViewModel * model,
  177. {
  178. model->game = ttt_multi_game_alloc();
  179. model->last_move = ttt_multi_game_move_alloc();
  180. ttt_multi_game_view_model_reset(model);
  181. },
  182. true);
  183. return game_view;
  184. }
  185. void ttt_multi_game_view_free(TttMultiGameView* game_view) {
  186. furi_assert(game_view);
  187. with_view_model(
  188. game_view->view,
  189. TttMultiGameViewModel * model,
  190. {
  191. ttt_multi_game_free(model->game);
  192. ttt_multi_game_move_free(model->last_move);
  193. },
  194. true);
  195. view_free(game_view->view);
  196. free(game_view);
  197. }
  198. View* ttt_multi_game_get_view(TttMultiGameView* game_view) {
  199. furi_assert(game_view);
  200. return game_view->view;
  201. }
  202. void ttt_multi_game_view_process_up(TttMultiGameView* game_view) {
  203. furi_assert(game_view);
  204. with_view_model(
  205. game_view->view,
  206. TttMultiGameViewModel * model,
  207. {
  208. if(model->position_y > 0) {
  209. model->position_y--;
  210. }
  211. },
  212. true);
  213. }
  214. void ttt_multi_game_view_process_down(TttMultiGameView* game_view) {
  215. furi_assert(game_view);
  216. with_view_model(
  217. game_view->view,
  218. TttMultiGameViewModel * model,
  219. {
  220. if(model->position_y < 2) {
  221. model->position_y++;
  222. }
  223. },
  224. true);
  225. }
  226. void ttt_multi_game_view_process_left(TttMultiGameView* game_view) {
  227. furi_assert(game_view);
  228. with_view_model(
  229. game_view->view,
  230. TttMultiGameViewModel * model,
  231. {
  232. if(model->position_x > 0) {
  233. model->position_x--;
  234. }
  235. },
  236. true);
  237. }
  238. void ttt_multi_game_view_process_right(TttMultiGameView* game_view) {
  239. furi_assert(game_view);
  240. with_view_model(
  241. game_view->view,
  242. TttMultiGameViewModel * model,
  243. {
  244. if(model->position_x < 2) {
  245. model->position_x++;
  246. }
  247. },
  248. true);
  249. }
  250. void ttt_multi_game_view_process_ok(TttMultiGameView* game_view) {
  251. furi_assert(game_view);
  252. TttMultiGameViewCallback callback = NULL;
  253. void* context = NULL;
  254. with_view_model(
  255. game_view->view,
  256. TttMultiGameViewModel * model,
  257. {
  258. if(ttt_multi_game_get_state(model->game) == TttMultiGameStateFinished) {
  259. ttt_multi_game_view_model_reset(model);
  260. } else {
  261. model->last_move->player = ttt_multi_game_current_player(model->game);
  262. model->last_move->x = model->position_x;
  263. model->last_move->y = model->position_y;
  264. if(ttt_multi_game_is_move_valid(model->game, model->last_move)) {
  265. callback = model->callback;
  266. context = model->context;
  267. }
  268. }
  269. },
  270. true);
  271. if(callback) {
  272. callback(context, TttMultiCustomEventGameMove);
  273. }
  274. }
  275. void ttt_multi_game_view_move(TttMultiGameView* game_view, TttMultiGameMove* move) {
  276. furi_assert(game_view);
  277. furi_assert(move);
  278. TttMultiGameResult result;
  279. TttMultiGameViewCallback callback = NULL;
  280. void* context = NULL;
  281. with_view_model(
  282. game_view->view,
  283. TttMultiGameViewModel * model,
  284. {
  285. ttt_multi_game_make_move(model->game, move);
  286. result = ttt_multi_game_get_result(model->game);
  287. if(result != TttMultiGameResultNone) {
  288. callback = model->callback;
  289. context = model->context;
  290. }
  291. },
  292. true);
  293. if(callback) {
  294. callback(context, TttMultiCustomEventGameFinish);
  295. }
  296. }
  297. void ttt_multi_game_view_set_callback(
  298. TttMultiGameView* game_view,
  299. TttMultiGameViewCallback callback,
  300. void* context) {
  301. furi_assert(game_view);
  302. with_view_model(
  303. game_view->view,
  304. TttMultiGameViewModel * model,
  305. {
  306. model->callback = callback;
  307. model->context = context;
  308. },
  309. true);
  310. }
  311. void ttt_multi_game_view_set_remote_play(TttMultiGameView* game_view, TttMultiGamePlayer player) {
  312. furi_assert(game_view);
  313. with_view_model(
  314. game_view->view,
  315. TttMultiGameViewModel * model,
  316. {
  317. model->local_player = player;
  318. model->is_local = false;
  319. },
  320. true);
  321. }
  322. void ttt_multi_game_view_set_local_play(TttMultiGameView* game_view) {
  323. furi_assert(game_view);
  324. with_view_model(
  325. game_view->view, TttMultiGameViewModel * model, { model->is_local = true; }, true);
  326. }
  327. void ttt_multi_game_view_get_last_move(TttMultiGameView* game_view, TttMultiGameMove* move) {
  328. furi_assert(game_view);
  329. furi_assert(move);
  330. with_view_model(
  331. game_view->view,
  332. TttMultiGameViewModel * model,
  333. {
  334. if(model->last_move->player != TttMultiGamePlayerNone) {
  335. ttt_multi_game_move_copy(move, model->last_move);
  336. }
  337. },
  338. true);
  339. }
  340. TttMultiGameResult ttt_multi_game_view_get_result(TttMultiGameView* game_view) {
  341. furi_assert(game_view);
  342. TttMultiGameResult result = TttMultiGameResultNone;
  343. with_view_model(
  344. game_view->view,
  345. TttMultiGameViewModel * model,
  346. { result = ttt_multi_game_get_result(model->game); },
  347. true);
  348. return result;
  349. }
  350. void ttt_multi_game_view_reset(TttMultiGameView* game_view) {
  351. furi_assert(game_view);
  352. with_view_model(
  353. game_view->view,
  354. TttMultiGameViewModel * model,
  355. { ttt_multi_game_view_model_reset(model); },
  356. true);
  357. }