#include <algorithm>
#include <iostream>
#include <map>
#include <string>
#include <vector>

extern "C"
{
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
}

inline unsigned randint(unsigned x)
{
        //return (unsigned)floor((double)rand() * (double)x / (double)RAND_MAX);
        return rand() % x;
}

inline bool magik(unsigned x)
{
        return randint(100) < x;
}

class SentientWeapon
{
public:
        static const unsigned NEW_GROUP_CHANCE = 40;

        unsigned pval, pval2, to_h, to_d, totalPoints;
        std::vector<std::string> realms;

        SentientWeapon(void)
                : pval(1)
                , pval2(0)
                , totalPoints(0)
                , to_h(0)
                , to_d(0)
        {
                if(not allRealmsInitialized)
                        initRealms();
        };

        void gainRealm(void)
        {
                unsigned tries = 1000;
                while(tries-- > 0)
                {
                        unsigned rv = randint(allRealms.size());
                        std::string realm = allRealms[rv];

                        std::vector<std::string>::iterator result =
                             find(realms.begin(), realms.end(), realm);

                        if((result == realms.end()) and
                           (realmPrices[realm] <= pval2))
                        {
                                pval2 -= realmPrices[realm];
                                realms.push_back(realm);
                                return;
                        }
                }
        };

        void gainPower(void)
        {
                // Unimplemented
        };

        void gainLevel(void)
        {
                unsigned die = randint(100);
                if(die < 33)
                {
                        to_h += randint(2);
                        to_d += 1;
                }
                else if(die < 66)
                {
                        to_h += 1;
                        pval2 += 1;
                        totalPoints += 1;

                        if(magik(NEW_GROUP_CHANCE))
                                gainRealm();
                }
                else
                {
                        if(realms.size() == 0)
                                gainRealm();

                        gainPower();

                        if(pval == 0)
                        {
                                pval = 1;
                        }
                        else
                        {
                                while(magik(20 - pval * 2) and (pval < 5))
                                        pval += 1;
                        }
                }
        };

        static std::map<std::string, unsigned> realmPrices;
        static std::vector<std::string> allRealms;
        static bool allRealmsInitialized;

        static void initRealms(void)
        {
                realmPrices["Fire"] = 1;
                realmPrices["Cold"] = 1;
                realmPrices["Lightning"] = 1;
                realmPrices["Poison"] = 2;
                realmPrices["Acid"] = 3;
                realmPrices["Air"] = 5;
                realmPrices["Earth"] = 5;
                realmPrices["Mind"] = 7;
                realmPrices["Shield"] = 7;
                realmPrices["Chaos"] = 7;
                realmPrices["Magic"] = 10;
                realmPrices["Antimagic"] = 10;
                for(std::map<std::string, unsigned>::iterator i = realmPrices.begin();
                    i != realmPrices.end(); ++i)
                {
                        allRealms.push_back((*i).first);
                }

        };
};

bool SentientWeapon::allRealmsInitialized = false;
std::map<std::string, unsigned> SentientWeapon::realmPrices;
std::vector<std::string> SentientWeapon::allRealms;

int main(int, char **)
{
        srand(time(0));
        std::vector<SentientWeapon> weaponList;

        for(int i = 0; i < 10000; ++i)
        {
                SentientWeapon weapon;
                for(int j = 1; j < 50; ++j)
                        weapon.gainLevel();

                weaponList.push_back(weapon);
        }

        std::map<std::string, unsigned> realmCount;
        for(std::vector<std::string>::iterator i = SentientWeapon::allRealms.begin();
            i != SentientWeapon::allRealms.end(); ++i)
        {
                realmCount[*i] = 0;
        }

        std::map<unsigned, unsigned> pvalCount;
        for(unsigned i = 1; i <= 5; ++i)
        {
                pvalCount[i] = 0;
        }

        for(std::vector<SentientWeapon>::iterator i = weaponList.begin();
            i != weaponList.end(); ++i)
        {
                SentientWeapon &weapon = *i;

                pvalCount[weapon.pval] = pvalCount[weapon.pval] + 1;
                for(std::vector<std::string>::iterator i = weapon.realms.begin();
                    i != weapon.realms.end(); ++i)
                {
                        realmCount[*i] = realmCount[*i] + 1;
                }
        }

        for(std::map<std::string, unsigned>::iterator i = realmCount.begin();
            i != realmCount.end(); ++i)
        {
                char percentString[10];
                snprintf(percentString, 10, "%.3f%%", 100.0 * (*i).second / weaponList.size());
                std::cout << (*i).first << ": " << percentString << std::endl;
        }

        for(unsigned i = 1; i <= 5; ++i)
        {
                char percentString[10];
                snprintf(percentString, 10, "%.3f%%", 100.0 * pvalCount[i] / weaponList.size());
                std::cout << i << ": " << percentString << std::endl;
        }

        return 0;
}

Chatter

KhymChanur: About this code snippet:

                        if(pval == 0) pval = 1;
                        while(magik(20 - pval * 2) and (pval < 5))
                                pval += 1;

If my understanding of the spoiler is correct, if the 34% chance "gain a power and attempt to raise pval" happens, then it will only attempt to increase the pval once, rather than keep on increasing the pval until an attempt fails:

However, I can't find the pval increasing code in the 2.x files, so maybe your version is right.

NeilStevens: I didn't reference any spoilers when writing this. I used your perl code to look up the realms and their prices, and I used object1.c for the implementation. The actual snippet is:

if (!o_ptr->pval) o_ptr->pval = 1;
else
{
 while (magik(20 - (o_ptr->pval * 2))) o_ptr->pval++;
 if (o_ptr->pval > 5) o_ptr->pval = 5;
}

Oops... I messed up... I missed the "else" there. My code gives an extra chance at extra gains that it shouldn't, as in the case when pval == 0 it should go to 1 and quit, while I set it to one then send it into the loop. Grr, that's really poor C style, the way that if/else is structured, so I say it's not my fault!

I'm sure by the time we're all done with tome 3, this won't happen again, right?

KhymChanur: Ah, right, I was grepping for TR4_LEVELS, so I missed that code. However, the pval code still isn't quite right, because object_prep() ensures that the pval for sentient weapons always starts at 1, so there's no need to check is the pval is 0.

NeilStevens: Nice, so the error would have no effect if I went in and finished the implementation. That's two of those mistakes I made.

Fixing it anyway though.

NeilStevens: Updated with more pval stuff.

By the way, STL is *slow*. I wrote this in C++ instead of Ruby in the hopes of getting something fast. My mistake.

Spoilers/Sentient Weapons/Statistics/tome2sentience.cpp (last edited 2006-03-10 02:27:44 by NeilStevens)