Page 1 of 1

Where to start on discovering the types?

Posted: Mon Aug 21, 2023 12:19 pm
by punt
I have stated I wanted to first determine the different types that uox will use, and try to group related data/functionality together. But how to go about it, and where to start.
Nouns are a good place to start. Look over the code or the problem, and start describing it in terms of nouns. Those nouns are a place to examine.

Let's take an example. In UO/UOX3 one has SKILLs. That can be used as a noun, let's examine it a bit closer.
What is a skill:
There is a type (what skill is this).
It has a value
It has a limits (a range of what the value can vary between).
It has an attribute(lock) that can influence if the skill value can vary (up, down, constant)
And it has a way to influence its advancement though some defined mechanism (more on this later).

The first I noticed, is that among this set of description, there is a subset of groups.
Value seems to be closely associated , with the limits, and lock.
And the concept of a limit (range) seems pretty defined all by itself( and might be generically useful).

So it would seem, that this "might" break down to:
range
RangeValue (a value,range,lock)
Skill (type definitions, collection of RangeValues for each type, and a collection of advancement items).

One can organize many ways, but this is how I decomposed it, and my thought process.

So now lets look at that.

Re: Where to start on discovering the types?

Posted: Mon Aug 21, 2023 12:25 pm
by punt
What is a range? it is really just two values, a low watermark, and high watermark.
So we want something, that offers: low, high.

Ideally it would be generic, as to allow different data types (ranges of ints, chars, floats, etc). So this might be a candidate for a template.

What is some of the common functionality we use with a range? If a value is contained in that range, some random value from that range are the to most common ones that come to mind. I also need way to describe this in terms of text, and take that text in to make a range.

Armed with that, this might be how a range type might look like (again, the implementation is not necessarily important for this discussion, just the concept and thought of how one got to defining it).

Code: Select all

    template <typename T>
    struct range_t {
        static_assert(std::is_integral_v<T>,"range_t must be an integral type");
        T lowValue ;
        T highValue ;
        
        auto convert(const std::string &value) const -> T {
            if (std::is_signed_v<T>) {
                return static_cast<T>(std::stoll(value)) ;
            }
            else {
                return static_cast<T>(std::stoull(value)) ;
            }
        }
        //============================================================
        range_t() {
            this->lowValue = 0;
            this->highValue = 0;
        }
        //============================================================
        range_t(T low, T high) {
            this->lowValue = low;
            this->highValue = high;
        }
        //============================================================
        range_t(const std::string &value) {
            auto pos = value.find(",") ;
            if (pos == std::string::npos){
                throw std::runtime_error("Invalid string passed to range");
            }
            lowValue = convert(value.substr(0,pos)) ;
            pos = value.find_first_not_of(" \t\f\r\v",pos+1);
            if (pos == std::string::npos) {
                throw std::runtime_error("Invalid string passed to range");
            }
            highValue = convert(value.substr(pos)) ;
        }
        //============================================================
        auto inRange(T value) const ->bool {
            return value >= lowValue && value<= highValue ;
        }
        //============================================================
        auto describe() const -> std::string{
            return  std::to_string(lowValue) + std::string(",") + std::to_string(highValue) ;
        }
        //============================================================
        // This is a another way to check if in range
        auto operator==(T value) const ->bool {
            return this->inRange(value);
        }
        //============================================================
        auto operator!=(T value) const ->bool{
            return !this->inRange(value);
        }
        //============================================================
        auto operator<(T value) const ->bool {
            return value < this->lowValue;
        }
        //============================================================
        auto operator>(T value) const ->bool {
            return value > this->highValue;

        }
        //============================================================
        auto random() const -> T {
            return effolkronium::random_static::get(this->lowValue,this->highValue) ;
        }

    };

Re: Where to start on discovering the types?

Posted: Mon Aug 21, 2023 12:30 pm
by punt
With range under our belt, we next tackle RangeValue (the grouping of a value, a range, and a lock).
But there are different lock types (increase,descrease,fixed). So we need to define that as well. And of course, we need a way to describe the type (input/output). There is a lot of support to provide "text" descriptions of our internal types (like LockType names), and functions to support them.
A common function may be adjusting the value by some amount (up or down), and then understanding how much we actually did adjust it (due to the range capping it).
So given this, and armed with our range type, we could define something like this:

Code: Select all

//======================================================================
// RangeValue
//=====================================================================
//=====================================================================
/**
 Type has a value, and a range it can not exceed.  In addition, it
 has a lock attribute, that the user can restrict if it goes up or down.
 */
