Upgrade Delete Function in Inventory For Text Based Game - c++

I am still a newbie at C++, but I am currently working on a text based game. For my game, I have an Inventory that works, but I want to add a couple functions to it so it is better. I have tried googling around, but I can't find enough help or resources for me to be able to do this myself.
The first thing I am trying to do is when I use my delete function. For the function, I am having the user enter the item they wish to remove from their inventory. I want to be able to check that the user enters an item that exists in their inventory and not make an item up. So after the user enters the item, I take that item and go through the list, check to see if it is there, and then if it is there, delete it, and if it is not there print out an error message.
This is my code so far:
void Inventory::DeleteItem()
{
string deleteitem = " ";
if (item_numbers == 0) {
cout << "\nError: No items in the Inventory.\n\n";
}
else {
cout << "\nPlease type the item you wish to use or take out of the inventory: ";
cin >> deleteitem;
}
bool found = false;
for (int i = 0; i<item_numbers && !found; i++) {
if (Info[i].item_name == deleteitem) {
while(found == true){
cout<<"Found an item matching what you wrote: "<<Info[i].item_name<<endl;
item_numbers--;
cout<<"Item: "<<Info[i].item_name<< " is now gone." <<endl;
break;
}
}
if (found == false){
cout<<"liar"<<endl;
break;
}
}
}
I bet the solution is so simple and I am missing it. I just honestly can't see it. I'm sorry! But I appreciate all the help!
Currently when I test the code, this is how it prints out:
Which item will you be adding to your inventory? Here
Which item will you be adding to your inventory? Now
Please type the item you wish to use or take out of the inventory: Here
liar

You can't delete it in the way you thought the program.
I have done something similar a while ago and my advice is:
1. Use another class "Item" with attribute "Name" and "NumberOfItems"(or more if you want);
2. In the Inventory class add an attribute "vector<Items> MyItems" (it this way you learn about vector as well)
3. when you find the object in your list, you call MyItems.erase(position), where position is the index of your found item.

Related

Search vector of objects by object data member attribute

