Jelajahi Sumber

upd minesweeper

MX 2 tahun lalu
induk
melakukan
d50f8b7b05

+ 1 - 1
base_pack/minesweeper/application.fam

@@ -16,7 +16,7 @@ App(
     fap_icon="assets/minesweeper.png",  # 10x10 1-bit PNG
     fap_category="Games",
     # Optional values
-    fap_version="1.1",
+    fap_version="1.2",
     fap_description="Flipper Zero Minesweeper Implementation",
     fap_author="Alexander Rodriguez",
     fap_weburl="https://github.com/squee72564/F0_Minesweeper_Fap",

+ 8 - 23
base_pack/minesweeper/docs/changelog.md

@@ -1,32 +1,17 @@
-
 # Changelog
-## TODO:
-- Add settings options to toggle hardware feedback
-- Maybe take a look at the board verifier algo and try to make faster/multi-thread or anything to allow better maps
+
+## Version 1.2.0 - 1/25/2024
+
+Added ability to toggle sound and haptic feedback.
+
+Minor reformats and fixes.
 
 ## Version 1.1.0 - 1/11/2024
 
 Added haptic / led functionality
 
-## Added
-- Haptic feedback on all button presses.
-    - Out of bounds movement
-    - Ok to clear tiles
-    - Holding back for flags
-    - Different haptic feedback on win/loss
-- LED changes on win loss
-    - Initially LED is just reset
-    - Set to red on loss
-    - Set to blue on win
-- Sound on some presses
+Code Reformats
 
 ## Version 1.0.0 - 1/10/2024
 
-Initial Full release for the game.
-
-## Added
-- Mine sweeper game with settings menu to choose variable board dimensions from 16x7 to 32x32.
-- Ability to set difficulty, width, and height for the game board in settings.
-- Ability to enable solvable boards only
-- The core game functionality is complete for the game with multiple types of button presses registered for different inputs.
-- The game will save your settings (besides "enable solvable") when you leave the game.
+Initial Full release for the game.

+ 10 - 5
base_pack/minesweeper/helpers/mine_sweeper_haptic.c

@@ -5,7 +5,8 @@
 void mine_sweeper_play_happy_bump(void* context) {
     MineSweeperApp* app = context;
 
-    notification_message(app->notification, &sequence_set_vibro_on);
+    if (app->feedback_enabled)
+        notification_message(app->notification, &sequence_set_vibro_on);
     furi_thread_flags_wait(0, FuriFlagWaitAny, 20);
     notification_message(app->notification, &sequence_reset_vibro);
 }
@@ -14,7 +15,8 @@ void mine_sweeper_play_long_ok_bump(void* context) {
     MineSweeperApp* app = context;
 
     for (int i = 0; i < 2; i++) {
-        notification_message(app->notification, &sequence_set_vibro_on);
+        if (app->feedback_enabled)
+            notification_message(app->notification, &sequence_set_vibro_on);
         furi_thread_flags_wait(0, FuriFlagWaitAny, 20);
         notification_message(app->notification, &sequence_reset_vibro);
         furi_thread_flags_wait(0, FuriFlagWaitAny, 20);
@@ -24,7 +26,8 @@ void mine_sweeper_play_long_ok_bump(void* context) {
 void mine_sweeper_play_oob_bump(void* context) {
     MineSweeperApp* app = context;
 
-    notification_message(app->notification, &sequence_set_vibro_on);
+    if (app->feedback_enabled)
+        notification_message(app->notification, &sequence_set_vibro_on);
     furi_thread_flags_wait(0, FuriFlagWaitAny, 20);
     notification_message(app->notification, &sequence_reset_vibro);
 }
@@ -32,7 +35,8 @@ void mine_sweeper_play_oob_bump(void* context) {
 void mine_sweeper_play_lose_bump(void* context) {
     MineSweeperApp* app = context;
 
-    notification_message(app->notification, &sequence_set_vibro_on);
+    if (app->feedback_enabled)
+        notification_message(app->notification, &sequence_set_vibro_on);
     furi_thread_flags_wait(0, FuriFlagWaitAny, 100);
     notification_message(app->notification, &sequence_reset_vibro);
     furi_thread_flags_wait(0, FuriFlagWaitAny, 400);
@@ -42,7 +46,8 @@ void mine_sweeper_play_win_bump(void* context) {
     MineSweeperApp* app = context;
 
     for (int i = 0; i < 4; i++) {
-        notification_message(app->notification, &sequence_set_vibro_on);
+        if (app->feedback_enabled)
+            notification_message(app->notification, &sequence_set_vibro_on);
         furi_thread_flags_wait(0, FuriFlagWaitAny, 50);
         notification_message(app->notification, &sequence_reset_vibro);
         furi_thread_flags_wait(0, FuriFlagWaitAny, 100);

+ 23 - 6
base_pack/minesweeper/helpers/mine_sweeper_speaker.c

@@ -5,7 +5,10 @@ static const float volume = 0.8f;
 
 void mine_sweeper_play_ok_sound(void* context) {
     MineSweeperApp* app = context;
-    UNUSED(app);
+    
+    if (!app->feedback_enabled) {
+        return;
+    }
 
     if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) {
         furi_hal_speaker_start(NOTE_LOSE, volume);
@@ -15,7 +18,10 @@ void mine_sweeper_play_ok_sound(void* context) {
 
 void mine_sweeper_play_flag_sound(void* context) {
     MineSweeperApp* app = context;
-    UNUSED(app);
+
+    if (!app->feedback_enabled) {
+        return;
+    }
 
     if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) {
         furi_hal_speaker_start(NOTE_FLAG, volume);
@@ -25,7 +31,10 @@ void mine_sweeper_play_flag_sound(void* context) {
 
 void mine_sweeper_play_oob_sound(void* context) {
     MineSweeperApp* app = context;
-    UNUSED(app);
+
+    if (!app->feedback_enabled) {
+        return;
+    }
 
     if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) {
         furi_hal_speaker_start(NOTE_OOB, volume);
@@ -35,7 +44,10 @@ void mine_sweeper_play_oob_sound(void* context) {
 
 void mine_sweeper_play_win_sound(void* context) {
     MineSweeperApp* app = context;
-    UNUSED(app);
+
+    if (!app->feedback_enabled) {
+        return;
+    }
 
     if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) {
         furi_hal_speaker_start(NOTE_WIN, volume);
@@ -45,8 +57,10 @@ void mine_sweeper_play_win_sound(void* context) {
 
 void mine_sweeper_play_lose_sound(void* context) {
     MineSweeperApp* app = context;
-    UNUSED(app);
 
+    if (!app->feedback_enabled) {
+        return;
+    }
     if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) {
         furi_hal_speaker_start(NOTE_LOSE, volume);
     }
@@ -55,7 +69,10 @@ void mine_sweeper_play_lose_sound(void* context) {
 
 void mine_sweeper_stop_all_sound(void* context) {
     MineSweeperApp* app = context;
-    UNUSED(app);
+
+    if (!app->feedback_enabled) {
+        return;
+    }
 
     if(furi_hal_speaker_is_mine()) {
         furi_hal_speaker_stop();

+ 12 - 13
base_pack/minesweeper/helpers/mine_sweeper_storage.c

@@ -49,14 +49,11 @@ void mine_sweeper_save_settings(void* context) {
     // Store Settings
     flipper_format_write_header_cstr(
         fff_file, MINESWEEPER_SETTINGS_HEADER, MINESWEEPER_SETTINGS_FILE_VERSION);
-    flipper_format_write_uint32(
-        fff_file, MINESWEEPER_SETTINGS_KEY_HAPTIC, &app->haptic, 1);
-    flipper_format_write_uint32(
-        fff_file, MINESWEEPER_SETTINGS_KEY_SPEAKER, &app->speaker, 1);
-    flipper_format_write_uint32(
-        fff_file, MINESWEEPER_SETTINGS_KEY_LED, &app->led, 1);
 
-    uint32_t w = app->settings_info.board_width, h = app->settings_info.board_height, d = app->settings_info.difficulty;
+    uint32_t w = app->settings_info.board_width,
+             h = app->settings_info.board_height,
+             d = app->settings_info.difficulty,
+             f = app->feedback_enabled;
 
     flipper_format_write_uint32(
         fff_file, MINESWEEPER_SETTINGS_KEY_WIDTH, &w, 1);
@@ -64,6 +61,8 @@ void mine_sweeper_save_settings(void* context) {
         fff_file, MINESWEEPER_SETTINGS_KEY_HEIGHT, &h, 1);
     flipper_format_write_uint32(
         fff_file, MINESWEEPER_SETTINGS_KEY_DIFFICULTY, &d, 1);
+    flipper_format_write_uint32(
+        fff_file, MINESWEEPER_SETTINGS_KEY_FEEDBACK, &f, 1);
     
     if(!flipper_format_rewind(fff_file)) {
         FURI_LOG_E(TAG, "Rewind error");
@@ -115,24 +114,24 @@ bool mine_sweeper_read_settings(void* context) {
         return false;
     }
 
-    uint32_t w = 7, h = 16, d = 0;
+    uint32_t w = 7, h = 16, d = 0, f = 1;
     flipper_format_read_uint32(fff_file, MINESWEEPER_SETTINGS_KEY_WIDTH, &w, 1);
     flipper_format_read_uint32(fff_file, MINESWEEPER_SETTINGS_KEY_HEIGHT, &h, 1);
     flipper_format_read_uint32(fff_file, MINESWEEPER_SETTINGS_KEY_DIFFICULTY, &d, 1);
+    flipper_format_read_uint32(fff_file, MINESWEEPER_SETTINGS_KEY_FEEDBACK, &f, 1);
 
-    if (w > 146) {w = 146;}
+    if (w > 32) {w = 32;}
     if (w < 16 ) {w = 16;}
-    if (h > 64 ) {h = 64;}
+    if (h > 32 ) {h = 32;}
     if (h < 7  ) {h = 7;}
     if (d > 2 ) {d = 2;}
+    if (f > 1) {f = 1;}
 
     app->settings_info.board_width = (uint8_t) w;
     app->settings_info.board_height = (uint8_t) h;
     app->settings_info.difficulty = (uint8_t) d;
+    app->feedback_enabled = (uint8_t) f;
 
-    flipper_format_read_uint32(fff_file, MINESWEEPER_SETTINGS_KEY_HAPTIC, &app->haptic, 1);
-    flipper_format_read_uint32(fff_file, MINESWEEPER_SETTINGS_KEY_SPEAKER, &app->speaker, 1);
-    flipper_format_read_uint32(fff_file, MINESWEEPER_SETTINGS_KEY_LED, &app->led, 1);
 
     flipper_format_rewind(fff_file);
 

+ 7 - 9
base_pack/minesweeper/helpers/mine_sweeper_storage.h

@@ -7,21 +7,19 @@
 #include <flipper_format/flipper_format.h>
 #include "../minesweeper.h"
 
-#define MINESWEEPER_SETTINGS_FILE_VERSION 1
-#define CONFIG_FILE_DIRECTORY_PATH EXT_PATH("apps_data/f0_mine_sweeper")
-#define MINESWEEPER_SETTINGS_SAVE_PATH CONFIG_FILE_DIRECTORY_PATH "/f0_mine_sweeper.conf"
+#define MINESWEEPER_SETTINGS_FILE_VERSION 2
+#define CONFIG_FILE_DIRECTORY_PATH EXT_PATH("apps_data/mine_sweeper_redux")
+#define MINESWEEPER_SETTINGS_SAVE_PATH CONFIG_FILE_DIRECTORY_PATH "/mine_sweeper_redux.conf"
 #define MINESWEEPER_SETTINGS_SAVE_PATH_TMP MINESWEEPER_SETTINGS_SAVE_PATH ".tmp"
-#define MINESWEEPER_SETTINGS_HEADER "Mine Sweeper Config File"
+#define MINESWEEPER_SETTINGS_HEADER "Mine Sweeper Redux Config File"
 
 #define MINESWEEPER_SETTINGS_KEY_WIDTH "BoardWidth"
 #define MINESWEEPER_SETTINGS_KEY_HEIGHT "BoardHeight"
 #define MINESWEEPER_SETTINGS_KEY_DIFFICULTY "BoardDifficulty"
+#define MINESWEEPER_SETTINGS_KEY_FEEDBACK "FeedbackEnabled"
 
-#define MINESWEEPER_SETTINGS_KEY_HAPTIC "Haptic"
-#define MINESWEEPER_SETTINGS_KEY_LED "Led"
-#define MINESWEEPER_SETTINGS_KEY_SPEAKER "Speaker"
-
+void mine_sweeper_quick_save_feedback(void* context);
 void mine_sweeper_save_settings(void* context);
 bool mine_sweeper_read_settings(void* context);
 
-#endif
+#endif

+ 1 - 3
base_pack/minesweeper/minesweeper.c

@@ -53,9 +53,7 @@ static MineSweeperApp* app_alloc() {
         app->settings_info.board_width = 16;
         app->settings_info.board_height = 7;
         app->settings_info.difficulty = 0;
-        app->haptic = 1;
-        app->speaker = 1;
-        app->led = 1;
+        app->feedback_enabled = 1;
 
         mine_sweeper_save_settings(app);
     } else {

+ 2 - 4
base_pack/minesweeper/minesweeper.h

@@ -56,12 +56,10 @@ typedef struct MineSweeperApp {
     MineSweeperAppSettings settings_info;
     MineSweeperAppSettings t_settings_info;
 
-    bool is_settings_changed;
+    uint8_t is_settings_changed;
     bool ensure_map_solvable;
 
-    uint32_t haptic;
-    uint32_t speaker;
-    uint32_t led;
+    uint8_t feedback_enabled;
 } MineSweeperApp;
 
 // View Id Enumeration

+ 13 - 5
base_pack/minesweeper/scenes/info_scene.c

@@ -1,5 +1,5 @@
 #include "../minesweeper.h"
-static const char* info_string= "GAME INFO BELOW\n\n"
+static const char* info_string= "--   GAME INFO BELOW   --\n\n"
                                 "1. Press OK to clear a tile.\n\n"
                                 "2. Hold OK on a numbered tile\n"
                                 "to clear all surrounding\n"
@@ -10,18 +10,26 @@ static const char* info_string= "GAME INFO BELOW\n\n"
                                 "4. Hold back on a cleared\n"
                                 "tile to jump to the\n"
                                 "closest tile.\n\n"
-                                "SETTINGS INFO\n\n"
+                                "---    SETTINGS INFO    ---\n\n"
                                 "Difficulty and map\n"
                                 "dimensions can be changed\n"
                                 "in the settings with a\n"
                                 "max map size of 1024\n"
-                                "tiles (32x32).\n\n"
-                                "ENSURE SOLVABLE\n"
+                                "tiles (32x32).\n"
+                                "You will be prompted to\n"
+                                "confirm any changes to these\n"
+                                "settings as it will reset the\n"
+                                "board.\n\n"
+                                "----      FEEDBACK     ----\n"
+                                "This settings enables/disables\n"
+                                "Haptic/Sound feedback for the\n"
+                                "game.\n\n"
+                                "-  ENSURE SOLVABLE  -\n"
                                 "This is a setting that\n"
                                 "enables a board verifier\n"
                                 "when generating a new\n"
                                 "board.\n\n"
-                                "-- WARNING --\n"
+                                "WARNING!:\n"
                                 "This setting will introduce\n"
                                 "a variable amount of\n"
                                 "overhead when generating\n"

+ 43 - 1
base_pack/minesweeper/scenes/settings_scene.c

@@ -21,6 +21,7 @@ typedef enum {
     MineSweeperSettingsScreenEventHeightChange,
     MineSweeperSettingsScreenEventSolvableChange,
     MineSweeperSettingsScreenEventInfoChange,
+    MineSweeperSettingsScreenEventFeedbackChange,
 } MineSweeperSettingsScreenEvent;
 
 static const char* settings_screen_difficulty_text[MineSweeperSettingsScreenDifficultyTypeNum] = {
@@ -163,11 +164,31 @@ static void minesweeper_scene_settings_screen_set_solvable(VariableItem* item) {
     
 }
 
+static void minesweeper_scene_settings_screen_set_feedback(VariableItem* item) { 
+    furi_assert(item);
+
+    MineSweeperApp* app = variable_item_get_context(item);
+
+    uint8_t index = variable_item_get_current_value_index(item);
+    
+
+    app->feedback_enabled = index;
+
+    FURI_LOG_I(TAG, "FEEDBACK CALLBACK INDEX %d", app->feedback_enabled);
+    
+    variable_item_set_current_value_text(
+            item,
+            ((index) ? "Enabled" : "Disabled"));
+
+    view_dispatcher_send_custom_event(app->view_dispatcher, MineSweeperSettingsScreenEventFeedbackChange);
+}
+
 static void minesweeper_scene_settings_screen_set_info(VariableItem* item) {
     furi_assert(item);
 
     MineSweeperApp* app = variable_item_get_context(item);
 
+
     view_dispatcher_send_custom_event(app->view_dispatcher, MineSweeperSettingsScreenEventInfoChange);
 }
 
@@ -268,6 +289,22 @@ void minesweeper_scene_settings_screen_on_enter(void* context) {
             item,
             settings_screen_verifier_text[idx]);
     
+    // Set sound feedback item 
+    item = variable_item_list_add(
+            va,
+            "Feedback",
+            2,
+            minesweeper_scene_settings_screen_set_feedback,
+            app);
+
+    variable_item_set_current_value_index(
+            item,
+            app->feedback_enabled);
+
+    variable_item_set_current_value_text(
+            item,
+            ((app->feedback_enabled) ? "Enabled" : "Disabled"));
+    
     // Set info item
     item = variable_item_list_add(
             va,
@@ -323,6 +360,10 @@ bool minesweeper_scene_settings_screen_on_event(void* context, SceneManagerEvent
                 scene_manager_next_scene(app->scene_manager, MineSweeperSceneInfoScreen);
                 break;
 
+            case MineSweeperSettingsScreenEventFeedbackChange : 
+                // If only the feedback option is changed we can just save without restarting
+                mine_sweeper_save_settings(app);
+                break;
             default :
                 break;
         };
@@ -330,11 +371,12 @@ bool minesweeper_scene_settings_screen_on_event(void* context, SceneManagerEvent
 
     } else if (event.type == SceneManagerEventTypeBack) {
 
-        // If there are changes in the width, height, or difficulty go to confirmation scren
         if (app->is_settings_changed) { 
+            // If there are changes in the width, height, or difficulty go to confirmation screen for restart
 
             scene_manager_next_scene(app->scene_manager, MineSweeperSceneConfirmationScreen);
         } else {
+            // Otherwise just go back
 
             memset(&app->t_settings_info, 0, sizeof(app->t_settings_info));
 

+ 403 - 473
base_pack/minesweeper/views/minesweeper_game_screen.c

@@ -1,12 +1,4 @@
 #include "minesweeper_game_screen.h"
-#include "minesweeper_redux_icons.h"
-
-#include <gui/elements.h>
-#include <gui/icon_animation.h>
-#include <input/input.h>
-
-#include <furi.h>
-#include <furi_hal.h>
 
 static const Icon* tile_icons[13] = {
     &I_tile_empty_8x8,
@@ -78,8 +70,9 @@ typedef struct {
     uint32_t start_tick;
     FuriString* info_str;
     bool ensure_solvable_board;
-    bool is_win_triggered;
+    bool is_restart_triggered;
     bool is_holding_down_button;
+    bool has_lost_game;
 } MineSweeperGameScreenModel;
 
 // Multipliers for ratio of mines to tiles
@@ -143,15 +136,14 @@ static void mine_sweeper_game_screen_set_board_information(
 
 static bool try_clear_surrounding_tiles(MineSweeperGameScreenModel* model);
 
-static Point bfs_to_closest_tile(MineSweeperGameScreenModel* model);
+static void bfs_to_closest_tile(MineSweeperGameScreen* instance, MineSweeperGameScreenModel* model);
 
 // Currently not using enter/exit callback
 static void mine_sweeper_game_screen_view_enter(void* context);
 static void mine_sweeper_game_screen_view_exit(void* context);
 
 // Different input/draw callbacks for play/win/lose state
-static void mine_sweeper_game_screen_view_win_draw_callback(Canvas* canvas, void* _model);
-static void mine_sweeper_game_screen_view_lose_draw_callback(Canvas* canvas, void* _model);
+static void mine_sweeper_game_screen_view_end_draw_callback(Canvas* canvas, void* _model);
 static void mine_sweeper_game_screen_view_play_draw_callback(Canvas* canvas, void* _model);
 
 // These consolidate the function calls for led/haptic/sound for specific events
@@ -163,6 +155,14 @@ static void mine_sweeper_oob_effect(void* context);
 static void mine_sweeper_lose_effect(void* context);
 static void mine_sweeper_win_effect(void* context);
 
+static inline bool handle_player_move(
+        MineSweeperGameScreen* instance,
+        MineSweeperGameScreenModel* model,
+        InputEvent* event);
+static int8_t handle_short_ok_input(MineSweeperGameScreen* instance, MineSweeperGameScreenModel* model);
+static int8_t handle_long_ok_input(MineSweeperGameScreen* instance, MineSweeperGameScreenModel* model);
+static bool handle_long_back_flag_input(MineSweeperGameScreen* instance, MineSweeperGameScreenModel* model);
+
 static bool mine_sweeper_game_screen_view_end_input_callback(InputEvent* event, void* context);
 static bool mine_sweeper_game_screen_view_play_input_callback(InputEvent* event, void* context);
 
@@ -199,7 +199,7 @@ static void setup_board(MineSweeperGameScreen* instance) {
      * and manipulate then save to actual model
      */
     MineSweeperGameScreenTileType tiles[MINESWEEPER_BOARD_MAX_TILES];
-    memset(&tiles, MineSweeperGameScreenTileNone, sizeof(tiles));
+    memset(&tiles, MineSweeperGameScreenTileZero, sizeof(tiles));
 
     // Randomly place tiles except in the corners to help guarantee solvability
     for (uint16_t i = 0; i < num_mines; i++) {
@@ -281,7 +281,8 @@ static void setup_board(MineSweeperGameScreen* instance) {
             model->curr_pos.y_abs = 0;
             model->right_boundary = MINESWEEPER_SCREEN_TILE_WIDTH;
             model->bottom_boundary = MINESWEEPER_SCREEN_TILE_HEIGHT;
-            model->is_win_triggered = false;         
+            model->is_restart_triggered = false;         
+            model->has_lost_game = false;
         },
         true
     );
@@ -655,7 +656,7 @@ static bool try_clear_surrounding_tiles(MineSweeperGameScreenModel* model) {
 
     MineSweeperTile tile = model->board[curr_pos_1d];
 
-    // Return true if tile is zero tile or not cleared
+    // Return false if tile is zero tile or not cleared
     if (tile.tile_state != MineSweeperGameScreenTileStateCleared || tile.tile_type == MineSweeperGameScreenTileZero) {
         return false;
     }
@@ -713,7 +714,8 @@ static bool try_clear_surrounding_tiles(MineSweeperGameScreenModel* model) {
  * Function is used on a long backpress on a cleared tile and returns the position
  * of the first found uncleared tile using a bfs search
  */
-static inline Point bfs_to_closest_tile(MineSweeperGameScreenModel* model) {
+static void bfs_to_closest_tile(MineSweeperGameScreen* instance, MineSweeperGameScreenModel* model) {
+
     furi_assert(model);
 
     // Init both the set and dequeue
@@ -724,7 +726,7 @@ static inline Point bfs_to_closest_tile(MineSweeperGameScreenModel* model) {
     point_set_init(set);
 
     // Return the value in this point
-    Point result;
+    Point result = (Point) {.x = 0, .y = 0};
 
     // Point_t pos will be used to keep track of the current point
     Point_t pos;
@@ -777,113 +779,317 @@ static inline Point bfs_to_closest_tile(MineSweeperGameScreenModel* model) {
     point_set_clear(set);
     point_deq_clear(deq);
 
-    return result;
+    // Save cursor to new closest tile position
+    // If the cursor moves outisde of the model boundaries we need to
+    // move the boundary appropriately
+    
+    model->curr_pos.x_abs = result.x;
+    model->curr_pos.y_abs = result.y;
+
+    bool is_outside_top_boundary = model->curr_pos.x_abs <
+        (model->bottom_boundary - MINESWEEPER_SCREEN_TILE_HEIGHT);
+
+    bool is_outside_bottom_boundary = model->curr_pos.x_abs >=
+        model->bottom_boundary;
+
+    bool is_outside_left_boundary = model->curr_pos.y_abs <
+        (model->right_boundary - MINESWEEPER_SCREEN_TILE_WIDTH);
+
+    bool is_outside_right_boundary = model->curr_pos.y_abs >=
+        model->right_boundary;
+
+    if (is_outside_top_boundary) {
+        model->bottom_boundary = model->curr_pos.x_abs + MINESWEEPER_SCREEN_TILE_HEIGHT;
+    } else if (is_outside_bottom_boundary) {
+        model->bottom_boundary = model->curr_pos.x_abs+1;
+    }
+
+    if (is_outside_right_boundary) {
+        model->right_boundary = model->curr_pos.y_abs+1;
+    } else if (is_outside_left_boundary) {
+        model->right_boundary = model->curr_pos.y_abs + MINESWEEPER_SCREEN_TILE_WIDTH;
+    }
+    
+    mine_sweeper_play_happy_bump(instance->context);
 }
 
-static void mine_sweeper_game_screen_view_enter(void* context) {
+static void mine_sweeper_short_ok_effect(void* context) {
     furi_assert(context);
-    UNUSED(context);
+    MineSweeperGameScreen* instance = context;
+
+    mine_sweeper_led_blink_magenta(instance->context);
+    mine_sweeper_play_ok_sound(instance->context);
+    mine_sweeper_play_happy_bump(instance->context);
+    mine_sweeper_stop_all_sound(instance->context);
 }
 
-static void mine_sweeper_game_screen_view_exit(void* context) {
+static void mine_sweeper_long_ok_effect(void* context) {
     furi_assert(context);
-    UNUSED(context);
+    MineSweeperGameScreen* instance = context;
+
+    mine_sweeper_led_blink_magenta(instance->context);
+    mine_sweeper_play_ok_sound(instance->context);
+    mine_sweeper_play_long_ok_bump(instance->context);
+    mine_sweeper_stop_all_sound(instance->context);
 }
 
-static void mine_sweeper_game_screen_view_win_draw_callback(Canvas* canvas, void* _model) {
-    furi_assert(canvas);
-    furi_assert(_model);
-    MineSweeperGameScreenModel* model = _model;
+static void mine_sweeper_flag_effect(void* context) {
+    furi_assert(context);
+    MineSweeperGameScreen* instance = context;
 
-    canvas_clear(canvas);
+    mine_sweeper_led_blink_cyan(instance->context);
+    mine_sweeper_play_flag_sound(instance->context);
+    mine_sweeper_play_happy_bump(instance->context);
+    mine_sweeper_stop_all_sound(instance->context);
+}
 
-    canvas_set_color(canvas, ColorBlack);
+static void mine_sweeper_move_effect(void* context) {
+    furi_assert(context);
+    MineSweeperGameScreen* instance = context;
     
-    uint16_t cursor_pos_1d = model->curr_pos.x_abs * model->board_width + model->curr_pos.y_abs;
+    mine_sweeper_play_happy_bump(instance->context);
+}
+
+static void mine_sweeper_oob_effect(void* context) {
+    furi_assert(context);
+    MineSweeperGameScreen* instance = context;
     
-    for (uint8_t x_rel = 0; x_rel < MINESWEEPER_SCREEN_TILE_HEIGHT; x_rel++) {
-        uint16_t x_abs = (model->bottom_boundary - MINESWEEPER_SCREEN_TILE_HEIGHT) + x_rel;
-        
-        for (uint8_t y_rel = 0; y_rel < MINESWEEPER_SCREEN_TILE_WIDTH; y_rel++) {
-            uint16_t y_abs = (model->right_boundary - MINESWEEPER_SCREEN_TILE_WIDTH) + y_rel;
+    mine_sweeper_led_blink_red(instance->context);
+    mine_sweeper_play_flag_sound(instance->context);
+    mine_sweeper_play_oob_bump(instance->context);
+    mine_sweeper_stop_all_sound(instance->context);
 
-            uint16_t curr_rendering_tile_pos_1d = x_abs * model->board_width + y_abs;
-            MineSweeperTile tile = model->board[curr_rendering_tile_pos_1d];
+}
 
-            if (cursor_pos_1d == curr_rendering_tile_pos_1d) {
-                canvas_set_color(canvas, ColorWhite);
-            } else {
-                canvas_set_color(canvas, ColorBlack);
+static void mine_sweeper_lose_effect(void* context) {
+    furi_assert(context);
+    MineSweeperGameScreen* instance = context;
+
+    mine_sweeper_led_set_rgb(instance->context, 255, 0, 000);
+    mine_sweeper_play_lose_sound(instance->context);
+    mine_sweeper_play_lose_bump(instance->context);
+    mine_sweeper_stop_all_sound(instance->context);
+
+}
+
+static void mine_sweeper_win_effect(void* context) {
+    furi_assert(context);
+    MineSweeperGameScreen* instance = context;
+
+    mine_sweeper_led_set_rgb(instance->context, 0, 0, 255);
+    mine_sweeper_play_win_sound(instance->context);
+    mine_sweeper_play_win_bump(instance->context);
+    mine_sweeper_stop_all_sound(instance->context);
+
+}
+
+static inline bool handle_player_move(MineSweeperGameScreen* instance, MineSweeperGameScreenModel* model, InputEvent* event) {
+
+    bool consumed = false;
+    bool is_outside_boundary;
+
+    switch (event->key) {
+
+        case InputKeyUp :
+            (model->curr_pos.x_abs-1 < 0)  ? mine_sweeper_oob_effect(instance) :
+                    mine_sweeper_move_effect(instance);
+            
+            model->curr_pos.x_abs = (model->curr_pos.x_abs-1 < 0) ? 0 : model->curr_pos.x_abs-1;
+
+            is_outside_boundary = model->curr_pos.x_abs <
+                (model->bottom_boundary - MINESWEEPER_SCREEN_TILE_HEIGHT);
+            
+            if (is_outside_boundary) {
+                model->bottom_boundary--;
             }
 
-            canvas_draw_icon(
-                canvas,
-                y_rel * icon_get_width(tile.icon_element.icon),
-                x_rel * icon_get_height(tile.icon_element.icon),
-                tile.icon_element.icon);
+            consumed = true;
+            break;
+
+        case InputKeyDown :
+
+            (model->curr_pos.x_abs+1 >= model->board_height)  ? mine_sweeper_oob_effect(instance) :
+                    mine_sweeper_move_effect(instance);
+
+            model->curr_pos.x_abs = (model->curr_pos.x_abs+1 >= model->board_height) ?
+                model->board_height-1 : model->curr_pos.x_abs+1;
+
+            is_outside_boundary = model->curr_pos.x_abs >= model->bottom_boundary;
+
+            if (is_outside_boundary) {
+                model->bottom_boundary++;
+            }
+
+            consumed = true;
+            break;
+
+        case InputKeyLeft :
+            (model->curr_pos.y_abs-1 < 0)  ? mine_sweeper_oob_effect(instance) :
+                    mine_sweeper_move_effect(instance);
+
+            model->curr_pos.y_abs = (model->curr_pos.y_abs-1 < 0) ? 0 : model->curr_pos.y_abs-1;
+
+            is_outside_boundary = model->curr_pos.y_abs <
+                (model->right_boundary - MINESWEEPER_SCREEN_TILE_WIDTH);
+            
+            if (is_outside_boundary) {
+                model->right_boundary--;
+            }
+
+            consumed = true;
+            break;
+
+        case InputKeyRight :
+            (model->curr_pos.y_abs+1 >= model->board_width)  ? mine_sweeper_oob_effect(instance) :
+                    mine_sweeper_move_effect(instance);
+
+            model->curr_pos.y_abs = (model->curr_pos.y_abs+1 >= model->board_width) ?
+                model->board_width-1 : model->curr_pos.y_abs+1;
+
+            is_outside_boundary = model->curr_pos.y_abs >= model->right_boundary;
+
+            if (is_outside_boundary) {
+                model->right_boundary++;
+            }
+
+            consumed = true;
+            break;
+
+        default:
+            consumed = true;
+            break;
 
-        }
     }
+    
+    return consumed;
+}
 
-    canvas_set_color(canvas, ColorBlack);
-    // If any borders are at the limits of the game board we draw a border line
+static int8_t handle_short_ok_input(MineSweeperGameScreen* instance, MineSweeperGameScreenModel* model) {
+    furi_assert(instance);
+    furi_assert(model);
     
-    // Right border 
-    if (model->right_boundary == model->board_width) {
-        canvas_draw_line(canvas, 127,0,127,63-8);
+    uint16_t curr_pos_1d = model->curr_pos.x_abs * model->board_width + model->curr_pos.y_abs;
+    bool is_win_condition_triggered = false;
+    bool is_lose_condition_triggered = false;
+
+    MineSweeperGameScreenTileState state = model->board[curr_pos_1d].tile_state;
+    MineSweeperGameScreenTileType type = model->board[curr_pos_1d].tile_type;
+
+    // LOSE/WIN CONDITION OR TILE CLEAR
+    if (state == MineSweeperGameScreenTileStateUncleared && type == MineSweeperGameScreenTileMine) {
+
+        is_lose_condition_triggered = true;
+        model->board[curr_pos_1d].tile_state = MineSweeperGameScreenTileStateCleared;
+
+    } else if (state == MineSweeperGameScreenTileStateUncleared) {
+
+        uint16_t tiles_cleared = bfs_tile_clear(
+                                    model->board,
+                                    model->board_width,
+                                    model->board_height,
+                                    (uint16_t)model->curr_pos.x_abs,
+                                    (uint16_t)model->curr_pos.y_abs);
+
+        model->tiles_left -= tiles_cleared;
+
+        // Check win condition
+        if (model->mines_left == 0 && model->flags_left == 0 && model->tiles_left == 0) {
+            is_win_condition_triggered = true;
+        } else {
+            // if not met play ok effect
+            mine_sweeper_short_ok_effect(instance);
+        }
     }
 
-    // Left border
-    if ((model->right_boundary - MINESWEEPER_SCREEN_TILE_WIDTH) == 0) {
-        canvas_draw_line(canvas, 0,0,0,63-8);
+    if (is_lose_condition_triggered) {
+        return -1;
+    } else if (is_win_condition_triggered) {
+        return 1;
     }
+    
+    return 0;
+}
 
-    // Bottom border
-    if (model->bottom_boundary == model->board_height) {
-        canvas_draw_line(canvas, 0,63-8,127,63-8);
+static int8_t handle_long_ok_input(MineSweeperGameScreen* instance, MineSweeperGameScreenModel* model) {
+    furi_assert(instance);
+    furi_assert(model);
+    
+    uint16_t curr_pos_1d = model->curr_pos.x_abs * model->board_width + model->curr_pos.y_abs;
+    bool is_win_condition_triggered = false;
+    bool is_lose_condition_triggered = false;
+
+    MineSweeperGameScreenTileType type = model->board[curr_pos_1d].tile_type;
+
+    // Try to clear surrounding tiles if correct number is flagged.
+    is_lose_condition_triggered = try_clear_surrounding_tiles(model);
+    model->is_holding_down_button = true;
+
+    // Check win condition
+    if (model->mines_left == 0 && model->flags_left == 0 && model->tiles_left == 0) {
+        is_win_condition_triggered = true;
     }
 
-    // Top border
-    if ((model->bottom_boundary - MINESWEEPER_SCREEN_TILE_HEIGHT) == 0) {
-        canvas_draw_line(canvas, 0,0,127,0);
+    // We need to check if it is ok to play this or else we conflict
+    // with the lose effect and crash
+    if (!is_win_condition_triggered && !is_lose_condition_triggered &&
+        type != MineSweeperGameScreenTileZero) {
+        mine_sweeper_long_ok_effect(instance);
     }
 
+    if (is_lose_condition_triggered) {
+        return -1;
+    } else if (is_win_condition_triggered) {
+        return 1;
+    }
+    
+    return 0;
+}
 
-    // Draw win text
-    furi_string_printf(
-            model->info_str,
-            "YOU WIN!");
+static bool handle_long_back_flag_input(MineSweeperGameScreen* instance, MineSweeperGameScreenModel* model) {
+    furi_assert(instance);
+    furi_assert(model);
+    
+    uint16_t curr_pos_1d = model->curr_pos.x_abs * model->board_width + model->curr_pos.y_abs;
+    MineSweeperGameScreenTileState state = model->board[curr_pos_1d].tile_state;
+    
+    bool is_win_condition_triggered = false;
 
-    canvas_draw_str_aligned(
-            canvas,
-            0,
-            64-7,
-            AlignLeft,
-            AlignTop,
-            furi_string_get_cstr(model->info_str));
+    if (state == MineSweeperGameScreenTileStateFlagged) {
+        if (model->board[curr_pos_1d].tile_type == MineSweeperGameScreenTileMine) model->mines_left++;
+        model->board[curr_pos_1d].tile_state = MineSweeperGameScreenTileStateUncleared;
+        model->flags_left++;
+    
+    } else if (model->flags_left > 0) {
+        if (model->board[curr_pos_1d].tile_type == MineSweeperGameScreenTileMine) model->mines_left--;
+        model->board[curr_pos_1d].tile_state = MineSweeperGameScreenTileStateFlagged;
+        model->flags_left--;
+    }
 
-    // Draw time text
-    uint32_t ticks_elapsed = furi_get_tick() - model->start_tick;
-    uint32_t sec = ticks_elapsed / furi_kernel_get_tick_frequency();
-    uint32_t minutes = sec / 60;
-    sec = sec % 60;
+    // WIN CONDITION
+    // This can be a win condition where the non-mine tiles are cleared and they place the last flag
+    if (model->flags_left == 0 && model->mines_left == 0 && model->tiles_left == 0) {
+        is_win_condition_triggered = true;
+        mine_sweeper_win_effect(instance);
+        mine_sweeper_led_set_rgb(instance->context, 0, 0, 255);
+    } else {
+        mine_sweeper_flag_effect(instance);
+    }
 
-    furi_string_printf(
-             model->info_str,
-             "%02ld:%02ld",
-             minutes,
-             sec);
+    return is_win_condition_triggered;
+}
 
-    canvas_draw_str_aligned(
-            canvas,
-            126 - canvas_string_width(canvas, furi_string_get_cstr(model->info_str)),
-            64 - 7,
-            AlignLeft,
-            AlignTop,
-            furi_string_get_cstr(model->info_str));
+
+static void mine_sweeper_game_screen_view_enter(void* context) {
+    furi_assert(context);
+    UNUSED(context);
+}
+
+static void mine_sweeper_game_screen_view_exit(void* context) {
+    furi_assert(context);
+    UNUSED(context);
 }
 
-static void mine_sweeper_game_screen_view_lose_draw_callback(Canvas* canvas, void* _model) {
+static void mine_sweeper_game_screen_view_end_draw_callback(Canvas* canvas, void* _model) {
     furi_assert(canvas);
     furi_assert(_model);
     MineSweeperGameScreenModel* model = _model;
@@ -938,11 +1144,20 @@ static void mine_sweeper_game_screen_view_lose_draw_callback(Canvas* canvas, voi
     if ((model->bottom_boundary - MINESWEEPER_SCREEN_TILE_HEIGHT) == 0) {
         canvas_draw_line(canvas, 0,0,127,0);
     }
+    
+    
+    const char* end_status_str = "";
 
-    // Draw lose text
+    if (model->has_lost_game) {
+        end_status_str = "YOU LOSE!  PRESS OK.\0";
+    } else {
+        end_status_str = "YOU WIN!   PRESS OK.\0";
+    }
+    
+    // Draw win/lose text
     furi_string_printf(
             model->info_str,
-            "YOU LOSE!");
+            "%s", end_status_str);
 
     canvas_draw_str_aligned(
             canvas,
@@ -1118,76 +1333,6 @@ static void mine_sweeper_game_screen_view_play_draw_callback(Canvas* canvas, voi
 
 }
 
-static void mine_sweeper_short_ok_effect(void* context) {
-    furi_assert(context);
-    MineSweeperGameScreen* instance = context;
-
-    mine_sweeper_led_blink_magenta(instance->context);
-    mine_sweeper_play_ok_sound(instance->context);
-    mine_sweeper_play_happy_bump(instance->context);
-    mine_sweeper_stop_all_sound(instance->context);
-}
-
-static void mine_sweeper_long_ok_effect(void* context) {
-    furi_assert(context);
-    MineSweeperGameScreen* instance = context;
-
-    mine_sweeper_led_blink_magenta(instance->context);
-    mine_sweeper_play_ok_sound(instance->context);
-    mine_sweeper_play_long_ok_bump(instance->context);
-    mine_sweeper_stop_all_sound(instance->context);
-}
-
-static void mine_sweeper_flag_effect(void* context) {
-    furi_assert(context);
-    MineSweeperGameScreen* instance = context;
-
-    mine_sweeper_led_blink_cyan(instance->context);
-    mine_sweeper_play_flag_sound(instance->context);
-    mine_sweeper_play_happy_bump(instance->context);
-    mine_sweeper_stop_all_sound(instance->context);
-}
-
-static void mine_sweeper_move_effect(void* context) {
-    furi_assert(context);
-    MineSweeperGameScreen* instance = context;
-    
-    mine_sweeper_play_happy_bump(instance->context);
-}
-
-static void mine_sweeper_oob_effect(void* context) {
-    furi_assert(context);
-    MineSweeperGameScreen* instance = context;
-    
-    mine_sweeper_led_blink_red(instance->context);
-    mine_sweeper_play_flag_sound(instance->context);
-    mine_sweeper_play_oob_bump(instance->context);
-    mine_sweeper_stop_all_sound(instance->context);
-
-}
-
-static void mine_sweeper_lose_effect(void* context) {
-    furi_assert(context);
-    MineSweeperGameScreen* instance = context;
-
-    mine_sweeper_led_set_rgb(instance->context, 255, 0, 000);
-    mine_sweeper_play_lose_sound(instance->context);
-    mine_sweeper_play_lose_bump(instance->context);
-    mine_sweeper_stop_all_sound(instance->context);
-
-}
-
-static void mine_sweeper_win_effect(void* context) {
-    furi_assert(context);
-    MineSweeperGameScreen* instance = context;
-
-    mine_sweeper_led_set_rgb(instance->context, 0, 0, 255);
-    mine_sweeper_play_win_sound(instance->context);
-    mine_sweeper_play_win_bump(instance->context);
-    mine_sweeper_stop_all_sound(instance->context);
-
-}
-
 static bool mine_sweeper_game_screen_view_end_input_callback(InputEvent* event, void* context) {
     furi_assert(context);
     furi_assert(event);
@@ -1199,108 +1344,63 @@ static bool mine_sweeper_game_screen_view_end_input_callback(InputEvent* event,
         instance->view,
         MineSweeperGameScreenModel * model,
         {
-            if (event->type == InputTypeRelease) {
 
+            if (model->is_holding_down_button && event->type == InputTypeRelease) { 
+                //When we lose we are holding the button down, record this release
+                
                 model->is_holding_down_button = false;
                 consumed = true;
 
-            } else if (!model->is_holding_down_button && (event->type == InputTypePress || event->type == InputTypeRepeat)) {
-
-                bool is_outside_boundary;
-                switch (event->key) {
-
-                    case InputKeyUp :
-                        model->curr_pos.x_abs = (model->curr_pos.x_abs-1 < 0) ? 0 : model->curr_pos.x_abs-1;
-
-                        is_outside_boundary = model->curr_pos.x_abs <
-                            (model->bottom_boundary - MINESWEEPER_SCREEN_TILE_HEIGHT);
-                        
-                        if (is_outside_boundary) {
-                            model->bottom_boundary--;
-                        }
-
-                        consumed = true;
-                        break;
-
-                    case InputKeyDown :
-                        model->curr_pos.x_abs = (model->curr_pos.x_abs+1 >= model->board_height) ?
-                            model->board_height-1 : model->curr_pos.x_abs+1;
-
-                        is_outside_boundary = model->curr_pos.x_abs >= model->bottom_boundary;
-
-                        if (is_outside_boundary) {
-                            model->bottom_boundary++;
-                        }
-
-                        consumed = true;
-                        break;
-
-                    case InputKeyLeft :
-                        model->curr_pos.y_abs = (model->curr_pos.y_abs-1 < 0) ? 0 : model->curr_pos.y_abs-1;
-
-                        is_outside_boundary = model->curr_pos.y_abs <
-                            (model->right_boundary - MINESWEEPER_SCREEN_TILE_WIDTH);
-                        
-                        if (is_outside_boundary) {
-                            model->right_boundary--;
-                        }
-
-                        consumed = true;
-                        break;
-
-                    case InputKeyRight :
-                        model->curr_pos.y_abs = (model->curr_pos.y_abs+1 >= model->board_width) ?
-                            model->board_width-1 : model->curr_pos.y_abs+1;
+            }
 
-                        is_outside_boundary = model->curr_pos.y_abs >= model->right_boundary;
+            if (!model->is_holding_down_button && event->key == InputKeyOk && event->type == InputTypeRelease) { 
+                // After release when user presses and releases ok we want to restart the next time this function is pressed
 
-                        if (is_outside_boundary) {
-                            model->right_boundary++;
-                        }
+                model->is_restart_triggered = true;
+                consumed = true;
 
-                        consumed = true;
-                        break;
+            } else if (!model->is_holding_down_button && model->is_restart_triggered && event->key == InputKeyOk) {
+                // After restart flagged is triggered this should also trigger and restart the game
 
-                    default: // Anything other than movement around the screen should restart game
-                        mine_sweeper_led_reset(instance->context);
+                mine_sweeper_led_reset(instance->context);
 
-                        mine_sweeper_game_screen_reset_clock(instance);
-                        view_set_draw_callback(
-                                instance->view,
-                                mine_sweeper_game_screen_view_play_draw_callback);
-                        view_set_input_callback(
-                                instance->view,
-                                mine_sweeper_game_screen_view_play_input_callback);
+                mine_sweeper_game_screen_reset_clock(instance);
+                view_set_draw_callback(
+                        instance->view,
+                        mine_sweeper_game_screen_view_play_draw_callback);
+                view_set_input_callback(
+                        instance->view,
+                        mine_sweeper_game_screen_view_play_input_callback);
 
-                        // Here we are going to generate a valid map for the player 
-                        bool is_valid_board = false;
+                // Here we are going to generate a valid map for the player 
+                bool is_valid_board = false;
 
-                        size_t memsz = sizeof(MineSweeperTile) * MINESWEEPER_BOARD_MAX_TILES;
+                size_t memsz = sizeof(MineSweeperTile) * MINESWEEPER_BOARD_MAX_TILES;
 
-                        do {
-                            setup_board(instance);
+                do {
+                    setup_board(instance);
 
-                            memset(board_t, 0, memsz);
-                            memcpy(board_t, model->board, sizeof(MineSweeperTile) * (model->board_width * model->board_height));
+                    memset(board_t, 0, memsz);
+                    memcpy(board_t, model->board, sizeof(MineSweeperTile) * (model->board_width * model->board_height));
 
-                            is_valid_board = check_board_with_verifier(
-                                                            board_t,
-                                                            model->board_width,
-                                                            model->board_height,
-                                                            model->mines_left);
-                        
+                    is_valid_board = check_board_with_verifier(
+                                                    board_t,
+                                                    model->board_width,
+                                                    model->board_height,
+                                                    model->mines_left);
+                
 
-                        } while (model->ensure_solvable_board && !is_valid_board);
+                } while (model->ensure_solvable_board && !is_valid_board);
 
-                        consumed = true;
-                        break;
-                    }
+                consumed = true;
 
-                    consumed = true; 
+            } else if ((event->type == InputTypePress || event->type == InputTypeRepeat)) {
+                // Any other input we consider generic player movement
 
-                }
+                consumed = handle_player_move(instance, model, event);
+            }
 
-            },
+        },
         false
     );
 
@@ -1315,282 +1415,112 @@ static bool mine_sweeper_game_screen_view_play_input_callback(InputEvent* event,
     bool consumed = false;
 
 
-    // Checking button types
+    with_view_model(
+        instance->view,
+        MineSweeperGameScreenModel * model,
+        {
+            // Checking button types
 
-    if (event->type == InputTypeRelease) {
-        with_view_model(
-            instance->view,
-            MineSweeperGameScreenModel * model,
-            {
+            if (event->type == InputTypeRelease) {
                 model->is_holding_down_button = false;
                 consumed = true;
-            },
-            true);
-    }
 
-    if (!consumed && event->key == InputKeyOk) { // Attempt to Clear Space !! THIS CAN BE A LOSE CONDITION
+            } else if ( event->key == InputKeyOk) { // Attempt to Clear Space !! THIS CAN BE A LOSE CONDITION
 
-        bool is_lose_condition_triggered = false;
-        bool is_win_condition_triggered = false;
+                bool is_lose_condition_triggered = false;
+                bool is_win_condition_triggered = false;
 
-        with_view_model(
-            instance->view,
-            MineSweeperGameScreenModel * model,
-            {   
-                uint16_t curr_pos_1d = model->curr_pos.x_abs * model->board_width + model->curr_pos.y_abs;
 
                 if (!model->is_holding_down_button && event->type == InputTypePress) { 
                     
-                    MineSweeperGameScreenTileState state = model->board[curr_pos_1d].tile_state;
-                    MineSweeperGameScreenTileType type = model->board[curr_pos_1d].tile_type;
-
-                    // LOSE/WIN CONDITION OR TILE CLEAR
-                    if (state == MineSweeperGameScreenTileStateUncleared && type == MineSweeperGameScreenTileMine) {
-
-                        is_lose_condition_triggered = true;
-                        model->board[curr_pos_1d].tile_state = MineSweeperGameScreenTileStateCleared;
-
-                    } else if (state == MineSweeperGameScreenTileStateUncleared) {
+                    //ret : -1 lose condition, 1 win condition, 0 neutral 
+                    int8_t ret = handle_short_ok_input(instance, model);
 
-                        uint16_t tiles_cleared = bfs_tile_clear(
-                                                    model->board,
-                                                    model->board_width,
-                                                    model->board_height,
-                                                    (uint16_t)model->curr_pos.x_abs,
-                                                    (uint16_t)model->curr_pos.y_abs);
-
-                        model->tiles_left -= tiles_cleared;
-
-                        // Check win condition
-                        if (model->mines_left == 0 && model->flags_left == 0 && model->tiles_left == 0) {
-                            is_win_condition_triggered = true;
-                        } else {
-                            // if not met play ok effect
-                            mine_sweeper_short_ok_effect(instance);
-                        }
+                    if (ret != 0) {
+                        (ret == -1) ? (is_lose_condition_triggered = true) : (is_win_condition_triggered = true);
                     }
 
                 // LOSE/WIN CONDITION OR CLEAR SURROUNDING
                 } else if (!model->is_holding_down_button && event->type == InputTypeLong) {
                     
-                    // Try to clear surrounding tiles if correct number is flagged.
-                    is_lose_condition_triggered = try_clear_surrounding_tiles(model);
-                    model->is_holding_down_button = true;
-
-                    // Check win condition
-                    if (model->mines_left == 0 && model->flags_left == 0 && model->tiles_left == 0) {
-                        is_win_condition_triggered = true;
-                    }
+                    //ret : -1 lose condition, 1 win condition, 0 neutral 
+                    int8_t ret = handle_long_ok_input(instance, model);
 
-                    // We need to check if it is ok to play this or else we conflict
-                    // with the lose effect and crash
-                    if (!is_win_condition_triggered && !is_lose_condition_triggered &&
-                        model->board[curr_pos_1d].tile_type != MineSweeperGameScreenTileZero) {
-                        mine_sweeper_long_ok_effect(instance);
+                    if (ret != 0) {
+                        (ret == -1) ? (is_lose_condition_triggered = true) : (is_win_condition_triggered = true);
                     }
 
                 } 
 
-            },
-            true
-        );
-        
-        // Check  if win or lose condition was triggered on OK press
-        if (is_lose_condition_triggered) {
-            mine_sweeper_lose_effect(instance);
-            view_set_draw_callback(instance->view, mine_sweeper_game_screen_view_lose_draw_callback);
-            view_set_input_callback(instance->view, mine_sweeper_game_screen_view_end_input_callback);
-        } else if (is_win_condition_triggered) {
-            mine_sweeper_win_effect(instance);
-            view_set_draw_callback(instance->view, mine_sweeper_game_screen_view_win_draw_callback);
-            view_set_input_callback(instance->view, mine_sweeper_game_screen_view_end_input_callback);
-        }
+                // Check  if win or lose condition was triggered on OK press
+                if (is_lose_condition_triggered) {
 
+                    model->has_lost_game = true;
+                    mine_sweeper_lose_effect(instance);
 
-        consumed = true;
+                    view_set_draw_callback(instance->view, mine_sweeper_game_screen_view_end_draw_callback);
+                    view_set_input_callback(instance->view, mine_sweeper_game_screen_view_end_input_callback);
 
-    }
+                } else if (is_win_condition_triggered) {
 
-    if (!consumed && (event->key == InputKeyBack)) { // We can use holding the back button for either
-                                                   // Setting a flag on a covered tile, or moving to
-                                                   // the next closest covered tile on when on a uncovered
-                                                   // tile
-
-        if (event->type == InputTypeLong || event->type == InputTypeRepeat) {    // Only process longer back keys;
-                                                                                        // short presses should take
-                                                                                        // us to the menu
-            with_view_model(
-                instance->view,
-                MineSweeperGameScreenModel * model,
-                {
-                    uint16_t curr_pos_1d = model->curr_pos.x_abs * model->board_width + model->curr_pos.y_abs;
-                    
-                    MineSweeperGameScreenTileState state = model->board[curr_pos_1d].tile_state;
+                    mine_sweeper_win_effect(instance);
 
-                    if (state == MineSweeperGameScreenTileStateCleared) {
+                    view_set_draw_callback(instance->view, mine_sweeper_game_screen_view_end_draw_callback);
+                    view_set_input_callback(instance->view, mine_sweeper_game_screen_view_end_input_callback);
 
-                        // BFS to closest uncovered position
-                        Point res = bfs_to_closest_tile(model);
+                }
 
-                        // Save cursor to new closest tile position
-                        // If the cursor moves outisde of the model boundaries we need to
-                        // move the boundary appropriately
-                        
-                        model->curr_pos.x_abs = res.x;
-                        model->curr_pos.y_abs = res.y;
+                consumed = true;
 
-                        bool is_outside_top_boundary = model->curr_pos.x_abs <
-                            (model->bottom_boundary - MINESWEEPER_SCREEN_TILE_HEIGHT);
+            } else if (event->key == InputKeyBack) { // We can use holding the back button for either
+                                                           // Setting a flag on a covered tile, or moving to
+                                                           // the next closest covered tile on when on a uncovered
+                                                           // tile
 
-                        bool is_outside_bottom_boundary = model->curr_pos.x_abs >=
-                            model->bottom_boundary;
+                if (event->type == InputTypeLong || event->type == InputTypeRepeat) {    // Only process longer back keys;
+                                                                                                // short presses should take
+                                                                                                // us to the menu
 
-                        bool is_outside_left_boundary = model->curr_pos.y_abs <
-                            (model->right_boundary - MINESWEEPER_SCREEN_TILE_WIDTH);
+                    bool is_win_condition_triggered = false;
 
-                        bool is_outside_right_boundary = model->curr_pos.y_abs >=
-                            model->right_boundary;
+                    uint16_t curr_pos_1d = model->curr_pos.x_abs * model->board_width + model->curr_pos.y_abs;
+                    MineSweeperGameScreenTileState state = model->board[curr_pos_1d].tile_state;
+                    
 
-                        if (is_outside_top_boundary) {
-                            model->bottom_boundary = model->curr_pos.x_abs + MINESWEEPER_SCREEN_TILE_HEIGHT;
-                        } else if (is_outside_bottom_boundary) {
-                            model->bottom_boundary = model->curr_pos.x_abs+1;
-                        }
+                    if (state == MineSweeperGameScreenTileStateCleared) {
+
+                        // BFS to closest uncovered position
+                        bfs_to_closest_tile(instance, model);
 
-                        if (is_outside_right_boundary) {
-                            model->right_boundary = model->curr_pos.y_abs+1;
-                        } else if (is_outside_left_boundary) {
-                            model->right_boundary = model->curr_pos.y_abs + MINESWEEPER_SCREEN_TILE_WIDTH;
-                        }
-                        
-                        mine_sweeper_play_happy_bump(instance->context);
                         model->is_holding_down_button = true;
 
-                    // Flag or Unflag tile and check win condition 
-                    } else if (!model->is_holding_down_button &&
-                        (state == MineSweeperGameScreenTileStateUncleared || state == MineSweeperGameScreenTileStateFlagged)) { 
 
+                    // Flag or Unflag tile and check win condition 
+                    } else if (!model->is_holding_down_button && state != MineSweeperGameScreenTileStateCleared) { 
 
-                        if (state == MineSweeperGameScreenTileStateFlagged) {
-                            if (model->board[curr_pos_1d].tile_type == MineSweeperGameScreenTileMine) model->mines_left++;
-                            model->board[curr_pos_1d].tile_state = MineSweeperGameScreenTileStateUncleared;
-                            model->flags_left++;
-                            model->is_holding_down_button = true;
+                        is_win_condition_triggered = handle_long_back_flag_input(instance, model); 
                         
-                        } else if (model->flags_left > 0) {
-                            if (model->board[curr_pos_1d].tile_type == MineSweeperGameScreenTileMine) model->mines_left--;
-                            model->board[curr_pos_1d].tile_state = MineSweeperGameScreenTileStateFlagged;
-                            model->flags_left--;
-                            model->is_holding_down_button = true;
-                        }
+                        model->is_holding_down_button = true;
 
-                        // WIN CONDITION
-                        // This can be a win condition where the non-mine tiles are cleared and they place the last flag
-                        if (model->flags_left == 0 && model->mines_left == 0 && model->tiles_left == 0) {
-                            //mine_sweeper_play_long_bump(instance->context);
-                            
-                            mine_sweeper_win_effect(instance);
-                            mine_sweeper_led_set_rgb(instance->context, 0, 0, 255);
+                        if (is_win_condition_triggered) {
 
-                            view_set_draw_callback(instance->view, mine_sweeper_game_screen_view_win_draw_callback);
+                            view_set_draw_callback(instance->view, mine_sweeper_game_screen_view_end_draw_callback);
                             view_set_input_callback(instance->view, mine_sweeper_game_screen_view_end_input_callback);
-                        } else {
-                            // Making sure that win and flag effect are not played together
-                            mine_sweeper_flag_effect(instance);
-                        }
-
-                    }
-                },
-                false
-            );
 
-            consumed = true;
-
-        }
-    }
-
-    if (!consumed && (event->type == InputTypePress || event->type == InputTypeRepeat)) { // Finally handle move
-
-        with_view_model(
-            instance->view,
-            MineSweeperGameScreenModel * model,
-            {
-                bool is_outside_boundary;
-                switch (event->key) {
-
-                    case InputKeyUp :
-                        (model->curr_pos.x_abs-1 < 0)  ? mine_sweeper_oob_effect(instance) :
-                                mine_sweeper_move_effect(instance);
-                        
-                        model->curr_pos.x_abs = (model->curr_pos.x_abs-1 < 0) ? 0 : model->curr_pos.x_abs-1;
-
-                        is_outside_boundary = model->curr_pos.x_abs <
-                            (model->bottom_boundary - MINESWEEPER_SCREEN_TILE_HEIGHT);
-                        
-                        if (is_outside_boundary) {
-                            model->bottom_boundary--;
                         }
 
-                        consumed = true;
-                        break;
-
-                    case InputKeyDown :
-
-                        (model->curr_pos.x_abs+1 >= model->board_height)  ? mine_sweeper_oob_effect(instance) :
-                                mine_sweeper_move_effect(instance);
-
-                        model->curr_pos.x_abs = (model->curr_pos.x_abs+1 >= model->board_height) ?
-                            model->board_height-1 : model->curr_pos.x_abs+1;
-
-                        is_outside_boundary = model->curr_pos.x_abs >= model->bottom_boundary;
-
-                        if (is_outside_boundary) {
-                            model->bottom_boundary++;
-                        }
-
-                        consumed = true;
-                        break;
-
-                    case InputKeyLeft :
-                        (model->curr_pos.y_abs-1 < 0)  ? mine_sweeper_oob_effect(instance) :
-                                mine_sweeper_move_effect(instance);
-
-                        model->curr_pos.y_abs = (model->curr_pos.y_abs-1 < 0) ? 0 : model->curr_pos.y_abs-1;
-
-                        is_outside_boundary = model->curr_pos.y_abs <
-                            (model->right_boundary - MINESWEEPER_SCREEN_TILE_WIDTH);
-                        
-                        if (is_outside_boundary) {
-                            model->right_boundary--;
-                        }
-
-                        consumed = true;
-                        break;
-
-                    case InputKeyRight :
-                        (model->curr_pos.y_abs+1 >= model->board_width)  ? mine_sweeper_oob_effect(instance) :
-                                mine_sweeper_move_effect(instance);
-
-                        model->curr_pos.y_abs = (model->curr_pos.y_abs+1 >= model->board_width) ?
-                            model->board_width-1 : model->curr_pos.y_abs+1;
-
-                        is_outside_boundary = model->curr_pos.y_abs >= model->right_boundary;
-
-                        if (is_outside_boundary) {
-                            model->right_boundary++;
-                        }
+                    }
 
-                        consumed = true;
-                        break;
+                    consumed = true;
 
-                    default:
-                        consumed = true;
-                        break;
                 }
-            },
-            true
-        );
-    }
+            } else if (event->type == InputTypePress || event->type == InputTypeRepeat) { // Finally handle move
+
+                        consumed = handle_player_move(instance, model, event);
+            }
+        },
+        true
+    );
     
 
     if (!consumed && instance->input_callback != NULL) {

+ 7 - 0
base_pack/minesweeper/views/minesweeper_game_screen.h

@@ -8,6 +8,13 @@
 
 #include <gui/view.h>
 
+#include <gui/elements.h>
+#include <gui/icon_animation.h>
+#include <input/input.h>
+#include <furi.h>
+#include <furi_hal.h>
+
+#include "minesweeper_redux_icons.h"
 #include "minesweeper_game_screen_i.h"
 #include "../helpers/mine_sweeper_haptic.h"
 #include "../helpers/mine_sweeper_led.h"