struct RangeValue {
    enum LockType {INCREASE,DECREASE,LOCKED};
    static constexpr auto MAX_VALUE = 1000 ;
    static constexpr auto MIN_VALUE = 0 ;
    static const std::map<LockType,std::string>LOCK_TYPE_NAMES ;
    static auto nameForType(LockType type)  -> const std::string & ;
    static auto typeForName(const std::string &name) -> LockType ;

    util::range_t<int> range ;
    int value ;
    LockType lock;
    /**
     Provides a text representation of the value
     */
    auto describe() const -> std::string ;
    /**
     Constructor, provide the range, the initial value will bet set to the
     min value.
     - Parameters:
        - maxValue: The maximum value the value can obtain.
        - minValue: The minimum value the value can be
     */
    RangeValue(int maxVALUE = MAX_VALUE,int minValue = MIN_VALUE,LockType lock = RangeValue::INCREASE) ;
    /**
     Constructor, initailize by the text representation of the entry
     - Parameters:
        - line: Text string that is formatted: "value,locktype;range" Where range is "min,max"
     */
    RangeValue(const std::string &line) ;
    /**
     Constructor, initialize by the components
     - Parameters:
        - value: The value of the entry
        - range: The range of the entry;
        - lock:  The lock type of the entry
     */
    RangeValue(int value, util::range_t<int> &range, LockType lock);
    /**
     Applies a change to the entry (negative or positive). If the change
     would exceed the range, the actual change is limited by the range.
     - Parameters:
        - change: The amount ot change the value by (negative or positive)
     - Returns: The amount of change that actually occurred (impacted by the range)
     */
    [[maybe_unused]] auto applyDelta(int change) -> int;
    auto operator+=(int change) -> RangeValue&;
    auto operator-=(int change) -> RangeValue&;
    auto operator<(const RangeValue &entry) const  -> bool ;
    auto operator==(const RangeValue &entry) const -> bool ;
    auto operator!=(const RangeValue &entry) const -> bool ;
};

Re: Where to start on discovering the types?

Posted: Mon Aug 21, 2023 12:36 pm
by punt
Now, I mentioned Advancments. Those are constant in the fact that they don't vary by every instance of the same skill. They are defined once, for each skill. We could keep these separate in their own container, but it is going to be needed when we deal with skills. So let's make it a static container as part of our Skill type.

Given this, Skill might look something like this (again, a lot of addition to support description, and some other static attributes (delay, verb when used, etc).
I haven't added any "functionality" we might want on a skill, but we have a nice grouping to add it to, when we determine what all that might be.

Code: Select all

//======================================================================
struct Skill {
    static constexpr auto TOTAL_SKILL_CAP = 7000 ;
    static constexpr auto DEFAULT_SKILL_DELAY = 1 ;
    enum Type {
        ALCHEMY = 0,        ANATOMY,        ANIMALLORE,         ITEMID,
        ARMSLORE,           PARRYING,       BEGGING,            BLACKSMITHING,
        BOWCRAFT,           PEACEMAKING,    CAMPING,            CARPENTRY,
        CARTOGRAPHY,        COOKING,        DETECTINGHIDDEN,    ENTICEMENT,
        EVALUATINGINTEL,    HEALING,        FISHING,            FORENSICS,
        HERDING,            HIDING,         PROVOCATION,        INSCRIPTION,
        LOCKPICKING,        MAGERY,         MAGICRESISTANCE,    TACTICS,
        SNOOPING,           MUSICIANSHIP,   POISONING,          ARCHERY,
        SPIRITSPEAK,        STEALING,       TAILORING,          TAMING,
        TASTEID,            TINKERING,      TRACKING,           VETERINARY,
        SWORDSMANSHIP,      MACEFIGHTING,   FENCING,            WRESTLING,
        LUMBERJACKING,      MINING,         MEDITATION,         STEALTH,
        REMOVETRAP,         NECROMANCY,     FOCUS,              CHIVALRY,
        BUSHIDO,            NINJITSU,       SPELLWEAVING,       IMBUING,
        MYSTICISM,          THROWING
    };
    static const std::map<Skill::Type,std::string> SKILL_TYPE_NAMES ;
    static auto nameForType(Skill::Type type)  -> const std::string & ;
    static auto typeForName(const std::string &name) -> Skill::Type ;

    static const std::map<Skill::Type,std::string> MAKEWORD_FOR_SKILL ;
    static auto makeWordForSkill(Skill::Type type) -> const std::string& ;

    static const std::map<Skill::Type,int> DELAY_FOR_SKILL ;
    static auto delayForSkill(Skill::Type type) -> int ;

    static const std::string EMPTY_STRING ;
    static std::vector<Advancement> advancement ;
    static auto loadAdvancement(const std::filesystem::path &path) -> void;
    
    std::vector<RangeValue> skills ;
    Skill() ;
};