beliefcalcs.t

documentation
#charset "us-ascii"
#include "advlite.h"

/* 
 *   BELIEF CALCULATIONS EXTENSION by Eric Eve
 *
 *   Version 1.0 23rd April 2024
 *
 *   The (curernlty experimental) beliefcalcs.t extension implements various calculations involving
 *   the degree of belief enums likely, dubious, unlikely and untrue, together with true.
 *
 *   To do this it defines a set of corresponding wrapper objects bTrue, bLikely, bDubious,
 *   bUnlikely, and bUntrue, which asssign a probability score to each of these possible values.
 *
 *   To perform calculations with the belief values, it's necessary to wrap them in the
 *   corresponding wrapper object (of the BelVal class). This can be done with the BV() macro; e.g.
 *   BV(dubious) evaluates to bDubious, and BV(x) evaiuaates to bLikely if x is likely.
 *
 *   This then allows us to use the following expressions:
 *
 *.     BV(a) | BV(b)   the belief enum for a or b
 *.     BV(a) + BV(b)   the probability (as a number out of 100) of a or b.
 *.     BV(a) & BV(b)   the belief enum for a and b
 *.     BV(a) + BV(b)   the probability (as a number out of 100) of a and b.
 *.     ~BV(a)          the negation/complement of a, e.g. ~BV(likely) = unlikely
 *.     BV(a) >> BV(b)  test whether a.score > b.score
 *.     BV(a) << BV(b)  test whether a.score < b.score
 *.     BV(a) >>> BV(b) test whether a.score >= b.score
 *
 *   One major limiation of this extension is that it does not repreaent how most people reason
 *   about probabilities in practice. A second, related, limitation is that the probabilities
 *   assigned to each enum (except for true and untrue, at 100 and 0 respectively) are somewhat
 *   arbitrary, e.g. should calling something 'likely' assign it a probability of 55% or 95%?
 *
 *   The extension does allow for further gradations if desired. For example one could define
 *   additional enums such as veryLikely and veryUnlikely and then define correspdonding objects of
 *   the BelVal class:
 *
 *.  bVeryLikely: BelVal status = veryLikely score = 90;
 *.  bVeryUnlikely: BelVal status = veryUnlikely score = 10;
 *
 *   Note that this extension does NOT require the facts.t module to be present (although it is
 *   [perfectly compatinle with it.
 */
 

/* 
 *   Object that carries out the preinitialization for the BelVal class. */
beliefManager: PreinitObject   
    
//    /* Function to convert arg from a belief type to an integer or vice versa */
//    convert(arg)
//    {
//        switch(dataType(arg))
//        {
//        case TypeObject:
//            if(arg.ofKind(BelVal))
//                return arg.score;
//            return nil;
//        case TypeEnum:
//        case TypeTrue:
//            return BV(arg).score;
//        case TypeInt:
//            return intToVal(arg);
//        default:
//            return nil;
//        }        
//    }
    
    
    
//    /* Function to compute the result of belielf type 1 AND belief type 2 */
//    and(belief1, belief2)
//    {
//        local b1 = convert(belief1);
//        local b2 = convert(belief2);
//        
//        if(b1==nil || b2 == nil)
//            return nil;
//        
//        return intToVal((b1 * b2) /100);
//    }
//    
//    /* Function to compute the result of belielf type 1 OR belief type 2 */
//    or(belief1, belief2)
//    {
//        local b1 = convert(belief1);
//        local b2 = convert(belief2);
//        
//        if(b1==nil || b2 == nil)
//            return nil;
//        
//        return intToVal(max(b1, b2));
//    }
    
