I recently started with C++; I'm an hobby programmer, and I know a bit of Python.
I programmed a little snake. I wanted to insert another snake guided by the computer.
I decided to put the possible direction that the snake can take in an enum:
enum directions{UP, DOWN, RIGHT, LEFT, IN, OUT, FW, RW,NONE};
void fill_map(std::map<directions,V4> &map_vec);
void fill_map(std::map<int, directions*> &map_dir);
void fill_map(std::map<directions,directions> &map);
and map the enum for the needed function:
void fill_map(std::map<directions,V4> &map_vec){
map_vec[UP] = V4(0,1,0,0);
map_vec[DOWN] = V4(0,-1,0,0);
//others
}
void fill_map(std::map<directions, directions> &map){
map[UP]= DOWN;
map[DOWN]= UP;
//others
}
void fill_map_axis(std::map<int, directions*> &map_dir){
directions array_x[2] = {RIGHT,LEFT};
map_dir[0] = array_x;
directions array_y[2] = {UP,DOWN};//store the array
map_dir[1] = array_y;
directions array_z[2] = {FW,RW};//store the array
map_dir[2] = array_z;
directions array_w[2] = {IN,OUT};//store the array
map_dir[3] = array_w;
}
The fill_map functions are called in the snake constructor.
Basically what I wanted to do in the fill_map_axis is to map an integer corresponding to the index of the coordinate (0 coord x, 1 coord y etc) and map the two directions that move along those axis.
So I stored an array of two directions.
Now I call the function:
directions SnakeCPU::find_dir(V4 point){
//point is the target point
directions dir;
int index = get_coord_index(point); //get the index where to move
double diff = head_pos[index]-point[index]; //find the difference between the head and the target point
directions* axis = dir_coords[index]; //call the map containing the directions stored in an array.
if(diff<0.){
dir = *axis; //use the first
}
else if(diff>0.) {
axis++;
dir = *axis; //use the second
}
else{
dir = NONE;
}
return dir;
}
Although the map are initialized in the Snake constructor, it seems that the returned value from the pointer axis is a random memory block.
So my question: do you see a mistake in the code? did I used the pointer axis with sense?
I'm really not expert with pointer; in Python the map is instantiated with a dictionary like this:
dir_coords = {0:[LEFT,RIGHT], ...}
so I just need to call it:
axis = dir_coords[index]
dir = axis[0]
#or
dir = axis[1]
edit:
Snake constructor:
Snake::Snake()
{
fill_map(dir_vectors);
fill_map(dir_coords);
fill_map(opposite_dir);
head_pos = V4(0.,0.,0.,0.);
//other stuff...
}
Just a shot in the blue, here is how I would design this.
#include <map>
enum EDirection { NONE = 0, UP, DOWN, RIGHT, LEFT, IN, OUT, FW, RW };
typedef std::map<EDirection, V4> DirectionMap;
typedef std::pair<EDirection, EDirection> DirectionPair;
typedef std::map<int, DirectionPair> PairMap;
extern const DirectionMap map_vec {
{ UP, (0, 1, 0, 0) },
{ DOWN, (0,-1, 0, 0) },
// ...
}; // using C++11 initialization lists for convenience
extern const PairMap map_dir {
{ 0, { RIGHT, LEFT } },
{ 1, { UP, DOWN } },
// ...
};
Here I decided to make map_vec and map_dir global constants, because I gathered that that's essentially what they are. To initialize those, I rely on the new C++11 initialization syntax. If that's not an option, we can also fill the map in the traditional way:
PairMap map_dir;
map_dir.insert(std::make_pair(0, DirectionPair(RIGHT, LEFT)));
map_dir.insert(std::make_pair(1, DirectionPair(UP, DOWN)));
// ...
DirectionMap map_vec;
map_vec.insert(std::make_pair(UP, V4(0, 1, 0, 0)));
map_vec.insert(std::make_pair(DOWN, V4(0,-1, 0, 0)));
// ...
(Yes, you can also write map_dir[0] = DirectionPair(RIGHT, LEFT)'. I don't like the square brackets, though, they feel too violent for my taste.)
As Kerrek said, you filled map_dir with automatic scoped variables, which means they are automatically destroyed when the function ended.
void fill_map_axis(std::map<int, directions*> &map_dir){
//This variable will be destroyed when the function ends
directions array_x[2] = {RIGHT,LEFT};
map_dir[0] = array_x; //should have a warning at least, probably not compile
What you probably want is actual directions in the map
void fill_map_axis(std::map<int, std::pair<directions, directions> > &map_dir){
//This variable will be destroyed when the function ends
std::pair<directions, directions> array_x = {RIGHT,LEFT};
map_dir[0] = array_x; //makes a copy
Related
Firstly, while not new to programming, I am very new to C++, so please bear with me.
I am using the Raylib library to attempt making a particle system for a game.
This consists of a struct with a few private members and public functions:
struct Particle {
Particle() {
mPosVector = {(float)GetMouseX(), (float)GetMouseY()};
mVelVector = {(float)GetRandomValue(15, 70)/100, (float)GetRandomValue(15, 70)/100};
mSize = GetRandomValue(5, 15);
}
void update(double deltaTime) {
mPosVector.x += mVelVector.x;
mPosVector.y += mVelVector.y;
}
void draw() {
DrawRectangleV(mPosVector, {(float)mSize, (float)mSize}, WHITE);
}
private:
Vector2 mPosVector;
Vector2 mVelVector;
int mSize;
};
The Vector2 type is defined by Raylib:
struct Vector2 {
float x;
float y;
};
In my main function I have an std::vector storing Particles. A particle gets added when the left mouse button is pressed. I loop through the Particles vector twice, once for updating position based on velocity and once for drawing. I was originally doing these both in one loop, but was still getting the problem that I will get onto, so tried it this way.
This is the current code:
std::vector<Particle> particles = {Particle()};
while (!WindowShouldClose()) {
deltaTime = GetFrameTime();
if (IsMouseButtonDown(0)) {
particles.push_back(Particle());
}
for (Particle part : particles) {
part.update(deltaTime);
}
BeginDrawing();
ClearBackground(BLACK);
DrawFPS(10, 10);
DrawText((numToString<double>(deltaTime*1000).substr(0, 5) + "ms").c_str(), 10, 40, 20, WHITE);
for (Particle part : particles) {
part.draw();
}
EndDrawing();
So, my problem: While particles are being instantiated as expected while pressing the left mouse button and being drawn, for some reason their positions are not being updated by their velocity. I have tried printing debug information to the console, such as the velocity, and it is as expected, but for some unknown reason to me (probably just me being stupid) their positions aren't being updated.
Any help would be greatly appreciated.
for (Particle part : particles) {
part.update(deltaTime);
}
this is making a copy of each entry , you need
for (Particle &part : particles) {
part.update(deltaTime);
}
to get a reference to the object in the vector to update it in place
To understand, think that the ranged for is just short hand for this
for(int i = 0; i < particles.size(); i++)
{
// this line copies the value
particle p = particles[i];
}
whereas the one with & in it does
for(int i = 0; i < particles.size9); i++)
{
// this line gets a reference to the ith entry
particle &p = particles[i];
}
Its nothing special to do with the ranged for loop.
I am making my homework and I have 2 functions unionRect and intersectRect.I am creating a set of my first class Rectangle from a file. I have to return the union and intersect rectangle of that oColl.I am having a problem with returning the values because the object returns two 0 values.
I have tried to return different things but I couldn't do it.
This is from the first class Rectangle
Rectangle unionRect(const Rectangle& rec) const {
int ux1, ux2, uy1, uy2;
ux1 = min(ix1, rec.ix1);
uy1 = min(iy1, rec.iy1);
ux2 = max(ix2, rec.ix2);
uy2 = max(iy2, rec.iy2);
Rectangle a(ux1, ux2, uy1, uy2);
return a;
}
This is second class RectangleCollection function to read from file
RectangleCollection(const string& strFileName) {
ifstream ifile(strFileName.data());
copy(istream_iterator<Rectangle>(ifile), istream_iterator<Rectangle>(), inserter(oColl,oColl.begin()));
};
this is my RectangleCollection class function for union Rect
Rectangle calcUnionColl() {
set<Rectangle>::iterator it;
Rectangle a;
for (it = oColl.begin(); it != oColl.end(); ++it) {
a = unionRect(*it);
}
return a;
}
and the .txt file is
5 5 10 10
6 6 12 12
but when i call calcUnionColl it returns me
x1:0 x2:6 y1:0 y2:12
I expect the output to be x1:5 x2:6 y1:5 y2:12.
Thank you in advance!
You are not union'ing all of the Rectangles together that are in the collection. You are union'ing each individual Rectangle only with the one Rectangle that calcUnionColl() is called on, and then you return the result of just the last union that was performed.
Try something more like this instead:
class Rectangle {
public:
...
Rectangle unionRect(const Rectangle& rec) const;
...
}
Rectangle Rectangle::unionRect(const Rectangle& rec) const {
int ux1, ux2, uy1, uy2;
ux1 = std::min(ix1, rec.ix1);
uy1 = std::min(iy1, rec.iy1);
ux2 = std::max(ix2, rec.ix2);
uy2 = std::max(iy2, rec.iy2);
return Rectangle(ux1, ux2, uy1, uy2);
}
...
class RectangleCollection {
public:
...
Rectangle calcUnionColl() const;
...
}
Rectangle RectangleCollection::calcUnionColl() const {
Rectangle a;
if (!oColl.empty()) {
std::set<Rectangle>::iterator it = oColl.begin();
a = *it++;
while (it != oColl.end()) {
a = a.unionRect(*it++);
}
}
return a;
}
The answer from Remy Lebeau has a minor issue. If oColl is empty, calcUnionColl will return the the default-constructed value of a. So if the default-constructed value of a is x1:0, x2:0, y1:0, y2:0 then if calcUnionColl returns that value, it is impossible to know if that is the actual union of the values in oColl, or if
oColl was empty.
A common trick when finding the the maximum value of multiple ints is to initialize the running maximum with INT_MIN. And in the same way, when finding a minimum value, we initialize the the running minimum with INT_MAX.
Finding the union of rectagles is nothing more than finding the min/max value of the rectangles corner coordinates, so we can use the above trick.
For instance if a = {x1:INT_MAX, x2:INT_MIN, y1:INT_MAX, y2:INT_MIN}, then when calculating the union of a and any rectangle b, we will have:
b.x1 <= a.x1 // a.x1 == INT_MAX
b.y1 <= a.y1 // a.y1 == INT_MAX
b.x2 >= a.x1 // a.x1 == INT_MIN
b.y2 >= a.y2 // a.y2 == INT_MIN
So the union of a and b in this case will be b
Making use of this in calcUnionColl():
// I assume the data-type for your rectangle coordinates is `int`.
// If you use another datatype, change this accoringly.
Rectangle RectangleCollection::calcUnionColl() const {
int i_min = std::numeric_limit<int>::min(); // c++ way of getting INT_MIN
int i_max = std::numeric_limit<int>::max(); // c++ way of getting INT_MAX
set<Rectangle>::iterator it;
Rectangle a(i_max, i_min, i_max, i_min);
for (it = oColl.begin(); it != oColl.end(); ++it) {
a = a.unionRect(*it);
}
return a;
}
Now, if oColl is empty, calcUnionColl() will return x1:INT_MAX, x2:INT_MIN, y1:INT_MAX, y2:INT_MIN. This should be an invalid value for a rectangle since x1>x2 and y1>y2, and should be easy to test for.
Sometimes you don't even have to test for it, since it often is an invalid value "that makes sense" for further calculations.
I'm making a small OpenGL program for my intro to C++ class in Uni. I have a program that is complete but I want to change it up a bit to make it more unique. I have a Cube class:
class Cube {
public:
Cube(Mesh* mesh, Texture2D* texture, float x, float y, float z);
~Cube();
void Draw();
void Update(float rSpeed);
Vector3 position;
private:
GLfloat rotationSpeed;
Vector3 rotationVector;
Mesh* _mesh;
Texture2D* _texture;
};
I then create an array of type Cube:
Cube* cubes[CUBE_AMOUNT];
I then fill each index of this array with data to draw the cube on screen later in the program:
for (int i = 0; i < CUBE_AMOUNT; i++) {
float x = ((rand() % 400) / 10.0f) - 20.0f;
float y = ((rand() % 200) / 10.0f) - 10.0f;
float z = -(rand() % 1000);
if (i % 2 == 1) {
cubes[i] = new Cube(cubeMesh, textureStars, x, y, z);
}
else {
cubes[i] = new Cube(cubeMesh, texturePenguins, x, y, z);
}
}
With this new thing I want to add to the program, I want to check whether an index of cubes[] has been filled with the data yet. However I keep getting exceptions when running. I have tried to check whether cubes[i] is equal to nullptr, and tried checking whether it is NULL too, but neither seem to match.
Sorry for any errors in terminology that I used. New to C++, and having come from only doing Python before this, it is confusing!
Solution:
When I create the array, I changed it to Cube* cubes[CUBE_AMOUNT] = { NULL }, and now when checking the array, cubes[i] == NULL!
If cubes is not a global variable, you can use:
Cube* cubes[CUBE_AMOUNT] = {};
to initialize all the elements to nullptr.
You can also use:
std::vector<std::unique_ptr<Cube>> cubes(CUBE_AMOUNT);
to remove the burden of having to deallocate dynamic memory in your code.
In either case, can use:
if ( cubes[index] )
{
// Got a valid pointer. Use it.
}
Your cubes variable is not automatically initialized with null_ptr's. Until you either fill it with null_ptr's or good pointers it initially points to random garbage.
I think this would work
//This bit should check if theres anything stored currently.
cout << "\nWhich Slot would you like to store the informaton in ?(1-10)";
cin >> i;
i--;
if (information[i] != NULL){
// Already written
cout << "THERES SOMETHING HERE";
}
else{
cout << "\nEMPTY!!!!!!!!!";
}
I'm writing a Snake game in C++, I have a structure for a section of the snake which contains, data such as x position, y position, direction etc.
I have it all working, setting all the data to integers, I just would like to change some of the data types to enum's because it looks a lot neater and easier to understand.
I've tried lots and looked online but I can't seem to find anything.
This is some of the Structure:
struct SnakeSection
{
int snakePosX;
int snakePosY;
int SectionType;
// Tail = 0, Body = 1, Head = 2
int animation;
enum Direction
{
Up = 0,
Right = 1,
Down = 2,
Left = 3
};
};
My attempt at trying to pass one of the Directions to another function:
void PlayerSnake::createSnake()
{
// Parameters are direction, x and y pos, the blocks are 32x32
addSection(SnakeSection::Direction::Right, mStartX, mStartY, 2);
}
Then I tried setting the direction to the one passed in in that function:
void PlayerSnake::addSection(SnakeSection::Direction dir, int x, int y, int type)
{
//Create a temp variable of a Snake part structure
SnakeSection bufferSnake;
bufferSnake.Direction = dir;
bufferSnake.animation = 0;
//is it head tail or what? This is stored in the Snake section struct
//TODO Add different sprites for each section
bufferSnake.SectionType = type;
//assign the x and y position parameters to the snake section struct buffer
bufferSnake.snakePosX = x;
bufferSnake.snakePosY = y;
//Push the new section to the back of the snake.
lSnake.push_back(bufferSnake);
}
error: invalid use of enum SnakeSection::Direction
Thanks
The error on the following line ...
bufferSnake.Direction = dir;
... is reasoned, that besides declaring the enum type, you'll still have to have a class member variable to store it:
struct SnakeSection
{
int snakePosX;
int snakePosY;
int SectionType;
// Tail = 0, Body = 1, Head = 2
int animation;
enum Direction
{
Up = 0,
Right = 1,
Down = 2,
Left = 3
};
Direction direction_; // <<<<<<<<<<<<<< THAT'S WHAT'S MISSING IN YOUR CODE
};
And refer to
bufferSnake.direction_= dir; // <<<<<<<<<<<<<< THAT'S THE MEMBER VARIABLE YOU'LL
// HAVE TO REFER TO!
I have loaded an array from a text file, which contains positions of objects, and it looks like this:
0,0,0,5
0,5,0,0
0,0,5,0
0,5,5,0
The object looks like this:
struct object
{
int x, y, value;
}
Where x,y are coordinates, and value is 1 or 0 (it tells if an object was "picked", all objects have 1 at the beginning). Objects are stored in an array object obj_array[5].
To draw them, I use this function:
(BOARD_Y and BOARD_Y is size of the array, here is 4x4)
void draw_board(){
for (int iy = 0; iy < BOARD_Y; iy++) {
for (int ix = 0; ix < BOARD_X; ix++) {
if ( (board[iy][ix] == 5) )
{
glPushMatrix();
glTranslatef( ix, iy, 0 );
glutSolidCube(1);
glPopMatrix();
}
}
}
}
And it draws all of them perfectly. But I want to skip drawing an object, if its value is 0 (the object was picked by a player). How can I do this?
Okey, I can see what's going on; you've complicated the things a bit. There's no way to access the arbitrary object just from this loop, apart from pretty stupid comparison of position:
if ( (board[iy][ix] == 5) ) {
for (auto const& obj : objects) {
if (obj.x == ix && obj.y == iy) {
// obj is the object in given position
// ...
break;
}
}
}
Don't do that.
Instead either store some reference to the objects on the board. By reference I mean (not limited to!):
An unique object ID
Pointer to the object
Then, you will be able to access the object residing on given tile much faster and easier. If you want examples, drop a comment, but I think both options are fairly easy to implement.
If you still want to hold these "5" inside, change board to array of structs. And oh, please use std::array instead of int**.
Here's the example
using id_t = unsigned;
std::map<id_t, object> objects;
constexpr std::size_t size_x = 4, size_y = 4;
std::array<id_t, size_x * size_y> board;
Let's assume that id equal to 0 means that the object is not there.
Now you can access the specific object by:
unsigned x, y;
id_t obj_id = board[x + size_x * y];
if (obj_id != 0) // is there any object?
if (objects[obj_id].value != 0) // is its value equal to 0?
// ...
And set it by board[...] = obj;
Easy way to generate unique id for every object is just to increment the counter.
id_t last_id = 1;
objects[last_id++] = obj_id;