Sign in to follow this  
Dragonsoulj

C++ Classes and Arrays

Recommended Posts

*I'm trying to find a way to do this without vectors. A bit of a learning thing.* I have this class:
class Piece
{
private:
  Block *tetromino;
  int rotation;
  Shape shape;
public:
  Piece(Shape shape);
  void translate();
  void rotate();
  bool collides();
};

and this one:
class Block
{
private:
  static sf::Image *image;
  sf::Sprite sprite;
  int x, y;
  Shape shape;

public:
  Block(Shape shape);
  void setPosition(int xCoord, int yCoord);
  void setSprite();
  friend class Piece;
};

For the Piece class, I want to allocate 4 blocks (I mean my block class) of a desired shape:
tetromino = new Block(shapeName)[4];

But you cannot do this. Is there a way to specify a constructor to use and use it? Or would I have to make the tetromino a double pointer and declare each individually?

Share this post


Link to post
Share on other sites
Now this one has me stumped.
Quote:
piece.cpp: In function `void initializeShape()':
piece.cpp:20: error: `tetromino' was not declared in this scope

Is what my compiler tells me.

class Piece
{
private:
std::vector<Block> tetromino;
int rotation;
Shape shape;
public:
Piece(Shape shape);
void translate();
void rotate();
bool collides();
};

Is my header file and
Piece::Piece(Shape shape)
{
tetromino.push_back(Block(shape));
tetromino.push_back(Block(shape));
tetromino.push_back(Block(shape));
tetromino.push_back(Block(shape));
rotation = 0;
shape = shape;
}

void initializeShape()
{
tetromino[0].setPosition(5, 19);
tetromino[1].setPosition(4, 19);
tetromino[2].setPosition(4, 18);
tetromino[3].setPosition(5, 18);
}

void Piece::translate()
{

}

void Piece::rotate()
{

}

bool Piece::collides()
{

}

is my definition file.

Share this post


Link to post
Share on other sites
Quote:
Is there a way to specify a constructor to use and use it?


No, but you can define and use the default constructor, and then assign over those values.

Quote:
Or would I have to make the tetromino a double pointer and declare each individually?


Please wipe this idea from your head. You should never explicitly make your data structure more complicated, and manage that complexity yourself, just to satisfy the compiler. It's bad enough that you have to be so explicit about your data structure in C++.

If you really want to do "a bit of a learning thing", research how std::vector does its magic. There are a fair few subtle things in there.

Quote:
piece.cpp: In function `void initializeShape()':
piece.cpp:20: error: `tetromino' was not declared in this scope


'initializeShape()' is a free function, not a member function. Therefore, there is no implicit Piece whose .tetromino to work with.

But you're going about this part all wrong, anyway. Set up that data by passing those parameters to the Block constructor, not by calling setPosition. You don't need a setPosition, anyway, if you give the Piece its own position and update that (and take the Block's position as being relative to its containing Piece).

That said, encapsulating individual Blocks is probably overkill for Tetris, and most implementations I've seen (and all the ones I've written) represent a piece with a 4x4 grid of "is there a block at this location relative to the piece location?" instead of a set of 4 "here is the relative location of a block in the piece" values. It makes the rest of the algorithmic work simpler overall. Trust me. ;)

Share this post


Link to post
Share on other sites
Quote:
Original post by Dragonsoulj
tetromino = new Block(shapeName)[4];


tetromino = operator new(4 * sizeof Block);
new(tetromino+0) Block(shapeName);
new(tetromino+1) Block(shapeName);
new(tetromino+2) Block(shapeName);
new(tetromino+3) Block(shapeName);

Don't forget to delete[] tetromino; in the destructor. Once you figured out how this works and understand the exception-related issues, remove this ugly low-level code and just use std::vector.

Share this post


Link to post
Share on other sites
If you want to do it that way. I know some about vectors:
  tetromino.push_back(Block(shape));
tetromino.push_back(Block(shape));
tetromino.push_back(Block(shape));
tetromino.push_back(Block(shape));



Is what I did.

My problem now is why when I try to delete a piece, I get an error. Is this correct (abbreviated code)?
Board Header:
private:
Piece *currentPiece;
Piece *nextPiece;



Board Declaration:
Board::Board()
{
currentPiece = new Piece(O,filledSquares);
nextPiece = new Piece(O,filledSquares);
}



And the deletion:
void Board::getNextPiece()
{
delete currentPiece;
currentPiece = nextPiece;
delete nextPiece;
nextPiece = new Piece(O,filledSquares);
}




EDIT: O is a defined shape enum and filledSquares is a boolean pointer, both of which work fine. The code I have works without the delete statements, which it should, just deleting them should work as well (memory leaks otherwise).

