Explorar o código

update 2 apps

MX %!s(int64=2) %!d(string=hai) anos
pai
achega
5b5c743a64

+ 21 - 0
base_pack/solitaire/LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2023 Tibor Tálosi
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 9 - 3
base_pack/solitaire/defines.h

@@ -19,10 +19,15 @@ typedef struct {
     InputEvent input;
 } AppEvent;
 
-typedef enum { GameStateGameOver, GameStateStart, GameStatePlay, GameStateAnimate } PlayState;
+typedef enum {
+    GameStateGameOver,
+    GameStateStart,
+    GameStatePlay,
+    GameStateAnimate
+} PlayState;
 
 typedef struct {
-    uint8_t* buffer;
+    uint8_t *buffer;
     Card card;
     int8_t deck;
     int indexes[4];
@@ -44,6 +49,7 @@ typedef struct {
     InputKey input;
 
     bool started;
+    bool had_change;
     bool processing;
     bool longPress;
     PlayState state;
@@ -52,6 +58,6 @@ typedef struct {
     uint8_t selectColumn;
     int8_t selected_card;
     CardAnimation animation;
-    uint8_t* buffer;
+    uint8_t *buffer;
     FuriMutex* mutex;
 } GameState;

+ 296 - 304
base_pack/solitaire/solitaire.c

@@ -7,257 +7,235 @@
 #include "solitaire_icons.h"
 #include <notification/notification.h>
 #include <notification/notification_messages.h>
-void init(GameState* game_state);
+void init(GameState *game_state);
+
 const NotificationSequence sequence_fail = {
-    &message_vibro_on,
-    &message_note_c4,
-    &message_delay_10,
-    &message_vibro_off,
-    &message_sound_off,
-    &message_delay_10,
-
-    &message_vibro_on,
-    &message_note_a3,
-    &message_delay_10,
-    &message_vibro_off,
-    &message_sound_off,
-    NULL,
+        &message_vibro_on,
+        &message_note_c4,
+        &message_delay_10,
+        &message_vibro_off,
+        &message_sound_off,
+        &message_delay_10,
+
+        &message_vibro_on,
+        &message_note_a3,
+        &message_delay_10,
+        &message_vibro_off,
+        &message_sound_off,
+        NULL,
 };
 int8_t columns[7][3] = {
-    {1, 1, 25},
-    {19, 1, 25},
-    {37, 1, 25},
-    {55, 1, 25},
-    {73, 1, 25},
-    {91, 1, 25},
-    {109, 1, 25},
+        {1,   1, 25},
+        {19,  1, 25},
+        {37,  1, 25},
+        {55,  1, 25},
+        {73,  1, 25},
+        {91,  1, 25},
+        {109, 1, 25},
 };
 
 bool can_place_card(Card where, Card what) {
-    FURI_LOG_D(
-        APP_NAME,
-        "TESTING pip %i, letter %i with pip %i, letter %i",
-        where.pip,
-        where.character,
-        what.pip,
-        what.character);
     bool a_black = where.pip == 0 || where.pip == 3;
     bool b_black = what.pip == 0 || what.pip == 3;
-    if(a_black == b_black) return false;
+    if (a_black == b_black) return false;
 
-    int8_t a_letter = (int8_t)where.character;
-    int8_t b_letter = (int8_t)what.character;
-    if(a_letter == 12) a_letter = -1;
-    if(b_letter == 12) b_letter = -1;
+    int8_t a_letter = (int8_t) where.character;
+    int8_t b_letter = (int8_t) what.character;
+    if (a_letter == 12) a_letter = -1;
+    if (b_letter == 12) b_letter = -1;
 
     return (a_letter - 1) == b_letter;
 }
 
-static void draw_scene(Canvas* const canvas, const GameState* game_state) {
-    int deckIndex = game_state->deck.index;
-    if(game_state->dragging_deck) deckIndex--;
-
-    if((game_state->deck.index < (game_state->deck.card_count - 1) ||
-        game_state->deck.index == -1) &&
-       game_state->deck.card_count > 0) {
-        draw_card_back_at(columns[0][0], columns[0][1], canvas);
-        if(game_state->selectRow == 0 && game_state->selectColumn == 0) {
-            draw_rounded_box(
-                canvas,
-                columns[0][0] + 1,
-                columns[0][1] + 1,
-                CARD_WIDTH - 2,
-                CARD_HEIGHT - 2,
-                Inverse);
-        }
-    } else
-        draw_card_space(
-            columns[0][0],
-            columns[0][1],
-            game_state->selectRow == 0 && game_state->selectColumn == 0,
-            canvas);
-    //deck side
-    if(deckIndex >= 0) {
-        Card c = game_state->deck.cards[deckIndex];
-        draw_card_at_colored(
-            columns[1][0],
-            columns[1][1],
-            c.pip,
-            c.character,
-            game_state->selectRow == 0 && game_state->selectColumn == 1,
-            canvas);
-    } else
-        draw_card_space(
-            columns[1][0],
-            columns[1][1],
-            game_state->selectRow == 0 && game_state->selectColumn == 1,
-            canvas);
-
-    for(uint8_t i = 0; i < 4; i++) {
-        Card current = game_state->top_cards[i];
-        bool selected = game_state->selectRow == 0 && game_state->selectColumn == (i + 3);
-        if(current.disabled) {
-            draw_card_space(columns[i + 3][0], columns[i + 3][1], selected, canvas);
-        } else {
-            draw_card_at(
-                columns[i + 3][0], columns[i + 3][1], current.pip, current.character, canvas);
-            if(selected) {
-                draw_rounded_box(
-                    canvas, columns[i + 3][0], columns[i + 3][1], CARD_WIDTH, CARD_HEIGHT, Inverse);
+static void draw_scene(Canvas *const canvas, const GameState *game_state) {
+
+    if(game_state->had_change){
+        int deckIndex = game_state->deck.index;
+        if (game_state->dragging_deck)
+            deckIndex--;
+
+        if ((game_state->deck.index < (game_state->deck.card_count - 1) || game_state->deck.index == -1) && game_state->deck.card_count>0) {
+            draw_card_back_at(columns[0][0], columns[0][1], canvas);
+            if (game_state->selectRow == 0 && game_state->selectColumn == 0) {
+                draw_rounded_box(canvas, columns[0][0] + 1, columns[0][1] + 1, CARD_WIDTH - 2, CARD_HEIGHT - 2,
+                                 Inverse);
+            }
+        } else
+            draw_card_space(columns[0][0], columns[0][1],
+                            game_state->selectRow == 0 && game_state->selectColumn == 0,
+                            canvas);
+        //deck side
+        if (deckIndex >= 0) {
+            Card c = game_state->deck.cards[deckIndex];
+            draw_card_at_colored(columns[1][0], columns[1][1], c.pip, c.character,
+                                 game_state->selectRow == 0 && game_state->selectColumn == 1, canvas);
+        } else
+            draw_card_space(columns[1][0], columns[1][1],
+                            game_state->selectRow == 0 && game_state->selectColumn == 1,
+                            canvas);
+
+        for (uint8_t i = 0; i < 4; i++) {
+            Card current = game_state->top_cards[i];
+            bool selected = game_state->selectRow == 0 && game_state->selectColumn == (i + 3);
+            if (current.disabled) {
+                draw_card_space(columns[i + 3][0], columns[i + 3][1], selected, canvas);
+            } else {
+                draw_card_at(columns[i + 3][0], columns[i + 3][1], current.pip, current.character, canvas);
+                if (selected) {
+                    draw_rounded_box(canvas, columns[i + 3][0], columns[i + 3][1], CARD_WIDTH, CARD_HEIGHT,
+                                     Inverse);
+                }
             }
         }
-    }
 
-    for(uint8_t i = 0; i < 7; i++) {
-        bool selected = game_state->selectRow == 1 && game_state->selectColumn == i;
-        int8_t index = (game_state->bottom_columns[i].index - 1 - game_state->selected_card);
-        if(index < 0) index = 0;
-        draw_hand_column(
-            game_state->bottom_columns[i],
-            columns[i][0],
-            columns[i][2],
-            selected ? index : -1,
-            canvas);
-    }
+        for (uint8_t i = 0; i < 7; i++) {
+            bool selected = game_state->selectRow == 1 && game_state->selectColumn == i;
+            int8_t index= (game_state->bottom_columns[i].index - 1 - game_state->selected_card);
+            if(index<0)index=0;
+            draw_hand_column(game_state->bottom_columns[i], columns[i][0], columns[i][2],
+                             selected ? index : -1, canvas);
+        }
 
-    int8_t pos[2] = {
-        columns[game_state->selectColumn][0],
-        columns[game_state->selectColumn][game_state->selectRow + 1]};
+        int8_t pos[2] = {columns[game_state->selectColumn][0],
+                         columns[game_state->selectColumn][game_state->selectRow + 1]};
 
-    /*     draw_icon_clip(canvas, &I_card_graphics, pos[0] + CARD_HALF_WIDTH, pos[1] + CARD_HALF_HEIGHT, 30, 5, 5, 5,
-                        Filled);*/
+        /*     draw_icon_clip(canvas, &I_card_graphics, pos[0] + CARD_HALF_WIDTH, pos[1] + CARD_HALF_HEIGHT, 30, 5, 5, 5,
+                            Filled);*/
+
+        if (game_state->dragging_hand.index > 0) {
+            draw_hand_column(game_state->dragging_hand,
+                             pos[0] + CARD_HALF_WIDTH + 3, pos[1] + CARD_HALF_HEIGHT + 3,
+                             -1, canvas);
+        }
 
-    if(game_state->dragging_hand.index > 0) {
-        draw_hand_column(
-            game_state->dragging_hand,
-            pos[0] + CARD_HALF_WIDTH + 3,
-            pos[1] + CARD_HALF_HEIGHT + 3,
-            -1,
-            canvas);
+        clone_buffer(get_buffer(canvas), game_state->animation.buffer);
+    }else{
+        clone_buffer(game_state->animation.buffer, get_buffer(canvas));
     }
 }
 
-static void draw_animation(Canvas* const canvas, const GameState* game_state) {
-    if(!game_state->animation.started) {
+static void draw_animation(Canvas *const canvas, const GameState *game_state) {
+    if (!game_state->animation.started) {
         draw_scene(canvas, game_state);
     } else {
         clone_buffer(game_state->animation.buffer, get_buffer(canvas));
 
-        draw_card_at(
-            (int8_t)game_state->animation.x,
-            (int8_t)game_state->animation.y,
-            game_state->animation.card.pip,
-            game_state->animation.card.character,
-            canvas);
+        draw_card_at((int8_t) game_state->animation.x, (int8_t) game_state->animation.y, game_state->animation.card.pip,
+                     game_state->animation.card.character, canvas);
+
     }
 
     clone_buffer(get_buffer(canvas), game_state->animation.buffer);
 }
 
-static void render_callback(Canvas* const canvas, void* ctx) {
-    furi_assert(ctx);
-    const GameState* game_state = ctx;
-    furi_mutex_acquire(game_state->mutex, FuriWaitForever);
-
-    switch(game_state->state) {
-    case GameStateAnimate:
-        draw_animation(canvas, game_state);
-        break;
-    case GameStateStart:
-        canvas_draw_icon(canvas, 0, 0, &I_solitaire_main);
-        break;
-    case GameStatePlay:
-        draw_scene(canvas, game_state);
-        break;
-    default:
-        break;
+static void render_callback(Canvas *const canvas, void *ctx) {
+    const GameState *game_state = ctx;
+    furi_mutex_acquire(game_state->mutex, 25);
+    if (game_state == NULL) {
+        return;
+    }
+
+    switch (game_state->state) {
+        case GameStateAnimate:
+            draw_animation(canvas, game_state);
+            break;
+        case GameStateStart:
+            canvas_draw_icon(canvas, 0, 0, &I_solitaire_main);
+            break;
+        case GameStatePlay:
+            draw_scene(canvas, game_state);
+            break;
+        default:
+            break;
     }
 
     furi_mutex_release(game_state->mutex);
+
 }
 
-void remove_drag(GameState* game_state) {
-    if(game_state->dragging_deck) {
+void remove_drag(GameState *game_state) {
+    if (game_state->dragging_deck) {
         remove_from_deck(game_state->deck.index, &(game_state->deck));
         game_state->dragging_deck = false;
-    } else if(game_state->dragging_column < 7) {
+    } else if (game_state->dragging_column < 7) {
         game_state->dragging_column = 8;
     }
     game_state->dragging_hand.index = 0;
 }
 
-bool handleInput(GameState* game_state) {
+bool handleInput(GameState *game_state) {
     Hand currentHand = game_state->bottom_columns[game_state->selectColumn];
-    switch(game_state->input) {
-    case InputKeyUp:
-        if(game_state->selectRow > 0) {
-            int first = first_non_flipped_card(currentHand);
-            first = currentHand.index - first;
-            if((first - 1) > game_state->selected_card && game_state->dragging_hand.index == 0 &&
-               !game_state->longPress) {
-                game_state->selected_card++;
+    switch (game_state->input) {
+        case InputKeyUp:
+            if (game_state->selectRow > 0) {
+                int first = first_non_flipped_card(currentHand);
+                first = currentHand.index - first;
+                if ((first - 1) > game_state->selected_card && game_state->dragging_hand.index == 0 &&
+                    !game_state->longPress) {
+                    game_state->selected_card++;
+                } else {
+                    game_state->selectRow--;
+                    game_state->selected_card = 0;
+                }
+            }
+            break;
+        case InputKeyDown:
+            if (game_state->selectRow < 1) {
+                game_state->selectRow++;
+                game_state->selected_card = 0;
             } else {
-                game_state->selectRow--;
+                if (game_state->selected_card > 0) {
+                    if (game_state->longPress)
+                        game_state->selected_card = 0;
+                    else
+                        game_state->selected_card--;
+                }
+            }
+            break;
+        case InputKeyRight:
+            if (game_state->selectColumn < 6) {
+                game_state->selectColumn++;
                 game_state->selected_card = 0;
             }
-        }
-        break;
-    case InputKeyDown:
-        if(game_state->selectRow < 1) {
-            game_state->selectRow++;
-            game_state->selected_card = 0;
-        } else {
-            if(game_state->selected_card > 0) {
-                if(game_state->longPress)
-                    game_state->selected_card = 0;
-                else
-                    game_state->selected_card--;
+            break;
+        case InputKeyLeft:
+            if (game_state->selectColumn > 0) {
+                game_state->selectColumn--;
+                game_state->selected_card = 0;
             }
-        }
-        break;
-    case InputKeyRight:
-        if(game_state->selectColumn < 6) {
-            game_state->selectColumn++;
-            game_state->selected_card = 0;
-        }
-        break;
-    case InputKeyLeft:
-        if(game_state->selectColumn > 0) {
-            game_state->selectColumn--;
-            game_state->selected_card = 0;
-        }
-        break;
-    case InputKeyOk:
-        return true;
-        break;
-    default:
-        break;
+            break;
+        case InputKeyOk:
+            return true;
+            break;
+        default:
+            break;
     }
-    if(game_state->selectRow == 0 && game_state->selectColumn == 2) {
-        if(game_state->input == InputKeyRight)
+    if (game_state->selectRow == 0 && game_state->selectColumn == 2) {
+        if (game_state->input == InputKeyRight)
             game_state->selectColumn++;
         else
             game_state->selectColumn--;
     }
-    if(game_state->dragging_hand.index > 0) game_state->selected_card = 0;
+    if (game_state->dragging_hand.index > 0)
+        game_state->selected_card = 0;
     return false;
 }
 
-bool place_on_top(Card* where, Card what) {
-    if(where->disabled && what.character == 12) {
+bool place_on_top(Card *where, Card what) {
+    if (where->disabled && what.character == 12) {
         where->disabled = what.disabled;
         where->pip = what.pip;
         where->character = what.character;
         return true;
-    } else if(where->pip == what.pip) {
-        int8_t a_letter = (int8_t)where->character;
-        int8_t b_letter = (int8_t)what.character;
-        if(a_letter == 12) a_letter = -1;
-        if(b_letter == 12) b_letter = -1;
-
-        if(where->disabled && b_letter != -1) return false;
-
-        if((a_letter + 1) == b_letter) {
+    } else if (where->pip == what.pip) {
+        int8_t a_letter = (int8_t) where->character;
+        int8_t b_letter = (int8_t) what.character;
+        if (a_letter == 12) a_letter = -1;
+        if (b_letter == 12) b_letter = -1;
+        if(where->disabled && b_letter!=-1)
+            return false;
+
+        if ((a_letter + 1) == b_letter) {
             where->disabled = what.disabled;
             where->pip = what.pip;
             where->character = what.character;
@@ -267,92 +245,91 @@ bool place_on_top(Card* where, Card what) {
     return false;
 }
 
-void tick(GameState* game_state, NotificationApp* notification) {
+void tick(GameState *game_state, NotificationApp *notification) {
     game_state->last_tick = furi_get_tick();
     uint8_t row = game_state->selectRow;
     uint8_t column = game_state->selectColumn;
-    if(game_state->state != GameStatePlay && game_state->state != GameStateAnimate) return;
+    if (game_state->state != GameStatePlay && game_state->state != GameStateAnimate) return;
     bool wasAction = false;
-    if(game_state->state == GameStatePlay) {
-        if(game_state->top_cards[0].character == 11 && game_state->top_cards[1].character == 11 &&
-           game_state->top_cards[2].character == 11 && game_state->top_cards[3].character == 11) {
-            dolphin_deed(DolphinDeedPluginGameWin);
+    if (game_state->state == GameStatePlay) {
+        if (game_state->top_cards[0].character == 11 && game_state->top_cards[1].character == 11 &&
+            game_state->top_cards[2].character == 11 && game_state->top_cards[3].character == 11) {
             game_state->state = GameStateAnimate;
+            game_state->had_change=true;
+            dolphin_deed(DolphinDeedPluginGameWin);
+
             return;
         }
     }
-    if(handleInput(game_state)) {
-        if(game_state->state == GameStatePlay) {
-            if(game_state->longPress && game_state->dragging_hand.index == 1) {
-                for(uint8_t i = 0; i < 4; i++) {
-                    if(place_on_top(
-                           &(game_state->top_cards[i]), game_state->dragging_hand.cards[0])) {
+    if (handleInput(game_state)) {
+        if (game_state->state == GameStatePlay) {
+            if (game_state->longPress && game_state->dragging_hand.index == 1) {
+                for (uint8_t i = 0; i < 4; i++) {
+                    if (place_on_top(&(game_state->top_cards[i]), game_state->dragging_hand.cards[0])) {
                         remove_drag(game_state);
                         wasAction = true;
                         break;
                     }
                 }
             } else {
-                if(row == 0 && column == 0 && game_state->dragging_hand.index == 0) {
+                if (row == 0 && column == 0 && game_state->dragging_hand.index == 0) {
                     FURI_LOG_D(APP_NAME, "Drawing card");
                     game_state->deck.index++;
                     wasAction = true;
-                    if(game_state->deck.index >= (game_state->deck.card_count))
+                    if (game_state->deck.index >= (game_state->deck.card_count))
                         game_state->deck.index = -1;
                 }
-                //pick/place from deck
-                else if(row == 0 && column == 1) {
+                    //pick/place from deck
+                else if (row == 0 && column == 1) {
                     //place
-                    if(game_state->dragging_deck) {
+                    if (game_state->dragging_deck) {
                         wasAction = true;
                         game_state->dragging_deck = false;
                         game_state->dragging_hand.index = 0;
                     }
-                    //pick
+                        //pick
                     else {
-                        if(game_state->dragging_hand.index == 0 && game_state->deck.index >= 0) {
+                        if (game_state->dragging_hand.index == 0 && game_state->deck.index >= 0) {
                             wasAction = true;
                             game_state->dragging_deck = true;
-                            add_to_hand(
-                                &(game_state->dragging_hand),
-                                game_state->deck.cards[game_state->deck.index]);
+                            add_to_hand(&(game_state->dragging_hand), game_state->deck.cards[game_state->deck.index]);
                         }
                     }
                 }
-                //place on top row
-                else if(row == 0 && game_state->dragging_hand.index == 1) {
+                    //place on top row
+                else if (row == 0 && game_state->dragging_hand.index == 1) {
                     column -= 3;
                     Card currCard = game_state->dragging_hand.cards[0];
                     wasAction = place_on_top(&(game_state->top_cards[column]), currCard);
-                    if(wasAction) remove_drag(game_state);
+                    if (wasAction)
+                        remove_drag(game_state);
                 }
-                //pick/place from bottom
-                else if(row == 1) {
-                    Hand* curr_hand = &(game_state->bottom_columns[column]);
+                    //pick/place from bottom
+                else if (row == 1) {
+                    Hand *curr_hand = &(game_state->bottom_columns[column]);
                     //pick up
-                    if(game_state->dragging_hand.index == 0) {
+                    if (game_state->dragging_hand.index == 0) {
                         Card curr_card = curr_hand->cards[curr_hand->index - 1];
-                        if(curr_card.flipped) {
+                        if (curr_card.flipped) {
                             curr_hand->cards[curr_hand->index - 1].flipped = false;
                             wasAction = true;
                         } else {
-                            if(curr_hand->index > 0) {
-                                extract_hand_region(
-                                    curr_hand,
-                                    &(game_state->dragging_hand),
-                                    curr_hand->index - game_state->selected_card - 1);
+                            if (curr_hand->index > 0) {
+                                extract_hand_region(curr_hand, &(game_state->dragging_hand),
+                                                    curr_hand->index - game_state->selected_card - 1);
                                 game_state->selected_card = 0;
                                 game_state->dragging_column = column;
                                 wasAction = true;
                             }
                         }
                     }
-                    //place
+                        //place
                     else {
                         Card first = game_state->dragging_hand.cards[0];
-                        if(game_state->dragging_column == column ||
-                           (curr_hand->index == 0 && first.character == 11) ||
-                           can_place_card(curr_hand->cards[curr_hand->index - 1], first)) {
+                        if (game_state->dragging_column == column ||
+                            (curr_hand->index == 0 && first.character == 11) ||
+                            can_place_card(curr_hand->cards[curr_hand->index - 1], first)
+                                ) {
                             add_hand_region(curr_hand, &(game_state->dragging_hand));
                             remove_drag(game_state);
                             wasAction = true;
@@ -361,34 +338,41 @@ void tick(GameState* game_state, NotificationApp* notification) {
                 }
             }
 
-            if(!wasAction) {
+
+            if (!wasAction) {
                 notification_message(notification, &sequence_fail);
             }
         }
     }
-    if(game_state->state == GameStateAnimate) {
-        if(game_state->animation.started && !game_state->longPress &&
-           game_state->input == InputKeyOk) {
+    if (game_state->state == GameStateAnimate) {
+        if (game_state->animation.started && !game_state->longPress && game_state->input==InputKeyOk) {
             init(game_state);
             game_state->state = GameStateStart;
         }
 
         game_state->animation.started = true;
-        if(game_state->animation.x < -20 || game_state->animation.x > 128) {
+        if (game_state->animation.x < -20 || game_state->animation.x > 128) {
             game_state->animation.deck++;
-            if(game_state->animation.deck > 3) game_state->animation.deck = 0;
+            if (game_state->animation.deck > 3)
+                game_state->animation.deck = 0;
             int8_t cardIndex = 11 - game_state->animation.indexes[game_state->animation.deck];
 
-            if(game_state->animation.indexes[0] == 13 && game_state->animation.indexes[1] == 13 &&
-               game_state->animation.indexes[2] == 13 && game_state->animation.indexes[3] == 13) {
+            if (game_state->animation.indexes[0] == 13 &&
+                game_state->animation.indexes[1] == 13 &&
+                game_state->animation.indexes[2] == 13 &&
+                game_state->animation.indexes[3] == 13) {
                 init(game_state);
                 game_state->state = GameStateStart;
                 return;
             }
 
-            if(cardIndex == -1) cardIndex = 12;
-            game_state->animation.card = (Card){
-                game_state->top_cards[game_state->animation.deck].pip, cardIndex, false, false};
+            if (cardIndex == -1)
+                cardIndex = 12;
+            game_state->animation.card = (Card) {
+                    game_state->top_cards[game_state->animation.deck].pip,
+                    cardIndex,
+                    false, false
+            };
             game_state->animation.indexes[game_state->animation.deck]++;
             game_state->animation.vx = -(rand() % 3 + 1) * (rand() % 2 == 1 ? 1 : -1);
             game_state->animation.vy = (rand() % 3 + 1);
@@ -398,16 +382,17 @@ void tick(GameState* game_state, NotificationApp* notification) {
         game_state->animation.x += game_state->animation.vx;
         game_state->animation.y -= game_state->animation.vy;
         game_state->animation.vy -= 1;
-        if(game_state->animation.vy < -10) game_state->animation.vy = -10;
+        if (game_state->animation.vy < -10)game_state->animation.vy = -10;
 
-        if(game_state->animation.y > 41) {
+        if (game_state->animation.y > 41) {
             game_state->animation.y = 41;
             game_state->animation.vy = -(game_state->animation.vy * 0.7f);
         }
     }
 }
 
-void init(GameState* game_state) {
+void init(GameState *game_state) {
+    dolphin_deed(DolphinDeedPluginGameStart);
     game_state->selectColumn = 0;
     game_state->selected_card = 0;
     game_state->selectRow = 0;
@@ -420,49 +405,52 @@ void init(GameState* game_state) {
     game_state->state = GameStatePlay;
     game_state->dragging_column = 8;
 
-    for(uint8_t i = 0; i < 7; i++) {
+    for (uint8_t i = 0; i < 7; i++) {
         free_hand(&(game_state->bottom_columns[i]));
         init_hand(&(game_state->bottom_columns[i]), 21);
         game_state->bottom_columns[i].index = 0;
-        for(uint8_t j = 0; j <= i; j++) {
+        for (uint8_t j = 0; j <= i; j++) {
             Card cur = remove_from_deck(0, &(game_state->deck));
             cur.flipped = i != j;
             add_to_hand(&(game_state->bottom_columns[i]), cur);
         }
     }
 
-    for(uint8_t i = 0; i < 4; i++) {
+    for (uint8_t i = 0; i < 4; i++) {
         game_state->animation.indexes[i] = 0;
-        game_state->top_cards[i] = (Card){0, 0, true, false};
+        game_state->top_cards[i] = (Card) {0, 0, true, false};
     }
     game_state->deck.index = -1;
 }
 
-void init_start(GameState* game_state) {
+void init_start(GameState *game_state) {
     game_state->input = InputKeyMAX;
-    for(uint8_t i = 0; i < 7; i++) init_hand(&(game_state->bottom_columns[i]), 21);
+    for (uint8_t i = 0; i < 7; i++)
+        init_hand(&(game_state->bottom_columns[i]), 21);
 
     init_hand(&(game_state->dragging_hand), 13);
     game_state->animation.buffer = make_buffer();
+
 }
 
-static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
+
+static void input_callback(InputEvent *input_event, FuriMessageQueue *event_queue) {
     furi_assert(event_queue);
     AppEvent event = {.type = EventTypeKey, .input = *input_event};
     furi_message_queue_put(event_queue, &event, FuriWaitForever);
 }
 
-static void update_timer_callback(FuriMessageQueue* event_queue) {
+static void update_timer_callback(FuriMessageQueue *event_queue) {
     furi_assert(event_queue);
     AppEvent event = {.type = EventTypeTick};
     furi_message_queue_put(event_queue, &event, 0);
 }
 
-int32_t solitaire_app(void* p) {
+int32_t solitaire_app(void *p) {
     UNUSED(p);
     int32_t return_code = 0;
-    FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(AppEvent));
-    GameState* game_state = malloc(sizeof(GameState));
+    FuriMessageQueue *event_queue = furi_message_queue_alloc(8, sizeof(AppEvent));
+    GameState *game_state = malloc(sizeof(GameState));
     init_start(game_state);
     set_card_graphics(&I_card_graphics);
 
@@ -470,85 +458,87 @@ int32_t solitaire_app(void* p) {
 
     game_state->processing = true;
     game_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
-    if(!game_state->mutex) {
+    if (!game_state->mutex) {
         FURI_LOG_E(APP_NAME, "cannot create mutex\r\n");
         return_code = 255;
         goto free_and_exit;
     }
-    NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
+    NotificationApp *notification = furi_record_open(RECORD_NOTIFICATION);
 
     notification_message_block(notification, &sequence_display_backlight_enforce_on);
 
-    ViewPort* view_port = view_port_alloc();
+    ViewPort *view_port = view_port_alloc();
     view_port_draw_callback_set(view_port, render_callback, game_state);
     view_port_input_callback_set(view_port, input_callback, event_queue);
 
-    FuriTimer* timer = furi_timer_alloc(update_timer_callback, FuriTimerTypePeriodic, event_queue);
+    FuriTimer *timer =
+            furi_timer_alloc(update_timer_callback, FuriTimerTypePeriodic, event_queue);
     furi_timer_start(timer, furi_kernel_get_tick_frequency() / 30);
 
-    Gui* gui = furi_record_open(RECORD_GUI);
+    Gui *gui = furi_record_open("gui");
     gui_add_view_port(gui, view_port, GuiLayerFullscreen);
 
     AppEvent event;
-
-    // Call Dolphin deed on game start
-    dolphin_deed(DolphinDeedPluginGameStart);
-
-    for(bool processing = true; processing;) {
+    for (bool processing = true; processing;) {
         FuriStatus event_status = furi_message_queue_get(event_queue, &event, 150);
         furi_mutex_acquire(game_state->mutex, FuriWaitForever);
-        if(event_status == FuriStatusOk) {
-            if(event.type == EventTypeKey) {
-                if(event.input.type == InputTypeLong) {
+        game_state->had_change = false;
+        if (event_status == FuriStatusOk) {
+            if (event.type == EventTypeKey) {
+                game_state->had_change = true;
+                if (event.input.type == InputTypeLong) {
                     game_state->longPress = true;
-                    switch(event.input.key) {
-                    case InputKeyUp:
-                    case InputKeyDown:
-                    case InputKeyRight:
-                    case InputKeyLeft:
-                    case InputKeyOk:
-                        game_state->input = event.input.key;
-                        break;
-                    case InputKeyBack:
-                        processing = false;
-                        return_code = 1;
-                    default:
-                        break;
+                    switch (event.input.key) {
+                        case InputKeyUp:
+                        case InputKeyDown:
+                        case InputKeyRight:
+                        case InputKeyLeft:
+                        case InputKeyOk:
+                            game_state->input = event.input.key;
+                            break;
+                        case InputKeyBack:
+                            processing = false;
+                            return_code = 1;
+                        default:
+                            break;
                     }
-                } else if(event.input.type == InputTypePress) {
+                } else if (event.input.type == InputTypePress) {
                     game_state->longPress = false;
-                    switch(event.input.key) {
-                    case InputKeyUp:
-                    case InputKeyDown:
-                    case InputKeyRight:
-                    case InputKeyLeft:
-                    case InputKeyOk:
-                        if(event.input.key == InputKeyOk && game_state->state == GameStateStart) {
-                            game_state->state = GameStatePlay;
+                    switch (event.input.key) {
+                        case InputKeyUp:
+                        case InputKeyDown:
+                        case InputKeyRight:
+                        case InputKeyLeft:
+                        case InputKeyOk:
+                            if (event.input.key == InputKeyOk && game_state->state == GameStateStart) {
+                                game_state->state = GameStatePlay;
+                                init(game_state);
+                            }
+                            else {
+                                game_state->input = event.input.key;
+                            }
+                            break;
+                        case InputKeyBack:
                             init(game_state);
-                        } else {
-                            game_state->input = event.input.key;
-                        }
-                        break;
-                    case InputKeyBack:
-                        init(game_state);
-                        processing = false;
-                        return_code = 1;
-                        break;
-                    default:
-                        break;
+                            processing = false;
+                            return_code = 1;
+                            break;
+                        default:
+                            break;
                     }
                 }
-            } else if(event.type == EventTypeTick) {
+            } else if (event.type == EventTypeTick) {
                 tick(game_state, notification);
                 processing = game_state->processing;
                 game_state->input = InputKeyMAX;
             }
         }
+
         view_port_update(view_port);
         furi_mutex_release(game_state->mutex);
     }
 
+
     notification_message_block(notification, &sequence_display_backlight_enforce_auto);
     furi_timer_free(timer);
     view_port_enabled_set(view_port, false);
@@ -558,13 +548,15 @@ int32_t solitaire_app(void* p) {
     view_port_free(view_port);
     furi_mutex_free(game_state->mutex);
 
-free_and_exit:
+    free_and_exit:
     free(game_state->animation.buffer);
     ui_cleanup();
-    for(uint8_t i = 0; i < 7; i++) free_hand(&(game_state->bottom_columns[i]));
+    for (uint8_t i = 0; i < 7; i++)
+        free_hand(&(game_state->bottom_columns[i]));
 
     free(game_state->deck.cards);
     free(game_state);
     furi_message_queue_free(event_queue);
+
     return return_code;
 }

+ 8 - 0
non_catalog_apps/sudoku/LICENSE

@@ -0,0 +1,8 @@
+The MIT License (MIT)
+Copyright © 2023 @profelis
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 1 - 1
non_catalog_apps/sudoku/application.fam

@@ -14,6 +14,6 @@ App(
     fap_icon="sudoku.png",
     fap_description="Sudoku game",
     fap_author="@profelis",
-    # fap_weburl="https://github.com/user/template",
+    fap_weburl="https://github.com/profelis/fz-sudoku",
     # fap_icon_assets="images",  # Image assets to compile for this application
 )

+ 29 - 9
non_catalog_apps/sudoku/sudoku.c

@@ -41,6 +41,7 @@ typedef struct {
     GameState state;
     int8_t menuCursor;
     int8_t lastGameMode;
+    bool blockInputUntilRelease;
 } SudokuState;
 
 #define MENU_ITEMS_COUNT 5
@@ -93,7 +94,7 @@ static int get_mode_gaps(int index) {
     return HARD_GAPS;
 }
 
-#define SAVE_VERSION 1
+#define SAVE_VERSION 2
 #define SAVE_FILE APP_DATA_PATH("save.dat")
 
 static bool load_game(SudokuState* state) {
@@ -414,6 +415,7 @@ static bool start_game(SudokuState* state) {
     state->state = GameStateRunning;
     state->cursorX = 0;
     state->cursorY = 0;
+    state->blockInputUntilRelease = false;
     init_board(state);
     shuffle_board(state, 10);
     add_gaps(state, get_mode_gaps(state->lastGameMode));
@@ -496,23 +498,31 @@ int32_t sudoku_main(void* p) {
                 }
             }
 
+            bool invalidLidAndRow =
+                !(state->horizontalFlags & (1 << state->cursorY) ||
+                  state->vertivalFlags & (1 << state->cursorX));
+
             if(event.type == InputTypePress || event.type == InputTypeLong ||
                event.type == InputTypeRepeat) {
                 switch(event.key) {
                 case InputKeyLeft:
+                    state->blockInputUntilRelease = false;
                     state->cursorX = (state->cursorX + BOARD_SIZE - 1) % BOARD_SIZE;
                     break;
                 case InputKeyRight:
+                    state->blockInputUntilRelease = false;
                     state->cursorX = (state->cursorX + 1) % BOARD_SIZE;
                     break;
                 case InputKeyUp:
+                    state->blockInputUntilRelease = false;
                     state->cursorY = (state->cursorY + BOARD_SIZE - 1) % BOARD_SIZE;
                     break;
                 case InputKeyDown:
+                    state->blockInputUntilRelease = false;
                     state->cursorY = (state->cursorY + 1) % BOARD_SIZE;
                     break;
                 case InputKeyOk:
-                    if(userInput) {
+                    if(userInput && !state->blockInputUntilRelease) {
                         int flags = state->board[state->cursorX][state->cursorY] & FLAGS_MASK;
                         int value = state->board[state->cursorX][state->cursorY] & VALUE_MASK;
                         state->board[state->cursorX][state->cursorY] = flags | ((value + 1) % 10);
@@ -522,14 +532,24 @@ int32_t sudoku_main(void* p) {
                 default:
                     break;
                 }
+            } else if(event.type == InputTypeRelease) {
+                state->blockInputUntilRelease = false;
             }
-            if(invalidField && validate_board(state)) {
-                dolphin_deed(DolphinDeedPluginGameWin);
-                state->state = GameStateVictory;
-                state->menuCursor = 0;
-                for(int i = 0; i != BOARD_SIZE; ++i) {
-                    for(int j = 0; j != BOARD_SIZE; ++j) {
-                        state->board[i][j] &= ~USER_INPUT_FLAG;
+            if(invalidField) {
+                if(validate_board(state)) {
+                    dolphin_deed(DolphinDeedPluginGameWin);
+                    state->state = GameStateVictory;
+                    state->menuCursor = 0;
+                    for(int i = 0; i != BOARD_SIZE; ++i) {
+                        for(int j = 0; j != BOARD_SIZE; ++j) {
+                            state->board[i][j] &= ~USER_INPUT_FLAG;
+                        }
+                    }
+                } else {
+                    bool isValidLineOrRow = state->horizontalFlags & (1 << state->cursorY) ||
+                                            state->vertivalFlags & (1 << state->cursorX);
+                    if(invalidLidAndRow && isValidLineOrRow) {
+                        state->blockInputUntilRelease = true;
                     }
                 }
             }