#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include "termio.h" #define WID 10 #define HEI 22 #define USE_UNICODE const int stones[7][4]={ {0b0000010001110000, 0b0000001100100010, 0b0000000001110001, 0b0000001000100110}, {0b0000000101110000, 0b0000001000100011, 0b0000000001110100, 0b0000011000100010}, {0b0000001101100000, 0b0000010001100010, 0b0000001101100000, 0b0000010001100010}, {0b0000001001110000, 0b0000001000110010, 0b0000000001110010, 0b0000001001100010}, {0b0000011000110000, 0b0000001001100100, 0b0000011000110000, 0b0000001001100100}, {0b0000111100000000, 0b0100010001000100, 0b0000111100000000, 0b0100010001000100}, {0b0000011001100000, 0b0000011001100000, 0b0000011001100000, 0b0000011001100000}, }; static int leveldelay(int level){ int d=700-(level-1)*35; if(d<50)d=50; return d; } struct board { int lines[HEI]; int stone,str,stx,sty; int ncleared,level,delay; }; static bool boardvalid(const struct board *bd){ int st=stones[bd->stone][bd->str]; for(int y=0;y<4;y++){ for(int x=0;x<4;x++){ int bit=1<<(4*y+x); if(st&bit){ if(bd->sty+y>=HEI)return false; if(bd->stx+x<0||bd->stx+x>=WID)return false; } if(bd->lines[bd->sty+y]&((!!(st&bit))<<(bd->stx+x)))return false; } } return true; } static void persist(struct board *bd){ int st=stones[bd->stone][bd->str]; for(int y=0;y<4;y++){ for(int x=0;x<4;x++){ bd->lines[bd->sty+y]|=(!!(st&(1<<(4*y+x))))<<(bd->stx+x); } } } // [0,mx) static int randomrange(unsigned int mx){ assert(mx<=(unsigned int)RAND_MAX+1); unsigned int diff=((unsigned int)RAND_MAX+1)%mx; unsigned int r=random(); // happens at most 50% of the time if(r>RAND_MAX-diff)return randomrange(mx); return r%mx; } // returns false iff game over static bool spawn(struct board *bd){ bd->stone=randomrange(7); bd->str=random()%4; bd->stx=WID/2-1; for(bd->sty=0;(stones[bd->stone][bd->str]&((1<<(4+4*-bd->sty))-1))==0;bd->sty--); return boardvalid(bd); } static void show(const struct board *bd){ struct board bd2; memcpy(&bd2,bd,sizeof bd2); persist(&bd2); clearscreen(); #ifdef USE_UNICODE assert(WID%2==0); int boardwid=WID*3/2; #else int boardwid=WID; #endif for(int y=0;yncleared,bd->level); redraw(); } static void checklines(struct board *bd){ for(int y=HEI-1;y>=0;y--){ if(bd->lines[y]==(1<lines+1,bd->lines,y*sizeof(int)); bd->lines[0]=0; y++; bd->ncleared++; if(bd->ncleared%10==0){ bd->level++; bd->delay=leveldelay(bd->level); } } } } // returns false iff game over static bool advance(struct board *bd){ if(bd->stone<0){ return spawn(bd); } bd->sty++; if(!boardvalid(bd)){ bd->sty--; persist(bd); checklines(bd); return spawn(bd); } return true; } static void move(struct board *bd,int dir){ bd->stx+=dir; if(!boardvalid(bd))bd->stx-=dir; } static void rotR(struct board *bd){ bd->str=(bd->str+1)%4; if(!boardvalid(bd))bd->str=(bd->str+3)%4; } static void delaytimeout(const struct board *bd,struct timeval *tv){ tv->tv_sec=bd->delay/1000; tv->tv_usec=bd->delay%1000*1000; } int main(void){ { struct timeval tv; gettimeofday(&tv,NULL); srandom(tv.tv_sec*1000000L+tv.tv_usec); } initkeyboard(false); atexit(endkeyboard); initscreen(); atexit(endscreen); cursorvisible(false); struct board bd; memset(&bd,0,sizeof bd); bd.stone=-1; bd.ncleared=0; bd.level=1; bd.delay=leveldelay(bd.level); spawn(&bd); struct timeval timeleft; delaytimeout(&bd,&timeleft); while(true){ show(&bd); struct timeval start; gettimeofday(&start,NULL); fd_set inset; FD_ZERO(&inset); FD_SET(0,&inset); struct timeval timeout; memcpy(&timeout,&timeleft,sizeof timeout); int ret=select(1,&inset,NULL,NULL,&timeout); if(ret<0){ if(errno!=EINTR){ endscreen(); perror("select"); exit(1); } else { FD_ZERO(&inset); } } struct timeval end; gettimeofday(&end,NULL); if(ret==0){ if(!advance(&bd))break; delaytimeout(&bd,&timeleft); continue; } end.tv_sec-=start.tv_sec; end.tv_usec-=start.tv_usec; if(end.tv_usec<0){ end.tv_sec--; end.tv_usec+=1000000; } timeleft.tv_sec-=end.tv_sec; timeleft.tv_usec-=end.tv_usec; if(timeleft.tv_usec<0){ timeleft.tv_sec--; if(timeleft.tv_sec<0){ timeleft.tv_sec=timeleft.tv_usec=0; } else { timeleft.tv_usec+=1000000; } } if(FD_ISSET(0,&inset)){ int key=tgetkey(); if(key=='S'||key=='s'||key==KEY_DOWN){ if(!advance(&bd))break; delaytimeout(&bd,&timeleft); } else if(key=='A'||key=='a'||key==KEY_LEFT)move(&bd,-1); else if(key=='D'||key=='d'||key==KEY_RIGHT)move(&bd,1); else if(key=='W'||key=='w'||key==KEY_UP)rotR(&bd); else if(key==KEY_CTRL+'L')redrawfull(); else bel(); } } endscreen(); printf("Game Over!\nLines cleared: %d\nLevel reached: %d\n",bd.ncleared,bd.level); }