Forráskód Böngészése

Merge snake_2 from https://github.com/Willzvul/Snake_2.0

Willy-JL 1 éve
szülő
commit
980ce7ef57
7 módosított fájl, 309 hozzáadás és 93 törlés
  1. 22 15
      snake_2/ReadMe.md
  2. BIN
      snake_2/Snake 2.0.png
  3. 13 0
      snake_2/changelog.md
  4. BIN
      snake_2/img/1.png
  5. BIN
      snake_2/img/2.png
  6. BIN
      snake_2/img/3.png
  7. 274 78
      snake_2/snake_20.c

+ 22 - 15
snake_2/ReadMe.md

@@ -1,28 +1,35 @@
 # SNAKE 2.0
 
-A new version of Snake Game.
+Advanced Snake Game (Remake of original Snake).
 
-![Snake_2.0](https://github.com/Willzvul/Snake_2.0/blob/main/Snake%202.0.png)
+![Snake_2.0](https://github.com/Willzvul/Snake_2.0/blob/main/img/3.png)
 
 
 ## Features
 
-1. Pause of game proccess
-2. Possibility of speed Up by holding the arrows
+### v2.2
+1. Pause of game proccess.
+2. Possibility of speed Up by holding the arrows.
 3. Snake has a head.
-4. The target is an apple
-5. Complete progress bar
+4. The target is an apple.
+5. Complete progress bar (%).
 
-## Building FAP
+### v2.3
+1. Saving progress when exiting and loading it when starting the game.
+2. Endless mode switch (If turned ON -> the game continues even if the snake collides with the tail or the frame, so the player gets a chance to win the game).
+3. Game timer.
+4. Breaking by holding the back-direction arrow.
+5. Brand-new system of the next random fruit positioning (it fixed the major bug when the fruit appeared in the left-upper corner of a screen despite the fact that the field (0,0) is already taken by the snake's body).
 
-https://fap.playmean.xyz/Willzvul/Snake_2.0
+## Changelog
 
-## Links
-
-Special thanks for *.c file in https://github.com/DarkFlippers/unleashed-firmware.git
+v2.0 - Initial release,
+v2.1 - Various important fixes,
+v2.2 - Sync updates and latest API support
+v2.3 - Latest API support and various important fixes (see 'Features')
 
-## Free Beer
+## Links
 
-You can support me by using this link:
-(only RU payments accepted) 
-https://yoomoney.ru/to/410018138145748/100
+Special thanks for *.c files in:
+https://github.com/DarkFlippers/unleashed-firmware.git
+https://github.com/xMasterX/all-the-plugins/tree/dev

BIN
snake_2/Snake 2.0.png


+ 13 - 0
snake_2/changelog.md

@@ -0,0 +1,13 @@
+v2.2:
+1. Pause of game proccess.
+2. Possibility of speed Up by holding the arrows.
+3. Snake has a head.
+4. The target is an apple.
+5. Complete progress bar (%).
+
+v2.3:
+1. Saving progress when exiting and loading it when starting the game.
+2. Endless mode switch (If turned ON -> the game continues even if the snake collides with the tail or the frame, so the player gets a chance to win the game).
+3. Game timer.
+4. Breaking by holding the back-direction arrow.
+5. Brand-new system of the next random fruit positioning (it fixed the major bug when the fruit appeared in the left-upper corner of a screen despite the fact that the field (0,0) is already taken by the snake's body).

BIN
snake_2/img/1.png


BIN
snake_2/img/2.png


BIN
snake_2/img/3.png


+ 274 - 78
snake_2/snake_20.c

@@ -1,10 +1,12 @@
 #include <furi.h>
+#include <furi_hal.h>
 #include <gui/gui.h>
 #include <input/input.h>
 #include <stdlib.h>
 #include <dolphin/dolphin.h>
 #include <notification/notification.h>
 #include <notification/notification_messages.h>
+#include <storage/storage.h>
 
 typedef struct {
     //    +-----x
@@ -18,21 +20,10 @@ typedef struct {
 typedef enum {
     GameStateLife,
     GameStatePause,
-    // https://melmagazine.com/en-us/story/snake-nokia-6110-oral-history-taneli-armanto
-    // Armanto: While testing the early versions of the game, I noticed it was hard
-    // to control the snake upon getting close to and edge but not crashing — especially
-    // in the highest speed levels. I wanted the highest level to be as fast as I could
-    // possibly make the device "run," but on the other hand, I wanted to be friendly
-    // and help the player manage that level. Otherwise it might not be fun to play. So
-    // I implemented a little delay. A few milliseconds of extra time right before
-    // the player crashes, during which she can still change the directions. And if
-    // she does, the game continues.
     GameStateLastChance,
     GameStateGameOver,
 } GameState;
 
-// Note: do not change without purpose. Current values are used in smart
-// orthogonality calculation in `snake_game_get_turn_snake`.
 typedef enum {
     DirectionUp,
     DirectionRight,
@@ -40,11 +31,19 @@ typedef enum {
     DirectionLeft,
 } Direction;
 
-#define MAX_SNAKE_LEN 15 * 31 //128 * 64 / 4
+#define MAX_SNAKE_LEN 15 * 31 //128 * 64 / 4 - 1px border line
 
 #define x_back_symbol 50
 #define y_back_symbol 9
 
+#define x_arrow_left 81
+#define y_arrow_left 20
+
+#define x_arrow_right 104
+#define y_arrow_right 20
+
+#define SAVING_FILENAME APP_DATA_PATH("snake2.save")
+
 typedef struct {
     FuriMutex* mutex;
     Point points[MAX_SNAKE_LEN];
@@ -53,6 +52,9 @@ typedef struct {
     Direction nextMovement; // if backward of currentMovement, ignore
     Point fruit;
     GameState state;
+    bool Endlessmode;
+    uint32_t timer_start_timestamp;
+    uint32_t timer_stopped_seconds;
 } SnakeState;
 
 typedef enum {
@@ -114,7 +116,14 @@ static void snake_game_render_callback(Canvas* const canvas, void* ctx) {
     canvas_draw_rframe(canvas, f.x, f.y, 6, 6, 2);
     canvas_draw_dot(canvas, f.x + 3, f.y - 1);
     canvas_draw_dot(canvas, f.x + 4, f.y - 2);
-    //canvas_draw_dot(canvas,f.x+4,f.y-3);
+
+    //Dot in the middle of an apple (just to know if the Endless mode is turn off)
+    if(!snake_state->Endlessmode) {
+        canvas_draw_dot(canvas, f.x + 2, f.y + 2);
+        canvas_draw_dot(canvas, f.x + 2, f.y + 3);
+        canvas_draw_dot(canvas, f.x + 3, f.y + 2);
+        canvas_draw_dot(canvas, f.x + 3, f.y + 3);
+    }
 
     // Snake
     for(uint16_t i = 0; i < snake_state->len; i++) {
@@ -133,34 +142,53 @@ static void snake_game_render_callback(Canvas* const canvas, void* ctx) {
     if(snake_state->state == GameStatePause || snake_state->state == GameStateGameOver) {
         // Screen is 128x64 px
         canvas_set_color(canvas, ColorWhite);
-        canvas_draw_box(canvas, 33, 19, 64, 26);
+        canvas_draw_box(canvas, 33, 23, 64, 26);
 
         canvas_set_color(canvas, ColorBlack);
-        canvas_draw_frame(canvas, 34, 20, 62, 24);
+        canvas_draw_frame(canvas, 34, 24, 62, 24);
 
         canvas_set_font(canvas, FontPrimary);
         if(snake_state->state == GameStateGameOver) {
-            canvas_draw_str_aligned(canvas, 65, 31, AlignCenter, AlignBottom, "Game Over");
+            if(snake_state->len >= MAX_SNAKE_LEN - 1) {
+                canvas_draw_str_aligned(canvas, 65, 35, AlignCenter, AlignBottom, "You WON!");
+            } else {
+                canvas_draw_str_aligned(canvas, 65, 35, AlignCenter, AlignBottom, "Game Over");
+            }
         }
         if(snake_state->state == GameStatePause) {
-            canvas_draw_str_aligned(canvas, 65, 31, AlignCenter, AlignBottom, "Pause");
+            canvas_draw_str_aligned(canvas, 65, 35, AlignCenter, AlignBottom, "Pause");
         }
 
         canvas_set_font(canvas, FontSecondary);
-        char buffer[20];
+        char buffer[40];
         snprintf(buffer, sizeof(buffer), "Score: %u", snake_state->len - 7U);
-        canvas_draw_str_aligned(canvas, 65, 41, AlignCenter, AlignBottom, buffer);
+        canvas_draw_str_aligned(canvas, 65, 45, AlignCenter, AlignBottom, buffer);
 
-        // Painting "back"-symbol, Help message for Exit App, ProgressBar
+        // Painting "back"-symbol, Help message for Exit App, ProgressBar (Complete %)
         canvas_set_color(canvas, ColorWhite);
-        canvas_draw_box(canvas, 25, 2, 81, 11);
-        canvas_draw_box(canvas, 28, 54, 73, 9);
+        canvas_draw_box(canvas, 22, 1, 87, 22);
+        canvas_draw_box(canvas, 24, 54, 83, 9);
         canvas_set_color(canvas, ColorBlack);
         canvas_draw_str_aligned(
             canvas, 65, 10, AlignCenter, AlignBottom, "Hold        to Exit App");
+        //Endless mode ON/OFF
+        if(snake_state->Endlessmode == false) {
+            canvas_draw_str_aligned(canvas, 24, 21, AlignLeft, AlignBottom, "Endless mode   OFF");
+        } else {
+            canvas_draw_str_aligned(canvas, 24, 21, AlignLeft, AlignBottom, "Endless mode");
+            canvas_draw_str_aligned(canvas, 89, 21, AlignLeft, AlignBottom, "ON");
+        }
+
         snprintf(
-            buffer, sizeof(buffer), "Complete: %-5.1f%%", (double)((snake_state->len - 7U) / 4.58));
-        canvas_draw_str_aligned(canvas, 65, 62, AlignCenter, AlignBottom, buffer);
+            buffer,
+            sizeof(buffer),
+            "%-5.1f%% (%.2ld:%.2ld:%.2ld)",
+            (double)((snake_state->len - 7U) / 4.57),
+            snake_state->timer_stopped_seconds / 60 / 60,
+            snake_state->timer_stopped_seconds / 60 % 60,
+            snake_state->timer_stopped_seconds % 60);
+
+        //Back symbol
         {
             canvas_draw_dot(canvas, x_back_symbol + 0, y_back_symbol);
             canvas_draw_dot(canvas, x_back_symbol + 1, y_back_symbol);
@@ -186,11 +214,81 @@ static void snake_game_render_callback(Canvas* const canvas, void* ctx) {
             canvas_draw_dot(canvas, x_back_symbol - 1, y_back_symbol - 7);
             canvas_draw_dot(canvas, x_back_symbol - 1, y_back_symbol - 3);
         }
+        //Left Arrow
+        canvas_draw_str_aligned(canvas, 65, 62, AlignCenter, AlignBottom, buffer);
+        {
+            canvas_draw_dot(canvas, x_arrow_left + 0, y_arrow_left - 3);
+            canvas_draw_dot(canvas, x_arrow_left + 1, y_arrow_left - 2);
+            canvas_draw_dot(canvas, x_arrow_left + 1, y_arrow_left - 3);
+            canvas_draw_dot(canvas, x_arrow_left + 1, y_arrow_left - 4);
+            canvas_draw_dot(canvas, x_arrow_left + 2, y_arrow_left - 1);
+            canvas_draw_dot(canvas, x_arrow_left + 2, y_arrow_left - 2);
+            canvas_draw_dot(canvas, x_arrow_left + 2, y_arrow_left - 3);
+            canvas_draw_dot(canvas, x_arrow_left + 2, y_arrow_left - 4);
+            canvas_draw_dot(canvas, x_arrow_left + 2, y_arrow_left - 5);
+            canvas_draw_dot(canvas, x_arrow_left + 3, y_arrow_left - 0);
+            canvas_draw_dot(canvas, x_arrow_left + 3, y_arrow_left - 1);
+            canvas_draw_dot(canvas, x_arrow_left + 3, y_arrow_left - 2);
+            canvas_draw_dot(canvas, x_arrow_left + 3, y_arrow_left - 3);
+            canvas_draw_dot(canvas, x_arrow_left + 3, y_arrow_left - 4);
+            canvas_draw_dot(canvas, x_arrow_left + 3, y_arrow_left - 5);
+            canvas_draw_dot(canvas, x_arrow_left + 3, y_arrow_left - 6);
+        }
+
+        //Right Arrow
+        canvas_draw_str_aligned(canvas, 65, 62, AlignCenter, AlignBottom, buffer);
+        {
+            canvas_draw_dot(canvas, x_arrow_right + 0, y_arrow_right - 0);
+            canvas_draw_dot(canvas, x_arrow_right + 0, y_arrow_right - 1);
+            canvas_draw_dot(canvas, x_arrow_right + 0, y_arrow_right - 2);
+            canvas_draw_dot(canvas, x_arrow_right + 0, y_arrow_right - 3);
+            canvas_draw_dot(canvas, x_arrow_right + 0, y_arrow_right - 4);
+            canvas_draw_dot(canvas, x_arrow_right + 0, y_arrow_right - 5);
+            canvas_draw_dot(canvas, x_arrow_right + 0, y_arrow_right - 6);
+            canvas_draw_dot(canvas, x_arrow_right + 1, y_arrow_right - 1);
+            canvas_draw_dot(canvas, x_arrow_right + 1, y_arrow_right - 2);
+            canvas_draw_dot(canvas, x_arrow_right + 1, y_arrow_right - 3);
+            canvas_draw_dot(canvas, x_arrow_right + 1, y_arrow_right - 4);
+            canvas_draw_dot(canvas, x_arrow_right + 1, y_arrow_right - 5);
+            canvas_draw_dot(canvas, x_arrow_right + 2, y_arrow_right - 2);
+            canvas_draw_dot(canvas, x_arrow_right + 2, y_arrow_right - 3);
+            canvas_draw_dot(canvas, x_arrow_right + 2, y_arrow_right - 4);
+            canvas_draw_dot(canvas, x_arrow_right + 3, y_arrow_right - 3);
+        }
     }
 
     furi_mutex_release(snake_state->mutex);
 }
 
+bool load_game(SnakeState* snake_state) {
+    Storage* storage = furi_record_open(RECORD_STORAGE);
+
+    File* file = storage_file_alloc(storage);
+    uint16_t bytes_readed = 0;
+    if(storage_file_open(file, SAVING_FILENAME, FSAM_READ, FSOM_OPEN_EXISTING)) {
+        bytes_readed = storage_file_read(file, snake_state, sizeof(SnakeState));
+    }
+    storage_file_close(file);
+    storage_file_free(file);
+
+    furi_record_close(RECORD_STORAGE);
+
+    return bytes_readed == sizeof(SnakeState);
+}
+
+void save_game(const SnakeState* snake_state) {
+    Storage* storage = furi_record_open(RECORD_STORAGE);
+
+    File* file = storage_file_alloc(storage);
+    if(storage_file_open(file, SAVING_FILENAME, FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
+        storage_file_write(file, snake_state, sizeof(SnakeState));
+    }
+    storage_file_close(file);
+    storage_file_free(file);
+
+    furi_record_close(RECORD_STORAGE);
+}
+
 static void snake_game_input_callback(InputEvent* input_event, void* ctx) {
     furi_assert(ctx);
     FuriMessageQueue* event_queue = ctx;
@@ -220,49 +318,56 @@ static void snake_game_init_game(SnakeState* const snake_state) {
     Point f = {18, 6};
     snake_state->fruit = f;
 
+    DateTime curr_dt;
+    furi_hal_rtc_get_datetime(&curr_dt);
+    uint32_t curr_ts = datetime_datetime_to_timestamp(&curr_dt);
+
+    snake_state->timer_stopped_seconds = 0;
+    snake_state->timer_start_timestamp = curr_ts;
+
     snake_state->state = GameStateLife;
+
+    save_game(snake_state);
 }
 
 static Point snake_game_get_new_fruit(SnakeState const* const snake_state) {
-    // 1 bit for each point on the playing field where the snake can turn
-    // and where the fruit can appear
-    uint16_t buffer[8];
-    memset(buffer, 0, sizeof(buffer));
-    uint8_t empty = 8 * 16;
-
-    for(uint16_t i = 0; i < snake_state->len; i++) {
-        Point p = snake_state->points[i];
-
-        if(p.x % 2 != 0 || p.y % 2 != 0) {
-            continue;
-        }
-        p.x /= 2;
-        p.y /= 2;
+    // Max number of fruits on x axis = (16 * 2) - 1 = 31 (0<=x=>30)
+    // Max number of fruits on y axis = (8 * 2)  - 1 = 15 (0<=y=>14)
+    // Total fields for fruits and snake body = 31 * 15 = 465
+    // Empty fields for next random fruit = 465 - len(snake)
+
+    bool* all_fields;
+    int* empty_fields;
+    all_fields = (bool*)malloc(MAX_SNAKE_LEN * sizeof(bool));
+    empty_fields = (int*)malloc(MAX_SNAKE_LEN * sizeof(int));
+
+    for(uint16_t j = 0; j < MAX_SNAKE_LEN; j++) {
+        all_fields[j] = false;
+    }
 
-        buffer[p.y] |= 1 << p.x;
-        empty--;
+    for(uint16_t j = 0; j < snake_state->len; j++) {
+        Point p = snake_state->points[j];
+        all_fields[p.x + 31 * p.y] = true;
     }
-    // Bit set if snake use that playing field
-
-    uint16_t newFruit = rand() % empty;
-
-    // Skip random number of _empty_ fields
-    for(uint8_t y = 0; y < 8; y++) {
-        for(uint16_t x = 0, mask = 1; x < 16; x += 1, mask <<= 1) {
-            if((buffer[y] & mask) == 0) {
-                if(newFruit == 0) {
-                    Point p = {
-                        .x = x * 2,
-                        .y = y * 2,
-                    };
-                    return p;
-                }
-                newFruit--;
-            }
+
+    int empty_counter = 0;
+    for(uint16_t j = 0; j < MAX_SNAKE_LEN; j++) {
+        if(!all_fields[j]) {
+            empty_fields[empty_counter] = j;
+            empty_counter++;
         }
     }
-    // We will never be here
-    Point p = {0, 0};
+
+    int newFruit = rand() % empty_counter;
+
+    Point p = {
+        .x = empty_fields[newFruit] % 31,
+        .y = empty_fields[newFruit] / 31,
+    };
+
+    free(all_fields);
+    free(empty_fields);
+
     return p;
 }
 
@@ -334,7 +439,17 @@ static void
             snake_state->state = GameStateLastChance;
             return;
         } else if(snake_state->state == GameStateLastChance) {
-            snake_state->state = GameStateGameOver;
+            if(snake_state->Endlessmode) {
+                snake_state->state = GameStateLastChance;
+            } else {
+                DateTime curr_dt;
+                furi_hal_rtc_get_datetime(&curr_dt);
+                uint32_t curr_ts = datetime_datetime_to_timestamp(&curr_dt);
+
+                snake_state->timer_stopped_seconds = curr_ts - snake_state->timer_start_timestamp;
+
+                snake_state->state = GameStateGameOver;
+            }
             notification_message_block(notification, &sequence_fail);
             return;
         }
@@ -346,7 +461,17 @@ static void
 
     crush = snake_game_collision_with_tail(snake_state, next_step);
     if(crush) {
-        snake_state->state = GameStateGameOver;
+        if(snake_state->Endlessmode) {
+            snake_state->state = GameStateLastChance;
+        } else {
+            DateTime curr_dt;
+            furi_hal_rtc_get_datetime(&curr_dt);
+            uint32_t curr_ts = datetime_datetime_to_timestamp(&curr_dt);
+
+            snake_state->timer_stopped_seconds = curr_ts - snake_state->timer_start_timestamp;
+
+            snake_state->state = GameStateGameOver;
+        }
         notification_message_block(notification, &sequence_fail);
         return;
     }
@@ -354,8 +479,23 @@ static void
     bool eatFruit = (next_step.x == snake_state->fruit.x) && (next_step.y == snake_state->fruit.y);
     if(eatFruit) {
         snake_state->len++;
-        if(snake_state->len >= MAX_SNAKE_LEN) {
+
+        if(snake_state->len >= MAX_SNAKE_LEN - 1) {
             //You win!!!
+            //It's impossible to collect ALL fruits, because
+            //the number of rows is odd (15),
+            //the number of columnss is odd too (31).
+            //You just can't locate the snake's body
+            //on the odd number of cells.
+            //Because of it you win when you collect
+            //all but one fruits.
+
+            DateTime curr_dt;
+            furi_hal_rtc_get_datetime(&curr_dt);
+            uint32_t curr_ts = datetime_datetime_to_timestamp(&curr_dt);
+
+            snake_state->timer_stopped_seconds = curr_ts - snake_state->timer_start_timestamp;
+
             snake_state->state = GameStateGameOver;
             notification_message_block(notification, &sequence_fail);
             return;
@@ -377,7 +517,16 @@ int32_t snake_20_app(void* p) {
     FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(SnakeEvent));
 
     SnakeState* snake_state = malloc(sizeof(SnakeState));
-    snake_game_init_game(snake_state);
+    if(!load_game(snake_state)) {
+        snake_game_init_game(snake_state);
+    } else {
+        DateTime curr_dt;
+        furi_hal_rtc_get_datetime(&curr_dt);
+        uint32_t curr_ts = datetime_datetime_to_timestamp(&curr_dt);
+
+        snake_state->timer_start_timestamp = curr_ts - snake_state->timer_stopped_seconds;
+        snake_state->state = GameStateLife;
+    }
 
     snake_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
     if(!snake_state->mutex) {
@@ -426,12 +575,18 @@ int32_t snake_20_app(void* p) {
                         }
                         break;
                     case InputKeyRight:
-                        if(snake_state->state != GameStatePause) {
+                        if(snake_state->state == GameStatePause ||
+                           snake_state->state == GameStateGameOver) {
+                            snake_state->Endlessmode = !snake_state->Endlessmode;
+                        } else {
                             snake_state->nextMovement = DirectionRight;
                         }
                         break;
                     case InputKeyLeft:
-                        if(snake_state->state != GameStatePause) {
+                        if(snake_state->state == GameStatePause ||
+                           snake_state->state == GameStateGameOver) {
+                            snake_state->Endlessmode = !snake_state->Endlessmode;
+                        } else {
                             snake_state->nextMovement = DirectionLeft;
                         }
                         break;
@@ -440,6 +595,13 @@ int32_t snake_20_app(void* p) {
                             snake_game_init_game(snake_state);
                         }
                         if(snake_state->state == GameStatePause) {
+                            DateTime curr_dt;
+                            furi_hal_rtc_get_datetime(&curr_dt);
+                            uint32_t curr_ts = datetime_datetime_to_timestamp(&curr_dt);
+
+                            snake_state->timer_start_timestamp =
+                                curr_ts - snake_state->timer_stopped_seconds;
+
                             furi_timer_start(timer, furi_kernel_get_tick_frequency() / 4);
                             snake_state->state = GameStateLife;
                         }
@@ -448,16 +610,16 @@ int32_t snake_20_app(void* p) {
                         if(snake_state->state == GameStateLife) {
                             furi_timer_stop(timer);
                             snake_state->state = GameStatePause;
+
+                            DateTime curr_dt;
+                            furi_hal_rtc_get_datetime(&curr_dt);
+                            uint32_t curr_ts = datetime_datetime_to_timestamp(&curr_dt);
+
+                            snake_state->timer_stopped_seconds =
+                                curr_ts - snake_state->timer_start_timestamp;
+
                             break;
                         }
-                        if(snake_state->state == GameStatePause) {
-                            furi_timer_start(timer, furi_kernel_get_tick_frequency() / 4);
-                            snake_state->state = GameStateLife;
-                            break;
-                        }
-                        if(snake_state->state == GameStateGameOver) {
-                            snake_game_init_game(snake_state);
-                        }
                     default:
                         break;
                     }
@@ -468,29 +630,63 @@ int32_t snake_20_app(void* p) {
                     case InputKeyUp:
                         if(snake_state->state != GameStatePause) {
                             snake_state->nextMovement = DirectionUp;
-                            furi_timer_start(timer, furi_kernel_get_tick_frequency() / 8);
+                            //Speed Up
+                            if(snake_state->currentMovement == DirectionUp) {
+                                furi_timer_start(timer, furi_kernel_get_tick_frequency() / 8);
+                            }
+                            //Breaking
+                            if(snake_state->currentMovement == DirectionDown) {
+                                furi_timer_start(timer, furi_kernel_get_tick_frequency() / 2);
+                            }
                         }
                         break;
                     case InputKeyDown:
                         if(snake_state->state != GameStatePause) {
                             snake_state->nextMovement = DirectionDown;
-                            furi_timer_start(timer, furi_kernel_get_tick_frequency() / 8);
+                            //Speed Up
+                            if(snake_state->currentMovement == DirectionDown) {
+                                furi_timer_start(timer, furi_kernel_get_tick_frequency() / 8);
+                            }
+                            //Breaking
+                            if(snake_state->currentMovement == DirectionUp) {
+                                furi_timer_start(timer, furi_kernel_get_tick_frequency() / 2);
+                            }
                         }
                         break;
                     case InputKeyRight:
                         if(snake_state->state != GameStatePause) {
                             snake_state->nextMovement = DirectionRight;
-                            furi_timer_start(timer, furi_kernel_get_tick_frequency() / 8);
+                            //Speed Up
+                            if(snake_state->currentMovement == DirectionRight) {
+                                furi_timer_start(timer, furi_kernel_get_tick_frequency() / 8);
+                            }
+                            //Breaking
+                            if(snake_state->currentMovement == DirectionLeft) {
+                                furi_timer_start(timer, furi_kernel_get_tick_frequency() / 2);
+                            }
                         }
                         break;
                     case InputKeyLeft:
                         if(snake_state->state != GameStatePause) {
                             snake_state->nextMovement = DirectionLeft;
-                            furi_timer_start(timer, furi_kernel_get_tick_frequency() / 8);
+                            //Speed Up
+                            if(snake_state->currentMovement == DirectionLeft) {
+                                furi_timer_start(timer, furi_kernel_get_tick_frequency() / 8);
+                            }
+                            //Breaking
+                            if(snake_state->currentMovement == DirectionRight) {
+                                furi_timer_start(timer, furi_kernel_get_tick_frequency() / 2);
+                            }
                         }
                         break;
                     case InputKeyBack:
-                        processing = false;
+                        if(snake_state->state == GameStatePause ||
+                           snake_state->state == GameStateGameOver) {
+                            save_game(snake_state);
+                            processing = false;
+                        } else {
+                            snake_state->state = GameStateGameOver;
+                        }
                         break;
                     default:
                         break;