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

class AmmunitionVisitor;

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

    virtual void accept(const AmmunitionVisitor&) = 0;

    size_t amount() const { return _amount; }

private:
    size_t _amount = 0;
};

class RoundShot;
class ChainShot;
class GrapeShot;

class AmmunitionVisitor {
public:
    virtual ~AmmunitionVisitor() = default;
    virtual void visit(const RoundShot&) const = 0;
    virtual void visit(const ChainShot&) const = 0;
    virtual void visit(const GrapeShot&) const = 0;
};

class RoundShot : public Ammunition {
public:
    using Ammunition::Ammunition;
    void accept(const AmmunitionVisitor& visitor) {
        visitor.visit(*this);
    }
};

class ChainShot : public Ammunition {
public:
    using Ammunition::Ammunition;
    void accept(const AmmunitionVisitor& visitor) {
        visitor.visit(*this);
    }
};

class GrapeShot : public Ammunition {
public:
    using Ammunition::Ammunition;
    void accept(const AmmunitionVisitor& visitor) {
        visitor.visit(*this);
    }
};

class DamageVisitor : public AmmunitionVisitor {
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 AmmunitionVisitor {
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";
    }
};

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