Assignment Chef icon Assignment Chef
All English tutorials

Programming lesson

Building a Tavern in C++: Recursive Realms & OOP Adventures

Learn how to implement a Tavern class using C++ inheritance, operator overloading, and ArrayBag. This tutorial guides you through modifying Character, ArrayBag, and building a Tavern with level sums, enemy counts, and reports.

C++ Tavern class CSCI 235 project 3 ArrayBag subclass operator overloading C++ inheritance in C++ character class C++ tavern report C++ recursive realms object-oriented programming C++ game development data structures C++ incremental testing C++ tutorial for students algorithmic adventures C++ enemy count level sum calculation

Welcome to the Tavern: Your C++ OOP Quest Begins

In this algorithmic adventure, you'll build a Tavern class—a subclass of ArrayBag that holds Character objects. This project is a rite of passage for CSCI 235 students, blending object-oriented programming with data structures. Think of it as designing the backend for a fantasy RPG like Baldur's Gate 3 or World of Warcraft, where characters gather, battle, and level up. By the end, you'll have a fully functional tavern that tracks adventurers, enemies, and their stats.

Why This Matters: From School Projects to Game Dev

Understanding inheritance and operator overloading is crucial for any C++ developer. Whether you're building a game AI or a finance app, these concepts help you write reusable, clean code. For example, the viral app Character.AI uses similar OOP principles to manage thousands of conversational agents. In this tutorial, you'll apply these skills to a fantasy tavern—a perfect blend of C++ programming and game design.

Task 1: Modify the Character Class

First, you'll add operator== and operator!= to the Character class. Two characters are equal if they share the same name, race, level, and enemy status. This is like checking if two players in a League of Legends match have the same champion, rank, and role.

bool Character::operator==(const Character& rhs) const {
    return (name_ == rhs.name_ &&
            race_ == rhs.race_ &&
            level_ == rhs.level_ &&
            isEnemy_ == rhs.isEnemy_);
}

bool Character::operator!=(const Character& rhs) const {
    return !(*this == rhs);
}

Notice the const reference parameter and const member function. This ensures you don't accidentally modify the objects. Also add a display() method that prints character info in a formatted string.

void Character::display() const {
    std::cout << name_ << " is a Level " << level_ << " " << race_ << ". Vitality: " << vitality_ << " Max Armor: " << armor_ << " ";
    if (isEnemy_) std::cout << "They are an enemy.";
    else std::cout << "They are not an enemy.";
    std::cout << std::endl;
}

Task 2: Enhance the ArrayBag Class

Next, you'll add two operators to ArrayBag: operator/= (combine without duplicates) and operator+= (combine with duplicates). This is like merging two inventory lists in Minecraft—one mode stacks identical items, the other keeps them separate.

template<typename ItemType>
void ArrayBag<ItemType>::operator/=(const ArrayBag<ItemType>& other) {
    for (int i = 0; i < other.getCurrentSize(); ++i) {
        if (!contains(other.items_[i])) {
            add(other.items_[i]);
        }
    }
}

template<typename ItemType>
void ArrayBag<ItemType>::operator+=(const ArrayBag<ItemType>& other) {
    for (int i = 0; i < other.getCurrentSize() && getCurrentSize() < DEFAULT_CAPACITY; ++i) {
        add(other.items_[i]);
    }
}

These operators are essential for data structure manipulation. In a school project, you might use them to merge student records or game scores.

Task 3: Implement the Tavern Class

Now the main event: the Tavern class inherits from ArrayBag<Character>. It tracks two private members: levelSum_ and enemyCount_. These are updated every time a character enters or exits.

Private Members and Constructor

class Tavern : public ArrayBag<Character> {
private:
    int levelSum_;
    int enemyCount_;
public:
    Tavern() : ArrayBag<Character>(), levelSum_(0), enemyCount_(0) {}
    // ...
};

Entering and Exiting the Tavern

