1111#define CELL_HEIGHT 8
1212#define MOVE_TICKS 5
1313#define KEY_STACK_SIZE 16
14- #define TOP_RECORD_DIRECTORY "/ext/apps/Games"
15- #define TOP_RECORD_FILENAME TOP_RECORD_DIRECTORY "/game15.top"
14+ #define SAVING_DIRECTORY "/ext/apps/Games"
15+ #define SAVING_FILENAME SAVING_DIRECTORY "/game15.save"
16+ #define POPUP_MENU_ITEMS 2
1617
1718typedef enum {
1819 DirectionNone ,
@@ -22,7 +23,7 @@ typedef enum {
2223 DirectionRight
2324} direction_e ;
2425
25- typedef enum { ScenePlay , SceneWin } scene_e ;
26+ typedef enum { ScenePlay , SceneWin , ScenePopup } scene_e ;
2627
2728typedef struct {
2829 uint8_t cell_index ;
@@ -31,13 +32,24 @@ typedef struct {
3132 uint8_t move_ticks ;
3233} moving_cell_t ;
3334
34- static scene_e scene ;
35- static uint8_t board [16 ];
36- static uint16_t top_record ;
37- static uint16_t move_count ;
38- static uint32_t tick_count ;
35+ typedef struct {
36+ uint16_t top_record ;
37+ scene_e scene ;
38+ uint16_t move_count ;
39+ uint32_t tick_count ;
40+ uint8_t board [16 ];
41+ } game_state_t ;
42+
43+ static game_state_t game_state ;
3944static NotificationApp * notification ;
4045static moving_cell_t moving_cell ;
46+ static uint8_t loaded_saving_ticks ;
47+ static uint8_t popup_menu_selected_item ;
48+
49+ static const char * popup_menu_strings [] = {
50+ "Continue" ,
51+ "Reset"
52+ };
4153
4254static uint8_t keys [KEY_STACK_SIZE ];
4355static uint8_t key_stack_head = 0 ;
@@ -105,29 +117,32 @@ static int key_stack_push(uint8_t value) {
105117 return -1 ;
106118}
107119
108- static void storage_top_record_load () {
120+ static bool storage_game_state_load () {
109121 Storage * storage = furi_record_open (RECORD_STORAGE );
110122 File * file = storage_file_alloc (storage );
111123
112- if (storage_file_open (file , TOP_RECORD_FILENAME , FSAM_READ , FSOM_OPEN_EXISTING ))
113- storage_file_read (file , & top_record , 2 );
124+ uint16_t bytes_readed = 0 ;
125+ if (storage_file_open (file , SAVING_FILENAME , FSAM_READ , FSOM_OPEN_EXISTING ))
126+ bytes_readed = storage_file_read (file , & game_state , sizeof (game_state_t ));
114127 storage_file_close (file );
115128 storage_file_free (file );
116129 furi_record_close (RECORD_STORAGE );
130+ return bytes_readed == sizeof (game_state_t );
117131}
118132
119- static void storage_top_record_save () {
133+ static void storage_game_state_save () {
120134 Storage * storage = furi_record_open (RECORD_STORAGE );
121135
122- if (storage_common_stat (storage , TOP_RECORD_DIRECTORY , NULL ) == FSE_NOT_EXIST ) {
123- if (!storage_simply_mkdir (storage , TOP_RECORD_DIRECTORY )) {
136+ if (storage_common_stat (storage , SAVING_DIRECTORY , NULL ) == FSE_NOT_EXIST ) {
137+ if (!storage_simply_mkdir (storage , SAVING_DIRECTORY )) {
124138 return ;
125139 }
126140 }
127141
128142 File * file = storage_file_alloc (storage );
129- if (storage_file_open (file , TOP_RECORD_FILENAME , FSAM_WRITE , FSOM_CREATE_ALWAYS ))
130- storage_file_write (file , & top_record , 2 );
143+ if (storage_file_open (file , SAVING_FILENAME , FSAM_WRITE , FSOM_CREATE_ALWAYS )) {
144+ storage_file_write (file , & game_state , sizeof (game_state_t ));
145+ }
131146 storage_file_close (file );
132147 storage_file_free (file );
133148 furi_record_close (RECORD_STORAGE );
@@ -138,7 +153,7 @@ static void set_moving_cell_by_direction(direction_e direction) {
138153 moving_cell .zero_index = 0xff ;
139154
140155 for (int i = 0 ; i < 16 ; i ++ ) {
141- if (!board [i ]) {
156+ if (!game_state . board [i ]) {
142157 moving_cell .zero_index = i ;
143158 break ;
144159 }
@@ -168,51 +183,52 @@ static void set_moving_cell_by_direction(direction_e direction) {
168183static bool is_board_has_solution () {
169184 uint8_t i , j , inv = 0 ;
170185 for (i = 0 ; i < 16 ; ++ i )
171- if (board [i ])
186+ if (game_state . board [i ])
172187 for (j = 0 ; j < i ; ++ j )
173- if (board [j ] > board [i ]) ++ inv ;
188+ if (game_state . board [j ] > game_state . board [i ]) ++ inv ;
174189 for (i = 0 ; i < 16 ; ++ i )
175- if (board [i ] == 0 ) inv += 1 + i / 4 ;
190+ if (game_state . board [i ] == 0 ) inv += 1 + i / 4 ;
176191
177192 return inv % 2 == 0 ;
178193}
179194
180195static void board_init () {
181196 for (int i = 0 ; i < 16 ; i ++ ) {
182- board [i ] = (i + 1 ) % 16 ;
197+ game_state . board [i ] = (i + 1 ) % 16 ;
183198 }
184199
185200 do {
186201 for (int i = 15 ; i >= 1 ; i -- ) {
187202 int j = rand () % (i + 1 );
188- uint8_t tmp = board [j ];
189- board [j ] = board [i ];
190- board [i ] = tmp ;
203+ uint8_t tmp = game_state . board [j ];
204+ game_state . board [j ] = game_state . board [i ];
205+ game_state . board [i ] = tmp ;
191206 }
192207 } while (!is_board_has_solution ());
193208}
194209
195210static void game_init () {
196- scene = ScenePlay ;
197- move_count = 0 ;
198- tick_count = 0 ;
199- storage_top_record_load ();
211+ game_state .scene = ScenePlay ;
212+ game_state .move_count = 0 ;
213+ game_state .tick_count = 0 ;
200214 moving_cell .move_direction = DirectionNone ;
201215 board_init ();
202216 key_stack_init ();
217+ popup_menu_selected_item = 0 ;
203218}
204219
205220static bool is_board_solved () {
206221 for (int i = 0 ; i < 16 ; i ++ )
207- if (((i + 1 ) % 16 ) != board [i ]) return false;
222+ if (((i + 1 ) % 16 ) != game_state . board [i ]) return false;
208223 return true;
209224}
210225
211226static void game_tick () {
212- switch (scene ) {
227+ switch (game_state . scene ) {
213228 case ScenePlay :
214- tick_count ++ ;
215-
229+ game_state .tick_count ++ ;
230+ if (loaded_saving_ticks )
231+ loaded_saving_ticks -- ;
216232 if (moving_cell .move_direction == DirectionNone && !key_stack_is_empty ()) {
217233 set_moving_cell_by_direction (key_stack_pop ());
218234 if (moving_cell .move_direction == DirectionNone ) {
@@ -224,25 +240,52 @@ static void game_tick() {
224240 if (moving_cell .move_direction != DirectionNone ) {
225241 moving_cell .move_ticks ++ ;
226242 if (moving_cell .move_ticks == MOVE_TICKS ) {
227- board [moving_cell .zero_index ] = board [moving_cell .cell_index ];
228- board [moving_cell .cell_index ] = 0 ;
243+ game_state .board [moving_cell .zero_index ] =
244+ game_state .board [moving_cell .cell_index ];
245+ game_state .board [moving_cell .cell_index ] = 0 ;
229246 moving_cell .move_direction = DirectionNone ;
230- move_count ++ ;
247+ game_state . move_count ++ ;
231248 }
232249 if (is_board_solved ()) {
233250 notification_message (notification , & sequence_double_vibro );
234- if (move_count < top_record || top_record == 0 ) {
235- top_record = move_count ;
236- storage_top_record_save ();
251+ if (game_state . move_count < game_state . top_record || game_state . top_record == 0 ) {
252+ game_state . top_record = game_state . move_count ;
253+ storage_game_state_save ();
237254 }
238- scene = SceneWin ;
255+ game_state . scene = SceneWin ;
239256 }
240257 }
241258 break ;
242259
243260 case SceneWin :
244261 if (!key_stack_is_empty ()) game_init ();
245262 break ;
263+
264+ case ScenePopup :
265+ if (!key_stack_is_empty ()) {
266+ switch (key_stack_pop ())
267+ {
268+ case DirectionDown :
269+ popup_menu_selected_item ++ ;
270+ popup_menu_selected_item = popup_menu_selected_item % POPUP_MENU_ITEMS ;
271+ break ;
272+ case DirectionUp :
273+ popup_menu_selected_item -- ;
274+ popup_menu_selected_item = popup_menu_selected_item % POPUP_MENU_ITEMS ;
275+ break ;
276+ case DirectionNone :
277+ if (popup_menu_selected_item == 0 ) {
278+ game_state .scene = ScenePlay ;
279+ notification_message (notification , & sequence_single_vibro );
280+ }
281+ else if (popup_menu_selected_item == 1 ) {
282+ notification_message (notification , & sequence_single_vibro );
283+ game_init ();
284+ }
285+ break ;
286+ }
287+ }
288+ break ;
246289 }
247290}
248291
@@ -255,17 +298,17 @@ static void draw_cell(Canvas* canvas, uint8_t x, uint8_t y, uint8_t cell_number)
255298
256299static void board_draw (Canvas * canvas ) {
257300 for (int i = 0 ; i < 16 ; i ++ ) {
258- if (board [i ]) {
301+ if (game_state . board [i ]) {
259302 if (moving_cell .move_direction == DirectionNone || moving_cell .cell_index != i )
260- draw_cell (canvas , (i % 4 ) * 20 + 7 , (i / 4 ) * 16 + 1 , board [i ]);
303+ draw_cell (canvas , (i % 4 ) * 20 + 7 , (i / 4 ) * 16 + 1 , game_state . board [i ]);
261304 if (moving_cell .move_direction != DirectionNone && moving_cell .cell_index == i ) {
262305 uint8_t from_x = (moving_cell .cell_index % 4 ) * 20 + 7 ;
263306 uint8_t from_y = (moving_cell .cell_index / 4 ) * 16 + 1 ;
264307 uint8_t to_x = (moving_cell .zero_index % 4 ) * 20 + 7 ;
265308 uint8_t to_y = (moving_cell .zero_index / 4 ) * 16 + 1 ;
266309 int now_x = from_x + (to_x - from_x ) * moving_cell .move_ticks / MOVE_TICKS ;
267310 int now_y = from_y + (to_y - from_y ) * moving_cell .move_ticks / MOVE_TICKS ;
268- draw_cell (canvas , now_x , now_y , board [i ]);
311+ draw_cell (canvas , now_x , now_y , game_state . board [i ]);
269312 }
270313 }
271314 }
@@ -296,32 +339,40 @@ static void plate_draw(
296339}
297340
298341static void info_draw (Canvas * canvas ) {
299- plate_draw (canvas , 1 , pic_top , top_record , true);
300- plate_draw (canvas , 22 , pic_move , move_count , false);
301- plate_draw (canvas , 43 , pic_time , tick_count / FPS , false);
342+ plate_draw (canvas , 1 , pic_top , game_state .top_record , true);
343+ plate_draw (canvas , 22 , pic_move , game_state .move_count , false);
344+ plate_draw (canvas , 43 , pic_time , game_state .tick_count / FPS , false);
345+ }
346+
347+ static void gray_screen (Canvas * const canvas ) {
348+ canvas_set_color (canvas , ColorWhite );
349+ for (int x = 0 ; x < 128 ; x += 2 ) {
350+ for (int y = 0 ; y < 64 ; y ++ ) {
351+ canvas_draw_dot (canvas , x + (y % 2 == 1 ? 0 : 1 ), y );
352+ }
353+ }
302354}
303355
304356static void render_callback (Canvas * const canvas ) {
305357 canvas_set_color (canvas , ColorWhite );
306358 canvas_draw_box (canvas , 0 , 0 , 128 , 64 );
307359
308- if (scene == ScenePlay || scene == SceneWin ) {
360+ if (game_state . scene == ScenePlay || game_state . scene == SceneWin || game_state . scene == ScenePopup ) {
309361 canvas_set_color (canvas , ColorBlack );
310362 board_draw (canvas );
311363 info_draw (canvas );
312364
313- canvas_set_color (canvas , ColorWhite );
314- canvas_draw_box (canvas , 0 , 0 , 128 , 64 );
315- canvas_set_color (canvas , ColorBlack );
316- canvas_draw_str (canvas , 10 , 10 , "0123456789" );
317- }
318- if (scene == SceneWin ) {
319- canvas_set_color (canvas , ColorWhite );
320- for (int x = 0 ; x < 128 ; x += 2 ) {
321- for (int y = 0 ; y < 64 ; y ++ ) {
322- canvas_draw_dot (canvas , x + (y % 2 == 1 ? 0 : 1 ), y );
323- }
365+ if (loaded_saving_ticks && game_state .scene != ScenePopup ) {
366+ canvas_set_color (canvas , ColorWhite );
367+ canvas_draw_rbox (canvas , 20 , 24 , 88 , 16 , 4 );
368+ canvas_set_color (canvas , ColorBlack );
369+ canvas_draw_rframe (canvas , 20 , 24 , 88 , 16 , 4 );
370+ canvas_draw_str_aligned (canvas , 64 , 32 , AlignCenter , AlignCenter , "Restore game ..." );
324371 }
372+ }
373+
374+ if (game_state .scene == SceneWin ) {
375+ gray_screen (canvas );
325376 canvas_draw_box (canvas , 7 , 20 , 114 , 24 );
326377 canvas_set_color (canvas , ColorBlack );
327378 canvas_draw_box (canvas , 8 , 21 , 112 , 22 );
@@ -330,6 +381,23 @@ static void render_callback(Canvas* const canvas) {
330381 canvas_set_color (canvas , ColorBlack );
331382 canvas_draw_xbm (canvas , 14 , 27 , 100 , 10 , pic_puzzled );
332383 }
384+ else if (game_state .scene == ScenePopup ) {
385+ gray_screen (canvas );
386+ canvas_set_color (canvas , ColorWhite );
387+ canvas_draw_rbox (canvas , 28 , 16 , 72 , 32 , 4 );
388+ canvas_set_color (canvas , ColorBlack );
389+ canvas_draw_rframe (canvas , 28 , 16 , 72 , 32 , 4 );
390+
391+ for (int i = 0 ; i < POPUP_MENU_ITEMS ; i ++ ) {
392+ if ( i == popup_menu_selected_item ) {
393+ canvas_set_color (canvas , ColorBlack );
394+ canvas_draw_box (canvas , 34 , 20 + 12 * i , 60 , 12 );
395+ }
396+
397+ canvas_set_color (canvas , i == popup_menu_selected_item ? ColorWhite : ColorBlack );
398+ canvas_draw_str_aligned (canvas , 64 , 26 + 12 * i , AlignCenter , AlignCenter , popup_menu_strings [i ]);
399+ }
400+ }
333401}
334402
335403static void game_event_handler (GameEvent const event ) {
@@ -349,10 +417,21 @@ static void game_event_handler(GameEvent const event) {
349417 key_stack_push (DirectionLeft );
350418 break ;
351419 case InputKeyOk :
352- key_stack_push (DirectionNone );
420+ if (game_state .scene == ScenePlay ) {
421+ game_state .scene = ScenePopup ;
422+ key_stack_init ();
423+ }
424+ else
425+ key_stack_push (DirectionNone );
353426 break ;
354427 case InputKeyBack :
355- sandbox_loop_exit ();
428+ if (game_state .scene == ScenePopup ) {
429+ game_state .scene = ScenePlay ;
430+ }
431+ else {
432+ storage_game_state_save ();
433+ sandbox_loop_exit ();
434+ }
356435 break ;
357436 }
358437 }
@@ -376,6 +455,17 @@ static void game_free() {
376455int32_t game15_app () {
377456 game_alloc ();
378457 game_init ();
458+
459+ loaded_saving_ticks = 0 ;
460+ if (storage_game_state_load ()) {
461+ if (game_state .scene != ScenePlay )
462+ game_init ();
463+ else
464+ loaded_saving_ticks = FPS ;
465+ }
466+ else
467+ game_init ();
468+
379469 sandbox_init (
380470 FPS , (SandboxRenderCallback )render_callback , (SandboxEventHandler )game_event_handler );
381471 sandbox_loop ();
0 commit comments