Share this post


Link to post
Share on other sites
Quote:
Original post by Dragonsoulj
void Board::getNextPiece()
{
delete currentPiece;
currentPiece = nextPiece;
delete nextPiece;
nextPiece = new Piece(O,filledSquares);
}

You are deleting two pieces but only create one new piece. That cannot be correct. Why are you handling pieces indirectly to begin with? Why can't you store the pieces by value, without pointers?

Share this post


Link to post
Share on other sites
*Just a note: I'm not completely retarded. I've just been up a long time sorting out some issues in this and then caught some new ones that I just couldn't figure out.*

Missed that. 1 delete needed. I'm just generating a piece, letting the game loop through, and when the piece hits the bottom, storing it in a map and getting a new one.

I have this loop, and I wasn't sure of another way to keep a piece alive long enough for me to get use I need, then make a new one with the same name. And I probably could just make the clock and board in this loop variables. I didn't know if I was going to use them elsewhere or not.
void Interface::loop()
{
float TimeElapsed = 0;
clock->Reset();
board->drawBoard(windowApp);

while(windowApp->IsOpened())
{
sf::Event Event;
while(windowApp->GetEvent(Event))
{
if((Event.Type == sf::Event::Closed) || ((Event.Type == sf::Event::KeyPressed) && (Event.Key.Code == sf::Key::Escape)))
{
windowApp->Close();
}

if((Event.Type == sf::Event::KeyPressed) && (Event.Key.Code == sf::Key::Left))
{
board->currentPiece->translate(left);
}
if((Event.Type == sf::Event::KeyPressed) && (Event.Key.Code == sf::Key::Right))
{
board->currentPiece->translate(right);
}
}

TimeElapsed = clock->GetElapsedTime();
if(0.5 <= TimeElapsed)
{
if(board->currentPiece->translate(down))
{
for(int i = 0; i < 4; i++)
{
int x = board->currentPiece->tetromino.at(i).getX();
int y = board->currentPiece->tetromino.at(i).getY();
board->filledSquares[x*20 + y] = true;
board->blocks.erase(board->blocks.begin() + x*20 + y);
board->blocks.insert(board->blocks.begin() + x*20 + y, Block(board->currentPiece->shape));
board->blocks.at(x*20 + y).setPosition(x,y);
}
board->clearLine();
board->getNextPiece();
}
clock->Reset();
}
board->drawBoard(windowApp);
}
}





*EDIT*
Quote:
Original post by Zahlman
That said, encapsulating individual Blocks is probably overkill for Tetris, and most implementations I've seen (and all the ones I've written) represent a piece with a 4x4 grid of "is there a block at this location relative to the piece location?" instead of a set of 4 "here is the relative location of a block in the piece" values. It makes the rest of the algorithmic work simpler overall. Trust me. ;)

I was mainly going about this because the sprite images I was loading were only filling certain squares and I was using the same image. I probably could put the piece and block classes together and attempt a 4x4 grid. Maybe doing what I did for the board, taking one array that says whether a space was filled or not and another that held the image. That was another issue. I had a space for a block in my board class, in case part of the shape was destroyed.

[Edited by - Dragonsoulj on February 20, 2010 6:24:55 AM]

Share this post


Link to post
Share on other sites
Just for what it's worth, some elaboration on the design I had in mind:


struct Shape {
// all the image resources for individual blocks.
static sf::Image* blocks[NUM_BLOCKS];

// A 0 value corresponds to empty space in the block; anything else
// represents filled space, and is an index into blocks (store NULL in index 0).
int data[4][4];

// We can initialize these with aggregate struct initializers.
};

class Piece {
static Shape all_shapes[7]; // line, square, T, two Ls, two zigzags
int type; // index for all_shapes
int rotation;
int x, y;

int index_at(int x, int y) { // helper function.
// TODO: do math on x and y to account for the rotation
return all_shapes[type][x][y];
}

public:
bool block_at(int x, int y) { return index_at(x, y) != 0; }
sf::Image* appearance(int x, int y) { return Shape::blocks[index_at(x, y)]; }
void rotation(int amount); // +1 or -1
bool collides(const Board& board); // int[width][height] that we check against
void move(int dx, int dy);
};

// I *think* I set things up properly to allow initializing this way:
static Shape Piece::all_shapes[7] = {
{ { 0, 0, 0, 0 },
{ 1, 1, 1, 1 },
{ 0, 0, 0, 0 },
{ 0, 0, 0, 0 } }, // line
{ { 0, 0, 0, 0 },
{ 0, 2, 2, 0 },
{ 0, 2, 2, 0 },
{ 0, 0, 0, 0 } }, // square
// etc.
};

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this