xTetris-Game
Loading...
Searching...
No Matches
gameCtrl.c
Go to the documentation of this file.
1#include <stdio.h>
2#include <stdlib.h>
3#include <wchar.h>
4#include <stdlib.h>
5#include <time.h>
6#include <unistd.h>
7
8#include "gameCtrl.h"
9#include "common.h"
10#include "definitions.h"
11#include "struct.h"
12#include "manageStruct.h"
13#include "printGame.h"
14
15
16int randGen(int min, int max){
17
18 int range;
19 int r;
20
21 srand(time(NULL));
22
23 range = max - min + 1;
24 r = rand() % range;
25
26 return min + r;
27}
28
30
31 int i, j, totPoints, consecutive, row, points;
32 int fullRow[HEIGHT];
33
34 for(i=HEIGHT-1; i>=0; i--){
35 row = VALID;
36 fullRow[i] = INVALID;
37 for(j=0; j<WIDTH; j++){
38 if(board[i][j].status==EMPTY_BOX){
39 j = WIDTH;
40 row = INVALID;
41 }
42 }
43 if(row == VALID){
44 fullRow[i] = VALID;
45 }
46 }
47
48 for(i=0, consecutive=0, totPoints=0; i<HEIGHT; i++){
49 if(fullRow[i] == VALID){
50 consecutive++;
51 if((i+1<HEIGHT && fullRow[i+1]==VALID) || i==HEIGHT-1){
52 points=1;
53 if(consecutive>1)
54 for(j=2, points=3; j<consecutive; j++)
55 points *= 2;
56 totPoints += points;
57 consecutive = 0;
58 }
59 }
60 }
61
62 for(i=HEIGHT-1, row=HEIGHT-1; i>=0; i--, row--){
63 while(row>=0 && fullRow[row]==VALID){
64 row--;
65 }
66 if (row!=i){
67 for(j=0; j<WIDTH; j++){
68 if(row>=0)
69 board[i][j].status=board[row][j].status;
70 else
71 board[i][j].status=EMPTY_BOX;
72 }
73 }
74 }
75
76 return totPoints;
77}
78
79void makeMove(BoardPtr board, TetrominoPtr tetro, MovePtr storeMove){
80
81 int i, j, k, l, piece, rotation;
82
83 piece = storeMove->piece;
84 rotation = storeMove->rotation;
85
86 for(i=0; i<HEIGHT; i++){
87 for(j=0; j<WIDTH; j++){
88 if(board[i][j].status == TETRO_BOX)
89 board[i][j].status = EMPTY_BOX;
90 }
91 }
92
93 for(i=(storeMove->row), l=0; l<TETRO_DIM; i++, l++){
94 for(j=(storeMove->col), k=0; k<TETRO_DIM; j++, k++){
95 if(tetro[piece][rotation][l][k].status == TETRO_BOX)
96 board[i][j].status = TETRO_BOX;
97 }
98 }
99
100}
101
102int validRotation(BoardPtr board, TetrominoPtr tetro, MovePtr storeMove, int direction){
103
104 int i, j, k, l;
105 int piece, rotation;
106
107
108 piece = storeMove->piece;
109 rotation = storeMove->rotation + direction;
110
111 if(rotation<0 || rotation>=TETRO_ROT)
112 rotation -= (direction * 4);
113
114 for(k=0, i = storeMove->row; k<TETRO_DIM; i++, k++){
115 for(l=0, j = storeMove->col; l<TETRO_DIM; j++, l++){
116 if(i>=0 && i<HEIGHT && j>=0 && j<WIDTH && board[i][j].status==BOARD_BOX && tetro[piece][rotation][k][l].status==TETRO_BOX){
117 return INVALID;
118 }
119 else if((i<0 || i>=HEIGHT || j<0 || j>=WIDTH) && tetro[piece][rotation][k][l].status==TETRO_BOX){
120 return INVALID;
121 }
122 }
123 }
124
125 storeMove->rotation = rotation;
126
127 return VALID;
128}
129
130int validMove(BoardPtr board, MovePtr storeMove, int move){
131
132 int i, j;
133
134 for(i=0; i<HEIGHT; i++){
135 for(j=0; j<WIDTH; j++){
136 if(board[i][j].status==TETRO_BOX && (j+move<0 || j+move>=WIDTH || board[i][j+move].status>TETRO_BOX))
137 return INVALID;
138 }
139 }
140
141 (storeMove->col) += move;
142
143 return VALID;
144}
145
146int fallingTetromino(BoardPtr board, MovePtr storeMove){
147
148 int i, j;
149 int fall;
150
151 for(i=0, fall=VALID; i<HEIGHT; i++){
152 for(j=0; j<WIDTH && fall==VALID; j++){
153 if(board[i][j].status==TETRO_BOX && i+1>=HEIGHT){
154 fall = INVALID;
155 }
156 else if(board[i][j].status==TETRO_BOX && board[i+1][j].status>TETRO_BOX){
157 fall = INVALID;
158 }
159 }
160 }
161
162 if(fall==VALID){
163 for(i=HEIGHT-1; i>=0; i--){
164 for(j=0; j<WIDTH; j++){
165 if(board[i][j].status==TETRO_BOX){
166 board[i+1][j].status = TETRO_BOX;
167 board[i][j].status = EMPTY_BOX;
168 }
169 }
170 }
171 (storeMove->row)++;
172 }
173 else{
174 for(i=0; i<HEIGHT; i++){
175 for(j=0; j<WIDTH; j++){
176 if(board[i][j].status==TETRO_BOX){
177 board[i][j].status = BOARD_BOX;
178 }
179 }
180 }
181 }
182
183 return fall;
184}
185
186int addTetromino(BoardPtr board, TetrominoPtr tetro, MovePtr storeMove){
187
188 int i, j, l, y;
189 int shape, rotation, row, col;
190
191 storeMove->piece = randGen(0, N_PIECES-1);
192 storeMove->rotation = randGen(0, TETRO_ROT-1);
193
194 col = WIDTH/2 - TETRO_DIM/2;
195
196 shape = storeMove->piece;
197 rotation = storeMove->rotation;
198
199 for(i=0, row=-1; i<TETRO_DIM && row==-1; i++){
200 for(j=0; j<TETRO_DIM && row==-1; j++){
201 if(tetro[shape][rotation][i][j].status==TETRO_BOX && row==-1)
202 row = i;
203 }
204 }
205 for(i=0, y = row; y<TETRO_DIM; y++, i++){
206 for(j=col, l=0; l<TETRO_DIM; j++, l++){
207 if(tetro[shape][rotation][y][l].status==TETRO_BOX && board[i][j].status==BOARD_BOX)
208 return 1;
209 }
210 }
211 for(i=0, y = row; y<TETRO_DIM; y++, i++){
212 for(j=col, l=0; l<TETRO_DIM; j++, l++){
213 if(tetro[shape][rotation][y][l].status==TETRO_BOX)
214 board[i][j].status = TETRO_BOX;
215 }
216 }
217
218 storeMove->row = 0 - row;
219 storeMove->col = col - 0;
220
221 return 0;
222}
223
224void startGame(int mode){
225
226 int skip, prevPoints, fall, move, complete, variableTLimit;
227
228 int key = RESET;
229 int points_1 = 0;
230 int points_2 = 0;
231
232 clock_t start, timeDiff;
233
234 BoardPtr board_1, board_2;
235 MovePtr storeMove;
236 TetrominoPtr tetro;
237
238 board_1 = initializeBoard();
239 board_2 = initializeBoard();
240 storeMove = initializeMove();
241 tetro = initializeTetrominoes();
242
243 variableTLimit = timeLimit;
244 prevPoints = 0;
245 fall = 0;
246 move = 1;
247 complete = 0;
248 prevPoints = 0;
249 while(complete==0){
250 if(fall==0){
251 complete = addTetromino(board_1, tetro, storeMove);
252 move = 1;
253 }
254 if (complete==0){
255 fall = 1;
256 skip = 0;
257 start = clock();
258 do{
259 if(move==1){
260 printBoard(board_1, board_2, points_1, points_2, mode);
261 move = 0;
262 }
263 if(u_kbhit()){
264 key=u_getchar();
265 if(key==LOWER_W || key==UPPER_W){
266 move = validRotation(board_1, tetro, storeMove, -1);
267 if (move==1){
268 makeMove(board_1, tetro, storeMove);
269 }
270 }
271 else if(key==LOWER_S || key==UPPER_S){
272 move = validRotation(board_1, tetro, storeMove, 1);
273 if (move==1){
274 makeMove(board_1, tetro, storeMove);
275 }
276 }
277 else if(key==LOWER_A || key==UPPER_A){
278 move = validMove(board_1, storeMove, -1);
279 if (move==1){
280 makeMove(board_1, tetro, storeMove);
281 }
282 }
283 else if(key==LOWER_D || key==UPPER_D){
284 move = validMove(board_1, storeMove, 1);
285 if (move==1){
286 makeMove(board_1, tetro, storeMove);
287 }
288 }
289 else if(key==SPACEBAR){
290 move = 1;
291 skip = 1;
292 }
293 else if(key==CTRL_C)
294 exitFailure();
295 usleep(NSEC_TO_SLEEP);
296 }
297 timeDiff = (clock()-start)*1000/CLOCKS_PER_SEC;
298 }
299 while(timeDiff<variableTLimit && skip==0);
300 fall = fallingTetromino(board_1, storeMove);
301 printBoard(board_1, board_2, points_1, points_2, mode);
302 if(fall==0){
303 prevPoints = points_1;
304 points_1 += clearFullRows(board_1);
305 if (points_1 != prevPoints){
306 printBoard(board_1, board_2, points_1, points_2, mode);
307 variableTLimit -= MULTIPLIER * (points_1 - prevPoints);
308 move = 1;
309 }
310 }
311 }
312 }
313
314
315 if(mode == MULTIPLAYER){
316 clearCLI();
317 heightSpacing(1);
318 printCentered(L"🕹 Press ENTER to continue with second player...");
319 waitUser();
320 variableTLimit = timeLimit;
321 prevPoints = 0;
322 fall = 0;
323 move = 1;
324 complete = 0;
325 prevPoints = 0;
326 while(complete==0){
327 if(fall==0){
328 complete = addTetromino(board_2, tetro, storeMove);
329 move = 1;
330 }
331 if (complete==0){
332 fall = 1;
333 skip = 0;
334 start = clock();
335 do{
336 if(move==1){
337 printBoard(board_1, board_2, points_1, points_2, mode);
338 move = 0;
339 }
340 if(u_kbhit()){
341 key=u_getchar();
342 if(key==LOWER_W || key==UPPER_W){
343 move = validRotation(board_2, tetro, storeMove, -1);
344 if (move==1){
345 makeMove(board_2, tetro, storeMove);
346 }
347 }
348 else if(key==LOWER_S || key==UPPER_S){
349 move = validRotation(board_2, tetro, storeMove, 1);
350 if (move==1){
351 makeMove(board_2, tetro, storeMove);
352 }
353 }
354 else if(key==LOWER_A || key==UPPER_A){
355 move = validMove(board_2, storeMove, -1);
356 if (move==1){
357 makeMove(board_2, tetro, storeMove);
358 }
359 }
360 else if(key==LOWER_D || key==UPPER_D){
361 move = validMove(board_2, storeMove, 1);
362 if (move==1){
363 makeMove(board_2, tetro, storeMove);
364 }
365 }
366 else if(key==SPACEBAR){
367 move = 1;
368 skip = 1;
369 }
370 else if(key==CTRL_C)
371 exitFailure();
372 usleep(NSEC_TO_SLEEP);
373 }
374 timeDiff = (clock()-start)*1000/CLOCKS_PER_SEC;
375 }
376 while(timeDiff<variableTLimit && skip==0);
377 fall = fallingTetromino(board_2, storeMove);
378 printBoard(board_1, board_2, points_1, points_2, mode);
379 if(fall==0){
380 prevPoints = points_2;
381 points_2 += clearFullRows(board_2);
382 if (points_2 != prevPoints){
383 printBoard(board_1, board_2, points_1, points_2, mode);
384 variableTLimit -= MULTIPLIER * (points_2 - prevPoints);
385 move = 1;
386 }
387 }
388 }
389 }
390 }
391
392 gameOver(points_1, points_2, mode);
393
394 destroyBoard(board_1);
395 destroyBoard(board_2);
396 destroyMove(storeMove);
397 destroyTetromino(tetro);
398
399}
void printCentered(wchar_t *text)
Print text centered horizontally based on string length.
Definition common.c:71
void heightSpacing(int filledHeight)
Apply a vertical spacing based on window heigth and what need to be printed.
Definition common.c:76
const int timeLimit
Time waiting before letting the tetromino fall one row below.
Definition common.c:19
void waitUser(void)
Wait until Enter key is pressed.
Definition common.c:146
void exitFailure(void)
Exit the program after cleaning everything on screen.
Definition common.c:36
void clearCLI(void)
Call the system function to clear the cli.
Definition common.c:25
int u_getchar(void)
Definition common.c:118
int u_kbhit(void)
Definition common.c:91
#define BOARD_BOX
Definition definitions.h:12
#define UPPER_A
Definition definitions.h:26
#define LOWER_W
Definition definitions.h:30
#define WIDTH
Definition definitions.h:6
#define TETRO_ROT
Definition definitions.h:9
#define UPPER_D
Definition definitions.h:28
#define LOWER_A
Definition definitions.h:31
#define TETRO_BOX
Definition definitions.h:13
#define N_PIECES
Definition definitions.h:8
#define TETRO_DIM
Definition definitions.h:10
#define MULTIPLAYER
Definition definitions.h:36
#define LOWER_D
Definition definitions.h:33
#define EMPTY_BOX
Definition definitions.h:14
#define UPPER_S
Definition definitions.h:27
#define CTRL_C
Definition definitions.h:19
#define RESET
Definition definitions.h:22
#define MULTIPLIER
Definition definitions.h:41
#define SPACEBAR
Definition definitions.h:23
#define UPPER_W
Definition definitions.h:25
#define VALID
Definition definitions.h:16
#define INVALID
Definition definitions.h:17
#define HEIGHT
Definition definitions.h:5
#define LOWER_S
Definition definitions.h:32
#define NSEC_TO_SLEEP
Definition definitions.h:43
int fallingTetromino(BoardPtr board, MovePtr storeMove)
Check if tetromino has landed or will move it one step down.
Definition gameCtrl.c:146
int validMove(BoardPtr board, MovePtr storeMove, int move)
Check if a move of the tetromino is valid.
Definition gameCtrl.c:130
int randGen(int min, int max)
Generates a random integer within a specified range.
Definition gameCtrl.c:16
void startGame(int mode)
Start the gameplay.
Definition gameCtrl.c:224
int clearFullRows(BoardPtr board)
Change tetromino position as moved by the player.
Definition gameCtrl.c:29
void makeMove(BoardPtr board, TetrominoPtr tetro, MovePtr storeMove)
Change tetromino position as moved by the player.
Definition gameCtrl.c:79
int addTetromino(BoardPtr board, TetrominoPtr tetro, MovePtr storeMove)
Drop a tetromino into the board.
Definition gameCtrl.c:186
int validRotation(BoardPtr board, TetrominoPtr tetro, MovePtr storeMove, int direction)
Check if the tetromino rotation is valid.
Definition gameCtrl.c:102
BoardPtr initializeBoard(void)
Allocate memory for BoardPtr struct.
void destroyTetromino(TetrominoPtr parts)
Free the TetrominoPtr struct passed as parameter.
void destroyBoard(BoardPtr board)
Free the BoardPtr struct passed as parameter.
TetrominoPtr initializeTetrominoes(void)
Allocate memory for TetraminoPtr struct.
MovePtr initializeMove(void)
Allocate memory for MovePtr struct.
void destroyMove(MovePtr storeMove)
Free the MovePtr struct passed as parameter.
void gameOver(int points_1, int points_2, int mode)
Print end screen result.
Definition printGame.c:80
void printBoard(BoardPtr mat_1, BoardPtr mat_2, int points_1, int points_2, int mode)
Print the game with object in the terminal.
Definition printGame.c:38
Definition struct.h:5
int status
Definition struct.h:6
Definition struct.h:10
int row
Definition struct.h:13
int rotation
Definition struct.h:12
int piece
Definition struct.h:11
int col
Definition struct.h:14