I'm writing a Jukebox simulator and I'm trying to search a vector of Album objects by album title and return the index for use in other functions. The function is to be used for a number of different things such as deleting an album, printing an album etc.
I have gotten it to work in a previous application when the function was in the same Class as the data member to search for. I can however for some reason not get it to work using getters. No matter what I input as search key the idx returns 3 although the vector only contains indexes 0, 1 and 2 (only 3 albums right now).
The lambda function seem to be able to access data by using the getAlbum()-getter but somehow the comparison doesn't work.
My approach might be entirely wrong and I'd be grateful for any pointers in the right direction, or suggestions on how to accomplish the desired result using some other technique.
int Jukebox::findAlbumIdx(string key)
{
// Get search predicate
auto it = find_if(albvec.begin(), albvec.end(), [key](Album const &a)
{
return (a.getAlbum() == key);
});
int idx = it - albvec.begin();
return idx;
}
void Jukebox::delAlbum()
{
cin.get();
string key;
cout << "Input title of album to delete: ";
getline(cin, key);
int idx = findAlbumIdx(key);
if(idx > albvec.size() - 1)
cout << "No hits." << endl;
else
albvec.erase(albvec.begin() + idx);
}
getAlbum is just a simple inline getter as such:
string getAlbum() const {return album_title;}
Following Jonathan Wakely's suggestion to add std::cout << a.getAlbum() << " == " << key << std::endl; in the lambda the output is this:
Input title of album to delete: Abbey Road
== Abbey Road
== Abbey Road
== Abbey RoadLonely Hearts Club Band
No hits.
Obviously the getter isn't actually getting much to use for comparison. Not sure why it only gets the last entry and on the right hand side of the comparison.
If I add this to any of the functions above it gets and displays the Album titles correctly. The vector seems to be fine just before calling findAlbumIdx(key); and also inside the findAlbumIdx function.
for(unsigned int i = 0; i < albvec.size(); ++i)
cout << albvec[i].getAlbum() << endl;
The original playlist file that is read into the vector to be searched had dos newlines, after converting it using dos2unix (since I'm running Linux) the search, and I presume a lot of other things, is working correctly.
I suppose trimming newline characters while reading the file into the vector would be the more correct approach.

Comparing Substrings to JList Strings

In advance, please forgive me if I do not give adequate background information for my question. Long time reader, first time asker.
I am making a program where one has a database of cars accessed through a tab delimited .txt file (we did something like this recently in my programming class, so I wanted to expand upon it).
Instead of using the terminal window, my format is displaying the Car objects (containing make, model, year, price, etc.) in ArrayList. I'm using JFrame, a JList, and a ListModel since I'm using an array of Car objects.
In my program, I wanted to create a delete method where the user could delete items from the database. Initially they would select the item from the JList and then would click on the delete button. This invokes the delete() method, which is the tab shown below...
void delete()
{
int i = list.getSelectedIndex();
String string = (String)listModel.getElementAt(i);
for(Car c : cars)
{
String year = String.valueOf(c.getYear());
String conditionReport = String.valueOf(c.getConditionReport());
String price = String.valueOf(c.getPrice());
if(c.getMake().indexOf(string) != -1 && c.getModel().indexOf(string) != -1 && year.indexOf(string) != -1 && conditionReport.indexOf(string) != -1 && price.indexOf(string) != -1 && c.getWarranty().indexOf(string) != -1 && c.getComments().indexOf(string) != -1)
{
int choice = JOptionPane.showConfirmDialog(null, "Are you sure you would like to remove the " + cars.get(i).getYear() + " " + cars.get(i).getMake() + " " + cars.get(i).getModel() + "?", "Choose One", JOptionPane.YES_NO_OPTION);
if(choice == JOptionPane.NO_OPTION || choice == JOptionPane.CLOSED_OPTION)
{
return;
} else
{
cars.remove(c);
listModel.removeElementAt(i);
}
}
}
writeFile();
}
I have pinpointed my issue to be inside the if statement. (I printed things before and after to try to find where the program is lying. 'list' is my JList and 'listmodel' is my default list model. Car is an object I created that contains the elements (as seen by the get methods). The elements shown in the listModel are merely Strings that show getMake(), getModel(), and so forth... (Each 'get' item is separated by about 10 spaces.)
What am I doing wrong in the if statement? I figured that the getMake() and getModel() (and so forth) would be substrings of the index selected.
Thank you so much for your assistance! Any input regarding ways I could make further questions more specific and clear would be greatly appreciated!
It seems like you are doing this to find the selected Car in some kind of data structure. You would be better off doing something like programming a custom list model that had access to cars itself. Then you could retrieve the selection more immediately. If cars is an ArrayList that list merely parallels I also don't see why you can't do something to the effect of cars.remove(list.getSelectedIndex());. Or since JList can display any object, override Car's toString to display what the list currently displays and have the list display Cars. Then you can cars.remove((Car)list.getSelectedValue());.
But aside from that, based on your description it sounds like you mean to do the evaluation the other way. It's the list item that should contain all of the Car attributes, rather than all of the Car attributes containing the list item. So something like
if( string.contains(c.getMake()) && string.contains(year) // and so on
(Or with indexOf but since contains merely returns indexOf > -1, using contains makes your code somewhat shorter.)

possible misunderstanding of map iterators with for loops

very specific question I'm afraid (and I'm rather a novice, so apologies in advance):
I'm currently trying to finish my final project for a University object-oriented C++ course. I'm creating a student database to store exam results for students. My setup has loads of custom classes but all work perfectly (or at least do what I want them to do).
The project is set up as follows:
I have a "master" map of all "course"s, to which everything points to (so a course isn't duplicated if more than one student is taking it).
A "student" is a vector of pointers to "course"s and a corresponding double "result", and I have a master map of all students in the system.
a "degree" is a class of two vectors of pointers, one to courses offered by that degree, and one to students taking that degree. When a degree is created, it searches both master maps. If the first x letters in a course id matches the degree prefix, the course is added. If a student's subject matches the course name, the student is added.
My problem is this:
As I have some options to manually input courses and students after the initial setup from CSV files, I have writen a function to update my degrees if a course/result is added which should be included in a degree (see below). However, this code inevitably results in the first course and student being re-added (i.e. repeated) to the first degree the first time this function is called. this problem is not repeated if the function is called again. I have absolutely no idea why. A huge amount of time and cout statements later and I'm no closer to solving this. Am I missing something obvious about the first run? I may have set the loops up wrong (I'm not very familiar with maps). Don't hesitate to call me an idiot!
As I have said above, all the rest of the program is gravy, without this odd issue the program is fine. The problem does not appear to come from my print functions either.
Thank you in advance for your time.
//upgrade degrees function: used whenever new courses or students could be created by the user. It ticks through all stored degrees and scans cd and sd. If it finds an unstored course or student that should be stored, they are added.
void degree_database::update_degrees(course_database &cd, student_database &sd) {
cout << "updating degrees..." << endl;
bool found = false;
vector<degree>::iterator current;
for (current = start; current < end; ++current) {
//scan course list
map<string, course>::iterator x;
for (x = cd.get_start(); x != cd.get_end(); ++x) {
if (x->first.substr(0,3) == current->get_prefix().substr(0,3) || current->get_prefix() == "ALL") {
//check to see if course is already stored
vector<course*>::iterator a;
for (a = current->get_c_start(); a < current->get_c_end(); ++a) {
if (*a == &(x->second)) {
found = true;
break;
}
}
//if found == true, then while loop broke early (i.e. the course was already stored).
if (found == false) current->add_course(x->second);
found = false;
}
}
//scan student list
found = false;
map<string, student>::iterator y;
for (y = sd.get_start(); y != sd.get_end(); ++y) {
if (y->second.get_subject() == current->get_name() || current->get_name() == "All") {
//check to see if course is already stored
vector<student*>::iterator b;
for (b = current->get_s_start(); b < current->get_s_end(); ++b) {
if (*b == &(y->second)) {
found = true;
break;
}
}
//if found == true, then while loop broke early (i.e. the student was already stored).
if (found == false) current->add_student(y->second);
found = false;
}
}
}
cout << "done." << endl;
}
You store course by value in the course list and then you use pointer to this object in comparison. Apparently, you shoud store pointers in the map. I think (*a == &(x->second)) fails on the first run and pointer to the object from the course map is added to a degree object. On the second run, (*a == &(x->second)) succeeds and all looks ok. The same for student map.

logic issue or something more?

My program simulates a video store. In my list there are multiple copies of some videos. If I try to rent a video and the first copy of that video in the list is already rented, my program fails to continue checking to see if the other copies are available (a film is available if custId is '0000'). Take a look at the text file from where the list gets its members for a better understanding of what i'm describing:
Could anyone take a look and let me know if they spot an issue? Any help is appreciated, thanks.
Code from main
try
{
int index = 0;
bool found = false;
while (!found)
{
if (strncmp(filmId,filmList.getAt(index).number,6) == 0 && strncmp("0000",filmList.getAt(index).rent_id,5) == 0)//If that film is rented by NO customer
{
found = true;//customer can rent it
strcpy(newItem.number,filmId);//copy filmId into newItem
filmList.retrieve(newItem);//copy the struct in our orderedList with the same filmId/copy into newItem
filmList.remove(newItem);//delete the struct with same filmId/copy as newItem from the orderedList
strcpy(newItem.rent_id,custId);//update info in
strcpy(newItem.rent_date,rentDate);// newItem to show
strcpy(newItem.return_date,dueDate);// that it has been rented
filmList.insert(newItem);//put NewItem into list, effectivily replacing the removed item.
cout << "Rent confirmed!" << endl;
}
else
{
if (strncmp(filmId,filmList.getAt(index).number,6) > 0 || strncmp("0000",filmList.getAt(index).rent_id,5) > 0)
{
++ index;
}
else
{
throw string ("Not in list");
}
}
}
}
catch (string s)
{
cout << "\n***Failure*** " << s << endl;
}
Let me know if more code is required from any other parts of the program.
Here's my best guess with the code provided.
Let's say we are looking up 101001Casablanca, therefore I'm assuming filmId = "101001Casablanca". Also, assume the 101001Casablanca is checked out to customer 0001. We are comparing the first 6 characters of filmId to filmList.getAt(index).number, which I'm going to assume is at the very least "101001". This passes, but since it is checked out the second condition fails.
In the else we check the same strings in the first condition and still get 0 returned from strncmp which is false. The second condition is also false since strncmp("0000", "0001", 5) is -1. Therefore we go to the final else which throws.
If you are only checking string equality with strncmp, remember that it can return -1, therefore check if equal or not equal to 0.

In C++, I want to implement a ring iterator for a deque that contains a personally defined class

I have a function of a "Table" class that should add a player to the table. I decided that if the seat is taken, the function should try and go through all the seats and add the player to the next available seat. How do I implement this in my addPlayer function?
int Table::addPlayer(Player player, int position)
{
deque<Player>::iterator it;
if(playerList[position] != "(empty seat)") {
//What goes here?
}
playerList.put(player,it);
cout >> "Player " >> player.toString >> " sits at position " >> position >> endl;
}
Instead of using position, use the iterator to point to that position, using something like this:
it = playerList.begin() + position;
Then, check if the seat is taken using the iterator.
If the seat is taken, increment the iterator, but check for end, like this:
while (no empty seat found yet)
{
++it;
if (it==playerList.end()) it = playerList.begin();
}
Of course, if all seats have been taken, this will result in an endless loop.
Therefore, also keep the iterator you started from (let's call this itStart), and add a check on it:
while (no empty seat found yet)
{
++it;
if (it==playerList.end()) it = playerList.begin();
if (it==itStart) break; // We tried all seats
}
Maybe you should also take a look at the circular buffer from boost.
http://www.boost.org/doc/libs/1_43_0/libs/circular_buffer/doc/circular_buffer.html
I think it will do what you want, and is quite easy to use.