#include <algorithm>
#include <cassert>
#include <functional>
#include <iostream>
#include <memory>
#include <set>
#include <variant>
#include <vector>

class AbstractVisitor;

class Ammunition {
public:
    Ammunition(size_t amount)
        : _amount(amount) {}
    virtual ~Ammunition() = default;

    virtual void accept(const AbstractVisitor&) = 0;

    size_t amount() const { return _amount; }

private:
    size_t _amount = 0;
};

class RoundShot;
class ChainShot;
class GrapeShot;

class AbstractVisitor {
protected:
    // Can be only use by derived class
    virtual ~AbstractVisitor() = default;
};

class RoundShotVisitor {
public:
    virtual void visit(const RoundShot& shot) const = 0;

protected:
    virtual ~RoundShotVisitor() = default;
};

class GrapeShotVisitor {
public:
    virtual void visit(const GrapeShot& shot) const = 0;

protected:
    virtual ~GrapeShotVisitor() = default;
};

class ChainShotVisitor {
public:
    virtual void visit(const ChainShot& shot) const = 0;

protected:
    virtual ~ChainShotVisitor() = default;
};

class DamageVisitor : public AbstractVisitor,
                      public RoundShotVisitor,
                      public GrapeShotVisitor,
                      public ChainShotVisitor {
public:
    void visit(const RoundShot& shot) const override {
        std::cout << "Round shot make 20 damages\n";
    }
    void visit(const ChainShot& shot) const override {
        std::cout << "Chain shot make 30 damages\n";
    }
    void visit(const GrapeShot& shot) const override {
        std::cout << "Grape shot make 40 damages\n";
    }
};

class SoundVisitor : public AbstractVisitor,
                     public RoundShotVisitor,
                     public GrapeShotVisitor,
                     public ChainShotVisitor {
public:
    void visit(const RoundShot& shot) const override {
        std::cout << "Fiuuuuuuuu Bam!\n";
    }
    void visit(const ChainShot& shot) const override {
        std::cout << "Fiuuuuuuuuuuu Bam bam!\n";
    }
    void visit(const GrapeShot& shot) const override {
        std::cout << "Fiuuuuuuuuuuu Bam bam bam bam bam!\n";
    }
};

class RoundShot : public Ammunition {
public:
    using Ammunition::Ammunition;
    void accept(const AbstractVisitor& visitor) {
        if (const auto* v = dynamic_cast<const RoundShotVisitor*>(&visitor)) {
            v->visit(*this);
        }
    }
};

class ChainShot : public Ammunition {
public:
    using Ammunition::Ammunition;
    void accept(const AbstractVisitor& visitor) {
        if (const auto* v = dynamic_cast<const ChainShotVisitor*>(&visitor)) {
            v->visit(*this);
        }
    }
};

class GrapeShot : public Ammunition {
public:
    using Ammunition::Ammunition;
    void accept(const AbstractVisitor& visitor) {
        if (const auto* v = dynamic_cast<const GrapeShotVisitor*>(&visitor)) {
            v->visit(*this);
        }
    }
};

int main() {
    std::unique_ptr<Ammunition> grapeShot = std::make_unique<GrapeShot>(100);
    SoundVisitor soundVisitor;
    DamageVisitor damageVisitor;
    grapeShot->accept(soundVisitor);
    grapeShot->accept(damageVisitor);
}
