2017-04-18 2 views
-1

Ich habe ein Problem mit einem bestimmten Codeabschnitt, der einem Gegner basierend auf seinem voreingestellten Schwierigkeitsgrad eine zufällige Menge von Elementen geben soll. Ich verwende eine modifizierte Template-Version der zufälligen Funktion von Bjorne. Als ich seine ursprüngliche Version verwendet hatte ich immer noch das Problem:Verwenden von Zufallszahlen innerhalb von Switch-Statement-Fällen

Random number generator code from Stroustrup:

template<class T> 
T rand_num(const T & low, const T & high) 
{ 
    static std::default_random_engine re{}; 
    using Dist = std::uniform_int_distribution<T>; 
    static Dist uid{}; 
    return uid(re, Dist::param_type{ low,high }); 
} 

Wenn ich den Abschnitt zu testen, indem sie ein EnemyAI Objekt, das die OffensiveEntity und Einstellung der Schwierigkeit zu kapselt, sagen wir, 1, ist es immer setzt die Zufallszahl auf 1 und wählt immer in diesem Fall den Gesundheitstrank. Wenn ich die zweite if-Anweisung auf eine Bedingung von if (tempRandom == 1) setzen würde, würde sie den Stick wählen.

void EnemyAI::Equip() 
{ 
    m_offensiveEntity->ClearItems(); 

    std::vector<std::shared_ptr<Item>> tempItems; 

    int tempRandom = 0; 

    switch (m_difficultyLevel) 
    { 
    case 0: 
    case 1: 
    { 
     tempRandom = rand_num<int>(1, 4); 
     if ((tempRandom == 1) || (tempRandom == 2) || (tempRandom == 3) || (tempRandom == 4)) 
      tempItems.push_back(CreateTempItem("Health Potion : HP", 3, 3, 
-10, Result::Effect::nothing)); 
     if (tempRandom == 3) 
      tempItems.push_back(CreateTempItem("Wooden Stick : DMG", 5, 2, 10, Result::Effect::nothing, 3, 13, Result::Effect::nothing)); 
     break; 
    } 
    case 2: ... etc 

Was ist die Ursache für dieses Problem? Hier ist die Ausgabe:

Health Potion : HP name 
3 durability 
-10 total damage 
3 energy cost 
0 effect 

Wenn in dieser Typisierung:

int main() 
{ 
    std::shared_ptr<EnemyAI> offensiveEntityInterface = 
     std::make_shared<EnemyAI>(EnemyAI("Dank Memerson", 50, 1)); 

    offensiveEntityInterface->Equip(); 
    for (auto & i : offensiveEntityInterface->GetEquiped()) 
    { 
     std::cout << i->GetName() << " name \n"; 
     std::cout << i->GetHP() << " durability \n"; 
     std::shared_ptr<const Result> tempResult = i->Use(); 
     std::cout << tempResult->m_totalDamage << " total damage \n"; 
     std::cout << tempResult->m_energyCost << " energy cost \n"; 
     std::cout << tempResult->m_effect << " effect \n"; 
    } 
    std::cin.get(); 
    return 0; 
} 

Heres der Quellcode in einem Pastebin https://pastebin.com/F4Q74Gc6

Antwort

1

Ihr Motor ist ungeimpften und wird somit einen Standardwert verwenden, das ist Das gleiche gilt jedes Mal, wenn Sie Ihr Programm ausführen. Um sicherzustellen, dass es bei jedem Lauf anders ist, müssen Sie es säen.

Einige Old-Guard-Programmierer empfehlen normalerweise, die aktuelle Systemuhr als Integer zu bekommen und diese als Seed zu verwenden, aber C++ 11 empfiehlt ausdrücklich, std::random_device zu verwenden, um echte Zufallszahlen durch Lesen von Temperatursensoren zu erhalten die CPU oder auf ähnliche Weise, und wird auf eine Pseudozufallsmethode zurückgreifen (wie die Systemuhr oder eine andere Methode greifen), wenn es dazu nicht in der Lage ist.

template<class T> 
T rand_num(const T & low, const T & high) 
{ 
    static std::default_random_engine re{std::random_device{}()}; 
    using Dist = std::uniform_int_distribution<T>; 
    static Dist uid{}; 
    return uid(re, Dist::param_type{ low,high }); 
} 

Auch Ihr Code verwenden, wird Sie für jeden Typ einen neuen Motor schaffen Zahlen zu erzeugen mit. Wenn Sie beispielsweise rand_num<int32_t>, rand_num<int64_t>, rand_num<int16_t> aufrufen, sind dies drei verschiedene Engines, die von Ihrer Laufzeit auf dem Stack zugeordnet und verwaltet werden. Es sei denn, Sie das Gefühl haben, dass auf jeden Fall tun müssen, sind Sie besser dran Code zu schreiben, der wie folgt aussieht:

template<class T> 
T rand_num(std::default_random_engine & re, const T & low, const T & high) 
{ 
    using Dist = std::uniform_int_distribution<T>; 
    static Dist uid{}; 
    return uid(re, Dist::param_type{ low,high }); 
} 

int main() { 
    std::default_random_engine engine{std::random_device{}()}; 
    /*...*/ 
    int val = rand_num<int>(engine, 1, 4); 
    /*...*/ 
} 

Viele C++ Implementierungen map std::default_random_engine zu std::mt19937, die einen ziemlich schweren Gegenstand ist, bestehend aus vielen Staats das muss zugewiesen und generiert werden, wann immer es erstellt wird. Sicherzustellen, dass Sie den Motor immer wieder verwenden, kann einige Zeit sparen.

+0

Also Ihr zweites Beispiel ist nicht notwendig, wenn Sie die standardmäßige Zufallsmaschine als statisch wie im ersten Beispiel deklarieren? Wenn die Funktion mit dem std :: default_random_engine-Engine-Objekt mit std :: random_device in der zweiten Funktion aufgerufen wird, wird das exakt gleiche Ergebnis erzeugt, wenn das statische std :: default_random_engine-Engine-Objekt in der Funktion initialisiert und aufgerufen wird. – Chopdops

+0

@Chopfdops Die Ausgabe zwischen den beiden Versionen unterscheidet sich nicht wesentlich in dem, was sie "tun", d. H. Beide arbeiten, um Zufallszahlen in einem bestimmten Bereich zu erzeugen. Der Unterschied besteht darin, dass die erste Version neue Instanziierungen von 'std :: default_random_engine' für jeden eindeutigen Typ erzeugt, mit denen die Funktion aufgerufen wird, während die zweite Version nur eine Instanziierung erstellt, unabhängig davon, wie viele Typen Sie verwenden (außer Sie erstellen manuell) mehr Motoren selbst). – Xirema

+0

@Chopdops Der Grund, warum das wichtig ist, ist, dass "engine" -Objekte ziemlich teuer zu konstruieren sind, besonders wenn 'std :: default_random_engine' auf' std :: mt19937' mappt, was bei vielen C++ - Implementierungen Standard ist. Wenn Sie wissen, dass Sie nur 'rand_num ' aufrufen und keine andere Version dieser Funktion aufrufen, dann ist der Unterschied egal und Sie können die erste Version verwenden. Wenn nicht, erleiden Sie jedes Mal, wenn ein Typ zum ersten Mal verwendet wird, Leistungseinbußen. – Xirema

Verwandte Themen