//    /* 
//     *   Function to compare two belief types and return the numerical difference between them. A
//     *   poaitive result means belief 1 is believed that belief 2, a negative result that it is less
//     *   believed, and a result of 0 that the belief1 = belief 2
//     */
//    comp(belief1, belief2)
//    {
//        local b1 = convert(belief1);
//        local b2 = convert(belief2);
//        
//        if(b1==nil || b2 == nil)
//            return nil;
//        
//        return b1 - b2;
//    }
    
    /* Carry out our preinitialization */
    execute()
    {
        /* Set up a new working vector. */
        local vec = new Vector();
        
        /* 
         *   Obtain the list of value's from BelVal's valTab, which contains the initial boundary
         *   values for converting numerical probabilities to belief enums.
         */
        local vals = BelVal.valTab.valsToList();
        
        /* Obtain a list of keys from the same table */
        local keys = BelVal.valTab.keysToList();
        
        /* 
         *   Store the minumum key value. This should be the probability below which we regard
         *   something as untrue.
         */
        local minVal = keys.minVal(); 
        
        /* 
         *   Iterate through all BelVal objects in the game to constuct a vector containing all
         *   their scores.
         */
        for(local val = firstObj(BelVal); val != nil; val = nextObj(val, BelVal))
            vec.append(val);
        
        /* Sort the vector in descending order of score. */
        vec.sort(true, {x, y: x.score - y.score} );
        
        /* Iterate through the vector. */
        for(local i in 1..vec.length - 1)
        {            
            /* 
             *   Store the current item; this should have the higher score of the pair
             *   we're currently interested in.
             */
            local top = vec[i];
            
            /* 
             *   Store  the next item; this should have the lower score of the pair
             *   we're currently interested in.
             */
            local bottom = vec[i+1];
            
            /*   
             *   Calculate the mid-point of their two scores, which we'll use as the boundary
             *   between them.
             */
            local score = (top.score - bottom.score) / 2 + bottom.score;
                       
            /* 
             *   If we don't already have an entry for the top item of the pair and the bottom item
             *   is not the last in our list, store the mid-point score aa a key in our valTab table
             *   with the correspdonding enum as its key (to establish the minimum score for that
             *   value).             
             */
            if(vals.indexOf(top.status) == nil && i < vec.length - 1)
            {
                BelVal.valTab[score] = top.status;
            }
            /* 
             *   Otherwise, if we're at the penultime item, set the mimimum score for the final item
             *   (which should be untrue) to zero after setting the lower boundary for the next item
             *   up (by default unlikely) to what was previously the score for the final item. This
             *   might be a small number, so that we can regard a probability of less than, say 3%,
             *   as being effectively untrue.
             */
             
            else if(i == vec.length - 1)
            {
                BelVal.valTab[minVal] = top.status;
                BelVal.valTab[0] = bottom.status;
            }       
           
        }
        
        /* 
         *   Create a sorted list of the boundary values we've just stored in BekVal's valTab table
         *   and store them in BelVal's boundaries property, for BelVal to use to turn a probability
         *   into a belief enum.
         */
        BelVal.boundaries = BelVal.valTab.keysToList.sort();    
        
        /* 
         *   Iterate over all the BelVal objects in the game to add them into our bvTab table, which
         *   can be used to find the BelObject corresponding to any given belief enum.
         */
        for(local o = firstObj(BelVal); o != nil; o = nextObj(o, BelVal))
            bvTab[o.status] = o;
        
    }
    
    /* Our LookupTable for finding the BelVal object corresponding to any belief values. */     
    bvTab = [
        true -> bTrue
    ]    

;

/* 
 *   The BelVal class associates the belief enums with objects beginning with the letter b, e.g.
 *   bTrue is associated with true and bDubious with dubious.
 */

class BelVal: object
    status = nil    
    
    /* 
     *   Overridden operators to allow 'logical' calulations to be performed on belief enums; to use
     *   these operators we must either use the associated objects (e.g. bTrue for true) or wrap the
     *   enum in the BV macro, e.g. BV(true); the latter method will be needed when dealing with
     *   variables, e.g. BV(val) when val might be any of the enums
     */    
    operator &(x) { return intToVal(self * x); }
    operator |(x) { return intToVal(self + x); }
    
    operator -(x) { return self.score - x.score; }
    operator [] (x) {return bvTab[x]; }
    operator * (x) { return (self.score * x.score) / 100; }
    operator ~ () { return intToVal(100 - self.score); }
    operator + (x) { return 100 - ((100 - score) * (100 - x.score)) /100 ; }
    operator >> (x) { return self.score > x.score; }
    operator << (x) { return self.score < x.score; }
    operator >>> (x) { return self.score >= x.score; }
    
    /* Convert a number (a probability from 0 to 100) into a belief enum. */
    intToVal(num)
    {
        local idx = boundaries.lastValWhich({x: x <= num});    
        return valTab[idx];                                            
    }
    
    /* The Lookup Table that will be populated with the minimum scores for our belief enums. */
    valTab = [
        97 -> true,   
        3 -> untrue
    ]
    
    /* A list of the boundary scores between different belief enums. */
    boundaries = nil
   
    /* The score (probabiity as a number from 1 to 100) associated with this value. */
    score = 0

    
;


/* Define the five standard BelVal objects. */
bTrue: BelVal status = true score = 100;
bLikely: BelVal status = likely score = 75;
bDubious: BelVal status = dubious score = 50;
bUnlikely: BelVal status = unlikely score = 25;
bUntrue :BelVal status = untrue score = 0;

/* 
 *   Modify setRevealed() on libGlobal to accept a BelVal arg (by converting it to the equivalent
 *   enum)
 */
modify libGlobal
    setRevealed(tag, arg?)
    {        
        if(arg && objOfKind(arg, BelVal))
            arg = arg.status;
        
        inherited(tag, arg);
    }
;

/* 
 *   Modify setInformed() on Thing to accept a BelVal arg (by converting it to the equivalent
 *   enum)
 */
modify Thing
    setInformed(tag, val?)
    {
         if(val && objOfKind(val, BelVal))
            val= val.status;
        
        inherited(tag, val);
    }   
;
    
Adv3Lite Library Reference Manual
Generated on 03/07/2024 from adv3Lite version 2.1