| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689 |
- #include <stdio.h>
- #include <furi.h>
- #include <furi_hal.h>
- #include <dolphin/dolphin.h>
- #include <gui/gui.h>
- #include <input/input.h>
- #include <notification/notification_messages.h>
- #include <storage/storage.h>
- #define TAG "sudoku"
- #define BOARD_SIZE 9
- #define BOARD_SIZE_3 3
- #define FONT_SIZE 6
- #define VALUE_MASK 0x0F
- #define FLAGS_MASK ~VALUE_MASK
- #define USER_INPUT_FLAG 0x80
- static_assert(USER_INPUT_FLAG > VALUE_MASK);
- #define EASY_GAPS 37
- #define NORMAL_GAPS 44
- #define HARD_GAPS 51
- typedef enum {
- GameStateRunning,
- GameStatePaused,
- GameStateVictory,
- GameStateRestart, // util state
- } GameState;
- typedef struct {
- FuriMutex* mutex;
- uint8_t board[BOARD_SIZE][BOARD_SIZE];
- int8_t cursorX;
- int8_t cursorY;
- uint16_t horizontalFlags;
- uint16_t vertivalFlags;
- GameState state;
- int8_t menuCursor;
- int8_t lastGameMode;
- bool blockInputUntilRelease;
- } SudokuState;
- #define MENU_ITEMS_COUNT 5
- const char* MENU_ITEMS[] = {
- "Continue",
- "Easy game",
- "Nornal game",
- "Hard game",
- "Save+Exit",
- };
- /*
- Fontname: -Raccoon-Fixed4x6-Medium-R-Normal--6-60-75-75-P-40-ISO10646-1
- Copyright:
- Glyphs: 95/203
- BBX Build Mode: 0
- */
- const uint8_t u8g2_font_tom_thumb_4x6_tr[725] =
- "_\0\2\2\2\3\3\4\4\3\6\0\377\5\377\5\0\0\352\1\330\2\270 \5\340\315\0!\6\265\310"
- "\254\0\42\6\213\313$\25#\10\227\310\244\241\206\12$\10\227\310\215\70b\2%\10\227\310d\324F\1"
- "&\10\227\310(\65R\22'\5\251\313\10(\6\266\310\251\62)\10\226\310\304\224\24\0*\6\217\312\244"
- "\16+\7\217\311\245\225\0,\6\212\310)\0-\5\207\312\14.\5\245\310\4/\7\227\310Ve\4\60"
- "\7\227\310-k\1\61\6\226\310\255\6\62\10\227\310h\220\312\1\63\11\227\310h\220\62X\0\64\10\227"
- "\310$\65b\1\65\10\227\310\214\250\301\2\66\10\227\310\315\221F\0\67\10\227\310\314TF\0\70\10\227"
- "\310\214\64\324\10\71\10\227\310\214\64\342\2:\6\255\311\244\0;\7\222\310e\240\0<\10\227\310\246\32"
- "d\20=\6\217\311l\60>\11\227\310d\220A*\1\77\10\227\310\314\224a\2@\10\227\310UC\3"
- "\1A\10\227\310UC\251\0B\10\227\310\250\264\322\2C\7\227\310\315\32\10D\10\227\310\250d-\0"
- "E\10\227\310\214\70\342\0F\10\227\310\214\70b\4G\10\227\310\315\221\222\0H\10\227\310$\65\224\12"
- "I\7\227\310\254X\15J\7\227\310\226\252\2K\10\227\310$\265\222\12L\7\227\310\304\346\0M\10\227"
- "\310\244\61\224\12N\10\227\310\244q\250\0O\7\227\310UV\5P\10\227\310\250\264b\4Q\10\227\310"
- "Uj$\1R\10\227\310\250\64V\1S\10\227\310m\220\301\2T\7\227\310\254\330\2U\7\227\310$"
- "W\22V\10\227\310$\253L\0W\10\227\310$\65\206\12X\10\227\310$\325R\1Y\10\227\310$U"
- "V\0Z\7\227\310\314T\16[\7\227\310\214X\16\134\10\217\311d\220A\0]\7\227\310\314r\4^"
- "\5\213\313\65_\5\207\310\14`\6\212\313\304\0a\7\223\310\310\65\2b\10\227\310D\225\324\2c\7"
- "\223\310\315\14\4d\10\227\310\246\245\222\0e\6\223\310\235\2f\10\227\310\246\264b\2g\10\227\307\35"
- "\61%\0h\10\227\310D\225\254\0i\6\265\310\244\1j\10\233\307f\30U\5k\10\227\310\304\264T"
- "\1l\7\227\310\310\326\0m\7\223\310<R\0n\7\223\310\250d\5o\7\223\310U\252\2p\10\227"
- "\307\250\244V\4q\10\227\307-\225d\0r\6\223\310\315\22s\10\223\310\215\70\22\0t\10\227\310\245"
- "\25\243\0u\7\223\310$+\11v\10\223\310$\65R\2w\7\223\310\244q\4x\7\223\310\244\62\25"
- "y\11\227\307$\225dJ\0z\7\223\310\254\221\6{\10\227\310\251\32D\1|\6\265\310(\1}\11"
- "\227\310\310\14RR\0~\6\213\313\215\4\0\0\0\4\377\377\0";
- static int get_mode_gaps(int index) {
- if(index <= 0) {
- return EASY_GAPS;
- }
- if(index == 1) {
- return NORMAL_GAPS;
- }
- return HARD_GAPS;
- }
- #define SAVE_VERSION 2
- #define SAVE_FILE APP_DATA_PATH("save.dat")
- static bool load_game(SudokuState* state) {
- Storage* storage = furi_record_open(RECORD_STORAGE);
- File* file = storage_file_alloc(storage);
- bool res = false;
- if(storage_file_open(file, SAVE_FILE, FSAM_READ, FSOM_OPEN_EXISTING)) {
- uint16_t version = 0;
- uint64_t expectedSize = sizeof(version) + sizeof(SudokuState);
- uint64_t fileSize = storage_file_size(file);
- if(fileSize >= expectedSize) {
- storage_file_read(file, &version, sizeof(version));
- if(version != SAVE_VERSION) {
- storage_simply_remove(storage, SAVE_FILE);
- } else {
- res = storage_file_read(file, state, sizeof(SudokuState)) == sizeof(SudokuState);
- }
- }
- }
- storage_file_free(file); // Closes the file if it was open.
- furi_record_close(RECORD_STORAGE);
- return res;
- }
- static void save_game(SudokuState* app) {
- Storage* storage = furi_record_open(RECORD_STORAGE);
- File* file = storage_file_alloc(storage);
- if(storage_file_open(file, SAVE_FILE, FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
- uint16_t version = SAVE_VERSION;
- storage_file_write(file, &version, sizeof(version));
- storage_file_write(file, app, sizeof(SudokuState));
- }
- storage_file_free(file);
- furi_record_close(RECORD_STORAGE);
- }
- // inspired by game_2048
- static void gray_canvas(Canvas* const canvas) {
- canvas_set_color(canvas, ColorWhite);
- for(int x = 0, mx = canvas_width(canvas); x < mx; x += 2) {
- for(int y = 0, my = canvas_height(canvas); y != my; y++) {
- canvas_draw_dot(canvas, x + (y % 2 == 1 ? 0 : 1), y);
- }
- }
- }
- static void draw_callback(Canvas* canvas, void* ctx) {
- SudokuState* state = ctx;
- furi_mutex_acquire(state->mutex, FuriWaitForever);
- canvas_clear(canvas);
- canvas_set_custom_u8g2_font(canvas, u8g2_font_tom_thumb_4x6_tr);
- int gapX = 0;
- int xOffset = 2;
- int yOffset = -2;
- for(int i = 0; i != BOARD_SIZE; ++i) {
- int gapY = 0;
- bool vflag = state->vertivalFlags & (1 << i);
- if((i % 3) == 0) gapX += 2;
- if(vflag) {
- // draw vertical hint line
- canvas_set_color(canvas, ColorBlack);
- canvas_draw_line(
- canvas,
- i * FONT_SIZE + gapX + xOffset - 1,
- 0,
- i * FONT_SIZE + gapX + xOffset + FONT_SIZE - 3,
- 0);
- }
- for(int j = 0; j != BOARD_SIZE; ++j) {
- if((j % 3) == 0) gapY += 4;
- canvas_set_color(canvas, ColorBlack);
- if(i == 0) {
- bool hflag = state->horizontalFlags & (1 << j);
- if(hflag) {
- // draw horizontal hint line
- canvas_draw_line(
- canvas,
- 0,
- j * FONT_SIZE + gapY + yOffset + 1,
- 0,
- j * FONT_SIZE + gapY + yOffset + FONT_SIZE - 1);
- }
- }
- bool userInput = state->board[i][j] & USER_INPUT_FLAG;
- bool cursor = i == state->cursorX && j == state->cursorY;
- if(!userInput) {
- int xBoxOffset = cursor ? -1 : 0;
- // draw black box around the locked number
- canvas_draw_box(
- canvas,
- i * FONT_SIZE + gapX - 1 + xBoxOffset + xOffset,
- j * FONT_SIZE + gapY + yOffset,
- FONT_SIZE - 1 - xBoxOffset * 2,
- FONT_SIZE + 1);
- // text will be white
- canvas_set_color(canvas, ColorXOR);
- } else if(cursor) {
- // draw frame around the cursor
- canvas_draw_frame(
- canvas,
- i * FONT_SIZE + gapX - 2 + xOffset,
- j * FONT_SIZE + gapY + yOffset,
- FONT_SIZE + 1,
- FONT_SIZE + 1);
- }
- int value = state->board[i][j] & VALUE_MASK;
- if(value != 0) {
- canvas_draw_glyph(
- canvas,
- i * FONT_SIZE + gapX + xOffset,
- (j + 1) * FONT_SIZE + gapY + yOffset,
- '0' + value);
- }
- }
- }
- canvas_set_color(canvas, ColorBlack);
- gapX = 0;
- int gapY = 0;
- yOffset = 2;
- for(int i = 1; i != BOARD_SIZE / 3; ++i) {
- gapX += i;
- gapY += i * 2;
- // vertical lines
- canvas_draw_line(
- canvas,
- i * FONT_SIZE * 3 + xOffset + gapX,
- yOffset,
- i * FONT_SIZE * 3 + xOffset + gapX,
- FONT_SIZE * BOARD_SIZE + 8 + yOffset);
- // horizontal lines
- canvas_draw_line(
- canvas,
- xOffset,
- i * FONT_SIZE * 3 + gapY + yOffset,
- FONT_SIZE * BOARD_SIZE + xOffset + 3,
- i * FONT_SIZE * 3 + gapY + yOffset);
- }
- if(state->state == GameStateVictory || state->state == GameStatePaused) {
- gray_canvas(canvas);
- canvas_set_color(canvas, ColorWhite);
- int w = canvas_width(canvas);
- int h = canvas_height(canvas);
- int winW = 58;
- int winH = 48;
- int winX = (w - winW) / 2;
- int winY = (h - winH) / 2;
- canvas_draw_rbox(canvas, winX, winY, winW, winH, 4);
- canvas_set_color(canvas, ColorBlack);
- canvas_draw_rframe(canvas, winX, winY, winW, winH, 4);
- int offX = 6;
- int offY = 3;
- int itemH = FONT_SIZE + 2;
- for(int i = 0; i < MENU_ITEMS_COUNT; i++) {
- if(i == state->menuCursor) {
- canvas_set_color(canvas, ColorBlack);
- canvas_draw_box(
- canvas, winX + offX, winY + offY + itemH * i, winW - offX * 2, itemH);
- }
- canvas_set_color(canvas, i == state->menuCursor ? ColorWhite : ColorBlack);
- canvas_draw_str_aligned(
- canvas,
- w / 2,
- winY + offY + itemH * i + itemH / 2,
- AlignCenter,
- AlignCenter,
- i == 0 && state->state == GameStateVictory ? "VICTORY!" : MENU_ITEMS[i]);
- }
- }
- furi_mutex_release(state->mutex);
- }
- static void input_callback(InputEvent* input_event, void* ctx) {
- FuriMessageQueue* event_queue = ctx;
- furi_message_queue_put(event_queue, input_event, FuriWaitForever);
- }
- // static void print_board(SudokuState* state) {
- // char buf[BOARD_SIZE * 2 + 1];
- // for(int i = 0; i < BOARD_SIZE * 2; ++i) {
- // buf[i] = ' ';
- // }
- // buf[BOARD_SIZE * 2] = 0;
- // for(int i = 0; i != BOARD_SIZE; ++i) {
- // for(int j = 0; j != BOARD_SIZE; ++j) {
- // buf[j * 2] = state->board[j][i] == 0 ? '_' : '0' + state->board[j][i];
- // }
- // FURI_LOG_D(TAG, "%s", buf);
- // }
- // }
- static void init_board(SudokuState* state) {
- for(int i = 0; i != BOARD_SIZE; ++i) {
- for(int j = 0; j != BOARD_SIZE; ++j) {
- state->board[i][j] = 1 + (i * BOARD_SIZE_3 + i / 3 + j) % 9;
- }
- }
- }
- static void shuffle_board(SudokuState* state, int times) {
- uint8_t tmp[BOARD_SIZE];
- for(int t = 0; t < times; ++t) {
- // swap numbers
- int swapX, swapY;
- do {
- swapX = 1 + furi_hal_random_get() % BOARD_SIZE;
- swapY = 1 + furi_hal_random_get() % BOARD_SIZE;
- } while(swapX == swapY);
- for(int i = 0; i != BOARD_SIZE; ++i) {
- for(int j = 0; j != BOARD_SIZE; ++j) {
- if(state->board[i][j] == swapX) {
- state->board[i][j] = swapY;
- } else if(state->board[i][j] == swapY) {
- state->board[i][j] = swapX;
- }
- }
- }
- // swap columns
- for(int i = 0; i != BOARD_SIZE_3; ++i) {
- int swapX, swapY;
- int offset = i * BOARD_SIZE_3;
- do {
- swapX = offset + furi_hal_random_get() % BOARD_SIZE_3;
- swapY = offset + furi_hal_random_get() % BOARD_SIZE_3;
- } while(swapX == swapY);
- memcpy(tmp, state->board[swapX], BOARD_SIZE);
- memcpy(state->board[swapX], state->board[swapY], BOARD_SIZE);
- memcpy(state->board[swapY], tmp, BOARD_SIZE);
- }
- // swap rows
- for(int i = 0; i != BOARD_SIZE_3; ++i) {
- int swapX, swapY;
- int offset = i * BOARD_SIZE_3;
- do {
- swapX = offset + furi_hal_random_get() % BOARD_SIZE_3;
- swapY = offset + furi_hal_random_get() % BOARD_SIZE_3;
- } while(swapX == swapY);
- for(int k = 0; k != BOARD_SIZE; ++k) {
- FURI_SWAP(state->board[k][swapX], state->board[k][swapY]);
- }
- }
- }
- }
- static void add_gaps(SudokuState* state, int inputCells) {
- for(int i = 0; i <= inputCells; ++i) {
- int x, y;
- do {
- x = furi_hal_random_get() % BOARD_SIZE;
- y = furi_hal_random_get() % BOARD_SIZE;
- } while(state->board[x][y] & USER_INPUT_FLAG);
- state->board[x][y] = USER_INPUT_FLAG;
- }
- }
- static bool validate_board(SudokuState* state) {
- bool res = true;
- // check vertical lines for duplicates
- state->vertivalFlags = 0;
- for(int i = 0; i != BOARD_SIZE; ++i) {
- uint flags = 0;
- bool ok = true;
- for(int j = 0; j != BOARD_SIZE; ++j) {
- int value = state->board[i][j] & VALUE_MASK;
- if(value == 0) {
- ok = false;
- res = false;
- }
- if(flags & (1 << value)) {
- ok = false;
- res = false;
- }
- flags |= 1 << value;
- }
- if(ok) {
- state->vertivalFlags |= 1 << i;
- }
- }
- // check horizontal lines for duplicates
- state->horizontalFlags = 0;
- for(int i = 0; i != BOARD_SIZE; ++i) {
- bool ok = true;
- uint flags = 0;
- for(int j = 0; j != BOARD_SIZE; ++j) {
- int value = state->board[j][i] & VALUE_MASK;
- if(value == 0) {
- ok = false;
- res = false;
- }
- if(flags & (1 << value)) {
- ok = false;
- res = false;
- }
- flags |= 1 << value;
- }
- if(ok) {
- state->horizontalFlags |= 1 << i;
- }
- }
- if(!res) {
- return res;
- }
- // check 3x3 squares for duplicates
- for(int i = 0; i != BOARD_SIZE_3; ++i) {
- for(int j = 0; j != BOARD_SIZE_3; ++j) {
- uint flags = 0;
- for(int k = 0; k != BOARD_SIZE_3; ++k) {
- for(int l = 0; l != BOARD_SIZE_3; ++l) {
- int value = state->board[i * BOARD_SIZE_3 + k][j * BOARD_SIZE_3 + l] &
- VALUE_MASK;
- if(flags & (1 << value)) {
- return false;
- }
- flags |= 1 << value;
- }
- }
- }
- }
- return true;
- }
- // fast validation, checks only one given cell
- static bool board_cell_is_valid(SudokuState* state, int x, int y) {
- // check vertical lines for duplicates
- {
- uint flags = 0;
- for(int j = 0; j != BOARD_SIZE; ++j) {
- int value = state->board[x][j];
- if(value == 0) {
- continue;
- }
- if(flags & (1 << value)) {
- return false;
- }
- flags |= 1 << value;
- }
- }
- // check horizontal lines for duplicates
- {
- uint flags = 0;
- for(int j = 0; j != BOARD_SIZE; ++j) {
- int value = state->board[j][y];
- if(value == 0) {
- continue;
- }
- if(flags & (1 << value)) {
- return false;
- }
- flags |= 1 << value;
- }
- }
- // check 3x3 squares for duplicates
- {
- {
- int p = x - x % BOARD_SIZE_3;
- int q = y - y % BOARD_SIZE_3;
- uint flags = 0;
- for(int k = 0; k != BOARD_SIZE_3; ++k) {
- for(int l = 0; l != BOARD_SIZE_3; ++l) {
- int value = state->board[p + k][q + l];
- if(value == 0) {
- continue;
- }
- if(flags & (1 << value)) {
- return false;
- }
- flags |= 1 << value;
- }
- }
- }
- }
- return true;
- }
- static bool solve_board(SudokuState* state, int x, int y) {
- if(x == BOARD_SIZE) {
- x = 0;
- if(++y == BOARD_SIZE) {
- return true;
- }
- }
- while(state->board[x][y] != 0) {
- ++x;
- if(x == BOARD_SIZE) {
- x = 0;
- if(++y == BOARD_SIZE) {
- return true;
- }
- }
- }
- int offset = furi_hal_random_get() % BOARD_SIZE;
- for(int val = 1; val <= BOARD_SIZE; ++val) {
- state->board[x][y] = (val + offset) % BOARD_SIZE + 1;
- if(board_cell_is_valid(state, x, y) && solve_board(state, x + 1, y)) {
- return true;
- }
- }
- state->board[x][y] = 0;
- return false;
- }
- static bool generate_board(SudokuState* state) {
- memset(state->board, 0, sizeof(state->board));
- return solve_board(state, 0, 0);
- }
- static bool start_game(SudokuState* state) {
- state->state = GameStateRunning;
- state->cursorX = 0;
- state->cursorY = 0;
- state->blockInputUntilRelease = false;
- bool generated = false;
- for(int i = 0; i != 3; i++) {
- if(generate_board(state)) {
- FURI_LOG_D(TAG, "generate_board success on %d iteration", i);
- generated = true;
- break;
- }
- }
- if(!generated) {
- // fallback to init_board
- FURI_LOG_D(TAG, "board not generated, fallback to init_board");
- init_board(state);
- shuffle_board(state, 100);
- }
- add_gaps(state, get_mode_gaps(state->lastGameMode));
- return validate_board(state);
- }
- int32_t sudoku_main(void* p) {
- UNUSED(p);
- InputEvent event;
- FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
- SudokuState* state = malloc(sizeof(SudokuState));
- if(!load_game(state) || state->state == GameStateRestart) {
- state->menuCursor = 0;
- if(state->state != GameStateRestart)
- state->lastGameMode = 1; // set normal game mode by default, except restart
- start_game(state);
- }
- state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
- furi_check(state->mutex, "mutex alloc failed");
- ViewPort* view_port = view_port_alloc();
- view_port_draw_callback_set(view_port, draw_callback, state);
- view_port_input_callback_set(view_port, input_callback, event_queue);
- view_port_set_orientation(view_port, ViewPortOrientationVertical);
- Gui* gui = furi_record_open(RECORD_GUI);
- gui_add_view_port(gui, view_port, GuiLayerFullscreen);
- dolphin_deed(DolphinDeedPluginGameStart);
- while(true) {
- furi_check(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk);
- furi_mutex_acquire(state->mutex, FuriWaitForever);
- if(state->state == GameStatePaused || state->state == GameStateVictory) {
- bool exit = false;
- if(event.type == InputTypePress || event.type == InputTypeLong ||
- event.type == InputTypeRepeat) {
- switch(event.key) {
- case InputKeyLeft:
- case InputKeyUp:
- state->menuCursor =
- (state->menuCursor + MENU_ITEMS_COUNT - 1) % MENU_ITEMS_COUNT;
- break;
- case InputKeyRight:
- case InputKeyDown:
- state->menuCursor = (state->menuCursor + 1) % MENU_ITEMS_COUNT;
- break;
- case InputKeyOk:
- if(state->state == GameStatePaused && state->menuCursor == 0) {
- state->state = GameStateRunning;
- } else if(state->menuCursor >= 1 && state->menuCursor <= 3) {
- state->lastGameMode = state->menuCursor - 1;
- state->menuCursor = 0;
- start_game(state);
- } else if(state->menuCursor == 4) {
- exit = true;
- break;
- }
- break;
- default:
- break;
- }
- }
- if(exit) {
- furi_mutex_release(state->mutex);
- break;
- }
- } else if(state->state == GameStateRunning) {
- bool invalidField = false;
- bool userInput = state->board[state->cursorX][state->cursorY] & USER_INPUT_FLAG;
- if(event.key == InputKeyBack) {
- if(event.type == InputTypeLong) {
- state->state = GameStatePaused;
- } else if(userInput && event.type == InputTypeShort) {
- invalidField = state->board[state->cursorX][state->cursorY] & VALUE_MASK;
- state->board[state->cursorX][state->cursorY] &= FLAGS_MASK;
- }
- }
- 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 && !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);
- invalidField = true;
- }
- break;
- default:
- break;
- }
- } else if(event.type == InputTypeRelease) {
- state->blockInputUntilRelease = false;
- }
- 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;
- }
- }
- }
- }
- furi_mutex_release(state->mutex);
- view_port_update(view_port);
- }
- furi_message_queue_free(event_queue);
- view_port_enabled_set(view_port, false);
- gui_remove_view_port(gui, view_port);
- view_port_free(view_port);
- furi_record_close(RECORD_GUI);
- furi_mutex_free(state->mutex);
- if(state->state == GameStateVictory) {
- state->state = GameStateRestart;
- }
- state->menuCursor = 0; // reset menu cursor, because we stand on exit
- state->mutex = NULL;
- save_game(state);
- free(state);
- return 0;
- }
|