C++: Looking for critique (Tetris in allegro)

Started by
4 comments, last by DeFessler 14 years, 10 months ago
Hello, I'm still learning and haven't spent an enormous amount of time on it, but I am determined to be successful in completing this game as professional as possible so that I may move on and continue to grow towards being a competent programmer. I was wondering if it would be okay to ask for some critique on the code I have and possibly see if anyone could tell me if I am using good practices. I have yet to use OOP because of the lack of programs I have written that use it, but I am hoping to modify my Tetris game for use with classes eventually. I will post a link to my current build as well as the code, as well as some images previewing a bit more of the direction I am heading towards. I am taking inspiration from the Nintendo DS version of Tetris and hope to improve upon some of those ideas. I don't plan to sell this or anything it's only for learning and fun.

#include <allegro.h>
#include <time.h>

unsigned char controlTicks = 0;   // Used for the player input timer.
unsigned char downTicks = 0;      // Used for the blocks scrolling down timer.


void init();                      // Initialize allegro functions first.
void controlTimer();              // Timer function (could use more work)
void downTimer();                 // Timer function (could use more work)
void gameSetup();                 // Setting up the background and start of game.
void moveBlock();                 // Moving the Blocks and collision detection.
void draw();                      // Used to draw on to the screen.
void controlBlock();              // Player manipulation of blocks as they descend.
void resetBlock();                // Reloads the Block array with the default block positions.
void removeBlock();               // Clears full row of blocks
void rotateBlock(int rotateNum);  // Rotates block
void whatsNext();				  // Used to determine the next block.
void youWin();					  // Under construction

/*---Global variables---------------------------------------------------| x is used to determine which block form in the block[][] array to use.| 
| tempX is used to store the flip value plus plus x value.              |
| nextBlock is used in determining what block is next in whatsNext();   |
| hasRun is used to determine if the what's next function has run yet   |
\----------------------------------------------------------------------*/
int x = 0, tempX = 0, flip = 0, nextBlock = 0;
bool hasRun = false;

// Global constants
const int WIDTH = 10;
const int HEIGHT = 14;

/* gameBoard[][] is used to store the location of the blocks.   \  The value '1' is a block and '0' is the absense of a block. */
int gameBoard[HEIGHT][WIDTH] = {{0,0,0,0,0,0,0,0,0,0},
								{0,0,0,0,0,0,0,0,0,0},
								{0,0,0,0,0,0,0,0,0,0},
								{0,0,0,0,0,0,0,0,0,0},
					    		{0,0,0,0,0,0,0,0,0,0},
						    	{0,0,0,0,0,0,0,0,0,0},
							    {0,0,0,0,0,0,0,0,0,0},
							    {0,0,0,0,0,0,0,0,0,0},
							    {0,0,0,0,0,0,0,0,0,0},
							    {0,0,0,0,0,0,0,0,0,0},
							    {0,0,0,0,0,0,0,0,0,0},
							    {0,0,0,0,0,0,0,0,0,0},
							    {0,0,1,0,0,0,0,0,0,0},
							    {1,1,1,1,1,1,1,1,1,0}};

/* Used to display what the next block will be. */
int tempBlock[4][4] = {{0,0,0,0},
					   {0,0,0,0},
					   {0,0,0,0},
                       {0,0,0,0}};

/* This holds the 7 original block configurations. */
int block[7][4];

/* Declare containers for images. */
BITMAP *buffer;
BITMAP *background;
BITMAP *gBlock;


int main()
{
	init();
	gameSetup();
	  
	/* Main game loop */
	while(!key[KEY_ESC])
	{	
		controlBlock();
		draw();
		youWin();
	}

	destroy_bitmap(buffer);
    destroy_bitmap(background);
	destroy_bitmap(gBlock);
	
	return 0;
}
END_OF_MAIN();

void init ()
{
	allegro_init();
    install_keyboard();
	install_timer();
	LOCK_VARIABLE(downTicks);
    LOCK_FUNCTION(downTimer);
    install_int_ex(&downTimer, SECS_TO_TIMER(1));
    LOCK_VARIABLE(controlTicks);
    LOCK_FUNCTION(controlTimer);
    install_int_ex(&controlTimer, MSEC_TO_TIMER(100));
    set_color_depth(16);
    set_gfx_mode(GFX_AUTODETECT_WINDOWED, 1024,768, 0, 0);
}

