Dostris (a complete game in 3 hours)

This is a discussion on Dostris (a complete game in 3 hours) within the Game Programming forums, part of the General Programming Boards category; Basically its a tetris clone I wrote for the dos console. There are a few noticeable changes from the original ...

  1. #1
    mov.w #$1337,D0 Jeremy G's Avatar
    Join Date
    Nov 2001
    Posts
    704

    Dostris (a complete game in 3 hours)

    Basically its a tetris clone I wrote for the dos console. There are a few noticeable changes from the original for the purpose of simplification.

    I coded 2 hours between classes at my college, and finnished it up in 1 hour at home for a total of 3.

    Simplifications:
    -Playing area rotated sideways to fit into the console.
    -Playing Pieces are now 3x3 to accomidate for simple rotating
    -Only 5 types of pieces - I havent made the L shapes yet.
    -Rotate only goes one direction - Clockwise.
    -You can only move the block up and down for positioning. You cant speed up the falling rate voluntarily.

    The code concept is pretty simple - but it gets a bit messy accomidating for colored text's and basic drawing problems.

    The collision function needs a tiny fix - the long shape (1 row of 3 blocks) cant be moved to the top or bottom row when rotated long ways - experimenting will let you see what I mean. I dont think it will be too hard to fix it.


    Controls: When you open the exe up you should see the playing field and the preview block - and one active block - press enter to start.

    e moves the active block piece up one space
    d moves the active block piece down one space
    r rotates the block 90 clock wise
    esc stops execution of the program.

    Feel free to post comments about the emulation itself or the coding behind it. Progmatic suggestions and questions are welcomed.

    I'll post the source in a new thread to break up my post.

    source and executable are included in the attached zip.
    This has only been run in WinXP in the winXP console window. I have no idea as to if it fits in win98 - give it a try.
    Attached Files Attached Files
    c++->visualc++->directx->opengl->c++;
    (it should be realized my posts are all in a light hearted manner. And should not be taken offense to.)

  2. #2
    mov.w #$1337,D0 Jeremy G's Avatar
    Join Date
    Nov 2001
    Posts
    704
    Heres the source:

    Code:
    #include <iostream.h>
    #include <stdio.h>
    #include <windows.h>
    #include <conio.h>
    
    // bad practice defines
    #define ROWS 15
    #define COLS 45
    
    // game piece object
    struct TPiece{
    	int block[3][3];
    	int color;
    };
    
    struct boardSpace {
    	int value;
    	int color;
    };
    
    // board management class
    class CGame
    {
    public:
    	CGame();
    	~CGame();
    	
    	void spawn(); // spawn current preview piece - and replace a new piece
    	void fall(); // moves block sideways 1 column in specified time
    	void move(int y, int x); // voluntary movement and rotations 
    	void draw(); // draw the board - and active piece.
    	bool collision(int y, int x); // check for being in place
    	bool removeCol(); // check for completed columns and remove them
    	void fix(); // pushes down columns into new spaces created from completed columns
    	void set(); // sets active piece in place - calls spawn
    	bool checkLose(); // you suck..
    	TPiece * rotate(TPiece * piece);
    	TPiece * getActive() { return this->active; }
    	void setActive(TPiece * piece) { this->active = piece; }
    
    private:
    	TPiece *active; // currently active game piece
    	TPiece *preview; // preview of coming game piece
    	int aX, aY; // location of the activePiece
    	int X, Y; // piece location according to the board
    	boardSpace board[ROWS][COLS];
    	long lastTime; // last time block was shoved
    	int speed; // how fast does block move
    
    public:
    	bool paused; // game pauses
    	bool altered; // does screen need to be redrawn?
    	bool bPlaced; // is active place going to collide next move? If so -next move set its position but dont move it
    };
    
    
    TPiece rotatePiece();
    TPiece * createPiece( int ncolor );
    
    CGame *game;
    
    char * quitMessage; // win/lose message
    int score; // game score
    
    long lastkey;
    
    void createBox( TPiece * piece );
    void createLong( TPiece * piece );
    void createZig( TPiece * piece );
    void createZag( TPiece * piece );
    void createTee( TPiece * piece );
    
    void drawPiece(TPiece * piece, int x, int y);
    void gotoxy(int x, int y);
    
    void drawBoard();
    
    
    int main() {
    //	printf("This is a test.\n");
    	game = new CGame();
    	game->spawn();
    	game->draw();
    	getch();
    
    	game->paused = false;
    	
    	// game loop
    	char Input=0;
    	char lastIn = 0;
    	while(Input!=27) //Loop while ESC is not pressed.
    	{
    		Input=0;
    		if(kbhit()) Input=getch(); //Store the pressed key (if a key is pressed)
    		if(Input == 27 ) {
    			quitMessage = "I can't believe your quiting at this pivotal moment!! - Your score was:";
    			break;
    		}
    		if( Input ) lastIn = Input;
    		long currentTime=timeGetTime();
    		if(currentTime - lastkey > 50)
    		{
    			gotoxy(0, 23);
    			//printf("you pressed %i\n", (int)Input);
    			if(lastIn == 'e') 
    				game->move(-1,0);
    			else if(lastIn == 'd')
    				game->move(1,0);
    			if(lastIn== 'r')
    				game->setActive( game->rotate(game->getActive()) );
    		   
    			lastIn = 0;
    			
    			lastkey = currentTime; //reset start time variable
    
    		}
    		//drawBoard();
    		game->fall();
    
    	
    		if(game->altered) 
    			game->draw();
    
    		// if youve let things pile too much you lose
    		if(game->checkLose()) {
    			Input=27; // same as esc:
    			quitMessage = "Game over - you lost. Your score was:";
    		}
    		gotoxy(0, 19); printf("Your score is: %i", score);
    	}
    	gotoxy(0, 20);
    	printf("%s %i\n Thanks for playing my dos tetris clone!",quitMessage, score);
    
    return 0;
    }
    
    
    
    TPiece rotatePiece( TPiece * piece) {
    	TPiece result;
    
    	// rotate corners
    	result.block[0][0] = piece->block[2][0];
    	result.block[0][2] = piece->block[0][0];
    	result.block[2][2] = piece->block[0][2];
    	result.block[2][0] = piece->block[2][2];
    
    	// rotate middles
    	result.block[0][1] = piece->block[1][0];
    	result.block[1][2] = piece->block[0][1];
    	result.block[2][1] = piece->block[1][2];
    	result.block[1][0] = piece->block[2][1];
    
    	// center is the center
    	result.block[1][1] = piece->block[1][1];
    
    	// color
    	result.color = piece->color;
    
       return result;
    }
    
    TPiece * createPiece( int ncolor ) {
    	TPiece *result = new TPiece;
    	for(int i = 0; i < 3; i ++ )
    		for( int j = 0; j < 3; j ++)
    			result->block[i][j] = 0;
    	result->color = ncolor;
    	return result;
    }
    void createBox( TPiece * piece ) {
    	for(int i = 0; i < 3; i++ )
    		for( int j = 0; j < 3; j++ )
    			piece->block[i][j] = 1;
    }
    void createLong( TPiece * piece ) {
    	for( int j = 0; j < 3; j++ )
    		piece->block[1][j] = 1;
    }
    void createZig( TPiece * piece ) {
    	piece->block[0][0] = 1; // _
    	piece->block[0][1] = 1;
    	piece->block[1][1] = 1; // |
    	piece->block[2][1] = 1; // -
    	piece->block[2][2] = 1;
    }
    void createZag( TPiece * piece ) {
    	piece->block[0][1] = 1; // _
    	piece->block[0][2] = 1;
    	piece->block[1][1] = 1; // |
    	piece->block[2][0] = 1; // -
    	piece->block[2][1] = 1;
    }
    void createTee( TPiece * piece ) {
    	piece->block[0][1] = 1;
    	piece->block[1][1] = 1;
    	piece->block[1][2] = 1;
    	piece->block[2][1] = 1;
    }
    
    void drawPiece( TPiece * piece, int y, int x) {
    	HANDLE hStdout;
    	hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
    	gotoxy( x, y );
    	for( int i = 0; i < 3; i++ ) {
    		for( int j = 0; j < 3; j++ ) {
    			if( piece->block[i][j] ) {
    				gotoxy( x + j, y+i );
    				SetConsoleTextAttribute(hStdout, piece->color); // blue bg, white text
    				printf("%c", char(219));
    				SetConsoleTextAttribute(hStdout, 121); // reset to grey.
    			} else {
    				//printf(" ");
    			}
    		}
    	}
    }
    
    void gotoxy(int x, int y) {
    	COORD pos;
    	pos.X = x;
    	pos.Y = y;
    	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),pos);
    }
    
    void drawBoard() {
    //     201 205 187
    // 205 188 219 186
    //	   219 219 186
    // 205 187 219 186
    //     200 205 188
    	gotoxy(0,0);
    	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 121);
    	// preview area
    	gotoxy(0,3+2);
    	printf("%c", char(201));
    	for( int i = 0; i < 7; i++ )
    		printf("%c", char(205));
    		printf("%c", char(187));
    	gotoxy(8, 6); printf("%c", char(200)); printf("%c", char(188));
    	gotoxy(0, 6); printf("%c", char(186));	
    	gotoxy(0, 7); printf("%c", char(186));
    	gotoxy(0, 8); printf("%c", char(186));
    	gotoxy(0, 9); printf("%c", char(186));
    	gotoxy(0, 10); printf("%c", char(186));
    	gotoxy(8, 10); printf("%c", char(201)); printf("%c", char(187));
    	gotoxy(0, 11); printf("%c", char(200));
    	for( i = 0; i < 7; i++ )
    		printf("%c", char(205));
    		printf("%c", char(188));
    
    	gotoxy(9, 5); printf("%c", char(186));
    	gotoxy(9, 4); printf("%c", char(186));
    	gotoxy(9, 3); printf("%c", char(186));
    	gotoxy(9, 2); printf("%c", char(186));
    	gotoxy(9, 1); printf("%c", char(186));
    	gotoxy(9, 0); printf("%c", char(201));
    
    	gotoxy(9, 11); printf("%c", char(186));
    	gotoxy(9, 12); printf("%c", char(186));
    	gotoxy(9, 13); printf("%c", char(186));
    	gotoxy(9, 14); printf("%c", char(186));
    	gotoxy(9, 15); printf("%c", char(186));
    	gotoxy(9, 16); printf("%c", char(200));
    
    	gotoxy(10,0);
    	// playing area
    	for( i = 0; i < 49; i++ ) 
    		printf("%c", char(205));
    		printf("%c", char(187));
    
    	for( i = 0; i < 15; i++ ) {
    		gotoxy( 59, i+1 );
    		printf("%c", char(186));
    	}
    
    	gotoxy(10,16);
    	for( i = 0; i < 49; i++ ) 
    		printf("%c", char(205));
    		printf("%c", char(188));
    
    	// filling
    	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 121);
    	gotoxy(10, 1);
    	for( i = 0; i < 15; i++ ) {
    		for( int j = 0; j < 49; j++ ) {
    			gotoxy(10+j, i+1);
    			printf(" ");
    		}
    	}
    	gotoxy(1, 7);
    	for( i = 0; i < 5; i++ ) {
    		for( int j = 0; j < 7; j++ ) {
    			gotoxy(1+j, i+6);
    			printf(" ");
    		}
    	}
    	for( i = 0; i < 3; i++ ) {
    		for( int j = 0; j < 2; j++ ) {
    			gotoxy(8+j, i+7);
    			printf(" ");
    		}
    	}
    
    }
    
    
    CGame::CGame() {
    	this->aX = 0;
    	this->aY = 0;
    	this->X = 0;
    	this->Y = 6;
    
    	for( int i = 0; i < ROWS; i++ )
    		for( int j = 0; j < COLS; j++ ) {
    			this->board[i][j].value = 0; // empty board
    			this->board[i][j].color = 0; // doesnt really matter
    		}
    
    	this->paused = true;
    	this->altered = true;
    	this->active = NULL;
    	this->bPlaced = false;
    	this->speed = 150;
    	int random = rand()%5;
    	this->preview = createPiece( random );
    
    	switch( random ) 
    	{
    		case 1: // box
    			createBox(this->preview);
    			break;
    		case 2: // long
    			createLong(this->preview);
    			break;
    		case 3: // zig
    			createZig(this->preview);
    			break;
    		case 4: // zag
    			createZag(this->preview);
    			break;
    		case 5: // tee
    			createTee(this->preview);
    		default: // none
    			createBox(this->preview);
    		break;
    	}
    };
    
    CGame::~CGame() {
    
    };
    
    void CGame::draw() {
    	drawBoard();
     
    	for( int i = 0; i < ROWS; i++ ) {
    		for( int j = 0; j < COLS; j++ ) {
    			if( this->board[i][j].value ) {
    				gotoxy(14+j, i+1);
    				SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), this->board[i][j].color);
    				printf("%c", char(219));
    				SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 121);
    			}
    		}
    	}
    	// preview piece
    	drawPiece( this->preview, 7, 3 );
    	// active piece
    	drawPiece( this->active, aY, aX);
    /*
    	for( i = 0; i < ROWS; i++ ) {
    		for( int j = 0; j < COLS; j++ ) {
    			gotoxy(14+j, i+1);
    			printf("%i", this->board[i][j].value);
    		}
    	}
    /*/
    	this->altered = false;
    };
    
    void CGame::fall() {
    	long currentTime=timeGetTime();
    	if(currentTime - this->lastTime > this->speed)
    	{
    		score++; // 1 point every push
    		this->altered = true;
    		if( bPlaced ) {
    			set();
    			this->bPlaced = false;
    		} else {
    			if( collision( this->Y, this->X+1 ) ) {
    				this->bPlaced = true;
    			} else {
    				this->aX += 1;
    				this->X += 1;
    			}
    		   //do stuff
    		}
    	   this->lastTime = currentTime; //reset start time variable
    	}
    	if(removeCol())
    		fix();
    };
    
    void CGame::fix() {
    //	gotoxy(0, 22);
    //	printf("Entered fixed");
    	this->altered = true;
    	for( int i = 0; i < ROWS; i++ ) {
    		for( int j = COLS-1; j > 0; j-- ) {
    			if( !this->board[i][j].value && this->board[i][j-1].value ) {
    				this->board[i][j].value = this->board[i][j-1].value;
    				this->board[i][j-1].value = 0;
    			}
    		}
    	}
    //	gotoxy(0, 23);
    //	printf("Exited fixed");
    };
    
    void CGame::move(int y, int x) { // protects from top / bottome boundry
    	this->altered = true;
    
    	if( ((this->Y + y >= 0 && (this->Y+1) + y < ROWS-1) && this->Y != 0 ) && !collision(this->Y+y, this->X+x) ) {
    		this->Y +=y;
    		this->aY +=y;
    	}
    	if( ((this->X + x >= 0 && (this->X+2) + x < COLS) && this->X != 0 ) && !collision(this->Y+y, this->X+x) ) {
    		this->X +=x;
    		this->aX +=x;
    	}
    
    };
    
    void CGame::spawn() {
    	this->altered = true;
    	this->active = this->preview;
    	this->aX = 14;
    	this->aY = 7;
    	this->X = 0;
    	this->Y = 6;
    	int random = rand()%5;
    	this->preview = createPiece(random); 
    	
    	switch( random ) 
    	{
    		case 1: // box
    			createBox(this->preview);
    			break;
    		case 2: // long
    			createLong(this->preview);
    			break;
    		case 3: // zig
    			createZig(this->preview);
    			break;
    		case 4: // zag
    			createZag(this->preview);
    			break;
    		case 5: // tee
    			createTee(this->preview);
    			break;
    		default: // none
    			createBox(this->preview);
    		break;
    	}
    //	gotoxy(0, ROWS+5);
    //	printf("type: %i", random);
    //	drawPiece(this->preview, 0, ROWS+6);
    	bPlaced = false;
    };
    
    void CGame::set() {
    	this->altered = true;
    	// no error checking for proper placing - should be done in collision()
    	for( int i = 0; i < 3; i++ ) {
    		for( int j = 0; j < 3; j++ ) {
    			if( this->active->block[i][j] ) {
    				this->board[i+Y][j+X].value = 1;
    				this->board[i+Y][j+X].color = this->active->color;
    			}
    		}
    	}
    	spawn();
    }
    
    bool CGame::collision(int y, int x) { // true for hitting right boundry - or nestling into place
    	for( int i = 0; i < 3; i++ ) {
    		for( int j = 0; j < 3; j++ ) {
    			if( this->active->block[i][j] == 1 && this->board[y+i][x+j].value == 1 )
    				return true;
    			if( this->active->block[i][j] == 1 && this->X+j == COLS-1 ) 
    				return true;
    		}
    	}
    	return false;
    };
    
    bool CGame::removeCol() {
    	bool fixme = false;
    	bool cleared = false;
    	for(int j = 0; j < COLS; j++ ) {
    		cleared = true;
    		for( int i = 0; i < ROWS; i++ ) {
    			if( !this->board[i][j].value )
    				cleared = false;
    		}
    		if( cleared ) {
    			for( i = 0; i < ROWS; i++ )
    				this->board[i][j].value = 0; // clear out column
    			fixme = true;
    		}
    	}
    	if( fixme ) return true;
    	else return false;
    };
    
    bool CGame::checkLose() {
    	for(int i = 0; i < ROWS; i++ )
    		if(this->board[i][0].value)
    			return true;
    
    	return false;
    };
    
    TPiece * CGame::rotate(TPiece * piece) {
    	TPiece *result = createPiece(0);
    
    	// rotate corners
    	result->block[0][0] = piece->block[2][0];
    	result->block[0][2] = piece->block[0][0];
    	result->block[2][2] = piece->block[0][2];
    	result->block[2][0] = piece->block[2][2];
    
    	// rotate middles
    	result->block[0][1] = piece->block[1][0];
    	result->block[1][2] = piece->block[0][1];
    	result->block[2][1] = piece->block[1][2];
    	result->block[1][0] = piece->block[2][1];
    
    	// center is the center
    	result->block[1][1] = piece->block[1][1];
    
    	// color
    	result->color = piece->color;
    
       return result;
    };
    c++->visualc++->directx->opengl->c++;
    (it should be realized my posts are all in a light hearted manner. And should not be taken offense to.)

  3. #3
    carry on JaWiB's Avatar
    Join Date
    Feb 2003
    Location
    Seattle, WA
    Posts
    1,972
    Heh...You can post this in the sticky at the top of this board...and please attach the source next time...
    "Think not but that I know these things; or think
    I know them not: not therefore am I short
    Of knowing what I ought."
    -John Milton, Paradise Regained (1671)

    "Work hard and it might happen."
    -XSquared

  4. #4
    mov.w #$1337,D0 Jeremy G's Avatar
    Join Date
    Nov 2001
    Posts
    704
    I did attach the source in the first post - I just posted the source so those who didnt feel like downloading a zip could look and comment risk free.
    c++->visualc++->directx->opengl->c++;
    (it should be realized my posts are all in a light hearted manner. And should not be taken offense to.)

  5. #5
    Crazy Fool Perspective's Avatar
    Join Date
    Jan 2003
    Location
    Canada
    Posts
    2,640
    interesting. I think trying to find a way to double buffer console apps might be worth while. Nice job for such a short time.

  6. #6
    Pursuing knowledge confuted's Avatar
    Join Date
    Jun 2002
    Posts
    1,916
    kinda buggy, but good job for 3 hours
    Away.

  7. #7
    mov.w #$1337,D0 Jeremy G's Avatar
    Join Date
    Nov 2001
    Posts
    704
    Actually double buffering or "Page flipping" can be done. There is away to create an array of console type information (bg color, fore color, and text) and blast it into the console at a single time with a single time.

    Unfortunatly that would make me rewrite almost all the drawing code to format an array rather than printing to console using gotoxy. A converstion that would probably itself take another 3 hours.

    Actually I'm considering porting this code into OpenGL using 3d box's as the new graphic medium - and writing a tutorial over the whole process of idea to console to openGL called "concept to creation" about how to take an idea - simplify it - code into the console and expand upon it by porting to a more graphical medium.
    c++->visualc++->directx->opengl->c++;
    (it should be realized my posts are all in a light hearted manner. And should not be taken offense to.)

  8. #8
    Crazy Fool Perspective's Avatar
    Join Date
    Jan 2003
    Location
    Canada
    Posts
    2,640
    rewrite almost all the drawing code to format an array rather than printing to console using gotoxy
    you could use a find and replave to replace all gotoxy() calls with a new funtion that manipulated the array. just a though.

    writing a tutorial over the whole process of idea to console to openGL called "concept to creation" about how to take an idea - simplify it - code into the console and expand upon it by porting to a more graphical medium
    that sounds like an excellent idea. If you do you should submit it to NeHe. He is posting and looking for more articles about programming which is not neccessarily 100% OpenGL

  9. #9
    Registered User LogicError's Avatar
    Join Date
    Aug 2003
    Location
    г. Магнитогорск
    Posts
    76
    One more thing.. The game is ok but the flickering really bugged me. I did this in PASCAL but not sure on how to do this in C++ but you need some ASM code. What I usually do is make 2 functions, HideCursor() and ShowCursor() and put the asm code inside.. The rest was ok, real good for 3 hours good job

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Please comment on my c++ game
    By MegaManZZ in forum Game Programming
    Replies: 10
    Last Post: 01-22-2008, 10:03 AM
  2. New Project, text game, design stage.
    By Shamino in forum Game Programming
    Replies: 9
    Last Post: 05-23-2007, 06:39 AM
  3. game engine advice?
    By stien in forum Game Programming
    Replies: 0
    Last Post: 01-23-2007, 02:46 PM
  4. Engine <=> DX/OGL | c++ ?
    By darkcloud in forum Game Programming
    Replies: 6
    Last Post: 05-13-2005, 12:19 AM
  5. My Maze Game --- A Few Questions
    By TechWins in forum Game Programming
    Replies: 18
    Last Post: 04-24-2002, 11:00 PM

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21