3

I am having an issue getting a vector-based inventory system to work. I am able to list the items in the inventory, but not able to allow a user-selected item to be accessed. Here is the code:

struct aItem
{
    string  itemName;
    int     damage;

    bool operator==(aItem other)
    {
        if (itemName == other.itemName)
            return true;
        else
            return false;
    }
};

int main()
{
    int selection = 0;


    aItem healingPotion;
    healingPotion.itemName = "Healing Potion";
    healingPotion.damage= 6;

    aItem fireballPotion;
    fireballPotion.itemName = "Potion of Fiery Balls";
    fireballPotion.damage = -2;

    aItem testPotion;
    testPotion.itemName = "I R NOT HERE";
    testPotion.damage = 9001;
    int choice = 0;
    vector<aItem> inventory;
    inventory.push_back(healingPotion);
    inventory.push_back(healingPotion);
    inventory.push_back(healingPotion);
    inventory.push_back(fireballPotion);

    cout << "This is a test game to use inventory items. Woo!" << endl;
    cout << "You're an injured fighter in a fight- real original, I know." << endl;
    cout << "1) Use an Item. 2) ...USE AN ITEM." << endl;

switch (selection)
    {
    case 1:
        cout << "Which item would you like to use?" << endl;
        int a = 1;
        for( vector<aItem>::size_type index = 0; index < inventory.size(); index++ ) 
        {

            cout << "Item " << a << ": " <<  inventory[index].itemName << endl;
            a+= 1;
        }
        cout << "MAKE YOUR CHOICE." << endl << "Choice: ";

        cin >> choice;

^^^^ Everything above this line, works. I assume that my problem is the if statement, but I cannot figure out where I am going wrong in my syntax, or if there is a better way to do what I am doing.

        if (find(inventory.begin(), inventory.at(choice), healingPotion.itemName) != inventory.end())
            cout << "You used a healing potion!";
        else
            cout << "FIERY BALLS OF JOY!";
        break;

    case 2:
        cout << "Such a jerk, you are." << endl;
            break;

    }

EDIT: I think I'm not representing this correctly. I need for the player's choice to affect the message displayed. Here's a sample output of the 1st snippet:

Item 1: Healing Potion
Item 2: Healing Potion
Item 3: Healing Potion
Item 4: Potion of Fiery Balls

MAKE YOUR CHOICE. 
Choice: 

From there, the player can type 1-4, and what I would like is for the number (minus 1, to reflect the vector starting at zero) to be passed to the find, which would then determine (in this small example) if the item at inventory[choice - 1] is a healing potion. If so, display "You used a healing potion!" and if it is not, to display "Fiery balls of joy".

Vladimir Marenus
  • 393
  • 2
  • 3
  • 16
  • 1
    Shouldnt that be `if (find(inventory.begin(), inventory.end() , healingPotion.itemName) != inventory.end())`?? Further `inventory.at(choice)` will return the reference to the value of a particular object in that `vector` and not a reference to `iterator`. – Recker Nov 12 '12 at 18:55
  • What failure are you seeing? – DavidO Nov 12 '12 at 18:55
  • Your operator should be declared as: `bool operator==(const aItem& other) const` – John Dibling Nov 12 '12 at 18:59
  • 1>------ Build started: Project: StructPractice, Configuration: Debug Win32 ------ 1> StructPractice.cpp 1>c:\program files (x86)\microsoft visual studio 11.0\vc\include\xutility(3186): error C2678: binary '==' : no operator found which takes a left-hand operand of type 'aItem' (or there is no acceptable conversion) – Vladimir Marenus Nov 12 '12 at 18:59
  • @VladimirMarenus: Right, because the left-hand-side isn't `const`. See my answer. – John Dibling Nov 12 '12 at 19:03

3 Answers3

2

Three problems.

One, Your operator should be declared as:

bool operator==(const aItem& other) const

Two, in this code:

find(inventory.begin(), inventory.at(choice), healingPotion) != inventory.end())

you aren't searching the whole vector from begin() to end() -- you're only searching from begin() to at(choice) where at(choice) points to one-past-the-end of your search set. So you either should do this:

find(&inventory.at(0), &inventory.at(choice), healingPotion) != &inventory.at(choice))

or this...

find(inventory.begin(), inventory.end(), healingPotion.itemName) != inventory.end())

Edit Three, you are trying to compare apples to oranges. You are searching a vector of aItem objects to find a matching aItem object, but the parameter you send to find isn't an aItem object, it is one of the aItem data members.

You should either search for a matching item, like this:

find( inventory.begin(), inventory.end(), healingPotion ) != inventory.end() )
                                            ^^^^^^^^

In C++03 you can provide a functor:

#include <functional>
struct match_name : public std::unary_function<aItem, bool>
{
    match_name(const string& test) : test_(test) {}
    bool operator()(const aItem& rhs) const
    {
        return rhs.itemName == test_;
    }
private:
  std::string test_;
};

... and then search for a match using find_if:

find_if( inventory.begin(), inventory.end(), match_name(healingPotion.itemName) ) // ...

In C++11 you can simplify this mess using a closure:

