I've been given a task for my cpp homework, the task is long and has many more functions that this, but I am stuck at the beggining. What I am trying to do here is just write out the point that is given on the screen.
#include <iostream>
using namespace std;
class Point {
public:
double x, y;
Point(){
x=0.0;
y=0.0;
};
Point(double x,double y){
this -> x = x;
this -> y = y;
}
void print() {
cout << "(x,y) = ("<< x <<","<< y <<")"<<endl;
}
};
class Triangle {
public:
Point A;
Triangle(const Point& p1){
A.x = p1.x;
A.y = p1.y;
}
void print1(){
cout << "A(x,y) = ("<< A.x <<","<< A.y <<")"<<endl;
}
};
int main(){
Triangle A{1.0,2.0};
A.print1();
return 0;
}
What my thinking here is, I have a class named Point and it is made of two variables x and y, class Triangle in the task has 3 points, but I am using just one for simplicity, it has a point that is from class Point (so it should have x and y coordinates) and a constructor that has a point from class Point also. I was thinking just to link their x and y coordinates and to print them out. But it doesn't work like that, can you help me. I have more code from the task if you need, and code from our lessons. Thank you.
Triangle(const Point& p1) accepts a const reference to a Point. A reference is an alias to an existing variable. In this case rather than copying in a Point, the Triangle constructor receives the Point itself. The const is important because it is a promise that the Point will not be modified inside by Triangle's constructor. This allows you to pass in a reference to a temporary Point that otherwise would not be around long enough for modification to be meaningful and is rejected by the compiler to prevent possible errors.
Triangle A{1.0,2.0};
will attempt to make Triangle from two floating point values. Triangle needs a reference to a Point, so you must make that Point first.
Triangle A{ {1.0,2.0} };
^ ^
| Construct a temporary Point from 2 floating point numbers
Triangle constructor arguments: one Point
Unrelated improvement: Use the Member Initializer List
Triangle(const Point& p1): A{p1}{
}
I have a class like below
class Circle{
private:
int radius;
Circle* next
}
And I gonna creat set/get method...but i have no idea which data type i have to use.
int Circle::getRadius() const{return radius}
or
int& Circle::getRadius() const{return radius}
void Circle::setRadius(int r)
or
void Circle::setRadius(int& r)
CirCle* Circle::getNext() const{return next}
or
Circle& Circle::getNext() const{return *(next)}
void Circle::setNext(Circle& circle)
{
next = new Circle;
next = circle;
}
or
void Circle::setRadius(Circle circle)
{
next = new Circle;
next = circle;
}
or
void Circle::setRadius(Circle* circle)
{
next = circle;
}
I'm famliar with Java quite a lot. And Java argument are all reference. But cpp is quite different so it drive me crazy. Thanks for your help.
First I'd just recommend going through some C++ tutorials. Your question is probably going to get downvoted here because it looks like you really didn't try to search for a solution to your problem, but rather just ask SO for help.
Need to understand a few things with C++. Learn pass by reference vs pass by value. Are primitives passed by reference or by value?
You also should look at pointers. You're kinda mixing up syntax in there a little bit. * is to de reference a pointer. & is to get the memory address of a certain object.
Explore these concepts and you'll find the answer to your question, and learn more about C++.
Try looking at this site for some info.
http://www.learncpp.com/cpp-tutorial/84-access-functions-and-encapsulation/
If you have any other questions, feel free to let me know. :)
Happy to help.
You should use:
int Circle::getRadius() const{return radius}
Why?
First because you don't want your getter to modify your object (that is done by the const after () ), and you don't want caller to your getter to be able to modify your radius either, in fact:
int& Circle::getRadius() const{return radius}
should not even compile. it would have to be:
const int& Circle::getRadius() const{return radius}
In this case the reference to radius returned is const and therefore, the caller cannot modify the radius via this getter.
Although it's totally correct, when dealing with primitive types in C++ one usually prefer to copy rather than to hold const reference. Why? because copy on primitive costs less than have to dereference it each time you need to use it.
Use:
void Circle::setRadius(int r)
Why?
Like before, using an int, prefere to copy the value to use a reference that you'll have to (implicitly) derefence on use.
In this case:
CirCle* Circle::getNext() const{return next}
or
Circle& Circle::getNext() const{return *(next)}
Why? One thing is sure, you won't be able to use the second one, like in the first case your return value will have to be const Circle&. Plus, you want to be able to return a "invalid" value. In C++ not like in Java, you cannot return an "invalid" reference. So the best thing is to return a pointer which will have "NULL" value if "invalid".
After that, if you want your caller to be able to modify the "next Circle" you'll have to go with a Circle* return. If you don't want your caller to be able to modify the result you'll have to go with a const Circle*
const CirCle* Circle::getNext() const{return next}
or
Circle* Circle::getNext() const{return next}
Some people think it's a bad thing to have a const method that return a non const pointer. For certain reasons, I don't, but both are syntaxly correct.
Use:
void Circle::setNext(Circle* circle)
{
next = circle;
}
Why? For your SetNext, it depends on who will have to manage the memory (ie destruction) used by your "next circle" if it's an external class (I think it's the easiest), like a manager for exemple go with that
For your setRadius, simply use:
void Circle::setRadius(int value)
{
radius = value;
}
[Edit: ] Example of Circle class:
Here what would an external manager (like a Circle list) would look like:
class CircleList //Manager as I told
{
public:
Circle* createCircle(int _radius)
{
Circle* circle = new circle(_radius);
//manage here the add to the list of circle
}
void destroyCircle(Circle* _circle)
{
//Manage here the remove of the list
delete _circle;
}
~CircleList()
{
while( first )
{
destroyCircle(first);
}
}
private:
Circle* first = NULL;
};
class Circle
{
public:
Circle(int _radius) : radius(_radius) { }
void setNext(Circle* _circle)
{
next = _circle;
}
Circle* getNext() const
{
return next;
}
void setRadius(int _value)
{
radius = _value;
}
private:
Circle* next = NULL;
int radius = -1;
};
Here, only CircleList manage the list and memory used by circles. If you want to encapsulate even more, make setNext/getNext private and CircleList a friend of circle (once again some will judge, let them :p)
If you wanted to manage memory inside the Circle class, a circle would exist only if its predecessor exist, and if you delete one Circle you would delete all the ones after in the list(I can't see the application this...). In this case you would have something like:
class Circle
{
public:
Circle(int _radius) : radius(_radius) { }
void setNext(int _radius)
{
next = new Circle(radius);
}
void removeNext()
{
delete next;
next = NULL;
}
Circle* getNext() const
{
return next;
}
void setRadius(int _value)
{
radius = _value;
}
~Circle()
{
delete next;
}
private:
Circle* next = NULL;
int radius = -1;
};
Note that you now have a destructor on circle and that when destroying one circle, it destroys all the circle that follow in the list (only way I see to avoid leak and external "holder" of circles)
Plus if you have a really long list, it may cause problems when destroying as a destructor calls (implicitly) the destructor of its successor, you may end up with a stack overflow.
That's why I was telling you that a manager external to the class was the best solution to me, but maybe some other people have better ideas :)
First of all, in Java all are passed by value!
In case of references, it is passed the value of the reference. Because of that it is misunderstood generally.
In case of C++, you can return/pass by value or reference. In your example, int value would be returned by value simply; supposing that probably you would only need its value.
In case of a complex object, you could use reference or pointer. They are actually -almost- the same. You can find sources to look the differences in detail, but here let me tell simply: Pointers are kept in C++ to be compatible with C; references are considered instead. So, you could try to use references mostly.
You need to know the difference of using values by copy and by reference and using pointers.
Passing values by copy, say:
int radius = 2;
setRadius( radius );
Creates a copy of 2 within the function. Whatever changes you do to that value within the function won't change the variable radius that you created outside.
Pointers hold a value that is the memory address of some variable. No matter the type you're using, a pointer only takes up 4 bytes in memory.
int radius = 2;
int *radiusPtr = &radius; // radiusPtr now points to the address of radius
std::cout << *radiusPtr << std::endl; // > 2
setRadius( radiusPtr ); // Passing a pointer to setRadius
Passing by reference is, in a way, similar to pointers but it's defined as "passing the variable itself". You could use a pointer to change the value of the pointed left value, but you can use a reference to change the values of the original variable. You could even use a reference to a pointer to change the original pointer.
int radius = 2;
setRadius( radius ); // Passing by reference is done in the same way as by value, the difference is the method signature -> setRadius( int &radius );
int &radiusRef = radius;
radiusRef = 3;
std::cout << radius << std::endl; // > 3
To answer what to use. Depends on your types and what you need.
//If you use a setter with reference, like
void setRadius( int& radius );
//You cannot pass literal values like
setRadius( 2 );
setRadius( int(2) );
//Only variables/lvalues
int radius = 2;
setRadius( radius );
If you are using a more complex structure then the reference, or a pointer, makes more sense. It's also more efficient to use a reference than it is to copy a huge structure.
The same applies when returning values. If you don't want the user to change the value of your attribute, then a pointer or reference are not the best option. But you could always use a const pointer or reference,
const int *getRadius() const;
const int &getRadius() const;
To prevent the user from changing the radius from outside, yet be able to use the value. The last const means that this function can be called even if you're using a const Circle, or const *Circle or const &Circle.
I'd say for ints you could just use copies, but for more complex structures, like the Circle, consider using references or pointers. In your case, you even store pointers to the next Circle, so a pointer should do.
class Circle{
public:
void SetRadius(int const& value){ radius = value;} // 1
int GetRadius() const {return radius;} // 2
void SetCircle(std::shared_ptr<Circle> const& pCircle) { next = pCircle;} // 3
std::shared_ptr<Circle> GetCircle() { return next; } // 4
private:
int radius;
std::shared_ptr<Circle> next;
}
1 and 3 This is how you write set functions. Period.
2 and 4 Return by value and dont break the encapsulation
3 and 4 NEVER use raw pointers
A while ago I asked a question on why the following code did not work:
std::vector<std::vector<std::vector<Tile_Base*>>> map_tile; // This is located in Map object. See below.
int t_x, t_y;
t_x = t_y = 200;
map_tiles.begin(); // clear(), resize() and every other function still causes problems
The thing is, is that it should have worked, yet Visual Studios 2012 throws an exception when the resize function is called. The exception pointed to this piece of code:
*_Pnext != 0; *_Pnext = (*_Pnext)->_Mynextiter)
located in xutility. It said that there was an violating on access to reading the memory. I thought maybe somehow I lost access to the member along the way? (Using VS' watch I saw the memory was not corrupted)
So, I fiddled around with the code and tried to figure out what could possibly be going wrong, and after awhile I moved the map_tiles object down to the bottom of the list, and it worked:
// WORKS
class Map {
std::vector<Tile_Base*> spawn_tiles;
// map tile specific
bool Is_Valid(int,int);
std::string name;
std::vector<std::vector<std::vector<Tile_Base*> > > map_tiles;
public:
// ...
}
// DOESN'T WORK
class Map {
std::vector<std::vector<std::vector<Tile_Base*> > > map_tiles;
std::vector<Tile_Base*> spawn_tiles;
// map tile specific
bool Is_Valid(int,int);
std::string name;
public:
// ...
}
Any help pointing out what went wrong? I can't come up with any reasonable explanation.
A vector<T> comprises two discrete sets of data: the internal state and the array of Ts. The internal state - capacity, size, pointer - is separate from the array. The issue you're describing is normally caused by something overwriting the vector object, i.e the internal state. To track this down easily you could use a container class:
typedef std::vector<std::vector<std::vector<Tile_Base*> > > maptiles_t;
class CMapTiles
{
unsigned int m_guard;
maptiles_t m_tiles;
enum { Guard = 0xdeadbeef };
public:
CMapTiles() : m_guard(Guard), m_tiles() {}
~CMapTiles() { assert(m_guard == Guard); }
void Check()
{
#if defined(DEBUG)
if (m_guard != Guard)
DebugBreak();
#endif
}
void Resize(size_t x, size_t y)
{
Check();
auto init = std::vector<std::vector<Tile_Base*> >(y/32);
m_tiles.resize(m_x / 32, init);
Check();
}
const maptiles_t& tiles() const { Check(); return m_tiles; }
maptiles_t& tiles() { Check(); return m_tiles; }
};
And instead of using std::vector<...> map_tiles have CMapTiles map_tiles, and then when you want to get at the vector, map_tiles.tiles().
Hope this helps.
I'm designing a robot simulator for a university project and I've hit a big issue for some collision detection. Here is my robot.h header file:
#ifndef robot_h
#define robot_h
#include <vector>
enum direction
{
UP,DOWN,LEFT,RIGHT
};
enum motor
{
STOP,SLOW,FAST
};
class robot
{
public:
robot();
char bot; // The bot onscreen
int getX(); // The X position of robot
int getY(); // The Y position of robot
int dir; // The direction the robot is going
bool touchSensor; // Boolean value if collision
int lightSensor; // light sensor between 10-100
int motorA; // Motor A between 0, 1 and 2
int motorB; // Motor A between 0, 1 and 2
void detection(int x, int y);
void getReturnObject();
bool returnObjectDash;
bool returnObjectLine;
void move(); // Moving the robot
void draw(); // Drawing the robot on screen
void update(); // Updating the robot position
private:
int positionX; // Starting X value
int positionY; // Starting Y value
};
#endif
Basically, I have two boolean values being used:
returnObjectDash;and returnObjectLine. I have this code in my matrix.cpp file:
void matrix::detection(int x, int y)
{
if(vector2D[x][y]=='-')
{
returnObjectDash=true;
system("pause");
}
else
{
returnObjectDash=false;
}
if(vector2D[x][y]=='|')
{
returnObjectLine=true;
}
else
{
returnObjectLine=false;
}
}
Inside my robot.cpp I have this code which gets the two boolean values and then outputs to the console:
void robot::getReturnObject()
{
if(returnObjectDash==true)
{
std::cout<<"Dash\n";
//dir=DOWN;
}
if(returnObjectLine==true)
{
std::cout<<"Line\n";
//dir=DOWN;
}
}
This is my main.cpp
int main()
{
robot r;
while(true)
{
matrix m;
m.robotPosition(r.getX(), r.getY());
m.update(); // vector2D init and draw
m.detection(m.getX(), m.getY());
r.update();
Sleep(250);
}
}
I'm setting the default value of my two boolean variables in my matrix.cpp default constructor to false. When I hit the pause button and debug I seem to be getting two different returns. For my matrix it is returning false, though for my robot it is returning true, it is like my program is making two different variables. If someone could shed some light on this weird behaviour, then please do tell! Thank you
Your program is making two different values because it has two different values. The matrix class apparently has its own Boolean variable, matrix::returnObjectDash, and the robot class has its own variable, robot::returnObjectDash. Setting the variable in one instance of one class has no impact on the variables in any other classes (or any other instances).
You have not provided code to you matrix class, however, judging from void matrix::detection(int x, int y) I assume that you have a method in there, called detection, and that you have declared the same fields returnObjectLine and returnObjectDash.
You are correct in assuming that there are 2 versions of those variables: One of those versions is inside your matrix object, and the other one is inside your robot object.
Not only that, you can (and usually do!) have more than one matrix/robot object. Each of those will have its own separate copy of those variables, and changing one of them will not impact the other ones.
I've written a working tetris clone but it has a pretty messy layout. Could I please get feedback on how to restructure my classes to make my coding better. I focuses on making my code as generic as possible, trying to make it more an engine for games only using blocks.
Each block is created seperately in the game.
My game has 2 BlockLists (linked lists): StaticBlocks and Tetroid.
StaticBlocks is obviously the list of all non-moving blocks, and tetroid are the 4 blocks of the current tetroid.
In main a World is created.
First a new tetroid (4 blocks in a list Tetroid) is created by (NewTetroid)
Collision is detected by the (***Collide) functions, by comparing each of Tetroid with all of the StaticBlocks using the (If*****) functions.
When the tetroid stops (hits the bottom/blocks), it is copied (CopyTetroid) to the StaticBlocks and Tetroid is made empty, then tests are made for complete lines, blocks are destroyed/dropped etc by searching StaticBlocks with (SearchY).
A new tetroid is created.
(TranslateTetroid) and (RotateTetroid) perform operations on each block in the Tetroid list one by one ( I think this is bad practise).
(DrawBlockList) just goes through a list, running the Draw() function for each block.
Rotation is controlled by setting rotation axis relative to the first block in Tetroid when (NewTetroid) is called. My rotation function (Rotate) for each block rotates it around the axis, using an input +-1 for left/right rotation. RotationModes and States are for blocks that rotate in 2 or 4 different ways, defining what state they are currently in, and whether they should be rotated left or right. I am not happy with how these are defined in "World", but I don't know where to put them, whilst still keeping my (Rotate) function generic for each block.
My classes are as follows
class World
{
public:
/* Constructor/Destructor */
World();
~World();
/* Blocks Operations */
void AppendBlock(int, int, BlockList&);
void RemoveBlock(Block*, BlockList&);;
/* Tetroid Operations */
void NewTetroid(int, int, int, BlockList&);
void TranslateTetroid(int, int, BlockList&);
void RotateTetroid(int, BlockList&);
void CopyTetroid(BlockList&, BlockList&);
/* Draw */
void DrawBlockList(BlockList&);
void DrawWalls();
/* Collisions */
bool TranslateCollide(int, int, BlockList&, BlockList&);
bool RotateCollide(int, BlockList&, BlockList&);
bool OverlapCollide(BlockList&, BlockList&); // For end of game
/* Game Mechanics */
bool CompleteLine(BlockList&); // Test all line
bool CompleteLine(int, BlockList&); // Test specific line
void ColourLine(int, BlockList&);
void DestroyLine(int, BlockList&);
void DropLine(int, BlockList&); // Drops all blocks above line
int rotationAxisX;
int rotationAxisY;
int rotationState; // Which rotation it is currently in
int rotationModes; // How many diff rotations possible
private:
int wallX1;
int wallX2;
int wallY1;
int wallY2;
};
class BlockList
{
public:
BlockList();
~BlockList();
Block* GetFirst();
Block* GetLast();
/* List Operations */
void Append(int, int);
int Remove(Block*);
int SearchY(int);
private:
Block *first;
Block *last;
};
class Block
{
public:
Block(int, int);
~Block();
int GetX();
int GetY();
void SetColour(int, int, int);
void Translate(int, int);
void Rotate(int, int, int);
/* Return values simulating the operation (for collision purposes) */
int IfTranslateX(int);
int IfTranslateY(int);
int IfRotateX(int, int, int);
int IfRotateY(int, int, int);
void Draw();
Block *next;
private:
int pX; // position x
int pY; // position y
int colourR;
int colourG;
int colourB;
};
Sorry if this is a bit unclear or long winded, I'm just looking for some help restructuring.
What is the single responsibility of the World class?
It's just a blob containing practically every kind of functionality. That's not good design. One obvious responsibility is "represent the grid onto which blocks are placed". But that has nothing to do with creating tetroids or manipulating block lists or drawing. In fact, most of that probably doesn't need to be in a class at all. I would expect the World object to contain the BlockList you call StaticBlocks so it can define the grid on which you're playing.
Why do you define your own Blocklist? You said you wanted your code to be generic, so why not allow any container to be used? Why can't I use a std::vector<Block> if I want to? Or a std::set<Block>, or some home-brewed container?
Use simple names that don't duplicate information or contradict themselves. TranslateTetroid doesn't translate a tetroid. It translates all the blocks in a blocklist. So it should be TranslateBlocks or something. But even that is redundant. We can see from the signature (it takes a BlockList&) that it works on blocks. So just call it Translate.
Try to avoid C-style comments (/*...*/). C++-style (//..)behaves a bit nicer in that if you use a C-style comment out an entire block of code, it'll break if that block also contained C-style comments. (As a simple example, /*/**/*/ won't work, as the compiler will see the first */ as the end of the comment, and so the last */ won't be considered a comment.
What's with all the (unnamed) int parameters? It's making your code impossible to read.
Respect language features and conventions. The way to copy an object is using its copy constructor. So rather than a CopyTetroid function, give BlockList a copy constructor. Then if I need to copy one, I can simply do BlockList b1 = b0.
Rather than void SetX(Y) and Y GetX() methods, drop the redundant Get/Set prefix and simply have void X(Y) and Y X(). We know it's a getter because it takes no parameters and returns a value. And we know the other one is a setter because it takes a parameter and returns void.
BlockList isn't a very good abstraction. You have very different needs for "the current tetroid" and "the list of static blocks currently on the grid". The static blocks can be represented by a simple sequence of blocks as you have (although a sequence of rows, or a 2D array, may be more convenient), but the currently active tetroid needs additional information, such as the center of rotation (which doesn't belong in the World).
A simple way to represent a tetroid, and to ease rotations, might be to have the member blocks store a simple offset from the center of rotation. That makes rotations easier to compute, and means that the member blocks don't have to be updated at all during translation. Just the center of rotation has to be moved.
In the static list, it isn't even efficient for blocks to know their location. Instead, the grid should map locations to blocks (if I ask the grid "which block exists in cell (5,8), it should be able to return the block. but the block itself doesn't need to store the coordinate. If it does, it can become a maintenance headache. What if, due to some subtle bug, two blocks end up with the same coordinate? That can happen if blocks store their own coordinate, but not if the grid holds a list of which block is where.)
this tells us that we need one representation for a "static block", and another for a "dynamic block" (it needs to store the offset from the tetroid's center). In fact, the "static" block can be boiled down to the essentials: Either a cell in the grid contains a block, and that block has a colour, or it does not contain a block. There is no further behavior associated with these blocks, so perhaps it is the cell into which it is placed that should be modelled instead.
and we need a class representing a movable/dynamic tetroid.
Since a lot of your collision detection is "predictive" in that it deals with "what if I moved the object over here", it may be simpler to implement non-mutating translation/rotation functions. These should leave the original object unmodified, and a rotated/translated copy returned.
So here's a first pass on your code, simply renaming, commenting and removing code without changing the structure too much.
class World
{
public:
// Constructor/Destructor
// the constructor should bring the object into a useful state.
// For that, it needs to know the dimensions of the grid it is creating, does it not?
World(int width, int height);
~World();
// none of thes have anything to do with the world
///* Blocks Operations */
//void AppendBlock(int, int, BlockList&);
//void RemoveBlock(Block*, BlockList&);;
// Tetroid Operations
// What's wrong with using BlockList's constructor for, well, constructing BlockLists? Why do you need NewTetroid?
//void NewTetroid(int, int, int, BlockList&);
// none of these belong in the World class. They deal with BlockLists, not the entire world.
//void TranslateTetroid(int, int, BlockList&);
//void RotateTetroid(int, BlockList&);
//void CopyTetroid(BlockList&, BlockList&);
// Drawing isn't the responsibility of the world
///* Draw */
//void DrawBlockList(BlockList&);
//void DrawWalls();
// these are generic functions used to test for collisions between any two blocklists. So don't place them in the grid/world class.
///* Collisions */
//bool TranslateCollide(int, int, BlockList&, BlockList&);
//bool RotateCollide(int, BlockList&, BlockList&);
//bool OverlapCollide(BlockList&, BlockList&); // For end of game
// given that these functions take the blocklist on which they're operating as an argument, why do they need to be members of this, or any, class?
// Game Mechanics
bool AnyCompleteLines(BlockList&); // Renamed. I assume that it returns true if *any* line is complete?
bool IsLineComplete(int line, BlockList&); // Renamed. Avoid ambiguous names like "CompleteLine". is that a command? (complete this line) or a question (is this line complete)?
void ColourLine(int line, BlockList&); // how is the line supposed to be coloured? Which colour?
void DestroyLine(int line, BlockList&);
void DropLine(int, BlockList&); // Drops all blocks above line
// bad terminology. The objects are rotated about the Z axis. The x/y coordinates around which it is rotated are not axes, just a point.
int rotationAxisX;
int rotationAxisY;
// what's this for? How many rotation states exist? what are they?
int rotationState; // Which rotation it is currently in
// same as above. What is this, what is it for?
int rotationModes; // How many diff rotations possible
private:
int wallX1;
int wallX2;
int wallY1;
int wallY2;
};
// The language already has perfectly well defined containers. No need to reinvent the wheel
//class BlockList
//{
//public:
// BlockList();
// ~BlockList();
//
// Block* GetFirst();
// Block* GetLast();
//
// /* List Operations */
// void Append(int, int);
// int Remove(Block*);
// int SearchY(int);
//
//private:
// Block *first;
// Block *last;
//};
struct Colour {
int r, g, b;
};
class Block
{
public:
Block(int x, int y);
~Block();
int X();
int Y();
void Colour(const Colour& col);
void Translate(int down, int left); // add parameter names so we know the direction in which it is being translated
// what were the three original parameters for? Surely we just need to know how many 90-degree rotations in a fixed direction (clockwise, for example) are desired?
void Rotate(int cwSteps);
// If rotate/translate is non-mutating and instead create new objects, we don't need these predictive collision functions.x ½
//// Return values simulating the operation (for collision purposes)
//int IfTranslateX(int);
//int IfTranslateY(int);
//int IfRotateX(int, int, int);
//int IfRotateY(int, int, int);
// the object shouldn't know how to draw itself. That's building an awful lot of complexity into the class
//void Draw();
//Block *next; // is there a next? How come? What does it mean? In which context?
private:
int x; // position x
int y; // position y
Colour col;
//int colourR;
//int colourG;
//int colourB;
};
// Because the argument block is passed by value it is implicitly copied, so we can modify that and return it
Block Translate(Block bl, int down, int left) {
return bl.Translate(down, left);
}
Block Rotate(Block bl, cwSteps) {
return bl.Rotate(cwSteps);
}
Now, let's add some of the missing pieces:
First, we'll need to represent the "dynamic" blocks, the tetroid owning them, and the static blocks or cells in a grid.
(We'll also add a simple "Collides" method to the world/grid class)
class Grid
{
public:
// Constructor/Destructor
Grid(int width, int height);
~Grid();
// perhaps these should be moved out into a separate "game mechanics" object
bool AnyCompleteLines();
bool IsLineComplete(int line);
void ColourLine(int line, Colour col);Which colour?
void DestroyLine(int line);
void DropLine(int);
int findFirstInColumn(int x, int y); // Starting from cell (x,y), find the first non-empty cell directly below it. This corresponds to the SearchY function in the old BlockList class
// To find the contents of cell (x,y) we can do cells[x + width*y]. Write a wrapper for this:
Cell& operator()(int x, int y) { return cells[x + width*y]; }
bool Collides(Tetroid& tet); // test if a tetroid collides with the blocks currently in the grid
private:
// we can compute the wall positions on demand from the grid dimensions
int leftWallX() { return 0; }
int rightWallX() { return width; }
int topWallY() { return 0; }
int bottomWallY { return height; }
int width;
int height;
// let this contain all the cells in the grid.
std::vector<Cell> cells;
};
// represents a cell in the game board grid
class Cell {
public:
bool hasBlock();
Colour Colour();
};
struct Colour {
int r, g, b;
};
class Block
{
public:
Block(int x, int y, Colour col);
~Block();
int X();
int Y();
void X(int);
void Y(int);
void Colour(const Colour& col);
private:
int x; // x-offset from center
int y; // y-offset from center
Colour col; // this could be moved to the Tetroid class, if you assume that tetroids are always single-coloured
};
class Tetroid { // since you want this generalized for more than just Tetris, perhaps this is a bad name
public:
template <typename BlockIter>
Tetroid(BlockIter first, BlockIter last); // given a range of blocks, as represented by an iterator pair, store the blocks in the tetroid
void Translate(int down, int left) {
centerX += left;
centerY += down;
}
void Rotate(int cwSteps) {
typedef std::vector<Block>::iterator iter;
for (iter cur = blocks.begin(); cur != blocks.end(); ++cur){
// rotate the block (*cur) cwSteps times 90 degrees clockwise.
// a naive (but inefficient, especially for large rotations) solution could be this:
// while there is clockwise rotation left to perform
for (; cwSteps > 0; --cwSteps){
int x = -cur->Y(); // assuming the Y axis points downwards, the new X offset is simply the old Y offset negated
int y = cur->X(); // and the new Y offset is the old X offset unmodified
cur->X(x);
cur->Y(y);
}
// if there is any counter-clockwise rotation to perform (if cwSteps was negative)
for (; cwSteps < 0; --cwSteps){
int x = cur->Y();
int y = -cur->X();
cur->X(x);
cur->Y(y);
}
}
}
private:
int centerX, centerY;
std::vector<Block> blocks;
};
Tetroid Translate(Tetroid tet, int down, int left) {
return tet.Translate(down, left);
}
Tetroid Rotate(Tetroid tet, cwSteps) {
return tet.Rotate(cwSteps);
}
and we'll need to re-implement the speculative collision checks. Given the non-mutating Translate/Rotate methods, that is simple: We just create rotated/translated copies, and test those for collision:
// test if a tetroid t would collide with the grid g if it was translated (x,y) units
if (g.Collides(Translate(t, x, y))) { ... }
// test if a tetroid t would collide with the grid g if it was rotated x times clockwise
if (g.Collides(Rotate(t, x))) { ... }
I would personally ditch the static blocks and deal with them as rows. Having a static block you are keeping a lot more information than you need.
A world is made of rows, which is an array of single squares. The squares can be either empty, or a color (or extend it if you have special blocks).
The world also owns a single active block, as you have now. The class should have a rotate and translate method. The block will obviously need to maintain a reference to the world to determine if it will collide with existing bricks or the edge of the board.
When the active block goes out of play, it will call something like world.update() which will add the pieces of the active block to the appropriate rows, clear all full rows, decide if you have lost, etc, and finally create a new active block if needed.