I have a pointer that is set to 0, then later on in the same function, inside some loops/conditions I try to re assign it.. (please read the comments)
for(Entity* tile : _originalFloorTiles)
{
for(Turns turn : pointsUpLeftDownRight)
{
if(tile->GetCollisionRect().ContainsPoint(turn.first.x, turn.first.y)){
turn.second = tile; //everything looks fine here, turn.second is still null and tile is a valid pointer
assert(turn.second); //turn.second is definitely assigned the value of tile here.
}
HAPI->DebugText("check pointsUpLeftDownRight now");//!Here's where it gets weird,
// If i hover over turn and inspect it in visual studio now, turn.second is still completely valid
// (with the assigned value of tile).
// But hovering over pointsUpLeftDownRight shows its contents for each turn..
// and inside there the current turn is a NULL pointer for turn.second!
}
}
So one moment i have assignd my pointer no problem, and the next moment the pointer doesn't seem to have changed at all.
To clarify, Turns is a lazy typedef for std::pair<Vect, Entity*> , apologies if that makes my code harder to read, it's some quickly thrown together enemy ai. I'll post the complete function below.
I'm really stumped here and not sure if i'm being an idiot or something weird is going on, would really appreciate anyone taking the time to look.
//looks for turns that the ghost can take.
void IceGhostNPC::RespondToTimePassed()
{
//Entity* t = _originalFloorTiles[0];
//test if enough time has passed since ghost last decided to look for turns
if(_lastTimeTurned < timeGetTime() - _timeBeforeSearchingForTurns)
{
//store points surrounding ghost in a way that we can associate them with a valid floor tile to move onto
std::vector<Turns> pointsUpLeftDownRight;
pointsUpLeftDownRight.push_back(
Turns(Vect(GetCenterXPos(), GetCenterYPos() - floorTileHeight), 0)); //point above
pointsUpLeftDownRight.push_back(
Turns(Vect(GetCenterXPos() - floorTileWidth, GetCenterYPos()), 0)); //point left
pointsUpLeftDownRight.push_back(
Turns(Vect(GetCenterXPos(), GetCenterYPos() + floorTileHeight), 0)); //point down
pointsUpLeftDownRight.push_back(
Turns(Vect(GetCenterXPos() + floorTileWidth, GetCenterYPos()), 0)); //point right
//look through original floor tiles,
for(Entity* tile : _originalFloorTiles)
{
//see if its possible to take a turn
for(Turns turn : pointsUpLeftDownRight)
{
if(tile->GetCollisionRect().ContainsPoint(turn.first.x, turn.first.y)){
turn.second = tile;
assert(turn.second);
}
HAPI->DebugText("check pointsUpLeftDownRight now");
}
}
//Now to make the behaviour more interesting we have the ghost randomly take one of the turns,
// ( we use associated tile to check the turn is possible, and we can also change that tile to an icy patch )
bool turnTaken = false;
do{
int turnChoice = rand() % 4;
if(pointsUpLeftDownRight[turnChoice].second == 0)
continue; //go back to top of loop if that turn had a null tile
else
{
switch(turnChoice){
case 0: //turn upwards
_moveable->SetYDirection(Controller::UP);
_moveable->SetXDirection(Controller::NONE);
break;
case 1: //turn leftwards
_moveable->SetYDirection(Controller::NONE);
_moveable->SetXDirection(Controller::LEFT);
break;
case 2: //turn downwards
_moveable->SetYDirection(Controller::DOWN);
_moveable->SetXDirection(Controller::NONE);
break;
case 3: //turn right
_moveable->SetYDirection(Controller::NONE);
_moveable->SetXDirection(Controller::RIGHT);
break;
}
turnTaken = true;
_lastTimeTurned = timeGetTime();
//ice tile up baby
}
}while(turnTaken = false);
}
FinishResponding(timeGetTime());
}
Check this line:
for(Turns turn : pointsUpLeftDownRight)
You are iterating over copies of the elements in pointsUpLeftDownRight. Whatever value you assign to that copy will be lost when the copy is destroyed (at the end of the for body). Your assignment changes a temporary.
Try with this instead:
for(Turns& turn : pointsUpLeftDownRight)
Related
#assume everything needed is included
void Robot::moveRobot()
{
//calls a random directon for robot to move in
//if direction returns false (not able to move in that direction),
//call another random direction up to 4 times, excluding the one(s)
//already called. If they all return false, do not move the robot.
//vecDir = {moveForward(), moveBackward(), moveRight(), moveLeft()}
// = {0,1,2,3} initially
vector<int> vecDir{0,1,2,3}; //vetor indicating direction to move
int num = rand() % vecDir.size();
if(num == vecDir[0])
{
//if not able to move forward, try a different random direction
if(Robot::moveForward() == false)
{
vecDir.erase(num);
//here, vector will be vecDir={1,2,3}
}
}
else if(num == vecDir[1])
{
Robot::moveBackward();
}
else if(num == vecDir[2])
{
Robot::moveRight();
}
else //num == vecDir[3]
{
Robot::moveLeft();
}
}
Hi! I'm trying to randomly call these four functions within the moveRobot() function using a vector whose size is changed depending on if a direction cannot be called. I set moveForward() to the first element, moveBackward() to the second element, etc. If any of the moveXXXX() functions are false, I want to delete that element of the array. Example code shown
Example output:
//before doing anything, vecDir = {0,1,2,3}
int num = rand() % vecDir.size(); //assume num = 1, so it calls moveBackward()
//assume moveBackward() is false, so gets rid of that element
vecDir.erase(num); //new vecDir = {0,2,3};
// vecDir(0) would be moveForward(), vecDir(1) is now moveRight(), vecDir(1) is now moveLeft()
How would I continue this process to exhaust all elements and not move a robot? I know a for loop would be involved, but I cannot think of where to use it. I am also not sure if my thinking is correct by using if else for each element. Any help is appreciated, and I apologize if the question is confusing. I can clear it up if there are any misunderstandings.
Just have a vector of function pointers, rather then numbers.
void Robot::moveRobot() {
// vector of pointers to functions to move
std::vector<bool()> moves{
moveForward(), moveBackward(), moveRight(), moveLeft()
};
// we repeat the process until any moves are available.
while (moves.size() > 0) {
// pick a random move
const int num = rand() % moves.size();
// try to move
if (moves[num]() == true) {
// yay, we moved!
break;
}
// we did not move - remove current option and repeat
moves.erase(moves.begin() + num);
}
}
void updateGameData(const char g[][SIZEX], vector<Item>& bears, vector<Item>& bombs, Item& detonator, Item& exit, const int key, string& message, int& bearsSaved, bool& bombsActive, int& moves)
{ //move bear in required direction
bool isArrowKey(const int k);
void setKeyDirection(int k, int& dx, int& dy, int& moves);
void gameLost(string& message);
assert(isArrowKey(key));
//reset message to blank
message = " "; //reset message to blank
//calculate direction of movement for given key
int dx(0), dy(0);
setKeyDirection(key, dx, dy, moves);
//save number of bears saved ready to output later for the user
//check new target position in grid and update game data (incl. bear coordinates) if move is possible
for (unsigned int bearNo = 0; bearNo < bears.size(); ++bearNo)
{
switch (g[bears.at(bearNo).y + dy][bears.at(bearNo).x + dx])
{ //...depending on what's on the target position in grid...
case TUNNEL: //can move
{
bears.at(bearNo).y += dy; //go in that Y direction
bears.at(bearNo).x += dx; //go in that X direction
}
break;
case WALL: //hit a wall and stay there
{
cout << '\a'; //beep the alarm
message = "CANNOT GO THERE!";
}
break;
case BOMB:
{
bears.at(bearNo).y += dy; //go in that Y direction
bears.at(bearNo).x += dx;
//sets the bomb to active, how to pass this through to cheat??
if (bombsActive)
{
cout << "\a";
message = "BOOM!";
bears.clear(); // Remove all bears from the vector/end the game
if (bears.size() == 0)
gameLost(message);
}
}
break;
// The pop_back function only removes the last item in a (non-empty) vector, causing the size of the vector to decrease automatically.
// To remove an item knowing its position on the vector the above has been used (bears.begin() + 0 removes the first element)
case DETONATOR:
{
bears.at(bearNo).y += dy; //go in that Y direction
bears.at(bearNo).x += dx; //go in that X direction, means the bear can walk over the detonator and that it reappears afterwards
cout << "\a";
message = "BOMBS DISABLED!";
bombs.clear();
}
break;
case EXIT:
{
// Case for the exit symbol on the grid, if a bear walks into it that bear 'escapes' by being removed from the vector, and then the amount of
// bears saved by the user is increased and incorporated into their high score
cout << "\a";
message = "BEAR IS FREE!";
bears.erase(bears.begin() + ((bears.at(bearNo).y) || (bears.at(bearNo).x))); //I'm assuming it's because of the bears.begin line, but I don't know how to otherwise change it to delete the bear at the y || x coordinate
bearsSaved++;
}
break;
}
}
}
The case for exit is where I'm having the issue - as soon as a bear reaches the same coordinate at the exit, any bear in particular seems to disappear and another bear, that may be at the other side of the map, is placed at the side of the exit.
bears.erase(bears.begin() + ((bears.at(bearNo).y) || (bears.at(bearNo).x)));
//I'm assuming it's because of the bears.begin line, but I don't know
//how to otherwise change it to delete the bear at the y || x coordinate
For reference I've included some images that better illustrate what I'm talking about. There's other code where the bears are drawn and the grid (the name of my array) are initialised as well, but I don't think they're relevant.
The before image (the # symbols are the bears, the X is the exit)
The after image, the bear near the T has disappeared, instead of the one in the exit
You are using an iterator type to delete. For proof, see http://en.cppreference.com/w/cpp/container/vector/erase
bears.begin() is an iterator referring to the first bear.
bears.begin() + 1 is an iterator referring to the second bear.
bears.begin() + 2 an iterator referring to is the third ...
Your code is
bears.erase(bears.begin() + ((bears.at(bearNo).y) || (bears.at(bearNo).x)));
so you are deleting either the first or second bear, depending on the result of this expression (something1 || something2). Logical or can only be true or false, which will then be 0 or 1 when you convert it to an int to increment the iterator returned by bears.begin()
Besides the number being wrong, it doesn't make sense. You want to delete a bear from the vector of bears. Therefore where the bear is on the game board is completely irrelevant. If you use x or y, it's probably wrong.
If you want to delete the bear at bearNo, you could use
bears.erase( bears.begin() + bearNo )
But that will cause the for loop to be wrong. The next bear moves to bearNo, but you never look at it. The easiest fix is to start at the back and iterate forward. That way, when you change the vector, you don't change anything in the part you have not yet evaluated. (This will change some of the tricks players can exploit if they know which bear is which -- if you want no change to the code you will have to handle incrementing bearNo inside the for loop based on whether or not you deleted a bear.)
#include <iostream>
#include <vector>
struct bear_type { int x, y; };
static const int XSIZE = 10;
static const int YSIZE = 10;
int main(){
char gameboard[XSIZE][YSIZE];
memset(gameboard, '\0', sizeof(gameboard));
gameboard[3][3] = 'D';
std::vector<bear_type> bears = {{ 5, 7 }, { 3, 3 }, { 0, 0 }};
for (int bearnum = bears.size()-1; bearnum >= 0; --bearnum){
const bear_type &bear = bears[bearnum];
char gamepiece = gameboard[bear.x][bear.y];
if ('D' == gamepiece){
bears.erase(bears.begin() + bearnum);
}
}
}
If you used iterator based iteration, there are some other ways to do it, like std::remove_if, but I didn't want to mess with your code too much.
I'm trying to build simple painter (i.e. points, lines, circles ...etc) in glut. Each line must have two points of Point type, so every time the user enter the left button of the mouse, the chosen command is executed. For drawing a line, I need to track how many times the user click the mouse, so this is what I'v done
if ( command == 1 ){ // drawing a line
static int count(0); // track click no.
static std::vector<Point> p;
//static Point startPoint(mouseX, mouseY);
p.push_back(Point(mouseX, mouseY));
if ( count == 1 ){
Point endPoint(mouseX, mouseY);
Point startPoint = p[0];
shapes->addLine(Line(startPoint, endPoint));
count = 0;
p.clear();
}else{
count++;
}
I'm using std::vector only to use clear() so that I can delete startPoint which I need it to be static. My question is is there a way to destroy an object without making more lines by using vector? I've tried to call the destructor but it didn't help.
You could use a unique_ptr<Point>. Then you can use reset to set or destroy the Point:
static std::unique_ptr<Point> startPoint;
if (startPoint){
Point endPoint(mouseX, mouseY);
shapes->addLine({*startPoint, endPoint});
startPoint.reset();
} else {
startPoint.reset(new Point(mouseX, mouseY));
}
Your code is fine. If you're worried about the number of lines then this is a shorter version:
if ( command == 1 ){ // drawing a line
static std::vector<Point> p;
p.push_back(Point(mouseX, mouseY));
if (p.size() == 2){
shapes->addLine(Line(p[0], p[1]));
p.clear();
}
}
Note however that using less lines is only a good thing if this improves readability. If instead it becomes harder to understand the code then it's a bad idea.
Most code is written only once but read many times... saving time when writing is not such a big deal.
In this specific case in my opinion this shorter version is easier to understand, but your mileage may vary.
This is one of those times where something like std::optional<Point> would've been nice.
But regarding to the destruction and reconstruction part, placement new can be helpful here:
static int count(0);
// ('aligned_storage' requires C++11 and '#include <type_traits>')
static std::aligned_storage<sizeof(Point), alignof(Point)>::type startPointBuffer;
Point& startPoint = *static_cast<Point*>(static_cast<void*>(&startPointBuffer));
if (count == 1) {
Point endPoint(mouseX, mouseY);
shapes->addLine(Line(startPoint, endPoint));
count = 0;
startPoint.~Point();
} else {
new (&startPoint) Point(mouseX, mouseY);
count++;
}
I have a vector holding 10 items (all of the same class for simplicity call it 'a'). What I want to do is to check that 'A' isn't either a) hiding the walls or b) hiding another 'A'. I have a collisions function that does this.
The idea is simply to have this looping class go though and move 'A' to the next position, if that potion is causing a collision then it needs to give itself a new random position on the screen. Because the screen is small, there is a good chance that the element will be put onto of another one (or on top of the wall etc). The logic of the code works well in my head - but debugging the code the object just gets stuck in the loop, and stay in the same position. 'A' is supposed to move about the screen, but it stays still!
When I comment out the Do while loop, and move the 'MoveObject()' Function up the code works perfectly the 'A's are moving about the screen. It is just when I try and add the extra functionality to it is when it doesn't work.
void Board::Loop(void){
//Display the postion of that Element.
for (unsigned int i = 0; i <= 10; ++i){
do {
if (checkCollisions(i)==true){
moveObject(i);
}
else{
objects[i]->ResetPostion();
}
}
while (checkCollisions(i) == false);
objects[i]->SetPosition(objects[i]->getXDir(),objects[i]->getYDir());
}
}
The class below is the collision detection. This I will expand later.
bool Board::checkCollisions(int index){
char boundry = map[objects[index]->getXDir()][objects[index]->getYDir()];
//There has been no collisions - therefore don't change anything
if(boundry == SYMBOL_EMPTY){
return false;
}
else{
return true;
}
}
Any help would be much appreciated. I will buy you a virtual beer :-)
Thanks
Edit:
ResetPostion -> this will give the element A a random position on the screen
moveObject -> this will look at the direction of the object and adjust the x and Y cord's appropriately.
I guess you need: do { ...
... } while (checkCollisions(i));
Also, if you have 10 elements, then i = 0; i < 10; i++
And btw. don't write if (something == true), simply if (something) or if (!something)
for (unsigned int i = 0; i <= 10; ++i){
is wrong because that's a loop for eleven items, use
for (unsigned int i = 0; i < 10; ++i){
instead.
You don't define what 'doesn't work' means, so that's all the help I can give for now.
There seems to be a lot of confusion here over basic language structure and logic flow. Writing a few very simple test apps that exercise different language features will probably help you a lot. (So will a step-thru debugger, if you have one)
do/while() is a fairly advanced feature that some people spend whole careers never using, see: do...while vs while
I recommend getting a solid foundation with while and if/else before even using for. Your first look at do should be when you've just finished a while or for loop and realize you could save a mountain of duplicate initialization code if you just changed the order of execution a bit. (Personally I don't even use do for that any more, I just use an iterator with while(true)/break since it lets me pre and post code all within a single loop)
I think this simplifies what you're trying to accomplish:
void Board::Loop(void) {
//Display the postion of that Element.
for (unsigned int i = 0; i < 10; ++i) {
while(IsGoingToCollide(i)) //check is first, do while doesn't make sense
objects[i]->ResetPosition();
moveObject(i); //same as ->SetPosition(XDir, YDir)?
//either explain difference or remove one or the other
}
}
This function name seems ambiguous to me:
bool Board::checkCollisions(int index) {
I'd recommend changing it to:
// returns true if moving to next position (based on inertia) will
// cause overlap with any other object's or structure's current location
bool Board::IsGoingToCollide(int index) {
In contrast checkCollisions() could also mean:
// returns true if there is no overlap between this object's
// current location and any other object's or structure's current location
bool Board::DidntCollide(int index) {
Final note: Double check that ->ResetPosition() puts things inside the boundaries.
Here is my code for checking if future move is legal, I have assumed its legal and copied move into mySquares array. I then call this method in the game cycle set in the form and in the timer handler which is:
canvas->drawGrid();
testBlock->drawBlock();
testBlock->moveDown();//this method has checkBounds for when hit sides, top & bottom
if(newBlock->canMoveDown()==false)
{
newBlock->addMySelfToGameBoard();
mainGameBoard->updateGrid();
}
//timer1 handler finish
bool TTetrisBlock::canMoveDown()
{
array<Point>^ temporaryCopy = gcnew array<Point>(4);
bool canGoDown = true;
for(int i=0;i<mySquares->Length;i++)
{
//Set future move
temporaryCopy[i].X = mySquares[i].X;
temporaryCopy[i].Y = mySquares[i].Y+1;
}
//Check if future move cells are full, if not assign values to mySquares
//Check if future move is legal
for(int j=0;j<temporaryCopy->Length;j++)
{
if(gameBoard->isCellOccupied(temporaryCopy[j].X,temporaryCopy[j].Y) == true)
{
mySquares[j].X = temporaryCopy[j].X;
mySquares[j].Y = temporaryCopy[j].Y;
}
}
return canGoDown;
}
//end of moveDown
in my gameboard class i have the method which checks if TCell is occupied or not. TGameBoar holds an array of TCells which has a color and bool isOccupied = false;
bool TGameBoard::isCellOccupied(int c,int r)
{
//Checks if TCell is occupied
return myGrid[c,r]->getIsOccupied();
}
It Crashes and indicates here was the problem, Im currently learning C++ at school. I would appreciate some help. I am also struggling with the Keydown for moving left and right using e->KeyData == Keys::Left) etc. and creating a newblock when gone through loop.
I have my project rar if you want to check it out. I have all the classes done, its just putting it together is the hard bit.
Project Tetris
I see three problems.
First you should only move mySquares when isCellOccupied returns false (not true as you currently have it). I suspect this is the cause of your crash as it looks like you will be moving a block into a cell that is already occupied.
Second, when isCellOccupied returns true you should set canGoDown to false and break out of your for loop (or better yet, make canGoDown (==true) an additional condition of your for loop i.e. j < temporaryCopy->Length && canGoDown). As it is, your function always return true because it is never set to false and that can't be right.
Just making an assumption here, but don't all mySquares consist of 4 elements? You are initializing temporaryCopy with 4 elements but it isn't clear whether mySquares has 4 elements. If not, this could be dangerous as in your first loop you are looping on mySquares->Length and addressing temporaryCopy with that index value, which could be out of range. And then later doing the opposite. It might be better to use a constant (4) in all all loops or better yet, always use mySquares->Length (especially when creating the temporaryCopy array) to ensure that both arrays contain the same number of elements.