2Brute2Best
πŸ”₯0
C2

Bags Inside Pouches Inside the Inventory

High-frequency interview topic Pro OnlyComposite
⚑ 60-Second Architecture SummaryC2 · Composite Pattern
Naive Bottleneck→Composite Pattern Cure
The Naive Bottleneck: Hardcoded to a single level of nesting

Writing separate loops and type-checks for individual items versus containers (`if (isBag) ... else ...`) breaks recursive nesting. Naive designs force separate vectors for items and bags, duplicate traversal logic on every depth level, and fail to treat single items and composite pouches uniformly.

A Bag can only contain flat Items. If a player places an HerbPouch inside a LeatherBackpack, the naive model cannot represent it without constantly adding nested vector<Bag> subBags fields.

The Pattern Cure: Composite Pattern

Compose objects into tree structures to represent part-whole hierarchies, allowing clients to treat individual objects and compositions of objects uniformly.

We define an abstract InventoryNode interface declaring weight(), goldValue(), and print(). Leaf nodes (LootItem) return their own values directly. Composite nodes (ContainerBag) own a collection of children via std::vector<std::unique_ptr<InventoryNode>> and recursively aggregate values from their children. The root PlayerInventory interacts purely with InventoryNode, so a bag containing 50 pouches is evaluated with the exact same call as a single sword.

Sprint 2 of 8 Β· Nested Inventory Bags, Skill Trees & Raid Parties
Story C β€” 2D RPG Game Engine β€” Progressive System Build
1. Where our codebase left off
← C1

In Sprint 1 (C1 Β· Decorator), we implemented stackable weapon enchantments and hero buffs that wrap characters dynamically without subclass explosion. With buffed heroes fighting and collecting loot, our engine now needs to manage complex inventories and group structures.

2. What we are building now

We need to support recursive inventory containers where a PlayerInventory can hold individual loose items (LootItem like IronSword or HealthPotion) as well as container bags (ContainerBag like LeatherBackpack), which in turn hold smaller pouches (HerbPouch, BeltPouch) to arbitrary depth while calculating total weight and gold value.

3. The pain point we hit

Writing separate loops and type-checks for individual items versus containers (if (isBag) ... else ...) breaks recursive nesting. Naive designs force separate vectors for items and bags, duplicate traversal logic on every depth level, and fail to treat single items and composite pouches uniformly.

4. What this unlocks next
Next: C3 β†’

The Composite Pattern unifies leaf items and container groups behind the shared InventoryNode interface, allowing trees of arbitrary depth to be queried through a single recursive call. Next in Sprint 3 (C3 Β· Memento), before entering a boss room with their equipped gear and inventory, the player needs to save a checkpoint and roll back on death without exposing Hero's private fields.

01

The product ticket

Ticket Β· C2RPG-102 Β· Bags Inside Pouches Inside the Inventory

In our RPG engine, heroes collect loot: single items like an IronSword, a HealthPotion, and gemstones. As adventures continue, players organize inventory using containers: a large LeatherBackpack, inside of which they tuck a BeltPouch, an HerbPouch, or a GemChest to arbitrary depth.

Requirements
  • β€’

    Support both primitive single items (LootItem) and container bags (ContainerBag).

  • β€’

    Allow bags to hold both loose items and other bags to arbitrary nesting depth.

  • β€’

    Calculate totalWeight() recursively across all contents (including empty container tare weight) to enforce character encumbrance limits.

  • β€’

    Calculate totalGoldValue() across all items inside nested containers for vendor sales and wealth checks.

  • β€’

    Provide a hierarchical visualization via print(int indent) showing the tree structure.

The next requirement that breaks naive code

Designers want to introduce new specialized containers: LockedChest, EnchantedQuiver, and AlchemistKit. The inventory calculation logic must remain completely untouched when new container variants are introduced.

02

Step 1 β€” The naive code & why it breaks

The naive implementation creates distinct Item and Bag structs, with NaiveInventory holding separate vectors looseItems_ and bags_ where Bag holds std::vector<Item>.

Naive design (what not to ship) Β· naive.cpp
#include <print>
#include <string>
#include <vector>

// Naive design: separate structs for Item and Bag, hardcoded to 1 level of nesting!
struct Item {
    std::string name;
    double weight;
    int goldValue;
};

