📄 tetris.c
字号:
block_draw(WINDOW *win, Block* blk) {
return block_ctrl(win, blk, 1);
}
int
block_left(WINDOW *win, Block* blk) {
(blk->y)--;
if(block_check(blk) == 1) {
(blk->y)++;
if(block_clear(win, blk) == -1)
return -1;
(blk->y)--;
if(block_draw(win, blk) == -1)
return -1;
}
else {
(blk->y)++;
}
return 0;
}
int
block_right(WINDOW *win, Block* blk) {
(blk->y)++;
if(block_check(blk) == 1) {
(blk->y)--;
if(block_clear(win, blk) == -1)
return -1;
(blk->y)++;
if(block_draw(win, blk) == -1)
return -1;
}
else {
(blk->y)--;
}
return 0;
}
/* return -1: error
* 15: descend
* n: the number of the deleted lines
*/
int
block_down(WINDOW *win, Block* blk) {
(blk->x)++;
if(block_check(blk) == 1) {
(blk->x)--;
if(block_clear(win, blk) == -1)
return -1;
(blk->x)++;
if(block_draw(win, blk) == -1)
return -1;
return 0x0f;
}
(blk->x)--;
map_fill(blk);
return map_refresh();
}
int
block_drop(WINDOW *win, Block* blk) {
if(block_clear(win, blk) == -1)
return -1;
while(block_check(blk) == 1) {
(blk->x)++;
}
(blk->x)--;
if(block_draw(win, blk) == -1)
return -1;
map_fill(blk);
return map_refresh();
}
int
block_rotate(WINDOW *win, Block* blk, int dec) {
int oldflag = blk->flag;
if(dec == 0)
blk->flag = RETRORSE(blk->flag);
else
blk->flag = DIRECT(blk->flag);
/* but when flag equals 17 and 18 ,the situation is special, we need more codes to deal with it */
if(blk->flag == 18) {
(blk->x)-=2;
(blk->y)+=2;
}
if(blk->flag == 17) {
(blk->x)+=2;
(blk->y)-=2;
}
if(block_check(blk) == 1) {
int tmp = blk->flag;
blk->flag = oldflag;
if(tmp == 18) {
(blk->x)+=2;
(blk->y)-=2;
}
if(tmp == 17) {
(blk->x)-=2;
(blk->y)+=2;
}
if(block_clear(win, blk) == -1)
return -1;
blk->flag = tmp;
if(tmp == 18) {
(blk->x)-=2;
(blk->y)+=2;
}
if(tmp == 17) {
(blk->x)+=2;
(blk->y)-=2;
}
if(block_draw(win, blk) == -1)
return -1;
}
else {
if(blk->flag == 18) {
(blk->x)+=2;
(blk->y)-=2;
}
if(blk->flag == 17) {
(blk->x)-=2;
(blk->y)+=2;
}
blk->flag = oldflag;
}
return 0;
}
/* the followed three function are not efficient, if you what the game runs faster,
you can use 'map[x][y]' in the function 'map_fill()', 'map_init()' directly
*/
void
map_set(int x, int y) {
if(x<0)
x = 0;
if(y<0)
y = 0;
if(x>=CONX)
x = (CONX-1);
if(y>=CONY)
y = (CONY-1);
map[x][y] = 1;
}
void
map_clr(int x, int y) {
if(x<0)
x = 0;
if(y<0)
y = 0;
if(x>=CONX)
x = (CONX-1);
if(y>=CONY)
y = (CONY-1);
map[x][y] = 0;
}
unsigned char
map_get(int x, int y) {
return map[x][y];
}
/* in followed two function, I don't use 'map_set()' or 'map_clr()' */
void
map_clear() {
int i = 0;
int j = 0;
for(i=0; i<CONX; i++)
for(j=0; j<CONY; j++)
map[i][j] = 0;
//map_clr(i, j);
}
void
map_init() {
int i = 0;
int j = 0;
map_clear();
srand((unsigned)time(NULL));
for(i=CONX-1; i>=(CONX-opt.level); i--)
for(j=0; j<CONY; j++)
map[i][j] = (rand()%2);
}
void
map_fill(Block* blk) {
int i = 0;
int x = 0;
int y = 0;
for(i=0; i<4; i++) {
x = (blk->x)+XOFF(blk->flag, i);
y = (blk->y)+YOFF(blk->flag, i);
map[x][y] = 1;
//map_set(x, y);
}
}
/* the function map_set(), map_clr(), map_get(), we don't need them, why we reverse them ?
now we use array as the map, for some reason maybe we need more complex struct in the future,
then we can modify our function
*/
/* return the number of the deleted lines */
int
map_refresh() {
int i = 0;
int j = 0;
int k = 0;
int count = 0;
unsigned char tmp = 0;
for(i=CONX-1; i>=0; i--) {
for(j=0; j<CONY; j++) {
tmp = map[i][j];
//tmp = map_get(i, j);
if(tmp == 0)
break;
}
if(j != CONY)
continue;
for(k=i-1; k>=0; k--)
for(j=0; j<CONY; j++)
map[k+1][j] = map[k][j];
count++;
i++;
}
return count;
}
void
screen_redraw(WINDOW *main, WINDOW *container, long points, long lines) {
int x = 0;
int y = 0;
/* refresh the container */
touchwin(container);
for(x=0; x<CONX; x++)
for(y=0; y<CONY; y++)
if(map[x][y] == 0)
mvwaddstr(container, x+1, y*2+1, " ");
else
mvwaddstr(container, x+1, y*2+1, "■");
wrefresh(container);
/* renew the score */
// | # | CONX | # |
x = 1+2+1+CONX+1+2+1;
// | ## | CONY | ## | SB | ## |
y = 1+10+1+CONY*2+1+10+1+10+1+10+1;
touchwin(main);
char s[10];
switch(opt.mode) {
case 0: strcpy(s, "Normal");break;
case 1: strcpy(s, "SpeedUp");break;
case 2: strcpy(s, "Horrible");break;
default:
strcpy(s, "Error");break;
};
mvwprintw(main, 1+2+1+CONX+1-5, 1+10+1+CONY*2+1+10, "Mode: %s", s);
mvwprintw(main, 1+2+1+CONX+1-4, 1+10+1+CONY*2+1+10, "Speed: %d", opt.speed);
mvwprintw(main, 1+2+1+CONX+1-3, 1+10+1+CONY*2+1+10, "Level: %d", opt.level);
mvwprintw(main, 1+2+1+CONX+1-2, 1+10+1+CONY*2+1+10, "Score: %d", points);
mvwprintw(main, 1+2+1+CONX+1-1, 1+10+1+CONY*2+1+10, "Lines: %d", lines);
wrefresh(main);
touchwin(container);
}
void
game_end(WINDOW *main, WINDOW *container, long points, long lines) {
touchwin(container);
map_clear();
screen_redraw(main, container, points, lines);
mvwaddstr(container, (CONX/2)+1, (CONY*2+2-strlen("Game Over"))/2, "Game Over");
mvwaddstr(container, (CONX/2)+2, (CONY*2+2-strlen("Wait for 3 seconds..."))/2, "Wait for 3 seconds...");
wrefresh(container);
sleep(3);
/* there are still some codes for store... */
return;
}
void
play() {
int x = 0;
int y = 0;
long points = 0L; // points get
long lines = 0L; // lines kill
int count = 0; //counter for interval
long startpoint = 0L; //for level up
map_init();
// | # | CONX | # |
x = 1+2+1+CONX+1+2+1;
// | ## | CONY | ## | SB | ## |
y = 1+10+1+CONY*2+1+10+1+10+1+10+1;
WINDOW *main = newwin(x, y, (SCRX-x)/2, (SCRY-y)/2); // main window
box(main, 0, 0);
touchwin(main);
wrefresh(main);
WINDOW *container = newwin(1+CONX+1, 1+CONY*2+1, (SCRX-x)/2+1+2, (SCRY-y)/2+1+10); // container
box(container, 0, 0);
touchwin(container);
screen_redraw(main, container, points, lines);
wrefresh(container);
WINDOW *subwin = newwin(1+5+1, 1+10+1, (SCRX-x)/2+1+2, (SCRY-y)/2+1+10+1+CONY*2+1+10); // sub window
box(subwin, 0, 0);
touchwin(subwin);
wrefresh(subwin);
keypad(container, TRUE);
Block blk; // current block
blk.x = 0;
blk.y = CONY/2+1;
blk.flag = block_rand();
if(blk.flag == 17)
(blk.y)--;
touchwin(container);
block_draw(container, &blk); // show current block in container
wrefresh(container);
Block nextblk; // next block
nextblk.x = 0;
nextblk.y = 0;
nextblk.flag = block_rand();
/* impossiable */
if(block_check(&blk) == 0) {
game_end(main, container, points, lines);
keypad(container, FALSE);
touchwin(container);
wclear(container);
wrefresh(container);
delwin(container); // delete container
touchwin(subwin);
wclear(subwin);
wrefresh(subwin);
delwin(subwin); //delete sub window
touchwin(main);
wclear(main);
wrefresh(main);
delwin(main); //delete main window
return;
}
int tmp = 0;
int pf = 0; // pause flag
halfdelay(1); // set halfdelay mode to realize block auto down
touchwin(main);
char s[10];
switch(opt.mode) {
case 0: strcpy(s, "Normal");break;
case 1: strcpy(s, "SpeedUp");break;
case 2: strcpy(s, "Horrible");break;
default:
strcpy(s, "Error");break;
};
mvwprintw(main, 1+2+1+CONX+1-5, 1+10+1+CONY*2+1+10, "Mode: %s", s);
mvwprintw(main, 1+2+1+CONX+1-4, 1+10+1+CONY*2+1+10, "Speed: %d", opt.speed);
mvwprintw(main, 1+2+1+CONX+1-3, 1+10+1+CONY*2+1+10, "Level: %d", opt.level);
mvwprintw(main, 1+2+1+CONX+1-2, 1+10+1+CONY*2+1+10, "Score: %d", points);
mvwprintw(main, 1+2+1+CONX+1-1, 1+10+1+CONY*2+1+10, "Lines: %d", lines);
wrefresh(main);
touchwin(subwin);
block_draw(subwin, &nextblk); // show next block in sub window
wrefresh(subwin);
touchwin(container);
while(1) {
switch (wgetch(container)) {
/* up arrow */
case KEY_UP:
if(pf == 1) // pause mode check
break;
block_rotate(container, &blk, opt.rotate);
break;
/* auto down */
case ERR:
if(pf == 1)
break;
count++;
if(count < (10-opt.speed))
break;
count = 0;
/* down arrow */
case KEY_DOWN:
if(pf == 1)
break;
if((tmp=block_down(container, &blk)) == 0x0f) // block down
break;
if(tmp != 0) { // lines delete
points+=(tmp*2-1);
lines+=tmp;
if(opt.mode == 1) {
if((lines-startpoint) >= LEVELUP) {
startpoint+=LEVELUP;
opt.speed++;
if(opt.speed > 9)
opt.speed = 9;
}
}
for(x=0; x<30; x++) //sleep 0.3 second
usleep(10000);
screen_redraw(main, container, points, lines); //redraw the screen
}
blk.x = 0;
blk.y = CONY/2+1;
blk.flag = nextblk.flag; // next block to current block
if(blk.flag == 17) // flag == 17 is special
(blk.y)--;
touchwin(subwin);
block_clear(subwin, &nextblk);
nextblk.flag = block_rand(); // new next block
block_draw(subwin, &nextblk);
wrefresh(subwin); // sub window refresh
touchwin(container);
block_draw(container, &blk);
wrefresh(container); // draw container
if(block_check(&blk) == 0) { // check game over
game_end(main, container, points, lines);
keypad(container, FALSE);
touchwin(container);
wclear(container);
wrefresh(container);
delwin(container); // delete container
touchwin(subwin);
wclear(subwin);
wrefresh(subwin);
delwin(subwin); //delete sub window
touchwin(main);
wclear(main);
wrefresh(main);
delwin(main); //delete main window
return;
}
break;
/* left arrow */
case KEY_LEFT:
if(pf == 1)
break;
block_left(container, &blk);
break;
/* right arrow */
case KEY_RIGHT:
if(pf == 1)
break;
block_right(container, &blk);
break;
/* drop */
case ' ':
noecho();
if(pf == 1)
break;
tmp = block_drop(container, &blk);
/* follow codes same as KEY_DOWN */
if(tmp != 0) {
points+=(tmp*2-1);
lines+=tmp;
if(opt.mode == 1) {
if((points-startpoint) >= LEVELUP) {
startpoint+=LEVELUP;
opt.speed++;
if(opt.speed > 9)
opt.speed = 9;
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -