Erasing Element From a Vector Erases Multiple (C++) - c++

I've been building a small Roguelike ASCII game that uses the console to build my limited knowledge of C++. I have it set up so when the player object's coordinates are the same as an item(which is stored in a vector), it removes the item and adds 10 to the player's HP.
The program starts out with 9 items in the room. BUT, whenever I get the item, it deletes the first 5 items in the vector(But they're still being drawn on the screen for some reason), and adds 50 to the health. So I'm left with 4 items. The next time I get an item, it removes the first 2 and adds 20 to the health. Now I'm left with 2 items. The last two times, it removes one item at a time.
So, essentially, the vector is being split in half each time I get an item. I've narrowed down the problem through a lot of debugging, and I'm pretty sure it's coming from this chunk of code:
for(int i=0;i<ia.size();i++) //If the player touches an item
{
if(grid[p.getY()][p.getX()]==itm)
{
hp+=10;
cout << "Item "<<i<<" removed\n";
swap(ia[i], ia.back());
ia.pop_back();
system("pause");
}
}
I'm still trying to figure out vectors, so if someone could tell me what I'm doing wrong, I would really appreciate it. :)

There're comments suggesting erase(), but be aware that it will copy later array elements one by one to fill in the index being vacated: your swap approach may be massively faster and is better if you don't care about element order.
One issue is that you swap the back() element into the position occupied by the element you're removing, then increment the iterator to test the next ia index. This means that the items copied from the back() are never themselves tested - for that you want to avoid the i++ after doing a swap. Of course if only one item can ever occupy a grid position, you can just break as abiessu commented....
Here's some code that works as I think you want:
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> ia{ 10, 20, 30, 40, 50, 60, 70 };
for(size_t i=0;i<ia.size(); ) //If the player touches an item
{
if(ia[i] % 20)
{
cout << "Item "<<i<<" removed\n";
swap(ia[i], ia.back());
ia.pop_back();
continue;
}
else
++i;
}
for (size_t i = 0; i < ia.size(); ++i)
std::cout << '[' << i << "] " << ia[i] << '\n';
}
See it at http://ideone.com/JJ6CE6
That said, for some data types ia[i] = ia.back(); will be faster than swap - for others slower (if they use and benefit from C++11 style "move"). You may also want to skip any self-swap/assignment if i == size() - 1 (at least check that the array element type supports that safely).

Related

Show the percentage of process completion in a C++ program

