Thread: Tetris problem

  1. #16
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    Code:
    $ gcc -W -Wall -ansi -pedantic -g block.c -o block -lcurses
    block.c: In function ‘wfillrect’:
    block.c:34: warning: control reaches end of non-void function
    block.c: In function ‘main’:
    block.c:211: warning: control reaches end of non-void function
    $
    That corresponds to wfillrect() and main(). You never use the return value of wfillrect(), and main() doesn't really matter, but I suggest you return 0 from both of those. You could also make wfillrect() return void, but I'm guessting (sorry) that you wanted to match the ncurses style of prototypes and return int. (Assuming that's what it uses.)

    The shadow is much nicer than I had envisioned! By "shadow", I just meant the way KSirtet does it. (See attached screenshot.) But yours is even nicer! Just one thing: the shadow of the T shape is black on my screen. I'm sure it's just my screen, though. I've attached a screenshot of the game after I've lost (intentionally), to show you the strange colours. (See other attached screenshot.)

    Some more comments about the gameplay and such:
    • If you keep pressing down, over and over again, the game sort of pauses. It could be considered a cheat, I suppose, since you can get an indefinite break from the game. I'd try to fix it if I were you. Actually, this works for any movement key. Press and hold right or left and watch the fireworks.
      [edit] Never mind, I see this was intentional.
      I did make it so when you dropped you were given a second more to position your piece, but that's about all I'll do for now.
      [/edit]
    • If you press down, you still have time to press left or right and shift the block before it is cemented into place. I'm pretty sure you don't really want this.
    • The game always uses 100% of the CPU. I'm sure this isn't really necessary, though it might take another 4 AM programming spree for you to determine this for sure.
    • You get a big advantage in a larger screen mode. You can see a lot more blocks that are to come. Is this what you wanted? . . . .
    • You should consider making "down" move the blocks quickly, but not instantly. That way you could skip ahead if you need to slide a block in sideways or whatever. This is what I did in my tetris game. Note that you'll need to ignore down presses until you get a down keyup event or whatever for the next block, or they'll all come whizzing down, which isn't much fun.


    Code comments:
    • Your code isn't fully ctype.h'ized! What about that switch(in) statement in play()? . . . .
    • You should really have a pause feature. Then I can play, report comments here, and then go back to playing again, and find more bugs and stuff.
    • I know you like ugly macros, but how hard is it to use functions instead? I guess you didn't want to pass seven arguments or something, but you could always use a structure or two. (5 AM.)
    • I still think your rotation is a bit weird. For example, when a 'T' is flipped, the part that protruded down and now protrudes up has moved. In effect, the whole piece moves one unit to the right. It just feels counter-intuitive to me. (6 AM.)
    • Why not make pieces keep their colours once they've fallen down? (You'll never get any sleep at this rate.) Actually, it's not too difficult. Just use ncurses's chtype to store the characters -- chtype stores colour information, as I'm sure you are aware.
      Video attributes can be combined with a character argument passed to addch() or related functions by logical-ORing them into the character. (Thus, text, including attributes, can be copied from one place to another using inch() and addch().). See the curs_attr() page for values of predefined video attribute constants that can be usefully OR'ed into characters.
      From http://www.mkssoftware.com/docs/man3/curs_addch.3.asp
    • All those mvaddstr()'s in init() and mvprintw()'s in again() would be so much easier like this . . .
      Code:
      const char *message[] = {
          " Use arrow",
          "keys to play",
          "UP = Rotate",
          "'N' Next",
          "'S' Shadow",
          "'Q' to Quit"
      };
      size_t x;
      
      for(x = 0; x < sizeof(message) / sizeof(*message); x ++) {
          mvwaddstr (iwin, 5 + x, 2, message[x]);
      }
      You could also make the messages a little more consistent. You have three different formats of specifying keys there. Most of those messages would fit onto one line, too, if you want to save space.
    • Speaking of saving space, I find your practise of putting two for loops on one line disturbing.
    • In draw_piece():
      Code:
        for (d = 0; d < 4; d++) for (c = 0; c < 4; c++)
          if (blocks[rx(r, d, c)][p][rx((r + 1) % 4, d, c)] != '.')
            mvwaddstr(w, d + y, (c + x - 1) * 2 - 1, "  "); /* 2 spaces */
      It seems to me that you don't have to move if you're already in the right place. I'm not sure how efficient ncurses is with moving the cursor to where you already are. Probably very efficient. Forget I said anything.
    • The handlers for KEY_LEFT and KEY_RIGHT are the same except one uses +1 and the other, -1. Perhaps you could pass either +1 or -1 into a function that handled this . . . .
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  2. #17
    Registered User guesst's Avatar
    Join Date
    Feb 2008
    Location
    Lehi, UT
    Posts
    179
    Glad you like the shadow. I'm really proud of it myself, except what do you mean large screen mode gives you an advantage? The game doesn't decide any but one in advance and should be drawing the screen in the middle of your play field? What you describe shouldn't even be possible.

    Many of your game play questions were totally intentional. Rotate causes the piece to "hang", insta-drop, and having a moment to slide pieces in before they cement are all in line with the modern "tournament" versions of Tetris.

    As far as using 100&#37; of the CPU, I'm pretty sure that's me using a single main, no threads or anything. Know what, I'm not worried about it. Well, that and I don't know what to do about it.

    Most of your code comments I'm gonna blow off. No offense, man, your previous comments were brilliant, but I just don't have another all nighter in me for a while, and I have other projects. The exceptions are going to be that wfillrect should not return a value, main should, and the colors. I took some liberties with the colors and while it works pretty good on my system it appears to have some ill effects on yours. Pieces should keep their colors after they've fallen, the messages should be visible, and what's that big black block in the middle of your screen. In short, there's been some errors going on. Gonna have to fix it. I may need an enum.

    I like your solution to the message things, and perhaps i can implement something like that, but probably not on this project. I am absolutely refusing another all nighter.
    Type-ins are back! Visit Cymon's Games at http://www.cymonsgames.com for a new game every week!

  3. #18
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    Quote Originally Posted by guesst View Post
    Glad you like the shadow. I'm really proud of it myself, except what do you mean large screen mode gives you an advantage? The game doesn't decide any but one in advance and should be drawing the screen in the middle of your play field? What you describe shouldn't even be possible.
    Sorry, I should have been clearer. I was talking about the "next" blocks on the right-hand side of the screen. In a larger screen mode, you can see more of them.

    Many of your game play questions were totally intentional. Rotate causes the piece to "hang", insta-drop, and having a moment to slide pieces in before they cement are all in line with the modern "tournament" versions of Tetris.
    Mm'kay, I just mentioned them because they're not in any tetris I've ever played, which is admittedly not very many.

    As far as using 100% of the CPU, I'm pretty sure that's me using a single main, no threads or anything. Know what, I'm not worried about it. Well, that and I don't know what to do about it.
    A simple sleep(1) or something would probably fix this, but I don't know of a nice, portable way to do this. Maybe you could implement it with signals. Either way, it would be a bit complicated.

    Most of your code comments I'm gonna blow off. No offense, man, your previous comments were brilliant, but I just don't have another all nighter in me for a while, and I have other projects. The exceptions are going to be that wfillrect should not return a value, main should, and the colors. I took some liberties with the colors and while it works pretty good on my system it appears to have some ill effects on yours. Pieces should keep their colors after they've fallen, the messages should be visible, and what's that big black block in the middle of your screen. In short, there's been some errors going on. Gonna have to fix it. I may need an enum.
    Yes, well, it could just be my system. If I remember correctly, which I may not, you can use up to 16 colours from ncurses, and only the first 8 are guaranteed to be the same across systems.

    I like your solution to the message things, and perhaps i can implement something like that, but probably not on this project. I am absolutely refusing another all nighter.
    If you say so.

    Well, good luck anyway. It's a very nice program, much better than my tetris.
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  4. #19
    Registered User guesst's Avatar
    Join Date
    Feb 2008
    Location
    Lehi, UT
    Posts
    179
    Quote Originally Posted by dwks View Post
    I like your solution to the message things, and perhaps i can implement something like that, but probably not on this project. I am absolutely refusing another all nighter.
    If you say so.
    I lied. Sure enough, once I set my mind to writing this program... well it's 2:25am right now. I'm just gonna post this before I go to bed.

    Okay, it's been a while, but I really needed to rewrite this one. So I stared with a blank file, did some cutting and pasting, but basically I've gone over every inch of this program again. Incorporated a lot of wisdom I've gained and many suggestions; Structs, your message thing (tho the sizeof bits didn't work. Had to fudge it by passing the width) platform independent colors, as well as control suggestions, including space for drop and down for speed down. I still think it's going to eat up 100&#37; as it's running, but at least now it's only doing so in the space of 4 lines. Maybe the processor can optimize it. Maybe not. One thing I couldn't implement was tolower-ing the switch because it affected the DOWN_ARROW and UP_ARROW constants. Curses uses ints for its input. Tolower returns a char.

    All in all I'm pleased with this one. I'm gonna fork it and make a striped down version for my site.

    If anyone using linux could post me a screen shot, I'd love to see the new color implementation in action.

    Code:
    /* Tetris by Joseph Larson ver 2008-May-01
     */
    #include <curses.h>
    #include <stdlib.h>
    #include <time.h>
    #include <ctype.h>
    #include <string.h>
    
    #define WIDTH 10
    #define HEIGHT 23
    #define PIECES 9
    #define BCOL(x) (x % (COLORS / 2)) + (COLORS / 2)
    
    char blocks[4][PIECES][5] = /* [line][shape][col] */
    {{".X..",".X..",".XX.","..X.",".X..",".X..","....",".X..","..X."},
     {".XX.",".X..",".X..",".XX.",".XX.",".X..",".XX.",".XX.",".XX."},
     {".X..",".XX.",".X..",".X..","..X.",".X..",".XX.",".XX.",".XX."},
     {"....","....","....","....","....",".X..","....","....","...."}};
    int buf[HEIGHT + 1][WIDTH + 2]; /* Buffer for game screen */
    WINDOW *pwin, *nwin, *iwin;
    
    typedef struct {
      int x, y, rot, piece;
    } BRICKTYPE;
    
    char *instruct[10] =
    {"Use arrow", "keys to play", "", "UP- Rotate", "DOWN- Faster", "SPACE- Drop",
     "S- Shadow", "N- Next", "" ,"Q to Quit"};
    char *pausemsg[2] = {"    PAUSED", "Press any key"};
    char *againmsg[4] = {"Do you want", "  to play", "   again", "   (Y/N)"};
    
    void drawmsg(WINDOW *w, int y, int x, int lines, char **msg) {
      int c;
    
      for (c = 0; c < lines; c++)
        mvwprintw (w, y + c, x, msg[c]);
    }
    
    void wfillrect(WINDOW *w, int sy, int sx, int height, int width){
    	int x, y;
    
    	for (y = sy; y < (sy+height); y++) {
    	  wmove (w, y, sx);
    	  for (x = 0; x < width; x++) waddch (w, ' ');
    	}
    }
    
    int rx (int r, int y, int x) { /* Used for piece rotation. */
      int n;
    
      n = (r % 2 ? y : x);
      if(r / 2) n = 3 - n;
      return n;
    }
    
    int clip (BRICKTYPE *b) { /*collision detection */
      int c, d;
    
      for (d = 0; d < 4; d++) for (c = 0; c < 4; c++)
        if (blocks[rx(b->rot, d, c)][b->piece][rx((b->rot + 1) % 4, d, c)] != '.')
          if ((d + b->y < 0) && ((c + b->x - 1 < 1) || (c + b->x - 1 > WIDTH)))
            return 1; /* Edge collisions */
          else if ((d + b->y >= 0) && (buf[d + b->y][c + b->x - 1])) return 1;
      return 0;
    }
    
    void init () {
      int c, x, y;
    
      srand (time(NULL));
      initscr ();
      raw (); nodelay(stdscr,1); noecho(); curs_set(0); nonl(); keypad(stdscr,1);
      start_color();
      for (c = 0; c < COLORS; c++)
        if (c == COLOR_WHITE) init_pair(c, COLOR_BLACK, c);
        else init_pair(c, COLOR_WHITE, c);
      x = (COLS - WIDTH * 2 + 15) / 2 - 1; y = (LINES - HEIGHT) / 2;
      clear (); refresh ();
      pwin = newwin(HEIGHT + 1, WIDTH * 2 + 2, y, x);
      nwin = newwin(6, 10, y + 1, x - 14);
      iwin = newwin(15, 15, y + 8, x - 16);
      wattrset (iwin, COLOR_PAIR (COLORS / 2));
      wfillrect (iwin, 1, 1, 14, 13);
      box (iwin, 0,0);
      drawmsg (iwin, 4, 1, 10, instruct);
      wrefresh (iwin);
    }
    
    void drawplay (int lvl) { /* redraws the entire screen. */
      int backcol, nextcol, x, y;
    
      backcol = lvl % (COLORS / 2); nextcol = (lvl + 1) % (COLORS / 2);
      wattrset (pwin, COLOR_PAIR (backcol));
      wborder(pwin, 0, 0, ' ', 0, ACS_VLINE,  ACS_VLINE, 0, 0);
      for (y = 0; y < HEIGHT; y++) for (x = 1; x <= WIDTH; x++) {
        wattrset (pwin, COLOR_PAIR((buf[y][x]) ? buf[y][x] % 8 + 8 : lvl % 8));
        mvwaddstr (pwin, y, x * 2 - 1, "  ");
      }
      wattrset (nwin, COLOR_PAIR (nextcol));
      box (nwin,0,0);
      wfillrect (nwin, 1, 1, 4, 8);
      mvwaddstr (nwin, 0, 3, "NEXT");
      wrefresh (pwin); wrefresh (nwin);
    }
    
    int clearedlines (int lvl) {
      int x, y, c, d, ret;
    
      ret = 0;
      for (y = 0; y < HEIGHT; y++) {
        c = 0;
        for (x = 1; x <= WIDTH; x++) if (buf[y][x]) c++;
        if (c == WIDTH) {
          ret++;
          for (d = y; d > 0; d--) for (x = 1; x <= WIDTH; x++)
            buf[d][x] = buf[d - 1][x];
          drawplay (lvl); wrefresh (pwin); beep (); napms(75);
        }
      }
      return ret;
    }
    
    void drawpiece (WINDOW *w, int y, int x, int rot, int piece, int color) {
      int c, d;
    
      wattrset (w, COLOR_PAIR (color));
      for (d = 0; d < 4; d++) for (c = 0; c < 4; c++)
        if (blocks[rx(rot, d, c)][piece][rx((rot + 1) % 4, d, c)] != '.')
          mvwaddstr (w, d + y, (c + x - 1) * 2 - 1, "  "); /* 2 spaces */
    }
    
    int play () {
      BRICKTYPE brick, shadow;
      int level, next, lines, in, c, d, shadcol, nextop, shadop;
      double delay;
      clock_t start, check;
    
      level = 1; nextop = shadop = 1; lines = 0;
      for (c = 0; c < HEIGHT + 1; c++) for (d = 0; d < WIDTH + 2; d++)
        buf[c][d] = (c < HEIGHT  && d > 0 && d < WIDTH + 1) ? 0 : 1;
      brick.piece = rand () % PIECES;
      brick.x = WIDTH / 2;
      do { /* Next piece */
        delay = 1.0 - level * .05; if (delay < .05) delay = 0.05;
        brick.y = -3;
        next = rand () % PIECES;
    
        if (brick.piece % (COLORS / 2) == level % (COLORS / 2))
          shadcol = (brick.piece + 1) % (COLORS / 2);
        else shadcol = brick.piece % (COLORS / 2);
    
        drawplay (level);
        mvwprintw (iwin, 1, 2, "Level : %d", level);
        mvwprintw (iwin, 2, 2, "Lines : %d", lines);
        wrefresh (iwin);
        start = clock ();
        shadow.piece = brick.piece;
        do {
          shadow.x = brick.x; shadow.y = brick.y; shadow.rot = brick.rot;
          while (!clip(&shadow)) shadow.y++; shadow.y--;
          if (nextop) drawpiece (nwin, 1, 2, brick.rot, next, BCOL(next));
          if (shadop) drawpiece (pwin, shadow.y, shadow.x, shadow.rot, shadow.piece, shadcol);
          drawpiece (pwin, brick.y, brick.x, brick.rot, brick.piece, BCOL(brick.piece));
          wrefresh (pwin); wrefresh (nwin);
    
          do {
            in = getch ();
            check = clock ();
          } while ((in == ERR)&&((double)(check - start) / CLOCKS_PER_SEC < delay));
    
          if (nextop)
            drawpiece (nwin, 1, 2, brick.rot, next, (level + 1) % (COLORS / 2));
          if (shadop)
            drawpiece (pwin, shadow.y, shadow.x, shadow.rot, shadow.piece, level % (COLORS / 2));
          drawpiece (pwin, brick.y, brick.x, brick.rot, brick.piece, level % (COLORS / 2));
    
          if ((double)(check - start) / CLOCKS_PER_SEC > delay) {
            brick.y++; start = clock ();
          }
          else switch (in) {
            case KEY_RIGHT : brick.x++;
                             if (clip(&brick)) brick.x--;
                             else start = clock ();
                             break;
            case KEY_LEFT  : brick.x--;
                             if (clip(&brick)) brick.x++;
                             else start = clock ();
                             break;
            case KEY_UP    : brick.rot++; brick.rot %= 4;
                             if (clip(&brick)) {brick.rot += 3; brick.rot %= 4;}
                             else start = clock ();
                             break;
            case KEY_DOWN  : brick.y++;
                             start = clock ();
                             break;
            case ' '       : while (!clip(&brick)) brick.y++;
                             brick.y--;
                             start = clock ();
                             break;
            case 'n' :
            case 'N' : nextop = !nextop; break;
            case 's' :
            case 'S' : shadop = !shadop; break;
            case 'p' :
            case 'P' : wattrset (pwin, COLOR_PAIR(level % (COLORS / 2)));
                       wfillrect (pwin, 0, 1, HEIGHT, WIDTH * 2);
                       drawmsg (pwin, HEIGHT / 2, 4, 2, pausemsg);
                       wrefresh (pwin);
                       while (getch() == ERR) {}
                       drawplay (level);
                       wrefresh (pwin);
                       break;
            case 'q' :
            case 'Q' : return (lines);
          }
        } while (!clip (&brick)); /* end of brickfall */
        brick.y--;
        for (d = 0; d < 4; d++) for (c = 0; c < 4; c++) /* commit piece to buffer */
        if (blocks[rx(brick.rot, d, c)][brick.piece][rx((brick.rot + 1) % 4, d, c)] != '.')
          buf[d + brick.y][c + brick.x - 1] = BCOL(brick.piece);
        drawplay (level);
        lines += clearedlines (level);
        level = lines / 10 + 1;
        brick.piece = next; brick.x = WIDTH / 2; brick.y = 0;
        next = rand () % PIECES;
      } while (!clip (&brick)); /* end of game */
      drawpiece (pwin, brick.x, brick.y, brick.rot, brick.piece, BCOL(brick.piece));
      return (lines);
    }
    
    int again (int hs) {
      int x, y, ch;
      WINDOW *againwin;
    
      againwin = newwin(11, 13, (LINES / 2) - 6, (COLS / 2) - 4);
      wattrset (againwin, COLOR_PAIR (COLORS / 2 - 1));
      wfillrect (againwin, 0, 0, 11, 13);
      mvwprintw (againwin, 1, 1, "High Score:");
      mvwprintw (againwin, 3, 3, " %d lines", hs);
      drawmsg (againwin, 6, 1, 4, againmsg);
      wrefresh (againwin);
      wclear (againwin);
      delwin (againwin);
      do {ch = getch();}
      while ((tolower(ch) != 'q')&&(tolower(ch) != 'n')&&(tolower(ch) != 'y'));
      if (tolower(ch) == 'y') return 1;
      else return 0;
    }
    
    int main () {
      int highscore;
    
      init();
      do {highscore = play ();} while (again (highscore));
      endwin();
    }
    A fun variation is to change width to 4 (tho is messes with the exit message, hence the attempt to make a temporary window that crashes the second game through for some reason). I'm also going to make it so that an array of pieces are falling one right after the other in a snake. Gonna call it helltris.
    Last edited by guesst; 05-02-2008 at 03:18 AM.
    Type-ins are back! Visit Cymon's Games at http://www.cymonsgames.com for a new game every week!

  5. #20
    Registered User guesst's Avatar
    Join Date
    Feb 2008
    Location
    Lehi, UT
    Posts
    179
    i'm resurrecting this because this little program has had quite a life since now. My site featured Alleytris, the 4 column variation and I've been hooked on it. So in order to spread my addiction I've made a video of the game to inspire others to play.

    http://www.youtube.com/watch?v=-tTg5y1BLu4
    Type-ins are back! Visit Cymon's Games at http://www.cymonsgames.com for a new game every week!

  6. #21
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Start a new thread.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Memory problem with Borland C 3.1
    By AZ1699 in forum C Programming
    Replies: 16
    Last Post: 11-16-2007, 11:22 AM
  2. Someone having same problem with Code Block?
    By ofayto in forum C++ Programming
    Replies: 1
    Last Post: 07-12-2007, 08:38 AM
  3. A question related to strcmp
    By meili100 in forum C++ Programming
    Replies: 6
    Last Post: 07-07-2007, 02:51 PM
  4. WS_POPUP, continuation of old problem
    By blurrymadness in forum Windows Programming
    Replies: 1
    Last Post: 04-20-2007, 06:54 PM
  5. beginner problem
    By The_Nymph in forum C Programming
    Replies: 4
    Last Post: 03-05-2002, 05:46 PM