void controlTimer()
{
	controlTicks++;
}
END_OF_FUNCTION(controlTimer);

void downTimer()
{
	downTicks++;
}
END_OF_FUNCTION(downTimer);

void gameSetup()
{
	background = load_bitmap("background.bmp", NULL);
	buffer = create_bitmap(1024, 768);
	gBlock = load_bitmap("gBlock.bmp", NULL);
	srand ( (unsigned)time ( 0) ); 
    whatsNext();
	resetBlock();
	for(int r = 0; r <= 13; r++)
	{
		for(int c = 0; c <= 9; c++)
		{
			if(gameBoard[r][c] == 1)blit(gBlock, buffer, 0, 0, (50*c)+175, (50*r), 50, 50);
		}
	}
}

void moveBlock()
{
    while(downTicks > 0)
    {
	    /* Initialize variables */
        int r = 0, c = 0;

        /* Allowing block to move again. */
	    bool move = true;

	    /* Clear the original block location */
        for(int i = 0; i <= 3; i++)
        {
            r = (block[x] / WIDTH);
            c = (block[x] % WIDTH);
            if(r > 0) gameBoard[r-1][c] = 0;
        }

        /* Collision Detection (can the block move any further). */
        for(int i = 0; i <= 3; i++)
        {
            r = (block[x] / WIDTH);
            c = (block[x] % WIDTH);

            /* If the block form encounters another block, stop moving. */
		    if(gameBoard[r][c] == 1 || r >= HEIGHT)
            {
                move = false;
                break;
            }
        }

        /* If the block is unable to move. */
        if(!move)
        {
            /* Set permanent location of block. */
            for(int i = 0; i <= 3; i++)
            {
                r = (block[x] / WIDTH);
                c = (block[x] % WIDTH);
                if(r >0)gameBoard[r-1][c] = 1;
            }
    		
            removeBlock(); // check for any completed rows, remove them, move the grid down,
            resetBlock();  // and then reset the block array to default.
            flip = 0;
		    return;
        }
    	
        /* Continually move the block form currently stored in block[][]. */
        for(int i = 0; i <= 3; i++)
        {
            r = (block[x] / WIDTH);
            c = (block[x] % WIDTH);
            gameBoard[r][c] = 1;
			r ++;
		    block[x] = (r * WIDTH + c);
        }
        
        downTicks--;
    }
	
}

/* Player control of the block. */
void controlBlock()
{
    while(controlTicks > 0)
    {
	    /* Initalize and reset variables */
        int r = 0, c = 0;
        bool  kUp = false, kRight = false, kLeft = false, kDown = false;
        bool  cUp = true, cRight=true, cLeft = true, cDown=true;

	    /* Each direction also does a little bit of collision detecion   	    \  to insure that a block is not in the way before it can move. */
	    if(key[KEY_RIGHT]) kRight = true;
        if(kRight)
        {
		    /* Clear block first. */
            for(int i = 0; i <= 3; i++)
            {
			    r = (block[x] / WIDTH)-1;
                c = (block[x] % WIDTH);
			    if(r >=0)gameBoard[r][c] = 0;
            }
		    /* Check for collision. */
            for(int i = 0; i <= 3; i++)
            {
			    r = (block[x] / WIDTH)-1;
                c = (block[x] % WIDTH);
				if(gameBoard[r][c+1] == 1 || c >= WIDTH-1 || r <=0 ) cRight = false;
            }

		    /* If nothing collides move peice. */
		    if(cRight) 
		    { 
			    for(int i = 0; i <= 3; i++)
			    {  
				    block[x]++;
                    r = (block[x] / WIDTH)-1;
				    c = (block[x] % WIDTH);
                    gameBoard[r][c] = 1;
			    }
		    }
	    }

	    if(key[KEY_LEFT]) kLeft = true;
        if(kLeft)
        {
		    /* Clear block first. */
            for(int i = 0; i <= 3; i++)
            {
			    r = (block[x] / WIDTH)-1;
                c = (block[x] % WIDTH);
			    if(r >=0)gameBoard[r][c] = 0;
            }
		    /* Check for collision. */
            for(int i = 0; i <= 3; i++)
            {
			    r = (block[x] / WIDTH)-1;
                c = (block[x] % WIDTH);
                if(gameBoard[r][c-1] == 1 || c <= 0 ) cLeft = false;
            }
		    /* If nothing collides move peice. */
		    if(cLeft) 
		    { 
			    for(int i = 0; i <= 3; i++)
			    {  
				    block[x]--;
                    r = (block[x] / WIDTH)-1;
				    c = (block[x] % WIDTH);
                    if(r >= 0)gameBoard[r][c] = 1;
			    }
		    }
        }
		/* If the UP key is pressed, activate rotate. */
        if(key[KEY_UP]) kUp = true;
        if(kUp)
        {
		    /* Clear block first. */
            for(int i = 0; i <= 3; i++)
            {
			    r = (block[x] / WIDTH)-1;
                c = (block[x] % WIDTH);
			    if(r >=0)gameBoard[r][c] = 0;
            }
		    /* Check for collision. */
            for(int i = 0; i <= 3; i++)
            {
			    r = (block[x] / WIDTH)-1;
                c = (block[x] % WIDTH);
                if(gameBoard[r+1][c] == 1 || c <= 0 && i == 1 || c >= WIDTH-1 && i == 1) 
				{
					cUp = false;
				}
				if( x == 6 && c >= WIDTH-2 && i == 2 && flip < 6) cUp = false;
            }
		    /* If nothing collides move peice. */
		    if(cUp) 
		    {	
				rotateBlock(tempX);
			    for(int i = 0; i <= 3; i++)
			    {  
                    r = (block[x] / WIDTH)-1;
				    c = (block[x] % WIDTH);
                    if(r >=0)gameBoard[r][c] = 1;
			    }
		    }
        }

		/* If the down key is pressed, move down faster. */
	    if(key[KEY_DOWN]) kDown = true;
        if(kDown)
        {
		    // Clear block first.
            for(int i = 0; i <= 3; i++)
            {
			    r = (block[x] / WIDTH)-1;
                c = (block[x] % WIDTH);
			    if(r >=0)gameBoard[r][c] = 0;
            }
		    // Check for collision.
            for(int i = 0; i <= 3; i++)
            {
			    r = (block[x] / WIDTH)-1;
                c = (block[x] % WIDTH);
                if(gameBoard[r+1][c] == 1 || r >= HEIGHT-1) cDown = false;
            }
		    // If nothing collides move peice.
		    if(cDown) 
		    { 
			    for(int i = 0; i <= 3; i++)
			    {  
				    block[x] += 10;
                    r = (block[x] / WIDTH)-1;
				    c = (block[x] % WIDTH);
                    gameBoard[r][c] = 1;
			    }
		    }
        }
		if(!cRight || !cLeft || !cDown || !cUp ) 
        {
            for(int i = 0; i <= 3; i++)
            {
                r = (block[x] / WIDTH)-1;
                c = (block[x] % WIDTH);
                if(r >=0)gameBoard[r][c] = 1;
             }
        }
		
		/* If the block can not move in either direction, make sure it's drawn. */
        tempX = x + flip; //tempX is sent to rotate funtion for flip number.

        controlTicks--;
    }
     	moveBlock(); // After player input check is finished move the block   
}
// Clears row or rows when full.
void removeBlock()
{
    /* Initialize variables */
    int nRow = 0, rRow[4], i=0;
    bool shift=false;
	
    for(i = 0; i <= 4; i++)
    {
        rRow = 0;
    }

    i=0;

	/* Find all gameBoard rows that have 10 blocks and 	| save the row number to an array. Then allow      |
	\ shift condition if any row has 10 blocks.       */
    for(int r = 0; r <= HEIGHT-1; r++)                
	{
		for(int c = 0; c <= WIDTH-1; c++)
		{
            nRow += gameBoard[r][c];
            if(nRow >= 10) 
            {
                rRow = r;
                nRow = 0;
                if(i < 3) i++;
                shift = true;
            }
            
        }
		/* Reset the number in the row to 0 if it's 		\ not a complete line.                     */
        if(nRow < 10) nRow = 0;
    }

	/* If any row has ten blocks take the value you in 	\ that row and move all of the above block down.  */
    if(shift)
    {
        for(int i = 0; i <= 3; i++)
        {
			for(int r = rRow; r >0; r--)
			{
				for(int c = 0; c <= WIDTH-1; c++)
				{
					if(r > 0)gameBoard[r][c] = gameBoard[r-1][c];
				}
			}
        } 
    }
	
}

