@@ -68,7 +68,7 @@ static void timer_callback(void* ctx) {
6868}
6969
7070static void input_callback (InputEvent * input_event , FuriMessageQueue * event_queue ) {
71- furi_assert (event_queue );
71+ furi_assert (event_queue );
7272
7373 PluginEvent event = {.type = EventTypeKey , .input = * input_event };
7474 furi_message_queue_put (event_queue , & event , FuriWaitForever );
@@ -111,7 +111,7 @@ static void render_callback(Canvas* const canvas, void* ctx) {
111111 x * TILE_HEIGHT , // x
112112 8 + (y * TILE_WIDTH ), // y
113113 TILE_WIDTH ,
114- TILE_HEIGHT ,
114+ TILE_HEIGHT ,
115115 tile_0_bits );
116116 break ;
117117 case TileType1 :
@@ -120,7 +120,7 @@ static void render_callback(Canvas* const canvas, void* ctx) {
120120 x * TILE_HEIGHT , // x
121121 8 + (y * TILE_WIDTH ), // y
122122 TILE_WIDTH ,
123- TILE_HEIGHT ,
123+ TILE_HEIGHT ,
124124 tile_1_bits );
125125 break ;
126126 case TileType2 :
@@ -129,7 +129,7 @@ static void render_callback(Canvas* const canvas, void* ctx) {
129129 x * TILE_HEIGHT , // x
130130 8 + (y * TILE_WIDTH ), // y
131131 TILE_WIDTH ,
132- TILE_HEIGHT ,
132+ TILE_HEIGHT ,
133133 tile_2_bits );
134134 break ;
135135 case TileType3 :
@@ -138,7 +138,7 @@ static void render_callback(Canvas* const canvas, void* ctx) {
138138 x * TILE_HEIGHT , // x
139139 8 + (y * TILE_WIDTH ), // y
140140 TILE_WIDTH ,
141- TILE_HEIGHT ,
141+ TILE_HEIGHT ,
142142 tile_3_bits );
143143 break ;
144144 case TileType4 :
@@ -147,7 +147,7 @@ static void render_callback(Canvas* const canvas, void* ctx) {
147147 x * TILE_HEIGHT , // x
148148 8 + (y * TILE_WIDTH ), // y
149149 TILE_WIDTH ,
150- TILE_HEIGHT ,
150+ TILE_HEIGHT ,
151151 tile_4_bits );
152152 break ;
153153 case TileType5 :
@@ -156,7 +156,7 @@ static void render_callback(Canvas* const canvas, void* ctx) {
156156 x * TILE_HEIGHT , // x
157157 8 + (y * TILE_WIDTH ), // y
158158 TILE_WIDTH ,
159- TILE_HEIGHT ,
159+ TILE_HEIGHT ,
160160 tile_5_bits );
161161 break ;
162162 case TileType6 :
@@ -165,7 +165,7 @@ static void render_callback(Canvas* const canvas, void* ctx) {
165165 x * TILE_HEIGHT , // x
166166 8 + (y * TILE_WIDTH ), // y
167167 TILE_WIDTH ,
168- TILE_HEIGHT ,
168+ TILE_HEIGHT ,
169169 tile_6_bits );
170170 break ;
171171 case TileType7 :
@@ -174,7 +174,7 @@ static void render_callback(Canvas* const canvas, void* ctx) {
174174 x * TILE_HEIGHT , // x
175175 8 + (y * TILE_WIDTH ), // y
176176 TILE_WIDTH ,
177- TILE_HEIGHT ,
177+ TILE_HEIGHT ,
178178 tile_7_bits );
179179 break ;
180180 case TileType8 :
@@ -183,7 +183,7 @@ static void render_callback(Canvas* const canvas, void* ctx) {
183183 x * TILE_HEIGHT , // x
184184 8 + (y * TILE_WIDTH ), // y
185185 TILE_WIDTH ,
186- TILE_HEIGHT ,
186+ TILE_HEIGHT ,
187187 tile_8_bits );
188188 break ;
189189 case TileTypeFlag :
@@ -192,7 +192,7 @@ static void render_callback(Canvas* const canvas, void* ctx) {
192192 x * TILE_HEIGHT , // x
193193 8 + (y * TILE_WIDTH ), // y
194194 TILE_WIDTH ,
195- TILE_HEIGHT ,
195+ TILE_HEIGHT ,
196196 tile_flag_bits );
197197 break ;
198198 case TileTypeUncleared :
@@ -201,7 +201,7 @@ static void render_callback(Canvas* const canvas, void* ctx) {
201201 x * TILE_HEIGHT , // x
202202 8 + (y * TILE_WIDTH ), // y
203203 TILE_WIDTH ,
204- TILE_HEIGHT ,
204+ TILE_HEIGHT ,
205205 tile_uncleared_bits );
206206 break ;
207207 case TileTypeMine :
@@ -210,7 +210,7 @@ static void render_callback(Canvas* const canvas, void* ctx) {
210210 x * TILE_HEIGHT , // x
211211 8 + (y * TILE_WIDTH ), // y
212212 TILE_WIDTH ,
213- TILE_HEIGHT ,
213+ TILE_HEIGHT ,
214214 tile_mine_bits );
215215 break ;
216216 }
@@ -238,7 +238,7 @@ static void setup_playfield(Minesweeper* minesweeper_state) {
238238 int rand_y = rand () % PLAYFIELD_HEIGHT ;
239239 // make sure first guess isn't a mine
240240 if (minesweeper_state -> minefield [rand_x ][rand_y ] == FieldEmpty &&
241- (minesweeper_state -> cursor_x != rand_x && minesweeper_state -> cursor_y != rand_y )) {
241+ (minesweeper_state -> cursor_x != rand_x && minesweeper_state -> cursor_y != rand_y )) {
242242 minesweeper_state -> minefield [rand_x ][rand_y ] = FieldMine ;
243243 mines_left -- ;
244244 }
@@ -273,7 +273,7 @@ static bool game_lost(Minesweeper* minesweeper_state) {
273273 dialog_message_set_buttons (message , NULL , "Play again" , NULL );
274274
275275 dialog_message_set_icon (message , NULL , 0 , 10 );
276-
276+
277277 NotificationApp * notifications = furi_record_open (RECORD_NOTIFICATION );
278278 notification_message (notifications , & sequence_set_vibro_on );
279279 furi_record_close (RECORD_NOTIFICATION );
@@ -293,7 +293,7 @@ static bool game_won(Minesweeper* minesweeper_state) {
293293 tempStr = furi_string_alloc ();
294294
295295 int seconds = 0 ;
296- int minutes = 0 ;
296+ int minutes = 0 ;
297297 uint32_t ticks_elapsed = furi_get_tick () - minesweeper_state -> game_started_tick ;
298298 seconds = (int ) ticks_elapsed / furi_kernel_get_tick_frequency ();
299299 minutes = (int ) seconds / 60 ;
@@ -305,28 +305,31 @@ static bool game_won(Minesweeper* minesweeper_state) {
305305 dialog_message_set_header (message , header_text , 64 , 3 , AlignCenter , AlignTop );
306306 dialog_message_set_text (message , furi_string_get_cstr (tempStr ), 64 , 32 , AlignCenter , AlignCenter );
307307 dialog_message_set_buttons (message , NULL , "Play again" , NULL );
308- // TODO: create icon
309308 dialog_message_set_icon (message , NULL , 72 , 17 );
310309
311310 DialogMessageButton choice = dialog_message_show (dialogs , message );
312311 dialog_message_free (message );
313312 furi_string_free (tempStr );
314313 furi_record_close (RECORD_DIALOGS );
315- return choice == DialogMessageButtonCenter ;
314+ return choice == DialogMessageButtonCenter ;
316315}
317316
317+ // returns false if the move loses the game - otherwise true
318318static bool play_move (Minesweeper * minesweeper_state , int cursor_x , int cursor_y ) {
319- if (minesweeper_state -> playfield [cursor_x ][cursor_y ] != TileTypeUncleared ) {
320- // we're on an already uncovered field
321- return true;
319+ if (minesweeper_state -> playfield [cursor_x ][cursor_y ] == TileTypeFlag ) {
320+ // we're on a flagged field, do nothing
321+ return true;
322322 }
323323 if (minesweeper_state -> minefield [cursor_x ][cursor_y ] == FieldMine ) {
324- // TODO: player loses!
325- minesweeper_state -> playfield [cursor_x ][cursor_y ] = TileTypeMine ;
326- return false;
327- } else {
328- // get number of surrounding mines.
329- int hint = 0 ;
324+ // player loses - draw mine
325+ minesweeper_state -> playfield [cursor_x ][cursor_y ] = TileTypeMine ;
326+ return false;
327+ }
328+
329+ if (minesweeper_state -> playfield [cursor_x ][cursor_y ] >= TileType1 && minesweeper_state -> playfield [cursor_x ][cursor_y ] <= TileType8 ) {
330+ // click on a cleared cell with a number
331+ // count the flags around
332+ int flags = 0 ;
330333 for (int y = cursor_y - 1 ; y <= cursor_y + 1 ; y ++ ) {
331334 for (int x = cursor_x - 1 ; x <= cursor_x + 1 ; x ++ ) {
332335 if ( x == cursor_x && y == cursor_y ) {
@@ -335,37 +338,76 @@ static bool play_move(Minesweeper* minesweeper_state, int cursor_x, int cursor_y
335338 }
336339 // make sure we don't go OOB
337340 if ( x >= 0 && x < PLAYFIELD_WIDTH && y >= 0 && y < PLAYFIELD_HEIGHT ) {
338- if (minesweeper_state -> minefield [x ][y ] == FieldMine ) {
339- hint ++ ;
341+ if (minesweeper_state -> playfield [x ][y ] == TileTypeFlag ) {
342+ flags ++ ;
340343 }
341344 }
342345 }
343346 }
344- // 〜( ̄▽ ̄〜) don't judge me (〜 ̄▽ ̄)〜
345- minesweeper_state -> playfield [cursor_x ][cursor_y ] = hint ;
346- minesweeper_state -> fields_cleared ++ ;
347- FURI_LOG_D ("Minesweeper" , "Setting %d,%d to %d" , cursor_x , cursor_y , hint );
348- if (hint == 0 ) {
349- // auto open surrounding fields.
347+ int mines = minesweeper_state -> playfield [cursor_x ][cursor_y ]; // ¯\_(ツ)_/¯
348+ if (flags == mines ) {
349+ // auto uncover all non-flags around (to win faster ;)
350350 for (int auto_y = cursor_y - 1 ; auto_y <= cursor_y + 1 ; auto_y ++ ) {
351351 for (int auto_x = cursor_x - 1 ; auto_x <= cursor_x + 1 ; auto_x ++ ) {
352352 if ( auto_x == cursor_x && auto_y == cursor_y ) {
353353 continue ;
354354 }
355355 if ( auto_x >= 0 && auto_x < PLAYFIELD_WIDTH && auto_y >= 0 && auto_y < PLAYFIELD_HEIGHT ) {
356356 if (minesweeper_state -> playfield [auto_x ][auto_y ] == TileTypeUncleared ) {
357- play_move (minesweeper_state , auto_x , auto_y );
357+ if (!play_move (minesweeper_state , auto_x , auto_y )) {
358+ // flags were wrong, we got a mine!
359+ return false;
360+ }
358361 }
359362 }
360363 }
361364 }
365+ // we're done without hitting a mine - so return
366+ return true;
362367 }
363- return true;
364368 }
369+
370+ // calculate number of surrounding mines.
371+ int hint = 0 ;
372+ for (int y = cursor_y - 1 ; y <= cursor_y + 1 ; y ++ ) {
373+ for (int x = cursor_x - 1 ; x <= cursor_x + 1 ; x ++ ) {
374+ if ( x == cursor_x && y == cursor_y ) {
375+ // we're on the cell the user selected, so ignore.
376+ continue ;
377+ }
378+ // make sure we don't go OOB
379+ if ( x >= 0 && x < PLAYFIELD_WIDTH && y >= 0 && y < PLAYFIELD_HEIGHT ) {
380+ if (minesweeper_state -> minefield [x ][y ] == FieldMine ) {
381+ hint ++ ;
382+ }
383+ }
384+ }
385+ }
386+ // 〜( ̄▽ ̄〜) don't judge me (〜 ̄▽ ̄)〜
387+ minesweeper_state -> playfield [cursor_x ][cursor_y ] = hint ;
388+ minesweeper_state -> fields_cleared ++ ;
389+ FURI_LOG_D ("Minesweeper" , "Setting %d,%d to %d" , cursor_x , cursor_y , hint );
390+ if (hint == 0 ) {
391+ // the field is "empty"
392+ // auto open surrounding fields.
393+ for (int auto_y = cursor_y - 1 ; auto_y <= cursor_y + 1 ; auto_y ++ ) {
394+ for (int auto_x = cursor_x - 1 ; auto_x <= cursor_x + 1 ; auto_x ++ ) {
395+ if ( auto_x == cursor_x && auto_y == cursor_y ) {
396+ continue ;
397+ }
398+ if ( auto_x >= 0 && auto_x < PLAYFIELD_WIDTH && auto_y >= 0 && auto_y < PLAYFIELD_HEIGHT ) {
399+ if (minesweeper_state -> playfield [auto_x ][auto_y ] == TileTypeUncleared ) {
400+ play_move (minesweeper_state , auto_x , auto_y );
401+ }
402+ }
403+ }
404+ }
405+ }
406+ return true;
365407}
366408
367409static void minesweeper_state_init (Minesweeper * const minesweeper_state ) {
368- minesweeper_state -> cursor_x = minesweeper_state -> cursor_y = 0 ;
410+ minesweeper_state -> cursor_x = minesweeper_state -> cursor_y = 0 ;
369411 minesweeper_state -> game_started = false;
370412 for (int y = 0 ; y < PLAYFIELD_HEIGHT ; y ++ ) {
371413 for (int x = 0 ; x < PLAYFIELD_WIDTH ; x ++ ){
@@ -393,11 +435,11 @@ int32_t minesweeper_app(void* p) {
393435 furi_record_close (RECORD_DIALOGS );
394436
395437 FuriMessageQueue * event_queue = furi_message_queue_alloc (8 , sizeof (PluginEvent ));
396-
438+
397439 Minesweeper * minesweeper_state = malloc (sizeof (Minesweeper ));
398440 // setup
399441 minesweeper_state_init (minesweeper_state );
400-
442+
401443 ValueMutex state_mutex ;
402444 if (!init_mutex (& state_mutex , minesweeper_state , sizeof (minesweeper_state ))) {
403445 FURI_LOG_E ("Minesweeper" , "cannot create mutex\r\n" );
@@ -407,13 +449,13 @@ int32_t minesweeper_app(void* p) {
407449 // BEGIN IMPLEMENTATION
408450
409451 // Set system callbacks
410- ViewPort * view_port = view_port_alloc ();
452+ ViewPort * view_port = view_port_alloc ();
411453 view_port_draw_callback_set (view_port , render_callback , & state_mutex );
412454 view_port_input_callback_set (view_port , input_callback , event_queue );
413- minesweeper_state -> timer = furi_timer_alloc (timer_callback , FuriTimerTypeOnce , & state_mutex );
414-
455+ minesweeper_state -> timer = furi_timer_alloc (timer_callback , FuriTimerTypeOnce , & state_mutex );
456+
415457 // Open GUI and register view_port
416- Gui * gui = furi_record_open ("gui" );
458+ Gui * gui = furi_record_open ("gui" );
417459 gui_add_view_port (gui , view_port , GuiLayerFullscreen );
418460
419461 PluginEvent event ;
@@ -423,7 +465,7 @@ int32_t minesweeper_app(void* p) {
423465 if (event_status == FuriStatusOk ) {
424466 // press events
425467 if (event .type == EventTypeKey ) {
426- if (event .input .type == InputTypeShort ) {
468+ if (event .input .type == InputTypeShort ) {
427469 switch (event .input .key ) {
428470 case InputKeyUp :
429471 minesweeper_state -> cursor_y -- ;
@@ -461,15 +503,15 @@ int32_t minesweeper_app(void* p) {
461503 setup_playfield (minesweeper_state );
462504 } else {
463505 // player wants to exit :(
464- processing = false;
506+ processing = false;
465507 }
466508 } else {
467509 // check win condition.
468510 if (minesweeper_state -> fields_cleared == (PLAYFIELD_HEIGHT * PLAYFIELD_WIDTH ) - MINECOUNT ){
469511 if (game_won (minesweeper_state )) {
470512 //player wants to restart
471513 setup_playfield (minesweeper_state );
472- } else {
514+ } else {
473515 processing = false;
474516 }
475517 }
@@ -496,9 +538,9 @@ int32_t minesweeper_app(void* p) {
496538 case InputKeyBack :
497539 processing = false;
498540 break ;
499- }
541+ }
500542 }
501- }
543+ }
502544 } else {
503545 // event timeout
504546 ;
0 commit comments