Thread: Tetris problem

  1. #1
    Registered User guesst's Avatar
    Join Date
    Feb 2008
    Location
    Lehi, UT
    Posts
    179

    Tetris problem

    Okay, I'm thinking of cracking out a little tetris like blocks game. So what's everyone's "favorite" method of doing tetris-like rotations in a game?
    1. Pre-rotated arrays
    2. Code left/right/straight and rotate relative to starting block
    3. something I missed?
    Type-ins are back! Visit Cymon's Games at http://www.cymonsgames.com for a new game every week!

  2. #2
    Registered User
    Join Date
    Sep 2006
    Posts
    8,868
    If you put up your image or grids in advance, then the game has the greatest responsiveness for the player. I prefer images done in advance, myself.

  3. #3
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    I'm going to guess, from your other programs, that you'll be using a text mode here. In that case, rotating a 2D array of say 4x4 elements would take no time at all -- but it would also take very little memory should you decide to pre-rotate them. (I would rotate them with your program as it initializes, not by hand, though.) You should use whatever is easier, I guess.

    I would definitely not have any rotation-specific code, however.

    I'd be interested to see what you come up with.
    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. #4
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Since most tetris pieces have 2 orientations you could simply hard code them. You can place the patterns into an array and use the array to later draw the pattern based on current orientation. There will be some rare edge cases that won't completely fit this paradigm which you will have to account for.

  5. #5
    Registered User guesst's Avatar
    Join Date
    Feb 2008
    Location
    Lehi, UT
    Posts
    179
    Quote Originally Posted by dwks View Post
    I'd be interested to see what you come up with.
    Oh, you will.

    And bubba, two of the pieces of 4 rotations (the 'L's), four of them have 2 rotations (the 'T', the two zig zags, and the straight) and one of them only has 1 rotation (the square). But the basic idea is that they all have 4.
    Last edited by guesst; 04-04-2008 at 03:48 PM.
    Type-ins are back! Visit Cymon's Games at http://www.cymonsgames.com for a new game every week!

  6. #6
    Registered User Swarvy's Avatar
    Join Date
    Apr 2008
    Location
    United Kingdom
    Posts
    195
    I would say hard code them. There aren't many different orientations to deal with, so generating a function to rotate the images seems a little unnecessary if you ask me.

  7. #7
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    I'd be tempted to follow Swarvy's advice - however, I think there are four variants of the T shape as well - the "down" bar of a normal T can point right, up, left and down. [But I've got confused about rotation of stuff before].

    It would be fairly easy to come up with a data structure defining the different rotation states of the shapes. Just let all four possible states be represented, that makes the code nice and generic - there will be very little difference in changing the shape and keeping the old shape for those that have less than 4 states - the extra comparison needed will cost almost as much as assigning a new pointer, and pretty often the shape is changing anyways.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  8. #8
    Registered User guesst's Avatar
    Join Date
    Feb 2008
    Location
    Lehi, UT
    Posts
    179
    Quote Originally Posted by matsp View Post
    I'd be tempted to follow Swarvy's advice - however, I think there are four variants of the T shape as well - the "down" bar of a normal T can point right, up, left and down. [But I've got confused about rotation of stuff before].
    Doh! I knew that. I did.

    Know what, I'm going to have the program rotate it so I can reserve my data declarations for the "neutral" position of the pieces, that way I can add as many pieces as I want. What's more, I'm going to define nested the "wrong" way so that in my code the shapes themselves can be distinguished, just to be difficult.
    Type-ins are back! Visit Cymon's Games at http://www.cymonsgames.com for a new game every week!

  9. #9
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    One thing to keep in mind is your axis of rotation. For example, when the longest "I" piece is rotated, you probably don't want it rotated on the upper left corner of the shape. Perhaps the second . . . block from the left would stay in place as the object was rotated, but not the block in the corner.

    That was probably pretty confusing. Here's what I mean. You might not want this:
    Code:
    ****
    ----
    ----
    ----
    ->
    Code:
    *---
    *---
    *---
    *---
    As opposed to this:
    Code:
    ----
    ****
    ----
    ----
    ->
    Code:
    -*--
    -*--
    -*--
    -*--
    Then again, maybe you do.
    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.

  10. #10
    Registered User guesst's Avatar
    Join Date
    Feb 2008
    Location
    Lehi, UT
    Posts
    179
    I told my wife that I felt really bad not having time to program and that I really wanted to make this Tetris program. So she loving took the kids to see grandma and gave me some consecutive hours to get started. I finished it up after she went to bed because I'm up with a cough so basically this was done in one marathon session.

    Scroll to the bottom, I got some notes there.

    If you see anything obviously less efficient or just plain wrong lemme know.

    Code:
    /* Blocks by Joseph Larson ver 4 April 2008*/
    #include <curses.h>
    #include <stdlib.h>
    #include <time.h>
    
    #define WIDTH 10
    #define HEIGHT 20
    #define PIECES 8
    #define CLEARBLOCKS drawpiece (nwin, next, rot, (level + 1) % 8, 1, 2);\
      drawpiece (pwin, piece, rot, level % 8, y, x);
    #define DRAWBLOCKS drawpiece (nwin, next, rot, next % 8 + 8, 1, 2);\
      drawpiece (pwin, piece, rot, piece % 8 + 8, y, x);
    #define REF wrefresh (pwin); wrefresh (nwin);
    
    char blocks[4][PIECES][4] = // [line][shape][col]
    {{".X..",".X..",".XX.","..X.",".X..",".X..","....",".XX."},
     {".XX.",".X..",".X..",".XX.",".XX.",".X..",".XX.",".X.."},
     {".X..",".XX.",".X..",".X..","..X.",".X..",".XX.",".XX."},
     {"....","....","....","....","....",".X..","....","...."}};
    int buf[HEIGHT + 1][WIDTH + 2];
    WINDOW *pwin, *nwin, *iwin, *bwin;
    
    int wfillrect(WINDOW *w, int sy, int sx, int height, int width){
    	int x, y;
    
    	for (x = sx; x < (sx+width); x++){
    	  for (y = sy; y < (sy+height); y++){
          mvwaddch (w, y, x, ' ');
    	  }
    	}
    }
    
    int rx (int r, int y, int x) {
      switch (r) {
        case 0 : return x; break;
        case 1 : return y; break;
        case 2 : return 3 - x; break;
        case 3 : return 3 - y; break;
      }
      return 0;
    }
    
    int clip (int p, int r, int y, int x) {
      int c, d;
    
      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)] != '.')
          if (buf[d + y][c + x - 1]) return 1;
      return 0;
    }
    
    void drawpiece (WINDOW *w, int p, int r, int col, int y, int x) {
      int c, d;
    
      wattrset (w, COLOR_PAIR(col));
      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
    }
    
    void init() { // Setup Curses the way we want it.
      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++) init_pair(c, COLOR_WHITE, c);
      x = (COLS - WIDTH * 2) / 2 - 1; y = (LINES - HEIGHT) / 2;
      clear ();
      refresh ();
      pwin = newwin(HEIGHT + 1, WIDTH * 2 + 2, y, x);
      nwin = newwin(6, 10, y + 2, x - 14);
      iwin = newwin(10, 15, y + 10, x - 16);
      wattrset (iwin, COLOR_PAIR (8));
      box (iwin,0,0);
      wfillrect (iwin, 1, 1, 8, 13);
      mvwaddstr (iwin, 5, 3, "Use arrow");
      mvwaddstr (iwin, 6, 2, "keys to play");
      mvwaddstr (iwin, 7, 2, "UP = Rotate");
      mvwaddstr (iwin, 8, 2, "'Q' to Quit");
      bwin = newwin(LINES - 1, 10, 0, x + WIDTH * 2 + 6);
      for (y = 0; y < LINES - 3; y += 2) { // Decorate a bit
        c = rand () % PIECES; drawpiece (bwin, c, rand () % 4, c % 8 + 1, y, 2);
      }
      wrefresh (bwin);
    }
    
    void drawplay (int lvl) {
      int x, y;
    
      lvl %= 8;
      wattrset (pwin, COLOR_PAIR (lvl));
      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] : lvl));
        mvwaddstr (pwin, y, x * 2 - 1, "  ");
      }
      wattrset (nwin, COLOR_PAIR ((lvl + 1) % 8));
      box (nwin,0,0);
      wfillrect (nwin, 1, 1, 4, 8);
      mvwaddstr (nwin, 0, 3, "NEXT");
    }
    
    int clearedlines (int l) {
      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 (l); REF; beep (); napms(75);
        }
      }
      return ret;
    }
    
    int play () {
      int piece, rot, x, y, next, level, lines, in, c, d;
      double delay;
      clock_t start, check;
    
      for (y = 0; y < HEIGHT + 1; y ++) for (x = 0; x < WIDTH + 2; x++)
        buf[y][x] = (y < HEIGHT  && x > 0 && x < WIDTH + 1) ? 0 : 1;
      level = 1; lines = 0; rot = 0; x = WIDTH / 2; y = 0; next = rand () % PIECES;
      do {
        delay = 1.0 - level * .1;
        if (delay < 0.1) delay = 0.1;
        piece = next; next = rand () % PIECES;
        drawplay (level);
        do {
          mvwprintw (iwin, 1, 2, "Level : %d", level);
          mvwprintw (iwin, 3, 2, "Lines : %d", lines);
          wrefresh (iwin);
          CLEARBLOCKS; y++; DRAWBLOCKS; REF;
          start = clock ();
          do {
            in = getch();
            switch (in) {
              case KEY_RIGHT : if (!clip(piece, rot, y, x + 1)) {
                                CLEARBLOCKS; x++; DRAWBLOCKS; REF;
                              } break;
              case KEY_LEFT  : if (!clip(piece, rot, y, x - 1)) {
                                CLEARBLOCKS; x--; DRAWBLOCKS; REF;
                              } break;
              case KEY_DOWN  : CLEARBLOCKS;
                               while (!clip(piece, rot, y + 1, x)) y++;
                               DRAWBLOCKS; REF; break;
              case KEY_UP    : if (!clip(piece, (rot + 1) % 4, y, x)) {
                                CLEARBLOCKS; rot++; rot %= 4; DRAWBLOCKS; REF;
                                start = clock ();
                              }  break;
              case 'q' : return (lines);
            }
            check = clock ();
          } while ((double)(check - start) / CLOCKS_PER_SEC < delay);
        } while (!clip (piece, rot, y + 1, x));
        for (d = 0; d < 4; d++) for (c = 0; c < 4; c++) // commit piece to buffer;
        if (blocks[rx(rot, d, c)][piece][rx((rot + 1) % 4, d, c)] != '.')
          buf[d + y][c + x - 1] = piece + 8;
        drawplay (level);
        REF;
        lines += clearedlines(level);
        level = lines / 10 + 1;
        x = WIDTH / 2; y = 0;
      } while (!clip (next, rot, y, x));
      drawpiece (pwin, next, rot, next % 8 + 8, y, x);
      return (lines);
    }
    
    int again (int hs) {
      int x, y, ch;
    
      wattrset (pwin, COLOR_PAIR (9));
      y = (HEIGHT - 11) / 2; x = (WIDTH - 6);
      wfillrect (pwin, y, x, 11, 13);
      mvwprintw (pwin, y + 1, x + 1, "High Score:");
      mvwprintw (pwin, y + 3, x + 3, " %d lines", hs);
      mvwprintw (pwin, y + 6, x + 1, "Do you want");
      mvwprintw (pwin, y + 7, x + 3, "to play");
      mvwprintw (pwin, y + 8, x + 4, "again");
      mvwprintw (pwin, y + 9, x + 4, "(Y/N)");
      wrefresh (pwin);
      do {ch = getch();}
        while ((ch != 'q') && (ch != 'Q')
        && (ch != 'n') && (ch != 'N')
        && (ch != 'y') && (ch != 'Y'));
      if ((ch == 'y') || (ch == 'Y')) return 1;
      else return 0;
    }
    
    int main () {
      int highscore;
    
      init();
      do {highscore = play ();} while (again (highscore));
      endwin();
    }
    This is only my second curses program and the first time I've used curses WINDOWS*. The learning curve was steep and probably represented the largest portion of my time. (That, and errors brought up because I want to do my stuff in X,Y and curses does everything Y,X.) My one complaint about WINDOWS*s is the admonition in the documentation NOT to overlap windows. i would love to make the background something other than plain black but I don't want to deal with painting around the already established windows. Ah well, I can live with it, I guess.

    The I knew making the pieces apparent in the code would mean the indexing would get muddled, but the shape index being between the line and column number? Come on! Ah well, that's the price for being able to add/change any piece with the minimum of work. It's so easy I decided to add one, just to make my game different. I resisted the urge to throw in every pentomino that would fit in a 4x4 square. (I count 11.) Maybe I'll do that later. Instead of tetris, pentris.

    The rx() function is the one that converts y,x in the data array to a rotated shape on the game board. Originally there was a ry() as well, but strangely enough it was superfluous because if you incremented the rotation you gave to rx() it acted exactly the same way. It was one of those check-it-three-times-cuz-you-don't-believe-it's-that-easy sort of moments.

    Once I had nailed down rotation, actually a bit before that, I thought that having to unrotate the new piece every time wouldn't be hard, but wouldn't it be cooler if the piece in the NEXT window rotated with you? I kinda like the effect.
    Type-ins are back! Visit Cymon's Games at http://www.cymonsgames.com for a new game every week!

  11. #11
    Registered User guesst's Avatar
    Join Date
    Feb 2008
    Location
    Lehi, UT
    Posts
    179
    In case you were wondering, Penttris. Click to download. The game is very interesting. You'll start out good with pieces falling into place, thinking your the bomb, and then you'll get two or three pieces in a row that you just don't have room for. Then you get on top of the mess you've got again just in time to step up a level. This is one of the only games I know where advancing feels like a booby prize. "Oh, you thought that was pretty easy, did ya?" "Well, no, I actually I found it quite..." "WELL TRY THIS OUT!" "Whu?!"

    There is technically one tetronimo because the shapes have to fit in a 4x4 grid, so the one straight piece is only 4 long. But it's not a big deal.

    Now I'm going to go to bed and in the morning I'll decide if this was as cool an idea as it seems like right now.
    Type-ins are back! Visit Cymon's Games at http://www.cymonsgames.com for a new game every week!

  12. #12
    Registered User guesst's Avatar
    Join Date
    Feb 2008
    Location
    Lehi, UT
    Posts
    179
    New version added. Got to thinking that with pentominos as a puzzle you can flip pieces over. So you can now flip a piece left to right. Consequently some pieces became duplicates and unnecessary. Game play-wise it reduces the almost-just-the-wrong-piece moments a little. Same download.
    Type-ins are back! Visit Cymon's Games at http://www.cymonsgames.com for a new game every week!

  13. #13
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    My one complaint about WINDOWS*s is the admonition in the documentation NOT to overlap windows.
    You can actually do this with another add-on library to ncurses. I believe it is called "panel", as in /usr/include/panel.h and /usr/lib/libpanel.a (-lpanel). I've never tried it, but it looks quite useful.

    I have a few comments about your code:
    • I don't know what you think about single-line comments, but the fact is that they aren't part of C89. You might want to consider not using them.
    • Your indentation on the whole is pretty good, but there are some hiccups (wfillrect() for example).
    • Whenever you see code like this
      Code:
        do {ch = getch();}
          while ((ch != 'q') && (ch != 'Q')
          && (ch != 'n') && (ch != 'N')
          && (ch != 'y') && (ch != 'Y'));
        if ((ch == 'y') || (ch == 'Y')) return 1;
      you should immediately think "ctype.h". It makes things a lot easier.
    • Code:
        switch (r) {
          case 0 : return x; break;
          case 1 : return y; break;
          case 2 : return 3 - x; break;
          case 3 : return 3 - y; break;
        }
      Those break statements are unnecessary, of course. And you might be able to implement a fancier version like this . . .
      Code:
      n = (r &#37; 2 ? y : x);
      if(r / 2) n = 3 - n;
      Just a thought.
    • Please, please never have a variable called 'l' (lowercase L). It's annoying, and stupid, and hard to read, in my opinion.

    Also, maybe it's just my ncurses implementation, but I do not see any text at all. (Again, this is a 64-bit Debian GNU/Linux lenny system.)

    Something I implemented in my tetris a while back that I always miss in other tetrises is a "shadow" underneath the main block area that shows where the block will land. It helps avoid alignment errors, if you know what I mean. And you could also blink lines before making them disappear if you felt adventurous.

    I'd post my tetris3 game, but I think it would be too embarrassing.
    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.

  14. #14
    Registered User guesst's Avatar
    Join Date
    Feb 2008
    Location
    Lehi, UT
    Posts
    179
    Thanks. Seriously every comment was dead on. Inline comments. Duh. I should know better. Ctype! Duh again. And I love your re-write of rx. I hate switch statements when they're not necessary.

    As for not seeing text, do you have color? Is it possible I accidentally didn't do color on color properly so that there was, like, gray on gray. I don't think so, but it's possible.

    As for a shadow, yeah I could, that'd be easy enough except for 2 things, colors will eventually overlap because the duller colors used for the backgrounds would be used for the shadow of some pieces, and I'm not sure I want to do any more fiddling with it. 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.

    Meh, who am I kidding? I'll start on it next week and it'll be shadowed by Tuesday.
    Type-ins are back! Visit Cymon's Games at http://www.cymonsgames.com for a new game every week!

  15. #15
    Registered User guesst's Avatar
    Join Date
    Feb 2008
    Location
    Lehi, UT
    Posts
    179
    [shaking fist at dwks] Thanks for nothing. I mean about that shadow thing. You knew I wouldn't be able to resists. *sigh* at least it wasn't too much more. I think about 5 lines of code once it was all said and done. Then I had to make them optional. Then I had to make the next window optional. THEN I had to update the information window which took a little fandangling. Then I had to update penttris. So from my computer at 4am I say a very sarcastic "thank you veeeeery much."

    Lemme post the "final" code. As usual, if you spot anything...
    Code:
    /* Blocks by Joseph Larson ver 7 April 2008*/
    #include <curses.h>
    #include <stdlib.h>
    #include <time.h>
    #include <ctype.h>
    
    #define WIDTH 10
    #define HEIGHT 20
    #define PIECES 8
    #define CLEARBLOCKS c = 0; while (!clip(piece, rot, y + c, x)) c++; c--; \
      drawpiece (pwin, piece, rot, level, y + c, x); \
      drawpiece (nwin, next, rot, (level + 1) % 8, 1, 2); \
      drawpiece (pwin, piece, rot, level % 8, y, x);
    #define DRAWBLOCKS c = 0; while (!clip(piece, rot, y + c, x)) c++; c--;\
      d = (piece % 8 == level % 8) ? (piece + 1) % 8 : piece % 8; \
      if (sop) drawpiece (pwin, piece, rot, d, y + c, x); \
      if (nop) drawpiece (nwin, next, rot, next % 8 + 8, 1, 2);\
      drawpiece (pwin, piece, rot, piece % 8 + 8, y, x);
    #define REF wrefresh (pwin); wrefresh (nwin);
    
    char blocks[4][PIECES][4] = /* [line][shape][col] */
    {{".X..",".X..",".XX.","..X.",".X..",".X..","....",".XX."},
     {".XX.",".X..",".X..",".XX.",".XX.",".X..",".XX.",".X.."},
     {".X..",".XX.",".X..",".X..","..X.",".X..",".XX.",".XX."},
     {"....","....","....","....","....",".X..","....","...."}};
    int buf[HEIGHT + 1][WIDTH + 2];
    WINDOW *pwin, *nwin, *iwin, *bwin;
    
    int wfillrect(WINDOW *w, int sy, int sx, int height, int width){
    	int x, y;
    
    	for (x = sx; x < (sx+width); x++) for (y = sy; y < (sy+height); y++)
          mvwaddch (w, y, x, ' ');
    }
    
    int rx (int r, int y, int x) {
      int n;
      n = (r % 2 ? y : x);
      if(r / 2) n = 3 - n;
      return n;
    }
    
    int clip (int p, int r, int y, int x) {
      int c, d;
    
      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)] != '.')
          if (buf[d + y][c + x - 1]) return 1;
      return 0;
    }
    
    void drawpiece (WINDOW *w, int p, int r, int col, int y, int x) {
      int c, d;
    
      wattrset (w, COLOR_PAIR(col));
      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 */
    }
    
    void init() { /* Setup Curses the way we want it. */
      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++) init_pair(c, COLOR_WHITE, c);
      x = (COLS - WIDTH * 2) / 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(12, 15, y + 8, x - 16);
      wattrset (iwin, COLOR_PAIR (8));
      box (iwin,0,0);
      wfillrect (iwin, 1, 1, 10, 13);
      mvwaddstr (iwin, 5, 3, "Use arrow");
      mvwaddstr (iwin, 6, 2, "keys to play");
      mvwaddstr (iwin, 7, 2, "UP = Rotate");
      mvwaddstr (iwin, 8, 2, "'N' Next");
      mvwaddstr (iwin, 9, 2, "'S' Shadow");
      mvwaddstr (iwin, 10, 2, "'Q' to Quit");
      bwin = newwin(LINES - 1, 10, 0, x + WIDTH * 2 + 6);
      for (y = 0; y < LINES - 3; y += 2) { /* Decorate a bit */
        c = rand () % PIECES; drawpiece (bwin, c, rand () % 4, c % 8 + 1, y, 2);
      }
      wrefresh (bwin);
    }
    
    void drawplay (int lvl) {
      int x, y;
    
      lvl %= 8;
      wattrset (pwin, COLOR_PAIR (lvl));
      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));
        mvwaddstr (pwin, y, x * 2 - 1, "  ");
      }
      wattrset (nwin, COLOR_PAIR ((lvl + 1) % 8));
      box (nwin,0,0);
      wfillrect (nwin, 1, 1, 4, 8);
      mvwaddstr (nwin, 0, 3, "NEXT");
    }
    
    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); REF; beep (); napms(75);
        }
      }
      return ret;
    }
    
    int play () {
      int piece, rot, x, y, next, level, lines, in, c, d, nop, sop;
      double delay;
      clock_t start, check;
    
      for (y = 0; y < HEIGHT + 1; y ++) for (x = 0; x < WIDTH + 2; x++)
        buf[y][x] = (y < HEIGHT  && x > 0 && x < WIDTH + 1) ? 0 : 1;
      level = 1; nop = sop = 1; lines = 0; rot = 0;
      x = WIDTH / 2; y = 0; next = rand () % PIECES;
      do {
        delay = 1.0 - level * .05; if (delay < .2) delay = 0.2;
        piece = next; next = rand () % PIECES;
        drawplay (level);
        do {
          mvwprintw (iwin, 1, 2, "Level : %d", level);
          mvwprintw (iwin, 3, 2, "Lines : %d", lines);
          wrefresh (iwin);
          CLEARBLOCKS; y++;
          DRAWBLOCKS; REF;
          start = clock ();
          do {
            in = getch();
            switch (in) {
              case KEY_RIGHT : if (!clip(piece, rot, y, x + 1)) {
                                CLEARBLOCKS; x++; DRAWBLOCKS; REF;
                              } break;
              case KEY_LEFT  : if (!clip(piece, rot, y, x - 1)) {
                                CLEARBLOCKS; x--; DRAWBLOCKS; REF;
                              } break;
              case KEY_DOWN  : CLEARBLOCKS;
                               while (!clip(piece, rot, y + 1, x)) y++;
                               DRAWBLOCKS;  REF; start = clock (); break;
              case KEY_UP    : if (!clip(piece, (rot + 1) % 4, y, x)) {
                                CLEARBLOCKS; rot++; rot %= 4; DRAWBLOCKS; REF;
                                delay = 1.0 - level * .05;
                                if (delay < .2) delay = 0.2;
                                start = clock ();
                              }  break;
              case 'n' :
              case 'N' : nop = !nop; break;
              case 's' :
              case 'S' : sop = !sop; break;
              case 'q' :
              case 'Q' : return (lines);
            }
            check = clock ();
          } while ((double)(check - start) / CLOCKS_PER_SEC < delay);
          delay = 1.0 - level * .05; if (delay < .05) delay = 0.05;
        } while (!clip (piece, rot, y + 1, x));
        for (d = 0; d < 4; d++) for (c = 0; c < 4; c++) /* commit piece to buffer */
        if (blocks[rx(rot, d, c)][piece][rx((rot + 1) % 4, d, c)] != '.')
          buf[d + y][c + x - 1] = piece + 8;
        drawplay (level);
        REF;
        lines += clearedlines(level);
        level = lines / 10 + 1;
        x = WIDTH / 2; y = 0;
      } while (!clip (next, rot, y, x));
      drawpiece (pwin, next, rot, next % 8 + 8, y, x);
      return (lines);
    }
    
    int again (int hs) {
      int x, y, ch;
    
      wattrset (pwin, COLOR_PAIR (9));
      y = (HEIGHT - 11) / 2; x = (WIDTH - 6);
      wfillrect (pwin, y, x, 11, 13);
      mvwprintw (pwin, y + 1, x + 1, "High Score:");
      mvwprintw (pwin, y + 3, x + 3, " %d lines", hs);
      mvwprintw (pwin, y + 6, x + 1, "Do you want");
      mvwprintw (pwin, y + 7, x + 3, "to play");
      mvwprintw (pwin, y + 8, x + 4, "again");
      mvwprintw (pwin, y + 9, x + 4, "(Y/N)");
      wrefresh (pwin);
      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();
    }
    I'm probably going to post a stripped down version that shaved more than 1k off the code on the site to avoid a too-long program.
    Type-ins are back! Visit Cymon's Games at http://www.cymonsgames.com for a new game every week!

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