I am making a set of C++ library as a part of my Data Structures assignment, which includes custom implementation of vector, sorting algorithms, stacks, etc. I am supposed to work on the running time of sorting algorithms, bubble sort, selection sort, quick sort, etc., which are part of my library.
Now the data set given to test the algorithms in of the order of 10^6. I ran bubble sort on a data of 2*10^6 elements, and it took about 138 minutes for the program to run, and in all this time, I did not know if my sorting algorithm is working correctly or not, or is it even working or not. I would want to add another feature to the sorting functions, i.e they could display the percentage of sorting done, and I think this is possible, since algorithms like bubble sort are deterministic.
I need a message like something to appear as soon as I start the process:
Bubble sort under progress. Done: 17%
This percentage is to be determined by the algorithm. Consider the example of bubble sort with 10000 elements. If you look at the bubble sort algorithm(refer here: https://en.wikipedia.org/wiki/Bubble_sort), it has 2 loops, and after each iteration of the main loop, one element is fixed to its correct position in the sorted array. So after like 1 iteration, the percentage should increase by 0.01%.
Though this percentage calculation has a problem that in this case, the time for the percentage to increase keeps on decreasing, but something like this would do.
Also, this number should increase as and when required, on the same place. But I have no idea how to implement it.
You can pass a callback function of a generic type to your bubblesort function and call the function at reasonable intervals.
This will impact performance, but this shouldn't be a concern when you're using bubblesort anyway.
First we'll need some includes:
#include <iostream>
#include <vector>
#include <random>
#include <chrono>
And then the bubblesort function, which I essentially took from wikipedia: https://en.wikipedia.org/wiki/Bubble_sort#Optimizing_bubble_sort
template <typename T, typename Func>
void bubblesort(std::vector<T> &v, Func callback) {
size_t const len = v.size();
size_t n = v.size();
while(n > 0) {
size_t newn = 0;
for(size_t i = 1; i <= n-1; ++i) {
if (v[i - 1] > v[i]) {
std::swap(v[i-1], v[i]);
newn = i;
}
}
n = newn;
callback(100-static_cast<int>(n*100/len));
}
}
We will call the given callback function (or use operator() on an object) whenever it's done sorting in one element.
The parameter we pass is an integer percentage of how far we've come. Note that due to integer arithmetic you cannot change the order of operations with n*100/v.size() or else it would always result in 0, since n will always be smaller than v.size();
using namespace std::chrono; //to avoid the horrible line becoming even longer
int main() {
std::vector<int> vec;
/* fill vector with some data */
std::mt19937 generator(static_cast<unsigned long>(duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count())); //oh god
for(int i = 0; i < 100000; ++i) {
vec.push_back(static_cast<int>(generator()));
}
For initialization we get create a random number generator and seed it with the current time. Then we put some elements in the vector.
char const *prefix = "Bubble sort under progress. Done: ";
int lastp = -1;
bubblesort(vec, [&lastp,prefix](int p){
//if progress has changed, update it
if(p != lastp) {
lastp = p;
std::cout << "\r" << prefix << p << "%" << std::flush;
/*std::flush is needed when we don't start a new line
'\r' puts the cursor to the start of the line */
}
});
std::cout << "\r" << prefix << "100%" << std::endl;
//make sure we always end on 100% and end the line
}
Now the core part: we pass a C++ lambda function to our bubblesort function as a callback. Our bubblesort function will then call this lambda with the percentage value and write it to the screen.
And voilĂ , we got ourselves some neat output:
https://youtu.be/iFGN8Wy9T3o
Closing notes:
You can of course integrate the lamda function into the sort function itself, however I would not recommend this as you lose a lot of flexibility. But it's a design choice that's up to you - if you don't need the flexibility, just hardcode it.
The percentage is not very accurate, in fact knowing you're at 20% (and how long it took to get there) does not tell you much at all about the time it will take to get to 100% as it could very well be, that the last 20% of the vector were sorted (and thus were quick to sort with bubblesort - O(n)), but the remaining 80% are random, and take O(n^2) to sort.
In fact all it tells you is that you're making progress, but that's all you wanted in the first place so I guess that's okay.
If you want a more accurate percentage adjust your program like this:
#include <iomanip>
/* ... */
callback(10000-static_cast<int>(n*10000/len));
/* ... */
std::cout.fill('0'); //to fill leading zero of p%100
std::cout << "\r" << prefix << p/100 << "." << std::setw(2) << p%100 << "%" << std::flush;
If you decide to use floating point values instead remember to clear remnant characters from previous outputs - "\r" only resets the cursor position, but does not clear the line.
Use std::cout.precision(3); for a fixed precision or write some spaces after your message to clear previous runs.
For the special case of bubblesort, you can take the number of elements you have, then divide that by 100. If you have 552 elements, then you will get 5. (integers make sense to work with). Then, have a counter in your loop. If the counter is a multiple of 5, (you've so far sorted 5 elements) then you can increase the percentage by 1 and print it. As far as printing it so that the percentage appears on the spot instead of printing below, you can print backspaces! Either that or try using the ncurses library, though that might be overkill. Finally, a different way to do this might be to use a linux style progress bar that is 50 characters long or something similar.

How to chain delete pairs from a vector in C++?

I have this text file where I am reading each line into a std::vector<std::pair>,
handgun bullets
bullets ore
bombs ore
turret bullets
The first item depends on the second item. And I am writing a delete function where, when the user inputs an item name, it deletes the pair containing the item as second item. Since there is a dependency relationship, the item depending on the deleted item should also be deleted since it is no longer usable. For example, if I delete ore, bullets and bombs can no longer be usable because ore is unavailable. Consequently, handgun and turret should also be removed since those pairs are dependent on bullets which is dependent on ore i.e. indirect dependency on ore. This chain should continue until all dependent pairs are deleted.
I tried to do this for the current example and came with the following pseudo code,
for vector_iterator_1 = vector.begin to vector.end
{
if user_input == vector_iterator_1->second
{
for vector_iterator_2 = vector.begin to vector.end
{
if vector_iterator_1->first == vector_iterator_2->second
{
delete pair_of_vector_iterator_2
}
}
delete pair_of_vector_iterator_1
}
}
Not a very good algorithm, but it explains what I intend to do. In the example, if I delete ore, then bullets and bombs gets deleted too. Subsequently, pairs depending on ore and bullets will also be deleted (bombs have no dependency). Since, there is only one single length chain (ore-->bullets), there is only one nested for loop to check for it. However, there may be zero or large number of dependencies in a single chain resulting in many or no nested for loops. So, this is not a very practical solution. How would I do this with a chain of dependencies of variable length? Please tell me. Thank you for your patience.
P. S. : If you didn't understand my question, please let me know.
One (naive) solution:
Create a queue of items-to-delete
Add in your first item (user-entered)
While(!empty(items-to-delete)) loop through your vector
Every time you find your current item as the second-item in your list, add the first-item to your queue and then delete that pair
Easy optimizations:
Ensure you never add an item to the queue twice (hash table/etc)
personally, I would just use the standard library for removal:
vector.erase(remove_if(vector.begin(), vector.end(), [](pair<string,string> pair){ return pair.second == "ore"; }));
remove_if() give you an iterator to the elements matching the criteria, so you could have a function that takes in a .second value to erase, and erases matching pairs while saving the .first values in those being erased. From there, you could loop until nothing is removed.
For your solution, it might be simpler to use find_if inside a loop, but either way, the standard library has some useful things you could use here.
I couldn't help myself to not write a solution using standard algorithms and data structures from the C++ standard library. I'm using a std::set to remember which objects we delete (I prefer it since it has log-access and does not contain duplicates). The algorithm is basically the same as the one proposed by #Beth Crane.
#include <iostream>
#include <vector>
#include <utility>
#include <algorithm>
#include <string>
#include <set>
int main()
{
std::vector<std::pair<std::string, std::string>> v
{ {"handgun", "bullets"},
{"bullets", "ore"},
{"bombs", "ore"},
{"turret", "bullets"}};
std::cout << "Initially: " << std::endl << std::endl;
for (auto && elem : v)
std::cout << elem.first << " " << elem.second << std::endl;
// let's remove "ore", this is our "queue"
std::set<std::string> to_remove{"bullets"}; // unique elements
while (!to_remove.empty()) // loop as long we still have elements to remove
{
// "pop" an element, then remove it via erase-remove idiom
// and a bit of lambdas
std::string obj = *to_remove.begin();
v.erase(
std::remove_if(v.begin(), v.end(),
[&to_remove](const std::pair<const std::string,
const std::string>& elem)->bool
{
// is it on the first position?
if (to_remove.find(elem.first) != to_remove.end())
{
return true;
}
// is it in the queue?
if (to_remove.find(elem.second) != to_remove.end())
{
// add the first element in the queue
to_remove.insert(elem.first);
return true;
}
return false;
}
),
v.end()
);
to_remove.erase(obj); // delete it from the queue once we're done with it
}
std::cout << std::endl << "Finally: " << std::endl << std::endl;
for (auto && elem : v)
std::cout << elem.first << " " << elem.second << std::endl;
}
#vsoftco I looked at Beth's answer and went off to try the solution. I did not see your code until I came back. On closer examination of your code, I see that we have done pretty much the same thing. Here's what I did,
std::string Node;
std::cout << "Enter Node to delete: ";
std::cin >> Node;
std::queue<std::string> Deleted_Nodes;
Deleted_Nodes.push(Node);
while(!Deleted_Nodes.empty())
{
std::vector<std::pair<std::string, std::string>>::iterator Current_Iterator = Pair_Vector.begin(), Temporary_Iterator;
while(Current_Iterator != Pair_Vector.end())
{
Temporary_Iterator = Current_Iterator;
Temporary_Iterator++;
if(Deleted_Nodes.front() == Current_Iterator->second)
{
Deleted_Nodes.push(Current_Iterator->first);
Pair_Vector.erase(Current_Iterator);
}
else if(Deleted_Nodes.front() == Current_Iterator->first)
{
Pair_Vector.erase(Current_Iterator);
}
Current_Iterator = Temporary_Iterator;
}
Deleted_Nodes.pop();
}
To answer your question in the comment of my question, that's what the else if statement is for. It's supposed to be a directed graph so it removes only next level elements in the chain. Higher level elements are not touched.
1 --> 2 --> 3 --> 4 --> 5
Remove 5: 1 --> 2 --> 3 --> 4
Remove 3: 1 --> 2 4 5
Remove 1: 2 3 4 5
Although my code is similar to yours, I am no expert in C++ (yet). Tell me if I made any mistakes or overlooked anything. Thanks. :-)

Out of Range error

The question is:
Jay had borrowed a friend's coffee mug and somehow lost it. As his friend will be extremely angry when he finds out about it, Jay has decided to buy his friend a replacement mug to try to control the damage.
Unfortunately, Jay does not remember the color of the mug he had borrowed. He only knows that the color was one of White, Black, Blue, Red or Yellow.
Jay goes around his office asking his colleagues if they are able to recall the color but his friends don't seem to remember the color of the mug either. What they do know is what color the mug definitely was not.
Based on this information, help Jay figure out what the color of the mug was.
The way I'm going about this:
I create a vector of all possible colors: White, Black, Blue, Red or Yellow. Then ask the user to enter the number of colleagues he will be questioning. Then take the color suggestions, and for every entry I compare it against the vector. If it is in there, I pop the color out. Eventually only one color will be left in the vector which is the color of the lost mug.
My issue:
I get an out of bound error after entering the first color and I am not able to figure out why. The exact error is:
terminate called after throwing an instance of 'std::out_of_range'
what(): vector::_M_range_check
Abort (core dumped)
My code is:
#include <iostream>
#include <string>
#include <algorithm>
#include <climits>
#include <stdio.h>
#include <vector>
using namespace std;
int main(int argv, char* argc[])
{
string color;
vector<string> colorVector;
colorVector.push_back("White");
colorVector.push_back("Black");
colorVector.push_back("Blue");
colorVector.push_back("Red");
colorVector.push_back("Yellow");
int numColleagues;
cout<< "Please enter the number of Colleagues" << endl;
cin >> numColleagues;
cout<< "Please enter each suggested color" << endl;
int counter = 0;
while (counter < numColleagues) {
getline(cin, color);
counter++;
for (int i = 0; i < 5; i++) {
if (colorVector.at(i) == color) {
colorVector.erase(colorVector.begin() + i);
}
}
}
return 0;
}
You are erasing elements of your vector, but you wish to access all five elements (the loop runs from 0 to 5). So, let's say you remove the first and then you try to access the element at position 4. Out of bounds!
So change your loop to this:
colorVector.erase(std::remove(colorVector.begin(),
colorVector.end(), color), colorVector.end());
More on erase-remove idiom.
When you call vector::erase, it will return an iterator pointing to the new location of the element that followed the erased element. So if we erase this element:
1 2 3 4 5 6
^
Our iterator will automatically update to point to the 5. So we don't have to increment the iterator again, it's already sort of incremented. With that in mind:
auto it = colorVector.begin();
for (; it != colorVector.end(); /* do not increment it */ )
{
if (*it == color)
{
it = colorVector.erase(it); // erase and update
}
else
{
++it; // just update
}
}
Of course even better to just use the algorithms which are less error prone
colorVector.erase(
std::remove(colorVector.begin(), colorVector.end(), color),
colorVector.end()
);
You are modifying colorVector during an iteration.
As soon as you remove one of the colors, the vector is suddenly only 4 items in length. Now when you try to go to the 5th item (which was safe to do before the deletion) - it crashes
Try this:
for (int i = 0; i < colorVector.size(); i++) {
if (colorVector.at(i) == color) {
colorVector.erase(colorVector.begin() + i);
--i;
}
}
By stopping at colorVector.size instead of a hard-coded 5 you are ensured that the list never goes out of bounds.
Edit:
Added --i statement to avoid skipping next one
Edit2:
As the comments below state, it is generally a bad idea to remove items from the array you're currently iterating from.

PushBack() element from one Vector into another. Remove from originating Vector

Since This Post didn't exactly answer my question I'm going to start a new one.
I'm encoutering a problem when moving an element from one vector to another.
The originating vector is a simple deck of cards and the end vector is supposed to represent cards in the player's hand.
I'm using Hand.push_back(Deck.back()) to copy the "top" card and then Deck.pop_back() to remove it from the Deck vector.
Strangely though the next time I call the Draw Card function it "draws" the same card that should have been removed last turn.
I'm guessing it's a problem with resizing the vector? Should I try Deck.resize(Deck.size() - 1) after each pop_back()?
void Player::DrawStartingHand(int num)
{
for(int i = 0; i < num; i++)
{
Hand.push_back(Deck.back());
Deck.pop_back();
}
}
void Player::DrawCard()
{
std::cout<< "Drawing next card..." << std::endl;
if(Deck.size() >= 1)
{
//copy card from top of deck
//place at front of hand
Hand.push_back(Deck.back());
//remove care from top of deck
Deck.pop_back();
}
else
{
std::cout<< "No cards left to draw\n";
}
}
EDIT:: Each of the above functions are currently only called in one place a piece, and not under any extreme circumstances or anything weird. Just very basic, simple calls.

Insert into a desired element of an array and push all other elements one spot over in c++

Having some issues with one small function I'm working on for a homework assignment.
I have a static array size of 20 (shelfSize), however, I only need to use a max of 10 elements. So I don't have to worry about out of bounds etc (the entire array of 20 is initialized to 0).
What I am looking to do is insert an integer, booknum, into an element of an array it receives as input.
This my current logic:
void insert_at(int booknum, int element){
for(int i=element+1; i < shelfSize; i++)
bookshelf[i+1]=bookshelf[i]
bookshelf[element]=booknum;
}
so let's say I have the this array:
[5,4,3,1,7]
I want to insert an 8 at element 1 and have the array turn to:
[5,8,4,3,1,7]
Technically, everything after the final element 7 is a 0, however, I have a separate print function that only prints up to a certain element.
No matter how many times I take some pencil and paper and manually write out my logic, I can't get this to work.
Any help would be appreciated, thanks.
You should start from the end of the array, this should word for you:
void insert_at(int booknum, int element)
{
for (int i = shelfsize-1;i>element;i--)
bookshelf[i] = bookshelf[i-1];
bookshelf[element] = booknum;
}
Also I recommend that you get used to handling illegal values, for example, what if a user entered 21?
The optimized code would be:
bool insert_at(int booknum, int element)
{
if (element>=shelfsize-1)
return false;
for (int i = shelfsize-2;i>element;i--)
bookshelf[i] = bookshelf[i-1];
bookshelf[element] = booknum;
return true;
}
If your example is correct, then you're assuming 1-based indices instead of 0-based. Use the following instead:
void insert_at(int booknum, int element){
for(int i=element; i < shelfSize; i++)
bookshelf[i]=bookshelf[i-1];
bookshelf[element-1]=booknum;
}
However, I would prefer you just use the same code, and change "at element 2" in your example to "at element 1". Always remember C++ arrays are 0-based.
That being said, please tell your professor that this is why vectors (and other standard containers) were made, and that C++ arrays are evil.
http://www.parashift.com/c++-faq-lite/containers.html#faq-34.1
Just noticed, you are copying up, this means your function does this:
[5,4,3,1,7]
--^
[5,4,4,1,7]
--^
[5,4,4,4,7]
--^
[5,4,4,4,4]
--^
[5,4,4,4,4,4]
For moving values in an array, you always want to copy in the opposite direction to which you are moving, so to move up, you want to copy each item up from the top down:
[5,4,3,1,7]
--^
[5,4,3,1,7,7]
--^
[5,4,3,1,1,7]
--^
[5,4,3,3,1,7]
--^
[5,4,4,3,1,7]
And then you can overwrite the index you freed up.