Help on Designing an OOP Tetris-clone

Started by
20 comments, last by Zahlman 17 years ago
Hi, after the result of my last topic, I decided to continue my long time design of Tetris-clone, so these are the stuff i have designed till now: //-Designed till now:------------------------------------------------------------------------\ Having a: Class for the GameBoard. Class for each piece of Tetris (will make a three level hierarchical inheritance for this, not now probably) Class for MainGame (which will have an instance of each of other stuff, and i think will have control the whole game) Class for Score; managing high scores, displaying it and so on Class for displaying the GameBoard; i hope to make this part as much as independent from the game logic so the porting as easy as possible (on Nintendo DS), and i think in each loop of main game loop, it will have to update itself. All of SDL I hope to be in here and nowhere else so whenever I wanted to port my game on DS, i will just worry and change just this class and others should work as good as before. I think the main loop of the game will be something like this: While(!myGame.IsGameOVer()) { PutATris(); //randomly, will add A.I. here later While the piece can go down //collision detection or something like that will be here { CurrentPiece.MoveDownOnce(); } CheckForScores an interval here, a delay, but it shouldt halt the program or else we will miss the user's pressed key. rendering the scene } //-My concerns on design:------------------------------------------------------------------------\(I need to think about it when designing other parts) 1- As mentioned earlier, I want to design the pieces as best as possible so in later phases I will let the user have the ability of designing His/her own shape of Tetris-pieces no matter how its shape is. It’s because of that I'm making a three level of hierarchical inheritance (one for basic piece which is a 1x1, one inherits from it and another level which are my final shapes) 2- Want to add TCP/IP multiplayer and having something like friends list, so when ur friend opens his game, u can compete and so on (I know it will need a server probably), OR just playing head-to-head. 3- Separating the game logic and graphics (representations) as much as possible to increase portability over DS and other consoles. (Basically it’s a good practice for me to be able to port my bigger projects, easier) 4- Adding A.I. for later, its very important to me. //-Problems------------------------------------------------------------------------\ 1- When I wanted to design the movement of a piece in GameBoard, I faced two possibilities: If I use OOP I’ll have to pass more stuff and compute more (at least with my current knowledge) and basically it should have more reusability but if I do it old-fashioned with lots of IFs and computational stuff like checking like (if (CurrentPiece+2 == '0') then "u can move"), it will be faster and more dirty, which is better? 2- Since I want to my design of pieces be as reusable as possible, I was after some kind of algorithm so whenever i move or rotate anything, I wouldn’t worry about its results since I’m sure the smaller parts of it were well-written. but i cant find the best way since each of these current and default pieces have exceptions and i need to put some IFs for each of them and that will reduce the reusability that I’m after. So I'm after some kind of rotating/moving a singe 1x1 piece, so when I build bigger stuff from it, i will sure that rotation/movement of it is done before. 3- the game should respond real time to any pressed key appropriately, so how should i prepare for a key at anytime? in dos mode i know how to do it, but in windows mode which is event-based, how should i assign my desired keyboard handler to the event of a pressed key? (In C# we have event for that, but I donno his elder brother in C++) //-Need advice:------------------------------------------------------------------------\ 1- Do you suggest having an Array of like int[20][10] for my GameBoard OR treating it like array of Empty Objects? each one has its Pros and cons, having an integer array makes its managing much more easier, but having the object based makes its design more b-e-a-u-tiful but will need more processing power at runtime.
Advertisement
Different pieces have no use of inheritance in the same way you do not use inheritance when you add elements to std::list.

Given the nature of problem, there's little reason have any kind of virtual classes - a piece is a piece, just like Bitmap class can contain any bitmap - you do not derive classes if you want to load a picture of a dog instead of a cat.


Also keep in mind, that adding excessive virtuality to piece design doesn't really solve much. What if someone wants hexagonal or triagonal pieces (such Tetris clones exist). Or something more extravagant.

And when you settle for rectangular pieces, each one is nothing more but a small bitmap.
Quote:Original post by Antheus
Different pieces have no use of inheritance in the same way you do not use inheritance when you add elements to std::list.