The enterTavern method adds a character and updates sums. Think of it like a Twitch stream where a new viewer joins—the viewer count and average level change.

bool Tavern::enterTavern(const Character& entrant) {
    if (add(entrant)) {
        levelSum_ += entrant.getLevel();
        if (entrant.isEnemy()) enemyCount_++;
        return true;
    }
    return false;
}

bool Tavern::exitTavern(const Character& evictee) {
    if (remove(evictee)) {
        levelSum_ -= evictee.getLevel();
        if (evictee.isEnemy()) enemyCount_--;
        return true;
    }
    return false;
}

Notice we use the add and remove methods inherited from ArrayBag. This is the power of inheritance in C++—you reuse tested code.

Calculating Stats: Level Sum, Average, Enemy Count

These methods are straightforward. calculateAvgLevel rounds to the nearest integer using std::round.

int Tavern::getLevelSum() const { return levelSum_; }
int Tavern::getEnemyCount() const { return enemyCount_; }

int Tavern::calculateAvgLevel() const {
    if (getCurrentSize() == 0) return 0;
    return static_cast<int>(std::round(static_cast<double>(levelSum_) / getCurrentSize()));
}

double Tavern::calculateEnemyPercentage() const {
    if (getCurrentSize() == 0) return 0.0;
    double pct = 100.0 * enemyCount_ / getCurrentSize();
    return std::round(pct * 100.0) / 100.0; // round to 2 decimal places
}

Race Tally and Tavern Report

The tallyRace method counts characters of a given race. Use a loop and compare strings. The tavernReport method outputs a formatted summary—perfect for debugging or displaying in a game UI.

int Tavern::tallyRace(const std::string& race) const {
    int count = 0;
    for (int i = 0; i < getCurrentSize(); ++i) {
        if (items_[i].getRace() == race) count++;
    }
    return count;
}

void Tavern::tavernReport() const {
    std::cout << "Humans: " << tallyRace("HUMAN") << std::endl;
    std::cout << "Elves: " << tallyRace("ELF") << std::endl;
    std::cout << "Dwarves: " << tallyRace("DWARF") << std::endl;
    std::cout << "Lizards: " << tallyRace("LIZARD") << std::endl;
    std::cout << "Undead: " << tallyRace("UNDEAD") << std::endl;
    std::cout << "The average level is: " << calculateAvgLevel() << std::endl;
    std::cout << std::fixed << std::setprecision(2) << calculateEnemyPercentage() << "% are enemies." << std::endl;
}

Testing Incrementally: The Secret to Success

Don't write all code at once! Implement and test each method. For example, test enterTavern by adding a character and checking getLevelSum. Use a main.cpp with multiple cases:

int main() {
    Tavern tavern;
    Character hero("Aragorn", "HUMAN", 10, 100, 50, false);
    Character villain("Sauron", "UNDEAD", 20, 200, 100, true);
    tavern.enterTavern(hero);
    tavern.enterTavern(villain);
    std::cout << "Level sum: " << tavern.getLevelSum() << std::endl; // 30
    std::cout << "Enemy count: " << tavern.getEnemyCount() << std::endl; // 1
    tavern.tavernReport();
    return 0;
}

This incremental testing approach saves hours of debugging. It's like leveling up in a game—you gain XP one quest at a time.

Real-World Connections: From Taverns to Tech

This project mirrors real software engineering patterns. The Tavern class is a repository pattern—a common design in finance apps and AI systems. For instance, a stock portfolio might hold assets and track total value, similar to your level sum. Even social media algorithms use bags of data to recommend content. By mastering these C++ fundamentals, you're building skills for internships at FAANG or startups.

Conclusion: Your Tavern Awaits

You've now learned how to implement a Tavern class using inheritance, operator overloading, and ArrayBag. This project is a milestone in your algorithmic adventures. Remember to document your code, test often, and have fun. The recursive realms of C++ are vast—this tavern is just the beginning. Happy coding!