C++Guns – RoboBlog

01.06.2018

C++ Guns: Ein Paar Würfel

Filed under: Allgemein — Tags: — Thomas @ 14:06

Ich möchte zum Spaß ein Wüfelspiel programmieren. Dazu müssen einzelne Würfel implementiert werden. Doch was ist ein Würfel eigentlich? Offensichtlich ein Zufallszahlengenerator z.B. std::minstd_rand. Aber ein Würfel repräsentiert auch eine Gleichverteilung Ganzer Zahlen im Bereich von z.B. 1 bis 6. Und ein Würfel hat ein Status, seine Augenzahl. Die ändert sich ja nicht, egal wie oft man einen Würfel anschaut. Außer der Würfel wird neu geworfen, also gibt es eine roll() Funktion.
Und, ganz wichtig: Ein Würfel kann default initialisiert werden. Denn sobald ein Würfel existiert, zeigt er seine Augenanzahl an. Diese ist natürlich zufällig, aber existiert immer.

Zusammen mit meinem Generic Data Type Design Pattern sieht der Code etwas komisch aus, macht aber genau was er soll. Im Unterschied zu vorherigen Implementationen dieses Pattern ändern ein Würfel ständig seinen Zustand. Daher gibt es neben den einheitlichen const getter Funktionen z.B. number(), diesmal auch non-const setter, welcher aber als privat! deklariert sind. Denn nur der Würfel selbst darf seinen Zustand ändern.

Für mich wichtig ist, dass das Erstellen und Benutzen von Würfel-Objekten einfach und intuitiv ist. Sobald ein Würfel erstellt worden ist, ist er da und hat eine Zahl. Wenn der Würfel neu geworfen wird, ändert sich die Zahl. Ansonsten kann der Würfel nichts. Und so ist es doch gut.

Was mich noch richtig stört ist das seedn der Zufallgeneratoren. Jeder Würfel IST EIN Zufallsgenerator. Also muss der Seed immer unterschiedlich sein. Die Zeit in Sekunden kann hierfür nicht genommen werden, diese Ändert sich ja nur jede Sekunde. Daher wird noch auf einen realen Hardware Zufallsgenerator zugegriffen und der endgültige Seed mit std::seed_seq erstellt. std::seed_seq selbst darf aber kein temporäres Objekt sein, da der Zufallszahlengenerator beim initialisieren das std::seed_seq Objekt verändern darf.

template<int N=6>
struct Dice : std::tuple<std::minstd_rand, std::uniform_int_distribution<int>, int> {
    Dice() {
        // Dreck.
        std::seed_seq seed{unsigned(42), std::random_device()(), unsigned(std::time(nullptr))};
        gen() = std::minstd_rand(seed);
        dis() = std::uniform_int_distribution<int>(1,N);
        number() = dis()(gen());
    }
    
    auto roll() {
        number() = dis()(gen());
        return number();
    }
    
    auto number() const {
        return std::get<2>(*this);
    }
    
private:
    auto& gen() {
        return std::get<0>(*this);
    }
    
    auto& dis() {
        return std::get<1>(*this);
    }
    
    auto& number() {
        return std::get<2>(*this);
    }
};

template<int M>
struct Dices : std::array<Dice<>,M> {
    void rollAll() {
        for(auto& x : *this) {
            x.roll();
        }
    }
};


int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv);
    
    Dices<5> dices;
    
    for(int i=0; i < 3; ++i) {
        for(const auto& x : dices) {
            std::cout << x.number() << " ";
        }
        std::cout << "\n";
        dices.rollAll();
    }
    return 0;
}

5 2 3 5 1
5 6 3 1 3
2 6 5 5 2

No Comments

No comments yet.

RSS feed for comments on this post.

Sorry, the comment form is closed at this time.

Powered by WordPress