no use of inheritance?! so each piece will have its own move method and its own rotation? i agree that making a universal rotation might be hard, but I think we can have movement resused. like as said, having a well written move for 1x1 piece with all of its checkings and when we want to move a 2x2, (as in C#) you just say For Each myP in CurrentPiece {myP.MoveRight()}
is it possible or not? or is it good design or not?


Quote:Original post by Antheus
Given the nature of problem, there's little reason have any kind of virtual classes - a piece is a piece, just like Bitmap class can contain any bitmap - you do not derive classes if you want to load a picture of a dog instead of a cat.

my last concern is graphic for this game, my discussion on virtual classes are not for displaying an image, plz dont get insulted but please read my post again, maybe i didnt understand your meaning, or i didnt say mine clearly.

Quote:Original post by Antheus
Also keep in mind, that adding excessive virtuality to piece design doesn't really solve much. What if someone wants hexagonal or triagonal pieces (such Tetris clones exist). Or something more extravagant.

i hvae no idea about it, i have still difficulties with detection of where solid stuff is or not, i have no idea about triangle pieces, plz help or guide or give clue atleast.

Quote:Original post by Antheus
And when you settle for rectangular pieces, each one is nothing more but a small bitmap.

yes, but in OOP i can add X and Y for them, if not, how do u want to store the location of your current piece? do u store it permenantly in your array? even when still its moving?





plz answer to my other questions, there are still more important ones there, like movements and design on small basic tile issues.

thnx in advance to all.
Quote:Original post by test86
no use of inheritance?! so each piece will have its own move method and its own rotation? i agree that making a universal rotation might be hard, but I think we can have movement resused.


He means, have a single Piece class, with a single rotation function, a single movement function, and a single position system.

The difference between tetris pieces is shape, not behaviour. This is something that is handled through data, not through inheritance. As mentioned before, a 4x4 bitmap of occupied/free bricks for a piece should be more than enough.
Quote:Original post by test86
1- When I wanted to design the movement of a piece in GameBoard, I faced two possibilities: If I use OOP I’ll have to pass more stuff and compute more (at least with my current knowledge) and basically it should have more reusability but if I do it old-fashioned with lots of IFs and computational stuff like checking like (if (CurrentPiece+2 == '0') then "u can move"), it will be faster and more dirty, which is better?


Use the clean way. But make sure it's actually clean. It's very easy to delude oneself into thinking that a solution is clean just because it uses objects. The practical solution here would be to compute the new position of the piece, query the board for collisions between the piece and existing bricks, and stopping piece movement if collisions exist.

Quote:2- Since I want to my design of pieces be as reusable as possible, I was after some kind of algorithm so whenever i move or rotate anything, I wouldn’t worry about its results since I’m sure the smaller parts of it were well-written. but i cant find the best way since each of these current and default pieces have exceptions and i need to put some IFs for each of them and that will reduce the reusability that I’m after.


Rotation of a piece is merely a permutation of its component brick elements. It can be entirely expressed in terms of data, and requires neither inheritance nor conditionals.

Quote:3- the game should respond real time to any pressed key appropriately, so how should i prepare for a key at anytime? in dos mode i know how to do it, but in windows mode which is event-based, how should i assign my desired keyboard handler to the event of a pressed key? (In C# we have event for that, but I donno his elder brother in C++)


Usually, you poll for input at regular intervals.

Quote:1- Do you suggest having an Array of like int[20][10] for my GameBoard OR treating it like array of Empty Objects?
each one has its Pros and cons, having an integer array makes its managing much more easier, but having the object based makes its design more b-e-a-u-tiful but will need more processing power at runtime.


It's not about design beauty. It's about making sense. The game board being an array of integers does not make any sense to me—it's pretty obvious when playing the game that the game board is an array of bricks (some of which may be missing). So, typedef a brick type (or create a class for it) and create an array of that. Ultimately, depending on your program complexity, it could be a simple boolean indicating brick presence, or a more complex brick class containing rendering information.
Quote:Original post by ToohrVyk
Quote:Original post by test86
no use of inheritance?! so each piece will have its own move method and its own rotation? i agree that making a universal rotation might be hard, but I think we can have movement resused.


He means, have a single Piece class, with a single rotation function, a single movement function, and a single position system.

The difference between tetris pieces is shape, not behaviour. This is something that is handled through data, not through inheritance. As mentioned before, a 4x4 bitmap of occupied/free bricks for a piece should be more than enough.


i dont get it, how they dont differ just in shape? rotation of an L-shape is not like rotation of a mirrored L-shape, i mean when you rotate each of 7 types of pieaces, each will be in a unique destination and each need a specific collision testing for that target space.
If i can do them all with one type of code, that would be great. plz explain.

you want to treat all of the pieces like a 4x4, right? i dont get it, since a L-Shape will rotate it its way and will be in a different position than another shape, still dont understand.

and BIG THANKS to all people for your fast replies, it really motivates me, since i avoided reading the tetris tutorial and wanted to gain the info, not just some chewed food.
A right rotation for a 4x4 board is:

11 21 31 41 -> 14 12 12 1112 22 32 42 -> 24 23 22 2113 23 33 43 -> 34 33 32 3114 24 34 44 -> 44 43 42 41   ##       ->    ##       ->    ## ## ##   ## ##    ->    ##            ->


It does not matter what the values (brick, no brick) of the pieces of the board are, it still performs the same operation of shuffling around the values.
Quote:Original post by ToohrVyk
Use the clean way. But make sure it's actually clean. It's very easy to delude oneself into thinking that a solution is clean just because it uses objects. The practical solution here would be to compute the new position of the piece, query the board for collisions between the piece and existing bricks, and stopping piece movement if collisions exist.


so these are the things i'm thinking about, but this is my first OO program/game, i did OOP programs before, but its first time that i design one so please be more specific about my problems, as i see, i tried to do those things with mentioned classes, do u think i have not-needed classes? more than enough?

Quote:Original post by ToohrVyk
Rotation of a piece is merely a permutation of its component brick elements. It can be entirely expressed in terms of data, and requires neither inheritance nor conditionals.

as i asked in my previous post, i dont understand how you guys treat all the pieces like one. how it is in terms of data? what do u mean?


Quote:Original post by ToohrVyk
Usually, you poll for input at regular intervals.

poll?! what is this?!
and another thing, i should do this poll thing at certain intervals? can't i just use the way the way of traditional .net? like connecting an event to an event handler? do i have to actually WAIT for an input? the user may press left WHILE my piece is moving down, what about that?

Quote:Original post by ToohrVyk

It's not about design beauty. It's about making sense. The game board being an array of integers does not make any sense to me—it's pretty obvious when playing the game that the game board is an array of bricks (some of which may be missing). So, typedef a brick type (or create a class for it) and create an array of that. Ultimately, depending on your program complexity, it could be a simple boolean indicating brick presence, or a more complex brick class containing rendering information.


thanks for recommending this, so u mean and array of bricks which have a boolean member like Present? am i right?


and how do u multi qoute my answers? i do it by copy& paste the qoute tag, is there any easier way?


AGAIN, thnx for your time/effort/quick helps, i really really appreciate it.
and sorry for my bad english. :P
Quote:Original post by ToohrVyk
A right rotation for a 4x4 board is:

11 21 31 41 -> 14 12 12 1112 22 32 42 -> 24 23 22 2113 23 33 43 -> 34 33 32 3114 24 34 44 -> 44 43 42 41   ##       ->    ##       ->    ## ## ##   ## ##    ->    ##            ->




It does not matter what the values (brick, no brick) of the pieces of the board are, it still performs the same operation of shuffling around the values.

oof, cool ASCII art! ;)

i know this, you are talking about rotating a bitmap 90degrees, i know how to do it, thats not the problem (atleast as i understood what u meant), my problem is its target position in my gameboard's array for collision testing.

i think we are talking about two seperate things here!
i'm not concernd about graphics here, even dos is ok with me, my question is how do you treat all objects the same way as they occupy different positions on gameboard (i mean in the array that you store your blocks).

if i'm not clear yet, tell my i'll draw something to show what i mean.
thnx man for ur time/help/caring.
Quote:Original post by test86
i dont get it, how they dont differ just in shape? rotation of an L-shape is not like rotation of a mirrored L-shape, i mean when you rotate each of 7 types of pieaces, each will be in a unique destination and each need a specific collision testing for that target space.
If i can do them all with one type of code, that would be great. plz explain.

you want to treat all of the pieces like a 4x4, right? i dont get it, since a L-Shape will rotate it its way and will be in a different position than another shape, still dont understand.


A piece is a piece. The OO way is "what", not "how".

You can "rotate" a piece. "how" you do it is hidden. But the "rotate" of all pieces is exactly the same.

For tetris, the somewhat optimal representation of pieces is similar to bitmap, and as shown above - use a grid. Your Piece class can then use different array representation, but that is hidden.

Here's an example of how it might look:
class Piece{  // create new piece with certain max dimensions  Piece(width, height);  // piece creators, add or remove "filled" spots  void fill_spot(int x, int y);  void clear_spot(int x, int y);  // manipulator functions  void rotate_cw();  void rotate_ccw();  void move_by( int dx, int dy );};class Board{  // attempts to push the piece one row down  // returns false if piece would collide  //   bool push_down( Piece p );  // Tests for intersection between a piece and board  // also test for left, right and bottom margins  bool intersects( Piece p );  // Piece is blended into board  void blend( Piece p);}


Just a quick hack, so it's probably not very functional.

Everything else is a matter of implementation.

If you do want to get OO-ish, observe that both of the above classes have same common functionality - the representation of the board. When you blend the piece into board, you need to perform same functionality as when creating a piece - turning on some blocks, and turning off others.

So now you can merge this:

class Blocks{  Blocks(int width, int height);  // piece creators, add or remove "filled" spots  void fill_spot(int x, int y);  void clear_spot(int x, int y);}class Piece : Blocks{  void rotate_cw();  void rotate_ccw();  void move_by( int dx, int dy );}class Board : Blocks{   // Re-use paren't class functionality to blend the piece   void blend( Piece p)   {      for (every block in p)        if ( spot in p is filled)           fill_spot(x, y);   } }


Now you can even reuse the rendering code to render "Blocks" class, and then just pass it either a Piece or a Board object.

Just a few things to think over.

This topic is closed to new replies.

Advertisement