/* Resets block shape positions for use again and picks a new block. */
void resetBlock()
{
	//if(!hasRun) x = rand()%7;
	//else 
		x = nextBlock;
    
                /*  {4, 5,  6,  15},     // T-Block
                    {4, 5,  13, 14},     // S-Block
                    {4, 14, 24, 25},     // L-Block
                    {4, 5,  14, 15},     // Sq-Block
                    {4, 14, 24, 34}};*/  // Li-Block

    // T-Block
    block[0][0] = 13;
    block[0][1] = 14;
    block[0][2] = 15;
    block[0][3] = 4;
    // S-Block
    block[1][0] = 5;	
    block[1][1] = 4;
    block[1][2] = 14;
    block[1][3] = 13;
	// Z-Block
    block[2][0] = 3;	
    block[2][1] = 4;
    block[2][2] = 14;
    block[2][3] = 15;
    // L-Block
    block[3][0] = 4;
    block[3][1] = 14;
    block[3][2] = 24;
    block[3][3] = 25;
    // 7-Block
    block[4][0] = 4;
    block[4][1] = 14;
    block[4][2] = 24;
    block[4][3] = 23;
    // Sq-Block
    block[5][0] = 4;
    block[5][1] = 5;
    block[5][2] = 14;
    block[5][3] = 15;
    // Li-Block
    block[6][0] = 4;
    block[6][1] = 14;
    block[6][2] = 24;
    block[6][3] = 34;
    whatsNext();
}
void rotateBlock( int rotateNum)
{
		int r = 0, c = 0;

	    // Clear block first.
        for(int i = 0; i <= 3; i++)
        {
			r = (block[x] / WIDTH)-1;
            c = (block[x] % WIDTH);
			if(r >=0)gameBoard[r][c] = 0;
        }
	switch(rotateNum)
	{
	case 0:
		// T-Block (First Right)
        block[0][0] -= 9;
        block[0][1] += 0;
        block[0][2] += 9;
        block[0][3] += 11;
		flip += 7;
		break;
	case 1:
	    // S-Block{5, 4,  14, 13}, 
		block[1][0] -= 11;	
		block[1][1] += 0;
		block[1][2] -= 9;
		block[1][3] += 2;
		flip += 9; 
		break;
	case 2:
	    // Z-Block{3, 4,  14, 15}, 
		block[2][0] -= 9;	
		block[2][1] -= 0;
		block[2][2] -= 1;
		block[2][3] -= 12;
		flip += 9; 
		break;
	case 3:
		// L-Block {4, 14, 24, 25},
		block[3][0] += 11;
		block[3][1] -= 0;
		block[3][2] -= 11;
		block[3][3] -= 2;
		flip += 9;
		break;
	case 4:
		// 7-Block
		block[4][0] += 11;
		block[4][1] += 0;
		block[4][2] -= 11;
		block[4][3] -= 20;
        flip += 11;
        break;
	case 5:
	    // Sq-Block
		flip = 0; 
		break;
	case 6:
	    // Li-Block (Horizonal)
            block[6][0] += 9;
            block[6][1] -= 0;
            block[6][2] -= 9;
            block[6][3] -= 18;
            flip += 12;
	    break;
    case 7:
	    // T-Block (Second Right)
            block[0][0] += 11;
            block[0][1] += 0;
            block[0][2] -= 11;
            block[0][3] += 9;
	    flip += 1;
		break;
	case 8:
	    // T-Block
        block[0][0] += 9;
        block[0][1] += 0;
        block[0][2] -= 9;
        block[0][3] -= 11;
		flip += 1;
		break;
	case 9:
	    // T-Block
        block[0][0] -= 11;
        block[0][1] += 0;
        block[0][2] += 11;
        block[0][3] -= 9;
		flip -= 9;
		break;
	case 10:
	    // S-Block
		block[1][0] += 11;	
		block[1][1] += 0;
		block[1][2] += 9;
		block[1][3] -= 2;
		flip -= 9; 
		break;
	case 11:
	    // Z-Block{3, 4,  14, 15}, 
		block[2][0] += 9;	
		block[2][1] += 0;
		block[2][2] += 1;
		block[2][3] += 12;
		flip -= 9; 
		break;
	case 12:
		// L-Block {4, 14, 24, 25},
		block[3][0] += 9;
		block[3][1] -= 0;
		block[3][2] -= 9;
		block[3][3] -= 20;
		flip += 1;
		break;
	case 13:
		// L-Block {4, 14, 24, 25},
		block[3][0] -= 11;
		block[3][1] -= 0;
		block[3][2] += 11;
		block[3][3] += 2;
		flip += 1;
		break;
	case 14:
		// L-Block {4, 14, 24, 25},
		block[3][0] -= 9;
		block[3][1] -= 0;
		block[3][2] += 9;
		block[3][3] += 20;
		flip -= 11;
		break;
    case 15:
		// 7-Block
		block[4][0] += 9;
		block[4][1] += 0;
		block[4][2] -= 9;
		block[4][3] += 2;
        flip += 1;
        break;
    case 16:
		// 7-Block
		block[4][0] -= 9;
		block[4][1] += 0;
		block[4][2] += 9;
		block[4][3] += 20;
        flip += 1;
        break;
    case 17:
		// 7-Block
		block[4][0] -= 11;
		block[4][1] += 0;
		block[4][2] += 11;
		block[4][3] -= 2;
        flip -= 13;
        break;
	case 18:
	    // Li-Block (Vertical)
        block[6][0] -= 9;
        block[6][1] += 0;
        block[6][2] += 9;
        block[6][3] += 18;
		flip -= 12;
		break;
	}
}

