aipBase.cpp0000644000076400007640000003130011063254442012104 0ustar brianbrian//********************************************************************** // aipBase.cpp - function bodies for aipBase.h // // Copyright (c) 1999, 2005, 2007, 2008 Brian Marshall // // See the license at end of this file. // // Developers/Contributers: // [BRM] Brian Marshall - Calgary - bmarshal@agt.net // // 08/07/09 [BRM] new random_num(), logging, is_one_chance_in_x() // multiple small changes // 05/11/07 [BRM] random_num() now in class aipBase // 05/11/01 [BRM] added aip_strncpy() that always terminates string // 05/09/10 [BRM] development began // //---------------------------------------------------------------------- #include "aipBase.h" #include #include using namespace std; //====================================================================== // Definition of static members aipRand aipBase::m_rand; // random number generator aipLogger * aipBase::m_logger = 0; // optional logger //====================================================================== // Global functions // //---------------------------------------------------------------------- // strncpy that terminates the string, returns length int aip_strncpy (char *dest, const char *src, int max_len) { strncpy (dest, src, max_len); dest[max_len] = '\0'; return strlen(dest); } //---------------------------------------------------------------------- // Get a string from string x and write it to val. // // One or more sequential delimiters starting at x are ignored. // The string ends just before the terminating delimiter, and a // pointer to this delimiter is returned (or zero on failure). // // If the specified delimiter is '~', any character other than // a letter or digit will be a delimiter. // // The string will not be longer than maxlen (up to 100). const char * aip_get_str (const char *x, long maxlen, char *str, char delim) { if (maxlen < 1) return 0; if (maxlen > 100) maxlen = 100; *str = '\0'; int slen = 0; if (!x) return 0; while (1) { if ( aip_is_delim(*x,delim) ) { if (!slen) { x++; continue; // skip over leading delimiters } else { break; // found end of string } } if (slen < maxlen) { *str++ = *x; slen++; } x++; } *str = '\0'; if (!slen) return 0; // string was not found return x; // last character read from x } //---------------------------------------------------------------------- // Get an long value from string x and write it to val. // // One or more sequential delimiters starting at x are ignored. // The string ends just before the terminating delimiter, and a // pointer to this delimiter is returned (or zero on failure). // // If the specified delimiter is '~', any character other than // a letter or digit will be a delimiter. const char * aip_get_val (const char *x, long maxlen, long *val, char delim) { char buf[21]; const char *last_char = aip_get_str (x, 20, buf, delim); if (!last_char) return 0; if (maxlen < 10) buf[maxlen] = '\0'; *val = atol(buf); return last_char; } //---------------------------------------------------------------------- // Return true if x is a delimiter character. If delim is '~', // any char other than a letter or digit is a delimiter. int aip_is_delim (char x, char delim) { if (delim == '~') { if ( !( (x >= '0' && x <= '9') || (x >= 'A' && x <= 'Z') || (x >= 'a' && x <= 'z') ) ) return 1; } else { if (x == delim || x == '\0' || x == '\n') return 1; } return 0; } //====================================================================== // aipRand // //---------------------------------------------------------------------- // sub-random-number-generator - // Return a pseudo-random number from the set [0,1,2,3,...,9999]. // // This function can be used to implement 11 different sub-rngs - // for each parameter, there is an array with 11 values. At any // time, there are two sub-rngs - rand_1() calls this function // and uses the first set of parameters; rand_2() calls this // function and uses one of the remaining 10 sets of parameters. // // rand_1() and rand_2() produce low-quality random numbers; // combining them produces a better quality of random numbers. long aipRand::sub_rng (long idx) { if (idx < 0) idx = 0; if (idx >= Num_Sub_RNG) idx = Num_Sub_RNG - 1; long a = Rand_a[idx]; long c = Rand_c[idx]; long m = 10000; long x; if (idx == 0) { // for rand_1() x = m_rand_seed_1 = (a * m_rand_seed_1 + c) % m; } else { // for rand_2() x = m_rand_seed_2 = (a * m_rand_seed_2 + c) % m; } return x; } //---------------------------------------------------------------------- // first sub-RNG (that does not change) long aipRand::rand_1 () { return sub_rng(0); } //---------------------------------------------------------------------- // second sub-RNG (that changes periodically) long aipRand::rand_2 () { if (m_rand_2_idx < 1) m_rand_2_idx = 1; if (m_rand_2_idx >= Num_Sub_RNG) m_rand_2_idx = Num_Sub_RNG - 1; return sub_rng(m_rand_2_idx); } //---------------------------------------------------------------------- // return a pseudo-random number in the range 0 to 9999 inclusive // // Two low-quality random number are generated, with sub-RNGs // rand_1() and rand_2(), and combined to make a better-quality // random number. // // After this function is called a certain number of times, // the following static members are changed: // - which set of parameters, including the starting seed, // used by rand_2() is randomly changed // - number of calls until we do this again is randomly set long aipRand::random_num () { if (m_rand_2_idx < Rand_Null_Test) { // initial setup m_rand_2_idx = 1; m_rand_count = 500; m_rand_seed_1 = Rand_s[0]; m_rand_seed_2 = Rand_s[m_rand_2_idx]; for (int i=0; i<100; i++) { m_deck[i] = rand_1(); } m_deck_len = 100; } if ( --m_rand_count <= 0 ) { // use different sub-RNG in rand_2() m_rand_2_idx = (rand_1() * (Num_Sub_RNG-1)) / 10000 + 1; m_rand_count = 300 + ( (rand_1()*500) / 10000 ); m_rand_seed_2 = Rand_s[m_rand_2_idx]; } long j = rand_2() / 100; long x = m_deck[j]; m_deck[j] = rand_1(); return x; } //====================================================================== // aipBase - return a pseudo-random number in the range 0 to 9999 // //---------------------------------------------------------------------- // return a pseudo-random number in the range 0 to 9999 long aipBase::random_num () { return m_rand.random_num (); } //---------------------------------------------------------------------- // return a pseudo-random number in the specified range long aipBase::random_num (long lo, long hi) { if (lo == hi) return lo; if (lo > hi) { long temp_flip = lo; lo = hi; hi = temp_flip; } if ( (hi-lo) > 100000 ) hi = lo + 100000 + 1; long x = ( random_num() * ((hi-lo)+1) ) / 10000 + lo; return x; } //---------------------------------------------------------------------- // Return true 1 in x times, otherwise return false. int aipBase::is_one_chance_in_x (long x) { if (x < 2) return 1; if (x > 100000) return 0; return (random_num(1,x)==1) ? 1 : 0; } //---------------------------------------------------------------------- // Write a character string to the log (generally for debugging) void aipBase::log (const char *x) const { if (m_logger) { m_logger->log(x); } else { cout << x; } } //---------------------------------------------------------------------- // Write a double to the log (generally for debugging) void aipBase::log (double x) const { if (m_logger) { m_logger->log(x); } else { cout << x; } } //---------------------------------------------------------------------- // Write a long to the log (generally for debugging) void aipBase::log (long x) const { if (m_logger) { m_logger->log(x); } else { cout << x; } } //====================================================================== // aipMsg // //---------------------------------------------------------------------- // Constructor aipMsg::aipMsg(short t, short st, aipG g) { set(t, st, g); } //---------------------------------------------------------------------- // Destructor aipMsg::~aipMsg () {} //---------------------------------------------------------------------- // set the goodness void aipMsg::set (aipG g) { m_g = g; } //---------------------------------------------------------------------- // set the private variables void aipMsg::set (short t, short st, aipG g) { m_typ = t; m_subtyp = st; m_g = g; } //====================================================================== // aipFileLogger - not tested // //---------------------------------------------------------------------- // Constructor aipFileLogger::aipFileLogger (const char *file_name) { m_stream = fopen (file_name, "w"); } //---------------------------------------------------------------------- // Destructor aipFileLogger::~aipFileLogger () { if (m_stream) fclose(m_stream); } //---------------------------------------------------------------------- // is_valid int aipFileLogger::is_valid () { return (m_stream ? 1 : 0); } //---------------------------------------------------------------------- // write a string void aipFileLogger::log (const char *x) { if (!is_valid() || !x) return; fprintf (m_stream, "%s", x); } //---------------------------------------------------------------------- // write a double value void aipFileLogger::log (double x) { if (!is_valid()) return; fprintf (m_stream, "%f", x); } //---------------------------------------------------------------------- // write a long value void aipFileLogger::log (long x) { if (!is_valid()) return; fprintf (m_stream, "%ld", x); } //====================================================================== // aipStringLogger // //---------------------------------------------------------------------- // Constructor aipStringLogger::aipStringLogger (long max_len) { if (max_len < 1 || max_len > 10000) { m_max_len = 0; return; } m_str = new char[max_len+1]; if (!m_str) { m_max_len = 0; return; } m_str_len = 0; m_max_len = max_len; } //---------------------------------------------------------------------- // Destructor aipStringLogger::~aipStringLogger () { if (m_str) delete m_str; } //---------------------------------------------------------------------- // is_valid int aipStringLogger::is_valid () { return (m_max_len>0 ? 1 : 0); } //---------------------------------------------------------------------- // write a string void aipStringLogger::log (const char *x) { if (!is_valid() || !x || *x=='\0') return; long max_wrt = m_max_len - m_str_len; if (max_wrt <= 0) return; long xlen = strlen(x); if (xlen > max_wrt) xlen = max_wrt; aip_strncpy (m_str+m_str_len, x, xlen); // add to end m_str_len += xlen; } //---------------------------------------------------------------------- // write a double value void aipStringLogger::log (double x) { if (!is_valid()) return; char buf[31]; sprintf (buf, "%f", x); log(buf); } //---------------------------------------------------------------------- // write a long value void aipStringLogger::log (long x) { if (!is_valid()) return; char buf[31]; sprintf (buf, "%ld", x); log(buf); } //====================================================================== // License // // Permission is hereby granted, free of charge, to any // person obtaining a copy of this software and associated // documentation files (the "Software"), to deal in the Software // without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to // whom the Software is furnished to do so, subject to the // following conditions: // // The copyright notice and this license shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. // // //********************************************************************** aipBase.h0000755000076400007640000002752611063254442011573 0ustar brianbrian//********************************************************************** // aipBase.h - aipBase, aipMsg // // Provides: // Classes: aipBase aipMsg aipRand aipLogger // aipLogger aipFileLogger aipStringLogger // Functions: aip_strncpy() aip_get_str() aip_get_val() // Constants: intensity-constants, empty-string // // Classes that descend from aipBase can: // - take aipMsg messages // - log to cout (or, optionally, through a logger) // - using functions from a static aipRand member: // - generate random numbers from 0 to 9999 // - generate random numbers in any range // - randomly return true in some proportion of calls // // Copyright (c) 2005, 2007, 2008 Brian Marshall // // See the license at end of this file. // // Developers/Contributers: // [BRM] Brian Marshall - Calgary - bmarshal@agt.net // // 08/07/09 [BRM] new aipRand, random_num(); new functions: // aip_get_str() aip_get_val() aip_strncpy(); // modified logging multiple small changes // 05/11/07 [BRM] random_num() now in class aipBase // 05/11/01 [BRM] added aip_strncpy() that always terminates string // 05/09/10 [BRM] Development begins. // //---------------------------------------------------------------------- #ifndef aipBase_h_guard #define aipBase_h_guard #include "aipGood.h" #include #include //====================================================================== // About Logging // // Code for any object that descends from aipBase can call: // void log (const char *x); // void log (double x); // void log (long x); // // By default, calling a log() function will write to standard output. // // An application may create an instance of a subclass of aipLogger // and call: void set_logger (aipLogger *x) // (once) from any object that descends from aipBase // and any calls to log() functions will call corresponding calls // in the logger. (In the logger, the log() functions are virtual.) // // It is the responsibility of code that creates a logger to delete // it if necessary. // // An application may, of course, read and write to cout or files // in addition to, or instead of, calling log() functions. // //====================================================================== // About the Random Number Generator (RNG) // // The RNG in aipBase returns the same sequence of numbers // regardless of hardware or compiler. // // aipBase provides: // random_num() returns a number from 0 to 9999 // plus two functions that call random_num(): // random_num(lo,hi) returns a number in a range // is_one_chance_in_x(x) returns true one in x times called // // The RNG in aipBase is a static data member - it does not // increase the size of instances of subclasses of aipBase. // // random_num() returns a number in the range 0 to 9999. // In the opinion of the author, at the time of writing, // for AI applications, 4 significant digits should be lots. // It is not suitable for applications requiring high-quality // random numbers. // // random_num(lo,hi) returns a number in any range specified, // although if the range is greater than 10000 numbers, every // number in the range may not exist in the sequence, although // lo (the low limit of the range) will always be included - // ex. random_num(1,40000) will generate numbers from the set: // [1,5,9,13,17,...,39997] // // In class aipRand, functions rand_1() and rand_2() are // low-quality RNGs that are used by random_num(). // The parameters of rand_1() stay the same. // The parameters of rand_2() are periodically randomly rechosen. // //====================================================================== // Classes class aipBase; // base-class to many aiParts classes class aipMsg; // base-class for messages class aipRand; // random number generator class aipLogger; // to cout, a file, a string, etc. (optional) class aipFileLogger; // log to a file class aipStringLogger; // log to a string //====================================================================== // Global Constants static const long aipIntensity_None = 0; static const long aipIntensity_Slightly = 1; static const long aipIntensity_A_Little = 2; static const long aipIntensity_Somewhat = 3; static const long aipIntensity_A_Fair_Bit = 4; static const long aipIntensity_Quite_A_Bit = 5; static const long aipIntensity_A_Lot = 6; static const char aip_Endl[2] = "\n"; static const char aip_Empty_Str[2] = ""; //====================================================================== // Local Constants for the Random Number Generator (RNG) // a sub-RNG is defined: s = (a*s+c)%m where m=10000 const long Num_Sub_RNG = 11; // first for rand_1(); rest for rand_2() const long Rand_s[] = { 9281, 6733, 9613, 5107, 4241, 6733, 9613, 4783, 5821, 4241, 7477 }; const long Rand_a[] = { 80441, 69851, 69851, 76151, 76151, 85273, 58637, 85273, 58637, 67817, 55669 }; const long Rand_c[] = { 67519, 85273, 85273, 50471, 69851, 69851, 50471, 76151, 80441, 58637, 76151 }; const long Rand_Null = -999999999; const long Rand_Null_Test = -999999000; //====================================================================== // Global Functions // strncpy that terminates the string, returns length int aip_strncpy (char *dest, const char *src, int max_len); // Get a string or value (up to the specified length) // starting from x. If a delimiter is not specified, // any char other than a letter or digit will be the // delimiter. Return a pointer to the terminating // delimiter or zero if an error occurs. const char * aip_get_str (const char *x, long maxlen, char *str, char delim ='~'); const char * aip_get_val (const char *x, long maxlen, long *val, char delim ='~'); // Return true if x is a delimiter character. // If delim is '~', any char other than a letter // or digit is a delimiter. int aip_is_delim (char x, char delim); //====================================================================== // aipRand - Random Number Generator ("RNG") class aipRand { long m_rand_seed_1; // seed for first sub-RNG long m_rand_seed_2; // seed for second sub-RNG long m_rand_2_idx; // index of params for rand_2() long m_rand_count; // 0: choose a sub-RNG for rand_2() long m_deck[100]; // set of random numbers; long m_deck_len; // number of random numbers in m_deck; protected: virtual long sub_rng (long idx); // used by rand_1(), rand_2() long rand_1 (); // first sub-RNG (that does not change) long rand_2 (); // second sub-RNG (that changes periodically) public: aipRand () { m_rand_2_idx = Rand_Null; } ~aipRand () {} long random_num (); // returns 0 to 9999 }; //====================================================================== // aipBase - Base class for classes that can: // - take messages // - log to cout (or, optionally, through a logger) // - generate random numbers // // This is a base class for many aiParts classes. // // Subclasses may override send_msg() to send a message to // other objects that should receive it. class aipBase { static aipRand m_rand; // random number generator static aipLogger * m_logger; // optional logger public: aipBase () {} virtual ~aipBase () {} void set_logger (aipLogger *x) { m_logger = x; } virtual void take_msg (aipMsg *p) {} long random_num (); // returns 0 to 9999 long random_num (long lo, long hi); // (hi-lo) < 100000 int is_one_chance_in_x (long x); void log (const char *x) const; // using a logger if it has one void log (double x) const; void log (long x) const; }; //====================================================================== // aipMsg - a message // // Applications may create subclasses with application-specific // data members. // // subclasses and/or applications can define constants for // typ and subtyp. The default for each is zero. // // Users or subclasses may define constants for m_typ. class aipMsg { short m_typ; // primary type of message short m_subtyp; // secondary type of message aipG m_g; // goodness associated with message public: void set (short t, short st, aipG g); void set (aipG g); void set_subtyp (short st) { m_subtyp = st; } aipMsg(short t, short st =0, aipG g =aipNeutral); virtual ~aipMsg (); short typ () const { return m_typ; } short subtyp () const { return m_subtyp; } aipG g () const { return m_g; } }; //====================================================================== // aipLogger - optional log to cout, a file, a string, etc // // If software creates a logger, it can call aipBase::set_logger() // and the logger will be used in calls to aipBase.log(). // The creator of a logger owns it and deletes it if necessary. // // If no logger is created, calls to aipBase.log() will default // to writing to cout. // // This is a base class; to use it, create a sub-class. class aipLogger { public: aipLogger () {} virtual ~aipLogger () {} virtual int is_valid () = 0; virtual void log (const char *x) = 0; virtual void log (double x) = 0; virtual void log (long x) = 0; virtual const char * get () { return 0; } // defalt is no support }; //= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = // aipFileLogger class aipFileLogger : public aipLogger { FILE * m_stream; public: aipFileLogger (const char *file_name); virtual ~aipFileLogger (); virtual int is_valid (); virtual void log (const char *x); virtual void log (double x); virtual void log (long x); }; //= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = // aipStringLogger class aipStringLogger : public aipLogger { char * m_str; long m_str_len; long m_max_len; public: aipStringLogger (long max_len); virtual ~aipStringLogger (); virtual int is_valid (); virtual void log (const char *x); virtual void log (double x); virtual void log (long x); virtual const char * get () { return m_str; } }; //====================================================================== #endif //====================================================================== // License // // Permission is hereby granted, free of charge, to any // person obtaining a copy of this software and associated // documentation files (the "Software"), to deal in the Software // without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to // whom the Software is furnished to do so, subject to the // following conditions: // // The copyright notice and this license shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. // //********************************************************************** aipDecision.cpp0000644000076400007640000001101111063254442012764 0ustar brianbrian//********************************************************************** // aipDecision.cpp - function bodies for aipDecision.h // // Copyright (c) 1999, 2005, 2008 Brian Marshall // // See the license at end of this file. // // Developers/Contributers: // [BRM] Brian Marshall - Calgary - bmarshal@agt.net // // 08/01/01 [BRM] added logic for trivial decisions // 05/11/19 [BRM] decision take_msg() calls pandemonium take_msg() // 05/11/07 [BRM] changed following comment // support for decisions sharing a set of options // 05/10/28 [BRM] can choose randomly from multiple best options // 05/09/17 [BRM] development began // //---------------------------------------------------------------------- #include "aipDecision.h" #include #include using namespace std; //====================================================================== // aipDecision // //---------------------------------------------------------------------- // Constructor aipDecision::aipDecision () { m_owner = 0; m_options = new aipPandemonium; m_is_trivial = 0; } //---------------------------------------------------------------------- // Destructor aipDecision::~aipDecision () { if (m_options) delete m_options; } //---------------------------------------------------------------------- // add_option void aipDecision::add_option (aipOption *x) { if (!x) return; if ( x->owner() ) { x->set_is_shared(); } else { x->set_owner(this); } m_options->add(x); } //---------------------------------------------------------------------- // take_msg - take a message and distribute to options // // Overriding functions gemerally must call this function. void aipDecision::take_msg (aipMsg *m) { m_options->take_msg(m); } //---------------------------------------------------------------------- // option_iterator - return an iterator to the options of this decision. aipOptionItr aipDecision::option_iterator() const { aipOptionItr i(option_pandemonium()); return i; } //---------------------------------------------------------------------- // decide - choose an option aipOption * aipDecision::decide() { aipOption *best_opt = 0; aipG best_g = aipForbidden; long num_best = 0; aipOptionItr itr = option_iterator(); for ( aipOption *a = itr.first(); a; a = itr.next() ) { if ( a->is_shared() && a->chooser() ) continue; aipG g = a->g_opt(); if ( g.is_forbidden() ) continue; if ( !best_opt || g > best_g || ( g == best_g && is_one_chance_in_x(++num_best) ) ) { if ( !best_opt || g > best_g ) num_best = 1; best_opt = a; best_g = g; if (m_is_trivial) break; } } if (best_opt) { best_opt->set_chooser(this); } return best_opt; } //====================================================================== // aipOption // // all function bodies are declared in the header file // //====================================================================== // aipOptionItr - Option iterator // // All function bodies are in the header file // //====================================================================== // aipDecisionItr - Decision iterator // // All function bodies are in the header file // //====================================================================== // License // // Permission is hereby granted, free of charge, to any // person obtaining a copy of this software and associated // documentation files (the "Software"), to deal in the Software // without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to // whom the Software is furnished to do so, subject to the // following conditions: // // The copyright notice and this license shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. // // //********************************************************************** aipDecision.h0000755000076400007640000001505711063254442012452 0ustar brianbrian//********************************************************************** // aipDecision.h - Decision - a set of options, // one of which is to be chosen. // // Provides: aipDecision aipOption aipOptionItr // // An aipDecision decision: // - has a set of options // - takes a series of messages, which it passes to its options // - has a decide(). // // Copyright (c) 2005, 2008 Brian Marshall // // See the license at end of this file. // // Developers/Contributers: // [BRM] Brian Marshall - Calgary - bmarshal@agt.net // // 08/01/01 [BRM] removed delete_option(); untested trivial dcsns // 05/11/07 [BRM] changed following comment line // support for decisions sharing a set of options // 05/10/28 [BRM] can choose randomly from multiple best options // 05/09/17 [BRM] development begins // //---------------------------------------------------------------------- #ifndef aipDecision_h_guard #define aipDecision_h_guard #include "aipEmotion.h" //---------------------------------------------------------------------- // Classes. Sub-classing is shown by indentation. // class aipG is a typedef for aipGoodness // Hope is a compound emotion composed of Fear, Greed and Curiosity // class aipBase; ( in aipBase.h ) // class aipDemon; ( in aipPandemonium.h ) class aipDecision; // Set of options and decide() class aipOption; // Option in a decision. // class aipDemonItr; ( in aipPandemonium.h ) class aipOptionItr; // option iterator //====================================================================== // aipDecision // // A decision has: // - a set of options // - a decide() function to pick one // // A decision may be set to trivial, temporarily or permanently, and: // decide() will choose the first non-forbidden option it finds. // A decision would be trivial if it had only one (remaining) // non-forbidden option. A set of decisions would each be trivial // if they share a set of options and there are just enough. // // A set of options may be shared by more than one decision, // so long as an option can only be chosen by one decision. // The first decision to which an option is added becomes its // owner for destruction purposes. // // Use of m_owner, here, for decisions, is not required. class aipDecision : public aipDemon { aipBase * m_owner; // Owner of this decision or zero. aipPandemonium * m_options; int m_is_trivial; protected: aipPandemonium * option_pandemonium() const { // to make iterators return m_options; } public: aipDecision (); virtual ~aipDecision (); void set_owner (aipBase *x) { m_owner = x; } void add_option (aipOption *x); void set_is_trivial () { m_is_trivial = 1; } void reset_is_trivial () { m_is_trivial = 0; } virtual void take_msg (aipMsg *m); aipOptionItr option_iterator() const; aipBase * owner () const { return m_owner; } virtual aipG g() { return aipNeutral; } virtual aipOption * decide(); // decide }; //====================================================================== // aipOption - an option owned by a decision // // This is a base class - subclasses can provide: // g_opt() to return a goodness used for deciding. // g_opt_usr() to return goodness to report to users. // // The owner of an option is a decision. However, options can // be shared, and if this is happening, m_owner will not necessarily // be the decision that picked the option. // // The owner of an option is automatically set // in aipDecision::add_option(). class aipOption : public aipDemon { aipDecision * m_owner; // owner for destruction purposes aipDecision * m_chooser; // decision that chose this option int m_is_shared; // option shared between decisions public: aipOption () { m_owner = m_chooser = 0; m_is_shared = 0; } virtual ~aipOption () {} void set_owner (aipDecision *x) { m_owner = x; } void set_chooser (aipDecision *x) { m_chooser = x; } void set_is_shared () { m_is_shared = 1; } virtual void take_msg (aipMsg *m) {} virtual aipG g_opt() { return aipNeutral; } virtual aipG g_opt_usr() { return aipNeutral; } void reset_chooser () { m_chooser = 0; } aipDecision * owner () const { return m_owner; } aipDecision * chooser () const { return m_chooser; } int is_shared () const { return m_is_shared; } }; //====================================================================== // aipOptionItr - Iterator for options in a decision class aipOptionItr : public aipDemonItr { public: aipOptionItr () : aipDemonItr() {} aipOptionItr (aipPandemonium *p) { set_demon_itr (p,0); } aipOptionItr (const aipOptionItr& x) { set_demon_itr (x.pandemonium(), x.current()); } virtual ~aipOptionItr () {} aipOptionItr& operator = (const aipOptionItr& x) { set_demon_itr (x.pandemonium(), x.current()); return *this; } aipOption * first() { return (aipOption*)aipDemonItr::first(); } aipOption * next() { return (aipOption*)aipDemonItr::next(); } }; //====================================================================== #endif //====================================================================== // License // // Permission is hereby granted, free of charge, to any // person obtaining a copy of this software and associated // documentation files (the "Software"), to deal in the Software // without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to // whom the Software is furnished to do so, subject to the // following conditions: // // The copyright notice and this license shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. // //********************************************************************** aipEmotion.cpp0000644000076400007640000003622011063254442012652 0ustar brianbrian//********************************************************************** // aipEmotion.cpp - function bodies for aipEmotion.h // // Copyright (c) 1999, 2005, 2008 Brian Marshall // // See the license at end of this file. // // Developers/Contributers: // [BRM] Brian Marshall - Calgary - bmarshal@agt.net // // 08/06/16 [BRM] small changes for portability // 05/11/19 [BRM] emotion take_msg() calls pandemonium take_msg() // 05/11/15 [BRM] hope: fear, greed, curiosity now aspects // 05/10/24 [BRM] aipHope::set_g - simplified and slightly changed // weaken functions now do not weaken to aipNeutral // 05/10/24 [BRM] made emotion slowly_degrade optional // 05/09/10 [BRM] development began // //---------------------------------------------------------------------- #include "aipEmotion.h" #include #include //====================================================================== // aipAspect - Aspect to an emotion // // All function bodies are in the header file // //====================================================================== // aipEmotion - a pure virtual base class. // //---------------------------------------------------------------------- // Constructor aipEmotion::aipEmotion () { m_g = aipNeutral; m_aspects = new aipPandemonium; m_g_before_take_msg = aipNeutral; m_g_before_prev_msg = aipNeutral; m_should_slowly_degrade = 1; } //---------------------------------------------------------------------- // Destructor aipEmotion::~aipEmotion () { if (m_aspects) delete m_aspects; } //---------------------------------------------------------------------- // add_aspect - add and aspect to this emotion void aipEmotion::add_aspect (aipAspect *x) { x->set_owner_emotion(this); m_aspects->add(x); } //---------------------------------------------------------------------- // dump_to_ptr void aipEmotion::dump_to_ptr (char *p) const { long x = m_g.numeric_value(); x = (x<-999999) ? -999999 : ( (x>999999) ? 999999 : x ); sprintf (p,"%ld",x); } //---------------------------------------------------------------------- // aspect_iterator - return an iterator to the aspects of this emotion. aipAspectItr aipEmotion::aspect_iterator() const { aipAspectItr i(aspect_pandemonium()); return i; } //---------------------------------------------------------------------- // slowly_degrade void aipEmotion::slowly_degrade () { if ( g() != aipNeutral && g() == g_before_take_msg() && g() == g_before_prev_msg() ) { weaken(aipIntensity_Slightly); } } //---------------------------------------------------------------------- // take_msg - take a message and distribute to aspects // // Subclasses should, in general, NOT override this function. // Subclasses can define specific behavior by overriding the // pre_msg_behavior() and post_msg_behavior() functions. // Instances can have aspects added to them. void aipEmotion::take_msg (aipMsg *m) { m_g_before_prev_msg = m_g_before_take_msg; m_g_before_take_msg = m_g; pre_msg_behavior(m); m_aspects->take_msg(m); post_msg_behavior(m); } //---------------------------------------------------------------------- // set_g void aipEmotion::set_g (aipG x) { m_g = x; } //---------------------------------------------------------------------- // floor and ceiling void aipEmotion::floor (aipG x) { if (m_g < x) set_g(x); } void aipEmotion::ceiling (aipG x) { if (m_g > x) set_g(x); } //---------------------------------------------------------------------- // reset m_g void aipEmotion::reset (aipG x) { set_g(x); } //====================================================================== // aipPosEmotion - emotion where goodness is Neutral or Positive // //---------------------------------------------------------------------- // set_g void aipPosEmotion::set_g (aipG x) { aipEmotion::set_g ( (x < aipNeutral) ? aipNeutral : x ); } //---------------------------------------------------------------------- // strengthen void aipPosEmotion::strengthen (long intensity_constant) { aipG x = aipNeutral; if (intensity_constant == aipIntensity_Slightly) { x = aipVerySlightlyGood; } else if (intensity_constant == aipIntensity_A_Little) { x = aipSlightlyGood; } else if (intensity_constant == aipIntensity_Somewhat) { x = aipLittleBitGood; } else if (intensity_constant == aipIntensity_A_Fair_Bit) { x = aipSomewhatGood; } else if (intensity_constant == aipIntensity_Quite_A_Bit) { x = aipFairlyGood; } else if (intensity_constant == aipIntensity_A_Lot) { x = aipQuiteGood; } set_g (g() + x); } //---------------------------------------------------------------------- // weaken (so long as the value does not fall to aipNeutral) void aipPosEmotion::weaken (long intensity_constant) { aipG cur_g = g(); if (cur_g < aipSlightlyGood) return; aipG x = aipNeutral; if (intensity_constant == aipIntensity_Slightly) { x = aipVerySlightlyGood + cur_g.calc_fraction(1,8); } else if (intensity_constant == aipIntensity_A_Little) { x = aipVerySlightlyGood + cur_g.calc_fraction(1,6); } else if (intensity_constant == aipIntensity_Somewhat) { x = aipVerySlightlyGood + cur_g.calc_fraction(1,4); } else if (intensity_constant == aipIntensity_A_Fair_Bit) { x = aipVerySlightlyGood + cur_g.calc_fraction(3,8); } else if (intensity_constant == aipIntensity_Quite_A_Bit) { x = aipVerySlightlyGood + cur_g.calc_fraction(1,2); } else if (intensity_constant == aipIntensity_A_Lot) { x = aipVerySlightlyGood + cur_g.calc_fraction(3,4); } if (x >= cur_g) x = cur_g - 1; set_g (cur_g - x); // virtual function ensures in is not negative. } //====================================================================== // aipNegEmotion - emotion where goodness is Neutral or Negative // //---------------------------------------------------------------------- // set_g void aipNegEmotion::set_g (aipG x) { aipEmotion::set_g ( (x > aipNeutral) ? aipNeutral : x ); } //---------------------------------------------------------------------- // strengthen void aipNegEmotion::strengthen (long intensity_constant) { aipG x = aipNeutral; if (intensity_constant == aipIntensity_Slightly) { x = aipVerySlightlyBad; } else if (intensity_constant == aipIntensity_A_Little) { x = aipSlightlyBad; } else if (intensity_constant == aipIntensity_Somewhat) { x = aipLittleBitBad; } else if (intensity_constant == aipIntensity_A_Fair_Bit) { x = aipSomewhatBad; } else if (intensity_constant == aipIntensity_Quite_A_Bit) { x = aipFairlyBad; } else if (intensity_constant == aipIntensity_A_Lot) { x = aipQuiteBad; } set_g (g() + x); } //---------------------------------------------------------------------- // weaken (so long as the value does not rise to aipNeutral) void aipNegEmotion::weaken (long intensity_constant) { aipG cur_g = g(); if (cur_g > aipSlightlyBad) return; aipG x = aipNeutral; if (intensity_constant == aipIntensity_Slightly) { x = aipVerySlightlyBad + cur_g.calc_fraction(1,8); } else if (intensity_constant == aipIntensity_A_Little) { x = aipVerySlightlyBad + cur_g.calc_fraction(1,6); } else if (intensity_constant == aipIntensity_Somewhat) { x = aipVerySlightlyBad + cur_g.calc_fraction(1,4); } else if (intensity_constant == aipIntensity_A_Fair_Bit) { x = aipVerySlightlyBad + cur_g.calc_fraction(3,8); } else if (intensity_constant == aipIntensity_Quite_A_Bit) { x = aipVerySlightlyBad + cur_g.calc_fraction(1,2); } else if (intensity_constant == aipIntensity_A_Lot) { x = aipVerySlightlyBad + cur_g.calc_fraction(3,4); } if (x <= cur_g) x = cur_g + 1; set_g (cur_g - x); // virtual function ensures in is not positive. } //====================================================================== // aipCompEmotion - compound emotion - aspects are emotions // //---------------------------------------------------------------------- // calc_compound - calculate m_g as the sum of the goodness of // each emotion-aspect. // // Subclasses may want to override this function, for efficiency // reasons, if nothing else. void aipCompEmotion::calc_compound () { aipG g = aipNeutral; aipEmotionItr itr = emotion_iterator(); for ( aipEmotion *e=itr.first(); e; e=itr.next() ) { g += e->g(); } aipEmotion::set_g(g); // compound emotion is sum of its aspects } //---------------------------------------------------------------------- // add_emotion - set the owning emotion void aipCompEmotion::add_emotion (aipEmotion *x) { x->set_owner_emotion(this); aipEmotion::add_aspect(x); calc_compound(); } //---------------------------------------------------------------------- // set_g - works only if argument is aipNeutral // // If different behavior is required, override this function. void aipCompEmotion::set_g (aipG x) { if (x != aipNeutral) return; aipEmotionItr itr = emotion_iterator(); for ( aipEmotion *e=itr.first(); e; e=itr.next() ) { e->aipEmotion::set_g(aipNeutral); } aipEmotion::set_g(aipNeutral); } //---------------------------------------------------------------------- // post_msg_behavior - called after take_msg() is called for aspects // The behavior apart from the aspects (emotion components). // // Subclasses may want to override this function, but in any case, // calc_compound() must be called to set the emotion m_g. void aipCompEmotion::post_msg_behavior (aipMsg *m) { if (m) calc_compound(); } //---------------------------------------------------------------------- // emotion_iterator aipEmotionItr aipCompEmotion::emotion_iterator() const { aipEmotionItr i(aspect_pandemonium()); return i; } //====================================================================== // aipFear - how bad something might be. // //---------------------------------------------------------------------- // dump_to_ptr void aipFear::dump_to_ptr (char *p) const { p[0] = 'f'; p[1] = ':'; aipEmotion::dump_to_ptr(p+2); } //====================================================================== // aipGreed - how good something is known to be. // //---------------------------------------------------------------------- // dump_to_ptr void aipGreed::dump_to_ptr (char *p) const { p[0] = 'g'; p[1] = ':'; aipEmotion::dump_to_ptr(p+2); } //====================================================================== // aipCuriosity - how good something unknown might be. // //---------------------------------------------------------------------- // dump_to_ptr void aipCuriosity::dump_to_ptr (char *p) const { p[0] = 'c'; p[1] = ':'; aipEmotion::dump_to_ptr(p+2); } //====================================================================== // aipHope - fear plus greed plus curiosity // //---------------------------------------------------------------------- // Constructor // // This requires no further initialization - by default, emotions // are created with a goodness of aipNeutral. aipHope::aipHope () { m_fear = new aipFear; m_greed = new aipGreed; m_curiosity = new aipCuriosity; if (m_fear && m_greed && m_curiosity) { add_emotion (m_fear); add_emotion (m_greed); add_emotion (m_curiosity); } } //---------------------------------------------------------------------- // enable_slowly_degrade void aipHope::enable_slowly_degrade () { if (m_fear) m_fear->enable_slowly_degrade(); if (m_greed) m_greed->enable_slowly_degrade(); if (m_curiosity) m_curiosity->enable_slowly_degrade(); } //---------------------------------------------------------------------- // disable_slowly_degrade void aipHope::disable_slowly_degrade () { if (m_fear) m_fear->disable_slowly_degrade(); if (m_greed) m_greed->disable_slowly_degrade(); if (m_curiosity) m_curiosity->disable_slowly_degrade(); } //---------------------------------------------------------------------- // dump_to_ptr void aipHope::dump_to_ptr (char *p) const { strcpy (p, "h(fgc)("); long len = strlen(p); m_fear->aipEmotion::dump_to_ptr(p+len); strcat (p, ","); len = strlen(p); m_greed->aipEmotion::dump_to_ptr(p+len); strcat (p, ","); len = strlen(p); m_curiosity->aipEmotion::dump_to_ptr(p+len); strcat (p, ")"); } //---------------------------------------------------------------------- // set_g - set the goodness of this compound emotion void aipHope::set_g (aipG x) { if (x != aipNeutral) return; if (!m_fear || !m_greed || !m_curiosity) return; m_fear->set_g (aipNeutral); m_greed->set_g (aipNeutral); m_curiosity->set_g (aipNeutral); aipEmotion::set_g (aipNeutral); } //---------------------------------------------------------------------- // post_msg_behavior - called after take_msg() is called for aspects // // This function does the same thing as the one it is overriding, // aipCompEmotion::post_msg_behavior(), but it is faster because // we already have pointers to all the aspect-emotions. void aipHope::post_msg_behavior (aipMsg *m) { if (!m_fear || !m_greed || !m_curiosity) return; if (m) calc_compound(); } //---------------------------------------------------------------------- // set_fear void aipHope::set_fear (aipG x) { m_fear->set_g(x); calc_compound(); } //---------------------------------------------------------------------- // set_greed void aipHope::set_greed (aipG x) { m_greed->set_g(x); calc_compound(); } //---------------------------------------------------------------------- // set_curiosity void aipHope::set_curiosity (aipG x) { m_curiosity->set_g(x); calc_compound(); } //====================================================================== // aipAspectItr - Aspect iterator // // All function bodies are in the header file // //====================================================================== // aipEmotionItr - Emotion iterator // // All function bodies are in the header file // //====================================================================== // License // // Permission is hereby granted, free of charge, to any // person obtaining a copy of this software and associated // documentation files (the "Software"), to deal in the Software // without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to // whom the Software is furnished to do so, subject to the // following conditions: // // The copyright notice and this license shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. // // //********************************************************************** aipEmotion.h0000755000076400007640000003301211063254442012316 0ustar brianbrian//********************************************************************** // aipEmotion.h - Emotion - a kind of running sum of goodness. // // Provides: aipEmotion aipFear aipGreed aipCuriosity aipHope // // This header file declares classes to implement the Emotion // AI pattern. // // See www.aipatterns.org (or www.agt.net/public/bmarshal/aipatterns) // for information on Emotion AI pattern. // // An emotion has a set of aspects. // // Copyright (c) 2005, 2008 Brian Marshall // // See the license at end of this file. // // Developers/Contributers: // [BRM] Brian Marshall - Calgary - bmarshal@agt.net // // 08/06/16 [BRM] removed delete_aspect() and delete_emotion(); // small changes for portability // 05/11/14 [BRM] simplified base class aipCompEmotion // aipHope::weaken(), strengthen(), set_g() // removed and default to those from aipCompEmotion // aipHope::weaken_fear(), strengthen_greed(), etc. // have been removed // made const: aipHope::greed(), fear(), curiosity() // 05/10/24 [BRM] made emotion slowly_degrade optional // simplified set_g() and reset() // added floor() and ceiling() functions // removed public non-const access to hope components // 05/08/17 [BRM] Development begins. // //---------------------------------------------------------------------- // Notes // // Calling a 'weaken' function on a (non-compound) emotion will // never turn a value that is not aipNeutral to aipNeutral. // // If set_g(), weaken() or strengthen() are used with compound // emotions, they have to be overriden. The best way to use // compound-emotions, including aipHope, is to attach aspects // to the emotion-aspects. For example, a subclass of aipAspect // can be added to the fear in aipHope, with the code to affect // that fear in the take_msg() function of the aspect. // //---------------------------------------------------------------------- #ifndef aipEmotion_h_guard #define aipEmotion_h_guard #include "aipPandemonium.h" //---------------------------------------------------------------------- // Classes. Sub-classing is shown by indentation. // class aipG is a typedef for aipGoodness // class aipBase; ( in aipBase.h ) // class aipDemon; ( in aipPandemonium.h ) class aipAspect; // Aspect affecting an emotion class aipEmotion; // Kind of running sum of goodness class aipPosEmotion; // Positive emotion class aipGreed; // Wanting a known good class aipCuriosity; // Interested in potiential good class aipNegEmotion; // Negative emotion class aipFear; // Wanting to avoid a known bad class aipCompEmotion; // Compound emotion class aipHope; // Fear + Greed + Curiosity // class aipDemonItr; ( in aipPandemonium.h ) class aipAspectItr; // Aspect Iterator class aipEmotionItr; // Emotion Iterator //---------------------------------------------------------------------- // Constants // From aipBase.h - // long Intensity Constants (for function arguments)... // aipIntensity_None aipIntensity_Slightly // aipIntensity_A_Little aipIntensity_Somewhat // aipIntensity_A_Fair_Bit aipIntensity_Quite_A_Bit // aipIntensity_A_Lot // //====================================================================== // aipAspect - an aspect that affects an emotion class aipAspect : public aipDemon { aipEmotion * m_owner_emotion; public: aipAspect () { m_owner_emotion = 0; } virtual ~aipAspect () {} void set_owner_emotion (aipEmotion *x) { m_owner_emotion = x; } aipEmotion * emotion () const { return m_owner_emotion; } virtual void take_msg (aipMsg *m) {} }; //====================================================================== // aipEmotion - an abstract base class. // // The heart of an emotion is m_g, a goodness that changes as a // result of calls to strengthen() or weaken(), // or as a result of calls to take_msg(), which: // - calls pre_msg_behavior() // - calls take_msg() for each aspect of the emotion // - calls post_msg_behavior() // // Subclasses can override pre_msg_behavior() and post_msg_behavior(). // Instances of subclasses can have aspects added to them. // Subclasses should, in general, NOT override take_msg() class aipEmotion : public aipAspect { aipG m_g; // current emotion goodness aipPandemonium * m_aspects; // aspects that affect m_g aipG m_g_before_take_msg; // m_g before take_msg() is called. aipG m_g_before_prev_msg; // m_g before take_msg() is called. int m_should_slowly_degrade; // 0 = no protected: aipG g_before_take_msg () const { return m_g_before_take_msg; } aipG g_before_prev_msg () const { return m_g_before_prev_msg; } virtual void pre_msg_behavior (aipMsg *m =0) {} virtual void slowly_degrade (); virtual void post_msg_behavior (aipMsg *m =0) { if (m_should_slowly_degrade) slowly_degrade(); } aipPandemonium * aspect_pandemonium() const { // for iterators return m_aspects; } public: aipEmotion (); virtual ~aipEmotion (); void add_aspect (aipAspect *x); void enable_slowly_degrade () { m_should_slowly_degrade = 1; } void disable_slowly_degrade () { m_should_slowly_degrade = 0; } virtual void dump_to_ptr (char *p) const; // used for debugging aipAspectItr aspect_iterator() const; virtual void set_g (aipG x); virtual aipG g() const { return m_g; } virtual void take_msg (aipMsg *m); virtual void strengthen (long intensity_constant) =0; virtual void weaken (long intensity_constant) =0; void floor (aipG x); void ceiling (aipG x); virtual void reset (aipG x =aipNeutral); }; //====================================================================== // Basic-classes: aipPosEmotion, aipNegEmotion, aipCompEmotion // // aipPosEmotion and aipNegEmotion are generally used as base-classes // although they can be used as-is if desired. // // aipCompEmotion is generally a base class, and set_g() // is generally be overridden. // //---------------------------------------------------------------------- // aipPosEmotion - emotion where goodness is Neutral or Positive class aipPosEmotion : public aipEmotion { public: aipPosEmotion () {} virtual ~aipPosEmotion () {} virtual void set_g (aipG x); virtual void strengthen (long intensity_constant); virtual void weaken (long intensity_constant); }; //---------------------------------------------------------------------- // aipNegEmotion - emotion where goodness is Neutral or Negative class aipNegEmotion : public aipEmotion { public: aipNegEmotion () {} virtual ~aipNegEmotion () {} virtual void set_g (aipG x); virtual void strengthen (long intensity_constant); virtual void weaken (long intensity_constant); }; //---------------------------------------------------------------------- // aipCompEmotion - compond emotion - aspects are emotions // // Emotions are composites (they descend from aipAspect). // aipCompEmotion is used to create emotions where the aspects // are other emotions. It provides these behaviors: // - calc_compound() calculates the goodness - default is: // the sum of the goodness of the aspect-emotions // - set_g(aipNeutral) sets all aspect-emotions to aipNeutral // by default, any other argument has no effect // // - post_msg_behavior(), called by take_msg(), // calls calc_compound(). // // set_g() must be overridden for any value except aipNeutral. // // weaken() and strengthen() must be overriden to do anything. // // set_g() and calc_compound() may be overriden to make them // faster - knowing the components instead of the default // implementation that uses iterators. class aipCompEmotion : public aipEmotion { protected: void calc_compound(); virtual void post_msg_behavior (aipMsg *m =0); public: aipCompEmotion () {} virtual ~aipCompEmotion () {} void add_aspect (aipAspect *x) {} // use add_emotion void add_emotion (aipEmotion *x); aipEmotionItr emotion_iterator() const; // any of the following functions that are used // should be overriden (except set_g(aipNeutral) works) virtual void set_g (aipG x); // probably override this virtual void strengthen (long intensity_constant) {} virtual void weaken (long intensity_constant) {} }; //====================================================================== // aipFear, aipGreed, aipCuriosity, aipHope // //---------------------------------------------------------------------- // aipFear - how bad something might be. class aipFear : public aipNegEmotion { public: aipFear () {} virtual ~aipFear () {} virtual void dump_to_ptr (char *p) const; // used for debugging }; //---------------------------------------------------------------------- // aipGreed - how good something is known to be. class aipGreed : public aipPosEmotion { public: aipGreed () {} virtual ~aipGreed () {} virtual void dump_to_ptr (char *p) const; // used for debugging }; //---------------------------------------------------------------------- // aipCuriosity - how good something unknown might be. class aipCuriosity : public aipPosEmotion { public: aipCuriosity () {} virtual ~aipCuriosity () {} virtual void dump_to_ptr (char *p) const; // used for debugging }; //---------------------------------------------------------------------- // aipHope - Fear plus Greed plus Curiosity - the feeling about // whether things are going to work out well. // // The behavior is simplistic in this class. Subclasses may // define pre_msg_behavior() and post_msg_behavior() that considers // application-specific aspects. class aipHope : public aipCompEmotion { aipFear * m_fear; aipGreed * m_greed; aipCuriosity * m_curiosity; protected: void calc_compound () { if (m_fear && m_greed && m_curiosity) { aipEmotion::set_g ( m_fear->g() + m_greed->g() + m_curiosity->g() ); } } virtual void post_msg_behavior (aipMsg *m =0); public: aipHope (); virtual ~aipHope () {} void enable_slowly_degrade (); void disable_slowly_degrade (); virtual void dump_to_ptr (char *p) const; // used for debugging aipFear * fear () const { return m_fear; } aipGreed * greed () const { return m_greed; } aipCuriosity * curiosity () const { return m_curiosity; } virtual void set_g (aipG x); virtual void set_fear (aipG x); virtual void set_greed (aipG x); virtual void set_curiosity (aipG x); }; //====================================================================== // Aspect Iterators // //---------------------------------------------------------------------- // aipAspectItr - Iterator for aspects in a pandemonium class aipAspectItr : public aipDemonItr { public: aipAspectItr () {} aipAspectItr (aipPandemonium *p) { set_demon_itr (p,0); } aipAspectItr (const aipAspectItr& x) { set_demon_itr (x.pandemonium(), x.current()); } virtual ~aipAspectItr () {} aipAspectItr& operator = (const aipAspectItr& x) { set_demon_itr (x.pandemonium(), x.current()); return *this; } aipAspect * first() { return (aipAspect*)aipDemonItr::first(); } aipAspect * next() { return (aipAspect*)aipDemonItr::next(); } }; //---------------------------------------------------------------------- // aipEmotionItr - Iterator for emotion in a pandemonium class aipEmotionItr : public aipDemonItr { public: aipEmotionItr () {} aipEmotionItr (aipPandemonium *p) { set_demon_itr(p,0); } aipEmotionItr (const aipEmotionItr& x) { set_demon_itr (x.pandemonium(), x.current()); } virtual ~aipEmotionItr () {} aipEmotionItr& operator = (const aipEmotionItr& x) { set_demon_itr (x.pandemonium(), x.current()); return *this; } aipEmotion * first() { return (aipEmotion*)aipDemonItr::first(); } aipEmotion * next() { return (aipEmotion*)aipDemonItr::next(); } }; //====================================================================== #endif //====================================================================== // License // // Permission is hereby granted, free of charge, to any // person obtaining a copy of this software and associated // documentation files (the "Software"), to deal in the Software // without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to // whom the Software is furnished to do so, subject to the // following conditions: // // The copyright notice and this license shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. // //********************************************************************** aipGood.cpp0000644000076400007640000001143311063254442012127 0ustar brianbrian//********************************************************************** // aipGood.cpp - function bodies and global functions for aipGood.h // // aipGood.h implements goodness. // // Copyright (c) 1999, 2005 Brian Marshall // // See the license at end of this file. // // Developers/Contributers: // [BRM] Brian Marshall - Calgary - bmarshal@agt.net // // 05/09/07 [BRM] remove importance and likelihood // 05/07/31 [BRM] rework began // 99/05/01 [BRM] naming convention changed // 99/03/07 [BRM] development began // //---------------------------------------------------------------------- #include "aipGood.h" // #include // using namespace std; //====================================================================== // aipGBase. // All function bodies are in the header file. //====================================================================== // aipGoodness - How good or bad something is. // //---------------------------------------------------------------------- // Calculate the value of this goodness multiplied by a fraction. aipGoodness aipGoodness::calc_fraction (long numerator, long denominator) { if (is_forbidden()) return aipForbidden; if (denominator == 0) return aipNeutral; return aipGoodness ( (val() * numerator) / denominator ); } //---------------------------------------------------------------------- // Return a description of this aipGoodness. const char * aipGoodness::description () const { // log(" "); log(val()); log(" "); if (*this <= aipQuiteBad) { if (*this <= aipForbidden) { return "Forbidden"; } else if (*this <= aipVeryVeryBad) { return "Very Very Bad"; } else if (*this <= aipVeryBad) { return "Very Bad"; } else if (*this <= aipBad) { return "Bad"; } else { return "Quite Bad"; } } else if (*this <= aipVerySlightlyBad) { if (*this <= aipPrettyBad) { return "Pretty Bad"; } else if (*this <= aipFairlyBad) { return "Fairly Bad"; } else if (*this <= aipSomewhatBad) { return "Somewhat Bad"; } else if (*this <= aipSlightlyBad) { return "Slightly Bad"; } else { return "Very Slightly Bad"; } } else if (*this >= aipQuiteGood) { if (*this >= aipManditory) { return "Manditory"; } else if (*this >= aipVeryVeryGood) { return "Very Very Good"; } else if (*this >= aipVeryGood) { return "Very Good"; } else if (*this >= aipGood) { return "Good"; } else { return "Quite Good"; } } else if (*this >= aipVerySlightlyGood) { if (*this >= aipPrettyGood) { return "Pretty Good"; } else if (*this >= aipFairlyGood) { return "Fairly Good"; } else if (*this >= aipSomewhatGood) { return "Somewhat Good"; } else if (*this >= aipSlightlyGood) { return "Slightly Good"; } else { return "Very Slightly Good"; } } return "Neutral"; } //====================================================================== // Global functions // //---------------------------------------------------------------------- // Goodness plus a Goodness yields a Goodness aipGoodness operator + (const aipGoodness& x, const aipGoodness& y) { aipGoodness z = x; return (z += y); } aipGoodness operator - (const aipGoodness& x, const aipGoodness& y) { aipGoodness z = x; return (z -= y); } //---------------------------------------------------------------------- //====================================================================== // License // // Permission is hereby granted, free of charge, to any // person obtaining a copy of this software and associated // documentation files (the "Software"), to deal in the Software // without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to // whom the Software is furnished to do so, subject to the // following conditions: // // The copyright notice and this license shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. // // //********************************************************************** aipGood.h0000755000076400007640000002171311063254442011601 0ustar brianbrian//********************************************************************** // aipGood.h - Goodness // // Provides: aipGoodness (plus aipGBase) // // This header file declares classes, constants and functions // to provide a "goodness" type. // // See Design and Usage Notes near the end of this file. // The short story is: Goodnesses are combined by adding. // // Copyright (c) 1999, 2005 Brian Marshall // // See the license at end of this file. // // Developers/Contributers: // [BRM] Brian Marshall - Calgary - bmarshal@agt.net // // 05/10/28 [BRM] added constants aipPrettyGood and aipPrettyBad // 05/09/07 [BRM] removing importance and likelihood // 05/07/31 [BRM] reworking began. // 99/05/24 [BRM] added goodness * and / longs. // 99/05/01 [BRM] naming conventions changed. // 99/03/07 [BRM] development begun. // //---------------------------------------------------------------------- #ifndef aipGood_h_guard #define aipGood_h_guard //---------------------------------------------------------------------- // Classes and typedefs. Sub-classing is shown by indentation. class aipGBase; class aipGoodness; typedef aipGoodness aipG; //====================================================================== // aipGBase - Base class. // // This is a base class - the constructors are protected. class aipGBase { long m_val; protected: long val () const { return m_val; } void set (const aipGBase& x) { m_val = x.val(); } void set (long x) { m_val = x; } bool eq (const aipGBase& x) const { return m_val == x.val(); } bool ne (const aipGBase& x) const { return m_val != x.val(); } bool lt (const aipGBase& x) const { return m_val < x.val(); } bool gt (const aipGBase& x) const { return m_val > x.val(); } bool le (const aipGBase& x) const { return m_val <= x.val(); } bool ge (const aipGBase& x) const { return m_val >= x.val(); } void combine (const aipGBase& x) { set ( val() + x.val() ); } void uncombine (const aipGBase& x) { set ( val() - x.val() ); } aipGBase () { m_val = 0; } aipGBase (const aipGBase& x) { m_val = x.val(); } aipGBase (long x) { m_val = x; } public: ~aipGBase () {} long sign () const { return ( (m_val<0) ? -1 : 1 ); } long is_neg () const { return (m_val < 0); } long is_pos () const { return (m_val > 0); } long is_zero () const { return (m_val == 0); } long is_manditory () const { return (m_val > 900000); } long is_forbidden () const { return (m_val < -900000); } const char * description () const { return ""; } }; //====================================================================== // aipGoodness - How good or bad something is. class aipGoodness : public aipGBase { public: aipGoodness () : aipGBase() {} aipGoodness (const aipGoodness& x) : aipGBase(x) {} aipGoodness (long x) : aipGBase(x) {} ~aipGoodness () {} aipGoodness& operator = (const aipGoodness& x) { set(x); return *this; } bool operator == (const aipGoodness& x) const { return eq(x); } bool operator != (const aipGoodness& x) const { return ne(x); } bool operator < (const aipGoodness& x) const { return lt(x); } bool operator > (const aipGoodness& x) const { return gt(x); } bool operator <= (const aipGoodness& x) const { return le(x); } bool operator >= (const aipGoodness& x) const { return ge(x); } aipGoodness& operator += (const aipGoodness& x) { combine(x); return *this; } aipGoodness& operator -= (const aipGoodness& x) { uncombine(x); return *this; } void set_numeric_value (long x) { set(x); } aipGoodness calc_fraction (long numerator, long denominator); void apply_factor_1_to_5 (long f) { if (f <= 1) { set ( val() / 4 ); } else if (f == 2) { set ( val() / 2 ); } else if (f == 4) { set ( val() * 2 ); } else if (f >= 5) { set ( val() * 4 ); } } long numeric_value () const { return val(); } long absolute_value () const { return (val() >= 0) ? val() : (0 - val()); } const char * description () const; }; //====================================================================== // Global functions // //---------------------------------------------------------------------- // Goodness plus a Goodness yields a Goodness aipGoodness operator + (const aipGoodness& x, const aipGoodness& y); aipGoodness operator - (const aipGoodness& x, const aipGoodness& y); //====================================================================== // Constants // //---------------------------------------------------------------------- // aipGoodness constants. const aipGoodness aipForbidden = aipGoodness( -999999 ); const aipGoodness aipVeryVeryBad = aipGoodness( -1024 ); const aipGoodness aipVeryBad = aipGoodness( -512 ); const aipGoodness aipBad = aipGoodness( -256 ); const aipGoodness aipQuiteBad = aipGoodness( -64 ); const aipGoodness aipPrettyBad = aipGoodness( -32 ); const aipGoodness aipFairlyBad = aipGoodness( -16 ); const aipGoodness aipSomewhatBad = aipGoodness( -8 ); const aipGoodness aipLittleBitBad = aipGoodness( -4 ); const aipGoodness aipSlightlyBad = aipGoodness( -2 ); const aipGoodness aipVerySlightlyBad = aipGoodness( -1 ); const aipGoodness aipNeutral = aipGoodness( 0 ); const aipGoodness aipVerySlightlyGood = aipGoodness( 1 ); const aipGoodness aipSlightlyGood = aipGoodness( 2 ); const aipGoodness aipLittleBitGood = aipGoodness( 3 ); const aipGoodness aipSomewhatGood = aipGoodness( 4 ); const aipGoodness aipFairlyGood = aipGoodness( 6 ); const aipGoodness aipPrettyGood = aipGoodness( 8 ); const aipGoodness aipQuiteGood = aipGoodness( 10 ); const aipGoodness aipGood = aipGoodness( 12 ); const aipGoodness aipVeryGood = aipGoodness( 16 ); const aipGoodness aipVeryVeryGood = aipGoodness( 24 ); const aipGoodness aipManditory = aipGoodness( 999999 ); //---------------------------------------------------------------------- //====================================================================== #endif //====================================================================== // Design and Usage Notes // //---------------------------------------------------------------------- // Fast 4-byte objects - No Polymorphism // // aipGoodness is instantiated as a 4-byte objects that are can be // passed, copied and manipulated very quickly. Most class function // bodies are in this header file, to encourage the compiler to // implement them inline. // // The aipGoodness class is not polymorphic - the programmer is // expected to know the type of the object. The class contains no // virtual functions (and the destructors do nothing). // //---------------------------------------------------------------------- // Goodness // // Goodness is a measure of how good or bad something is. A goodness // is specified using constants (defined below). Goodnesses may be: // - compared as if are numeric values // - combined by adding (separated by subtracting) // // Application of Goodness: // In a decision, one option may be chosen over another // by comparing the goodness associated with each option. // // If the goodness of a situation is calculated as the sum of a large // number of goodness values, the value of this goodness, on its own, // might not have a lot of meaning. On the other hand, comparing the // goodness of two situations is meaningful. // //====================================================================== // License // // Permission is hereby granted, free of charge, to any // person obtaining a copy of this software and associated // documentation files (the "Software"), to deal in the Software // without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to // whom the Software is furnished to do so, subject to the // following conditions: // // The copyright notice and this license shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. // //********************************************************************** aipHighHope.cpp0000644000076400007640000006737711063254442012754 0ustar brianbrian//********************************************************************** // aipHighHope.cpp - function bodies for aipHighHope.h // // Copyright (c) 1999, 2005, 2008 Brian Marshall // // See the license at end of this file. // // Developers/Contributers: // [BRM] Brian Marshall - Calgary - bmarshal@agt.net // // 08/06/16 [BRM] added decision is_decided(), // removed decision groups, modified deciding, // new next_decision(), added aipHHImportance, // decisions may be decided more than once, // removed dcsn.m_opt_prev; small changes // 05/11/18 [BRM] added decision groups // worst decision optionally has extra weight // a failed decision gets increase in fear // moved call disable_slowly_degrade() to aipHHHope // fixed minor bug re: problem members: m_is_many_xxx // fixed minor bug re: m_tries_since_change_count // 05/11/12 [BRM] moved logic from problem to hope aspects // 05/11/07 [BRM] moved random_num() to class aipBase // 05/11/01 [BRM] count tries to get to best try // 05/10/20 [BRM] fixed bug re: count since improvement // disabled hope emotions slowly degrading // modified how decision and option hopes change // 05/10/02 [BRM] split out from aipProblem and aipDecision // 05/09/26 [BRM] development began // //---------------------------------------------------------------------- // The key functions are... // // aipG aipHHProblem::solve() // implements the High-Hope problem-solving technique // // void aipHHDecision::take_msg (aipMsg *m) // void aipHHOption::take_msg (aipMsg *m) // void aipHHFearAspect::take_msg (aipMsg *m) // void aipHHGreedAspect::take_msg (aipMsg *m) // void aipHHCuriosityAspect::take_msg (aipMsg *m) // decisions and options are affected by a received message // // Maybe: // void aipHHProblem::normalize_option_goodnesses() // something like this might be used in some kinds of problems // in which (some) options have a constant goodness that should // affect which option is chosen. // // Note that shortest-path-from-A-to-B problems, for example, // do not use normalize_option_goodnesses() - a short step is // not favored over a long step - people like non-stop flights. // //---------------------------------------------------------------------- #include "aipHighHope.h" #include #include using namespace std; //====================================================================== // aipHHImportance - importance for choosing decisions // //---------------------------------------------------------------------- // take_msg - take a message and maybe do something because of it void aipHHImportance::take_msg (aipMsg *mm) { aipHHMsg *m = (aipHHMsg *)mm; if (m->typ() == HH_Try_Has_Ended) { if (m->is_the_failure()) m_been_prob += 16; long imp_val = val(); if (imp_val > 64) { m_been_prob -= 12; } else if (imp_val > 16) { m_been_prob -= 4; } } else if (m->typ() == HH_No_Recent_Improve) { m_been_prob /= 2; } aipImportance::take_msg(mm); } //====================================================================== // aipHHHope - Hope with High-Hope aspects // //---------------------------------------------------------------------- // Constructor aipHHHope::aipHHHope () { aipHHFearAspect *fear_aspect = new aipHHFearAspect; aipHHGreedAspect *greed_aspect = new aipHHGreedAspect; aipHHCuriosityAspect *curiosity_aspect = new aipHHCuriosityAspect; if (fear_aspect && greed_aspect && curiosity_aspect) { if (fear()) fear()->add_aspect(fear_aspect); if (greed()) greed()->add_aspect(greed_aspect); if (curiosity()) curiosity()->add_aspect(curiosity_aspect); } disable_slowly_degrade(); } //====================================================================== // aipHHFearAspect - how messages affect fear in hope // //---------------------------------------------------------------------- // take_msg void aipHHFearAspect::take_msg (aipMsg *mm) { aipHHMsg *m = (aipHHMsg *)mm; aipHHSolveStat *ss = m->solve_stat(); if (!fear() || !ss) return; if (m->typ() == HH_Starting_Solve) { fear()->set_g(aipNeutral); } else if (m->typ() == HH_No_Recent_Change) { } else if (m->typ() == HH_No_Recent_Improve) { fear()->weaken(aipIntensity_A_Little); } else if (m->typ() == HH_No_Recent_Best_So_Far) { fear()->weaken(aipIntensity_Somewhat); } else if (m->typ() == HH_Try_Has_Ended) { if ( ss->try_result() == HH_Try_Has_Failed ) { if (m->is_in_cur_solution()) { fear()->strengthen(aipIntensity_Slightly); } else if (m->is_the_failure()) { fear()->strengthen(aipIntensity_A_Little); } } else { // a complete solution was found if (m->is_in_cur_solution()) { fear()->weaken(aipIntensity_A_Little); } } // end of block for when a complete solution was found } aipAspect::take_msg(mm); } //====================================================================== // aipHHGreedAspect - how messages affect greed in hope // //---------------------------------------------------------------------- // take_msg void aipHHGreedAspect::take_msg (aipMsg *mm) { aipHHMsg *m = (aipHHMsg *)mm; aipHHSolveStat *ss = m->solve_stat(); if (m->typ() == HH_Starting_Solve) { greed()->set_g(aipNeutral); } else if (m->typ() == HH_Try_Has_Ended) { if ( ss->try_result() == HH_Try_Is_New_Best ) { if (greed()->g() >= Greed_For_Best) { greed()->set_g(Greed_For_Was_Best); } } if ( ss->try_result() == HH_Try_Is_Improved ) { if (greed()->g() == Greed_For_Improved) { greed()->set_g(Greed_For_Changed); } } if ( ss->try_result() == HH_Try_Is_Changed ) { if (greed()->g() == Greed_For_Changed) { greed()->set_g(Greed_For_Complete); } } if ( ss->try_result() == HH_Try_Has_Failed ) { } else { // a complete solution was found if (m->is_in_cur_solution()) { if ( ss->try_result() == HH_Try_Is_New_Best ) { greed()->set_g(Greed_For_New_Best); } else if ( ss->try_result() == HH_Try_Is_A_Best ) { greed()->set_g(Greed_For_Best); } else if ( ss->try_result() == HH_Try_Is_Improved && greed()->g() < Greed_For_Improved ) { greed()->set_g(Greed_For_Improved); } else if ( ss->try_result() == HH_Try_Is_Changed && greed()->g() < Greed_For_Changed ) { greed()->set_g(Greed_For_Changed); } else if ( ss->try_result() == HH_Try_Is_Complete && greed()->g() < Greed_For_Complete ) { greed()->set_g(Greed_For_Complete); } } // end of block for decision in current solution } // end of block for when a complete solution was found } aipAspect::take_msg(mm); } //====================================================================== // aipHHCuriosityAspect - how messages affect curiosityin hope // //---------------------------------------------------------------------- // take_msg void aipHHCuriosityAspect::take_msg (aipMsg *mm) { aipHHMsg *m = (aipHHMsg *)mm; aipHHSolveStat *ss = m->solve_stat(); if (m->typ() == HH_Starting_Solve) { curiosity()->set_g(Curiosity_Starting); } else if (m->typ() == HH_No_Recent_Change) { if ( ! m->is_in_cur_solution() ) { curiosity()->set_g(Curiosity_Starting); } } else if (m->typ() == HH_No_Recent_Improve) { curiosity()->set_g(Curiosity_Starting); } else if (m->typ() == HH_No_Recent_Best_So_Far) { curiosity()->set_g(Curiosity_Starting); curiosity()->strengthen(aipIntensity_Somewhat); } else if (m->typ() == HH_Try_Has_Ended) { if ( ss->try_result() == HH_Try_Has_Failed ) { if (m->is_in_cur_solution()) { curiosity()->weaken(aipIntensity_A_Little); } } else { // a complete solution was found if (m->is_in_cur_solution()) { curiosity()->weaken(aipIntensity_Somewhat); if ( ss->try_result() == HH_Try_Is_New_Best ) { // Greed will go up, curiosity comes down... curiosity()->weaken(aipIntensity_Somewhat); // more } } // end of block for decision in current solution } // end of block for when a complete solution was found } aipAspect::take_msg(mm); } //====================================================================== // aipHHProblem // //---------------------------------------------------------------------- // Constructor aipHHProblem::aipHHProblem () { m_num_try = 1000; m_dcsn_is_progress = m_worst_is_extra_bad = 0; m_should_log_tries = 1; // default to: write to log about tries m_solve_stat = new aipHHSolveStat (this); } //---------------------------------------------------------------------- // Destructor aipHHProblem::~aipHHProblem () { if (m_solve_stat) delete m_solve_stat; } //---------------------------------------------------------------------- // add_hh_decision void aipHHProblem::add_hh_decision (aipHHDecision *x) { aipProblem::add_decision(x); } //---------------------------------------------------------------------- // hh_decision_iterator aipHHDecisionItr aipHHProblem::hh_decision_iterator() const { aipHHDecisionItr i(decision_pandemonium()); return i; } //---------------------------------------------------------------------- // return the number of decisions to be made long aipHHProblem::num_decisions() const { long num_dcsn = 0; aipHHDecisionItr itr = hh_decision_iterator(); aipHHDecision *d; for ( d = itr.first(); d; d = itr.next() ) { num_dcsn += d->num_to_decide(); } return num_dcsn; } //---------------------------------------------------------------------- // g - goodness meaningful to user aipG aipHHProblem::g() { return solve_stat()->g_usr_bsf(); } //---------------------------------------------------------------------- // normalize_option_goodnesses - sample - for some problems // // A function like this SAMPLE might be used in some types of // problems, in which the goodness of an option (to the user) // should be considered in picking options. // // This function is used to convert values meaningful to the user // (ex. miles, dollars, minutes) to goodness values that can be // combined and compared with other goodness values. // // A normalize_option_goodnesses() function (that might or might not // look like this sample) might be used in staff-scheduling, where // a supervisor could work a shift as a clerk, but only if it was // absolutely necessary. // // A normalize_option_goodnesses() function would not generally // be used in a find-the-shortest-path-from-A-to-B problem, where // a short step is not favored over a longer step; taking a short // step can simply mean another longer step is required. // // Option goodness m_g_constant is initially set to m_g_user_constant. // If these values are normal goodnesses, this function is not used. // // Note that m_g_constant and m_g_user_constant may contain negative // values because: // - they are values the user considers to be negative, or, // - they are value that the user wants solve() to minimize. // // This can really be improved, but for a first version... // // Calculate a divisor as: max-absolute-value / 16, and then: // - for positive values: x = (x / divisor) + 1 // - for negative values: x = (x / divisor) - 1 // // This could work ok. It will work very poorly if most of the // values are between, say, 1 and 100, and there are a few values // that are greater than 100000; all the smaller values will be // normalized to 1. void aipHHProblem::normalize_option_goodnesses () { aipHHDecisionItr d_itr = hh_decision_iterator(); aipHHDecision *dec; // used with the decision-iterator aipHHOption *opt; // used with the option-iterator aipG g_min = aipManditory; // Find min and max aipG g_max = aipForbidden; for ( dec = d_itr.first(); dec; dec = d_itr.next() ) { aipHHOptionItr o_itr = dec->hh_option_iterator(); for ( opt = o_itr.first(); opt; opt = o_itr.next() ) { if (opt->g_user_constant() < g_min) g_min = opt->g_user_constant(); if (opt->g_user_constant() > g_max) g_max = opt->g_user_constant(); } // end of loop through options in the decision } // end of loop through decisions long max_abs_val = g_min.absolute_value(); if (g_max.numeric_value() > max_abs_val) { max_abs_val = g_max.numeric_value(); } if (max_abs_val < 20) return; long divisor = max_abs_val / 16; for ( dec = d_itr.first(); dec; dec = d_itr.next() ) { aipHHOptionItr o_itr = dec->hh_option_iterator(); for ( opt = o_itr.first(); opt; opt = o_itr.next() ) { long val = opt->g_user_constant().numeric_value(); if (val > 0) { val = (val / divisor) + 1; } else if (val < 0) { val = (val / divisor) - 1; } if (val) opt->set_norm_g_constant(val); } } } //---------------------------------------------------------------------- // solve // // Return a goodness that is meaningful to the user - this might // be a goodness, or it might be dollars, miles, hours, etc. // // This function, plus the take_msg() function on decisions and // options, are the implementation of the High-Hope technique. // // It would seem that it would be appropriate to have a separate // try() function, but it would be only a small proportion of this // function, and it would change variables set in this function. // Having it all in this function is the clearest way. aipG aipHHProblem::solve() { aipHHSolveStat * ss = solve_stat(); aipHHMsg msg_starting_solve (this, HH_Starting_Solve); aipHHMsg msg_solve_has_ended (this, HH_Solve_Has_Ended); aipHHMsg msg_starting_try (this, HH_Starting_New_Try); aipHHMsg msg_try_has_ended (this, HH_Try_Has_Ended); aipHHMsg msg_this_is_bsf (this, HH_This_Is_Best_So_Far); aipHHMsg msg_no_recent_bsf (this, HH_No_Recent_Best_So_Far); aipHHMsg msg_no_recent_improve (this, HH_No_Recent_Improve); aipHHMsg msg_no_recent_change (this, HH_No_Recent_Change); take_msg(&msg_starting_solve); ss->m_g_cmp_cur = ss->m_g_cmp_bsf = ss->m_g_usr_bsf = aipForbidden; for (ss->m_i_try=0; ss->m_i_trym_i_try++) { ss->m_g_cmp_prev = ss->m_g_cmp_cur; ss->m_g_cmp_cur = aipNeutral; ss->m_num_decisions_prev = ss->m_num_decisions_made; ss->m_num_decisions_made = 0; ss->m_failed_decision = 0; ss->m_worst_decision = 0; ss->m_worst_decision_g_cmp = aipForbidden; take_msg(&msg_starting_try); try_to_find_solution(); // try take_msg(&msg_try_has_ended); if (ss->try_is_new_best()) { ss->m_g_cmp_bsf = ss->m_g_cmp_cur; ss->m_g_usr_bsf = ss->m_g_usr_cur; ss->m_num_tries_to_best = ss->m_i_try + 1; take_msg(&msg_this_is_bsf); ss->m_tries_since_bsf_count = 0; } else { ss->m_tries_since_bsf_count++; if (ss->m_tries_since_bsf_count > many_tries_since_bsf()) { take_msg(&msg_no_recent_bsf); ss->m_tries_since_bsf_count = 0; ss->m_tries_since_change_count = 0; // reset this count ss->m_tries_since_improve_count = 0; // reset this count ss->m_is_many_since_new_best = 1; } } if (ss->try_is_improvement()) { ss->m_tries_since_improve_count = 0; } else { ss->m_tries_since_improve_count++; if (ss->m_tries_since_improve_count > many_tries_since_improve()) { take_msg(&msg_no_recent_improve); ss->m_tries_since_improve_count = 0; ss->m_tries_since_change_count = 0; // reset this count ss->m_tries_since_bsf_count = 0; // reset this count ss->m_is_many_since_improve = 1; } } if (ss->try_is_a_change() || !ss->try_is_a_success()) { ss->m_tries_since_change_count = 0; } else { ss->m_tries_since_change_count++; if (ss->m_tries_since_change_count > many_tries_since_change()) { take_msg(&msg_no_recent_change); ss->m_tries_since_change_count = 0; // reset only this count ss->m_is_many_since_change = 1; } } if (should_log_tries()) log_this_try(); } // try loop end take_msg(&msg_solve_has_ended); return ss->m_g_usr_bsf; // best-so-far: goodness or miles, etc. } //---------------------------------------------------------------------- // try_to_find_solution - try to find a solution void aipHHProblem::try_to_find_solution () { aipHHSolveStat * ss = solve_stat(); ss->m_g_usr_cur = aipNeutral; // goodness or dollars or something ss->m_is_too_bad = 0; ss->m_is_many_since_new_best = 0; ss->m_is_many_since_improve = 0; ss->m_is_many_since_change = 0; aipHHDecision *decision; while ( 1 ) { decision = next_decision(); if (!decision) break; aipHHOption *opt = decision->hh_decide(); note_decide (decision, opt); if (!opt) { ss->m_failed_decision = decision; break; } ss->m_g_cmp_cur += opt->g_opt_cmp(); ss->m_g_usr_cur += opt->g_opt_usr(); if ( ss->m_g_cmp_cur.is_forbidden() ) break; if ( ss->m_g_cmp_cur < too_bad_to_go_on() ) { ss->m_is_too_bad = 1; break; } ss->m_num_decisions_made++; if ( !opt->g_opt_cmp().is_forbidden() && opt->g_opt_cmp() < ss->m_worst_decision_g_cmp ) { ss->m_worst_decision = decision; ss->m_worst_decision_g_cmp = opt->g_opt_cmp(); } if ( solution_is_complete() ) break; } // end of loop through decisions if (ss->m_problem && ss->m_problem->worst_is_extra_bad() && !ss->m_worst_decision_g_cmp.is_forbidden() ) { ss->m_g_cmp_cur += (ss->m_worst_decision_g_cmp + ss->m_worst_decision_g_cmp); } if ( solution_is_complete() ) { ss->m_solution_is_complete = 1; ss->m_solution_is_new_best = ss->m_g_cmp_cur > ss->m_g_cmp_bsf; } else { ss->m_solution_is_complete = 0; ss->m_solution_is_new_best = 0; ss->m_g_cmp_cur = ss->m_g_usr_cur = aipForbidden; } ss->m_try_result = 0; if (ss->try_is_new_best()) { ss->m_try_result = HH_Try_Is_New_Best; } else if (ss->try_is_a_best()) { ss->m_try_result = HH_Try_Is_A_Best; } else if (ss->try_is_improvement()) { ss->m_try_result = HH_Try_Is_Improved; } else if (!ss->try_is_a_success()) { ss->m_try_result = HH_Try_Has_Failed; } else if (ss->try_is_a_change()) { ss->m_try_result = HH_Try_Is_Changed; } else { ss->m_try_result = HH_Try_Is_Complete; } } //---------------------------------------------------------------------- // next_decision - the next decision to be decided (or zero) // // Return zero if there are no unmade decisions with hope better // than aipForbidden. Otherwise, choose the unmade decision with // the highest importance. aipHHDecision * aipHHProblem::next_decision() { aipHHDecision *dnext = 0; long imp_max = -9999999; aipHHDecisionItr itr = hh_decision_iterator(); for ( aipHHDecision *d = itr.first(); d; d = itr.next() ) { if ( d && !(d->is_decided()) ) { if (!dnext || dnext->imp() > imp_max) { dnext = d; imp_max = dnext->imp(); } } } return dnext; } //---------------------------------------------------------------------- // solution_is_complete - true if solution is complete // // By default, a solution is complete when all decisions have // been decided; subclasses may override this. int aipHHProblem::solution_is_complete () const { int done = m_solve_stat->m_num_decisions_made >= num_decisions(); return done; } //====================================================================== // aipHHDecision // //---------------------------------------------------------------------- // Constructor aipHHDecision::aipHHDecision (long the_num_to_decide) { m_hope = new aipHHHope; m_opt_cur = m_opt_bsf = 0; m_num_to_decide = the_num_to_decide; m_num_decided = 0; m_chsn_opts_cur = m_chsn_opts_bsf = 0; } //---------------------------------------------------------------------- // Destructor aipHHDecision::~aipHHDecision () { if (m_hope) delete m_hope; if (m_chsn_opts_cur) delete m_chsn_opts_cur; if (m_chsn_opts_bsf) delete m_chsn_opts_bsf; } //---------------------------------------------------------------------- // add_hh_option void aipHHDecision::add_hh_option(aipHHOption *x) { aipDecision::add_option(x); if ( num_to_decide() > 1 ) x->set_is_shared(); } //---------------------------------------------------------------------- // take_msg - take and react to a message void aipHHDecision::take_msg (aipMsg *mm) { aipHHMsg *m = (aipHHMsg *)mm; if (m->typ() == HH_Starting_New_Try) { m_opt_cur = 0; m_num_decided = 0; if (m_chsn_opts_cur) delete m_chsn_opts_cur; m_chsn_opts_cur = new aipPandemonium; } else if (m->typ() == HH_Try_Has_Ended) { int dcsn_in_cur_solution = opt_cur() ? 1 : 0; m->set_is_in_cur_solution(dcsn_in_cur_solution); aipHHSolveStat *ss = m->prob() ? m->prob()->solve_stat() : 0; int is_the_failure = ss ? ss->failed_dcsn() == this : 0; m->set_is_the_failure(is_the_failure); } else if (m->typ() == HH_This_Is_Best_So_Far) { if (is_decided()) { // ie. dcsn is in the solution m_opt_bsf = m_opt_cur; if (m_chsn_opts_bsf) delete m_chsn_opts_bsf; m_chsn_opts_bsf = m_chsn_opts_cur; m_chsn_opts_cur = 0; // so bsf does not get deleted } } m_importance.take_msg(mm); if (m_hope) m_hope->take_msg(mm); aipDecision::take_msg(mm); // decision gives message to options } //---------------------------------------------------------------------- // hh_option_iterator aipHHOptionItr aipHHDecision::hh_option_iterator () const { aipHHOptionItr i(option_pandemonium()); return i; } //---------------------------------------------------------------------- // current-option iterator aipHHOptionItr aipHHDecision::cur_opt_iterator () const { aipHHOptionItr i(m_chsn_opts_cur); return i; } //---------------------------------------------------------------------- // best-so-far-option iterator aipHHOptionItr aipHHDecision::bsf_opt_iterator () const { aipHHOptionItr i(m_chsn_opts_bsf); return i; } //---------------------------------------------------------------------- // g_hope aipG aipHHDecision::g_hope () const { return (m_hope ? m_hope->g() : aipNeutral); } //---------------------------------------------------------------------- // is_decided int aipHHDecision::is_decided () const { return (m_num_decided >= m_num_to_decide) ? 1 : 0; } //---------------------------------------------------------------------- // Return the number of unchosen, unforbidden options long aipHHDecision::num_opt_remaining () const { long num = 0; aipHHOptionItr itr = hh_option_iterator(); for ( aipHHOption * opt = itr.first(); opt; opt = itr.next() ) { if ( !(opt->is_chosen()) && !(opt->g_opt_cmp().is_forbidden()) ) num++; } return num; } //---------------------------------------------------------------------- // decide aipHHOption * aipHHDecision::hh_decide() { m_opt_cur = (aipHHOption*)decide(); if (m_opt_cur) { if (m_chsn_opts_cur) m_chsn_opts_cur->add(m_opt_cur); m_num_decided++; } return m_opt_cur; } //====================================================================== // aipHHOption - an option with an aipHHHope emotion // //---------------------------------------------------------------------- // Constructor aipHHOption::aipHHOption (aipG g_user_const) { m_g_user_constant = m_g_constant = g_user_const; m_bsf_chooser = 0; m_hope = new aipHHHope; } //---------------------------------------------------------------------- // Destructor aipHHOption::~aipHHOption () { if (m_hope) delete m_hope; } //---------------------------------------------------------------------- // take_msg - take and react to a message void aipHHOption::take_msg (aipMsg *mm) { aipHHMsg *m = (aipHHMsg *)mm; int opt_in_cur_solution = hh_opt_chooser() != 0; m->set_is_in_cur_solution(opt_in_cur_solution); if (m_hope) m_hope->take_msg(mm); if (m->typ() == HH_Starting_New_Try) { reset_chooser(); } else if (m->typ() == HH_This_Is_Best_So_Far) { m_bsf_chooser = hh_opt_chooser(); } aipOption::take_msg(mm); } //---------------------------------------------------------------------- // g_hope aipG aipHHOption::g_hope () const { return (m_hope ? m_hope->g() : aipNeutral); } //---------------------------------------------------------------------- // g_opt aipG aipHHOption::g_opt() { return (g_constant() + g_hope() + random_num(-2,2)); } //====================================================================== // aipHHDecisionItr - aipHHDecision iterator // // All function bodies are in the header file // //====================================================================== // aipHHOptionItr - aipHHOption iterator // // All function bodies are in the header file // //====================================================================== // aipHHSolveStat - solve() status and statistics // //---------------------------------------------------------------------- // Constructor aipHHSolveStat::aipHHSolveStat (aipHHProblem *p) { m_problem = p; if (!p) return; m_should_log_tries = p->should_log_tries(); m_g_cmp_cur = m_g_cmp_bsf = m_g_cmp_prev = aipForbidden; m_g_usr_cur = m_g_usr_bsf = aipForbidden; m_i_try = 0; // The following assignments are not necessary, but just // in case someone looks at this before calling solve()... m_solution_is_complete = m_solution_is_new_best = 0; m_num_decisions_made = m_num_decisions_prev = 0; m_num_tries_to_best = 0; m_failed_decision = 0; m_worst_decision = 0; m_worst_decision_g_cmp = aipForbidden; m_tries_since_bsf_count = 0; m_tries_since_improve_count = 0; m_tries_since_change_count = 0; m_is_too_bad = 0; m_is_many_since_new_best = 0; m_is_many_since_improve = m_is_many_since_change = 0; } //====================================================================== // aipHHMsg - High-Hope problem-solving message // //---------------------------------------------------------------------- // Constructor aipHHMsg::aipHHMsg (aipHHProblem *p, short typ) : aipMsg(typ) { m_prob = p; m_is_in_cur_solution = m_is_the_failure = 0; } //====================================================================== // License // // Permission is hereby granted, free of charge, to any // person obtaining a copy of this software and associated // documentation files (the "Software"), to deal in the Software // without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to // whom the Software is furnished to do so, subject to the // following conditions: // // The copyright notice and this license shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. // // //********************************************************************** aipHighHope.h0000755000076400007640000006370511063254442012413 0ustar brianbrian//********************************************************************** // aipHighHope.h - Problem Solving using the High-Hope Technique // // Provides: aipHHProblem aipHHDecision aipHHOption // aipHHImportance aipHHDecisionItr aipHHOptionItr // // An aipHHProblem problem is assembled with aipHHDecision and // aipHHOption objects. // // Calling aipHHProblem::solve() attempts to solve the problem // using the High-Hope technique. // // An aipHHProblem problem has aipHHDecision decisions and // aipHHOption options. // // Copyright (c) 2005, 2008 Brian Marshall // // See the license at end of this file. // // Developers/Contributers: // [BRM] Brian Marshall - Calgary - bmarshal@agt.net // // // 08/06/16 [BRM] added decision is_decided(), // removed decision groups, modified deciding, // new next_decision(), added aipHHImportance, // decisions may be decided multiple times, // removed dcsn.m_opt_prev; small changes // 05/11/18 [BRM] a failed try that got further than the previous // try is, optionally, an improvement // worst decision has extra affect on further tries // modified constant Curiosity_Starting // 05/11/18 [BRM] added aipHHDcsnGrp and support in decisions // 05/11/12 [BRM] added aipHHHope and aspect classes // moved logic from aipHHProblem to hope aspects // added aipHHSolveStat, try_to_find_solution() // removed redundant rand() function // 05/11/07 [BRM] moved random_num() to class aipBase // 05/11/01 [BRM] count tries to get to best try // 05/10/24 [BRM] added decision, option: any_fear(), any_greed() // added HH_Try_Is_A_Best // 05/10/21 [BRM] added aipHHOption::dump_hope_to_ptr() // 05/10/02 [BRM] Split out from non-HH problem, decision // 05/09/20 [BRM] Development begins. // //---------------------------------------------------------------------- // The High-Hope Technique // // In the High-Hope technique, decisions and options have an aipHHHope // member which models hope as fear + greed + curiosity... // Fear: the desire to avoid known bad things // Greed: the desire to take advantage of known good things // Curiosity: the desire to try new things // // Solving a problem involves trying many times. The tries affect // the hopes and the hopes affect the subsequent tries. // // The High-Hope technique: // - for a specified number of tries... // - attempt to find a solution: // - while the solution is not complete... // - pick a decision in an application-specific way // - for the decision: choose an option: // the one with the highest g_opt(): the sum of: // - the sum of: // - the option-hope-goodness // possibly plus: // - (normalized) constant-goodness // - application-specific non-constant goodness // - if this solution is the best-so-far: remember it // // One try differs from the next as a result of changes in the hope // of decisions and options. (For example, once an option has been // tried, the curiosity associated with that option goes down.) // // If a decision has multiple options that are the best, one is // chosen at random. (The random-number generator will always return // the same series of random numbers.) // // In a try, the next decision to decide is the one with the // highest importance. This may be overridden by subclasses of // aipHHProblem. // // Fear, Greed and Curiosity are handled differently after each try // (for each decision and option): // Fear // Starts at aipNeutral; when a try fails, fear becomes // stronger; when a solution is found, fear becomes weaker. // Greed // Starts at aipNeutral, and when a complete solution is found, // it is set to one of: // New-Best, Best, Improved, Changed, Complete. // Curiosity // Starts high, drops as decisions and options are tried; // // After a series of tries that result in the same solution, the // hope will be raised to an identical high value for all decisions // and options that are not in the solution. // // After many tries that do not result in a new-best or improved // solution, fear is weakened and all curiosities are reset to an // identical high value. // // The HighHope software does not use the hope of decisions; // applications may use it. Applications in which an option // determines the next decision (such as finding a path through // a network of nodes) will presumably use decision hope. // //---------------------------------------------------------------------- #ifndef aipHighHope_h_guard #define aipHighHope_h_guard #include "aipImportance.h" #include "aipProblem.h" //---------------------------------------------------------------------- // Constants static const aipG Curiosity_Starting = aipVeryGood + aipGood; static const aipG Greed_For_New_Best = aipGood; static const aipG Greed_For_Best = aipQuiteGood; static const aipG Greed_For_Was_Best = aipPrettyGood; static const aipG Greed_For_Improved = aipPrettyGood; static const aipG Greed_For_Changed = aipFairlyGood; static const aipG Greed_For_Complete = aipSomewhatGood;; // Note: High-Hope technique uses: 11000 to 11999 // aipMsg typ values: static const short HH_Starting_Solve = 11001; static const short HH_Solve_Has_Ended = 11002; static const short HH_Starting_New_Try = 11003; static const short HH_Try_Has_Ended = 11004; static const short HH_This_Is_Best_So_Far = 11005; static const short HH_No_Recent_Improve = 11006; static const short HH_No_Recent_Change = 11007; static const short HH_No_Recent_Best_So_Far = 11008; // Try-status after a try has ended static const short HH_Try_Has_Failed = 11101; static const short HH_Try_Is_New_Best = 11102; static const short HH_Try_Is_A_Best = 11103; static const short HH_Try_Is_Improved = 11104; static const short HH_Try_Is_Changed = 11105; static const short HH_Try_Is_Complete = 11106; //---------------------------------------------------------------------- // Classes. Sub-classing is shown by indentation. // class aipG is a typedef for aipGoodness // aipHope is a compound emotion: Fear + Greed + Curiosity // class aipBase; ( in aipBase.h ) class aipHHImportance; // Importance for choosing decisions // class aipDemon; ( in aipPandemonium.h ) // class aipAspect; ( in aipEmotion.h) // class aipEmotion; ( in aipEmotion.h) // class aipCompEmotion; ( in aipEmotion.h) // class aipHope; ( in aipEmotion.h) class aipHHHope; // hope with High-Hope aspects class aipHHFearAspect; // how messages affect fear in hope class aipHHGreedAspect; // how msgs affect greed in hope class aipHHCuriosityAspect; // msgs affect curiosity in hope // class aipProblem; ( in aipProblem.h ) class aipHHProblem; // problem with aipDecision // class aipDecision; ( in aipDecision.h ) class aipHHDecision; // Decision in aipHHProblem // class aipOption; ( in aipDecision.h ) class aipHHOption; // Option in aipHHDecision // class aipDemonItr; ( in aipPandemonium.h ) class aipHHDecisionItr; // aipHHDecision iterator class aipHHOptionItr; // aipHHOption iterator class aipHHSolveStat; // solve() status and statistics // class aipMsg; ( in aipBase.h ) class aipHHMsg; // High-Hope problem-solving message //====================================================================== // aipHHImportance - Importance for choosing decisions class aipHHImportance : public aipImportance { long m_been_prob; // has been a problem aspect public: aipHHImportance () { m_been_prob = 0; } virtual ~aipHHImportance () {} virtual long val () const { return aipImportance::val() + m_been_prob; } virtual void take_msg (aipMsg *m); }; //====================================================================== // aipHHHope - Hope with High-Hope aspects class aipHHHope : public aipHope { public: aipHHHope (); virtual ~aipHHHope () {} }; //====================================================================== // aipHHFearAspect - how messages affect fear in hope class aipHHFearAspect : public aipAspect { protected: aipFear * fear () const { return (aipFear *)emotion(); } public: aipHHFearAspect() {} virtual ~aipHHFearAspect() {} virtual void take_msg (aipMsg *m); }; //====================================================================== // aipHHGreedAspect - how msgs affect greed in hope class aipHHGreedAspect : public aipAspect { protected: aipGreed * greed () const { return (aipGreed *)emotion(); } public: aipHHGreedAspect() {} virtual ~aipHHGreedAspect() {} virtual void take_msg (aipMsg *m); }; //====================================================================== // aipHHCuriosityAspect - how msgs affect curiosity in hope class aipHHCuriosityAspect : public aipAspect { protected: aipCuriosity * curiosity() const { return (aipCuriosity*)emotion(); } public: aipHHCuriosityAspect() {} virtual ~aipHHCuriosityAspect() {} virtual void take_msg (aipMsg *m); }; //====================================================================== // aipHHProblem - Problem with solve() that uses High-Hope technique. // // m_g_cmp_cur, m_g_cmp_prev, m_g_cmp_bsf are goodnesses // of solutions - the sum of the goodness of each decision. These // values are meaningful when compared to each other; they may or // may not be meaningful when compared to standard goodness constants // like aipQuiteGood and aipVeryBad. They may not be actual goodness // values at all - in a shortest-path problem, they might be the // solution-distance, in metres or kilometres, multiplied by -1. // // For problems in which option-goodness should affect which option // is chosen by a decision... // normalize_option_goodnesses() should be called before solve() // if options do get goodnesses that are not really goodness values // (ex: they are kilometers or dollars), and there are values // that would be inappropriate goodness values - for example, a nice // low cost of $1200, stored in a goodness as -1200, interpreted as // a goodness would be very-very-bad. normalize_option_goodnesses() // will force the values into a range of roughly -17 to +17. // // Subclasses that require option-goodness normalization can: // - use normalize_option_goodnesses(), or, // - override normalize_option_goodnesses(), or, // - normalize the values before they are used to make options // // solve() tries to find a maximum; if the best solution is a minimum, // appropriate user-meaningful goodness values must be multiplied by // -1. If this sort of thing is going on, a subclass should override // the virtual function bsf_goodness_for_user(). // // bsf_goodness_for_user() returns the goodness value that is to // be reported to the user for the best-so-far solution found. // // If m_dcsn_is_progress is: // 1: each decision is a step closer to completing solution // 0: different solutions can have different number of decisions // // If m_worst_is_extra_bad is true, the goodess of the worst decision // will be added to the solution-goodness, for purposes of comparing // to other solutions, an extra couple of times. class aipHHProblem : public aipProblem { long m_num_try; // times solve() will try int m_dcsn_is_progress; // 0 = no int m_worst_is_extra_bad; // 0 = no int m_should_log_tries; // 0 = no aipHHSolveStat * m_solve_stat; // status and statistics protected: virtual void try_to_find_solution (); virtual aipHHDecision * next_decision (); virtual int solution_is_complete () const; virtual void log_this_try () {} // threshold values used in solve() virtual long many_tries_since_change() const { return (5 + m_num_try/400); } virtual long many_tries_since_improve() const { return (20 + m_num_try/200); } virtual long many_tries_since_bsf() const { return (40 + m_num_try/50); } virtual aipG too_bad_to_go_on() const { return aipForbidden; // subclasses can override } // used in some types of problems... virtual void normalize_option_goodnesses(); // sample public: aipHHProblem (); virtual ~aipHHProblem (); // setting up the problem void set_num_try (long x) { m_num_try = x; } void set_dcsn_is_progress (int x) { m_dcsn_is_progress = x; } void set_worst_is_extra_bad (int x) { m_worst_is_extra_bad = x; } void add_decision (aipDecision *x) {} // use add_hh_decsion void add_hh_decision (aipHHDecision *x); void enable_log_tries () { m_should_log_tries = 1; } void disable_log_tries () { m_should_log_tries = 0; } int should_log_tries () const { return m_should_log_tries; } // about the problem long num_try () const { return m_num_try; } long num_decisions() const; int dcsn_is_progress () const { return m_dcsn_is_progress; } int worst_is_extra_bad () const { return m_worst_is_extra_bad; } aipHHDecisionItr hh_decision_iterator() const; aipHHSolveStat * solve_stat () const { return m_solve_stat; } virtual aipG g(); virtual void take_msg (aipMsg *m) { aipProblem::take_msg(m); } virtual void note_decide (aipHHDecision *d, aipHHOption *o) {} virtual aipG solve(); // returns goodness for user }; //====================================================================== // aipHHDecision - Decision used in an High-Hope aipHHProblem // // A set of decisions may share a set of options. An option may be // added to more than one decision, but, for destruction purposes, // only one decision will own the option. // // An application may require that a decision be decided more than // once to complete a solution - m_num_to_decide is set to more // than one (in the constructor) and the chosen options for the // current and best-so-far solutions for this decision are stored // in the m_chsn_opts_xxx pandemoniums. (If the decision is only // to be decided once, the pointers to these pandemoniums are zero. class aipHHDecision : public aipDecision { aipHHImportance m_importance; // importance in decision order aipHHHope * m_hope; // how well this decision is doing aipHHOption * m_opt_cur; // The curremt option choice aipHHOption * m_opt_bsf; // The best-so-far option choice long m_num_to_decide; // times to decide in a try long m_num_decided; // times decided in this try aipPandemonium * m_chsn_opts_cur; // opts chosen in cur try aipPandemonium * m_chsn_opts_bsf; // opts chosen in bsf try public: aipHHDecision (long the_num_to_decide =1); virtual ~aipHHDecision (); void add_option (aipOption *x) {} // use add_hh_option void add_hh_option (aipHHOption *x); void dump_hope_to_ptr (char *p) const { m_hope->dump_to_ptr(p); } aipHHProblem * hh_prob () const { return (aipHHProblem*)owner(); } virtual void take_msg (aipMsg *m); aipHHOptionItr hh_option_iterator () const; aipHHOptionItr cur_opt_iterator () const; // current options aipHHOptionItr bsf_opt_iterator () const; // best-so-far opts aipG g_hope () const; long num_to_decide (void) const { return m_num_to_decide; } long num_decided (void) const { return m_num_decided; } int is_decided () const; virtual long imp () const { return m_importance.val(); } long num_opt_remaining () const; int any_fear () const { if (!m_hope) return 0; return (m_hope->fear()->g() < aipNeutral); } int any_greed () const { if (!m_hope) return 0; return (m_hope->greed()->g() > aipNeutral); } aipHHOption * opt_cur () const { return m_opt_cur; } aipHHOption * opt_bsf () const { return m_opt_bsf; } virtual aipHHOption * hh_decide(); }; //====================================================================== // aipHHOption - an option used in High-Hope problem-solving // // m_g_user_constant is a constant goodness that is meaningful // to the user (although it may have been multiplied by -1 if the // problem-solving is looking for a minimum). // // m_g_constant is m_g_user_constant that has been normalized and // scaled into a range so that it can be meaningfully added to the // goodness of the hope. // // In aipDecision::solve()... // g_opt() is used to make decisions // g_opt_cmp() is used to compare solutions // g_opt_usr() is reported to the user. // // Generally, g_opt() is partially or completely based on the hope // of the option, whereas g_opt_cmp() and g_opt_usr(), used in the // calculation of goodness of a solution, are not affected by hope. // // g_opt_cmp() and g_opt_usr() may return the same value, or, // if a minimum is desired, and therefore m_g_user_constant was made // negative, g_opt_usr() may return the negative of g_opt_cmp(). class aipHHOption : public aipOption { aipG m_g_user_constant; aipG m_g_constant; aipHHDecision * m_bsf_chooser; aipHHHope * m_hope; // how well this option is doing public: aipHHOption(aipG g_user_const); virtual ~aipHHOption(); // for aipHHProblem::normalize_option_goodnesses() void set_norm_g_constant (long new_val) { m_g_constant.set_numeric_value(new_val); } void dump_hope_to_ptr (char *p) const { m_hope->dump_to_ptr(p); } aipHHDecision * hh_opt_owner () const { return (aipHHDecision*)owner(); // all options have an owner } aipHHDecision * hh_opt_chooser () const { return (aipHHDecision*)chooser(); // or, zero before decide() } aipHHDecision * hh_bsf_chooser () const { return m_bsf_chooser; // best-so-far chooser of this opt } int is_chosen () const { return chooser() ? 1 : 0; } virtual void take_msg (aipMsg *m); aipG g_constant () const { return m_g_constant; } aipG g_user_constant () const { return m_g_user_constant; } aipG g_hope () const; int any_fear () const { if (!m_hope) return 0; return (m_hope->fear()->g() < aipNeutral); } int any_greed () const { if (!m_hope) return 0; return (m_hope->greed()->g() > aipNeutral); } virtual aipG g_opt(); virtual aipG g_opt_cmp() { return g_user_constant(); } virtual aipG g_opt_usr() { return g_user_constant(); } }; //====================================================================== // aipHHDecisionItr - Iterator for aipHHDecision in a aipHHProblem class aipHHDecisionItr : public aipDemonItr { public: aipHHDecisionItr () {} aipHHDecisionItr (aipPandemonium *p) { set_demon_itr(p,0); } aipHHDecisionItr (const aipHHDecisionItr& x) { set_demon_itr (x.pandemonium(), x.current()); } virtual ~aipHHDecisionItr () {} aipHHDecisionItr& operator = (const aipHHDecisionItr& x) { set_demon_itr (x.pandemonium(), x.current()); return *this; } aipHHDecision * first() { return (aipHHDecision*)aipDemonItr::first(); } aipHHDecision * next() { return (aipHHDecision*)aipDemonItr::next(); } }; //====================================================================== // aipHHOptionItr - Iterator for aipHHOption in an aipHHDecision class aipHHOptionItr : public aipDemonItr { public: aipHHOptionItr () {} aipHHOptionItr (aipPandemonium *p) { set_demon_itr(p,0); } aipHHOptionItr (const aipHHOptionItr& x) { set_demon_itr (x.pandemonium(), x.current()); } virtual ~aipHHOptionItr () {} aipHHOptionItr& operator = (const aipHHOptionItr& x) { set_demon_itr (x.pandemonium(), x.current()); return *this; } aipHHOption * first() { return (aipHHOption*)aipDemonItr::first(); } aipHHOption * next() { return (aipHHOption*)aipDemonItr::next(); } }; //====================================================================== // aipHHSolveStat - solve() status and statistics class aipHHSolveStat { aipHHProblem * m_problem; friend class aipHHProblem; // About the solving... int m_should_log_tries; // 0 = do not write about try in log // sum of option.g_opt_cmp() - can be compared to each other // (may include dollars or miles or something) aipG m_g_cmp_cur; // Goodness of current try aipG m_g_cmp_prev; // Goodness of previous try aipG m_g_cmp_bsf; // Goodness of best-so-far try // sum of option.g_opt_usr - to be reported to the user // (may be miles or dollars or minutes or goodness, etc.) aipG m_g_usr_cur; // Goodness (or $ or miles, etc. ) aipG m_g_usr_bsf; // Goodness of best-so-far try // About the current try... long m_i_try; short m_try_result; int m_solution_is_complete; int m_solution_is_new_best; long m_num_decisions_made; // in current try, in solve() long m_num_decisions_prev; // in previous try, in solve() long m_num_tries_to_best; aipHHDecision * m_failed_decision; // or zero if success or quit aipHHDecision * m_worst_decision; aipG m_worst_decision_g_cmp; long m_tries_since_change_count; long m_tries_since_improve_count; long m_tries_since_bsf_count; int m_is_too_bad; int m_is_many_since_new_best; int m_is_many_since_improve; int m_is_many_since_change; public: aipHHSolveStat(aipHHProblem *p); virtual ~aipHHSolveStat() {} // sum of option.g_opt_cmp() - can be compared to each other aipG g_cmp_cur () const { return m_g_cmp_cur; } aipG g_cmp_prev () const { return m_g_cmp_prev; } aipG g_cmp_bsf () const { return m_g_cmp_bsf; } // sum of option.g_opt_usr() - can be reported to user aipG g_usr_cur () const { return m_g_usr_cur; } aipG g_usr_bsf () const { return m_g_usr_bsf; } // about the current try ... long i_try () const { return m_i_try; } short try_result () const { return m_try_result; } int solution_is_complete () const { return m_solution_is_complete; } int solution_is_new_best () const { return m_solution_is_new_best; } long num_tries_to_best () const { return m_num_tries_to_best; } long percent_of_tries_done() { return ( (m_i_try * 100) / m_problem->num_try() ); } aipHHDecision * failed_dcsn () const { return m_failed_decision; } int is_too_bad () const { return m_is_too_bad; } int try_is_a_success () { return (m_g_cmp_cur > aipForbidden); } int prev_is_a_success () { return (m_g_cmp_prev > aipForbidden); } int try_is_new_best () { return m_solution_is_new_best; } int try_is_a_best () { return (try_is_a_success() && m_g_cmp_cur == m_g_cmp_bsf); } int try_is_a_change () { return (m_g_cmp_cur != m_g_cmp_prev); } int try_is_improvement() { return ( (prev_is_a_success() && m_g_cmp_cur > m_g_cmp_prev) || (m_problem && m_problem->dcsn_is_progress() && m_g_cmp_bsf.is_forbidden() && !m_is_too_bad && m_i_try > 0 && m_num_decisions_made > m_num_decisions_prev) ); } int is_many_since_new_best () const { return m_is_many_since_new_best; } int is_many_since_improve () const { return m_is_many_since_improve; } int is_many_since_change () const { return m_is_many_since_change; } long tries_since_change_count () const { return m_tries_since_change_count; } long tries_since_improve_count () const { return m_tries_since_improve_count; } long tries_since_bsf_count () const { return m_tries_since_bsf_count; } }; //====================================================================== // aipHHMsg - message for High-Hope problem-solving class aipHHMsg : public aipMsg { aipHHProblem * m_prob; int m_is_in_cur_solution; int m_is_the_failure; public: aipHHMsg (aipHHProblem *p, short typ); ~aipHHMsg() {} aipHHProblem * prob () const { return m_prob; } aipHHSolveStat * solve_stat () const { return m_prob ? m_prob->solve_stat() : 0; } void set_is_in_cur_solution (int x) { m_is_in_cur_solution = x; } void set_is_the_failure (int x) { m_is_the_failure = x; } int is_in_cur_solution () const { return m_is_in_cur_solution; } int is_the_failure () const { return m_is_the_failure; } }; //====================================================================== #endif //====================================================================== // License // // Permission is hereby granted, free of charge, to any // person obtaining a copy of this software and associated // documentation files (the "Software"), to deal in the Software // without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to // whom the Software is furnished to do so, subject to the // following conditions: // // The copyright notice and this license shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. // //********************************************************************** aipImportance.h0000755000076400007640000000550111063254442013007 0ustar brianbrian//********************************************************************** // aipImportance.h - Importance - a base class // // Provides: aipImportance // // Importances derive from aipBase; they can take_msg(). // // Copyright (c) 2007 Brian Marshall // // See the license at end of this file. // // Developers/Contributers: // [BRM] Brian Marshall - Calgary - bmarshal@agt.net // // 07/12/24 [BRM] development begun. // //---------------------------------------------------------------------- // Implementation // // Currently, a call to importance.val() recalculates the value each // time it is called: val() in a subclass returns val() in the parent // class plus one or more aspects. These aspects change, mostly // in calls to importance.take_msg(). // //---------------------------------------------------------------------- #ifndef aipImportance_h_guard #define aipImportance_h_guard #include "aipBase.h" //---------------------------------------------------------------------- // Classes. Sub-classing is shown by indentation. // class aipBase; (in aipBase.h) class aipImportance; // Base class for importances //====================================================================== // aipImportance - Base class for importances class aipImportance : public aipBase { public: aipImportance () {} virtual ~aipImportance () {} virtual long val () const { return 0; } virtual void take_msg (aipMsg *m) {} }; //====================================================================== #endif //====================================================================== // License // // Permission is hereby granted, free of charge, to any // person obtaining a copy of this software and associated // documentation files (the "Software"), to deal in the Software // without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to // whom the Software is furnished to do so, subject to the // following conditions: // // The copyright notice and this license shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. // //********************************************************************** aipPandemonium.cpp0000644000076400007640000002525711063254442013524 0ustar brianbrian//********************************************************************** // aipPandemonium.cpp - function bodies for aipPandemonium.h // // Copyright (c) 2005, 2008 Brian Marshall // // See the license at end of this file. // // Developers/Contributers: // [BRM] Brian Marshall - Calgary - bmarshal@agt.net // // 08/01/28 [BRM] removed delete_demon(), changed ownership scheme; // pandemoniums can be ordered // 05/11/09 [BRM] pandemonium can now add demon without owning it // 05/11/01 [BRM] made pandemonium a queue (rather than a stack) // 05/10/28 [BRM] Fixed implied pointer to int conversion // 05/09/10 [BRM] development began // //---------------------------------------------------------------------- #include "aipPandemonium.h" //====================================================================== // aipDemon // All function bodies are in the header file. //====================================================================== // aipPandemonium //---------------------------------------------------------------------- // Constructor aipPandemonium::aipPandemonium () { m_first = m_last = 0; m_num_demon = 0; m_is_distinct = 0; } //---------------------------------------------------------------------- // Destructor aipPandemonium::~aipPandemonium () { while (m_first) { if (m_first->next()) { m_first = m_first->next(); delete m_first->prev(); } else { delete m_first; m_first = 0; } } } //---------------------------------------------------------------------- // add - add a demon to the pandemonium // // if this is a distinct list of ordered deomons, and the new demon // keys match those of a demon already in the list, the new demon // will be deleted. void aipPandemonium::add (aipDemon *demon) { if (!demon) return; int is_duplicate = 0; int is_ordered = demon->num_keys() > 0; aipDemonLink * new_link = new aipDemonLink (demon, 0, 0); if (!new_link) return; if (!m_first) { // list is empty m_first = m_last = new_link; } else if ( is_ordered && is_distinct() && ( new_link->is_equal_to(m_first) || new_link->is_equal_to(m_last) ) ) { is_duplicate = 1; } else if ( !is_ordered || !(new_link->less_than(m_last)) ) { // add at end m_last->set_next(new_link); new_link->set_prev(m_last); m_last = new_link; } else if ( new_link->less_than(m_first) ) { // add at start new_link->set_next(m_first); m_first->set_prev(new_link); m_first = new_link; } else { // list has at least 2 links aipDemonLink *nxt_link = m_first->next(); while ( nxt_link->less_than(new_link) ) { nxt_link = nxt_link->next(); } if ( is_ordered && is_distinct() && nxt_link->is_equal_to(new_link) ) { is_duplicate = 1; } else { new_link->set_next(nxt_link); new_link->set_prev(nxt_link->prev()); nxt_link->prev()->set_next(new_link); nxt_link->set_prev(new_link); } } if (is_duplicate) { delete new_link; // and the demon } else { m_num_demon++; } } //---------------------------------------------------------------------- // take_msg - take a message and maybe do something because of it void aipPandemonium::take_msg (aipMsg *m) { aipDemonItr itr(this); for ( aipDemon *d =itr.first(); d; d=itr.next() ) { d->take_msg(m); } } //====================================================================== // aipDemonLink - a link in a doubly-linked-list // //---------------------------------------------------------------------- // Constructor aipDemonLink::aipDemonLink (aipDemon *demon, aipDemonLink *prev, aipDemonLink *next) { m_demon = demon; m_prev = prev; m_next = next; if ( demon->is_owned() ) { m_is_owner = 0; } else { m_is_owner = 1; demon->set_is_owned(); } } //---------------------------------------------------------------------- // Destructor aipDemonLink::~aipDemonLink () { if (m_is_owner && m_demon) delete m_demon; } //---------------------------------------------------------------------- // less_than - return 1 (true) if this is less than the argument int aipDemonLink::less_than (aipDemonLink *y) { if (!y) return 0; aipDemon *xdemon = this->demon(); aipDemon *ydemon = y->demon(); long nkey = m_demon->num_keys(); if (ydemon->num_keys() < nkey) nkey = ydemon->num_keys(); if (nkey < 1 || nkey > 4) return 0; // nkey==0 means not ordered if ( xdemon->key1() < ydemon->key1() ) return 1; if ( xdemon->key1() > ydemon->key1() ) return 0; if (nkey < 2) return 0; if ( xdemon->key2() < ydemon->key2() ) return 1; if ( xdemon->key2() > ydemon->key2() ) return 0; if (nkey < 3) return 0; if ( xdemon->key3() < ydemon->key3() ) return 1; if ( xdemon->key3() > ydemon->key3() ) return 0; if (nkey < 4) return 0; if ( xdemon->key4() < ydemon->key4() ) return 1; // if ( xdemon->key4() > ydemon->key4() ) return 0; return 0; } //---------------------------------------------------------------------- // is_equal_to - return 1 (true) if this is equal to the argument int aipDemonLink::is_equal_to (aipDemonLink *y) { if (!y) return 0; aipDemon *xdemon = this->demon(); aipDemon *ydemon = y->demon(); long nkey = m_demon->num_keys(); if (ydemon->num_keys() < nkey) nkey = ydemon->num_keys(); if (nkey < 1 || nkey > 4) return 0; // nkey==0 means not ordered if ( xdemon->key1() != ydemon->key1() ) return 0; if (nkey < 2) return 1; if ( xdemon->key2() != ydemon->key2() ) return 0; if (nkey < 3) return 1; if ( xdemon->key3() != ydemon->key3() ) return 0; if (nkey < 4) return 1; if ( xdemon->key4() != ydemon->key4() ) return 0; return 1; } //====================================================================== // aipDemonItr - an iterator through the demons in a pandemonium //---------------------------------------------------------------------- // first - return a pointer to the first demon in the list // (and the current link is set to the first link) // Return zero if the list is empty. aipDemon * aipDemonItr::first () { m_current = first_link(); return ( m_current ? m_current->demon() : 0); } //---------------------------------------------------------------------- // last - return a pointer to the last demon in the list // (and the current link is set to the last link) // Return zero if the list is empty. aipDemon * aipDemonItr::last () { m_current = last_link(); return ( m_current ? m_current->demon() : 0); } //---------------------------------------------------------------------- // next - return a pointer to the next demon in the list // (and the link to this demon becomes the current link) // Return zero if there is no next demon in the list. // If this is the first call to next() and first() has not been // called, it is equivalent to calling first(). aipDemon * aipDemonItr::next () { m_current = m_current ? m_current->next() : first_link(); return (m_current ? m_current->demon() : 0 ); } //---------------------------------------------------------------------- // prev - return a pointer to the previous demon in the list // (and the link to this demon becomes the current link) // Return zero if there is no previous demon in the list. // If this is the first call to prev() and last() has not been // called, it is equivalent to calling last(). aipDemon * aipDemonItr::prev () { m_current = m_current ? m_current->prev() : last_link(); return (m_current ? m_current->demon() : 0 ); } //---------------------------------------------------------------------- // find - return a pointer to the next demon in the list // that match the passed keys // (and the link to this demon becomes the current link) // Note that find() starts the search from the current link; // if there is no current-link (ex. this is the first call to find() // and first() has not been called), first() will be called, // and the search begins at the start of the list. // Return zero if a matching demon is not found. aipDemon * aipDemonItr::find (long akey1, long akey2, long akey3, long akey4) { if (!m_current) m_current = first_link(); while (m_current) { if ( keys_match (akey1, akey2, akey3, akey4) ) break; m_current = m_current->next(); } return (m_current ? m_current->demon() : 0 ); } //---------------------------------------------------------------------- // keys_match - return true if the keys of the current demon // match the passed keys. // Return zero if there is no current demon or if the demon // has fewer keys than the passed keys. int aipDemonItr::keys_match (long akey1, long akey2, long akey3, long akey4) { if (!m_current) return 0; if (m_current->demon()->num_keys() < 1) return 0; if (m_current->demon()->key1() != akey1) return 0; if (akey2 > -99999999) { if (m_current->demon()->num_keys() < 2) return 0; if (m_current->demon()->key2() != akey2) return 0; } if (akey3 > -99999999) { if (m_current->demon()->num_keys() < 3) return 0; if (m_current->demon()->key3() != akey3) return 0; } if (akey4 > -99999999) { if (m_current->demon()->num_keys() < 4) return 0; if (m_current->demon()->key4() != akey4) return 0; } return 1; } //====================================================================== // License // // Permission is hereby granted, free of charge, to any // person obtaining a copy of this software and associated // documentation files (the "Software"), to deal in the Software // without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to // whom the Software is furnished to do so, subject to the // following conditions: // // The copyright notice and this license shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. // // //********************************************************************** aipPandemonium.h0000755000076400007640000002133411063254442013164 0ustar brianbrian//********************************************************************** // aipPandemonim.h - Pandemonium // // Provides: aipPandemonium, aipDemon, aipDemonItr // (plus aipDemonLink) // // A pandemonium is a set of demons. // // A pandemonium of demons is an AI pattern in its own right, // but it is also very useful as a general purpose container. // // aipPandemonium is implemented as a doubly-linked-list of // aipDemonLink, which point to aipDdemon. This implementation // is hidden from every class except aipDemonItr (a demon iterator). // // Pandmoniums and Demons MUST be created with new. // // aipDemon is a base class - it is designed to be subclassed. // // Demons may be shared in and between pandemoniums. These classes // ensure that shared demons are deleted only once upon destruction. // // Pandemoniums may be ordered. If subclasses of aipDemon override // the num_key(), key1() and optionally key(2), key(3), key(4) // functions, aipPandemonium::add() will insert demons in order. // // A pandemonium of ordered demons may be set to be distinct // with respect to keys. If a subclass of aipPandemonium overrides // is_distinct() to return 1, add() will not add a deomon if all // the keys match. // // See www.aipatterns.org (or www.agt.net/public/bmarshal/aipatterns) // for information on the Pandemonium AI patterns. // // Copyright (c) 2005, 2008 Brian Marshall // // See the license at end of this file. // // Developers/Contributers: // [BRM] Brian Marshall - Calgary - bmarshal@agt.net // // 08/01/28 [BRM] removed delete_demon(); changed ownership scheme; // pandemoniums can be ordered and distinct // added last(), prev(), find() // 05/11/09 [BRM] pandemonium can now add demon without owning it // 05/11/01 [BRM] made pandemonium a queue (rather than a stack) // 05/08/17 [BRM] Development begins. // //---------------------------------------------------------------------- #ifndef aipPandemonium_h_guard #define aipPandemonium_h_guard #include "aipBase.h" //---------------------------------------------------------------------- // Classes. Sub-classing is shown by indentation. // class aipBase; ( in aipBase.h ) class aipPandemonium; // a set of demons class aipDemon; // base-class for demons. class aipDemonLink; // used internally for linked-list. class aipDemonItr; // demon iterator //====================================================================== // aipPandemonium - a set of demons // // When a demon is added to a pandemonim, the pandemonium may take // ownership of the demon; upon destruction, a pandemonium will // delete the demons it owns. // // Subclasses can override is_distinct() to return 1 to make // the pandemonium distinct with respect to the demon-keys - // aipPandemonium::add() will delete rather than add a demon. // // Pandmoniums and Demons and MUST be created with new. class aipPandemonium : public aipBase { aipDemonLink * m_first; // First demon in a linked-list. aipDemonLink * m_last; // Last demon in a linked-list. long m_num_demon; int m_is_distinct; public: aipPandemonium (); virtual ~aipPandemonium (); void set_is_distinct () { m_is_distinct = 1; } int is_distinct () const { return m_is_distinct; } aipDemonLink * first_link () const { return m_first; } // for itr aipDemonLink * last_link () const { return m_last; } void add (aipDemon *demon); long num() const { return m_num_demon; } virtual void take_msg (aipMsg *m); }; //====================================================================== // aipDemon - Base class for demons (items in a aipPandemonium). // // A pandemonium may take ownership of demons that are added to it, // and will delete owned demons upon destruction. Subclasses of a // pandemonium may provide functions to create new demons of the // appropriate type. Demons MUST be created with new. // // Subclasses can override num_keys(), key1() and optionally // key2(), key3() and key4() to make the pandemonium ordered - to // make aipPandemonium::add() insert the demons in order. class aipDemon : public aipBase { int m_is_owned; public: aipDemon () : aipBase() { m_is_owned = 0; } virtual ~aipDemon () {} virtual long num_keys (void) const { return 0; } virtual long key1 (void) const { return 0; } virtual long key2 (void) const { return 0; } virtual long key3 (void) const { return 0; } virtual long key4 (void) const { return 0; } void set_is_owned () { m_is_owned = 1; } int is_owned () const { return m_is_owned; } virtual void take_msg (aipMsg *m) {} }; //====================================================================== // aipDemonLink - Link to a demon in a doubly-linked-list. // // A demon-link may own the demon to which it points; if it does, // it will delete the demon upon destruction. // // Having public set_next() and set_prev() functions would seem to // be asking for trouble, but in fact, only the aipIterator can // acquire a pointer to an aipDemonLink object. class aipDemonLink { aipDemon * m_demon; aipDemonLink * m_prev; // Prev demon in a list or zero. aipDemonLink * m_next; // Next demon in a list or zero. int m_is_owner; // for destruction purposes public: aipDemonLink (aipDemon *demon, aipDemonLink *prev, aipDemonLink *next); ~aipDemonLink (); aipDemon * demon () const { return m_demon; } aipDemonLink * prev () const { return m_prev; } aipDemonLink * next () const { return m_next; } void set_prev (aipDemonLink *x) { m_prev = x; } void set_next (aipDemonLink *x) { m_next = x; } int less_than (aipDemonLink *y); int is_equal_to (aipDemonLink *y); }; //====================================================================== // aipDemonItr - Pandemonium Iterator - to iterate through the demons. class aipDemonItr { aipPandemonium * m_pandemonium; aipDemonLink * m_current; protected: aipDemonLink * first_link () const { return m_pandemonium ? m_pandemonium->first_link() : 0; } aipDemonLink * last_link () const { return m_pandemonium ? m_pandemonium->last_link() : 0; } aipPandemonium * pandemonium () const { return m_pandemonium; } aipDemonLink * current () const { return m_current; } void set_demon_itr ( aipPandemonium *pan, aipDemonLink *cur) { m_pandemonium = pan; m_current = cur; } int keys_match (long akey1, long akey2, long akey3, long key4); public: aipDemonItr () { set_demon_itr (0, 0); } aipDemonItr (aipPandemonium *p) { set_demon_itr (p, 0); } aipDemonItr (const aipDemonItr& x) { set_demon_itr (x.pandemonium(), x.current()); } virtual ~aipDemonItr () {} aipDemonItr& operator = (const aipDemonItr& x) { set_demon_itr (x.pandemonium(), x.current()); return *this; } aipDemon * first (); // returns zero if the list is empty aipDemon * last (); // returns zero if the list is empty aipDemon * next (); // Returns zero if at last aipDemon * prev (); // Returns zero if at first // Returns zero if not found aipDemon * find (long akey1, long akey2 =-999999999, long akey3 =-999999999, long akey4 =-999999999); }; //====================================================================== #endif //====================================================================== // License // // Permission is hereby granted, free of charge, to any // person obtaining a copy of this software and associated // documentation files (the "Software"), to deal in the Software // without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to // whom the Software is furnished to do so, subject to the // following conditions: // // The copyright notice and this license shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. // //********************************************************************** aipProblem.cpp0000644000076400007640000000557211063254442012646 0ustar brianbrian//********************************************************************** // aipProblem.cpp - function bodies for aipProblem.h // // Copyright (c) 1999, 2005 Brian Marshall // // See the license at end of this file. // // Developers/Contributers: // [BRM] Brian Marshall - Calgary - bmarshal@agt.net // // 05/11/19 [BRM] problem take_msg() calls pandemonium take_msg() // 05/09/26 [BRM] development began // //---------------------------------------------------------------------- #include "aipProblem.h" //====================================================================== // aipProblem // //---------------------------------------------------------------------- // Constructor aipProblem::aipProblem () { m_decisions = new aipPandemonium; } //---------------------------------------------------------------------- // Destructor aipProblem::~aipProblem () { if (m_decisions) delete m_decisions; } //---------------------------------------------------------------------- // decision_iterator - return an iterator to the decisions of this problem. aipDecisionItr aipProblem::decision_iterator() const { aipDecisionItr i(m_decisions); return i; } //---------------------------------------------------------------------- // take_msg - take a message and distribute to decisions // // Overriding functions gemerally must call this function. void aipProblem::take_msg (aipMsg *m) { m_decisions->take_msg(m); } //====================================================================== // aipDecisionItr - Decision iterator // // All function bodies are in the header file // //====================================================================== // License // // Permission is hereby granted, free of charge, to any // person obtaining a copy of this software and associated // documentation files (the "Software"), to deal in the Software // without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to // whom the Software is furnished to do so, subject to the // following conditions: // // The copyright notice and this license shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. // // //********************************************************************** aipProblem.h0000755000076400007640000001061611063254442012311 0ustar brianbrian//********************************************************************** // aipProblem.h - Problem - a set of problems for which // decisions have to be chosen. // // Provides: aipProblem aipDecisionItr // // An aipProblem problem: // - has a set of decisions // - takes a series of messages, which it passes to its decisions // - has a solve() function choose options for he decisions // // A pandemonium is a set of objects. // aipProblem is a set of aipDecision. // // Copyright (c) 2005, 2008 Brian Marshall // // See the license at end of this file. // // Developers/Contributers: // [BRM] Brian Marshall - Calgary - bmarshal@agt.net // // 08/01/01 [BRM] removed delete_decision() // 05/09/20 [BRM] Development begins // //---------------------------------------------------------------------- #ifndef aipProblem_h_guard #define aipProblem_h_guard #include "aipDecision.h" //---------------------------------------------------------------------- // Classes. Sub-classing is shown by indentation. // class aipG is a typedef for aipGoodness // Hope is a compound emotion composed of Fear, Greed and Curiosity // class aipBase; ( in aipBase.h ) class aipProblem; // set of decisions and solve() // class aipDemonItr; ( in aipPandemonium.h ) class aipDecisionItr; // decision iterator //====================================================================== // aipProblem // // The heart of a problem is: // - a set of decisions // - a solve() function choose options for each decision // // solve() is a pure virtual function in this class. // Applications will want to use a subclass that knows how to // solve itself, a class that provides solve(). class aipProblem : public aipBase { aipPandemonium * m_decisions; // a set of decisions protected: aipPandemonium * decision_pandemonium() const { // for iterators return m_decisions; } public: aipProblem (); virtual ~aipProblem (); void add_decision (aipDecision *x) { m_decisions->add(x); } aipDecisionItr decision_iterator() const; virtual void take_msg (aipMsg *m); virtual aipG g() { return aipNeutral; } long num_decisions() const { return m_decisions->num(); } virtual aipG solve() =0; // pure virtual function }; //====================================================================== // aipDecisionItr - Iterator for decisions in a problem class aipDecisionItr : public aipDemonItr { public: aipDecisionItr () : aipDemonItr() {} aipDecisionItr (aipPandemonium *p) { set_demon_itr (p,0); } aipDecisionItr (const aipDecisionItr& x) { set_demon_itr (x.pandemonium(), x.current()); } virtual ~aipDecisionItr () {} aipDecisionItr& operator = (const aipDecisionItr& x) { set_demon_itr (x.pandemonium(), x.current()); return *this; } aipDecision * first() { return (aipDecision*)aipDemonItr::first(); } aipDecision * next() { return (aipDecision*)aipDemonItr::next(); } }; //====================================================================== #endif //====================================================================== // License // // Permission is hereby granted, free of charge, to any // person obtaining a copy of this software and associated // documentation files (the "Software"), to deal in the Software // without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to // whom the Software is furnished to do so, subject to the // following conditions: // // The copyright notice and this license shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. // //********************************************************************** aipTime.cpp0000644000076400007640000005557711063254442012156 0ustar brianbrian//********************************************************************** // aipTime.cpp - function bodies for aipTime.h // // Copyright (c) 2008 Brian Marshall // // See the license at end of this file. // // Developers/Contributers: // [BRM] Brian Marshall - Calgary - bmarshal@agt.net // // 08/07/09 [BRM] fixed a few bugs, improved the structure // 08/02/03 [BRM] began development // //---------------------------------------------------------------------- #include "aipTime.h" #include #include // for debugging... // #include // using namespace std; //====================================================================== // Global Functions // //---------------------------------------------------------------------- // set a string from days-since-Sunday - ex: "Mon" from 1 const char * aip_str_day (long imon) { if (imon < 0 || imon > 6) return aip_Empty_Str; return aip_Day_Abbrv[imon-1]; } //---------------------------------------------------------------------- // return the month-number: 1 for "Jan", 2 for "feb", etc. // or return zero if x is not valid long aip_i_month (const char* x) { if (x[0] == 'J' || x[0] == 'j') { if (x[1] == 'A' || x[1] == 'a') { if (x[2] == 'N' || x[2] == 'n') return 1; } else { if (x[1] == 'U' || x[1] == 'u') { if (x[2] == 'N' || x[2] == 'n') return 6; if (x[2] == 'L' || x[2] == 'l') return 7; } } } else if (x[0] == 'F' || x[0] == 'f') { if (x[1] == 'E' || x[1] == 'e') { if (x[2] == 'B' || x[2] == 'b') return 2; } } else if (x[0] == 'M' || x[0] == 'm') { if (x[1] == 'A' || x[1] == 'a') { if (x[2] == 'R' || x[2] == 'r') return 3; if (x[2] == 'Y' || x[2] == 'y') return 5; } } else if (x[0] == 'A' || x[0] == 'a') { if (x[1] == 'P' || x[1] == 'p') { if (x[2] == 'R' || x[2] == 'r') return 4; } else if (x[1] == 'U' || x[1] == 'u') { if (x[2] == 'G' || x[2] == 'g') return 8; } } else if (x[0] == 'S' || x[0] == 's') { if (x[1] == 'E' || x[1] == 'e') { if (x[2] == 'P' || x[2] == 'p') return 9; } } else if (x[0] == 'O' || x[0] == 'o') { if (x[1] == 'C' || x[1] == 'c') { if (x[2] == 'T' || x[2] == 't') return 10; } } else if (x[0] == 'N' || x[0] == 'n') { if (x[1] == 'O' || x[1] == 'o') { if (x[2] == 'V' || x[2] == 'v') return 11; } } else if (x[0] == 'D' || x[0] == 'd') { if (x[1] == 'E' || x[1] == 'e') { if (x[2] == 'C' || x[2] == 'c') return 12; } } return 0; } //---------------------------------------------------------------------- // set a string from a month number - ex: "Jan" from 1 const char * aip_str_month (long imon) { if (imon < 1 || imon > 12) return aip_Empty_Str; return aip_Month_Abbrv[imon-1]; } //---------------------------------------------------------------------- // Return true if yr is a leap year int aip_is_leap_year (long yr) { if (yr%4 == 0 && (yr%100 != 0 || yr%400 == 0) ) return 1; return 0; } //---------------------------------------------------------------------- // Return the number of days in the month and year in ts. long aip_days_in_month (long yr, long mo) { if (mo < 1 || mo > 12) return 0; long dim = aip_Days_In_Month[mo]; if (mo == 2 && aip_is_leap_year(yr)) dim = 29; return dim; } //====================================================================== // aipTime // //---------------------------------------------------------------------- // Construct from components aipTime::aipTime (long a_year, long a_mon, long a_day, long a_hour, long a_min, long a_sec) { set (a_year, a_mon, a_day, a_hour, a_min, a_sec); } //---------------------------------------------------------------------- // Construct from components packed into two longs (second is optional) // // in the optional second argument, seconds is optional - // 43045 is 4:30:45, 430 is 4:30 am, 1630 is 4:30 pm aipTime::aipTime (long yyyymmdd, long hhmmss) { set (yyyymmdd, hhmmss); } //---------------------------------------------------------------------- // set from components // // Note: all constructors and other set functions call this function // which attempts to prevent Daylight Savings Time from // causing problems. void aipTime::set (long a_year, long a_mon, long a_day, long a_hour, long a_min, long a_sec) { if (a_year < 30) a_year += 100; // 2-digit year after 2000 if (a_year < 1000) a_year += 1900; // convert 2-digit year to 4-digit if (a_year < 1800 || a_year > 2300 || a_mon < 1 || a_mon > 12 || a_day < 1 || a_day > 31 || a_hour < 0 || a_hour > 23 || a_min < 0 || a_min > 59 || a_sec < 0 || a_sec > 59) return; struct tm ts; // broken-down time ts.tm_sec = a_sec; ts.tm_min = a_min; ts.tm_hour = a_hour; ts.tm_mday = a_day; ts.tm_mon = a_mon - 1; ts.tm_year = a_year - 1900; set_daylight_savings_time (&ts); m_time = mktime (&ts); } //---------------------------------------------------------------------- // set from components packed into two longs (second is optional) // // 430 is 4:30 am, 1630 is 4:30 pm void aipTime::set (long yyyymmdd, long hhmm) { long xyear, xmon, xday, xhour, xmin; long x = yyyymmdd; xyear = x / 10000; x -= (xyear * 10000); if (xyear < 30) xyear += 100; // 2-digit year after 2000 if (xyear < 200) xyear += 1900; // convert 2-digit year to 4-digit xmon = x / 100; x -= (xmon * 100); xday = x; x = hhmm; if (x > 0) { xhour = x / 100; x -= (xhour * 100); xmin = x; } else { xhour = xmin = 0; } set (xyear, xmon, xday, xhour, xmin, 0); } //---------------------------------------------------------------------- // set from another instance of aipTime void aipTime::set (const aipTime& x) { m_time = x.tt_time(); } //---------------------------------------------------------------------- // set from a string like: "17 Feb 1998" or "17/02/2008 16:30:45" // or insert-format: "2008-02-17" // // seconds is optional; hours, minutes, seconds are optional void aipTime::set (const char *x) { long xyear, xmon, xday, xhour, xmin, xsec, z; char buf[31]; xyear = xmon = xday = 0; xhour = xmin = xsec = 0; while (x && *x) { x = aip_get_str(x, 30, buf); if (x) { if (buf[0] >= '0' && buf[0] <= '9') { z = atol(buf); if (z <= 0) { x=0; break; } if (z > 31) { // must be year if (xyear) { x=0; break; } // already have a year xyear = z; } else if (!xday && !xmon && !xyear) { // first component xday = z; // assume dmy } else if ( xday && !xmon && !xyear) { // month or year xmon = z; // if day is first, assume dmy } else if ( xmon && !xday && !xyear) { // day or year xday = z; // if month is first, assume mdy } else if ( xyear && !xday && !xmon) { // day or month xmon = z; // if year is first, assume ymd } else if ( xday && xmon && !xyear) { xyear = z; } else if ( xday && xyear && !xmon) { xmon = z; } else if ( xmon && xyear && !xday) { xday = z; } else if (!xhour) { xhour = z; } else if (!xmin) { xmin = z; } else if (!xsec) { xsec = z; } else { x=0; break; // have all numeric components } } else { // am or pm or month for (int i=0; i<3; i++) { if (buf[i] >= 'a' && buf[i] <= 'z') buf[i] -= ('a' - 'A'); } if ( (buf[0] == 'A' || buf[0] == 'P') && buf[1] == 'M') { if (!xmin) { x=0; break; } if (buf[0] == 'P' && xhour <= 12) xhour += 12; break; // nothing follows am or pm } // end of block for when string is am or pm if (xmon) { x=0; break; } // already have a month xmon = aip_i_month(buf); if (xmon < 1 || xmon > 12) { x=0; break; } } // end of block for a string of letters } // end of block for successfully getting a string } // end of loop through date/time components if (x) { set (xyear, xmon, xday, xhour, xmin, xsec); } else { set(0,0,0,0,0,0); } } //---------------------------------------------------------------------- // compare functions int aipTime::eq (const aipTime& x) const { if ( difftime (m_time,x.tt_time()) == 0.0 ) return 1; return 0; } int aipTime::ne (const aipTime& x) const { if ( difftime (m_time,x.tt_time()) != 0.0 ) return 1; return 0; } int aipTime::lt (const aipTime& x) const { if ( difftime(m_time,x.tt_time()) < 0.0 ) return 1; return 0; } int aipTime::gt (const aipTime& x) const { if ( difftime(m_time,x.tt_time()) > 0.0 ) return 1; return 0; } int aipTime::le (const aipTime& x) const { if ( difftime(m_time,x.tt_time()) <= 0.0 ) return 1; return 0; } int aipTime::ge (const aipTime& x) const { if ( difftime(m_time,x.tt_time()) >= 0.0 ) return 1; return 0; } //---------------------------------------------------------------------- // Set ts->tm_isdst from the other components void aipTime::set_daylight_savings_time (struct tm *ts) const { long start_mon, start_day, end_mon, end_day; // Jan = 1 if (ts->tm_year <= 105) { // 2005 and earlier start_mon = 4; start_day = 3; end_mon = 10; end_day = 30; } else if (ts->tm_year == 106) { // 2006 start_mon = 4; start_day = 2; end_mon = 10; end_day = 29; } else if (ts->tm_year == 107) { // 2007 start_mon = 3; start_day = 11; end_mon = 11; end_day = 4; } else if (ts->tm_year == 108) { // 2008 start_mon = 3; start_day = 9; end_mon = 11; end_day = 2; } else if (ts->tm_year == 109) { // 2009 start_mon = 3; start_day = 8; end_mon = 11; end_day = 1; } else if (ts->tm_year >= 110) { // 2010 and later start_mon = 3; start_day = 14; end_mon = 11; end_day = 7; } long mon = ts->tm_mon + 1; long day = ts->tm_mday; long hr = ts->tm_hour; if ( ( (mon > start_mon) || (mon == start_mon && day > start_day) || (mon == start_mon && day == start_day && hr >= 2) ) && ( (mon < end_mon) || (mon == end_mon && day < end_day) || (mon == end_mon && day == end_day && hr < 2) ) ) { ts->tm_isdst = 1; } else { ts->tm_isdst = 0; } } //---------------------------------------------------------------------- // Add seconds to this date void aipTime::add_seconds (long num) { if (!is_valid()) return; struct tm ts = *(localtime(&m_time)); ts.tm_sec += num; m_time = mktime (&ts); // mktime will renormalize the date } //---------------------------------------------------------------------- // Add hours to this date void aipTime::add_hours (long num) { if (!is_valid()) return; struct tm ts = *(localtime(&m_time)); ts.tm_hour += num; m_time = mktime (&ts); // mktime will renormalize the date } //---------------------------------------------------------------------- // Add days to this date void aipTime::add_days (long num) { if (!is_valid()) return; struct tm ts = *(localtime(&m_time)); ts.tm_mday += num; m_time = mktime (&ts); // mktime will renormalize the date } //---------------------------------------------------------------------- // return the number of days since Sunday long aipTime::days_since_sunday () const { struct tm *ts = localtime (&m_time); return ts->tm_wday; } //---------------------------------------------------------------------- // return the number of days since January 1 long aipTime::days_since_jan1 () const { struct tm *ts = localtime (&m_time); return ts->tm_yday; } //---------------------------------------------------------------------- // return the number of seconds since x long aipTime::seconds_since (const aipTime& x) const { long nsec = (long)difftime (m_time, x.tt_time()); return nsec; } //---------------------------------------------------------------------- // return the number of minutes since x long aipTime::minutes_since (const aipTime& x) const { return (seconds_since(x) / 60); } //---------------------------------------------------------------------- // return the number of hours since x long aipTime::hours_since (const aipTime& x) const { return (seconds_since(x) / (60 * 60)); } //---------------------------------------------------------------------- // return the number of days since x long aipTime::days_since (const aipTime& x) const { return (seconds_since(x) / (60 * 60 * 24)); } //---------------------------------------------------------------------- // set the whole days, hours, minutes and seconds since x void aipTime::time_since (const aipTime& x, long *pdays, long *phrs, long *pmins, long *psecs) const { long nsec = seconds_since(x); const long sec_per_min = 60; const long sec_per_hr = 60 * 60; const long sec_per_day = 60 * 60 * 24; long days = 0; if (nsec >= sec_per_day) { days = nsec / sec_per_day; nsec -= (days * sec_per_day); } long hrs = 0; if (nsec >= sec_per_hr) { hrs = nsec / sec_per_hr; nsec -= (hrs * sec_per_hr); } long mins = 0; if (nsec >= sec_per_min) { mins = nsec / sec_per_min; nsec -= (mins * sec_per_min); } if (pdays) *pdays = days; if (phrs) *phrs = hrs; if (pmins) *pmins = mins; if (psecs) *psecs = nsec; } //---------------------------------------------------------------------- // return the difference in hours after x long aipTime::hours_after (const aipTime& x) const { long yr, mon, day, hr; get (&yr, &mon, &day, &hr); aipTime a(yr, mon, day, hr, 0, 0); x.get (&yr, &mon, &day, &hr); aipTime b(yr, mon, day, hr, 0, 0); return a.hours_since(b); } //---------------------------------------------------------------------- // return the difference in days after x long aipTime::days_after (const aipTime& x) const { long yr, mon, day; get (&yr, &mon, &day); aipTime a(yr, mon, day, 0, 0, 0); x.get (&yr, &mon, &day); aipTime b(yr, mon, day, 0, 0, 0); return a.days_since(b); } //---------------------------------------------------------------------- // get components for non-null pointers - return zero on failure int aipTime::get (long *a_year, long *a_mon, long *a_day, long *a_hour, long *a_min, long *a_sec, char *s_mon, char *s_weekday) const { if (!is_valid()) { if (a_year) *a_year = 0; if (a_mon) *a_mon = 0; if (a_day) *a_day = 0; if (a_hour) *a_hour = 0; if (a_min) *a_min = 0; if (a_sec) *a_sec = 0; if (s_mon) s_mon[0] = '\0'; if (s_weekday) s_weekday[0] = '\0'; return 0; } struct tm *ts = localtime (&m_time); if (a_year) *a_year = ts->tm_year + 1900; if (a_mon) *a_mon = ts->tm_mon + 1; if (a_day) *a_day = ts->tm_mday; if (a_hour) *a_hour = ts->tm_hour; if (a_min) *a_min = ts->tm_min; if (a_sec) *a_sec = ts->tm_sec; if (s_mon) aip_strncpy(s_mon, aip_Month_Abbrv[ts->tm_mon], 3); if (s_weekday) aip_strncpy(s_weekday, aip_Day_Abbrv[ts->tm_wday], 3); return 1; } //---------------------------------------------------------------------- // write the date to dstr - ex: "17/Jan/2008" void aipTime::dd_mon_yyyy (char *dstr) const { if (!dstr) return; long dyear, dmon, dday; if ( ! get(&dyear,&dmon,&dday) ) { *dstr='\0'; return; } const char *smon = aip_str_month(dmon); sprintf (dstr, "%02ld/%s/%ld", dday, smon, dyear); } //---------------------------------------------------------------------- // write the date to dstr - ex: "17/Jan/2008_16:30" void aipTime::dd_mon_yyyy_hh_mm (char *dstr) const { if (!dstr) return; long dyear, dmon, dday, dhour, dmin; if ( ! get(&dyear,&dmon,&dday,&dhour,&dmin) ) { *dstr='\0'; return; } const char *smon = aip_str_month(dmon); sprintf (dstr, "%02ld/%s/%ld_%02ld:%02ld", dday, smon, dyear, dhour, dmin); } //---------------------------------------------------------------------- // write the date to dstr - ex: "01/17/08" void aipTime::dd_mm_yy (char *dstr) const { if (!dstr) return; long dyear, dmon, dday; if ( ! get(&dyear,&dmon,&dday) ) { *dstr='\0'; return; } sprintf (dstr, "%02ld/%02ld/%02ld", dday, dmon, dyear%100); } //---------------------------------------------------------------------- // write the date to dstr - ex: "01/17/2008" void aipTime::dd_mm_yyyy (char *dstr) const { if (!dstr) return; long dyear, dmon, dday; if ( ! get(&dyear,&dmon,&dday) ) { *dstr='\0'; return; } sprintf (dstr, "%02ld/%02ld/%4ld", dday, dmon, dyear); } //---------------------------------------------------------------------- // write the date to dstr (insert-format) - ex: "2008-01-17" void aipTime::yyyy_mm_dd (char *dstr) const { if (!dstr) return; long dyear, dmon, dday; if ( ! get(&dyear,&dmon,&dday) ) { *dstr='\0'; return; } sprintf (dstr, "%04ld-%02ld-%2ld", dyear, dmon, dday); } //---------------------------------------------------------------------- // return the date as a long long aipTime::yyyymmdd () const { long dyear, dmon, dday; if ( ! get(&dyear,&dmon,&dday) ) return 0; return ( dyear*10000 + dmon*100 + dday ); } //---------------------------------------------------------------------- // return the hhmm as a long long aipTime::hhmm () const { long dyear, dmon, dday, dhour, dmin; if ( ! get(&dyear,&dmon,&dday,&dhour,&dmin) ) return 0; return ( dhour*100 + dmin ); } //====================================================================== // aipTimeOfDay // //---------------------------------------------------------------------- // set from components void aipTimeOfDay::set (long a_hour, long a_min) { m_hour = a_hour; m_minute = a_min; if (m_hour < 0 || m_hour > 23 || m_minute < 0 || m_minute > 59) reset(); } //---------------------------------------------------------------------- // set from another instance of aipTimeOfDay void aipTimeOfDay::set (const aipTimeOfDay& x) { set (x.hour(), x.minute()); if (!is_valid()) reset(); } //---------------------------------------------------------------------- // set from a string - ex: "16:30" or "4:30 pm" void aipTimeOfDay::set (const char *x) { long hr, min; char ampm[2]; // only care whether first char is 'p' or 'P' if (x && *x) x = aip_get_val(x, 2, &hr); if (x && *x) x = aip_get_val(x, 2, &min); if (x && *x) x = aip_get_str(x, 1, ampm); if (x) { if (*ampm == 'p' || *ampm == 'P') { // pm if (hr <= 12) hr += 12; // convert to 24 hour clock } } if (hr == 24) hr = 0; set (hr, min); } //---------------------------------------------------------------------- // compare functions int aipTimeOfDay::eq (const aipTimeOfDay& x) const { return m_minute == x.minute() && m_hour == x.hour(); } int aipTimeOfDay::ne (const aipTimeOfDay& x) const { return !eq(x); } int aipTimeOfDay::lt (const aipTimeOfDay& x) const { if (m_hour < x.hour()) return 1; if (m_hour == x.hour()) { if ( m_minute < x.minute()) return 1; } return 0; } int aipTimeOfDay::gt (const aipTimeOfDay& x) const { return !lt(x) && !eq(x); } int aipTimeOfDay::le (const aipTimeOfDay& x) const { return lt(x) || eq(x); } int aipTimeOfDay::ge (const aipTimeOfDay& x) const { return !lt(x); } //---------------------------------------------------------------------- // add hours to this time-of-day void aipTimeOfDay::add_hours (long num) { m_hour += num; if (m_hour >= 24) m_hour %= 24; } //---------------------------------------------------------------------- // add minutes to this time-of-day void aipTimeOfDay::add_minutes (long num) { m_minute += num; if (m_minute >= 60) { long nhour = m_minute / 60; m_minute %= 60; if (nhour > 0) add_hours(nhour); } } //---------------------------------------------------------------------- // return the number of hours since x long aipTimeOfDay::hours_since (const aipTimeOfDay& x) { long nhour = m_hour - x.hour(); return nhour; } //---------------------------------------------------------------------- // return the number of minutes since x long aipTimeOfDay::minutes_since (const aipTimeOfDay& x) { long nmin = (m_minute - x.minute()) + (m_hour - x.hour()) * 60; return nmin; } //---------------------------------------------------------------------- // write the time-of-day to the pointer on a 24 hour clock // // ex: "08:30" "16:45" writes 6 chars (with null) void aipTimeOfDay::hh_mm (char *dstr) const { if (!dstr) return; sprintf (dstr, "%02d:%02d", m_hour, m_minute); } //---------------------------------------------------------------------- // write the time-of-day with seconds to the pointer on a 24 hour clock // // ex: "08:30:00" "16:45:00 writes 9 chars (with null) void aipTimeOfDay::hh_mm_ss (char *dstr) const { if (!dstr) return; sprintf (dstr, "%02d:%02d:00", m_hour, m_minute); } //---------------------------------------------------------------------- // write the time-of-day to the pointer on a 12 hour clock // // ex: "08:30 am" "04:45 pm" writes 9 chars (with null) void aipTimeOfDay::hh_mm_12 (char *dstr) const { if (!dstr) return; int hr = m_hour; char ampm[3]; if (hr > 12) { strcpy (ampm, "pm"); hr -= 12; } else { strcpy (ampm, "am"); } sprintf (dstr, "%02d:%02d %s", hr, m_minute, ampm); } //---------------------------------------------------------------------- //====================================================================== // License // // Permission is hereby granted, free of charge, to any // person obtaining a copy of this software and associated // documentation files (the "Software"), to deal in the Software // without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to // whom the Software is furnished to do so, subject to the // following conditions: // // The copyright notice and this license shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. // // //********************************************************************** aipTime.h0000755000076400007640000002040411063254442011603 0ustar brianbrian//********************************************************************** // aipTime.h - times and dates // // Copyright (c) 2008 Brian Marshall // // See the license at end of this file. // // With some compilers, when an instance of aipTime is created, the // hour will be off if the date is not in Daylight Savings Time (DST) // and the current time is in DST or vice versa. // // Developers/Contributers: // [BRM] Brian Marshall - Calgary - bmarshal@agt.net // // 08/08/02 [BRM] added day-abbreviation to aipTime.get() // 08/07/09 [BRM] fixed a few bugs, improved the structure // 08/02/03 [BRM] began development // //---------------------------------------------------------------------- #ifndef aipTime_h_guard #define aipTime_h_guard #include // just for the functions and constants... #include "aipBase.h" //---------------------------------------------------------------------- // Classes class aipTime; // time and date class aipTimeOfDay; // hours and minutes //---------------------------------------------------------------------- // Constants static const char * aip_Day_Abbrv[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; static const char * aip_Month_Abbrv[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; static const int aip_Days_In_Month[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; // Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec //---------------------------------------------------------------------- // Global Functions const char * aip_str_day (long iday); // return "Sun" from 0 long aip_i_month (const char* x); // return 1 from jan const char * aip_str_month (long imon); // return "Jan" from 1 int aip_is_leap_year (long yr); long aip_days_in_month (long yr, long mo); //====================================================================== // aipTime - date and time // // aipTime objects are small and can be copied around. // Comparing and determining differences is fast. // The following are more involved, but are generally used // only in setting-up the problem: // - creating and setting instances // - adding seconds or days to an instance class aipTime { time_t m_time; protected: void set (long year, long mon, long day, long hour =0, long min =0, long sec =0); void set (long yyyymmdd, long hhmmss =0); void set (const aipTime& x); // delim is any non-alphanum; hh, mm, ss are optional void set (const char* x); // "17 Feb 1998" or "17/02/2008 16:30" // "2008-02-17" ie. insert format int eq (const aipTime& x) const; int ne (const aipTime& x) const; int lt (const aipTime& x) const; int gt (const aipTime& x) const; int le (const aipTime& x) const; int ge (const aipTime& x) const; public: aipTime () {} aipTime (long year, long mon, long day, long hour =0, long min =0, long sec =0); aipTime (long yyyymmdd, long hhmmss =0); aipTime (const aipTime& x) { set(x); } aipTime (const char* x) { set(x); } // ex: "12 Feb 2008 16:30" ~aipTime () {} aipTime& operator = (const aipTime& x) { set(x); return *this; } int is_valid () const { return m_time != (time_t)(-1); } void set_daylight_savings_time (struct tm *ts) const; void add_seconds (long num); void add_hours (long num); void add_days (long num); time_t tt_time () const { return m_time; } long days_since_sunday () const; long days_since_jan1 () const; long seconds_since (const aipTime& x) const; long minutes_since (const aipTime& x) const; long hours_since (const aipTime& x) const; long days_since (const aipTime& x) const; void time_since (const aipTime& x, long *days, long *hrs, long *mins, long *secs) const; long hours_after (const aipTime& x) const; // num different long days_after (const aipTime& x) const; // num different // ex: 2008, 7, 12, 16, 30, 0, "Jul", "Sat" int get (long *year, long *mon =0, long *day =0, long *hour =0, long *min =0, long *sec =0, char *smon =0, char *sweekday =0) const; // write date string to a pointer void dd_mon_yyyy (char *dstr) const; // "17/Jan/2008" void dd_mon_yyyy_hh_mm (char *dstr) const; // "17/Jan/2008_16:30" void dd_mm_yy (char *dstr) const; // "17/01/08" void dd_mm_yyyy (char *dstr) const; // "17/01/2008" void yyyy_mm_dd (char *dstr) const; // "2008-01-17" long yyyymmdd () const; // return date as long long hhmm () const; // return hhmm as long int operator == (const aipTime& x) const { return eq(x); } int operator != (const aipTime& x) const { return ne(x); } int operator < (const aipTime& x) const { return lt(x); } int operator > (const aipTime& x) const { return gt(x); } int operator <= (const aipTime& x) const { return le(x); } int operator >= (const aipTime& x) const { return ge(x); } }; //====================================================================== // aipTimeOfDay - hours and minutes class aipTimeOfDay { char m_hour; // 0 to 23 char m_minute; // 0 to 59 protected: void reset () { m_hour = m_minute = -1; } void set (long hour, long minute); void set (const aipTimeOfDay& x); void set (const char *x); // ex: "16:30" or "4:30 pm" int eq (const aipTimeOfDay& x) const; int ne (const aipTimeOfDay& x) const; int lt (const aipTimeOfDay& x) const; int gt (const aipTimeOfDay& x) const; int le (const aipTimeOfDay& x) const; int ge (const aipTimeOfDay& x) const; public: aipTimeOfDay (long hr, long min) { set(hr,min); } aipTimeOfDay (const aipTimeOfDay& x) { set(x); } aipTimeOfDay (const char* x) { set(x); } ~aipTimeOfDay () {} // ex: "16:30" or "4:30 pm" aipTimeOfDay& operator = (const aipTimeOfDay& x) { set(x); return *this; } void add_hours (long num); void add_minutes (long num); long hours_since (const aipTimeOfDay& x); long minutes_since (const aipTimeOfDay& x); int is_valid () const { return m_hour >= 0; } long hour () const { return (long)m_hour; } long minute () const { return (long)m_minute; } void hh_mm (char *dstr) const; // writes to dstr - "16:30" void hh_mm_ss (char *dstr) const; // writes to dstr - "16:30:00" void hh_mm_12 (char *dstr) const; // writes to dstr - "04:30 pm" int operator == (const aipTimeOfDay& x) const { return eq(x); } int operator != (const aipTimeOfDay& x) const { return ne(x); } int operator < (const aipTimeOfDay& x) const { return lt(x); } int operator > (const aipTimeOfDay& x) const { return gt(x); } int operator <= (const aipTimeOfDay& x) const { return le(x); } int operator >= (const aipTimeOfDay& x) const { return ge(x); } }; //====================================================================== #endif //====================================================================== // License // // Permission is hereby granted, free of charge, to any // person obtaining a copy of this software and associated // documentation files (the "Software"), to deal in the Software // without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to // whom the Software is furnished to do so, subject to the // following conditions: // // The copyright notice and this license shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. // //********************************************************************** LICENSE.txt0000755000076400007640000000257611063254442011677 0ustar brianbrian aiParts - Open Source License The software distributed by the aiParts Project is copyrighted by its authors and then licensed with the MIT License, and as such, it is OSI Certified Open Source Software. The MIT License Copyright (c) 2005, 2008 Brian Marshall Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. mk_samples_linux2.sh0000664000076400007640000000161711063254442014036 0ustar brianbrian# Alternative way of building sample programs on a Linux system # # This file is a guide to what aiParts software is used by # each sample program. # # Lines from this file may be copied and pasted to build only # a particular sample program. g++ -o samp_1_good samp_good.cpp aipGood.cpp g++ -o samp_2_pandemonium samp_pandemonium.cpp aipPandemonium.cpp \ aipBase.cpp aipGood.cpp g++ -o samp_3_decision samp_decision.cpp samp_deer_fear.cpp \ aipDecision.cpp aipEmotion.cpp \ aipPandemonium.cpp aipBase.cpp aipGood.cpp g++ -o samp_4_a_to_b samp_a_to_b.cpp samp_a2b.cpp \ aipHighHope.cpp \ aipProblem.cpp aipDecision.cpp \ aipEmotion.cpp aipPandemonium.cpp \ aipBase.cpp aipGood.cpp g++ -o samp_5_time samp_time.cpp aipTime.cpp aipBase.cpp mk_samples_linux.sh0000755000076400007640000000226211063254442013752 0ustar brianbrian# Create the sample programs on a Linux system echo " Setting up..." touch aiParts.a rm aiParts.a echo " Compiling aiParts software..." g++ -c aipHighHope.cpp aipProblem.cpp aipDecision.cpp \ aipEmotion.cpp aipPandemonium.cpp aipTime.cpp \ aipBase.cpp aipGood.cpp echo " Creating library..." ar rcs aiParts.a aipHighHope.o aipProblem.o aipDecision.o \ aipEmotion.o aipPandemonium.o aipTime.o \ aipBase.o aipGood.o rm aipHighHope.o aipProblem.o aipDecision.o \ aipEmotion.o aipPandemonium.o aipTime.o \ aipBase.o aipGood.o echo " Compiling and linking samp_1_good..." g++ -o samp_1_good samp_good.cpp aiParts.a echo " Compiling and linking samp_2_pandemonium..." g++ -o samp_2_pandemonium samp_pandemonium.cpp aiParts.a echo " Compiling and linking samp_3_decision..." g++ -o samp_3_decision samp_decision.cpp samp_deer_fear.cpp aiParts.a echo " Compiling and linking samp_4_a_to_b..." g++ -o samp_4_a_to_b samp_a_to_b.cpp samp_a2b.cpp aiParts.a echo " Compiling and linking samp_5_time..." g++ -o samp_5_time samp_time.cpp aiParts.a echo " Cleaning up..." rm aiParts.a echo "Done" mk_samples_windows.bat0000755000076400007640000000253011063254442014437 0ustar brianbrian:: Create the sample programs on a Windows system :: :: This script uses the Borland C++ 5.5 compiler and library; :: it will have to be modified for a different compiler and library. @echo off echo ... echo Setting up... echo ... echo "dummy" >aiParts.LIB del aiParts.LIB echo ... echo Compiling aiParts software... echo ... bcc32 -c aipHighHope.cpp aipProblem.cpp aipDecision.cpp bcc32 -c aipEmotion.cpp aipPandemonium.cpp aipTime.cpp bcc32 -c aipBase.cpp aipGood.cpp echo ... echo Creating library... echo ... tlib aiParts +aipHighHope.obj +aipProblem.obj tlib aiParts +aipDecision.obj +aipEmotion.obj tlib aiParts +aipPandemonium.obj +aipTime.obj tlib aiParts +aipBase.obj +aipGood.obj del aipHighHope.obj aipProblem.obj del aipDecision.obj aipEmotion.obj del aipPandemonium.obj aipTime.obj del aipBase.obj aipGood.obj echo ... echo Compiling and linking sample programs... echo ... bcc32 -esamp_1_good.exe samp_good.cpp aiParts.LIB bcc32 -esamp_2_pandemonium.exe samp_pandemonium.cpp aiParts.LIB bcc32 -esamp_3_decision.exe samp_decision.cpp samp_deer_fear.cpp aiParts.LIB bcc32 -esamp_4_a_to_b.exe samp_a_to_b.cpp samp_a2b.cpp aiParts.LIB bcc32 -esamp_5_time.exe samp_time.cpp aiParts.LIB echo ... echo Cleaning up... echo ... del *.obj *.tds del aiParts.BAK del aiParts.LIB echo ... echo Done README.txt0000664000076400007640000002615411063254442011547 0ustar brianbrian================================================================= aiParts README 0.9.1 Project Home Page: http://www.aiparts.org To see what is new in this release, see the file: RELEASE.txt This is Open Source software. Download it free of charge. Do whatever you want with it. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file LICENSE.txt. As an Open Source project, contributions are always welcome: software, documentation, bug reports, enhancement requests, comments and criticism. Contact: Brian Marshall at bmarshal@agt.net or +1-403-651-0584 = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = Table of Contents Introduction to aiParts Status of the Software Status of the AI Making the Sample Programs Using aiParts The High-Hope AI Technique aiParts Source Files Future Development Notes = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = Introduction to aiParts aiParts is a set of C++ classes that can be used to develop artificial intelligence in multi-decision problems. It includes classes that implement the High-Hope technique and some small sample programs. An application can assemble a problem from subclasses of the High-Hope classes. The problem class has a solve() function that will search for a good solution. The High-Hope technique is an example of machine-learning. Options have models of emotions which affect and are affected by repeated attempts to solve the problem. The chaotic interaction between emotions controls the balance between exploring the unknown and taking advantage of what has been learned. aiParts can be used: - in applications, to make decisions or solve problems - as a platform for AI research and development Examples of multi-decision problems: - navigating through streets, pipelines or networks - assigning packages to couriers - assigning people and equipment to projects - staff scheduling - scheduling steam-injection in a heavy-oil field - exploring a solution-space too large too search A "find the shortest path from A to B" sample program uses the High-Hope classes. Subclasses of the High-Hope classes can be developed for a problem-domain. aiParts includes rr_solve.h/cpp for Requirement-Resource problems - packages and couriers, projects and staff/equipment, etc. = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = Status of the Software Any C++ compiler should be able to compile this software. It is basic C++. The aipPandemonium class is used as a general container-class. aiParts 0.9.1 provides improvements in this README.txt file. aiParts 0.9.0 has many new features - see the RELEASES.txt file. These features were added during the development of staff-assignment software in the StaffWhen and rrSolve Open Source projects. The new aiParts software has had some alpha-testing. The older aiParts software has had a fair bit of alpha-testing, both with the sample programs and during the debugging and testing of the StaffWhen solver software. aiParts is ready for beta-testing. = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = Status of the AI The High-Hope AI technique appears to be work moderately well. There is much that can be done to improve it - see the last section of this file. The AI is ready for initial beta testing. The High-Hope technique is chaotically sensitive to initial conditions, parameters and the sequence of random numbers it uses for choosing equal options. A tiny change in the software or the problem can result in a different choice in a try, which may affect subsequent choices and tries. The High-Hope technique and rr_solve.h/cpp are used in solver software used by two Open Source projects: rrSolve - assign people and/or equipment to projects StaffWhen - assign staff to events The source for this solver will probably be released in late-September 2008. The project websites URLs will change. Search Google for: rrSolve and/or StaffWhen = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = Making the Sample Programs Sample scripts are provided for building the sample programs on Windows and Linux machines. They will generally have to be modified for the particular compiler (and possibly library) that you use. See the files: mk_samples_windows.bat mk_samples_linux.sh mk_samples_linux2.sh On Unix/Linux systems, you might want to change g++ (which is good for Fedora) to gcc or cc or CC or whatever command you use to run your compiler. If you want a free compiler for Windows, and you like to work from the command line, you might want to try the Borland 5.5 C++ compiler. = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = Using aiParts There are two approaches to using aiParts... The first approach is to: - Identify the most abstract layer of aiParts that is appropriate for your problem and develop a set of subclasses that specialize them for your problem. - Write a program that assembles a problem from your new classes and call the appropriate virtual function(s) - like: yourDecision.decide() or yourProblem.solve(). The second approach is to: - Identify the sample program (or other software using aiParts) that is closest to your problem, and take a copy of the source files. - Identify the classes that specialize the aiParts software for the particular problem and incrementally modify them to address your problem. The second approach is easier because you learn how the aiParts software works as you go, rather than having to learn about it right from the start. = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = The High-Hope AI Technique At the highest level, the High-Hope technique tries to solve the problem many times, remembering the best try found. At each decision, the option with the highest 'hope for success' is chosen. The success or failure of each try affects strategic aspects in relevant options. These aspects affect the hope of the options. Tries affect aspects which affects tries - it is a chaotic system. It causes one try to (usually) differ from the next). The High-Hope AI technique uses simplistic models of some emotions. The emotions have names that are misleading because the words have multiple subtle meanings. This is probably unavoidable. The hope of an option is the feeling that the option is a good choice - that it could be part of the best solution that will be found. In aiParts, Hope is a composite emotion: Fear - the desire to avoid something bad Greed - the desire for something known and good Curiosity - the desire to try something new Hope - Fear + Greed + Curiosity Why emotions? Consider deer and fear. It is easy to define a bunch of different reasons why a deer might become more or less afraid; when the fear is high enough, the deer runs away. It is much more difficult to design an algorithm to decide when a deer should run away. As a chaotic system, the High-Hope technique is extremely sensitive to the initial state. It might find a good solution to a problem in 50 or 500 or 5000 tries. Trying is how it learns about the problem. When solving a problem, it is worth experimenting with the number of tries. Changing the number of tries will change various cutoff values (ex. max tries without an improvement), and this will change the series of solutions that are found. In the A-to-B sample program, if the last parameter is a 2, you can see the fear, greed and curiosity changing as the software considers each option in each try: samp_4_a_to_b.exe A B 30 2 >samp_A_B.txt This can produce a lot of output, so it was redirected to a file. = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = aiParts Source Files Files generally depend on the files above it in this list... Low-Level Types, Base Class and Utility Classes: aipTime.h Time and date aipGood.h Goodness aipBase.h base class, messages, random numbers Classes that implement AI Patterns: aipPandemonium.h Pandemonium, demons aipEmotion.h Emotion - Fear, Greed, Curiosity, Hope Classes for Decision-Making and Problem-Solving: aipDecision.h Decisions, options aipProblem.h Problems Classes that implement AI techniques: aipHighHope.h High-Hope problem-solving Classes that specialize AI techniques: rr_solve.h Requirement-Resource frame-work Files for Sample Programs samp_time.cpp Using the date and time classes samp_good.cpp Using the goodness class samp_pandemonium.cpp Using the container class samp_decision.cpp Making decisions using an emotion samp_deer_fear.h samp_deer_fear.cpp samp_a_to_b.cpp Navigating using the High-Hope classes samp_a2b.h samp_a2b.cpp = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = Future Development Notes Developing new AI techniques (alternatives to the High-Hope technique) Developing abstraction-layers for an AI technique (just as the classes in rr_solve.h/cpp specialize the High-Hope technique for Requirement-Resource problems) aipTime.h/cpp: add seconds to class aipTimeOfDay There is much that can be done to improve the High-Hope AI: - improve the choice of which decision to decide next - improve the behavior in the exploring stage - maybe add an initial feeling-out stage - improve the refining stage - analyze the output for sequences and cycles - improve how the AI gets out of ruts - further improvement of the way hope works - and many other ways of refining the AI Handling Failure Maybe: If a try fails, decisions and options later in the solution get more fear than those earlier in the solution. Good But Different Recognize when a good result is significantly different from other good results found. Handling Good Improvements After a recent try that was a good-improvement, increase the curiosity of acceptable-looking options that look like they would have a significant effect on the solution. A "good-improvement" is a try that is better than the current best-so-far try or acceptably good and significantly different than the best-so-far try. Problem-Hope The problem itself could have a hope which affects how option hopes are determined. The idea is that if problem-curiosity is high, the aspects that control curiosity in options would be given more weight. The same principle would apply to fear and greed. This would allow for higher curiosity (and more exploration) early in the problem-solving, and lower curiosity (and more optimization) later in the problem-solving. Finding Best Path When on New Path If following a new path and come to the best-so-far path, keep following the bsf path. This could be done with problem-hope; when a node in the bsf solution is chosen, drop problem-curiosity to (near) aipNeurtal. Implement Ways A 'way' is a set of options chosen for a set of decisions. Maybe, in a try, force some ways while exploring other ways. ================================================================= RELEASE.txt0000664000076400007640000001346111063254442011667 0ustar brianbrian================================================================= RELEASE.txt - aiParts - Releases and Contribution Credits aiParts can be downloaded from: www.aiparts.org/download.htm Source files are viewable at: www.aiparts.org/source.htm Initial Release: aiParts 0.8.0 2005-Oct-10 See the end of this file for Old Credits and old releases. For more information about the project and software, see the README.txt file. = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = Developers and Contributors [BRM] Brian Marshall - Calgary - bmarshal@agt.net = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = Release History Release Date Status ------- ------------ ---------- 0.9.1 2008-Sep-14 Beta README.txt was improved. 0.9.0 2008-Sep-01 Beta aipTime.h/cpp new: date/time and time-of-day classes aipBase.h/cpp new random number generator; simplified logging new functions to get values and sub-strings from strings aipImportance.h new: base class for importances aipPandemonium.h/cpp pandemoniums can be ordered and optionally distinct by keys new iterator functions: last(), prev(), find() aipEmotion.h/cpp removed delete_aspect(), delete_emotion() aipDecision.h/cpp removed delete_option() support for option sharing (and, later, trivial decisions) aipHighHope.h/cpp decisions can be decided multiple times; new note_decide() removed decision-groups, decision.m_opt_prev rr_solve.h/cpp new classes to solve requirement-resource problems based on the HighHope classes 0.8.5 2005-Nov-20 Beta In this release, the High-Hope classes have been enhanced and two minor bugs were fixed. Other minor code changes were made in four files. aipHighHope.h/cpp - added support for decision-groups modified constant Curiosity_Starting failed try that got further is optionally an improvement failed-decision hope-fear is increased worst decision has extra affect on solution compare-value moved call to disable_slowly_degrade() to aipHope fixed minor bug re: problem members: m_is_many_xxx fixed minor bug re: m_tries_since_change_count aipEmotion, aipDecision.cpp, aipProblem.cpp, aipHighHope.cpp - take_msg() now uses pandemonium take_msg() 0.8.4 2005-Nov-17 Beta This is a primarily a code-improvement release - behavior should not change from aiParts 0.8.3. Support was added for a set of decisions sharing a set of options. aipHighHope.h/cpp - moved logic in problem to aspects of hope added aipHHHope aipHHFearAspect aipHHGreedAspect, aipHHCuriosityAspect added aipHHSolveStat, aipHHProblem::try_to_find_solution() removed redundant rand() function moved random_num() to class aipBase aipDecision.h/cpp - support for decisions sharing a set of options comment change aipPandemonium.h/cpp, samp_pandemonium.cpp - can now add demon without owning it aipEmotion.h - hope: fear, greed, curiosity are now aspects simplified base class aipCompEmotion made const: aipHope::greed(), fear(), curiosity() aipHope::weaken(), strengthen(), set_g() removed aipHope::weaken_fear(), strengthen_greed(), etc. removed aipBase.h/cpp - random_num() now in aipBase samp_decision.cpp - improved sample problem series of messages samp_a_to_b.cpp, samp_a2b.cpp - removed call to a2bDecision::set_decision() modified to log tries using aipHHSolveStat Removed version number from final output. 0.8.3 2005-Nov-02 Beta samp_a2b.h/cpp, samp_a_to_b.cpp - start, end, num-try, logging-level on the command line usage help from the command line improved try-logging aipHighHope.h/cpp - fixed is_improved bug; improved code in solve() aipPandemonium.h/cpp, samp_pandemonium.cpp - made pandemonium a queue (used to be a stack) aipBase.h/cpp - added aip_strncpy() that always terminates dest string 0.8.2 2005-Oct-30 Alpha samp_a2b.h/cpp, samp_a_to_b.cpp - option-distance no longer affects choice of option added optional option-logging, and other small changes aipHighHope.h/cpp - fixed hope calculation bug (compound emotion being current) major changes: how tries affect hope on decisions, options if more than one best option, choose randomly now considering tries-since-change, and other small changes aipEmotion.h/cpp - removed public non-constant access to hope components weaken functions now do not weaken to aipNeutral added floor() and ceiling(), and other small changes aipPandemonium.cpp - small change for a new compiler - code now more standard aipDecision.h/cpp - support for choosing randomly from multiple best options aipGood.h/cpp - added constants aipPrettyGood and aipPrettyBad samp_deer_fear.h, samp_decision.cpp - small changes to stay/run-away point and series of messages 0.8.1 2005-Oct-20 Alpha samp_a_to_b.cpp - Sample A-to-B problem made bigger and harder. Improved error-handling assembling the problem. Simplified specifying 2-way links. aipHighHope.cpp - Fixed bug re: count since improvement Modified how decision and option hopes change aipHighHope.h - Fixed a2bProblem::too_bad_to_go_on() bug Improved comments and ability to debug. Requires more work on decision and option hope changing. 0.8.0 2005-Oct-11 Alpha First release of aiParts software - ready for more alpha testing, beta testing and experimenting. Written by [BRM]. = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = Historical and Old Credits aiParts evolved from work done by Brian Marshall, off and on, between 1991 and 2003. aiParts, in its current form, was developed in 2005 and significantly enhanced in 2008. In 1999, there was an aiParts 1.1.1; that software was replaced by this project. Late in the earlier work, in the old aiParts 1.1.1: Aubrey Soper - provided Makefile suggestions ================================================================= rr_solve.cpp0000664000076400007640000004404211063254442012404 0ustar brianbrian//********************************************************************** // rr_solve.cpp - function bodies for rr_solve.h // // Copyright (c) 2008 Brian Marshall // // Developers/Contributers: // [BRM] Brian Marshall - Calgary - bmarshal@agt.net // // 08/01/21 [BRM] began development: split out layer from staffwhen // //---------------------------------------------------------------------- #include "rr_solve.h" #include #include using namespace std; //====================================================================== // rrsReqImp - importance of a requirement // //---------------------------------------------------------------------- // take_msg - take and react to a message void rrsReqImp::take_msg (aipMsg *mm) { aipHHImportance::take_msg(mm); rrsMsg *m = (rrsMsg *)mm; if (m->typ() == HH_Starting_New_Try) { m_left_bhnd = 0; } } //---------------------------------------------------------------------- // Note that a decision for this requirement has been decided. void rrsReqImp::note_rrs_decide (rrsDecision *d, rrsOption *o) { // o (ptr to an option) may be null if (d && o) { m_left_bhnd -= 1; } } //====================================================================== // rrsProblem // //---------------------------------------------------------------------- // Constructor rrsProblem::rrsProblem () { m_requirements = new aipPandemonium; m_resources = new aipPandemonium; m_should_log_options = 0; } //---------------------------------------------------------------------- // Destructor rrsProblem::~rrsProblem () { if (m_requirements) delete m_requirements; if (m_resources) delete m_resources; } //---------------------------------------------------------------------- // add_requirement void rrsProblem::add_requirement (rrsRequirement *x) { if (!x) return; m_requirements->add (x); } //---------------------------------------------------------------------- // add_resource void rrsProblem::add_resource (rrsResource *x) { if (!x) return; m_resources->add (x); } //---------------------------------------------------------------------- // add_decision void rrsProblem::add_decision (rrsDecision *x) { add_hh_decision(x); } //---------------------------------------------------------------------- // In a try, choose the next decision to decide. aipHHDecision * rrsProblem::next_decision() { rrsReqDay *rd; // <<<<< can make dcsn trivial (I think) but have to call hh decide function // rd = trivial_reqday(); //cout << "** found a trivial reqday - forced exit\n"; ????? //exit(1); // if (rd) return rd->dcsn(); rrsDecision * best_dcsn = 0; long best_dcsn_imp = -999999999; rrsRequirementItr itr = requirement_iterator(); for ( rrsRequirement * req = itr.first(); req; req = itr.next() ) { rd = req->first_unsatisfied_reqday(); if (rd) { rrsDecision *dcsn = rd->dcsn(); if (dcsn) { long dcsn_imp = dcsn->imp(); if (!best_dcsn || dcsn_imp > best_dcsn_imp) { best_dcsn = dcsn; best_dcsn_imp = dcsn_imp; } } } } return best_dcsn; } //---------------------------------------------------------------------- // requirement_iterator - return an iterator: requirements in the problem rrsRequirementItr rrsProblem::requirement_iterator() const { rrsRequirementItr i(m_requirements); return i; } //---------------------------------------------------------------------- // resource_iterator - return an iterator: resources in the problem rrsResourceItr rrsProblem::resource_iterator() const { rrsResourceItr i(m_resources); return i; } //---------------------------------------------------------------------- // decision_iterator - return an iterator: decisions in the problem rrsDecisionItr rrsProblem::decision_iterator() const { rrsDecisionItr i(decision_pandemonium()); return i; } //---------------------------------------------------------------------- // too_bad_to_go_on aipG rrsProblem::too_bad_to_go_on() const { // aipHHSolveStat *ss = solve_stat(); // return ss->g_cmp_bsf().calc_fraction(3,2); from samp_a_to_b return aipForbidden; // default from aipHHProblem } //---------------------------------------------------------------------- // log_this_try - to cout - for debugging or watching the thing work // // This works best if decisions are stored in Req,Day order void rrsProblem::log_this_try () { aipHHSolveStat *ss = solve_stat(); cout << "\n===== Try " << ss->i_try()+1 << "...\n"; rrsRequirement *req_prev = 0; aipTime day_prev = aipTime(19010101); rrsDecisionItr itr = decision_iterator(); for ( rrsDecision * dcsn = itr.first(); dcsn; dcsn = itr.next() ) { rrsReqDay *reqday = dcsn->reqday(); rrsRequirement *req = reqday->req(); aipTime day = reqday->day(); rrsOption *opt = dcsn->rrs_opt_cur(); rrsResource *res = opt ? opt->resday()->res() : 0; if (req != req_prev) { cout << "\nReq: " << req->id(); req_prev = req; } if (day != day_prev) { char cday[21]; day.dd_mon_yyyy(cday); cout << " Day: " << cday; day_prev = day; } if (res) cout << " " << res->id(); } cout << "\n"; cout << "-- Goodness of this try: " << ss->g_usr_cur().numeric_value() << " "; if (ss->try_result() == HH_Try_Has_Failed) { if (ss->is_too_bad()) cout << "(quit)"; } else if (ss->try_result() == HH_Try_Is_New_Best) { cout << "(New Best)"; } else if (ss->try_result() == HH_Try_Is_A_Best) { cout << "(a best)"; } else if (ss->try_result() == HH_Try_Is_Improved) { cout << "(improved)"; } else if (ss->try_result() == HH_Try_Is_Changed) { cout << "(changed)"; } cout << "\n"; if (ss->is_many_since_new_best()) { cout << " ... many tries since new best ...\n"; } if (ss->is_many_since_improve()) { cout << " ... many tries since improve ...\n"; } if (ss->is_many_since_change()) { cout << " ... many tries since change ...\n"; } } //---------------------------------------------------------------------- // take_msg void rrsProblem::take_msg (aipMsg *mm) { aipHHProblem::take_msg(mm); // pass message to parent class m_requirements->take_msg(mm); m_resources->take_msg(mm); } //---------------------------------------------------------------------- // Note that a decision has been decided void rrsProblem::note_decide (aipHHDecision *dd, aipHHOption *oo) { if (!dd) return; aipHHProblem::note_decide (dd, oo); rrsDecision *d = (rrsDecision*)dd; rrsOption *o = (rrsOption*)oo; rrsReqDay *reqd = d->reqday(); if (!reqd) return; rrsRequirement *req = reqd->req(); if (!req) return; req->note_rrs_decide (d, o); reqd->note_rrs_decide (d, o); if (o) { // if an option was chosen rrsResDay *resd = o->resday(); if (!resd) return; rrsResource *res = resd->res(); if (!res) return; res->note_rrs_decide (d, o); resd->note_rrs_decide (d, o); } } //---------------------------------------------------------------------- // Return a requirement-day that has just enough options left, so we // can decide the decisions the remaining number of times and // for each decide, take the first available option. //rrsReqDay * rrsProblem::trivial_reqday () const { // rrsReqDay *rd; // rrsRequirementItr itr = requirement_iterator(); // for ( rrsRequirement *req = itr.first(); req; req = itr.next() ) { // rd = req->trivial_reqday(); // if (rd) return rd; // } // return 0; //} //---------------------------------------------------------------------- // solve_rr_problem - solve a staffwhen staff scheduling problem // // return 1 (true) if a schedule is found, zero otherwise int rrsProblem::solve_rr_problem () { aipG g = solve(); // high-hope solve if (g.is_forbidden()) return 0; return 1; } //====================================================================== // rrsDecision // //---------------------------------------------------------------------- // Constructor rrsDecision::rrsDecision (rrsReqDay *a_reqday, long num_to_decide) : aipHHDecision(num_to_decide) { m_reqday = a_reqday; } //---------------------------------------------------------------------- // Destructor rrsDecision::~rrsDecision () {} //---------------------------------------------------------------------- // add_rrs_option void rrsDecision::add_rrs_option (rrsOption *o) { add_hh_option(o); if ( o && o->resday() ) o->resday()->add_opt(o); } //---------------------------------------------------------------------- // Return the importance of this decision long rrsDecision::imp () const { if (!m_reqday) return -999999; rrsRequirement *req = m_reqday->req(); if (!req) return -999999; return aipHHDecision::imp() + req->imp() + m_reqday->imp(); } //---------------------------------------------------------------------- // take_msg - take and react to a message void rrsDecision::take_msg (aipMsg *mm) { aipHHDecision::take_msg(mm); // pass message to parent class // rrsMsg *m = (rrsMsg *)mm; // if (m->typ() == HH_Starting_New_Try) { // reset_is_trivial(); // } } //====================================================================== // rrsOption - an resource working a requirement on a day // //---------------------------------------------------------------------- // Constructor rrsOption::rrsOption(rrsResDay *a_resday, aipG g_static) : aipHHOption(g_static) { m_resday = a_resday; m_g_dynamic = aipNeutral; } //---------------------------------------------------------------------- // Destructor rrsOption::~rrsOption() {} //---------------------------------------------------------------------- // take_msg - take and react to a message void rrsOption::take_msg (aipMsg *mm) { aipHHOption::take_msg(mm); // pass message to parent class rrsMsg *m = (rrsMsg *)mm; if (m->typ() == HH_Starting_New_Try) { m_g_dynamic = aipNeutral; } } //---------------------------------------------------------------------- // set_g_dynamic void rrsOption::set_g_dynamic (aipG x) { m_g_dynamic = x; } //---------------------------------------------------------------------- // add_g_dynamic void rrsOption::add_g_dynamic (aipG x) { m_g_dynamic += x; } //---------------------------------------------------------------------- // g_opt aipG rrsOption::g_opt() { return aipHHOption::g_opt() + g_user_constant() + m_g_dynamic; } //---------------------------------------------------------------------- // g_opt_cmp aipG rrsOption::g_opt_cmp() { return g_user_constant() + m_g_dynamic; } //---------------------------------------------------------------------- // g_opt_usr aipG rrsOption::g_opt_usr() { return g_user_constant() + m_g_dynamic; } //====================================================================== // rrsRequirement // //---------------------------------------------------------------------- // Constructor rrsRequirement::rrsRequirement (long a_id, rrsProblem *a_prob) { m_id = a_id; m_prob = a_prob; m_reqdays = new aipPandemonium; m_hope = new aipHHHope; } //---------------------------------------------------------------------- // Destructor rrsRequirement::~rrsRequirement () { if (m_reqdays) delete m_reqdays; if (m_hope) delete m_hope; } //---------------------------------------------------------------------- // add_reqday void rrsRequirement::add_reqday (rrsReqDay *x) { if (!x) return; m_reqdays->add (x); } //---------------------------------------------------------------------- // take_msg - take and react to a message void rrsRequirement::take_msg (aipMsg *mm) { rrsMsg *m = (rrsMsg *)mm; m->set_is_in_cur_solution(1); m_hope->take_msg(mm); m_importance.take_msg(mm); m_reqdays->take_msg(mm); } //---------------------------------------------------------------------- // Note that a decision for this requirement has been decided void rrsRequirement::note_rrs_decide (rrsDecision *d, rrsOption *o) { // o (ptr to an option) may be null m_importance.note_rrs_decide(d, o); } //---------------------------------------------------------------------- // Return true if all req-days are satisfied int rrsRequirement::is_satisfied () const { rrsReqDay *rd = first_unsatisfied_reqday(); return rd ? 0 : 1; } //---------------------------------------------------------------------- // Return the first unsatisfied requirement-day rrsReqDay * rrsRequirement::first_unsatisfied_reqday () const { rrsReqDayItr itr = reqday_iterator(); for ( rrsReqDay *prd = itr.first(); prd; prd = itr.next() ) { if ( ! prd->is_satisfied() ) return prd; } return 0; } //---------------------------------------------------------------------- // Return a requirement-day that has just enough options left, so we // can decide the decisions the remaining number of times and // for each decide, take the first available option. //rrsReqDay * rrsRequirement::trivial_reqday () const { // rrsReqDayItr itr = reqday_iterator(); // for ( rrsReqDay *rd = itr.first(); rd; rd = itr.next() ) { // if ( !(rd->has_surplus_opts()) ) return rd; // } // return 0; //} //---------------------------------------------------------------------- // return an iterator: requirement-days for the requirement rrsReqDayItr rrsRequirement::reqday_iterator() const { rrsReqDayItr i(m_reqdays); return i; } //====================================================================== // rrsReqDay // //---------------------------------------------------------------------- // Constructor // // set_dcsn() must be called after the decision is created rrsReqDay::rrsReqDay (rrsRequirement *a_req, long a_yyyymmdd) { m_req = a_req; m_day = aipTime(a_yyyymmdd); m_dcsn = 0; m_hope = new aipHHHope; } //---------------------------------------------------------------------- // Destructor rrsReqDay::~rrsReqDay () { if (m_hope) delete m_hope; } //---------------------------------------------------------------------- // take_msg - take and react to a message void rrsReqDay::take_msg (aipMsg *mm) { rrsMsg *m = (rrsMsg *)mm; m->set_is_in_cur_solution(1); m_hope->take_msg(mm); m_importance.take_msg(mm); } //---------------------------------------------------------------------- // Note that a decision for this requirement-day has been decided void rrsReqDay::note_rrs_decide (rrsDecision *d, rrsOption *o) { // o (ptr to an option) may be null if (!d) { if (o) { log ("rrsReqDay d is null"); } else { log ("rrsReqDay d,o are null"); } } } //---------------------------------------------------------------------- // Return true if the reqday has all the resourcess it needs int rrsReqDay::is_satisfied () const { if ( m_dcsn && m_dcsn->is_decided() ) return 1; return 0; } //---------------------------------------------------------------------- // Return true if the reqday has more than enough options int rrsReqDay::has_surplus_opts () const { if (!m_dcsn) return 0; long remaining_to_decide = m_dcsn->num_to_decide() - m_dcsn->num_decided(); if (m_dcsn->num_opt_remaining() > remaining_to_decide) return 1; return 0; } //====================================================================== // rrsResource // //---------------------------------------------------------------------- // Constructor rrsResource::rrsResource (long a_id, rrsProblem *a_prob) { m_id = a_id; m_prob = a_prob; m_resdays = new aipPandemonium; m_hope = new aipHHHope; m_is_in_cur_solution = 0; } //---------------------------------------------------------------------- // Destructor rrsResource::~rrsResource () { if (m_resdays) delete m_resdays; if (m_hope) delete m_hope; } //---------------------------------------------------------------------- // add_resday void rrsResource::add_resday (rrsResDay *x) { if (!x) return; m_resdays->add (x); } //---------------------------------------------------------------------- // take_msg - take and react to a message void rrsResource::take_msg (aipMsg *mm) { rrsMsg *m = (rrsMsg *)mm; if (m->typ() == HH_Starting_New_Try) { m_is_in_cur_solution = 0; } m->set_is_in_cur_solution(m_is_in_cur_solution); m_hope->take_msg(mm); m_resdays->take_msg(mm); } //---------------------------------------------------------------------- // Note that a decision involving this resource has been decided void rrsResource::note_rrs_decide (rrsDecision *d, rrsOption *o) { if (!d) log ("rrsReqDay d is null"); // o (ptr to an option) may be null if (o) m_is_in_cur_solution = 1; } //---------------------------------------------------------------------- // resday_iterator rrsResDayItr rrsResource::resday_iterator() const { rrsResDayItr i(m_resdays); return i; } //====================================================================== // rrsResDay // //---------------------------------------------------------------------- // Constructor rrsResDay::rrsResDay (rrsResource *a_res, long a_yyyymmdd) { m_res = a_res; m_day = aipTime(a_yyyymmdd); m_opts = new aipPandemonium; m_hope = new aipHHHope; } //---------------------------------------------------------------------- // Destructor rrsResDay::~rrsResDay () { if (m_hope) delete m_hope; if (m_opts) delete m_opts; } //---------------------------------------------------------------------- // take_msg - take and react to a message void rrsResDay::take_msg (aipMsg *mm) { rrsMsg *m = (rrsMsg *)mm; if (m->typ() == HH_Starting_New_Try) { m_is_in_cur_solution = 0; } m->set_is_in_cur_solution(m_is_in_cur_solution); m_hope->take_msg(mm); } //---------------------------------------------------------------------- // Note that a decision involving this resday has been decided void rrsResDay::note_rrs_decide (rrsDecision *d, rrsOption *o) { if (!d) log ("rrsReqDay d is null"); // o (ptr to an option) may be null if (o) m_is_in_cur_solution = 1; } //====================================================================== rr_solve.h0000755000076400007640000003652511063254442012061 0ustar brianbrian//********************************************************************** // rr_solve.h - Assign Resources to Requirements // // An rrsProblem is a subclass of aipHHProblem. // rrsProblem::solve_sw_problem() uses the High-Hope technique. // See aipHighHope.h // // A rrsProblem has a set of decisions, each of which has a set // of options. // // In this version, a Decision is associated with a Requirement-Day; // an Option refers to a Resource that can satisfy it. // // Everything to do with a decision being trivial is commented out. // It will be implemented in a later release. // // Copyright (c) 2008 Brian Marshall // // Developers/Contributers: // [BRM] Brian Marshall - Calgary - bmarshal@agt.net // // 08/01/20 [BRM] began development: split out layer from staffwhen // //---------------------------------------------------------------------- // The Data Model // // In this version, only Requirements have Decisions, which means // that Resources always have an Option. // // [Problem] // + [Requirements] // | + // v v // v v // [Decisions]<---+[Req-Days] // v----o + // v | // [chosen_opts] | [Resources] // ^ | + // | v v // | v v // ----o[Options]<<--+[Res-Days] // // Notes: // - Problems have Decisions, which have Options // - Requirements and Resources may have a Decision // - Decisions may be decided multiple times, so they have a list // of chosen Options. // //---------------------------------------------------------------------- // Important Note // // In this file, the functions in rrsOption: // virtual aipG g_opt_cmp() { return g_user_constant() + m_g_dynamic; } // virtual aipG g_opt_usr() { return g_user_constant() + m_g_dynamic; } // override functions in the HighHope classes - g_dynamic affects // making decisions, comparing solutions and the goodness that can // be reported to the user. // // To solve a particular type of Requirement-Resource problem, // a set of sub-classes of rr_solve classes are written, and // they call option.set_g_dynamic(aipG x) based on aspects // of the particular problem. // //---------------------------------------------------------------------- #ifndef rr_solve_h_guard #define rr_solve_h_guard #include "aipHighHope.h" #include "aipTime.h" //---------------------------------------------------------------------- // Classes. Sub-classing is shown by indentation. // class aipG is a typedef for aipGoodness // class aipBase; ( in aipBase.h ) // class aipImportance; ( in aipImportance.h ) // class aipHHImportance; ( in aipHighHope.h ) // class rrsReqImp; // importance of a requirement // class aipProblem; ( in aipProblem.h ) // class aipHHProblem; ( in aipHighHope.h ) class rrsProblem; // rr-solve problem // class aipDemon; ( in aipPandemonium.h ) // class aipDecision; ( in aipDecision.h ) // class aipHHDecision; ( in aipHighHope.h ) class rrsDecision; // decision: for a requirement // class aipOption; ( in aipDecision.h ) // class aipHHOption; ( in aipHighHope.h ) class rrsOption; // option: a resource class rrsRequirement; // requirement to be satisfied class rrsReqDay; // requirement on a day class rrsResource; // resource to satisfy requirement class rrsResDay; // resource on a day // class aipDemonItr; ( in aipPandemonium.h ) class rrsRequirementItr; // decision iterator class rrsReqDayItr; // decision iterator class rrsResourceItr; // decision iterator class rrsResDayItr; // decision iterator class rrsDecisionItr; // decision iterator // class aipMsg ( in aipBase.h ) class rrsMsg; // message for rr-solve solving //====================================================================== // rrsReqImp - Importance of a position class rrsReqImp : public aipHHImportance { long m_left_bhnd; // being left behind public: rrsReqImp () { m_left_bhnd = 0; } virtual ~rrsReqImp () {} virtual long val () const { return aipHHImportance::val() + m_left_bhnd; } virtual void take_msg (aipMsg *m); virtual void note_rrs_decide (rrsDecision *d, rrsOption *o); }; //====================================================================== // rrsProblem - rrSolve problem - assigning resources to requirements // // solve_rrs_problem () returns 1 (true) if a solution is found, // zero otherwise. // // log_this_try() works best if decisions are stored in Req,Day order class rrsProblem : public aipHHProblem { aipPandemonium * m_requirements; aipPandemonium * m_resources; int m_should_log_options; // 1 (true) = log to cout protected: virtual aipHHDecision * next_decision(); virtual aipG too_bad_to_go_on() const; virtual void log_this_try (); // to cout aipPandemonium * requirement_pandemonium () const { // for iterators return m_requirements; } aipPandemonium * resource_pandemonium () const { // for iterators return m_resources; } public: rrsProblem (); virtual ~rrsProblem (); void set_num_try (long x) { aipHHProblem::set_num_try(x); } void add_requirement (rrsRequirement *x); void add_resource (rrsResource *x); void add_decision (rrsDecision *x); void enable_log_options () { m_should_log_options = 1; } void disable_log_options () { m_should_log_options = 0; } int should_log_options () const { return m_should_log_options; } rrsRequirementItr requirement_iterator() const; rrsResourceItr resource_iterator() const; rrsDecisionItr decision_iterator() const; virtual void take_msg (aipMsg *m); virtual void note_decide (aipHHDecision *d, aipHHOption *o); // rrsReqDay * trivial_reqday () const; virtual int solve_rr_problem (); // use this, not solve() }; //====================================================================== // rrsDecision - choosing a res for a reqday // // inherits: importance, a set of options, more class rrsDecision : public aipHHDecision { rrsReqDay * m_reqday; // reqday that needs resource-days public: rrsDecision (rrsReqDay *a_reqday, long num_to_decide =1); virtual ~rrsDecision(); void add_rrs_option (rrsOption *x); // use this, not add_hh_option virtual void take_msg (aipMsg *m); rrsProblem * prob () const { return (rrsProblem*)owner(); } rrsReqDay * reqday () const { return m_reqday; } virtual long imp () const; rrsOption * rrs_opt_cur () const { return (rrsOption*)opt_cur(); } rrsOption * rrs_opt_bsf () const { return (rrsOption*)opt_bsf(); } }; //====================================================================== // rrsOption - a resource assigned to a requirement on a day class rrsOption : public aipHHOption { rrsResDay * m_resday; // the subject of this option aipG m_g_dynamic; // g of situational aspects public: rrsOption(rrsResDay *a_resday, aipG g_constant); virtual ~rrsOption (); virtual void take_msg (aipMsg *m); void set_g_dynamic (aipG x); void add_g_dynamic (aipG x); rrsDecision * rrs_chooser () const { return (rrsDecision*)hh_opt_chooser(); } rrsResDay * resday () const { return m_resday; } virtual aipG g_opt(); virtual aipG g_opt_cmp(); virtual aipG g_opt_usr(); }; //====================================================================== // rrsRequirement - a requirement requiring resources // // A requirement is owned by a problem; it owns requirement-days. class rrsRequirement : public aipDemon { long m_id; rrsProblem * m_prob; aipPandemonium * m_reqdays; aipHHHope * m_hope; // how well this requirement is doing rrsReqImp m_importance; // for choosing next decision protected: aipPandemonium * reqday_pandemonium () const { // for iterators return m_reqdays; } public: rrsRequirement(long a_id, rrsProblem *a_prob); virtual ~rrsRequirement(); void add_reqday (rrsReqDay *x); long imp () const { return m_importance.val(); } virtual void take_msg (aipMsg *m); virtual void note_rrs_decide (rrsDecision *d, rrsOption *o); int is_satisfied () const; rrsReqDay * first_unsatisfied_reqday () const; // rrsReqDay * trivial_reqday () const; rrsProblem * prob () const { return m_prob; } long id () const { return m_id; } rrsReqDayItr reqday_iterator() const; }; //====================================================================== // rrsReqDay - a requirement on a day (requiring one or more resources) // // set_dcsn() must be called after the decision is created. class rrsReqDay : public aipDemon { rrsRequirement * m_req; aipTime m_day; rrsDecision * m_dcsn; aipHHHope * m_hope; // how well this reqday is doing aipHHImportance m_importance; public: rrsReqDay(rrsRequirement *a_req, long a_yyyymmdd); virtual ~rrsReqDay(); void set_dcsn (rrsDecision *a_dcsn) { m_dcsn = a_dcsn; } long imp () const { return m_importance.val(); } virtual void take_msg (aipMsg *m); virtual void note_rrs_decide (rrsDecision *d, rrsOption *o); aipTime day () const { return m_day; } rrsRequirement * req () const { return m_req; } rrsDecision * dcsn () const { return m_dcsn; } long num_decided () const { return m_dcsn->num_decided(); } int is_satisfied () const; int has_surplus_opts () const; }; //====================================================================== // rrsResource - a resource that can satisfy requirements class rrsResource : public aipDemon { long m_id; rrsProblem * m_prob; aipPandemonium * m_resdays; aipHHHope * m_hope; // how well this resource is doing int m_is_in_cur_solution; protected: aipPandemonium * resday_pandemonium () const { // for iterators return m_resdays; } public: rrsResource(long a_id, rrsProblem *a_prob); virtual ~rrsResource(); void add_resday (rrsResDay *x); virtual void take_msg (aipMsg *m); virtual void note_rrs_decide (rrsDecision *d, rrsOption *o); rrsProblem * prob () const { return m_prob; } long id () const { return m_id; } rrsResDayItr resday_iterator() const; }; //====================================================================== // rrsResDay - a resource on a day class rrsResDay : public aipDemon { aipTime m_day; rrsResource * m_res; aipPandemonium * m_opts; // opts where this resday is the subject aipHHHope * m_hope; // how well this resday is doing int m_is_in_cur_solution; protected: aipPandemonium * opt_pandemonium () const { // for iterators return m_opts; } public: rrsResDay(rrsResource *a_res, long a_yyyymmdd); virtual ~rrsResDay(); void add_opt (rrsOption *o) { m_opts->add(o); } virtual void take_msg (aipMsg *m); virtual void note_rrs_decide (rrsDecision *d, rrsOption *o); aipTime day () const { return m_day; } rrsResource * res () const { return m_res; } }; //====================================================================== // rrsRequirementItr - Iterator for Requirements in a Problem class rrsRequirementItr : public aipDemonItr { public: rrsRequirementItr (aipPandemonium *p) { set_demon_itr(p,0); } rrsRequirementItr (const rrsRequirementItr& x) { set_demon_itr (x.pandemonium(), x.current()); } virtual ~rrsRequirementItr () {} rrsRequirementItr& operator = (const rrsRequirementItr& x) { set_demon_itr(x.pandemonium(), x.current()); return *this; } rrsRequirement * first () { return (rrsRequirement*)aipDemonItr::first(); } rrsRequirement * next () { return (rrsRequirement*)aipDemonItr::next(); } }; //====================================================================== // rrsReqDayItr - Iterator for ReqDays of a Requirement class rrsReqDayItr : public aipDemonItr { public: rrsReqDayItr (aipPandemonium *p) { set_demon_itr(p,0); } rrsReqDayItr (const rrsReqDayItr& x) { set_demon_itr (x.pandemonium(), x.current()); } virtual ~rrsReqDayItr () {} rrsReqDayItr& operator = (const rrsReqDayItr& x) { set_demon_itr(x.pandemonium(), x.current()); return *this; } rrsReqDay * first () { return (rrsReqDay*)aipDemonItr::first(); } rrsReqDay * next () { return (rrsReqDay*)aipDemonItr::next(); } }; //====================================================================== // rrsResourceItr - Iterator for Resources in a Problem class rrsResourceItr : public aipDemonItr { public: rrsResourceItr (aipPandemonium *p) { set_demon_itr(p,0); } rrsResourceItr (const rrsResourceItr& x) { set_demon_itr (x.pandemonium(), x.current()); } virtual ~rrsResourceItr () {} rrsResourceItr& operator = (const rrsResourceItr& x) { set_demon_itr(x.pandemonium(), x.current()); return *this; } rrsResource * first () { return (rrsResource*)aipDemonItr::first(); } rrsResource * next () { return (rrsResource*)aipDemonItr::next(); } }; //====================================================================== // rrsResDayItr - Iterator for ResDays of a Resource class rrsResDayItr : public aipDemonItr { public: rrsResDayItr (aipPandemonium *p) { set_demon_itr(p,0); } rrsResDayItr (const rrsResDayItr& x) { set_demon_itr (x.pandemonium(), x.current()); } virtual ~rrsResDayItr () {} rrsResDayItr& operator = (const rrsResDayItr& x) { set_demon_itr(x.pandemonium(), x.current()); return *this; } rrsResDay * first () { return (rrsResDay*)aipDemonItr::first(); } rrsResDay * next () { return (rrsResDay*)aipDemonItr::next(); } }; //====================================================================== // rrsDecisionItr - Iterator for Decisions in a Problem class rrsDecisionItr : public aipDemonItr { public: rrsDecisionItr (aipPandemonium *p) { set_demon_itr(p,0); } rrsDecisionItr (const rrsDecisionItr& x) { set_demon_itr (x.pandemonium(), x.current()); } virtual ~rrsDecisionItr () {} rrsDecisionItr& operator = (const rrsDecisionItr& x) { set_demon_itr(x.pandemonium(), x.current()); return *this; } rrsDecision * first () { return (rrsDecision*)aipDemonItr::first(); } rrsDecision * next () { return (rrsDecision*)aipDemonItr::next(); } }; //====================================================================== // rrsMsg - message for StaffWhen problem-solving class rrsMsg : public aipHHMsg { public: rrsMsg (rrsProblem *p, short typ) : aipHHMsg(p,typ) {} ~rrsMsg() {} }; //====================================================================== #endif //====================================================================== samp_a2b.cpp0000644000076400007640000003131011063254442012225 0ustar brianbrian//********************************************************************** // samp_a2b.cpp - function bodies for samp_a2b.h // // Copyright (c) 1999, 2005, 2007 Brian Marshall // // See the license at end of this file. // // Developers/Contributers: // [BRM] Brian Marshall - Calgary - bmarshal@agt.net // // 07/11/29 [BRM] changed option logging // 05/11/10 [BRM] removed call to a2bDecision::set_decision() // modified to log tries using aipHHSolveStat // 05/10/23 [BRM] added option logging // 05/10/21 [BRM] improved option debugging logging // 05/09/27 [BRM] development began // //---------------------------------------------------------------------- #include "samp_a2b.h" #include #include using namespace std; //====================================================================== // a2bProblem // //---------------------------------------------------------------------- // Constructor a2bProblem::a2bProblem () { m_node_start = m_node_end = m_node_current = 0; m_should_log_options = 0; } //---------------------------------------------------------------------- // Destructor a2bProblem::~a2bProblem () {} //---------------------------------------------------------------------- // add_node void a2bProblem::add_node (a2bNode *x) { add_hh_decision(x); } //---------------------------------------------------------------------- // add_link - return zero on success, -1 if option new failed // -2 if from_label not found, // -3 if to_label not found. // int a2bProblem::add_link (const char *from_label, const char *to_label, long distance) { a2bNode *node_from = find_node(from_label); if (!node_from) return -2; a2bNode *node_to = find_node(to_label); if (!node_to) return -3; a2bOption *link = new a2bOption(node_to,distance); if (!link) return -1; node_from->add_a2b_option(link); return 0; } //---------------------------------------------------------------------- // node_iterator - all the nodes in no particular order a2bNodeItr a2bProblem::node_iterator() const { a2bNodeItr i(decision_pandemonium()); return i; } //---------------------------------------------------------------------- // solution_iterator - nodes in best solution in solution order a2bSolutionItr a2bProblem::solution_iterator() const { a2bSolutionItr i(this); return i; } //---------------------------------------------------------------------- // cur_solution_iterator - nodes in current solution in order a2bCurSolutionItr a2bProblem::cur_solution_iterator() const { a2bCurSolutionItr i(this); return i; } //---------------------------------------------------------------------- // next_decision - the next decision to do // // In this default function, the next decision is the first unmade // decision found by an iterator. aipHHDecision * a2bProblem::next_decision() { if (!m_node_current) return (m_node_current = m_node_start); a2bOption *cur_opt = (a2bOption*)m_node_current->opt_cur(); if (!cur_opt) return (m_node_current = m_node_start); // this should not happen if called by solve_a_to_b(); return (m_node_current = cur_opt->reachable_node()); } //---------------------------------------------------------------------- // too_bad_to_go_on aipG a2bProblem::too_bad_to_go_on() const { aipHHSolveStat *ss = solve_stat(); return ss->g_cmp_bsf().calc_fraction(3,2); } //---------------------------------------------------------------------- // solution_is_complete // // The A-to-B solution is complete when, in the last decision made, // the chosen option refers to the ending-node ( == m_node_end). int a2bProblem::solution_is_complete () const { if (m_node_current && m_node_current->a2b_opt_cur() && m_node_current->a2b_opt_cur()->reachable_node() == m_node_end) return 1; return 0; } //---------------------------------------------------------------------- // log_this_try - used for debugging or watching the thing work void a2bProblem::log_this_try () { aipHHSolveStat *ss = solve_stat(); log(ss->i_try()+1); log(": "); a2bCurSolutionItr itr = cur_solution_iterator(); for ( a2bNode * n = itr.first(); n; n = itr.next() ) { log(" "); log(n->label()); } log(" ("); log(ss->g_usr_cur().numeric_value()); log (") "); if (ss->try_result() == HH_Try_Has_Failed) { if (ss->is_too_bad()) log("quit"); } else if (ss->try_result() == HH_Try_Is_New_Best) { log("New Best"); } else if (ss->try_result() == HH_Try_Is_A_Best) { log("a best"); } else if (ss->try_result() == HH_Try_Is_Improved) { log("improved"); } else if (ss->try_result() == HH_Try_Is_Changed) { log("changed"); } log("\n"); if (ss->is_many_since_new_best()) { log(" ... many tries since new best ...\n"); } if (ss->is_many_since_improve()) { log(" ... many tries since improve ...\n"); } if (ss->is_many_since_change()) { log(" ... many tries since change ...\n"); } } //---------------------------------------------------------------------- // take_msg void a2bProblem::take_msg (aipMsg *m) { if (m->typ() == HH_Starting_New_Try) { m_node_current = 0; } aipHHProblem::take_msg(m); } //---------------------------------------------------------------------- // solve_a_to_b - look for shortest path from the starting node // to the ending node, returning distance, or: // -1 if no solution found // -2 if starting-node not found // -3 if ending-node not found long a2bProblem::solve_a_to_b (const char *from_label, const char *to_label) { if ( should_log_options() ) { log("\nOption logging: option-hope + next-node-hope\n"); log(" ( hopes are fear+greed+curiosity ie. (fgc) )\n\n"); } // Note that in this problem, normalize_option_goodnesses() // is not used because the constant-option-goodnesses are // not used when choosing an option. a2bNode *node_start = find_node(from_label); if (!node_start) return -2; set_start_node(node_start); a2bNode *node_end = find_node(to_label); if (!node_end) return -3; set_end_node(node_end); aipG g_solution = solve(); // high-hope solve if (g_solution.is_forbidden()) return -1; return g_solution.numeric_value(); // distance of best solution } //---------------------------------------------------------------------- // find_node - given the node label a2bNode * a2bProblem::find_node (const char *label) { a2bNodeItr itr = node_iterator(); for ( a2bNode * n = itr.first(); n; n = itr.next() ) { if ( !strcmp(n->label(), label) ) return n; } return 0; } //====================================================================== // a2bNode // //---------------------------------------------------------------------- // Constructor a2bNode::a2bNode (const char *label) { strncpy (m_label, label, 20); m_label[20] = '\0'; } //---------------------------------------------------------------------- // Destructor a2bNode::~a2bNode () {} //---------------------------------------------------------------------- // add_a2b_option void a2bNode::add_a2b_option (a2bOption *x) { add_hh_option(x); } //---------------------------------------------------------------------- // g_hope aipG a2bNode::g_hope () const { return aipHHDecision::g_hope(); } //====================================================================== // a2bOption - a path from the owning node to another node // //---------------------------------------------------------------------- // Constructor // // Note that the parent-class is passed the negative distance. // The software finds a maximum; we find minimum distance by // finding the maximum negative distance. a2bOption::a2bOption(a2bNode *n, long distance) : aipHHOption(0-distance) { m_reachable_node = n; } //---------------------------------------------------------------------- // Destructor a2bOption::~a2bOption() {} //---------------------------------------------------------------------- // g_opt - Goodness for comparing options // // Because we are adding in two hopes (for the option and for the // node to which the option points), we divide the sum of the two // hopes in half. aipG a2bOption::g_opt() { // if node is already in the solution... if (m_reachable_node->opt_cur()) return aipForbidden; aipG g_hope_total = g_hope() + reachable_node()->g_hope(); aipG g_tot = g_hope_total.calc_fraction(1,2); // whether to add a little randomness should be // re-evaluated as the AI is improved. g_tot += random_num(-2,2); if ( a2b_dcsn() && a2b_dcsn()->a2b_prob() && a2b_dcsn()->a2b_prob()->should_log_options() ) { char hope_str[31], rhope_str[31]; dump_hope_to_ptr(hope_str); strcat (hope_str, "/2"); reachable_node()->dump_hope_to_ptr(rhope_str); strcat (rhope_str, "/2"); log(" option - "); log (a2b_dcsn()->label()); log(" to "); log(m_reachable_node->label()); log(" = "); log(g_tot.numeric_value()); log(" = "); log(hope_str); log(" "); log(rhope_str); log("\n"); } // end of logging goodnesses that control decision-making return g_tot; } //---------------------------------------------------------------------- // g_opt_cmp - goodness used by solve() to compare solutions aipG a2bOption::g_opt_cmp() { return g_user_constant(); } //---------------------------------------------------------------------- // g_opt_usr - value reported to user (inside a goodness) aipG a2bOption::g_opt_usr() { return (aipNeutral - g_user_constant()); // make it positive } //====================================================================== // a2bSolItrBase - base class for Solution Iterators // //---------------------------------------------------------------------- // set_itr - given an a2bProblem void a2bSolItrBase::set_itr (const a2bProblem *p) { if (p) { m_first = p->start_node(); } else { m_first = 0; } m_current = 0; } //---------------------------------------------------------------------- // set_itr - given a reference to a Solution Iterator to copy void a2bSolItrBase::set_itr (const a2bSolItrBase& i) { m_first = i.const_first(); m_current = 0; } //---------------------------------------------------------------------- // first a2bNode * a2bSolItrBase::first() { return (m_current = m_first); } //====================================================================== // a2bSolutionItr - Solution Iterator for Best Solution // //---------------------------------------------------------------------- // next a2bNode * a2bSolutionItr::next() { if (!const_current()) return first(); a2bOption *link_opt = (a2bOption*)(const_current()->opt_bsf()); set_cur ( link_opt ? link_opt->reachable_node() : 0 ); return const_current(); } //====================================================================== // a2bCurSolutionItr - Solution Iterator for Current Solution // //---------------------------------------------------------------------- // next a2bNode * a2bCurSolutionItr::next() { if (!const_current()) return first(); a2bOption *link_opt = (a2bOption*)(const_current()->opt_cur()); set_cur ( link_opt ? link_opt->reachable_node() : 0 ); return const_current(); } //====================================================================== // License // // Permission is hereby granted, free of charge, to any // person obtaining a copy of this software and associated // documentation files (the "Software"), to deal in the Software // without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to // whom the Software is furnished to do so, subject to the // following conditions: // // The copyright notice and this license shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. // // //********************************************************************** samp_a2b.h0000755000076400007640000002456511063254442011713 0ustar brianbrian//********************************************************************** // samp_a2b.h - Find the shortest path from A to B. // // Provides: a2bProblem a2bNode a2bOption a2bSolutionItr // // An a2bProblem is a subclass of aipHHProblem. // a2bProblem::solve_a_to_b() uses the High-Hope technique. // See aipHighHope.h // // An a2bProblem is setup with // a set of a2bNode nodes, // each of which have between 1 and 5 a2bOption options, // each of which refer to a node reachable from this node. // (ie. nodes have options that refer to a node) // // Copyright (c) 2005 Brian Marshall // // See the license at end of this file. // // Developers/Contributers: // [BRM] Brian Marshall - Calgary - bmarshal@agt.net // // 05/11/10 [BRM] comments at top of file improved // modified to log tries using aipSolveStat // 05/10/23 [BRM] added option logging // 05/10/20 [BRM] Fixed bug in a2bProblem::too_bad_to_go_on() // 05/09/20 [BRM] Development begins. // //---------------------------------------------------------------------- // About the status... // // This is a very early version. The existing techniques can be // improved - mostly by improving how different messages affect // the hope of decisions and options. This should be done before // the High-Hope technique is made more sophisticated. // // Ways of making the High-Hope technique more sophisticated include: // - early in problem-solving, curiosity encourages exploration // - later in problem-solving, greed encourages refinement // - if try is duplicating previous try, favor alternate options // // The program will frequently find the same solution a number of // times, one after another. To a certain extent, this is a normal // aspect of how the software works. // //---------------------------------------------------------------------- #ifndef samp_a2b_h_guard #define samp_a2b_h_guard #include "aipHighHope.h" //---------------------------------------------------------------------- // Classes. Sub-classing is shown by indentation. // class aipG is a typedef for aipGoodness // Hope is a compound emotion composed of Fear, Greed and Curiosity // class aipBase; ( in aipBase.h ) // class aipProblem; ( in aipProblem.h ) // class aipHHProblem; ( in aipHighHope.h ) class a2bProblem; // a-to-b problem // class aipDemon; ( in aipPandemonium.h ) // class aipDecision; ( in aipDecision.h ) // class aipHHDecision; ( in aipHighHope.h ) class a2bNode; // decision: which node next // class aipOption; ( in aipDecision.h ) // class aipHHOption; ( in aipHighHope.h ) class a2bOption; // option: a node to go to next // class aipDemonItr; ( in aipPandemonium.h ) class a2bNodeItr; // node iterator class a2bSolItrBase; // solution iterator base class class a2bSolutionItr; // best solution node iterator class a2bCurSolutionItr; // current solution node iterator //====================================================================== // a2bProblem - Find the shortest path from A to B // // solve_a_to_b returns the distance of the best solution found, or, // -1 = no solution found // -2 = starting node not found // -3 = ending node not found // // Once nodes (ie. decisions) have been added to a problem, // links (ie. options) can be added 2 ways: // - add_a2b_option() for nodes // - calling add_link() for the problem (returns 1 on success) // If add_link() is used, applications do not explicitly create // or deal with the options. class a2bProblem : public aipHHProblem { a2bNode * m_node_start; a2bNode * m_node_end; a2bNode * m_node_current; int m_should_log_options; // 0 = do not log option goodnesses protected: virtual aipHHDecision * next_decision(); virtual aipG too_bad_to_go_on() const; virtual int solution_is_complete () const; virtual void log_this_try (); public: a2bProblem (); virtual ~a2bProblem (); void set_num_try (long x) { aipHHProblem::set_num_try(x); } void add_node (a2bNode *x); int add_link (const char *from_label, const char *to_label, long distance); void set_start_node (a2bNode *x) { m_node_start = x; } void set_end_node (a2bNode *x) { m_node_end = x; } void enable_log_options () { m_should_log_options = 1; } void disable_log_options () { m_should_log_options = 0; } int should_log_options () const { return m_should_log_options; } a2bNode * start_node () const { return m_node_start; } a2bNode * end_node () const { return m_node_end; } a2bNodeItr node_iterator() const; a2bSolutionItr solution_iterator() const; a2bCurSolutionItr cur_solution_iterator() const; virtual void take_msg (aipMsg *m); virtual long solve_a_to_b (const char *from_label, const char *to_label); a2bNode * find_node (const char *label); }; //====================================================================== // a2bNode - a decision - a node that can reach other nodes class a2bNode : public aipHHDecision { char m_label[21]; public: a2bNode (const char *label); virtual ~a2bNode(); void add_a2b_option (a2bOption *x); aipG g_hope () const; a2bProblem * a2b_prob () const { return (a2bProblem*)owner(); } a2bOption * a2b_opt_cur () const { return (a2bOption*)opt_cur(); } a2bOption * a2b_opt_bsf () const { return (a2bOption*)opt_bsf(); } const char * label () const { return m_label; } }; //====================================================================== // a2bOption // // An option, at a node, specifies another reachable node. class a2bOption : public aipHHOption { a2bNode * m_reachable_node; long m_distance; public: a2bOption(a2bNode *n, long distance); virtual ~a2bOption (); // the following function only works because we know // that each option belongs to only one decision. a2bNode * a2b_dcsn () const { return (a2bNode*)owner(); } a2bNode * reachable_node () const { return m_reachable_node; } long distance () const { return m_distance; } virtual aipG g_opt(); virtual aipG g_opt_cmp(); virtual aipG g_opt_usr(); }; //====================================================================== // a2bNodeItr - Node Iterator class a2bNodeItr : public aipDemonItr { public: a2bNodeItr (aipPandemonium *p) { set_demon_itr(p,0); } a2bNodeItr (const a2bNodeItr& x) { set_demon_itr (x.pandemonium(), x.current()); } virtual ~a2bNodeItr () {} a2bNodeItr& operator = (const a2bNodeItr& x) { set_demon_itr(x.pandemonium(), x.current()); return *this; } a2bNode * first () { return (a2bNode*)aipDemonItr::first(); } a2bNode * next () { return (a2bNode*)aipDemonItr::next(); } }; //====================================================================== // a2bSolItrBase - base class for Solution Iterators // // This is a parent class for Iterators that iterate through the // the nodes in a solution, in solution order. class a2bSolItrBase { a2bNode * m_first; a2bNode * m_current; protected: void set_itr (const a2bProblem *p); void set_itr (const a2bSolItrBase& i); void set_cur (a2bNode *n) { m_current = n; } a2bNode * const_first () const { return m_first; } a2bNode * const_current () const { return m_current; } public: a2bSolItrBase () { set_itr(0); } a2bSolItrBase (const a2bProblem *p) { set_itr(p); } a2bSolItrBase (const a2bSolItrBase& x) { set_itr(x); } virtual ~a2bSolItrBase () {} a2bNode * first(); a2bNode * next() { return 0; } // override this }; //====================================================================== // a2bSolutionItr - Iterator for nodes in the Best Solution class a2bSolutionItr : public a2bSolItrBase { public: a2bSolutionItr () : a2bSolItrBase() {} a2bSolutionItr (const a2bProblem *p) : a2bSolItrBase(p) {} a2bSolutionItr (const a2bSolutionItr& x) : a2bSolItrBase(x) {} virtual ~a2bSolutionItr () {} a2bSolutionItr& operator = (const a2bSolutionItr& x) { set_itr (x); return *this; } a2bNode * first() { return a2bSolItrBase::first(); } a2bNode * next(); }; //====================================================================== // a2bCurSolutionItr - Iterator for nodes in the current solution class a2bCurSolutionItr : public a2bSolItrBase { public: a2bCurSolutionItr () : a2bSolItrBase() {} a2bCurSolutionItr (const a2bProblem *p) : a2bSolItrBase(p) {} a2bCurSolutionItr (const a2bCurSolutionItr& x) : a2bSolItrBase(x) {} virtual ~a2bCurSolutionItr () {} a2bCurSolutionItr& operator = (const a2bCurSolutionItr& x) { set_itr (x); return *this; } a2bNode * first() { return a2bSolItrBase::first(); } a2bNode * next(); }; //====================================================================== #endif //====================================================================== // License // // Permission is hereby granted, free of charge, to any // person obtaining a copy of this software and associated // documentation files (the "Software"), to deal in the Software // without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to // whom the Software is furnished to do so, subject to the // following conditions: // // The copyright notice and this license shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. // //********************************************************************** samp_a_to_b.cpp0000644000076400007640000004036411063254442013015 0ustar brianbrian//====================================================================== // samp_a_to_b.cpp - sample program: shortest path from A to B // // This is a tiny sample program to show how aiParts problem-solving // classes can be used. These classes will be improved over time. // // Note: You can uncomment a line in the main() function so that // the program does not list each try. Or, you can uncomment // another line and see each option in each decision. // // The problem: // Given a number of nodes, // each of which is connected to one or more other nodes, // find the shortest path from one specified node to another. // // Each node-to-node option has a 'distance', which might // be in miles, or it could be dollars or travel-time-minutes - // something that is to be minimized. // // The problem is constructed using classes from samp_a_to_b.h: // a2bProblem a2bNode a2bOption // which are subclasses of: // aipHHProblem aipHHDecision aipHHOption // so the problem knows how to solve itself // using the High-Hope technique. (See aipHighHope.h) // // Developers/Contributers: // [BRM] Brian Marshall - Calgary - bmarshal@agt.net // // 07/11/29 [BRM] Removed class aipLog. // 05/11/15 [BRM] Removed version number from final output. // 05/11/01 [BRM] Parameters now specified on command line. // Logs the number of tries to get best solution. // Try-logging improved. // 05/10/19 [BRM] Sample A-to-B problem made bigger and harder. // Improved error-handling assembling the problem. // Simplified specifying 2-way links. // 05/09/29 [BRM] development begins // //---------------------------------------------------------------------- // Building and running the executable // // On Linux, you can call the compiler gcc or g++ (although on // Fedora, use g++ because gcc finds the wrong libraries)... // // gcc -o samp_a_to_b samp_a_to_b.cpp samp_a2b.cpp // aipHighHope.cpp \ // aipProblem.cpp aipDecision.cpp \ // aipEmotion.cpp aipPandemonium.cpp \ // aipBase.cpp aipGood.cpp // // In the example, above, spaces have been added after the // backshlashs - the Borland compiler does not like them at the ends // of lines, even in comments. // // The executable, samp_a_to_b, can be run on the command line. // // To direct the results into a file... // samp_a_to_b >samp_a_to_b_test_out.txt // //---------------------------------------------------------------------- // The sample problem (as of 0.8.4) // // [A]__ // / | // ___(R)_____[S] // / / / | // (V)__(U)_ / (T) // \ | \ / / // \ / [Q]_/ __(O) // (C)___/ \ / | // / \ [D]--(N) | // | | / | \ \__(P) // | |_(I) | \ // | ___/ | _(F)--(K) // | / | / | // (E)--[J]---[H] | // |\ \ \_(G)--(L)--(M) // | \___[X]_____/ \_ // | / \ \ // |__(Y) \__[W]____(Z) // |__ / / // \_[B]______/ // // This problem is harder than it looks. You can see the spatial // relationship between the nodes; the software only knows the // node-to-node distances (or costs or travel-time - something // to be minimized). // // Nodes on the best path from [A] to [B] are in square brackets. // // See the function assemble_new_problem() for the actual // definition of the sample problem. // //---------------------------------------------------------------------- // About trying to find a minimum... // // In this sample program, we want a solution with a minimum, // but the problem-solving software finds a maximum. // // Internally, distances are made negative (subtracted from zero) // so that the largest one, the least negative one, is considered // to be the best. // // Making the distances negative internally happens automatically // in the a2bOption constructor. // // The distance displayed to the user is automatically made positive // again by the function a2bOption::g_opt_usr(). // // //---------------------------------------------------------------------- // About number of tries... // // This program uses (classes that use) the High-Hope strategy, // which, at the top level, tries to solve the solution a number // of times and remembers the best one. // // The number of tries can be set in the code of the main function. // // In this fairly simple problem (26-nodes), 300 tries is ample. // The problem of finding a path from "A" to "B" appears to be // the hardest for this set of nodes; other problems ("B" to "A" // or "Y" to "P" require fewer tries. // // For harder problems, even very-hard problems, the number of tries // should probably be somewhere between 1000 and 5000. // //---------------------------------------------------------------------- // About the status... // // This is an early version. The existing techniques can be // improved - mostly by improving how different messages affect // the hope of decisions and options. This should be done before // the High-Hope technique is made more sophisticated. // // //---------------------------------------------------------------------- #include "samp_a2b.h" #include #include using namespace std; //---------------------------------------------------------------------- // Forward Declarations of Functions a2bProblem * assemble_new_problem(); void report_solution (long distance, a2bProblem *prob); int add_link (a2bProblem *problem, const char *from_label, const char *to_label, long distance); int add_2_links (a2bProblem *problem, const char *node1_label, const char *node2_label, long distance); //====================================================================== // a2bParam - Parameters for running this program class a2bParam : public aipBase { char m_from_label[21]; char m_to_label[21]; long m_num_try; int m_should_log_tries; int m_should_log_options; public: a2bParam() { m_from_label[0] = m_to_label[0] = '\0'; m_num_try = 0; m_should_log_tries = m_should_log_options = 0; } virtual ~a2bParam() {} void log_usage(); // write usage information to log int set (int argc, char *argv[]); // return zero on failure const char * from_label () const { return m_from_label; } const char * to_label () const { return m_to_label; } long num_try () const { return m_num_try; } int should_log_tries () const { return m_should_log_tries; } int should_log_options () const { return m_should_log_options; } }; //---------------------------------------------------------------------- // a2bParam log_usage - write usage information to log void a2bParam::log_usage() { log("\nUsage:\n"); log(" samp_a_to_b []\n"); log(" is optional - default is 300\n"); log(" number of solution attempts (1-5000)\n"); log(" 500 or 1000 or 3000 should find the best \n"); log(" solution (it is a chaotic system.\n"); log(" (optional parameter) - default is 0:\n"); log(" 0=result - best solution found\n"); log(" 1=tries - result of each try\n"); log(" 2=options - each option considered\n"); log(" note: 1 and 2 can produce a lot of output\n"); log("Examples:\n"); log(" samp_4_a_to_b A B 1000\n"); log(" samp_4_a_to_b A B 2000 >A_to_B_out.txt\n"); log(" samp_4_a_to_b S Y 500 1 >S_to_Y_out.txt\n"); log(" samp_4_a_to_b Q J 20 2 >Q_to_J_out.txt\n"); log("\n"); } //---------------------------------------------------------------------- // a2bParam set - set parameters from command line arguments // Return zero on failure. int a2bParam::set (int argc, char *argv[]) { if (argc == 1) return 0; // no args: print usage if (argc < 4 || argc > 5) { cout << "Error: bad number of command line arguments\n"; return 0; } if (strlen(argv[1]) > 20) { cout << "Error: from-label is too long ( > 20 characters)\n"; return 0; } aip_strncpy (m_from_label, argv[1], 20); if (strlen(argv[2]) > 20) { cout << "Error: to-label is too long ( > 20 characters)\n"; return 0; } aip_strncpy (m_to_label, argv[2], 20); m_num_try = atol(argv[3]); if (m_num_try < 1 || m_num_try > 5000) { cout << "Error: num-try must be between 1 and 5000\n"; return 0; } m_should_log_tries = m_should_log_options = 0; // default if (argc >= 5) { if (argv[4][0] == '0' && argv[4][1] == '\0') { m_should_log_tries = m_should_log_options = 0; } else if (argv[4][0] == '1' && argv[4][1] == '\0') { m_should_log_tries = 1; m_should_log_options = 0; } else if (argv[4][0] == '2' && argv[4][1] == '\0') { m_should_log_tries = m_should_log_options = 1; } else { cout << "Error: logging must be 0 or 1 or 2\n"; return 0; } } return 1; } //====================================================================== // Program main int main (int argc, char *argv[]) { a2bParam param; if ( ! param.set(argc,argv) ) { param.log_usage(); return 1; } a2bProblem *problem = assemble_new_problem(); if (!problem) { cout << "ABORT - Could not create problem\n\n"; return 1; } problem->set_num_try(param.num_try()); if ( param.should_log_tries() ) { problem->enable_log_tries(); } else { problem->disable_log_tries(); } if ( param.should_log_options() ) { problem->enable_log_options(); } else { problem->disable_log_options(); } long distance = problem->solve_a_to_b (param.from_label(), param.to_label()); if (distance == -1) { cout << "\nSolution could not be found.\n\n"; } else if (distance == -2) { cout << "\nStarting-node not found\n\n"; } else if (distance == -3) { cout << "\nEnding-node not found\n\n"; } int status = 0; if (distance > 0) { report_solution (distance, problem); } else { cout << "Solve-Failure: No solution was found.\n"; status = -1; } delete problem; return status; } //---------------------------------------------------------------------- // Report the solution to the problem void report_solution (long distance, a2bProblem *problem) { cout << "\nBest solution found - distance: " << distance; if (distance > 0) { cout << " (in " << problem->solve_stat()->num_tries_to_best() << " tries)"; } cout << "...\n"; a2bSolutionItr itr = problem->solution_iterator(); for ( a2bNode *n = itr.first(); n; n = itr.next() ) { cout << n->label() << " "; } cout << "\n\n"; cout << "The best path from A to B is:\n" << "A S Q D H J X W B distance = 1850\n\n"; } //---------------------------------------------------------------------- // assemble_new_problem a2bProblem * assemble_new_problem() { a2bProblem *problem = new a2bProblem; if (!problem) { cout << "ABORT - no new problem\n\n"; return 0; } const long num_node = 26; char *node_label[num_node] = { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" }; int problem_ok = 1; int ok1 = 1; // adding nodes starts ok for (long inode = 0; inodeset_owner(problem); problem->add_node(node); } // end of loop creating and attaching nodes if (problem_ok && !ok1) problem_ok = ok1; int ok2 = problem_ok; // Links are added in both directions if (ok2) ok2 = add_2_links (problem, "A", "R", 150); if (ok2) ok2 = add_2_links (problem, "A", "S", 200); if (ok2) ok2 = add_2_links (problem, "B", "W", 150); if (ok2) ok2 = add_2_links (problem, "B", "Y", 400); if (ok2) ok2 = add_2_links (problem, "B", "Z", 400); if (ok2) ok2 = add_2_links (problem, "C", "E", 550); if (ok2) ok2 = add_2_links (problem, "C", "I", 300); if (ok2) ok2 = add_2_links (problem, "C", "Q", 300); if (ok2) ok2 = add_2_links (problem, "C", "U", 300); if (ok2) ok2 = add_2_links (problem, "C", "V", 300); if (ok2) ok2 = add_2_links (problem, "D", "F", 250); if (ok2) ok2 = add_2_links (problem, "D", "H", 400); if (ok2) ok2 = add_2_links (problem, "D", "I", 150); if (ok2) ok2 = add_2_links (problem, "D", "N", 150); if (ok2) ok2 = add_2_links (problem, "D", "Q", 200); if (ok2) ok2 = add_2_links (problem, "E", "I", 500); if (ok2) ok2 = add_2_links (problem, "E", "J", 350); if (ok2) ok2 = add_2_links (problem, "E", "X", 400); if (ok2) ok2 = add_2_links (problem, "E", "Y", 450); if (ok2) ok2 = add_2_links (problem, "F", "G", 250); if (ok2) ok2 = add_2_links (problem, "F", "H", 300); if (ok2) ok2 = add_2_links (problem, "F", "K", 100); if (ok2) ok2 = add_2_links (problem, "G", "H", 150); if (ok2) ok2 = add_2_links (problem, "G", "L", 100); if (ok2) ok2 = add_2_links (problem, "G", "X", 300); if (ok2) ok2 = add_2_links (problem, "G", "Z", 350); if (ok2) ok2 = add_2_links (problem, "H", "J", 200); if (ok2) ok2 = add_2_links (problem, "J", "X", 150); if (ok2) ok2 = add_2_links (problem, "L", "M", 100); if (ok2) ok2 = add_2_links (problem, "N", "O", 150); if (ok2) ok2 = add_2_links (problem, "N", "P", 150); if (ok2) ok2 = add_2_links (problem, "O", "P", 150); if (ok2) ok2 = add_2_links (problem, "Q", "S", 300); if (ok2) ok2 = add_2_links (problem, "Q", "T", 200); if (ok2) ok2 = add_2_links (problem, "Q", "U", 250); if (ok2) ok2 = add_2_links (problem, "R", "S", 300); if (ok2) ok2 = add_2_links (problem, "R", "U", 150); if (ok2) ok2 = add_2_links (problem, "R", "V", 250); if (ok2) ok2 = add_2_links (problem, "S", "T", 200); if (ok2) ok2 = add_2_links (problem, "U", "V", 150); if (ok2) ok2 = add_2_links (problem, "W", "X", 250); if (ok2) ok2 = add_2_links (problem, "W", "Z", 300); if (ok2) ok2 = add_2_links (problem, "X", "Y", 100); if (problem_ok && !ok2) { cout << "ABORT - error adding link \n\n"; problem_ok = ok2; } if ( ! problem_ok ) { delete problem; return 0; } return problem; } //---------------------------------------------------------------------- // add_link - add option to a node, linking to another node. // Return 1 on success, 0 on failure. int add_link (a2bProblem *problem, const char *from_label, const char *to_label, long distance) { int status = problem->add_link(from_label, to_label, distance); if (status == -1) { cout << "ERROR: add_link: no new a2bOption\n"; } else if (status == -2) { cout << "ERROR: add_link could not find from-node " << from_label << aip_Endl; } else if (status == -3) { cout << "ERROR: add_link could not find to-node " << to_label << aip_Endl; } else if (status != 0) { cout << "ERROR: add_link unknown status code: " << status << aip_Endl; } return ( status ? 0 : 1 ); } //---------------------------------------------------------------------- // add_2_links - add 2 links, from node1 to node2 and // from node2 to node1, // both with the same distance. // Return 1 on success, 0 on failure. int add_2_links (a2bProblem *problem, const char *node1_label, const char *node2_label, long distance) { int ok = 1; if (ok) ok = add_link (problem, node1_label, node2_label, distance); if (ok) ok = add_link (problem, node2_label, node1_label, distance); return ok; } //====================================================================== samp_decision.cpp0000644000076400007640000001465511063254442013373 0ustar brianbrian//====================================================================== // samp_decision.cpp - trivial sample program using aipDecision.h // // The decision is whether a deer, eating grass in a meadow, // should run away. // // In this program, a deer-decision has two options: // Stay - which has a fear which is affected by messages // Run - which has a goodness // A series of messages is passed to the decision, which passes // them to each option. A message can contain information about // what the deer has just heard or smelled. // // The idea of using the emotion of fear is that it is easier to // define when the deer should become more afraid, than it is to // define when the deer should run away. // // In this trivial example, messages are provided for only a short // period of time, so the goodness associated with Run_Away is // constant. // // Run_Away goodness is quite negative - the deer needs to eat - // it can't run away every time the slightest sound or smell is // detected. Running away is also negative because it increases // the chance of getting separated from the other deer. // // Run_Away will only be chosen if the fear of staying becomes // worse than the Run goodness. // // In other applications, options and/or the decision might have // emotions, or there may be no emotions used at all. // // This program does not use aipHopeOption or aipHopeDecision. // The program samp_problem.cpp does use these classes to do // problem-solving. // // Developers/Contributers: // [BRM] Brian Marshall - Calgary - bmarshal@agt.net // // 05/11/09 [BRM] improved sample problem series of messages // 05/09/17 [BRM] development begins // //---------------------------------------------------------------------- // Building the executable // // On Linux, you can call the compiler gcc or g++ (although on // Fedora, use g++ because gcc finds the wrong libraries)... // // gcc -o samp_decision samp_decision.cpp samp_deer_fear.cpp \ // aipDecision.cpp aipEmotion.cpp \ // aipPandemonium.cpp aipBase.cpp aipGood.cpp // // In the example, above, spaces have been added after the // backshlashs - the Borland compiler does not like them at the ends // of lines, even in comments. // // //---------------------------------------------------------------------- #include "samp_deer_fear.h" #include using namespace std; //---------------------------------------------------------------------- // Forward Declarations of Functions DeerDecision * assemble_new_decision(); DeerMsg * get_msg (); //====================================================================== // Program main int main () { // We know about argc and argv, but the Borland compiler // does not like them if they are not used. DeerDecision *decision = assemble_new_decision(); if (!decision) { cout << "ABORT - Could not create decision\n\n"; return 1; } // This next pointer is just used for reporting aipFear *stay_fear = decision->stay_fear(); if (!stay_fear) { cout << "ABORT - decision stay-fear not set\n\n"; return 1; } cout << "\nA deer is eating grass. The deer repeatedly\n" << "has to decide whether to stay or run away.\n" << "We create a decsion-object and pass it a series\n" << "of messages that affect this decision.\n\n"; for (DeerMsg *msg = get_msg(); msg; msg = get_msg() ) { cout << "msg: " << msg->typ_description(); cout << " " << msg->subtyp_description(); decision->take_msg(msg); DeerOption *choice = decision->deer_decide(); if (!choice) { cout << "ABORT - failure to make choice\n\n"; break; } cout << " choice: " << choice->description() << " stay-fear: (" << stay_fear->g().numeric_value() << ") " << stay_fear->g().description() << "\n"; } cout << "\n"; delete decision; return 0; } //---------------------------------------------------------------------- // assemble_new_decision DeerDecision * assemble_new_decision() { DeerDecision *decision = new DeerDecision; if (!decision) { cout << "ABORT - no new decision\n\n"; return 0; } RunOption *run_option = new RunOption; if (!run_option) { cout << "ABORT - no new run_option\n\n"; return 0; } decision->add_deer_option(run_option); StayOption *stay_option = new StayOption; if (!stay_option) { cout << "ABORT - no new stay_option\n\n"; return 0; } decision->add_deer_option(stay_option); FearSmellWolf *fear_smell_wolf = new FearSmellWolf; if (!fear_smell_wolf) { cout << "ABORT - no new fear_smell_wolf\n\n"; return 0; } stay_option->fear()->add_aspect(fear_smell_wolf); FearHearNoise *fear_hear_noise = new FearHearNoise; if (!fear_hear_noise) { cout << "ABORT - no new fear_hear_noise\n\n"; return 0; } stay_option->fear()->add_aspect(fear_hear_noise); FearSeemsSafe *fear_seems_safe = new FearSeemsSafe; if (!fear_seems_safe) { cout << "ABORT - no new fear_seems_safe\n\n"; return 0; } stay_option->fear()->add_aspect(fear_seems_safe); return decision; } //---------------------------------------------------------------------- // get_msg - get the next message to be passed to the decision DeerMsg * get_msg () { static int num_msg = 21; static int next_msg = 0; static DeerMsg msg[] = { DeerMsg (Info_Seems_Safe, 0), DeerMsg (Info_Hear_Noise, Info_Weak), DeerMsg (Info_Seems_Safe, 0), DeerMsg (Info_Smell_Wolf, Info_Weak), DeerMsg (Info_Hear_Noise, Info_Weak), DeerMsg (Info_Seems_Safe, 0), DeerMsg (Info_Hear_Noise, Info_Weak), DeerMsg (Info_Hear_Noise, Info_Strong), DeerMsg (Info_Seems_Safe, 0), DeerMsg (Info_Seems_Safe, 0), DeerMsg (Info_Hear_Noise, Info_Weak), DeerMsg (Info_Smell_Wolf, Info_Medium), DeerMsg (Info_Hear_Noise, Info_Weak), DeerMsg (Info_Hear_Noise, Info_Medium), DeerMsg (Info_Smell_Wolf, Info_Weak), DeerMsg (Info_Hear_Noise, Info_Weak), DeerMsg (Info_Seems_Safe, 0), DeerMsg (Info_Seems_Safe, 0), DeerMsg (Info_Seems_Safe, 0), DeerMsg (Info_Seems_Safe, 0), DeerMsg (Info_Seems_Safe, 0), DeerMsg (Info_Seems_Safe, 0) }; if (next_msg == num_msg) return 0; return msg + next_msg++; } //====================================================================== samp_deer_fear.cpp0000644000076400007640000001005011063254442013473 0ustar brianbrian//====================================================================== // samp_deer_fear.cpp - function bodies for samp_deer_fear.h // // Developers/Contributers: // [BRM] Brian Marshall - Calgary - bmarshal@agt.net // // 05/09/17 [BRM] development begins // //---------------------------------------------------------------------- #include "samp_deer_fear.h" //====================================================================== // DeerDecision // //---------------------------------------------------------------------- // add_deer_option void DeerDecision::add_deer_option (DeerOption *x) { add_option(x); if (x->option_fear()) m_stay_fear_ptr = x->option_fear(); } //====================================================================== // StayOption // //---------------------------------------------------------------------- // g_opt aipG StayOption::g_opt() { return m_fear ? m_fear->g() : aipNeutral; } //====================================================================== // FearSmellWolf // //---------------------------------------------------------------------- // take_msg void FearSmellWolf::take_msg (aipMsg *m) { if (!m || m->typ() != Info_Smell_Wolf) return; if (m->subtyp() == Info_Weak) { emotion()->strengthen(aipIntensity_A_Fair_Bit); } else if (m->subtyp() == Info_Medium) { emotion()->strengthen(aipIntensity_Quite_A_Bit); } else if (m->subtyp() == Info_Strong) { emotion()->strengthen(aipIntensity_A_Lot); } } //====================================================================== // FearHearNoise // //---------------------------------------------------------------------- // take_msg void FearHearNoise::take_msg (aipMsg *m) { if (!m || m->typ() != Info_Hear_Noise) return; if (emotion()->g() < aipFairlyBad) { // jumpy if (m->subtyp() == Info_Weak) { emotion()->strengthen(aipIntensity_Somewhat); } else if (m->subtyp() == Info_Medium) { emotion()->strengthen(aipIntensity_A_Fair_Bit); } else if (m->subtyp() == Info_Strong) { emotion()->strengthen(aipIntensity_Quite_A_Bit); } } else if (emotion()->g() < aipSomewhatBad) { // wary if (m->subtyp() == Info_Weak) { emotion()->strengthen(aipIntensity_A_Little); } else if (m->subtyp() == Info_Medium) { emotion()->strengthen(aipIntensity_Somewhat); } else if (m->subtyp() == Info_Strong) { emotion()->strengthen(aipIntensity_A_Fair_Bit); } } else { if (m->subtyp() == Info_Weak) { emotion()->strengthen(aipIntensity_Slightly); } else if (m->subtyp() == Info_Medium) { emotion()->strengthen(aipIntensity_A_Little); } else if (m->subtyp() == Info_Strong) { emotion()->strengthen(aipIntensity_Somewhat); } } } //====================================================================== // FearSeemsSafe // //---------------------------------------------------------------------- // take_msg void FearSeemsSafe::take_msg (aipMsg *m) { if (!m || m->typ() != Info_Seems_Safe) return; emotion()->weaken(aipIntensity_A_Little); } //====================================================================== // DeerMsg - message of/in a deer that is passed to the decision // //---------------------------------------------------------------------- // typ_description const char * DeerMsg::typ_description () { if (typ() == Info_Seems_Safe) { return "Seems Safe"; } else if (typ() == Info_Smell_Wolf) { return "Smell Wolf"; } else if (typ() == Info_Hear_Noise) { return "Hear Noise"; } return " "; } //---------------------------------------------------------------------- // description to log const char * DeerMsg::subtyp_description () { if (typ() == Info_Seems_Safe) { return " "; } else { if (subtyp() == Info_Weak) { return "(weak) "; } else if (subtyp() == Info_Medium) { return "(medium)"; } else if (subtyp() == Info_Strong) { return "(strong)"; } } return " "; } //====================================================================== samp_deer_fear.h0000644000076400007640000001351711063254442013153 0ustar brianbrian//********************************************************************** // samp_deer_fear.h - sample program using aipDecision.h, aipEmotion // // The decision is whether a deer, eating grass in a meadow, // should run away. // // In these classes, a deer-decision has two options: // Stay - which has a fear which is affected by messages // Run_Away - which has a goodness // A series of messages is passed to the decision, which passes // them to each option. A message can contain information about // what the deer has just heard or smelled. // // The idea of using the emotion of fear is that it is easier to // define when the deer should become more afraid, than it is to // define when the deer should run away. // // In this trivial example, messages are provided for only a short // period of time, so the goodness associated with Run_Away is // constant. // // Run_Away goodness is quite negative - the deer needs to eat - // it can't run away every time the slightest sound or smell is // detected. Running away is also negative because it increases // the chance of getting separated from the other deer. // // Run_Away will only be chosen if the Stay fear goodness is more // negative than the Run_Away goodness. // // Developers/Contributers: // [BRM] Brian Marshall - Calgary - bmarshal@agt.net // // 05/09/17 [BRM] development begins // //---------------------------------------------------------------------- #ifndef samp_deer_h_guard #define samp_deer_h_guard #include "aipDecision.h" //---------------------------------------------------------------------- // Forward Declaration of Classes - subclassing shown by indentation // class aipBase; ( in aipBase.h ) // class aipDemon; ( in aipPandemonium.h ) // class aipDecision; ( in aipDecision.h ) class DeerDecision; // decision to run or stay // class aipOption; ( in aipDecision.h ) class DeerOption; // option in the decision class RunOption; // option to run class StayOption; // option stay // class aipAspect; // aspect in the fear emotion class FearSmellWolf; // fear of staying: smell-wolf class FearHearNoise; // fear of staying: hear-noise // class aipMsg; ( in aipBase.h ) class DeerMsg; // info about hearing, smelling //---------------------------------------------------------------------- // Constants // run away if staying is worse than... const aipG g_Run_Away = aipPrettyBad; // message typ const short Info_Seems_Safe = 1; const short Info_Smell_Wolf = 2; const short Info_Hear_Noise = 3; // message subtyp const short Info_Weak = 1; const short Info_Medium = 2; const short Info_Strong = 3; //---------------------------------------------------------------------- // DeerDecision class DeerDecision : public aipDecision { aipFear * m_stay_fear_ptr; // for reporting (if not zero) public: DeerDecision () { m_stay_fear_ptr = 0; } virtual ~DeerDecision () {} void add_deer_option (DeerOption *x); // sets m_stay_fear_ptr DeerOption * deer_decide () { return (DeerOption *) decide(); } aipFear * stay_fear () const { return m_stay_fear_ptr; } }; //---------------------------------------------------------------------- // DeerOption class DeerOption : public aipOption { // a base class public: DeerOption () {} virtual ~DeerOption () {} virtual aipG g() { return aipNeutral; } virtual aipFear * option_fear () const { return 0; } virtual const char * description () const =0; }; // - - - - - - - - - - - - - - - - - - - - - - - - - - - // Option: Run_Away class RunOption : public DeerOption { public: RunOption () {} virtual ~RunOption () {} virtual aipG g_opt() { return g_Run_Away; } const char * description () const { return "Run_Away"; } }; // - - - - - - - - - - - - - - - - - - - - - - - - - - - // Option: Stay class StayOption : public DeerOption { aipFear * m_fear; public: StayOption () { m_fear = new aipFear; } virtual ~StayOption () { if (m_fear) delete m_fear; } virtual void take_msg (aipMsg *m) { m_fear->take_msg(m); } aipFear * fear () { return m_fear; } virtual aipG g_opt(); virtual aipFear * option_fear () const { return m_fear; } const char * description () const { return "Stay"; } }; //---------------------------------------------------------------------- // Fear aspects associated with the Stay Option // // A stay-option has a fear, and this fear has aspects. // - - - - - - - - - - - - - - - - - - - - - - - - - - - class FearSmellWolf : public aipAspect { public: FearSmellWolf () {} virtual ~FearSmellWolf () {} virtual void take_msg (aipMsg *m); }; // - - - - - - - - - - - - - - - - - - - - - - - - - - - class FearHearNoise : public aipAspect { public: FearHearNoise () {} virtual ~FearHearNoise () {} virtual void take_msg (aipMsg *m); }; // - - - - - - - - - - - - - - - - - - - - - - - - - - - class FearSeemsSafe : public aipAspect { public: FearSeemsSafe () {} virtual ~FearSeemsSafe () {} virtual void take_msg (aipMsg *m); }; //====================================================================== // DeerMsg - message of/in a deer that is passed to the decision // // In this subclass, the message typ and subtyp carry information. class DeerMsg : public aipMsg { public: DeerMsg (short condition_type, short condition_strength) : aipMsg(condition_type,condition_strength) {} virtual ~DeerMsg () {} const char * typ_description (); const char * subtyp_description (); }; //====================================================================== #endif //********************************************************************** samp_good.cpp0000755000076400007640000000662111063254442012523 0ustar brianbrian//====================================================================== // samp_good.cpp - sample program using aipGood.h // // Developers/Contributers: // [BRM] Brian Marshall - Calgary - bmarshal@agt.net // // 05/09/07 [BRM] removed importance and likelihood // 05/07/31 [BRM] Reworked this sample program. // //---------------------------------------------------------------------- // Building the executable // // This is very generic, standard C++. The files: // aipGood.h aipGood.cpp aipGood_samp.cpp // // On Linux, you can call the compiler gcc or g++ (although on // Fedora, use g++ because gcc finds the wrong libraries)... // // gcc -osamp_good samp_good.cpp aipGood.cpp // // //---------------------------------------------------------------------- #include #include "aipGood.h" using namespace std; int main () { // We know about argc and argv, but the Borland compiler // does not like them if they are not used. // There is nothing subtle about the way the following code is // written - it just illustrates different ways of doing things. // Note that aipG is a typedef for aipGoodness //------------------------------------------------ // Having to get up at 7:00 is Fairly-Bad, // but being watch the News on tv is Fairly-Good, // and the being-late-for-work factor is Slightly-Bad // because it is very important, but highly-unlikely. aipGoodness g_sleep_to_700 = aipFairlyBad; aipG g_watch_news = aipFairlyGood; g_sleep_to_700 += g_watch_news; aipG g_700_late_for_work = aipSlightlyBad; g_sleep_to_700 = g_sleep_to_700 + g_700_late_for_work; //------------------------------------------------ // Having to get up at 7:30 is Slightly-Bad, // and there is radio news that is Somewhat-Good, // and the being-late-for-work factor is Somewhat-Bad // because being late is a little more likely. aipGoodness g_sleep_to_730 = aipSlightlyBad; aipG g_radio_news = aipSomewhatGood; g_sleep_to_730 += g_radio_news; aipG g_730_late_for_work = aipSomewhatBad; g_sleep_to_730 += g_730_late_for_work; //------------------------------------------------ // Having to get up at 8:00 is Good, // and the being-late-for-work factor is Quite-Bad, // because there is a greater chance of being late. aipGoodness g_sleep_to_800 = aipGood; g_sleep_to_800 += aipSomewhatGood; // Radio news. g_sleep_to_800 += aipQuiteBad; // Maybe late for work. //------------------------------------------------ // Compare the results. cout << "\n"; cout << "Getting up at 7:00 is " << g_sleep_to_700.description() << "\n"; cout << "Getting up at 7:30 is " << g_sleep_to_730.description() << "\n"; cout << "Getting up at 8:00 is " << g_sleep_to_800.description() << "\n"; cout << "\n"; cout << "Note that, individually, these values " << "may not be meaningful - \n"; cout << " it is the difference between them " << "that is useful...\n"; cout << "\n"; if (g_sleep_to_700 > g_sleep_to_730 && g_sleep_to_700 > g_sleep_to_800) { cout << "Geting up at 7:00 is best.\n"; } else if (g_sleep_to_730 > g_sleep_to_700 && g_sleep_to_730 > g_sleep_to_800) { cout << "Geting up at 7:30 is best.\n"; } else { cout << "Geting up at 8:00 is best.\n"; } cout << "\n"; return 0; } //====================================================================== samp_pandemonium.cpp0000644000076400007640000001476511063254442014114 0ustar brianbrian//====================================================================== // samp_pandemonium.cpp - sample program using aipPandemonium.h // // This is a trivial example that illustrates that a pandemonium // is really just a first-in-last-out list (a queue). It... // - puts words into two pandemoniums (sharing some words) // - sends a message to each word, telling it to make its // first letter upper case // - Uses iterators to write the words to standard output // // Developers/Contributers: // [BRM] Brian Marshall - Calgary - bmarshal@agt.net // // 08/01/01 [BRM] removed delete_deomon() // 05/11/01 [BRM] changed because pandemonium now queue (not stack) // 05/09/11 [BRM] development begins // //---------------------------------------------------------------------- // Building the executable // // On Linux, you can call the compiler gcc or g++ (although on // Fedora, use g++ because gcc finds the wrong libraries)... // // gcc -o samp_pandemonium samp_pandemonium.cpp aipPandemonium.cpp \ // aipBase.cpp aipGood.cpp // // In the example, above, spaces have been added after the // backshlashs - the Borland compiler does not like them at the ends // of lines, even in comments. // //---------------------------------------------------------------------- #include "aipPandemonium.h" #include #include using namespace std; // - - - - - - - - - - - - - - - - - - - - - - - - // Initial Declarations and constants static const long Upcase_First_Char = 1; // a new message-type code // Word is a subclass of aipDemon so it can go into a pandemonium // (and handle messages) class Word : public aipDemon { char m_word[61]; public: Word (const char *w) { strcpy(m_word,w); } virtual ~Word () {} char * str () { return m_word; } virtual void take_msg (aipMsg *m) { if (m->typ() == Upcase_First_Char) { if (m_word[0] >= 'a' && m_word[0] <= 'z') m_word[0] -= ('a'-'A'); } } }; class WordItr : public aipDemonItr { public: WordItr (aipPandemonium *p) : aipDemonItr(p) {} virtual ~WordItr () {} Word * first() { return (Word*)aipDemonItr::first(); } Word * last() { return (Word*)aipDemonItr::last(); } Word * next() { return (Word*)aipDemonItr::next(); } Word * prev() { return (Word*)aipDemonItr::prev(); } }; // - - - - - - - - - - - - - - - - - - - - - - - - // Quad has a 4-part key - the list will be ordered class Quad : public aipDemon { long m_w; long m_x; long m_y; long m_z; public: Quad (long w, long x, long y, long z) { m_w = w; m_x = x; m_y = y; m_z = z; } ~Quad () {} virtual long num_keys (void) const { return 4; } virtual long key1 (void) const { return m_w; } virtual long key2 (void) const { return m_x; } virtual long key3 (void) const { return m_y; } virtual long key4 (void) const { return m_z; } void write () const { cout << m_w << "/" << m_x << "/" << m_y << "/" << m_z; } }; class QuadItr : public aipDemonItr { public: QuadItr (aipPandemonium *p) : aipDemonItr(p) {} virtual ~QuadItr () {} Quad * first() { return (Quad*)aipDemonItr::first(); } Quad * last() { return (Quad*)aipDemonItr::last(); } Quad * next() { return (Quad*)aipDemonItr::next(); } Quad * prev() { return (Quad*)aipDemonItr::prev(); } Quad * find(long key1, long key2 =-999999, long key3 =-999999, long key4 =-999999) { return (Quad*)aipDemonItr::find(key1, key2, key3, key4); } }; // - - - - - - - - - - - - - - - - - - - - - - - - // The main entry-point int main () { // We know about argc and argv, but the Borland compiler // does not like them if they are not used. // Pandemoniums and Demons MUST be created with new. // words we are going to share Word *nl = new Word("\n"); Word *ep = new Word("!"); // line1 aipPandemonium *line1 = new aipPandemonium; line1->add (nl); line1->add ( new Word("hello") ); // another way line1->add ( new Word("world") ); line1->add (ep); line1->add (nl); WordItr itr1(line1); for ( Word *w = itr1.first(); w; w = itr1.next() ) { cout << w->str() << " "; } cout << "\n"; // line1a aipPandemonium *line1a = new aipPandemonium; line1a->add ( new Word("single") ); cout << "A pandemonium may contain only one item: "; WordItr itr1a(line1a); for ( Word *w = itr1a.first(); w; w = itr1a.next() ) { cout << w->str() << " "; } cout << "\n"; // line2 aipPandemonium *line2 = new aipPandemonium; line2->add (nl); line2->add ( new Word("pandemoniums") ); line2->add ( new Word("can") ); line2->add ( new Word("share") ); line2->add ( new Word("demons") ); line2->add (ep); line2->add (nl); line2->add (nl); // send a message to each pandemonium aipMsg msg(Upcase_First_Char); line1->take_msg(&msg); line2->take_msg(&msg); WordItr itr2(line2); for ( Word *w = itr2.first(); w; w = itr2.next() ) { cout << w->str() << " "; } cout << "\n"; cout << "Can also iterate backwards...\n"; for ( Word *w = itr2.last(); w; w = itr2.prev() ) { cout << w->str() << " "; } cout << "\n"; // line3 aipPandemonium *line3 = new aipPandemonium; line3->add ( new Quad(3,4,3,4) ); line3->add ( new Quad(2,2,3,1) ); line3->add ( new Quad(2,3,3,5) ); line3->add ( new Quad(3,3,3,3) ); line3->add ( new Quad(1,2,1,5) ); line3->add ( new Quad(3,3,3,3) ); line3->add ( new Quad(1,1,2,6) ); line3->add ( new Quad(1,1,2,4) ); Quad *q; aipPandemonium *line4 = new aipPandemonium; line4->set_is_distinct(); cout << "Quads in order:\n"; QuadItr itr3(line3); for ( q = itr3.first(); q; q = itr3.next() ) { cout << " "; q->write(); line4->add(q); } cout << "\n\n"; // line4 cout << "Distinct Quads in order:\n"; QuadItr itr4(line4); for ( q = itr4.first(); q; q = itr4.next() ) { cout << " "; q->write(); } cout << "\n\n"; cout << "Can search for an entry (by one or more keys)\n" << "and then print out remaining quads\n" << "(in this case, beginning with 2/3/3/5)...\n"; q = itr4.first(); // get to start of list if (!q) cout << "Error: itr4.first() returns zero\n"; q = itr4.find(2,3,3,5); for ( ; q; q = itr4.next() ) { cout << " "; q->write(); } cout << "\n\n"; delete line1; // and its contents (links and demons) delete line1a; delete line2; delete line3; delete line4; return 0; } //====================================================================== samp_time.cpp0000755000076400007640000001547411063254442012537 0ustar brianbrian//====================================================================== // samp_time.cpp - sample program using aipTime.h // // Developers/Contributers: // [BRM] Brian Marshall - Calgary - bmarshal@agt.net // // 08/02/18 [BRM] Began development // //---------------------------------------------------------------------- // Building the executable // // This is very generic, standard C++. The files: // aipTime.h aipTime.cpp aipTime_samp.cpp // // On Linux, you can call the compiler gcc or g++ (although on // Fedora, use g++ because gcc finds the wrong libraries)... // // gcc -osamp_time samp_time.cpp aipTime.cpp // // //---------------------------------------------------------------------- #include #include "aipTime.h" using namespace std; int main () { // We know about argc and argv, but the Borland compiler // does not like them if they are not used. // This is really just a lot of simple examples to do // a moderate amount of testing. //------------------------------------------------ // Date and Time cout << "\n Date and Time\n\n"; long num, tyear, tmon, tday, thour, tmin, tsec; char smon[4], sweekday[4]; aipTime t1(1995,7,06); if (!t1.is_valid()) cout << "Error 1\n"; char st1[31]; t1.dd_mon_yyyy(st1); aipTime t3(19960229); if (!t3.is_valid()) cout << "Error 3\n"; char st3[31]; t3.dd_mon_yyyy(st3); if (t1 < t3) { cout << st1 << " is less than " << st3 << "\n"; } else { cout << "Error 3a\n"; } aipTime t4(t3); if (!t4.is_valid()) cout << "Error 4\n"; char st4[31]; t4.dd_mon_yyyy(st4); long ldate = t4.yyyymmdd(); aipTime t4a(ldate); if (t4 != t3) { cout << "Error 4a\n"; } else if (t4 != t4a) { cout << "Error 4b\n"; } char st4a[31]; t4a.dd_mm_yy(st4a); char st4b[31]; t4a.dd_mm_yyyy(st4b); aipTime t4c("1996-02-29"); if (!t4c.is_valid()) cout << "Error 4c\n"; char st4c[31]; t4c.yyyy_mm_dd(st4c); cout << st4 << " = " << st4a << " = " << st4b << " = " << ldate << " = " << st4c << "\n"; aipTime t5("29 Feb 2000 16:30"); if (!t5.is_valid()) cout << "Error 5\n"; char st5[31]; t5.dd_mon_yyyy_hh_mm(st5); if (t5 > t4) { cout << st5 << " is greater than " << st4 << "\n"; } else { cout << "Error 5a\n"; } aipTime t7("23/Aug/2007_16:30"); if (!t7.is_valid()) cout << "Error 7\n"; char st7[31]; t7.dd_mon_yyyy_hh_mm(st7); aipTime t8("23-08-2007 4:30 pm"); if (!t8.is_valid()) cout << "Error 8\n"; char st8[31]; t8.dd_mon_yyyy_hh_mm(st8); if (t8 == t7) { cout << st8 << " equals " << st7 << "\n"; } else { cout << "Error 8a: " << st8 << " != " << st7 << "\n"; } aipTime t9("23 Aug 07 4:30 am"); if (!t9.is_valid()) cout << "Error 9\n"; char st9[31]; t9.dd_mon_yyyy_hh_mm(st9); if (t9 < t8) { cout << st9 << " is less than " << st8 << "\n"; } else { cout << "Error 9a\n"; } t9 = t8; t9.dd_mon_yyyy_hh_mm(st9); cout << "Assignment: " << st9 << " now equals " << st8 << "\n"; if (t9 != t8) cout << "Error 9b\n"; cout << "hhmm of " << st9 << " is " << t9.hhmm() << "\n"; aipTime t10("11 jan 2008 18:30"); if (!t10.is_valid()) cout << "Error 10\n"; char st10[31]; t10.dd_mon_yyyy_hh_mm(st10); t10.get(&tyear, &tmon, &tday, &thour, &tmin, &tsec, smon, sweekday); if (tmon != 1) cout << "Error 10a\n"; cout << "11/Jan/2008_18:30 is " << st10 << " is " << sweekday << " " << tday << " " << smon << " " << tyear << " " << thour << ":"; if (tmin < 10) cout << "0"; cout << tmin << "\n"; num = t10.days_since_sunday(); cout << " which is " << num << " days since Sunday\n"; num = t10.days_since_jan1(); cout << " and " << num << " days since Jan 1\n"; t10.add_hours(3); t10.dd_mon_yyyy_hh_mm(st10); cout << st10 << " is 3 hours later\n"; aipTime t11(2008,12,27,23,55); if (!t11.is_valid()) cout << "Error 11\n"; char st11[31]; t11.dd_mon_yyyy_hh_mm(st11); aipTime t12 = t11; if (!t12.is_valid()) cout << "Error 12\n"; t12.add_days(4); char st12[31]; t12.dd_mon_yyyy_hh_mm(st12); long dday = t12.days_since(t11); cout << st12 << " is " << dday << " days since " << st11 << "\n"; aipTime t12a = t12; t12a.add_seconds(600); char st12a[31]; t12a.dd_mon_yyyy_hh_mm(st12a); long dmin = t12a.minutes_since(t12); long ahour = t12a.hours_after(t12); long aday = t12a.days_after(t12); cout << st12a << " is " << dmin << " minutes since " << st12 << "\n"; cout << " (also " << ahour << " hour after" << " and " << aday << " day after)\n"; long yr1, yr2; yr1 = 1999; yr2 = 2000; cout << yr1 << " is"; if ( ! aip_is_leap_year(yr1) ) cout << " not"; cout << " a leap year; "; cout << yr2 << " is"; if ( ! aip_is_leap_year(yr2) ) cout << " not"; cout << " a leap year\n"; yr1 = 2008; yr2 = 2009; cout << yr1 << " is"; if ( ! aip_is_leap_year(yr1) ) cout << " not"; cout << " a leap year; "; cout << yr2 << " is"; if ( ! aip_is_leap_year(yr2) ) cout << " not"; cout << " a leap year\n"; //------------------------------------------------ cout << "\n Time of Day\n\n"; aipTimeOfDay tod1(16,30); // 4:30 pm on a 24 hour clock char stod1[11]; tod1.hh_mm (stod1); aipTimeOfDay tod2(tod1); char stod2[11]; tod2.hh_mm (stod2); char stod2a[11]; tod2.hh_mm_ss (stod2a); // insert format aipTimeOfDay tod3("16:45"); char stod3[11]; tod3.hh_mm (stod3); aipTimeOfDay tod4("09:00 pm"); char stod4[11]; tod4.hh_mm_12 (stod4); aipTimeOfDay tod5("09:00 AM"); char stod5[11]; tod5.hh_mm_12 (stod5); aipTimeOfDay tod6("9:00 am"); char stod6[11]; tod6.hh_mm_12 (stod6); if (tod1 == tod2) { cout << stod1 << " == " << stod2 << " == " << stod2a << "\n"; } else { cout << "ERROR with == operator\n"; } if (tod4 > tod3) { cout << stod4 << " > " << stod3 << "\n"; } else { cout << "ERROR with > operator\n"; } if (tod5 < tod3 && tod6 < tod2) { cout << stod5 << " < " << stod3 << " and " << stod6 << " < " << stod2 << "\n"; } else { cout << "ERROR with < operator\n"; } aipTimeOfDay tod7(0,0); tod7 = tod6; char stod7[11]; tod7.hh_mm_12 (stod7); cout << "Assignment: " << stod7 << " now equals " << stod6 << "\n"; aipTimeOfDay tod8 = tod7; tod8.add_hours(4); char stod8[11]; tod8.hh_mm (stod8); long dhour = tod8.hours_since(tod7); cout << stod8 << " is " << dhour << " hours after " << stod7 << "\n"; aipTimeOfDay tod8a = tod8; tod8a.add_minutes(15); tod8a.hh_mm (stod8); long dminute = tod8a.minutes_since(tod8); cout << stod8 << " is " << dminute << " minutes after that" << "\n"; //------------------------------------------------ cout << "\n"; //------------------------------------------------ return 0; } //======================================================================