Skip to content

Commit 3b07cf7

Browse files
authored
Merge pull request #25 from noiob/improve-tetris
Improve tetris app
2 parents 854bd83 + 07f04d2 commit 3b07cf7

File tree

7 files changed

+155
-41
lines changed

7 files changed

+155
-41
lines changed

ReadMe.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ The Flipper and its community wouldn't be as rich as it is without your contribu
9797
| Flappy Bird | ![Games Badge] | [by DroomOne](https://github.com/DroomOne/flipperzerofirmware/tree/dev/applications/flappy_bird) | | [![UFW Badge]](https://lab.flipper.net/apps/flappy_bird) |
9898
| Arkanoid | ![Games Badge] | by xMasterX | refactored by xMasterX, [original by gotnull](https://github.com/gotnull/flipperzerofirmwarewPlugins) | [![UFW Badge]](https://lab.flipper.net/apps/arkanoid) |
9999
| Tic Tac Toe | ![Games Badge] | by xMasterX | refactored by xMasterX, [original by gotnull](https://github.com/gotnull/flipperzerofirmwarewPlugins) | [![UFW Badge]](https://lab.flipper.net/apps/tictactoe) |
100-
| Tetris (with fixes) | ![Games Badge] | [by jeffplang](https://github.com/jeffplang/flipperzerofirmware/tree/tetris_game/applications/tetris) | | [![UFW Badge]](https://lab.flipper.net/apps/tetris_game) |
100+
| Tetris (with fixes) | ![Games Badge] | [by jeffplang](https://github.com/jeffplang/flipperzerofirmware/tree/tetris_game/applications/tetris) & [noiob](https://github.com/noiob) | | [![UFW Badge]](https://lab.flipper.net/apps/tetris_game) |
101101
| Minesweeper | ![Games Badge] | [by panki27](https://github.com/panki27/minesweeper) | | [![UFW Badge]](https://lab.flipper.net/apps/minesweeper) |
102102
| Heap Defence (aka Stack Attack) | ![Games Badge] | by xMasterX | ported to latest firmware by xMasterX, [original by wquinoa & Vedmein](https://github.com/Vedmein/flipperzerofirmware/tree/hd/svistoperdelki) | [![UFW Badge]](https://lab.flipper.net/apps/heap_defence) |
103103
| Game15 | ![Games Badge] | [by x27](https://github.com/x27/flipperzerogame15) | | [![UFW Badge]](https://lab.flipper.net/apps/game15) |

base_pack/tetris_game/application.fam

+3-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ App(
88
order=20,
99
fap_icon="tetris_10px.png",
1010
fap_category="Games",
11-
fap_author="@xMasterX & @jeffplang",
12-
fap_version="1.1",
11+
fap_author="@xMasterX & @jeffplang & @noiob",
12+
fap_version="1.2",
1313
fap_description="Tetris Game",
14+
fap_icon_assets="assets"
1415
)
Loading

base_pack/tetris_game/img/1.png

218 Bytes
Loading

base_pack/tetris_game/img/2.png

535 Bytes
Loading

base_pack/tetris_game/img/3.png

850 Bytes
Loading

base_pack/tetris_game/tetris_game.c

+151-38
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,18 @@
77
#include <furi_hal_resources.h>
88
#include <furi_hal_gpio.h>
99
#include <dolphin/dolphin.h>
10+
#include "tetris_icons.h"
1011

1112
#define BORDER_OFFSET 1
1213
#define MARGIN_OFFSET 3
1314
#define BLOCK_HEIGHT 6
1415
#define BLOCK_WIDTH 6
1516

16-
#define FIELD_WIDTH 11
17-
#define FIELD_HEIGHT 24
17+
#define FIELD_WIDTH 10
18+
#define FIELD_HEIGHT 20
19+
20+
#define FIELD_X_OFFSET 3
21+
#define FIELD_Y_OFFSET 20
1822

1923
#define MAX_FALL_SPEED 500
2024
#define MIN_FALL_SPEED 100
@@ -62,10 +66,12 @@ static Piece shapes[] = {
6266
{.p = {{5, 1}, {5, 0}, {6, 0}, {6, 1}}, .rotIdx = 0, .offsetType = OffsetTypeO} // O
6367
};
6468

65-
typedef enum { GameStatePlaying, GameStateGameOver } GameState;
69+
typedef enum { GameStatePlaying, GameStateGameOver, GameStatePaused } GameState;
6670

6771
typedef struct {
6872
bool playField[FIELD_HEIGHT][FIELD_WIDTH];
73+
bool bag[7];
74+
uint8_t next_id;
6975
Piece currPiece;
7076
uint16_t numLines;
7177
uint16_t fallSpeed;
@@ -85,61 +91,107 @@ typedef struct {
8591
} TetrisEvent;
8692

8793
static void tetris_game_draw_border(Canvas* const canvas) {
88-
canvas_draw_line(canvas, 0, 0, 0, 127);
89-
canvas_draw_line(canvas, 0, 127, 63, 127);
90-
canvas_draw_line(canvas, 63, 127, 63, 0);
94+
canvas_draw_line(
95+
canvas,
96+
FIELD_X_OFFSET,
97+
FIELD_Y_OFFSET,
98+
FIELD_X_OFFSET,
99+
FIELD_Y_OFFSET + FIELD_HEIGHT * 5 + 7);
100+
canvas_draw_line(
101+
canvas,
102+
FIELD_X_OFFSET,
103+
FIELD_Y_OFFSET + FIELD_HEIGHT * 5 + 7,
104+
FIELD_X_OFFSET + FIELD_WIDTH * 5 + 8,
105+
FIELD_Y_OFFSET + FIELD_HEIGHT * 5 + 7);
106+
canvas_draw_line(
107+
canvas,
108+
FIELD_X_OFFSET + FIELD_WIDTH * 5 + 8,
109+
FIELD_Y_OFFSET + FIELD_HEIGHT * 5 + 7,
110+
FIELD_X_OFFSET + FIELD_WIDTH * 5 + 8,
111+
FIELD_Y_OFFSET);
112+
113+
canvas_draw_line(
114+
canvas,
115+
FIELD_X_OFFSET + 2,
116+
FIELD_Y_OFFSET + 0,
117+
FIELD_X_OFFSET + 2,
118+
FIELD_Y_OFFSET + FIELD_HEIGHT * 5 + 5);
119+
canvas_draw_line(
120+
canvas,
121+
FIELD_X_OFFSET + 2,
122+
FIELD_Y_OFFSET + FIELD_HEIGHT * 5 + 5,
123+
FIELD_X_OFFSET + FIELD_WIDTH * 5 + 6,
124+
FIELD_Y_OFFSET + FIELD_HEIGHT * 5 + 5);
125+
canvas_draw_line(
126+
canvas,
127+
FIELD_X_OFFSET + FIELD_WIDTH * 5 + 6,
128+
FIELD_Y_OFFSET + FIELD_HEIGHT * 5 + 5,
129+
FIELD_X_OFFSET + FIELD_WIDTH * 5 + 6,
130+
FIELD_Y_OFFSET);
131+
}
91132

92-
canvas_draw_line(canvas, 2, 0, 2, 125);
93-
canvas_draw_line(canvas, 2, 125, 61, 125);
94-
canvas_draw_line(canvas, 61, 125, 61, 0);
133+
static void tetris_game_draw_block(Canvas* const canvas, uint16_t xOffset, uint16_t yOffset) {
134+
canvas_draw_rframe(
135+
canvas,
136+
BORDER_OFFSET + MARGIN_OFFSET + xOffset,
137+
BORDER_OFFSET + MARGIN_OFFSET + yOffset - 1,
138+
BLOCK_WIDTH,
139+
BLOCK_HEIGHT,
140+
1);
141+
canvas_draw_dot(
142+
canvas,
143+
BORDER_OFFSET + MARGIN_OFFSET + xOffset + 2,
144+
BORDER_OFFSET + MARGIN_OFFSET + yOffset + 1);
145+
canvas_draw_dot(
146+
canvas,
147+
BORDER_OFFSET + MARGIN_OFFSET + xOffset + 3,
148+
BORDER_OFFSET + MARGIN_OFFSET + yOffset + 1);
149+
canvas_draw_dot(
150+
canvas,
151+
BORDER_OFFSET + MARGIN_OFFSET + xOffset + 2,
152+
BORDER_OFFSET + MARGIN_OFFSET + yOffset + 2);
95153
}
96154

97155
static void tetris_game_draw_playfield(Canvas* const canvas, const TetrisState* tetris_state) {
98-
// Playfield: 11 x 24
99-
100156
for(int y = 0; y < FIELD_HEIGHT; y++) {
101157
for(int x = 0; x < FIELD_WIDTH; x++) {
102158
if(tetris_state->playField[y][x]) {
103-
uint16_t xOffset = x * 5;
104-
uint16_t yOffset = y * 5;
105-
106-
canvas_draw_rframe(
107-
canvas,
108-
BORDER_OFFSET + MARGIN_OFFSET + xOffset,
109-
BORDER_OFFSET + MARGIN_OFFSET + yOffset - 1,
110-
BLOCK_WIDTH,
111-
BLOCK_HEIGHT,
112-
1);
113-
canvas_draw_dot(
159+
tetris_game_draw_block(
114160
canvas,
115-
BORDER_OFFSET + MARGIN_OFFSET + xOffset + 2,
116-
BORDER_OFFSET + MARGIN_OFFSET + yOffset + 1);
117-
canvas_draw_dot(
118-
canvas,
119-
BORDER_OFFSET + MARGIN_OFFSET + xOffset + 3,
120-
BORDER_OFFSET + MARGIN_OFFSET + yOffset + 1);
121-
canvas_draw_dot(
122-
canvas,
123-
BORDER_OFFSET + MARGIN_OFFSET + xOffset + 2,
124-
BORDER_OFFSET + MARGIN_OFFSET + yOffset + 2);
161+
FIELD_X_OFFSET + x * (BLOCK_WIDTH - 1),
162+
FIELD_Y_OFFSET + y * (BLOCK_HEIGHT - 1));
125163
}
126164
}
127165
}
128166
}
129167

168+
static void tetris_game_draw_next_piece(Canvas* const canvas, const TetrisState* tetris_state) {
169+
Piece* next_piece = &shapes[tetris_state->next_id];
170+
for(int i = 0; i < 4; i++) {
171+
uint8_t x = next_piece->p[i].x;
172+
uint8_t y = next_piece->p[i].y;
173+
174+
tetris_game_draw_block(
175+
canvas,
176+
FIELD_X_OFFSET + x * (BLOCK_WIDTH - 1) - (BLOCK_WIDTH * 4),
177+
y * (BLOCK_HEIGHT - 1));
178+
}
179+
}
180+
130181
static void tetris_game_render_callback(Canvas* const canvas, void* ctx) {
131182
furi_assert(ctx);
132183
const TetrisState* tetris_state = ctx;
133184
furi_mutex_acquire(tetris_state->mutex, FuriWaitForever);
134185

135186
tetris_game_draw_border(canvas);
136187
tetris_game_draw_playfield(canvas, tetris_state);
188+
tetris_game_draw_next_piece(canvas, tetris_state);
137189

138190
// Show score on the game field
139-
if(tetris_state->gameState == GameStatePlaying) {
191+
if(tetris_state->gameState == GameStatePlaying || tetris_state->gameState == GameStatePaused) {
140192
char buffer2[6];
141193
snprintf(buffer2, sizeof(buffer2), "%u", tetris_state->numLines);
142-
canvas_draw_str_aligned(canvas, 58, 10, AlignRight, AlignBottom, buffer2);
194+
canvas_draw_str_aligned(canvas, 62, 10, AlignRight, AlignBottom, buffer2);
143195
}
144196

145197
if(tetris_state->gameState == GameStateGameOver) {
@@ -158,6 +210,22 @@ static void tetris_game_render_callback(Canvas* const canvas, void* ctx) {
158210
canvas_set_font(canvas, FontSecondary);
159211
canvas_draw_str_aligned(canvas, 32, 73, AlignCenter, AlignBottom, buffer);
160212
}
213+
214+
if(tetris_state->gameState == GameStatePaused) {
215+
// 128 x 64
216+
canvas_set_color(canvas, ColorWhite);
217+
canvas_draw_box(canvas, 1, 52, 62, 24);
218+
219+
canvas_set_color(canvas, ColorBlack);
220+
canvas_draw_frame(canvas, 1, 52, 62, 24);
221+
222+
canvas_set_font(canvas, FontPrimary);
223+
canvas_draw_str(canvas, 4, 63, "Paused");
224+
225+
canvas_set_font(canvas, FontSecondary);
226+
canvas_draw_str(canvas, 4, 73, "hold to quit");
227+
canvas_draw_icon(canvas, 22, 66, &I_Pin_back_arrow_10x8);
228+
}
161229
furi_mutex_release(tetris_state->mutex);
162230
}
163231

@@ -168,13 +236,42 @@ static void tetris_game_input_callback(InputEvent* input_event, FuriMessageQueue
168236
furi_message_queue_put(event_queue, &event, FuriWaitForever);
169237
}
170238

239+
static uint8_t tetris_game_get_next_piece(TetrisState* tetris_state) {
240+
bool full = true;
241+
for(int i = 0; i < 7; i++) {
242+
if(!tetris_state->bag[i]) {
243+
full = false;
244+
break;
245+
}
246+
}
247+
if(full == true) {
248+
for(int i = 0; i < 7; i++) {
249+
tetris_state->bag[i] = false;
250+
}
251+
}
252+
253+
int next_piece = rand() % 7;
254+
while(tetris_state->bag[next_piece]) {
255+
next_piece = rand() % 7;
256+
}
257+
tetris_state->bag[next_piece] = true;
258+
int result = tetris_state->next_id;
259+
tetris_state->next_id = next_piece;
260+
261+
return result;
262+
}
263+
171264
static void tetris_game_init_state(TetrisState* tetris_state) {
172265
tetris_state->gameState = GameStatePlaying;
173266
tetris_state->numLines = 0;
174267
tetris_state->fallSpeed = MAX_FALL_SPEED;
175268
memset(tetris_state->playField, 0, sizeof(tetris_state->playField));
269+
memset(tetris_state->bag, 0, sizeof(tetris_state->bag));
176270

177-
memcpy(&tetris_state->currPiece, &shapes[rand() % 7], sizeof(tetris_state->currPiece));
271+
// init next_piece display
272+
tetris_game_get_next_piece(tetris_state);
273+
int next_piece_id = tetris_game_get_next_piece(tetris_state);
274+
memcpy(&tetris_state->currPiece, &shapes[next_piece_id], sizeof(tetris_state->currPiece));
178275

179276
furi_timer_start(tetris_state->timer, tetris_state->fallSpeed);
180277
}
@@ -294,7 +391,8 @@ static void tetris_game_update_timer_callback(FuriMessageQueue* event_queue) {
294391

295392
static void
296393
tetris_game_process_step(TetrisState* tetris_state, Piece* newPiece, bool wasDownMove) {
297-
if(tetris_state->gameState == GameStateGameOver) return;
394+
if(tetris_state->gameState == GameStateGameOver || tetris_state->gameState == GameStatePaused)
395+
return;
298396

299397
tetris_game_remove_curr_piece(tetris_state);
300398

@@ -335,7 +433,8 @@ static void
335433
}
336434

337435
// Check for game over
338-
Piece* spawnedPiece = &shapes[rand() % 7];
436+
int next_piece_id = tetris_game_get_next_piece(tetris_state);
437+
Piece* spawnedPiece = &shapes[next_piece_id];
339438
if(!tetris_game_is_valid_pos(tetris_state, spawnedPiece->p)) {
340439
tetris_state->gameState = GameStateGameOver;
341440
} else {
@@ -439,7 +538,21 @@ int32_t tetris_game_app() {
439538
}
440539
break;
441540
case InputKeyBack:
442-
processing = false;
541+
if(event.input.type == InputTypeLong) {
542+
processing = false;
543+
} else if(event.input.type == InputTypePress) {
544+
switch(tetris_state->gameState) {
545+
case GameStatePaused:
546+
tetris_state->gameState = GameStatePlaying;
547+
break;
548+
case GameStatePlaying:
549+
tetris_state->gameState = GameStatePaused;
550+
break;
551+
552+
default:
553+
break;
554+
}
555+
}
443556
break;
444557
default:
445558
break;

0 commit comments

Comments
 (0)