struct Bag {
    std::string name;
    double emptyWeight;
    std::vector<Item> items; // Only holds flat items, CANNOT hold another Bag!
};

class NaiveInventory {
    std::vector<Item> looseItems_;
    std::vector<Bag> bags_;
public:
    void addLooseItem(Item it) {
        looseItems_.push_back(std::move(it));
    }

    void addBag(Bag b) {
        bags_.push_back(std::move(b));
    }

    double totalWeight() const {
        double total = 0.0;
        for (const auto& item : looseItems_) {
            total += item.weight;
        }
        for (const auto& bag : bags_) {
            total += bag.emptyWeight;
            for (const auto& item : bag.items) {
                total += item.weight;
            }
        }
        return total;
    }

    int totalGoldValue() const {
        int total = 0;
        for (const auto& item : looseItems_) {
            total += item.goldValue;
        }
        for (const auto& bag : bags_) {
            for (const auto& item : bag.items) {
                total += item.goldValue;
            }
        }
        return total;
    }
};

int main() {
    NaiveInventory inv;
    inv.addLooseItem({"Iron Sword", 5.0, 150});
    inv.addLooseItem({"Health Potion", 0.5, 25});

    Bag backpack{"Adventurer's Backpack", 1.2, {
        {"Rope", 2.0, 10},
        {"Torch", 1.0, 5}
    }};
    inv.addBag(backpack);

    // Problem: Player found a "Herb Pouch" (a bag).
    // How to put Herb Pouch inside Adventurer's Backpack?
    // Naive Bag only holds std::vector<Item>, so it's impossible without hacking the struct!

    std::println("[Naive Inventory] Total weight: {:.1f} kg, Total value: {} gold",
                 inv.totalWeight(), inv.totalGoldValue());
}
Hardcoded to a single level of nesting

A Bag can only contain flat Items. If a player places an HerbPouch inside a LeatherBackpack, the naive model cannot represent it without constantly adding nested vector<Bag> subBags fields.

Duplicated calculation logic everywhere

Computing total weight requires handwritten nested loops across loose items, then bags, then items inside bags. Adding a 3rd level of nesting forces rewriting every traversal function.

Asymmetric caller code

Callers must distinguish between addItem(item) and addBag(bag). Client code cannot treat a single item and a container uniformly, violating polymorphism.

03

Step 2 β€” The fix: Composite Pattern

Core idea in one sentence

Compose objects into tree structures to represent part-whole hierarchies, allowing clients to treat individual objects and compositions of objects uniformly.

We define an abstract InventoryNode interface declaring weight(), goldValue(), and print(). Leaf nodes (LootItem) return their own values directly. Composite nodes (ContainerBag) own a collection of children via std::vector<std::unique_ptr<InventoryNode>> and recursively aggregate values from their children. The root PlayerInventory interacts purely with InventoryNode, so a bag containing 50 pouches is evaluated with the exact same call as a single sword.

Pattern roles in our story
Component (Interface)

InventoryNode β€” declares common operations (name(), weight(), goldValue(), print()) shared by items and containers.

Leaf

LootItem β€” primitive items (sword, potion, gem) that have no children and implement base calculations.

Composite

ContainerBag β€” container holding std::vector<std::unique_ptr<InventoryNode>> and recursively delegating operations to its children.

Client / Context

PlayerInventory β€” manages the root inventory container and provides top-level access.

Brute2Best Pro Content

Unlock Full UML Diagram & C++23 Solution

The interactive UML architecture diagrams, refactored C++23 code, real-world extensions, and interview Q&A are part of Brute2Best Pro. All 7 foundation chapters are 100% free.

All 440 visual dry runs & algorithms
Interactive step-by-step playback with pointer & array state transitions
52 comprehensive LLD chapters
Interactive UML diagrams, real-world stories (Food Delivery, Trading, RPG), and practice solutions
40 C++ basics labs & hardware memory models
Vtables, byte layouts, assembly insights, and multi-threading race conditions
Production-ready C++23 source code
Modern idioms, clean contracts, time/space complexity derivations, and interview takeaways

First anchor question per pattern, Part 1 LLD & Module 1 C++ basics are 100% Free forever.

Systems Integration

Related C++ Systems & Algorithmic Foundations

Deep dive into underlying layers