void whatsNext()
{
    int r=0, c=0;
    bool hasRun = true;
	for(r = 0; r <= 3; r++)
    {
        for(c = 0; c <= 3; c++)
        {
           tempBlock[r][c] = 0;
        }
    }

	nextBlock = rand()%7;

    for(int i = 0; i <= 3; i++)
    {
        r = (block[nextBlock] / WIDTH)+1;
        c = (block[nextBlock] % WIDTH)-6;
        tempBlock[r][c] = 1;
    }
}

void youWin()
{
	int value = 0;
	bool val = false;

   for(int c = 0; c <= WIDTH-1; c++)
	{
		for(int r = 0; r <= HEIGHT-1; r++)
		{
			if(gameBoard[r][c] == 1)value++;
			if(value >= HEIGHT) val = true;
        }
		
		value = 0;
	}
   		if(val)
		{
			for(int r = 0; r <= HEIGHT-1; r++)
			{
				for(int c = 0; c <= WIDTH-1; c++)
				{
					gameBoard[r][c] = 0;
				}
				textout_ex(buffer, font, "You Lose!", 10, 10, makecol(0, 0, 255), -1);
				
			}
			rest(5000);
		}
}
void draw()
{
    blit(background, buffer, 0, 0, 0, 0, 1024, 768);
    for(int r = 0; r <= HEIGHT-1; r++)
	{
		for(int c = 0; c <= WIDTH-1; c++)
		{
		    if(gameBoard[r][c] == 1) blit(gBlock, buffer, 0, 0, (50*c)+175, r*50, 50, 50);
        }
	}
    for(int r = 0; r <= 3; r++)
    {
        for(int c = 0; c <= 3; c++)
        {
            if(tempBlock[r][c] == 1) blit(gBlock, buffer, 0, 0, (50*c)+700, (r*50)+225, 50, 50);
        }
    }
    blit(buffer, screen, 0, 0, 0, 0, 1024, 768);
}




Images:

Download: http://defessler.jeremycrapsey.com/ontheblocks/ontheblocks.rar
Advertisement
Hey I just played your game, awesome man! :) I would be happy if I could make that happen..

anyway, it was a little bit unclear how to rotate the shapes, i got lucky and hit the right button to flip it a few times but am not sure what I hit.

I think there might be a small glitch, when the game should have been over (when my blocks stacked up to the top) the next block went haywire and was switching from shape to shape very fast.

gj anyway


edit / addition: Just wondering if you have any experience with the SDL library and what you think about each one. which is easier to get started with etc..
I've never tried SDL. Allegro was the first thing I found to program will easily with C++. Also, the UP key is what flips the blocks. There's a bug with the flip on the first block and the lose condition has not yet but fully finished.
Excellent job my friend. A well done start for a Tetris clone if I've ever seen one. I did find that flipping and moving the shapes is a bit unpredictable when they are first emerging from the top. Other than that it seems like its mostly the art and animation that you should be focusing on. Maybe even add in an original element or two to make your tetris stand out from others. Good job!
There are definitely a few bugs to work out. Also a lot of new features I want to add. This being my first real attempt at finishing a game it's taking a while. As you see in he images, the graphics will be improved.
Would it be possible for someone to give me some feed back on how I am approachig problems?

This topic is closed to new replies.

Advertisement