Skip to content

Commit abfe705

Browse files
authored
Merge pull request #4 from HappyAmos/master
Changes and improvements
2 parents d0f8ba0 + 3f907e7 commit abfe705

File tree

2 files changed

+115
-45
lines changed

2 files changed

+115
-45
lines changed

games/connect_wires/.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
build

games/connect_wires/flipper_game_connect_wires.c

+114-45
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
/*
1+
/*
22
* Copyright 2023 Alexander Taran
3+
*
34
* Use of this source code is governed by an MIT-style
45
* license that can be found in the LICENSE file or at
56
* https://opensource.org/licenses/MIT
6-
*
7+
*
78
* Thanks to:
89
* - Eugene Kirzhanov: https://github.com/eugene-kirzhanov/flipper-zero-2048-game
910
*/
@@ -20,6 +21,8 @@
2021
#define SCREEN_WIDTH 128
2122
#define SCREEN_HIGHT 64
2223

24+
#define TAG "connect_wires" // For logging
25+
2326
enum AppStatus {
2427
ST_PLAYING,
2528
ST_MAIN_MENU,
@@ -83,11 +86,34 @@ static const int8_t DY[4] = { 0, -1, 0, 1};
8386
static const int8_t OPP[4] = {DIR_RIGHT, DIR_BOTTOM, DIR_LEFT, DIR_TOP};
8487

8588
static const char* WINNING_MESSAGE = "Congratulations!";
89+
static const char* SCORE_MESSAGE = "Moves: %u"; // Will show the user how many "rotations" they made
90+
91+
const NotificationSequence sequence_winning = {
92+
&message_vibro_on,
93+
&message_delay_50,
94+
&message_vibro_off,
95+
NULL,
96+
};
97+
98+
// Give a notification when the user changes direction
99+
// show green led for clockwise.
100+
const NotificationSequence sequence_clockwise = {
101+
&message_green_255,
102+
&message_vibro_on,
103+
&message_delay_50,
104+
&message_vibro_off,
105+
&message_green_0,
106+
NULL,
107+
};
86108

87-
const NotificationSequence sequence_winning = {
109+
// Give a notification when the user changes direction
110+
// show red led for counter-clockwise.
111+
const NotificationSequence sequence_counter_clockwise = {
112+
&message_red_255,
88113
&message_vibro_on,
89114
&message_delay_50,
90115
&message_vibro_off,
116+
&message_red_0,
91117
NULL,
92118
};
93119

@@ -103,12 +129,22 @@ GridElement createGridElement() {
103129
return g;
104130
}
105131

106-
void rotate_grid_element(GridElement* elem) {
107-
bool tmp = elem->edges[0];
108-
for (int8_t i = 0; i < 3; ++i) {
109-
elem->edges[i] = elem->edges[i+1];
132+
133+
void rotate_grid_element(GridElement* elem, bool clockwise) {
134+
//FURI_LOG_I(TAG, "Attempt Rotation: clockwise = %d", clockwise);
135+
if (!clockwise) { // Counter-clockwise logic
136+
bool tmp = elem->edges[0];
137+
for (int8_t i = 0; i < 3; ++i) {
138+
elem->edges[i] = elem->edges[i+1];
139+
}
140+
elem->edges[3] = tmp;
141+
} else { // Clockwise logic
142+
bool tmp = elem->edges[3];
143+
for (int8_t i = 3; i > 0; --i) {
144+
elem->edges[i] = elem->edges[i-1];
145+
}
146+
elem->edges[0] = tmp;
110147
}
111-
elem->edges[3] = tmp;
112148
}
113149

114150
uint8_t count_edges(GridElement* elem) {
@@ -141,6 +177,8 @@ typedef struct{
141177
Coord startingPoint;
142178
GridElement elements[MAX_FIELD_WIDTH][MAX_FIELD_HEIGHT];
143179
Coord currentSelection;
180+
uint16_t gameMoves; // 65535 theres no check for this, but if they go over....
181+
bool clockwise; // CLOCKWISE(true) || COUNTERCLOCKWISE(false)
144182

145183
// calculated properties
146184
bool reachable[MAX_FIELD_WIDTH][MAX_FIELD_HEIGHT];
@@ -157,7 +195,10 @@ GameState createNewGameState(Coord fieldSize) {
157195
GameState gs;
158196
gs.fieldSize = fieldSize;
159197
Coord start = createCoord(1 + random() % (gs.fieldSize.x-2), 1 + random() % (gs.fieldSize.y - 2));
160-
198+
199+
gs.gameMoves = 0; // Initialize beginning moves
200+
gs.clockwise = false; // This was this games original default, we'll keep it the same.
201+
161202
gs.startingPoint = start;
162203

163204
gs.currentSelection = start;
@@ -199,7 +240,7 @@ GameState createNewGameState(Coord fieldSize) {
199240
dirs[num_dirs] = i;
200241
num_dirs++;
201242
}
202-
}
243+
}
203244
if (num_dirs != 0) break;
204245
candidates[cand_id] = candidates[num_candidates - 1]; num_candidates--;
205246
}
@@ -235,7 +276,7 @@ GameState createNewGameState(Coord fieldSize) {
235276
}
236277
}
237278
free(candidates);
238-
279+
239280
return gs;
240281
}
241282

@@ -246,7 +287,7 @@ void shuffle(GameState* gs) {
246287
for (uint8_t j = 0; j < gs->fieldSize.y; ++j) {
247288
uint8_t rounds = rand() % 4;
248289
for (uint8_t r = 0; r < rounds; ++r) {
249-
rotate_grid_element(&gs->elements[i][j]);
290+
rotate_grid_element(&gs->elements[i][j], false);
250291
shuffles++;
251292
}
252293
}
@@ -266,7 +307,9 @@ void moveSelection(GameState* gs, uint8_t dir) {
266307

267308
void rotateSelection(GameState* gs) {
268309
Coord* cs = &gs->currentSelection;
269-
rotate_grid_element(&gs->elements[cs->x][cs->y]);
310+
rotate_grid_element(&gs->elements[cs->x][cs->y], gs->clockwise);
311+
312+
gs->gameMoves++; // Increment moves counter
270313
}
271314

272315
void recalculateReachables(GameState* gs) {
@@ -311,13 +354,13 @@ bool checkIsWinning(GameState* gs) {
311354
static void draw_selection(Canvas* canvas, Coord at, uint8_t cellSize, uint8_t cornerSize) {
312355
canvas_draw_line(canvas, at.x, at.y, at.x + cornerSize - 1, at.y);
313356
canvas_draw_line(canvas, at.x, at.y, at.x, at.y + cornerSize - 1);
314-
357+
315358
canvas_draw_line(canvas, at.x + cellSize - 1, at.y, at.x + cellSize - cornerSize, at.y);
316359
canvas_draw_line(canvas, at.x + cellSize - 1, at.y, at.x + cellSize - 1, at.y + cornerSize - 1);
317-
360+
318361
canvas_draw_line(canvas, at.x, at.y + cellSize - 1, at.x, at.y + cellSize - cornerSize);
319362
canvas_draw_line(canvas, at.x, at.y + cellSize - 1, at.x + cornerSize - 1, at.y + cellSize - 1);
320-
363+
321364
canvas_draw_line(canvas, at.x + cellSize - 1, at.y + cellSize - 1, at.x + cellSize - 1, at.y + cellSize - cornerSize);
322365
canvas_draw_line(canvas, at.x + cellSize - 1, at.y + cellSize - 1, at.x + cellSize - cornerSize, at.y + cellSize - 1);
323366
}
@@ -333,7 +376,7 @@ static void draw_interleaved_dots(Canvas* canvas, Coord at, uint8_t size) {
333376

334377
static void draw_grid_element_size12(Canvas* canvas, Coord at, Coord element, GameState* gs) {
335378
GridElement* elem = &gs->elements[element.x][element.y];
336-
379+
337380
canvas_set_color(canvas, ColorWhite);
338381
canvas_draw_box(canvas, at.x, at.y, 12, 12);
339382

@@ -463,7 +506,7 @@ void draw_menu(Canvas* canvas, const MenuEntry* menu, uint8_t selectedIndex) {
463506
int w = canvas_string_width(canvas, menu[i].text);
464507
if (w > max_width) {
465508
max_width = w;
466-
}
509+
}
467510
}
468511
max_width += 2;
469512
for (uint8_t i = 0; i < nitems; ++i) {
@@ -488,7 +531,7 @@ void draw_about(Canvas* canvas) {
488531
int w = canvas_string_width(canvas, AboutStrings[i]);
489532
if (w > max_width) {
490533
max_width = w;
491-
}
534+
}
492535
}
493536
canvas_set_color(canvas, ColorBlack);
494537
for (uint8_t i = 0; i < nitems; ++i) {
@@ -497,31 +540,42 @@ void draw_about(Canvas* canvas) {
497540
}
498541
}
499542

500-
void draw_winning(Canvas* canvas) {
543+
void draw_winning(Canvas* canvas, GameState* gs) {
501544
canvas_set_font(canvas, FontPrimary);
545+
546+
size_t s = snprintf(NULL, 0, "%s:%u", SCORE_MESSAGE, gs->gameMoves);
547+
char moves[s];
548+
// Use snprintf to combine the score message and the actual score
549+
snprintf(moves, s, SCORE_MESSAGE, gs->gameMoves);
550+
502551
int w = canvas_string_width(canvas, WINNING_MESSAGE);
503-
int h = canvas_current_font_height(canvas);
552+
int h = canvas_current_font_height(canvas) * 2;
504553
const int paddingV = 2;
505554
const int paddingH = 4;
506-
555+
507556
canvas_set_color(canvas, ColorWhite);
508-
canvas_draw_box(canvas, SCREEN_WIDTH/2 - w/2 - paddingH, SCREEN_HIGHT/2 - h/2 - paddingV, w + paddingH*2, h + paddingV*2);
557+
canvas_draw_box(canvas, SCREEN_WIDTH/2 - w/2 - paddingH, SCREEN_HIGHT/2 - h/2 - paddingV, w + paddingH*2, h + paddingV*2 + canvas_current_font_height(canvas));
509558
canvas_set_color(canvas, ColorBlack);
510-
canvas_draw_rframe(canvas, SCREEN_WIDTH/2 - w/2 - paddingH, SCREEN_HIGHT/2 - h/2 - paddingV, w + paddingH*2, h + paddingV*2, 2);
559+
canvas_draw_rframe(canvas, SCREEN_WIDTH/2 - w/2 - paddingH, SCREEN_HIGHT/2 - h/2 - paddingV, w + paddingH*2, h + paddingV*2 + canvas_current_font_height(canvas), 2);
511560
canvas_draw_str_aligned(canvas, SCREEN_WIDTH/2, SCREEN_HIGHT/2, AlignCenter, AlignCenter, WINNING_MESSAGE);
561+
canvas_draw_str_aligned(canvas, SCREEN_WIDTH/2, (SCREEN_HIGHT/2) + canvas_current_font_height(canvas), AlignCenter, AlignCenter, moves);
512562
}
513563

564+
565+
566+
567+
514568
static void game_draw_callback(Canvas* canvas, void* ctx) {
515569
AppState* appState = (AppState*)ctx;
516570
furi_mutex_acquire(appState->mutex, FuriWaitForever);
517571
GameState* gs = &appState->gameState;
518572

519573
canvas_clear(canvas);
520-
if (appState->status == ST_PLAYING) {
574+
if (appState->status == ST_PLAYING) {
521575
draw_grid(canvas, gs, determine_element_size(gs->fieldSize));
522576
} else if (appState->status == ST_WINNING) {
523577
draw_grid(canvas, gs, determine_element_size(gs->fieldSize));
524-
draw_winning(canvas);
578+
draw_winning(canvas, gs);
525579
} else if (appState->status == ST_MAIN_MENU) {
526580
draw_menu(canvas, MainMenu, appState->currentMenuSelection);
527581
} else if (appState->status == ST_SELECTION_MENU) {
@@ -564,42 +618,49 @@ int32_t flipper_game_connect_wires(void* p) {
564618
UNUSED(p);
565619
furi_assert((uint16_t)MAX_FIELD_WIDTH * MAX_FIELD_HEIGHT < 256);
566620

567-
dolphin_deed(DolphinDeedPluginGameStart);
568-
569621
InputEvent event;
570622
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
571623

572-
// Configure view port
573-
ViewPort* view_port = view_port_alloc();
574-
575-
AppState* appState = malloc(sizeof(AppState));
624+
AppState* appState = malloc(sizeof(AppState));
576625
appState->gameState.fieldSize = createCoord(0, 0);
577626
appState->status = ST_MAIN_MENU;
578627

579-
// temp
580-
//appState->status = ST_MAIN_MENU;
581-
//appState->currentMenuSelection = 0;
582-
// temp end
628+
// Allocate our appState->mutex
629+
appState->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
583630

631+
// Configure view port
632+
ViewPort* view_port = view_port_alloc();
584633
view_port_draw_callback_set(view_port, game_draw_callback, appState);
585634
view_port_input_callback_set(view_port, game_input_callback, event_queue);
586635

587636
// Register view port in GUI
588637
Gui* gui = furi_record_open(RECORD_GUI);
589-
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
638+
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
639+
590640
NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
591641

642+
dolphin_deed(DolphinDeedPluginGameStart);
643+
644+
592645
while(1) {
593646
furi_check(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk);
594647

595-
if(event.type != InputTypePress) continue;
648+
if ((event.type != InputTypeShort) && (event.type != InputTypeLong)) continue;
596649

597650
furi_mutex_acquire(appState->mutex, FuriWaitForever);
598651

599652
if (appState->status == ST_PLAYING) {
600653
if(event.key == InputKeyBack) {
601-
appState->currentMenuSelection = 0;
602-
appState->status = ST_MAIN_MENU;
654+
if (event.type == InputTypeShort) {
655+
appState->currentMenuSelection = 0;
656+
appState->status = ST_MAIN_MENU;
657+
}
658+
if (event.type == InputTypeLong) {
659+
// Change rotation to default: counter-clockwise
660+
appState->gameState.clockwise = false;
661+
notification_message_block(notification, &sequence_counter_clockwise);
662+
//FURI_LOG_I(TAG, "Rotation: counter-clockwise > clockwise = %d", appState->gameState.clockwise);
663+
}
603664
} else if (event.key == InputKeyLeft) {
604665
moveSelection(&appState->gameState, DIR_LEFT);
605666
} else if (event.key == InputKeyRight) {
@@ -609,11 +670,18 @@ int32_t flipper_game_connect_wires(void* p) {
609670
} else if (event.key == InputKeyDown) {
610671
moveSelection(&appState->gameState, DIR_BOTTOM);
611672
} else if (event.key == InputKeyOk) {
612-
rotateSelection(&appState->gameState);
613-
recalculateReachables(&appState->gameState);
614-
if (checkIsWinning(&appState->gameState)) {
615-
appState->status = ST_WINNING;
616-
notification_message_block(notification, &sequence_winning);
673+
if (event.type == InputTypeShort) {
674+
rotateSelection(&appState->gameState);
675+
recalculateReachables(&appState->gameState);
676+
if (checkIsWinning(&appState->gameState)) {
677+
appState->status = ST_WINNING;
678+
notification_message_block(notification, &sequence_winning);
679+
}
680+
} else if (event.type == InputTypeLong) {
681+
// Change rotation direction to clockwise
682+
appState->gameState.clockwise = true;
683+
notification_message_block(notification, &sequence_clockwise);
684+
// FURI_LOG_I(TAG, "Rotation: clockwise > clockwise = %d", appState->gameState.clockwise);
617685
}
618686
}
619687
} else if (appState->status == ST_WINNING) {
@@ -665,6 +733,7 @@ int32_t flipper_game_connect_wires(void* p) {
665733
}
666734

667735
furi_mutex_release(appState->mutex);
736+
view_port_update(view_port);
668737
}
669738

670739
free(appState);

0 commit comments

Comments
 (0)