string test = healingPotion.itemName;
if( find_if( inventory.begin(), inventory.end(), [&test](const aItem& rhs)
{
    return test == rhs.itemName;
}) == inventory.end() )
{
  // not found
}
John Dibling
  • 99,718
  • 31
  • 186
  • 324
  • When I use that answer, I get the following: 1>c:\users\dmarr\documents\csharper\dallas\cplusplus\structpractice\structpractice\structpractice.cpp(75): error C2782: '_InIt std::find(_InIt,_InIt,const _Ty &)' : template parameter '_InIt' is ambiguous 1> c:\program files (x86)\microsoft visual studio 11.0\vc\include\xutility(3215) : see declaration of 'std::find' 1> could be 'aItem' 1> or 'std::_Vector_iterator<_Myvec>' 1> with 1> [ 1> _Myvec=std::_Vector_val> 1> ] – Vladimir Marenus Nov 12 '12 at 19:07
  • `vector::at` returns a reference, not an iterator. How can it be used as the second parameter to `find`? – Benjamin Lindley Nov 12 '12 at 19:08
  • @BenjaminLindley: As originally written it can't. See my edit. – John Dibling Nov 12 '12 at 19:15
  • @JohnDibling Almost! New error: error C2678: binary '==' : no operator found which takes a left-hand operand of type 'aItem' (or there is no acceptable conversion) // But I defined it in the struct, unless it's talking about something else... – Vladimir Marenus Nov 12 '12 at 19:17
  • @VladimirMarenus: You defined it wrong. See my first point. – John Dibling Nov 12 '12 at 19:17
  • @JohnDibling - This is using your example. – Vladimir Marenus Nov 12 '12 at 19:18
  • @JohnDibling - I realize that may have been unclear. What I meant is that I also made the first change in the struct as well, and it's still giving me the C2678 error. – Vladimir Marenus Nov 12 '12 at 19:30
  • @VladimirMarenus: Right, there were actually 3 problems, not 2. See my latest edit. – John Dibling Nov 12 '12 at 19:33
  • Thank you for your help! ...but I think this got away from my original intent- please see my edit. – Vladimir Marenus Nov 12 '12 at 19:51
0

To add onto John Dibling's answer, the last part is that you are looking for a name, not an aItem.

So it either needs to be:

find(inventory.begin(), inventory.end(), healingPotion) != inventory.end();

where the operator== is defined as:

bool operator==(const aItem& other) const
{
   return itemName == other.itemName;
}

Or you need to have your operator== take a string:

find(inventory.begin(), inventory.end(), healingPotion.itemName) != inventory.end();

where the operator== is defined as:

bool operator==(const std::string& name) const
{
   return itemName == name;
}
Ryan Guthrie
  • 688
  • 3
  • 11
  • Thank you for your help! ...but I think this got away from my original intent- please see my edit. – Vladimir Marenus Nov 12 '12 at 19:51
  • 1
    Then just validate the input value for the size of the vector, and grab the nth value directly. No need to search the vector if you looking for a specific offset. – Ryan Guthrie Nov 12 '12 at 20:12
  • However, just because inventory[3] is a healing potion NOW, doesn't mean it will be the next time inventory[3] is selected- these are consumable items. – Vladimir Marenus Nov 12 '12 at 20:46
0

Instead of:

case 1:
        cout << "Which item would you like to use?" << endl;
        int a = 1;
        for( vector<aItem>::size_type index = 0; index < inventory.size(); index++ ) 
        {

            cout << "Item " << a << ": " <<  inventory[index].itemName << endl;
            a+= 1;
        }
        cout << "MAKE YOUR CHOICE." << endl << "Choice: ";

        cin >> choice;
        if (find(inventory.begin(), inventory.at(choice), healingPotion.itemName) != inventory.end())
            cout << "You used a healing potion!";
        else
            cout << "FIERY BALLS OF JOY!";
        break;

    case 2:
        cout << "Such a jerk, you are." << endl;
            break;

    }

I neglected to realize that one of the wonders of vectors is the ability to access the value directly- Ryan Guthrie mentioned this in his comment, but I found a simpler "answer". Namely:

case 1:
    cout << "Which item would you like to use?" << endl;
    //TODO: Learn what the hell the following line actually means.
    for( vector<aItem>::size_type index = 0; index < inventory.size(); index++ ) 
    {
        //Makes a numerical list.
        cout << "Item " << index + 1 << ": " <<  inventory[index].itemName << endl;
        a+= 1;
    }
    cout << "MAKE YOUR CHOICE." << endl << "Choice: ";

    cin >> choice;
    //Cannot define this outside of the statement, or it'll initialize to -1
    invVecPos = (choice - 1);

    //This checks for an invalid response. TODO: Add in non-int checks. 
    if ((invVecPos) >= inventory.size())
    {
        cout << "Choice out of bounds. Stop being a dick." << endl;
    }
    //If the choice is valid, proceed.
    else
    {
        //checking for a certain item type.
        if(inventory[invVecPos].itemType == "ITEM_HEALTHPOT")
        {
            cout << "You used a healing potion!" << endl;
            //this erases the potion, and automagically moves everything up a tick.
            inventory.erase (inventory.begin() + (invVecPos));
        }

        else if(inventory[invVecPos].itemType == "ITEM_FIREPOT")
        {
            cout << "FIERY BALLS OF JOY!" << endl;
        }

        else
        {
            //error-handling! Whee!
            cout << "Invalid Item type" << endl;
        }
    }


    break;
case 2:
    cout << "Why do you have to be so difficult? Pick 1!" << endl;
    break;

Thank you, Ryan- with your prodding, I was able to look elsewhere and find the code I needed! The "fixed" code is commented heavily, so anyone else who runs into issues should be able to glean what they need!

Vladimir Marenus
  • 393
  • 2
  • 3
  • 16