aipBase.cpp0000644000076400007640000003130011104463242012100 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.h0000755000076400007640000002752611104463242011567 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.cpp0000644000076400007640000001101111104463242012760 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.h0000755000076400007640000001505711104463242012446 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.cpp0000644000076400007640000003622011104463242012646 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.h0000755000076400007640000003301211104463242012312 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.cpp0000644000076400007640000001143311104463242012123 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.h0000755000076400007640000002171311104463242011575 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.cpp0000644000076400007640000006737711104463242012750 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.h0000755000076400007640000006370511104463242012407 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.h0000755000076400007640000000550111104463242013003 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.cpp0000644000076400007640000002525711104463242013520 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.h0000755000076400007640000002133411104463242013160 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.cpp0000644000076400007640000000557211104463242012642 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.h0000755000076400007640000001061611104463242012305 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. // //********************************************************************** aipReqRes.cpp0000664000076400007640000004715211104463242012445 0ustar brianbrian//********************************************************************** // aipReqRes.cpp - function bodies for aipReqRes.h // // Copyright (c) 2008 Brian Marshall // // See the license at end of this file. // // Developers/Contributers: // [BRM] Brian Marshall - Calgary - bmarshal@agt.net // // 08/01/21 [BRM] began development: split out layer from rr_solve // //---------------------------------------------------------------------- #include "aipReqRes.h" #include #include using namespace std; //====================================================================== // aiprrReqImp - importance of a requirement // //---------------------------------------------------------------------- // take_msg - take and react to a message void aiprrReqImp::take_msg (aipMsg *mm) { aipHHImportance::take_msg(mm); aiprrMsg *m = (aiprrMsg *)mm; if (m->typ() == HH_Starting_New_Try) { m_left_bhnd = 0; } } //---------------------------------------------------------------------- // Note that a decision for this requirement has been decided. void aiprrReqImp::note_aiprr_decide (aiprrDecision *d, aiprrOption *o) { // o (ptr to an option) may be null if (d && o) { m_left_bhnd -= 1; } } //====================================================================== // aiprrProblem // //---------------------------------------------------------------------- // Constructor aiprrProblem::aiprrProblem () { m_requirements = new aipPandemonium; m_resources = new aipPandemonium; m_should_log_options = 0; } //---------------------------------------------------------------------- // Destructor aiprrProblem::~aiprrProblem () { if (m_requirements) delete m_requirements; if (m_resources) delete m_resources; } //---------------------------------------------------------------------- // add_requirement void aiprrProblem::add_requirement (aiprrRequirement *x) { if (!x) return; m_requirements->add (x); } //---------------------------------------------------------------------- // add_resource void aiprrProblem::add_resource (aiprrResource *x) { if (!x) return; m_resources->add (x); } //---------------------------------------------------------------------- // add_decision void aiprrProblem::add_decision (aiprrDecision *x) { add_hh_decision(x); } //---------------------------------------------------------------------- // In a try, choose the next decision to decide. aipHHDecision * aiprrProblem::next_decision() { aiprrReqDay *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(); aiprrDecision * best_dcsn = 0; long best_dcsn_imp = -999999999; aiprrRequirementItr itr = requirement_iterator(); for ( aiprrRequirement * req = itr.first(); req; req = itr.next() ) { rd = req->first_unsatisfied_reqday(); if (rd) { aiprrDecision *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 aiprrRequirementItr aiprrProblem::requirement_iterator() const { aiprrRequirementItr i(m_requirements); return i; } //---------------------------------------------------------------------- // resource_iterator - return an iterator: resources in the problem aiprrResourceItr aiprrProblem::resource_iterator() const { aiprrResourceItr i(m_resources); return i; } //---------------------------------------------------------------------- // decision_iterator - return an iterator: decisions in the problem aiprrDecisionItr aiprrProblem::decision_iterator() const { aiprrDecisionItr i(decision_pandemonium()); return i; } //---------------------------------------------------------------------- // too_bad_to_go_on aipG aiprrProblem::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 aiprrProblem::log_this_try () { aipHHSolveStat *ss = solve_stat(); cout << "\n===== Try " << ss->i_try()+1 << "...\n"; aiprrRequirement *req_prev = 0; aipTime day_prev = aipTime(19010101); aiprrDecisionItr itr = decision_iterator(); for ( aiprrDecision * dcsn = itr.first(); dcsn; dcsn = itr.next() ) { aiprrReqDay *reqday = dcsn->reqday(); aiprrRequirement *req = reqday->req(); aipTime day = reqday->day(); aiprrOption *opt = dcsn->aiprr_opt_cur(); aiprrResource *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 aiprrProblem::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 aiprrProblem::note_decide (aipHHDecision *dd, aipHHOption *oo) { if (!dd) return; aipHHProblem::note_decide (dd, oo); aiprrDecision *d = (aiprrDecision*)dd; aiprrOption *o = (aiprrOption*)oo; aiprrReqDay *reqd = d->reqday(); if (!reqd) return; aiprrRequirement *req = reqd->req(); if (!req) return; req->note_aiprr_decide (d, o); reqd->note_aiprr_decide (d, o); if (o) { // if an option was chosen aiprrResDay *resd = o->resday(); if (!resd) return; aiprrResource *res = resd->res(); if (!res) return; res->note_aiprr_decide (d, o); resd->note_aiprr_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. //aiprrReqDay * aiprrProblem::trivial_reqday () const { // aiprrReqDay *rd; // aiprrRequirementItr itr = requirement_iterator(); // for ( aiprrRequirement *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 aiprrProblem::solve_rr_problem () { aipG g = solve(); // high-hope solve if (g.is_forbidden()) return 0; return 1; } //====================================================================== // aiprrDecision // //---------------------------------------------------------------------- // Constructor aiprrDecision::aiprrDecision (aiprrReqDay *a_reqday, long num_to_decide) : aipHHDecision(num_to_decide) { m_reqday = a_reqday; } //---------------------------------------------------------------------- // Destructor aiprrDecision::~aiprrDecision () {} //---------------------------------------------------------------------- // add_aiprr_option void aiprrDecision::add_aiprr_option (aiprrOption *o) { add_hh_option(o); if ( o && o->resday() ) o->resday()->add_opt(o); } //---------------------------------------------------------------------- // Return the importance of this decision long aiprrDecision::imp () const { if (!m_reqday) return -999999; aiprrRequirement *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 aiprrDecision::take_msg (aipMsg *mm) { aipHHDecision::take_msg(mm); // pass message to parent class // aiprrMsg *m = (aiprrMsg *)mm; // if (m->typ() == HH_Starting_New_Try) { // reset_is_trivial(); // } } //====================================================================== // aiprrOption - an resource working a requirement on a day // //---------------------------------------------------------------------- // Constructor aiprrOption::aiprrOption(aiprrResDay *a_resday, aipG g_static) : aipHHOption(g_static) { m_resday = a_resday; m_g_dynamic = aipNeutral; } //---------------------------------------------------------------------- // Destructor aiprrOption::~aiprrOption() {} //---------------------------------------------------------------------- // take_msg - take and react to a message void aiprrOption::take_msg (aipMsg *mm) { aipHHOption::take_msg(mm); // pass message to parent class aiprrMsg *m = (aiprrMsg *)mm; if (m->typ() == HH_Starting_New_Try) { m_g_dynamic = aipNeutral; } } //---------------------------------------------------------------------- // set_g_dynamic void aiprrOption::set_g_dynamic (aipG x) { m_g_dynamic = x; } //---------------------------------------------------------------------- // add_g_dynamic void aiprrOption::add_g_dynamic (aipG x) { m_g_dynamic += x; } //---------------------------------------------------------------------- // g_opt aipG aiprrOption::g_opt() { return aipHHOption::g_opt() + g_user_constant() + m_g_dynamic; } //---------------------------------------------------------------------- // g_opt_cmp aipG aiprrOption::g_opt_cmp() { return g_user_constant() + m_g_dynamic; } //---------------------------------------------------------------------- // g_opt_usr aipG aiprrOption::g_opt_usr() { return g_user_constant() + m_g_dynamic; } //====================================================================== // aiprrRequirement // //---------------------------------------------------------------------- // Constructor aiprrRequirement::aiprrRequirement (long a_id, aiprrProblem *a_prob) { m_id = a_id; m_prob = a_prob; m_reqdays = new aipPandemonium; m_hope = new aipHHHope; } //---------------------------------------------------------------------- // Destructor aiprrRequirement::~aiprrRequirement () { if (m_reqdays) delete m_reqdays; if (m_hope) delete m_hope; } //---------------------------------------------------------------------- // add_reqday void aiprrRequirement::add_reqday (aiprrReqDay *x) { if (!x) return; m_reqdays->add (x); } //---------------------------------------------------------------------- // take_msg - take and react to a message void aiprrRequirement::take_msg (aipMsg *mm) { aiprrMsg *m = (aiprrMsg *)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 aiprrRequirement::note_aiprr_decide (aiprrDecision *d, aiprrOption *o) { // o (ptr to an option) may be null m_importance.note_aiprr_decide(d, o); } //---------------------------------------------------------------------- // Return true if all req-days are satisfied int aiprrRequirement::is_satisfied () const { aiprrReqDay *rd = first_unsatisfied_reqday(); return rd ? 0 : 1; } //---------------------------------------------------------------------- // Return the first unsatisfied requirement-day aiprrReqDay * aiprrRequirement::first_unsatisfied_reqday () const { aiprrReqDayItr itr = reqday_iterator(); for ( aiprrReqDay *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. //aiprrReqDay * aiprrRequirement::trivial_reqday () const { // aiprrReqDayItr itr = reqday_iterator(); // for ( aiprrReqDay *rd = itr.first(); rd; rd = itr.next() ) { // if ( !(rd->has_surplus_opts()) ) return rd; // } // return 0; //} //---------------------------------------------------------------------- // return an iterator: requirement-days for the requirement aiprrReqDayItr aiprrRequirement::reqday_iterator() const { aiprrReqDayItr i(m_reqdays); return i; } //====================================================================== // aiprrReqDay // //---------------------------------------------------------------------- // Constructor // // set_dcsn() must be called after the decision is created aiprrReqDay::aiprrReqDay (aiprrRequirement *a_req, long a_yyyymmdd) { m_req = a_req; m_day = aipTime(a_yyyymmdd); m_dcsn = 0; m_hope = new aipHHHope; } //---------------------------------------------------------------------- // Destructor aiprrReqDay::~aiprrReqDay () { if (m_hope) delete m_hope; } //---------------------------------------------------------------------- // take_msg - take and react to a message void aiprrReqDay::take_msg (aipMsg *mm) { aiprrMsg *m = (aiprrMsg *)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 aiprrReqDay::note_aiprr_decide (aiprrDecision *d, aiprrOption *o) { // o (ptr to an option) may be null if (!d) { if (o) { log ("aiprrReqDay d is null"); } else { log ("aiprrReqDay d,o are null"); } } } //---------------------------------------------------------------------- // Return true if the reqday has all the resourcess it needs int aiprrReqDay::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 aiprrReqDay::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; } //====================================================================== // aiprrResource // //---------------------------------------------------------------------- // Constructor aiprrResource::aiprrResource (long a_id, aiprrProblem *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 aiprrResource::~aiprrResource () { if (m_resdays) delete m_resdays; if (m_hope) delete m_hope; } //---------------------------------------------------------------------- // add_resday void aiprrResource::add_resday (aiprrResDay *x) { if (!x) return; m_resdays->add (x); } //---------------------------------------------------------------------- // take_msg - take and react to a message void aiprrResource::take_msg (aipMsg *mm) { aiprrMsg *m = (aiprrMsg *)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 aiprrResource::note_aiprr_decide (aiprrDecision *d, aiprrOption *o) { if (!d) log ("aiprrReqDay d is null"); // o (ptr to an option) may be null if (o) m_is_in_cur_solution = 1; } //---------------------------------------------------------------------- // resday_iterator aiprrResDayItr aiprrResource::resday_iterator() const { aiprrResDayItr i(m_resdays); return i; } //====================================================================== // aiprrResDay // //---------------------------------------------------------------------- // Constructor aiprrResDay::aiprrResDay (aiprrResource *a_res, long a_yyyymmdd) { m_res = a_res; m_day = aipTime(a_yyyymmdd); m_opts = new aipPandemonium; m_hope = new aipHHHope; } //---------------------------------------------------------------------- // Destructor aiprrResDay::~aiprrResDay () { if (m_hope) delete m_hope; if (m_opts) delete m_opts; } //---------------------------------------------------------------------- // take_msg - take and react to a message void aiprrResDay::take_msg (aipMsg *mm) { aiprrMsg *m = (aiprrMsg *)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 aiprrResDay::note_aiprr_decide (aiprrDecision *d, aiprrOption *o) { if (!d) log ("aiprrReqDay d is null"); // o (ptr to an option) may be null if (o) m_is_in_cur_solution = 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. // //====================================================================== aipReqRes.h0000755000076400007640000004167511104463242012117 0ustar brianbrian//********************************************************************** // aipReqRes.h - Requirement-Resource problems // // An aiprrProblem is a subclass of aipHHProblem. // solve_rr_problem() uses the High-Hope technique. // See aipHighHope.h // // A aiprrProblem 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 // // See the license at end of this file. // // Developers/Contributers: // [BRM] Brian Marshall - Calgary - bmarshal@agt.net // // 08/01/20 [BRM] began development: split out layer from rr_solve // //---------------------------------------------------------------------- // 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 aiprrOption: // 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, // you develop a set of sub-classes of aipReqRes classes and // they call option.set_g_dynamic(aipG x) based on aspects of // your particular problem. // //---------------------------------------------------------------------- #ifndef aipReqRes_h_guard #define aipReqRes_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 aiprrReqImp; // importance of a requirement // class aipProblem; ( in aipProblem.h ) // class aipHHProblem; ( in aipHighHope.h ) class aiprrProblem; // rr-solve problem // class aipDemon; ( in aipPandemonium.h ) // class aipDecision; ( in aipDecision.h ) // class aipHHDecision; ( in aipHighHope.h ) class aiprrDecision; // decision: for a requirement // class aipOption; ( in aipDecision.h ) // class aipHHOption; ( in aipHighHope.h ) class aiprrOption; // option: a resource class aiprrRequirement; // requirement to be satisfied class aiprrReqDay; // requirement on a day class aiprrResource; // resource to satisfy requirement class aiprrResDay; // resource on a day // class aipDemonItr; ( in aipPandemonium.h ) class aiprrRequirementItr; // requirement iterator class aiprrReqDayItr; // requirement-day iterator class aiprrResourceItr; // resource iterator class aiprrResDayItr; // resource-day iterator class aiprrDecisionItr; // decision iterator // class aipMsg ( in aipBase.h ) class aiprrMsg; // message for rr-solve solving //====================================================================== // aiprrReqImp - Importance of a position class aiprrReqImp : public aipHHImportance { long m_left_bhnd; // being left behind public: aiprrReqImp () { m_left_bhnd = 0; } virtual ~aiprrReqImp () {} virtual long val () const { return aipHHImportance::val() + m_left_bhnd; } virtual void take_msg (aipMsg *m); virtual void note_aiprr_decide (aiprrDecision *d, aiprrOption *o); }; //====================================================================== // aiprrProblem - rrSolve problem - assigning resources to requirements // // solve_rr_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 aiprrProblem : 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: aiprrProblem (); virtual ~aiprrProblem (); void set_num_try (long x) { aipHHProblem::set_num_try(x); } void add_requirement (aiprrRequirement *x); void add_resource (aiprrResource *x); void add_decision (aiprrDecision *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; } aiprrRequirementItr requirement_iterator() const; aiprrResourceItr resource_iterator() const; aiprrDecisionItr decision_iterator() const; virtual void take_msg (aipMsg *m); virtual void note_decide (aipHHDecision *d, aipHHOption *o); // aiprrReqDay * trivial_reqday () const; virtual int solve_rr_problem (); // use this, not solve() }; //====================================================================== // aiprrDecision - choosing a res for a reqday // // inherits: importance, a set of options, more class aiprrDecision : public aipHHDecision { aiprrReqDay * m_reqday; // reqday that needs resource-days public: aiprrDecision (aiprrReqDay *a_reqday, long num_to_decide =1); virtual ~aiprrDecision(); // use this function, not add_hh_option void add_aiprr_option (aiprrOption *x); virtual void take_msg (aipMsg *m); aiprrProblem * prob () const { return (aiprrProblem*)owner(); } aiprrReqDay * reqday () const { return m_reqday; } virtual long imp () const; aiprrOption * aiprr_opt_cur () const { return (aiprrOption*)opt_cur(); } aiprrOption * aiprr_opt_bsf () const { return (aiprrOption*)opt_bsf(); } }; //====================================================================== // aiprrOption - a resource assigned to a requirement on a day class aiprrOption : public aipHHOption { aiprrResDay * m_resday; // the subject of this option aipG m_g_dynamic; // g of situational aspects public: aiprrOption(aiprrResDay *a_resday, aipG g_constant); virtual ~aiprrOption (); virtual void take_msg (aipMsg *m); void set_g_dynamic (aipG x); void add_g_dynamic (aipG x); aiprrDecision * aiprr_chooser () const { return (aiprrDecision*)hh_opt_chooser(); } aiprrResDay * resday () const { return m_resday; } virtual aipG g_opt(); virtual aipG g_opt_cmp(); virtual aipG g_opt_usr(); }; //====================================================================== // aiprrRequirement - a requirement requiring resources // // A requirement is owned by a problem; it owns requirement-days. class aiprrRequirement : public aipDemon { long m_id; aiprrProblem * m_prob; aipPandemonium * m_reqdays; aipHHHope * m_hope; // how well this requirement is doing aiprrReqImp m_importance; // for choosing next decision protected: aipPandemonium * reqday_pandemonium () const { // for iterators return m_reqdays; } public: aiprrRequirement(long a_id, aiprrProblem *a_prob); virtual ~aiprrRequirement(); void add_reqday (aiprrReqDay *x); long imp () const { return m_importance.val(); } virtual void take_msg (aipMsg *m); virtual void note_aiprr_decide (aiprrDecision *d, aiprrOption *o); int is_satisfied () const; aiprrReqDay * first_unsatisfied_reqday () const; // aiprrReqDay * trivial_reqday () const; aiprrProblem * prob () const { return m_prob; } long id () const { return m_id; } aiprrReqDayItr reqday_iterator() const; }; //====================================================================== // aiprrReqDay - a requirement on a day (requiring one or more resources) // // set_dcsn() must be called after the decision is created. class aiprrReqDay : public aipDemon { aiprrRequirement * m_req; aipTime m_day; aiprrDecision * m_dcsn; aipHHHope * m_hope; // how well this reqday is doing aipHHImportance m_importance; public: aiprrReqDay(aiprrRequirement *a_req, long a_yyyymmdd); virtual ~aiprrReqDay(); void set_dcsn (aiprrDecision *a_dcsn) { m_dcsn = a_dcsn; } long imp () const { return m_importance.val(); } virtual void take_msg (aipMsg *m); virtual void note_aiprr_decide (aiprrDecision *d, aiprrOption *o); aipTime day () const { return m_day; } aiprrRequirement * req () const { return m_req; } aiprrDecision * dcsn () const { return m_dcsn; } long num_decided () const { return m_dcsn->num_decided(); } int is_satisfied () const; int has_surplus_opts () const; }; //====================================================================== // aiprrResource - a resource that can satisfy requirements class aiprrResource : public aipDemon { long m_id; aiprrProblem * 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: aiprrResource(long a_id, aiprrProblem *a_prob); virtual ~aiprrResource(); void add_resday (aiprrResDay *x); virtual void take_msg (aipMsg *m); virtual void note_aiprr_decide (aiprrDecision *d, aiprrOption *o); aiprrProblem * prob () const { return m_prob; } long id () const { return m_id; } aiprrResDayItr resday_iterator() const; }; //====================================================================== // aiprrResDay - a resource on a day class aiprrResDay : public aipDemon { aipTime m_day; aiprrResource * 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: aiprrResDay(aiprrResource *a_res, long a_yyyymmdd); virtual ~aiprrResDay(); void add_opt (aiprrOption *o) { m_opts->add(o); } virtual void take_msg (aipMsg *m); virtual void note_aiprr_decide (aiprrDecision *d, aiprrOption *o); aipTime day () const { return m_day; } aiprrResource * res () const { return m_res; } }; //====================================================================== // aiprrRequirementItr - Iterator for Requirements in a Problem class aiprrRequirementItr : public aipDemonItr { public: aiprrRequirementItr (aipPandemonium *p) { set_demon_itr(p,0); } aiprrRequirementItr (const aiprrRequirementItr& x) { set_demon_itr (x.pandemonium(), x.current()); } virtual ~aiprrRequirementItr () {} aiprrRequirementItr& operator = (const aiprrRequirementItr& x) { set_demon_itr(x.pandemonium(), x.current()); return *this; } aiprrRequirement * first () { return (aiprrRequirement*)aipDemonItr::first(); } aiprrRequirement * next () { return (aiprrRequirement*)aipDemonItr::next(); } }; //====================================================================== // aiprrReqDayItr - Iterator for ReqDays of a Requirement class aiprrReqDayItr : public aipDemonItr { public: aiprrReqDayItr (aipPandemonium *p) { set_demon_itr(p,0); } aiprrReqDayItr (const aiprrReqDayItr& x) { set_demon_itr (x.pandemonium(), x.current()); } virtual ~aiprrReqDayItr () {} aiprrReqDayItr& operator = (const aiprrReqDayItr& x) { set_demon_itr(x.pandemonium(), x.current()); return *this; } aiprrReqDay * first () { return (aiprrReqDay*)aipDemonItr::first(); } aiprrReqDay * next () { return (aiprrReqDay*)aipDemonItr::next(); } }; //====================================================================== // aiprrResourceItr - Iterator for Resources in a Problem class aiprrResourceItr : public aipDemonItr { public: aiprrResourceItr (aipPandemonium *p) { set_demon_itr(p,0); } aiprrResourceItr (const aiprrResourceItr& x) { set_demon_itr (x.pandemonium(), x.current()); } virtual ~aiprrResourceItr () {} aiprrResourceItr& operator = (const aiprrResourceItr& x) { set_demon_itr(x.pandemonium(), x.current()); return *this; } aiprrResource * first () { return (aiprrResource*)aipDemonItr::first(); } aiprrResource * next () { return (aiprrResource*)aipDemonItr::next(); } }; //====================================================================== // aiprrResDayItr - Iterator for ResDays of a Resource class aiprrResDayItr : public aipDemonItr { public: aiprrResDayItr (aipPandemonium *p) { set_demon_itr(p,0); } aiprrResDayItr (const aiprrResDayItr& x) { set_demon_itr (x.pandemonium(), x.current()); } virtual ~aiprrResDayItr () {} aiprrResDayItr& operator = (const aiprrResDayItr& x) { set_demon_itr(x.pandemonium(), x.current()); return *this; } aiprrResDay * first () { return (aiprrResDay*)aipDemonItr::first(); } aiprrResDay * next () { return (aiprrResDay*)aipDemonItr::next(); } }; //====================================================================== // aiprrDecisionItr - Iterator for Decisions in a Problem class aiprrDecisionItr : public aipDemonItr { public: aiprrDecisionItr (aipPandemonium *p) { set_demon_itr(p,0); } aiprrDecisionItr (const aiprrDecisionItr& x) { set_demon_itr (x.pandemonium(), x.current()); } virtual ~aiprrDecisionItr () {} aiprrDecisionItr& operator = (const aiprrDecisionItr& x) { set_demon_itr(x.pandemonium(), x.current()); return *this; } aiprrDecision * first () { return (aiprrDecision*)aipDemonItr::first(); } aiprrDecision * next () { return (aiprrDecision*)aipDemonItr::next(); } }; //====================================================================== // aiprrMsg - message for StaffWhen problem-solving class aiprrMsg : public aipHHMsg { public: aiprrMsg (aiprrProblem *p, short typ) : aipHHMsg(p,typ) {} ~aiprrMsg() {} }; //====================================================================== #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.cpp0000644000076400007640000005557711104463242012152 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.h0000755000076400007640000002040411104463242011577 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.txt0000755000076400007640000000257611104463243011674 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_rrproj_linux.sh0000775000076400007640000000070111104463242013616 0ustar brianbrian# create the executable: rrproj # # g++ is good for Fedora; your compiler might be gcc cc CC etc. echo " " echo "Compiling and Linking rrproj..." g++ -o rrproj \ aipGood.cpp \ aipBase.cpp \ aipPandemonium.cpp \ aipEmotion.cpp \ aipDecision.cpp \ aipProblem.cpp \ aipHighHope.cpp \ aipTime.cpp \ aipReqRes.cpp \ rrproj_solv.cpp \ rrproj_prob.cpp \ rrproj_fctry.cpp \ rrproj_main.cpp echo "Done." echo " " mk_rrproj_windows.bat0000755000076400007640000000212511104463243014306 0ustar brianbrian:: create the executable rrproj.exe :: :: This script was written for the Borland C++ 5.5 compiler, tlib @echo off echo ... echo Making rrproj.exe... echo compiling aiParts and rrproj code echo and putting object code in a library... bcc32 -c aipGood.cpp tlib rrproj +aipGood.obj bcc32 -c aipBase.cpp tlib rrproj +aipBase.obj bcc32 -c aipPandemonium.cpp tlib rrproj +aipPandemonium.obj bcc32 -c aipEmotion.cpp tlib rrproj +aipEmotion.obj bcc32 -c aipDecision.cpp tlib rrproj +aipDecision.obj bcc32 -c aipProblem.cpp tlib rrproj +aipProblem.obj bcc32 -c aipHighHope.cpp tlib rrproj +aipHighHope.obj bcc32 -c aipTime.cpp tlib rrproj +aipTime.obj bcc32 -c aipReqRes.cpp tlib rrproj +aipReqRes.obj bcc32 -c rrproj_solv.cpp tlib rrproj +rrproj_solv.obj bcc32 -c rrproj_prob.cpp tlib rrproj +rrproj_prob.obj bcc32 -c rrproj_fctry.cpp tlib rrproj +rrproj_fctry.obj echo compiling rrproj_main.cpp... bcc32 -c rrproj_main.cpp echo linking rr_prob.exe... bcc32 -errproj.exe rrproj_main.obj rrproj.LIB echo cleaning up... del *.obj *.tds del rrproj.BAK del rrproj.LIB echo Done. echo ... mk_samples_linux2.sh0000775000076400007640000000166211104463242014035 0ustar brianbrian# Slower way of building sample programs on a Linux system # # This file shows the software that is used by each sample program. # # Lines from this file may be copied and pasted to build only # a particular sample program. # # g++ is good for Fedora; your compiler might be gcc cc CC etc. g++ -o samp_1_good samp_good.cpp aipGood.cpp g++ -o samp_2_pandemonium samp_pandemonium.cpp aipPandemonium.cpp \ aipBase.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.sh0000755000076400007640000000236511104463242013752 0ustar brianbrian# Create the sample programs on a Linux system # # g++ is good for Fedora; your compiler might be gcc cc CC etc. 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.bat0000755000076400007640000000253011104463243014434 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.txt0000664000076400007640000004062411104463243011542 0ustar brianbrian================================================================= aiParts README 0.9.3 Website: http://www.aiparts.org Everything in this file is on the website. Release and Credit details are in: 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. Contact: Brian Marshall at bmarshal@agt.net or +1-403-651-0584 = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = Table of Contents About aiParts About rrproj Status of the Software Making the Sample Programs Using aiParts Support and Services The High-Hope AI Technique aiParts Source Files The aiParts Open Source Project Future Development Notes = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = About aiParts aiParts is a set of C++ classes that can be used to develop artificial intelligence for multi-decision problems. It includes classes that implement the High-Hope technique and some sample programs. Release and Credit details are in the RELEASE.txt file. = = = = = = = = = = = = = = = = Using the High-Hope Classes An application can assemble a problem from subclasses of the High-Hope classes. The High-Hope problem class has a solve() function that will search for a good solution. The High-Hope technique is a type of machine-learning. Options have models of emotions. The software learns about the solution space by repeatedly trying to find a good solution. Emotions affect tries which affect emotions. Emotions control the balance between exploring the unknown and taking advantage of what has been learned. The technique is an application of aiPatterns - see: http://www.agt.net/public/bmarshal/aipatterns = = = = = = = = = = = = = = = = Applications for aiParts aiParts can be used: - in applications, to make decisions or solve problems - as a framework for AI research and development Two of the sample programs use the High-Hope classes. They can be used to solve a variety of problems, or as a starting point in custom development. A-to-B - find the shortest path using only distance information, not spatial information rrproj - assign people and/or equipment to projects or events 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 decision-tree too large too search The aipPandemonium class is used as a container-class for: - sets, lists - structure in one-to-many relationships = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = About rrproj rrproj is free Open Source software that assigns people and/or equipment to projects. rrproj is a sample program in the aiParts distribution. It uses the Requirement-Resource and High-Hope classes. Sample scripts are provided for building and testing the solver program. These scripts must be run from the directory in which they reside. A free Open Source graphical user-interface and database are being developed using Open Office 3. Your organization can develop an applications to use the solver, or the solver can be integrated into other software. The make script compiles and links a program that reads and writes disk files. A better call interface is planned. = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = Status of the Software aiParts is available for beta testing. The AI is still experimental. The rrproj sample program needs much more testing. The rrproj solver is slow. This will be improved. Any C++ compiler should be able to compile this software. In September 2008, aiParts 0.9.0 added many new features, and the existing code was changed in many places. See RELEASES.txt for details. Most of the code has not changed since version 0.8.5 and it has had a fair bit of testing with the sample programs. The rrproj sample program has had a little testing, which also tested the changes in the aiParts code. There is much to be done to improve aiParts - see the last section of this file. = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = 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 For the rrproj sample program, see these files in the rrproj subdirectory: mk_windows_rrproj.bat mk_linux_rrproj.sh On Windows, the script is written to use the Borland 5.5 C++ compiler and library. They are free, high-quality and they are run from the command line. 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. = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = 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. = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = Support and Services Suggestions for enhancements and changes are always welcome. FAQ pages will be on the website in the future. aiParts software is Open Source - you get the source code. Anyone you want can support it, add to it, change it or use it in other software. Services are available by independent firms and developers. These services are not endorsed by the aiParts project. Setup, support, analysis and development services are available by Brian Marshall, the developer of aiParts and rrproj. Contact: bmarshal@agt.net +1 403-651-0584 (and leave a message if necessary) = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = The High-Hope AI Technique The High-Hope technique is used to find solutions to problems in which a number of decisions have to be made and each decision affects subsequent decisions. The technique can be useful for many types of problems, including arbitrarily constrained problems that have an enormous number of possible solutions. The technique is easy to implement in software, and easy to specialize to solve a particular type of problem. Software size and complexity grow very slowly with an increase in the number of problem constraints. For more information, see www.aiparts.org/highhope.htm = = = = = = = = = = = = = = = = The Technique The High-Hope Technique... - Given a problem, try to solve it repeatedly (physically or by consideration) and remember the best solution(s). - In a try, where there is a choice of which decision to address next, choose the one that is most urgent - either because it is particularly important or it is particularly safe. - At each decision, pick the option with the most hope for success. The hope of an option is the sum of problem-domain-specific aspects plus strategic aspects. - After each try, change the relevant strategic aspects to reflect the result of the try. The technique is chaotically sensitive to initial conditions, parameters and the sequence of random numbers used for choosing equal options. A tiny change can result in a different choice in a try, which may affect subsequent choices and tries. = = = = = = = = = = = = = = = = Hope Combines Aspects Hope is the result of the evaluation of all aspects being considered. Option-hope is a single quantity that can be used to compare options. The hope associated with an option combines the results of the evaluation of... - static aspects ("It's good for Bill to work evenings.)" - dynamic aspects based on decisions made up to this point ("It would be bad for Sally to work today; she has already worked five days in a row.") - strategic aspects based on what has been learned in previous tries ("This option worked really well last time.") Static and dynamic aspects are particular to the problem domain. For each aspect, you write a bit of code that evaluates the aspect and assigns a goodness value. The strategic aspects are handled by the High-Hope classes. You create subclasses for the objects in your problem. An application assembles a problem using these subclasses. = = = = = = = = = = = = = = = = Fear, Greed and Curiosity In the High-Hope technique, the strategic aspects are emotions. The use of the word "emotion" and the names of the emotions are misleading because the words have multiple subtle meanings. The strategic aspects that contribute to hope are: Fear is the feeling that something bad may happen. Fear causes you to avoid an option that has been a problem in the past. Fear starts at Neutral and becomes more negative as an option is involved in unsuccessful tries. Greed is the feeling that you know how to make something good happen. Greed attracts you to options that have worked well in the past. Greed starts at Neutral and becomes more positive as an option is involved in successful tries. Curiosity is the feeling that an untried option might be good. Curiosity attracts you to options that might turn out even better than any you have already tried. Curiosity about an apparently acceptable option starts fairly high and decreases as the option is tried. Hope = Fear + Greed + Curiosity = = = = = = = = = = = = = = = = Hope is a Sum A hope resolves to a goodness. Aspects that contribute to a hope resolve to a goodnesses. Aspects can be combined by adding, if goodnesses are implemented like... VeryVeryBad = -1024 VeryBad = -512 QuiteBad = -256 Bad = -64 FairlyBad = -32 SomewhatBad = -8 SlightlyBad = -2 VerySlightlyBad = -1 Neutral = 0 VerySlightlyGood = 1 SlightlyGood = 2 SomewhatGood = 3 FairlyGood = 4 Good = 5 QuiteGood = 6 VeryGood = 7 VeryVeryGood = 8 The lack of symmetry around Neutral is a reflection of the fact that the worse an aspect is, the more important it is. There may be, for example, a variety of good aspects to setting the alarm clock an hour later, but if one of them is probably getting fired, it's probably a bad option. = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = 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: aipReqRes.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 = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = The aiParts Open Source Project aiParts is an Open Source project that develops software parts that implement artificial intelligence. The software distributed by this project is copyrighted by its authors and then licensed with the MIT License and as such, it is OSI Certified Open Source Software. See the file LICENSE.txt. This means that you can do pretty much whatever you want with aiParts software... use it, distribute it, build it into other software, whatever. When you get aiParts software, you get the source code. You have control of your software (regardless of what this project does in the future). Contributions are always welcome: software, documentation, testing, suggestions, bug reports, enhancement requests, comments and criticism. See the last section of this file. aiParts is currently focused on the High-Hope technique for multi-decision problems. Anyone is welcome to contribute software parts that implement other approaches to artificial intelligence. For more information, contact Brian Marshall at: bmarshal@agt.net See www.opensource.org for information on the Open Source Initiative. = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = Future Development Notes Make the A-to-B sample program problem bigger and harder. Developing new AI techniques (alternatives to the High-Hope technique) Developing abstraction-layers for an AI technique (just as the classes in aipReqRes.h/cpp specialize the High-Hope technique for Requirement-Resource problems) Recognize and support 'trivial' decisions to provide for: - better performance - selecting a decision and choosing an option for a resource (rather than for a requirement) and the decision can be (set to be) trivial so the pre-chosen will be chosen. 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. Ways are a very rich abstraction - they can make it possible to work on parts of a problem while keeping other parts constant. Ways may be the key to recognizing similar solutions. ================================================================= RELEASE.txt0000664000076400007640000001415111104463243011661 0ustar brianbrian================================================================= RELEASE.txt - aiParts - Releases and 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-11 See the end of this file for Old Credits and old releases. For more information about the software and project, see the README.txt file. = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = Developers and Contributors [BRM] Brian Marshall - Calgary - bmarshal@agt.net = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = Release History Release Date Status ------- ------------ ---------- 0.9.3 2008-Nov-05 Beta This release includes the sample program rrproj, which assigns people and/or equipment to projects or events. README.txt was improved. 0.9.2 2008-Sep-30 Beta rr_solve.h/cpp were renamed to aipReqRes.h/cpp; classes declared in these files were renamed. README.txt was improved. 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 ================================================================= rrproj_fctry.cpp0000664000076400007640000004245511104463242013300 0ustar brianbrian//********************************************************************** // rrproj_fctry.cpp function bodies for rrproj_fctry.h // // Copyright (c) 2008 Brian Marshall // // See the license at end of this file. // // Developers/Contributers: // [BRM] Brian Marshall - Calgary - bmarshal@agt.net // // 08/01/28 [BRM] began development // //---------------------------------------------------------------------- #include "rrproj_fctry.h" // for debugging... // #include // #include // using namespace std; //====================================================================== // Factory Data // // 3 datasets: // - problem - setup with data from the user // - solver - factory creates it from the problem dataset // - factory - temporary data owned by the factory // // variable names in functions: // The first letter of some variables in functions indicates the // dataset from which the function is set: // pemp is an emp from the problem // semp is an emp from the solver // fempid is an empid from the factory // // class name prefix // rpp rrproj problem (setup by application) // rps rrproj solver (used by the problem) // rpf rrproj factory (solver-factory) // //---------------------------------------------------------------------- // Constructor rpfFactory::rpfFactory (rppProblem *pdata) { m_problem_data = pdata; m_emp_ids = new aipPandemonium; if (m_emp_ids) m_emp_ids->set_is_distinct(); m_opts = new aipPandemonium; if (m_opts) m_opts->set_is_distinct(); } //---------------------------------------------------------------------- // Destructor rpfFactory::~rpfFactory () { if (m_emp_ids) delete m_emp_ids; if (m_opts) delete m_opts; } //---------------------------------------------------------------------- // create and setup a new rpsProblem from the problem data. rpsProblem * rpfFactory::make_rps_problem () { if (!m_problem_data) return 0; rpsProblem *sp = new rpsProblem (); if (!sp) return 0; sp->set_num_try (m_problem_data->num_tries()); int status = 1; if (status) status = setup(sp); if (!status) return 0; return sp; } //---------------------------------------------------------------------- // setup an rpsProblem - return true on success int rpfFactory::setup (rpsProblem *sprob) { int status = 1; // iterators in the problem data rppPosDayItr pditr = pprob()->posday_iterator(); rppPosEmpItr peitr = pprob()->posemp_iterator(); rppAssgnItr aitr = pprob()->assgn_iterator(); // iterators in the factory data rpfOptItr oitr = opt_iterator(); // setup pos, posday, dcsn if (status) status = handle_pd (pditr, sprob); // setup emp, empday if (status) status = handle_pe_a (peitr, aitr, sprob); // add to the opt list if (status) status = handle_a_ed (aitr, sprob); // add to opt list if (status) status = handle_pe_pd_ed (peitr, sprob); // setup options from opt list if (status) status = handle_o (oitr, sprob); return status; } //---------------------------------------------------------------------- // handle the set of PosDays in the problem data // // create positions and handle individual posdays int rpfFactory::handle_pd (rppPosDayItr pditr, rpsProblem *sprob) { int status = 1; long min_yyyymmdd = 20200101; long max_yyyymmdd = 19800101; long prev_pos_id = very_unlikely_id(); long pner; // num-emps_required in the problem rppPosDay *ppd; // posday in the problem rpsPosDay *spd; // posday in the solver rpsPosition *spos; // pos in the solver if (status) { // create (Pos), (PosDay), (Dcsn) for (ppd = pditr.first(); ppd; ppd = pditr.next()) { if (ppd->pos_id() != prev_pos_id) { spos = new rpsPosition(ppd->pos_id(), sprob); if (!spos) { status = 0; log(" Err new rpsPosition"); break; } sprob->add_position(spos); prev_pos_id = ppd->pos_id(); } spd = new rpsPosDay(spos, ppd->yyyymmdd(), ppd->proj_id(), ppd->start_hhmm(), ppd->end_hhmm()); if (!spd) { status = 0; log("Err new rpsPosDay"); break; } spos->add_posday(spd); pner = ppd->num_emps_req(); if (pner < 1 || pner > 1000) { status = 0; log("Err bad num-emps-required"); break; } rpsDecision *sdcsn = new rpsDecision(spd, pner); if (!sdcsn) {status = 0; log("Err new rpsDecision"); break; } sprob->add_decision(sdcsn); spd->set_dcsn(sdcsn); long ppd_yyyymmdd = ppd->yyyymmdd(); if (ppd_yyyymmdd < min_yyyymmdd) min_yyyymmdd = ppd_yyyymmdd; if (ppd_yyyymmdd > max_yyyymmdd) max_yyyymmdd = ppd_yyyymmdd; } } if (status) { sprob->set_day_range (min_yyyymmdd, max_yyyymmdd); } return status; } //---------------------------------------------------------------------- // handle the sets of posemps and assignments in the problem data // // create a distinct list of empids, and then create emps and empdays // (which also uses empdows and empoffs) int rpfFactory::handle_pe_a (rppPosEmpItr peitr, rppAssgnItr aitr, rpsProblem *sprob) { int status = 1; rpfEmpId *feid; // an empid owned by the factory long prev_eid = very_unlikely_id(); if (status) { // start {EmpId} rppPosEmp *ppe; // a posemp in the problem for (ppe = peitr.first(); ppe; ppe = peitr.next()) { if (ppe->emp_id() != prev_eid) { feid = new rpfEmpId(ppe->emp_id()); if (!feid) { status = 0; log("Err new rpfEmpId"); break; } m_emp_ids->add (feid); // list is forced distinct prev_eid = ppe->emp_id(); } } } if (status) { // add to {EmpId} rppAssignment *pa; // an assignment in the problem for (pa = aitr.first(); pa; pa = aitr.next()) { if (pa->emp_id() != prev_eid) { feid = new rpfEmpId(pa->emp_id()); if (!feid) { status = 0; log("Err new rpfEmpId"); break; } m_emp_ids->add (feid); // list is forced distinct prev_eid = pa->emp_id(); } } } // we now have a distinct list of emp_ids rpsEmployee *semp; // an emp in the solver if (status) { // create (Emp) rpfEmpIdItr eiditr = empid_iterator(); for (feid = eiditr.first(); feid; feid = eiditr.next()) { semp = new rpsEmployee(feid->emp_id(), sprob); if (!semp) { status = 0; log("Err new rpsEmployee"); break; } sprob->add_employee(semp); status = handle_e (semp, sprob); if (!status) break; } } return status; } //---------------------------------------------------------------------- // handle a solver emp - create empday int rpfFactory::handle_e (rpsEmployee *semp, rpsProblem *sprob) { int status = 1; if (status) { apply_empdaypos(semp); } if (status) { // create (EmpDay) aipTime curday( (sprob->start_day()) ); aipTime lastday( (sprob->end_day()) ); for ( ; curday <= lastday; curday.add_days(1) ) { long x_yyyymmdd = curday.yyyymmdd(); if (!x_yyyymmdd) { status = 0; log("Err h_e yyyymmdd"); break; } if ( emp_is_off (semp->emp_id(), curday) ) continue; long x_start_hhmm, x_end_hhmm; long x_dow = curday.days_since_sunday(); get_dow_data (semp->emp_id(), x_dow, &x_start_hhmm, &x_end_hhmm); if (x_start_hhmm == x_end_hhmm) continue; rpsEmpDay *sed = new rpsEmpDay (semp, x_yyyymmdd, x_start_hhmm, x_end_hhmm); if (!sed) { status = 0; log("Err new rpsEmpDay"); break; } semp->add_empday(sed); } } return status; } //---------------------------------------------------------------------- // handle the set of assignments (plus solution EmpDays) // // start a list of options (that is forced to have distinct keys) int rpfFactory::handle_a_ed (rppAssgnItr aitr, rpsProblem *sprob) { int status = 1; if (status) { // create {opt} from [Assgnmnt] rppAssignment *pa; // an assignment in the problem rpsPosDay *spd; // a posday in the solver rpsEmpDay *sed; // an empday in the solver for (pa = aitr.first(); pa; pa = aitr.next()) { aipTime curday(pa->start_yyyymmdd()); aipTime lastday(pa->end_yyyymmdd()); for ( ; curday <= lastday; curday.add_days(1) ) { spd = find_posday (pa->pos_id(), curday.yyyymmdd(), pa->proj_id(), sprob); if (!spd) continue; sed = find_empday (pa->emp_id(), curday.yyyymmdd(), sprob); if (!sed) continue; rpfOpt *fopt = new rpfOpt (spd, sed, aipManditory); if (!fopt) { status = 0; log("Err new rpfOpt"); break; } m_opts->add (fopt); // list is forced distinct } } } return status; } //---------------------------------------------------------------------- // handle the set of PosEmps (plus solution PosDays and EmpDays) // // add to the opt-list int rpfFactory::handle_pe_pd_ed (rppPosEmpItr peitr, rpsProblem *sprob) { int status = 1; if (status) { // create {opt} from [PosEmp] rppPosEmp * ppe; // a posemp from the problem rpsPosition * sp; // pointers to objects in the solver rpsPosDay * spd; rpsEmployee * se; rpsEmpDay * sed; rpsPositionItr pitr = sprob->position_iterator(); rpsEmployeeItr eitr = sprob->employee_iterator(); pitr.first(); eitr.first(); // for each pos_emp... for ( ppe = peitr.first(); ppe; ppe = peitr.next() ) { sp = (rpsPosition*)(pitr.find(ppe->pos_id())); if (!sp) continue; se = (rpsEmployee*)(eitr.find(ppe->emp_id())); if (!se) continue; rpsPosDayItr pditr = sp->posday_iterator(); rpsEmpDayItr editr = se->empday_iterator(); // for each pos_day and emp_day... for ( spd = pditr.first(); spd; spd = pditr.next() ) { for ( sed = editr.first(); sed; sed = editr.next() ) { if (spd->yyyymmdd() == sed->yyyymmdd()) { if ( sed->start_time() > spd->start_time() || sed->end_time() < spd->end_time() ) continue; rpfOpt *fopt = new rpfOpt (spd, sed, aipPrettyGood); if (!fopt) { status = 0; log("Err new rpfOpt"); break; } m_opts->add (fopt); // list is forced distinct } } // end of loop through emp_days (in the solver) } // end of loop through pos_days (in the solver) } // end of loop through posemps (in the problem) } return status; } //---------------------------------------------------------------------- // handle the list of opts // // create solver-options and attach them to decisions int rpfFactory::handle_o (rpfOptItr foitr, rpsProblem *sprob) { int status = 1; if (!sprob) { status = 0; log("handle_o sprob"); } if (status) { // create (Option) from {opt} rpfOpt * fo; // an opt-list element (stored in the factory) for ( fo = foitr.first(); fo; fo = foitr.next() ) { rpsDecision *sd = fo->pos_day()->dcsn(); if (!sd) { status = 0; log("Err h_o dcsn"); break; } rpsOption *sopt = new rpsOption (fo->emp_day(), fo->g_constant()); if (!sopt) { status = 0; log("new rpsOption"); break; } sd->add_rps_option (sopt); } // end of elements in distinct opt-list } return status; } //---------------------------------------------------------------------- // return a posday from the solver given an pos_id and a date rpsPosDay * rpfFactory::find_posday (long a_pos_id, long a_yyyymmdd, long a_proj_id, rpsProblem *sprob) { if (!sprob) return 0; rpsPositionItr pitr = sprob->position_iterator(); rpsPosition * sp = (rpsPosition*)(pitr.find(a_pos_id)); if (!sp) return 0; rpsPosDayItr pditr = sp->posday_iterator(); rpsPosDay * spd = (rpsPosDay*) (pditr.find(a_pos_id, a_yyyymmdd, a_proj_id)); return spd; } //---------------------------------------------------------------------- // return an empday from the solver given an emp_id and a date rpsEmpDay * rpfFactory::find_empday (long emp_id, long a_yyyymmdd, rpsProblem *sprob) { if (!sprob) return 0; rpsEmployeeItr eitr = sprob->employee_iterator(); rpsEmployee * se = (rpsEmployee*)(eitr.find(emp_id)); if (!se) return 0; rpsEmpDayItr editr = se->empday_iterator(); rpsEmpDay * sed; for ( sed = editr.first(); sed; sed = editr.next() ) { if (sed->yyyymmdd() == a_yyyymmdd) break; } return sed; } //---------------------------------------------------------------------- // apply historic EmpDayPos data to the emp void rpfFactory::apply_empdaypos (rpsEmployee *e) { rppEmpDayPosItr edpitr = pprob()->empdaypos_iterator(); rppEmpDayPos *edp = (rppEmpDayPos*)(edpitr.find(e->emp_id())); if (!edp) return; long last_yyyymmdd = edp->yyyymmdd(); for ( edp = edpitr.next(); edp; edp = edpitr.next() ) { if (edp->emp_id() != e->emp_id()) break; if (edp->yyyymmdd() > last_yyyymmdd) { last_yyyymmdd = edp->yyyymmdd(); } } aipTime lw(last_yyyymmdd); e->set_start_last_work(lw); } //---------------------------------------------------------------------- // return true if emp is off on the specified day int rpfFactory::emp_is_off (long a_emp_id, aipTime a_day) { int is_off = 0; rppEmpOffItr eoitr = pprob()->empoff_iterator(); rppEmpOff *eoff = (rppEmpOff*)(eoitr.find(a_emp_id)); for ( ; eoff; eoff = eoitr.next() ) { if (eoff->emp_id() != a_emp_id) break; if (a_day >= eoff->start_day() && a_day <= eoff->end_day() ) { is_off = 1; break; } } return is_off; } //---------------------------------------------------------------------- // get data about the day of week for the emp or, if there is none // for this emp, set default values - return false on failure void rpfFactory::get_dow_data (long a_emp_id, long a_dow, long *a_start_hhmm, long *a_end_hhmm) { int is_any = 0; int is_match = 0; *a_start_hhmm = *a_end_hhmm = 0; rppEmpDowItr edowitr = pprob()->empdow_iterator(); rppEmpDow *edow = (rppEmpDow*)(edowitr.find(a_emp_id)); for ( ; edow; edow = edowitr.next()) { if (edow->emp_id() != a_emp_id) break; is_any = 1; if (edow->dow() == a_dow) { *a_start_hhmm = edow->start_hhmm(); *a_end_hhmm = edow->end_hhmm(); is_match = 1; break; } } if (!is_match && !is_any) { *a_start_hhmm = 0; // emp can work all days *a_end_hhmm = 2359; } } //---------------------------------------------------------------------- // get an iterator for empids in the list owned by the factory rpfEmpIdItr rpfFactory::empid_iterator () const { rpfEmpIdItr i(m_emp_ids); return i; } //---------------------------------------------------------------------- // get an iterator for opts in the list owned by the factory rpfOptItr rpfFactory::opt_iterator () const { rpfOptItr i(m_opts); return i; } //====================================================================== // rpfOpt // //---------------------------------------------------------------------- // Constructor rpfOpt::rpfOpt (rpsPosDay *a_pos_day, rpsEmpDay *a_emp_day, aipG a_g_constant) { m_pos_day = a_pos_day; m_emp_day = a_emp_day; m_g_constant = a_g_constant; m_proj_id = m_pos_day ? m_pos_day->proj_id() : 0; m_pos_id = m_pos_day ? m_pos_day->pos_id() : 0; m_emp_id = m_emp_day ? m_emp_day->emp_id() : 0; m_yyyymmdd = m_emp_day ? m_emp_day->yyyymmdd() : 0; } //---------------------------------------------------------------------- // Destructor rpfOpt::~rpfOpt () {} //====================================================================== // 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. // //====================================================================== rrproj_fctry.h0000755000076400007640000002151511104463242012740 0ustar brianbrian//********************************************************************** // rrproj_fctry.h Used by a problem to build a solver // // An application should not directly include this file; // an application should include: rrproj_prob.h // // Copyright (c) 2008 Brian Marshall // // See the license at end of this file. // // Developers/Contributers: // [BRM] Brian Marshall - Calgary - bmarshal@agt.net // // 08/01/28 [BRM] began development // //---------------------------------------------------------------------- // A note on prefixes // // rpp rrproj problem (setup by application) // rps rrproj solver (used by the problem) // rpf rrproj factory (solver-factory) // //---------------------------------------------------------------------- // Data-sets used to create other data-sets // // key: [Problem-data] (solver-data) {temp-data} // // // [PosDay] [PosEmp] [Assgnmnt]______ // | | | | | | | // (Pos) | | | {EmpId} | // | | | | ___[EmpDayPos] | // (Dcsn) | __| (Emp) | // | | | | // (PosDay) | [EmpOff] | [EmpDOW] | // | |__ | | | | // | | (EmpDay) | // | |_______| or _| | // |_______________{Opt}_______________| // | // (Option) // // 1: [PosDay] => (Pos) (Dcsn) (PosDay) // 2: [PosEmp] [Assgnmnt] [EmpDayPos] [EmpOff] [EmpDOW] // => (Emp) (EmpDay) // 3: [Assgnmnt] (EmpDay) => {Opt} // 4: [PosEmp] (PosDay) (EmpDay) => {Opt} // 5: {Opt} => (Option) // // in the future... // {Opt} => {Opt} remove opts where a manditory opt exists // and manditory opts satisfy the dcsn // //---------------------------------------------------------------------- // Future development // // if, after all options have been created, an empday has no opts // in its list, delete the empday, and if an emp has no empdays, // delete the emp // //---------------------------------------------------------------------- #ifndef rrproj_fctry_h_guard #define rrproj_fctry_h_guard #include "rrproj_prob.h" #include "rrproj_solv.h" //---------------------------------------------------------------------- // Classes - subclassing is shown by indentation class rpfFactory; // builds a solver from a problem // class aipDemon ( in aipPandemonium.h ) class rpfEmpId; // for making a distinct list of emps class rpfOpt; // for making a distinct list of opts // class aipDemonItr; ( in aipPandemonium.h ) class rpfEmpIdItr; // iterator for rpfEmpId class rpfOptItr; // iterator for rpfOpt //====================================================================== // rpfFactory - Factory that can build a solver from a problem // // setup() and handle_xxx() functions return true on success class rpfFactory { rppProblem * m_problem_data; // temporary datasets aipPandemonium * m_emp_ids; // distinct emp_ids aipPandemonium * m_opts; // distinct option data protected: long very_unlikely_id () { return -938258186; } int setup (rpsProblem *sprob); int handle_pd (rppPosDayItr pditr, rpsProblem *sprob); int handle_pe_a (rppPosEmpItr peitr, rppAssgnItr aitr, rpsProblem *sprob); int handle_e (rpsEmployee *semp, rpsProblem *sprob); int handle_a_ed (rppAssgnItr aitr, rpsProblem *sprob); int handle_pe_pd_ed (rppPosEmpItr peitr, rpsProblem *sprob); int handle_o (rpfOptItr oitr, rpsProblem *sprob); rpsPosDay * find_posday (long pos_id, long yyyymmdd, long proj_id, rpsProblem *sprob); rpsEmpDay * find_empday (long emp_id, long yyyymmdd, rpsProblem *sprob); void apply_empdaypos (rpsEmployee *e); int emp_is_off (long a_emp_id, aipTime a_day); void get_dow_data (long a_emp_id, long a_dow, long *a_start_hhmm, long *a_end_hhmm); public: rpfFactory (rppProblem *pdata); // factory uses problem data virtual ~rpfFactory (); const char * rslt_desc () const { return m_problem_data->get_rslt_desc(); } rppProblem * pprob () { return m_problem_data; } rpsProblem * make_rps_problem (); // to create a solver-problem rpfEmpIdItr empid_iterator () const; // get an iterator rpfOptItr opt_iterator () const; void log (const char *str) const { m_problem_data->log(str); } }; //====================================================================== // rpfEmpId - for making a distinct list of emps class rpfEmpId : public aipDemon { long m_emp_id; public: rpfEmpId (long a_emp_id) { m_emp_id = a_emp_id; } ~rpfEmpId () {} virtual long num_keys (void) const { return 1; } virtual long key1 (void) const { return m_emp_id; } long emp_id () const { return m_emp_id; } }; //====================================================================== // rpfOpt - for making a distinct list of opts class rpfOpt : public aipDemon { rpsPosDay * m_pos_day; rpsEmpDay * m_emp_day; aipG m_g_constant; long m_proj_id; long m_pos_id; long m_emp_id; long m_yyyymmdd; public: rpfOpt (rpsPosDay *a_pos_day, rpsEmpDay *a_emp_day, aipG a_g_constant); ~rpfOpt (); virtual long num_keys (void) const { return 4; } virtual long key1 (void) const { return m_proj_id; } virtual long key2 (void) const { return m_pos_id; } virtual long key3 (void) const { return m_yyyymmdd; } virtual long key4 (void) const { return m_emp_id; } long pos_id () const { return m_emp_id; } long emp_id () const { return m_emp_id; } long yyyymmdd () const { return m_yyyymmdd; } aipG g_constant () const { return m_g_constant; } rpsPosDay * pos_day () const { return m_pos_day; } rpsEmpDay * emp_day () const { return m_emp_day; } }; //====================================================================== // iterator for rpfEmpId class rpfEmpIdItr : public aipDemonItr { public: rpfEmpIdItr (aipPandemonium *p) { set_demon_itr(p,0); } rpfEmpIdItr (const rpfEmpIdItr& x) { set_demon_itr (x.pandemonium(), x.current()); } virtual ~rpfEmpIdItr () {} rpfEmpIdItr& operator = (const rpfEmpIdItr& x) { set_demon_itr(x.pandemonium(), x.current()); return *this; } rpfEmpId * first () { return (rpfEmpId*)aipDemonItr::first(); } rpfEmpId * next () { return (rpfEmpId*)aipDemonItr::next(); } }; //====================================================================== // iterator for rpfOpt class rpfOptItr : public aipDemonItr { public: rpfOptItr (aipPandemonium *p) { set_demon_itr(p,0); } rpfOptItr (const rpfOptItr& x) { set_demon_itr (x.pandemonium(), x.current()); } virtual ~rpfOptItr () {} rpfOptItr& operator = (const rpfOptItr& x) { set_demon_itr(x.pandemonium(), x.current()); return *this; } rpfOpt * first () { return (rpfOpt*)aipDemonItr::first(); } rpfOpt * next () { return (rpfOpt*)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. // //********************************************************************** rrproj_main.cpp0000664000076400007640000001076611104463242013075 0ustar brianbrian//====================================================================== // main() for a stand-alone program to solve rrproj problems. // // Copyright (c) 2008 Brian Marshall // // See the license at end of this file. // // This main() is used when the application runs a stand-alone // solver executable to solve rrproj problems. // - Problem data is passed to the solver in a disk file // - Results are passed back to the application in a disk file // // As an alternative to a stand-alone program using disk files, // the rrproj solver may be linked into an application, which: // - creates an instance of rppProblem // - calls rppProblem functions to provide problem data // - calls rppProblem::solve() // - calls rppProblem::rslt_desc() to get the result description // - calls rppProblem::get_pos_day_emp() to get the results // - deletes the instance of rppProblem if necessary //---------------------------------------------------------------------- #include "rrproj_prob.h" # include using namespace std; //---------------------------------------------------------------------- // Forward Declarations void write_err (const char *outfilnam, const char *rslt_str); //---------------------------------------------------------------------- int main (int argc, char *argv[]) { if (argc != 3) { cout << "\nUsage: " << "rrproj \n\n"; return 1; } char input_file[201] = ""; if (strlen(argv[1]) > 200) { cout << "\nError: " << "input_file must not be longer than 200 chars\n\n"; return 1; } strcpy (input_file, argv[1]); char output_file[201] = ""; if (strlen(argv[2]) > 200) { cout << "\nError: " << "output_file must be no longer than 200 chars\n\n"; return 1; } strcpy (output_file, argv[2]); int n = 1; // current status rppProblem * problem = 0; if (n) { problem = new rppProblem(); if ( ! problem ) { cout << " Error creating problem\n\n"; n=0; } else if ( ! problem->is_valid() ) { problem->log (" invalid problem"); n=0; } } if (n) { if ( ! problem->read(input_file) ) { n=0; // Error reading input } } if (n) { if ( ! problem->solve() ) { problem->log(" Error solving"); n=0; } } if (n) { rppProbWrite *prob_write = new rppProbWriteWeek1(problem, output_file); if (!prob_write) { problem->log(" Error creating problem-writer"); n=0; } if (n && ! prob_write->write() ) { problem->log(" Error writing"); n=0; } if (prob_write) delete prob_write; } if (!n) write_err (output_file, problem->get_rslt_desc()); if (problem) delete problem; if (!n) return 1; // for main(), this means an error return 0; } //---------------------------------------------------------------------- // Write the description of an error to the output file void write_err (const char *outfilnam, const char *rslt_desc) { FILE *ofp = fopen (outfilnam, "w"); if (!ofp) { printf ("\nError opening output file to describe error\n\n"); return; } fprintf (ofp, "# Error:\n"); fprintf (ofp, "# %s\n", rslt_desc); fclose (ofp); } //====================================================================== // 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. // //====================================================================== rrproj_prob.cpp0000664000076400007640000014024311104463242013105 0ustar brianbrian//====================================================================== // rrproj_prob.cpp - function bodies for rrproj_prob.h // // Copyright (c) 2008 Brian Marshall // // See the license at end of this file. // // Developers/Contributers: // [BRM] Brian Marshall - Calgary - bmarshal@agt.net // // 08/01/27 [BRM] began development // //---------------------------------------------------------------------- #include "rrproj_prob.h" #include "rrproj_fctry.h" #include #include // #include // for debugging // #include // using namespace std; //====================================================================== // local functions // //---------------------------------------------------------------------- // save the solution from a solver-problem in a problem-problem void save_solution (rppProblem *rpp_problem, rpsProblem *rps_problem) { rpsDecision * rd; rpsOption * ro; rpsPosDay * rpd; rpsPosition * rp; rpsEmployee * re; long syyyymmdd; aipHHOption * shhopt; aipHHOptionItr oitr; rpsDecisionItr ditr = rps_problem->decision_iterator(); for ( rd=ditr.first(); rd; rd=ditr.next() ) { oitr = rd->bsf_opt_iterator(); for ( shhopt=oitr.first(); shhopt; shhopt=oitr.next() ) { ro = (rpsOption*)shhopt; rpd = ro->bsf_posday(); rp = rpd->pos(); re = ro->emp(); syyyymmdd = rpd->yyyymmdd(); rpp_problem->add_posdayemp ( rpd->proj_id(), rp->pos_id(), syyyymmdd, re->emp_id() ); } // end of loop through options in best-so-far solution } // end of loop through decisions } //====================================================================== // rppProblem // //---------------------------------------------------------------------- // Constructor rppProblem::rppProblem () { m_start_yyyymmdd = 19000101; m_end_yyyymmdd = 19000101; m_num_tries = 0; m_logger = new aipStringLogger(Max_Log_Len); if (m_logger) set_logger(m_logger); m_posdays = new aipPandemonium; m_empdows = new aipPandemonium; m_empoffs = new aipPandemonium; m_posemps = new aipPandemonium; m_assignments = new aipPandemonium; m_empdaypos = new aipPandemonium; m_posdayemps = new aipPandemonium; m_posdayemp_itr = 0; // managed by get_pos_day_emp() } //---------------------------------------------------------------------- // Destructor rppProblem::~rppProblem () { if (m_logger) delete m_logger; if (m_posdays) delete m_posdays; if (m_empdows) delete m_empdows; if (m_empoffs) delete m_empoffs; if (m_posemps) delete m_posemps; if (m_assignments) delete m_assignments; if (m_empdaypos) delete m_empdaypos; if (m_posdayemps) delete m_posdayemps; if (m_posdayemp_itr) delete m_posdayemp_itr; } //---------------------------------------------------------------------- // Return true if the problem is valid after construction. // Log problems. int rppProblem::is_valid () { if ( ! m_posdays ) { log("Error creating m_posdays"); return 0; } if ( ! m_empdows ) { log("Error creating m_empdows"); return 0; } if ( ! m_empoffs ) { log("Error creating m_empoffs"); return 0; } if ( ! m_posemps ) { log("Error creating m_posemps"); return 0; } if ( ! m_assignments ) { log("Error creating m_assignments"); return 0; } if ( ! m_empdaypos ) { log("Error creating m_empdaypos"); return 0; } if ( ! m_posdayemps ) { log("Error creating m_posdayemps"); return 0; } if (m_posdayemp_itr != 0) { log("m_posdayemp_itr should be zero"); return 0; } return 1; } //---------------------------------------------------------------------- // Read the input data from a file; return 1 (true) on success int rppProblem::read (const char *filename) { FILE *ifp = fopen (filename, "r"); if (!ifp) { log("Error opening input file: "); log(filename); return 0; } int istatus = 1; int rstatus; char buf[201], record_type[31]; const char *p; while (istatus) { rstatus = fgets(buf,200,ifp) != 0; if (!rstatus) break; buf[200] = '\0'; // make sure it is terminated char *q = buf; int anything = 0; while (*q) { // terminate at the newline if ( (*q >= 'A' && *q <= 'Z') || (*q >= 'a' && *q <= 'z') || (*q >= '0' && *q <= '9') ) { anything = 1; // has some data } else if (*q == '\n') { *q = '\0'; break; } q++; } if (buf[0] == '#' || !anything) continue; p = buf; if (*p) p = aip_get_str(p, 30, record_type, '|'); if (!p) continue; if ( strcmp (record_type, "rppProblem") == 0) { istatus = read_str(p); } else if ( strcmp (record_type, "rppPosDay") == 0) { rppPosDay *pd = new rppPosDay; if (!pd) { log("new pd failure"); return 0; } istatus = pd->read_str(p); if (istatus && m_posdays) { m_posdays->add(pd); } else { delete pd; } } else if ( strcmp (record_type, "rppPosEmp") == 0) { rppPosEmp *pqe = new rppPosEmp; if (!pqe) { log("new pqe failure"); return 0; } istatus = pqe->read_str(p); if (istatus && m_posemps) { m_posemps->add(pqe); } else { delete pqe; } } else if ( strcmp (record_type, "rppEmpDow") == 0) { rppEmpDow *edow = new rppEmpDow; if (!edow) { log("new edow failure"); return 0; } istatus = edow->read_str(p); if (istatus && m_empdows) { m_empdows->add(edow); } else { delete edow; } } else if ( strcmp (record_type, "rppEmpOff") == 0) { rppEmpOff *eoff = new rppEmpOff; if (!eoff) { log("new eoff failure"); return 0; } istatus = eoff->read_str(p); if (istatus && m_empoffs) { m_empoffs->add(eoff); } else { delete eoff; } } else if ( strcmp (record_type, "rppAssignment") == 0) { rppAssignment *assg = new rppAssignment; if (!assg) { log("new assg failure"); return 0; } istatus = assg->read_str(p); if (istatus && m_assignments) { m_assignments->add(assg); } else { delete assg; } } else if ( strcmp (record_type, "rppEmpDayPos") == 0) { rppEmpDayPos *edp = new rppEmpDayPos; if (!edp) { log("new edp failure"); return 0; } istatus = edp->read_str(p); if (istatus && m_empdaypos) { m_empdaypos->add(edp); } else { delete edp; } } else { log("Read - bad record_type: "); log(record_type); return 0; } } // end of loop through records in input file if (!istatus) { log(" Error reading input: "); log(buf); } fclose (ifp); return istatus; } //---------------------------------------------------------------------- // Read data from string to setup this object int rppProblem::read_str (const char *x) { m_start_yyyymmdd = m_end_yyyymmdd = 19000101; m_week_start_day = m_num_tries = 0; char s_start_date[31], s_end_date[31]; if (x && *x) x = aip_get_str(x, 30, s_start_date, '|'); if (x && *x) x = aip_get_str(x, 30, s_end_date, '|'); if (x && *x) x = aip_get_val(x, 10, &m_week_start_day, '|'); if (x && *x) x = aip_get_val(x, 10, &m_num_tries, '|'); if (!x) return 0; rppFileDate x_start_date(s_start_date); if ( !x_start_date.is_valid() ) { log("problem start-date must be set and valid"); return 0; } m_start_yyyymmdd = x_start_date.long_date(); if (m_start_yyyymmdd < 19000101 || m_start_yyyymmdd > 21000101) { log("problem start-date must be between 1900 and 2100"); return 0; } rppFileDate x_end_date(s_end_date); if ( !x_end_date.is_valid() ) { log("problem end-date must be set and valid"); return 0; } m_end_yyyymmdd = x_end_date.long_date(); if (m_end_yyyymmdd < 19000101 || m_end_yyyymmdd > 21000101) { log("problem end-date must be between 1900 and 2100"); return 0; } if (m_end_yyyymmdd < m_start_yyyymmdd) { log("problem end-date must be after problem start-date"); return 0; } if (m_week_start_day < 1 || m_week_start_day > 7) { log("week_start_day must be between 1 and 7"); return 0; } if (m_num_tries < 1 || m_num_tries > 100000) { log("num_tries must be between 1 and 100000"); return 0; } return 1; } //---------------------------------------------------------------------- // Write the data in this object to the string; return 0 on failure int rppProblem::write_str (char *x) const { rppFileDate x_start_date(m_start_yyyymmdd); if ( !x_start_date.is_valid() ) return 0; rppFileDate x_end_date(m_end_yyyymmdd); if ( !x_end_date.is_valid() ) return 0; int n = sprintf (x, "rppProblem|%s|%s|%ld|%ld", x_start_date.file_date(), x_end_date.file_date(), m_week_start_day, m_num_tries); return (n ? 1 : 0); } //---------------------------------------------------------------------- // Write the input data to a file; return 1 (true) on success int rppProblem::write_input (const char *filename) { FILE *ofp = fopen (filename, "w"); if (!ofp) { log("Error opening export file"); return 0; } int n = 1; if (n) n = fprintf (ofp, "# rrproj export file\n"); if (n) n = fprintf (ofp, "# \n"); char rec[1001]; if (n) n = write_str(rec); if (n) n = fprintf (ofp, "%s\n", rec); // rppProblem record rppPosDayItr pditr = posday_iterator(); rppPosDay *pd; for (pd = pditr.first(); pd && n; pd = pditr.next()) { if (n) n = pd->write_str(rec); if (n) n = fprintf (ofp, "%s\n", rec); } rppPosEmpItr peitr = posemp_iterator(); rppPosEmp *pe; for (pe = peitr.first(); pe && n; pe = peitr.next()) { if (n) n = pe->write_str(rec); if (n) n = fprintf (ofp, "%s\n", rec); } rppEmpDowItr edowitr = empdow_iterator(); rppEmpDow *edow; for (edow = edowitr.first(); edow && n; edow = edowitr.next()) { if (n) n = edow->write_str(rec); if (n) n = fprintf (ofp, "%s\n", rec); } rppEmpOffItr eoffitr = empoff_iterator(); rppEmpOff *eoff; for (eoff = eoffitr.first(); eoff && n; eoff = eoffitr.next()) { if (n) n = eoff->write_str(rec); if (n) n = fprintf (ofp, "%s\n", rec); } rppAssgnItr assgnitr = assgn_iterator(); rppAssignment *assgn; for (assgn = assgnitr.first(); assgn && n; assgn = assgnitr.next()) { if (n) n = assgn->write_str(rec); if (n) n = fprintf (ofp, "%s\n", rec); } rppEmpDayPosItr edpitr = empdaypos_iterator(); rppEmpDayPos *edp; for (edp = edpitr.first(); edp && n; edp = edpitr.next()) { if (n) n = edp->write_str(rec); if (n) n = fprintf (ofp, "%s\n", rec); } fclose(ofp); if (!n) { log("Error writing export file"); return 0; } return 1; } //---------------------------------------------------------------------- // Return a pointer to a description of the results of trying to // to solve this problem. const char * rppProblem::get_rslt_desc () const { return m_logger->get(); } //---------------------------------------------------------------------- // Add a Position-Day void rppProblem::add_pos_day (long a_proj_id, long a_pos_id, long a_yyyymmdd, long a_num_emps_req) { rppPosDay *x = new rppPosDay (a_proj_id, a_pos_id, a_yyyymmdd, a_num_emps_req); if (x) m_posdays->add(x); } //---------------------------------------------------------------------- // Add an Employee-Day-of-week void rppProblem::add_emp_dow (long a_emp_id, long a_dow, long a_start_hhmm, long a_end_hhmm) { rppEmpDow *x = new rppEmpDow (a_emp_id, a_dow, a_start_hhmm, a_end_hhmm); if (x) m_empdows->add(x); } //---------------------------------------------------------------------- // Add a Employee-Off void rppProblem::add_emp_off (long a_emp_id, long a_start_yyyymmdd, long a_end_yyyymmdd) { rppEmpOff *x = new rppEmpOff (a_emp_id, a_start_yyyymmdd, a_end_yyyymmdd); if (x) m_empoffs->add(x); } //---------------------------------------------------------------------- // Add a Position-Employee pair void rppProblem::add_pos_emp (long a_pos_id, long a_emp_id) { rppPosEmp *x = new rppPosEmp (a_pos_id, a_emp_id); if (x) m_posemps->add(x); } //---------------------------------------------------------------------- // Add an Assignment void rppProblem::add_assignment (long a_proj_id, long a_pos_id, long a_emp_id, long a_start_yyyymmdd, long a_end_yyyymmdd) { rppAssignment *x = new rppAssignment (a_proj_id, a_pos_id, a_emp_id, a_start_yyyymmdd, a_end_yyyymmdd); if (x) m_assignments->add(x); } //---------------------------------------------------------------------- // Add an historic Emp-Day-Pos void rppProblem::add_empdaypos (long a_emp_id, long a_yyyymmdd, long a_proj_id, long a_pos_id) { rppEmpDayPos *x = new rppEmpDayPos (a_emp_id, a_yyyymmdd, a_proj_id, a_pos_id); if (x) m_empdaypos->add(x); } //---------------------------------------------------------------------- // Add a Position-Day-Employee void rppProblem::add_posdayemp (long a_proj_id, long a_pos_id, long a_yyyymmdd, long a_emp_id) { rppPosDayEmp *x = new rppPosDayEmp (a_proj_id, a_pos_id, a_yyyymmdd, a_emp_id); if (x) m_posdayemps->add(x); } //---------------------------------------------------------------------- // Attempt to solve the rrproj problem // // A description of the result of calling this function is logged. // // Return 1 (true) if a solution is found, 0 otherwise. int rppProblem::solve () { rpfFactory *factory = new rpfFactory(this); if (!factory) { log("ABORT - could not make factory"); return 0; } rpsProblem * rps_problem = factory->make_rps_problem(); if (!rps_problem) { log("ABORT - error creating rps_problem"); return 0; } delete factory; // rps_problem->enable_log_tries(); rps_problem->disable_log_tries(); // rps_problem->enable_log_options(); rps_problem->disable_log_options(); int status = rps_problem->solve_rr_problem(); if (status) { log("Solution found."); save_solution(this, rps_problem); } else { log("Solution was not found."); } delete rps_problem; return status; } //---------------------------------------------------------------------- // get_pos_day_emp - get a solution datum, or zero after last int rppProblem::get_pos_day_emp (long *a_proj_id, long *a_pos_id, long *a_yyyymmdd, long *a_emp_id) { rppPosDayEmp *pde; if (m_posdayemp_itr) { pde = m_posdayemp_itr->next(); } else { m_posdayemp_itr = new rppPosDayEmpItr(m_posdayemps); pde = m_posdayemp_itr->first(); } if (!pde) return 0; *a_proj_id = pde->proj_id(); *a_pos_id = pde->pos_id(); *a_yyyymmdd = pde->yyyymmdd(); *a_emp_id = pde->emp_id(); return 1; } //---------------------------------------------------------------------- // Get an iterator for Position-Days in a problem rppPosDayItr rppProblem::posday_iterator () const { rppPosDayItr i(m_posdays); return i; } //---------------------------------------------------------------------- // Get an iterator for Employee-Days-of-week in a problem rppEmpDowItr rppProblem::empdow_iterator () const { rppEmpDowItr i(m_empdows); return i; } //---------------------------------------------------------------------- // Get an iterator for Employee-days-Off in a problem rppEmpOffItr rppProblem::empoff_iterator () const { rppEmpOffItr i(m_empoffs); return i; } //---------------------------------------------------------------------- // Get an iterator for Position-Employee pairs in a problem rppPosEmpItr rppProblem::posemp_iterator () const { rppPosEmpItr i(m_posemps); return i; } //---------------------------------------------------------------------- // Get an iterator for Assignments in a problem rppAssgnItr rppProblem::assgn_iterator () const { rppAssgnItr i(m_assignments); return i; } //---------------------------------------------------------------------- // Get an iterator for historic Emp-Day-Pos in a problem rppEmpDayPosItr rppProblem::empdaypos_iterator () const { rppEmpDayPosItr i(m_empdaypos); return i; } //---------------------------------------------------------------------- // Get an iterator for Position-Day-Employees in a problem rppPosDayEmpItr rppProblem::posdayemp_iterator () const { rppPosDayEmpItr i(m_posdayemps); return i; } //====================================================================== // rppPosDay // //---------------------------------------------------------------------- // Constructor rppPosDay::rppPosDay (long a_proj_id, long a_pos_id, long a_yyyymmdd, long a_num_emps_req, long a_start_hhmm, long a_end_hhmm) { m_proj_id = a_proj_id; m_pos_id = a_pos_id; m_yyyymmdd = a_yyyymmdd; m_num_emps_req = a_num_emps_req; m_start_hhmm = a_start_hhmm; m_end_hhmm = a_end_hhmm; } //---------------------------------------------------------------------- // Destructor rppPosDay::~rppPosDay () {} //---------------------------------------------------------------------- // Read data from string to setup this object int rppPosDay::read_str (const char *x) { m_proj_id = m_pos_id = m_yyyymmdd = 0; m_num_emps_req = m_start_hhmm = m_end_hhmm = 0; char s_date[31], s_start_time[31], s_end_time[31]; if (x && *x) x = aip_get_val(x, 10, &m_proj_id, '|'); if (x && *x) x = aip_get_val(x, 10, &m_pos_id, '|'); if (x && *x) x = aip_get_str(x, 30, s_date, '|'); if (x && *x) x = aip_get_val(x, 10, &m_num_emps_req, '|'); if (x && *x) x = aip_get_str(x, 30, s_start_time, '|'); if (x && *x) x = aip_get_str(x, 30, s_end_time, '|'); if (!x) return 0; rppFileDate x_date(s_date); if ( !x_date.is_valid() ) return 0; m_yyyymmdd = x_date.long_date(); rppFileTime x_start_time(s_start_time); if ( !x_start_time.is_valid() ) return 0; m_start_hhmm = x_start_time.long_time(); rppFileTime x_end_time(s_end_time); if ( !x_end_time.is_valid() ) return 0; m_end_hhmm = x_end_time.long_time(); return 1; } //---------------------------------------------------------------------- // Write the data in this object to the string; return 0 on failure int rppPosDay::write_str (char *x) const { rppFileDate x_date(m_yyyymmdd); if ( !x_date.is_valid() ) return 0; rppFileTime x_start_time(m_start_hhmm); if ( !x_start_time.is_valid() ) return 0; rppFileTime x_end_time(m_end_hhmm); if ( !x_end_time.is_valid() ) return 0; int n = sprintf (x, "rppPosDay|%ld|%ld|%s|%ld|%s|%s", m_proj_id, m_pos_id, x_date.file_date(), m_num_emps_req, x_start_time.file_time(), x_end_time.file_time()); return (n ? 1 : 0); } //====================================================================== // rppEmpDow // //---------------------------------------------------------------------- // Constructor rppEmpDow::rppEmpDow (long a_emp_id, long a_dow, long a_start_hhmm, long a_end_hhmm) { m_emp_id = a_emp_id; m_dow = a_dow; m_start_hhmm = a_start_hhmm; m_end_hhmm = a_end_hhmm; } //---------------------------------------------------------------------- // Destructor rppEmpDow::~rppEmpDow () {} //---------------------------------------------------------------------- // Read data from string to setup this object int rppEmpDow::read_str (const char *x) { char s_start_time[31], s_end_time[31]; if (x && *x) x = aip_get_val(x, 10, &m_emp_id, '|'); if (x && *x) x = aip_get_val(x, 10, &m_dow, '|'); if (x && *x) x = aip_get_str(x, 30, s_start_time, '|'); if (x && *x) x = aip_get_str(x, 30, s_end_time, '|'); if (!x) return 0; rppFileTime x_start_time(s_start_time); if ( !x_start_time.is_valid() ) return 0; m_start_hhmm = x_start_time.long_time(); rppFileTime x_end_time(s_end_time); if ( !x_end_time.is_valid() ) return 0; m_end_hhmm = x_end_time.long_time(); return 1; } //---------------------------------------------------------------------- // Write the data in this object to the string; return 0 on failure int rppEmpDow::write_str (char *x) const { rppFileTime x_start_time(m_start_hhmm); if ( !x_start_time.is_valid() ) return 0; rppFileTime x_end_time(m_end_hhmm); if ( !x_end_time.is_valid() ) return 0; int n = sprintf (x, "rppEmpDow|%ld|%ld|%s|%s", m_emp_id, m_dow, x_start_time.file_time(), x_end_time.file_time()); return (n ? 1 : 0); } //====================================================================== // rppEmpOff // //---------------------------------------------------------------------- // Constructor rppEmpOff::rppEmpOff (long a_emp_id, long a_start_yyyymmdd, long a_end_yyyymmdd) { m_emp_id = a_emp_id; m_start_yyyymmdd = a_start_yyyymmdd; m_end_yyyymmdd = a_end_yyyymmdd; derive_data(); } //---------------------------------------------------------------------- // Destructor rppEmpOff::~rppEmpOff () {} //---------------------------------------------------------------------- // Derive data void rppEmpOff::derive_data() { m_start_day = aipTime(m_start_yyyymmdd); m_end_day = aipTime(m_end_yyyymmdd); } //---------------------------------------------------------------------- // Read data from string to setup this object int rppEmpOff::read_str (const char *x) { char s_start_date[31], s_end_date[31]; if (x && *x) x = aip_get_val(x, 10, &m_emp_id, '|'); if (x && *x) x = aip_get_str(x, 30, s_start_date, '|'); if (x && *x) x = aip_get_str(x, 30, s_end_date, '|'); if (!x) return 0; rppFileDate x_start_date(s_start_date); if ( !x_start_date.is_valid() ) return 0; m_start_yyyymmdd = x_start_date.long_date(); rppFileDate x_end_date(s_end_date); if ( !x_end_date.is_valid() ) return 0; m_end_yyyymmdd = x_end_date.long_date(); if (x) derive_data(); // must get called return 1; } //---------------------------------------------------------------------- // Write the data in this object to the string; return 0 on failure int rppEmpOff::write_str (char *x) const { rppFileDate x_start_date(m_start_yyyymmdd); if ( !x_start_date.is_valid() ) return 0; rppFileDate x_end_date(m_end_yyyymmdd); if ( !x_end_date.is_valid() ) return 0; int n = sprintf (x, "rppEmpOff|%ld|%s|%s", m_emp_id, x_start_date.file_date(), x_end_date.file_date()); return (n ? 1 : 0); } //====================================================================== // rppPosEmp // //---------------------------------------------------------------------- // Constructor rppPosEmp::rppPosEmp (long a_pos_id, long a_emp_id) { m_pos_id = a_pos_id; m_emp_id = a_emp_id; } //---------------------------------------------------------------------- // Destructor rppPosEmp::~rppPosEmp () {} //---------------------------------------------------------------------- // Read data from string to setup this object int rppPosEmp::read_str (const char *x) { if (x && *x) x = aip_get_val(x, 10, &m_pos_id, '|'); if (x && *x) x = aip_get_val(x, 10, &m_emp_id, '|'); return (x ? 1 : 0); } //---------------------------------------------------------------------- // Write the data in this object to the string; return 0 on failure int rppPosEmp::write_str (char *x) const { int n = sprintf (x, "rppPosEmp|%ld|%ld", m_pos_id, m_emp_id); return (n ? 1 : 0); } //====================================================================== // rppAssignment // //---------------------------------------------------------------------- // Constructor rppAssignment::rppAssignment (long a_proj_id, long a_pos_id, long a_emp_id, long a_start_yyyymmdd, long a_end_yyyymmdd) { m_proj_id = a_proj_id; m_pos_id = a_pos_id; m_emp_id = a_emp_id; m_start_yyyymmdd = a_start_yyyymmdd; m_end_yyyymmdd = a_end_yyyymmdd; } //---------------------------------------------------------------------- // Destructor rppAssignment::~rppAssignment () {} //---------------------------------------------------------------------- // Read data from string to setup this object int rppAssignment::read_str (const char *x) { char s_start_date[31], s_end_date[31]; if (x && *x) x = aip_get_val(x, 10, &m_proj_id, '|'); if (x && *x) x = aip_get_val(x, 10, &m_pos_id, '|'); if (x && *x) x = aip_get_val(x, 10, &m_emp_id, '|'); if (x && *x) x = aip_get_str(x, 10, s_start_date, '|'); if (x && *x) x = aip_get_str(x, 10, s_end_date, '|'); if (!x) return 0; rppFileDate x_start_date(s_start_date); if ( !x_start_date.is_valid() ) return 0; m_start_yyyymmdd = x_start_date.long_date(); rppFileDate x_end_date(s_end_date); if ( !x_end_date.is_valid() ) return 0; m_end_yyyymmdd = x_end_date.long_date(); return 1; } //---------------------------------------------------------------------- // Write the data in this object to the string; return 0 on failure int rppAssignment::write_str (char *x) const { rppFileDate x_start_date(m_start_yyyymmdd); if ( !x_start_date.is_valid() ) return 0; rppFileDate x_end_date(m_end_yyyymmdd); if ( !x_end_date.is_valid() ) return 0; int n = sprintf (x, "rppAssignment|%ld|%ld|%ld|%s|%s", m_proj_id, m_pos_id, m_emp_id, x_start_date.file_date(), x_end_date.file_date()); return (n ? 1 : 0); } //====================================================================== // rppEmpDayPos // //---------------------------------------------------------------------- // Constructor rppEmpDayPos::rppEmpDayPos (long a_emp_id, long a_yyyymmdd, long a_proj_id, long a_pos_id) { m_emp_id = a_emp_id; m_yyyymmdd = a_yyyymmdd; m_proj_id = a_proj_id; m_pos_id = a_pos_id; } //---------------------------------------------------------------------- // Destructor rppEmpDayPos::~rppEmpDayPos () {} //---------------------------------------------------------------------- // Read data from string to setup this object int rppEmpDayPos::read_str (const char *x) { char s_date[31]; if (x && *x) x = aip_get_val(x, 10, &m_emp_id, '|'); if (x && *x) x = aip_get_str(x, 30, s_date, '|'); if (x && *x) x = aip_get_val(x, 10, &m_proj_id, '|'); if (x && *x) x = aip_get_val(x, 10, &m_pos_id, '|'); if (!x) return 0; rppFileDate x_date(s_date); if ( !x_date.is_valid() ) return 0; m_yyyymmdd = x_date.long_date(); return 1; } //---------------------------------------------------------------------- // Write the data in this object to the string; return 0 on failure int rppEmpDayPos::write_str (char *x) const { rppFileDate x_date(m_yyyymmdd); if ( !x_date.is_valid() ) return 0; int n = sprintf (x, "rppEmpDayPos|%ld|%s|%ld|%ld", m_emp_id, x_date.file_date(), m_proj_id, m_pos_id); return (n ? 1 : 0); } //====================================================================== // rppProjWeekEmpDay // //---------------------------------------------------------------------- // Constructor rppProjWeekEmpDay::rppProjWeekEmpDay () { m_proj_id = m_week_num = m_emp_id = 0; m_week_pos = m_yyyymmdd = m_pos_id = 0; } //---------------------------------------------------------------------- // Destructor rppProjWeekEmpDay::~rppProjWeekEmpDay () {} //---------------------------------------------------------------------- // Set the data members void rppProjWeekEmpDay::set (long a_proj_id, long a_week_num, long a_emp_id, long a_week_pos, long a_yyyymmdd, long a_pos_id) { m_proj_id = a_proj_id; m_week_num = a_week_num; m_emp_id = a_emp_id; m_week_pos = a_week_pos; m_yyyymmdd = a_yyyymmdd; m_pos_id = a_pos_id; } //====================================================================== // rppPosDayEmp // //---------------------------------------------------------------------- // Constructor rppPosDayEmp::rppPosDayEmp (long a_proj_id, long a_pos_id, long a_yyyymmdd, long a_emp_id) { m_proj_id = a_proj_id; m_pos_id = a_pos_id; m_yyyymmdd = a_yyyymmdd; m_emp_id = a_emp_id; } //---------------------------------------------------------------------- // Destructor rppPosDayEmp::~rppPosDayEmp () {} //---------------------------------------------------------------------- // Write the header for the data in this object int rppPosDayEmp::write_hdr (char *x) const { int n = sprintf (x, "\n#rppPosDayEmp|proj_id|pos_id|date|emp_id"); return (n ? 1 : 0); } //---------------------------------------------------------------------- // Write the data in this object to the string; return 0 on failure int rppPosDayEmp::write_str (char *x) const { rppFileDate x_date(m_yyyymmdd); if ( !x_date.is_valid() ) return 0; int n = sprintf (x, "rppPosDayEmp|%ld|%ld|%s|%ld", m_proj_id, m_pos_id, x_date.file_date(), m_emp_id); return (n ? 1 : 0); } //====================================================================== // rppProbWrite // //---------------------------------------------------------------------- // Constructor rppProbWrite::rppProbWrite (const rppProblem *x, const char * out_file_name) : m_prob(x) { if (out_file_name) { aip_strncpy (m_out_file_name, out_file_name, Max_File_Name); } m_any_pos_day_emp_written = 0; } //---------------------------------------------------------------------- // Destructor rppProbWrite::~rppProbWrite () {} //---------------------------------------------------------------------- // Write the solution data - return zero on error int rppProbWrite::write () { FILE *ofp = fopen (m_out_file_name, "w"); if (!ofp) { log("Error opening output file: "); log(m_out_file_name); return 0; } if ( !pre_proc(ofp) ) return 0; rppPosDayEmpItr pdeitr = m_prob->posdayemp_iterator(); rppPosDayEmp *pde; for (pde = pdeitr.first(); pde; pde = pdeitr.next()) { if ( !handle_pos_day_emp(ofp, pde) ) return 0; } // end of loop through pos-day-emp data if ( !post_proc(ofp) ) return 0; fclose(ofp); return 1; } //---------------------------------------------------------------------- // pre_proc - called before solution-detail data is handled int rppProbWrite::pre_proc (FILE *ofp) { if (!ofp) return 0; int n = 1; if (n) n = fprintf (ofp, "# rrproj output file\n\n"); if (n) n = fprintf (ofp, "rppRsltDscr|%s\n", m_prob->get_rslt_desc()); if (!n) { log("Error writing beginning of output file"); return 0; } return 1; } //---------------------------------------------------------------------- // handle a normalized solution detail int rppProbWrite::handle_pos_day_emp (FILE *ofp, const rppPosDayEmp *pde) { if (!ofp || !pde) return 0; char rec[201]; if (!m_any_pos_day_emp_written) { if ( !pde->write_hdr(rec) ) { log("Error writing pos-emp-day header to the output string"); return 0; } if ( !fprintf (ofp, "%s\n", rec) ) { log("Error writing pos-emp-day header to the output file"); return 0; } m_any_pos_day_emp_written = 1; } if ( !pde->write_str(rec) ) { log("Error writing pos-emp-day data to the output string"); return 0; } if ( !fprintf (ofp, "%s\n", rec) ) { log("Error writing pos-emp-day data to the output file"); return 0; } return 1; } //---------------------------------------------------------------------- // post_proc - called after solution-detail data is handled int rppProbWrite::post_proc (FILE *ofp) { if (!ofp) return 0; return 1; } //---------------------------------------------------------------------- // write a blank line to the output file int rppProbWrite::write_blank_line (FILE *ofp) { if (!ofp) return 0; if ( !fprintf (ofp, "\n") ) { log("Error writing blank line to the output file"); return 0; } return 1; } //====================================================================== // rppProbWriteWeek1 // //---------------------------------------------------------------------- // Constructor rppProbWriteWeek1::rppProbWriteWeek1 (const rppProblem *x, const char *out_file_name) : rppProbWrite(x,out_file_name) { set_week1_start_dt(); m_proj_week_emp_days = new aipPandemonium; if (!m_proj_week_emp_days) { log("new m_proj_week_emp_days error"); } m_proj_week = new rppProjWeek; if (!m_proj_week) { log("new m_proj_week error"); } m_proj_week_emp = new rppProjWeekEmp; if (!m_proj_week_emp) { log("new m_proj_week_emp error"); } reset(); } //---------------------------------------------------------------------- // Destructor rppProbWriteWeek1::~rppProbWriteWeek1 () { if (m_proj_week_emp_days) delete m_proj_week_emp_days; if (m_proj_week) delete m_proj_week; if (m_proj_week_emp) delete m_proj_week_emp; } //---------------------------------------------------------------------- // Return zero if this object is not valid int rppProbWriteWeek1::is_valid () const { if (!m_proj_week_emp_days || !m_proj_week || !m_proj_week_emp) return 0; return 1; } //---------------------------------------------------------------------- // Reset current data - ie. everything except m_week1_start_dt void rppProbWriteWeek1::reset () { if (!is_valid()) return; m_proj_week->reset(); m_proj_week_emp->reset(); m_any_week_data = m_any_emp_data = 0; } //---------------------------------------------------------------------- // Set the date of the first day of week 1 void rppProbWriteWeek1::set_week1_start_dt () { aipTime prob_start_dt(prob()->start_yyyymmdd()); aipTime prob_end_dt(prob()->end_yyyymmdd()); m_week1_start_dt = prob_start_dt; long prob_start_day = prob_start_dt.days_since_sunday(); long wk_offset = (prob_start_day + 7 - prob()->week_start_day()) % 7; if (wk_offset) { wk_offset = 0 - wk_offset; // now zero or negative m_week1_start_dt.add_days(wk_offset); } // if the problem will fit into a week, force it if necessary long prob_num_days = prob_end_dt.days_since(prob_start_dt) + 1; if (prob_num_days <= 7) { long wnum1, wpos1, wnum2, wpos2; if( !week_dt(prob_start_dt, &wnum1, &wpos1) ) return; if( !week_dt(prob_end_dt, &wnum2, &wpos2) ) return; if (wnum1 != wnum2) { m_week1_start_dt = prob_start_dt; } } } //---------------------------------------------------------------------- // Determine the week-number and week_position (1 to 7) of a date. // Return zero on failure. int rppProbWriteWeek1::week_dt (aipTime x, long *week_num, long *week_pos) const { if ( !week_num || !week_pos ) return 0; long a, b; long ddif = x.days_after(m_week1_start_dt); if (ddif < -50000 || ddif > 50000) return 0; if (ddif >= 0) { a = 0; b = 1; } else { a = 1; b = 0; } *week_num = (ddif + a) / 7 + b; *week_pos = ((ddif + 70000) % 7) + 1; return 1; } //---------------------------------------------------------------------- // handle a normalized solution detail // // add the data to m_proj_week_emp_days to reorder it int rppProbWriteWeek1::handle_pos_day_emp (FILE *ofp, const rppPosDayEmp *pde) { if (!ofp || !pde || !is_valid()) return 0; rppProjWeekEmpDay *pwed = new rppProjWeekEmpDay; if (!pwed) { log("new rppProjWeekEmpDay error"); return 0; } long wnum, wpos; week_dt (pde->yyyymmdd(), &wnum, &wpos); pwed->set ( pde->proj_id(), wnum, pde->emp_id(), wpos, pde->yyyymmdd(), pde->pos_id() ); if (m_proj_week_emp_days) m_proj_week_emp_days->add(pwed); return 1; } //---------------------------------------------------------------------- // post_proc - called after solution-detail data is handled int rppProbWriteWeek1::post_proc (FILE *ofp) { if (!ofp || !is_valid()) return 0; int have_written_week_hdr = 0; int have_written_emp_hdr = 0; if ( !write_blank_line(ofp) ) return 0; rppProjWeekEmpDayItr pwed_itr = projweekempday_iterator(); rppProjWeekEmpDay *pwed; for (pwed=pwed_itr.first(); pwed; pwed=pwed_itr.next()) { if (m_any_week_data) { if (pwed->proj_id() != m_proj_week->proj_id() || pwed->week_num() != m_proj_week->week_num()) { m_proj_week->reset(); m_any_week_data = 0; } } if (m_any_emp_data) { if (pwed->proj_id() != m_proj_week_emp->proj_id() || pwed->week_num() != m_proj_week_emp->week_num() || pwed->emp_id() != m_proj_week_emp->emp_id()) { if (!have_written_emp_hdr) { if ( !m_proj_week_emp->write_hdr(ofp) ) return 0; have_written_emp_hdr = 1; } if ( !m_proj_week_emp->write(ofp) ) return 0; m_proj_week_emp->reset(); m_any_emp_data = 0; } } if (!m_any_week_data) { if (!have_written_week_hdr) { if ( !m_proj_week->write_hdr(ofp) ) return 0; have_written_week_hdr = 1; } if ( !set_proj_week(pwed) ) return 0; // set whole week if ( !m_proj_week->write(ofp) ) return 0; if ( !m_any_week_data ) m_any_week_data = 1; } if ( !add_to_proj_week_emp(pwed) ) return 0; // set one day if (!m_any_emp_data) m_any_emp_data = 1; } // end of loop through proj-week-emp-days if (m_any_emp_data) { if (!have_written_emp_hdr) { if ( !m_proj_week_emp->write_hdr(ofp) ) return 0; // have_written_emp_hdr = 1; not used } if ( !m_proj_week_emp->write(ofp) ) return 0; m_proj_week->reset(); m_proj_week_emp->reset(); m_any_emp_data = 0; } return 1; } //---------------------------------------------------------------------- // set m_proj_week for the week containing pwed int rppProbWriteWeek1::set_proj_week (rppProjWeekEmpDay *pwed) { m_proj_week->set_pw (pwed->proj_id(), pwed->week_num()); aipTime x_dt(pwed->yyyymmdd()); long diff = 1 - pwed->week_pos(); // zero or negative if (diff) x_dt.add_days(diff); long x_yyyymmdd; for (int iday=1; iday<=7; iday++) { x_yyyymmdd = x_dt.yyyymmdd(); m_proj_week->set_day (iday, x_yyyymmdd); x_dt.add_days(1); } return 1; } //---------------------------------------------------------------------- // set m_proj_week_emp for the day of pwed // set key data members if required int rppProbWriteWeek1::add_to_proj_week_emp (rppProjWeekEmpDay *pwed) { if (!m_any_emp_data) { m_proj_week_emp->set_pwe (pwed->proj_id(), pwed->week_num(), pwed->emp_id()); } m_proj_week_emp->set_day(pwed->week_pos(), pwed->pos_id()); return 1; } //---------------------------------------------------------------------- // Get an iterator for Proj-Week-Emp-Day in a Prob-Write-Week1 rppProjWeekEmpDayItr rppProbWriteWeek1::projweekempday_iterator () const { rppProjWeekEmpDayItr i(m_proj_week_emp_days); return i; } //====================================================================== // rppProjWeek // //---------------------------------------------------------------------- // reset this master-data void rppProjWeek::reset () { m_proj_id = m_week_num = -1; for (int i=0; i<7; i++) m_yyyymmdd[i] = -1; } //---------------------------------------------------------------------- // Write the header for the data in this object to the string; // return 0 on failure int rppProjWeek::write_hdr (char *x) const { int n = sprintf (x, "#rppProjWeek|proj|week|..."); return (n ? 1 : 0); } //---------------------------------------------------------------------- // Write the header for the data in this object to a file; // return 0 on failure int rppProjWeek::write_hdr (FILE *ofp) const { char rec[201]; if ( !write_hdr(rec) ) { log("Error writing proj_week to header string"); return 0; } if ( !fprintf (ofp, "%s\n", rec) ) { log("Error writing proj_week to header file"); return 0; } return 1; } //---------------------------------------------------------------------- // Write the data in this object to the string; return 0 on failure int rppProjWeek::write_str (char *x) const { long l_dom; // dom = day-of-month char s_mon[4], s_dow[4]; // dow = day-of-week int nn = sprintf (x, "rppProjWeek|%ld|%ld", m_proj_id, m_week_num); if (!nn) return 0; int n = nn; // total chars written = chars just written for (int i=0; i<7; i++) { if (m_yyyymmdd[i] <= 0) return 0; aipTime dt(m_yyyymmdd[i]); if (!dt.is_valid()) return 0; dt.get (0, 0, &l_dom, 0, 0, 0, s_mon, s_dow); if (i != 0 && l_dom != 1) strcpy (s_mon, " "); nn = sprintf (x+n, "|%s|%ld|%s", s_mon, l_dom, s_dow); if (!nn) return 0; n += nn; } // end of loop through day in week return 1; } //---------------------------------------------------------------------- // Write the data in this object to a file; return 0 on failure int rppProjWeek::write (FILE *ofp) const { char rec[201]; if ( !write_str(rec) ) { log("Error writing proj_week to output string"); return 0; } if ( !fprintf (ofp, "%s\n", rec) ) { log("Error writing proj_week to output file"); return 0; } return 1; } //====================================================================== // rppProjWeekEmp // //---------------------------------------------------------------------- // Reset this set of detail data void rppProjWeekEmp::reset () { m_proj_id = m_week_num = m_emp_id = -1; for (int i=0; i<7; i++) m_pos_id[i] = -1; } //---------------------------------------------------------------------- // Write the header for the data in this object to the string; // return 0 on failure int rppProjWeekEmp::write_hdr (char *x) const { char fmt[201]; strcpy (fmt, "#rppProjWeekEmp|proj|week|emp"); strcat (fmt, "|day1pos_id|day2pos_id|..."); int n = sprintf (x, fmt); return (n ? 1 : 0); } //---------------------------------------------------------------------- // Write the header for the data in this object to a file; // return 0 on failure int rppProjWeekEmp::write_hdr (FILE *ofp) const { char rec[201]; if ( !write_hdr(rec) ) { log("Error writing proj_week_emp to header string"); return 0; } if ( !fprintf (ofp, "%s\n", rec) ) { log("Error writing proj_week_emp to header file"); return 0; } return 1; } //---------------------------------------------------------------------- // Write the data in this object to the string; return 0 on failure int rppProjWeekEmp::write_str (char *x) const { int nn = sprintf (x, "rppProjWeekEmp|%ld|%ld|%ld", m_proj_id, m_week_num, m_emp_id); if (!nn) return 0; int n = nn; // total chars written = chars just written for (int i=0; i<7; i++) { if (m_pos_id[i] >= 0) { nn = sprintf (x+n, "|%ld", m_pos_id[i]); } else { nn = sprintf (x+n, "| "); } if (!nn) return 0; n += nn; } // end of loop through day in week return 1; } //---------------------------------------------------------------------- // Write the data in this object to a file; return 0 on failure int rppProjWeekEmp::write (FILE *ofp) const { char rec[201]; if ( !write_str(rec) ) { log("Error writing proj_week_emp to output string"); return 0; } if ( !fprintf (ofp, "%s\n", rec) ) { log("Error writing proj_week_emp to output file"); return 0; } return 1; } //====================================================================== // Iterators // // All function bodies are in the header file // //====================================================================== // rppFileDate // //---------------------------------------------------------------------- // Constructor - from a string "yyyy-mm-dd" rppFileDate::rppFileDate (const char *a_yyyy_mm_dd) { char *p = m_yyyy_mm_dd; aip_strncpy (p, a_yyyy_mm_dd, File_Date_Len); if ( strlen(p) != File_Date_Len) { reset(); return; } p[4] = p[7] = '\0'; long yr = atol(p); long mo = atol(p+5); long dy = atol(p+8); if ( yr < 1800 || yr > 2300 || mo < 1 || mo > 12 || dy < 1 || dy > aip_days_in_month(yr,mo) ) { reset(); return; } m_yyyymmdd = yr*10000 + mo*100 + dy; } //---------------------------------------------------------------------- // Constructor - from a long yyyymmdd rppFileDate::rppFileDate (long a_yyyymmdd) { long yr = a_yyyymmdd / 10000; a_yyyymmdd -= (yr * 10000); long mo = a_yyyymmdd / 100; a_yyyymmdd -= (mo * 100); long dy = a_yyyymmdd; if ( yr < 1800 || yr > 2300 || mo < 1 || mo > 12 || dy < 1 || dy > aip_days_in_month(yr,mo) ) { reset(); return; } sprintf (m_yyyy_mm_dd, "%ld-%02ld-%02ld", yr, mo, dy); } //---------------------------------------------------------------------- // Destructor rppFileDate::~rppFileDate () {} //---------------------------------------------------------------------- // Return true if the date is valid int rppFileDate::is_valid () { return ( m_yyyymmdd ? 1 : 0 ); } //====================================================================== // rppFileTime // //---------------------------------------------------------------------- // Constructor - from a string "hh:mm" rppFileTime::rppFileTime (const char *a_hh_mm) { char *p = m_hh_mm; aip_strncpy (p, a_hh_mm, File_Time_Len); if ( strlen(p) != File_Time_Len) { reset(); return; } if (p[1] == ':') { p[1] = '\0'; } else { p[2] = '\0'; } long hr = atol(p); long mn = atol(p+3); if ( hr < 0 || hr > 23 || mn < 0 || mn > 59 ) { reset(); return; } m_hhmm = hr*100 + mn; } //---------------------------------------------------------------------- // Constructor - from a long hhmm rppFileTime::rppFileTime (long a_hhmm) { long hr = a_hhmm / 100; a_hhmm -= (hr * 100); long mn = a_hhmm; if ( hr < 0 || hr > 23 || mn < 0 || mn > 59 ) { reset(); return; } sprintf (m_hh_mm, "%02ld:%02ld", hr, mn); } //---------------------------------------------------------------------- // Destructor rppFileTime::~rppFileTime () {} //---------------------------------------------------------------------- // Return true if the time is valid int rppFileTime::is_valid () { return ( m_hhmm<2400 ? 1 : 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. // //====================================================================== rrproj_prob.h0000755000076400007640000010504411104463242012553 0ustar brianbrian//********************************************************************** // rrproj_prob.h - rrproj Problem // // An rrproj problem is the problem of assigning people and/or // equipment to projects. An application includes this file to // define and solve this type of problem. // // Copyright (c) 2008 Brian Marshall // // See the license at end of this file. // // Developers/Contributers: // [BRM] Brian Marshall - Calgary - bmarshal@agt.net // // 08/01/19 [BRM] began development // //---------------------------------------------------------------------- // Required Source Files // // To use the solve() function, the following files must be compiled: // rrproj_prob.cpp (with rrproj_prob.h) // rrproj_solv.cpp (with rrproj_solv.h) // rrproj_fctry.cpp (with rrproj_fctry.h) // aipReqRes.cpp (with aipReqRes.h) // aipHighHope.cpp (with aipHighHope.h) // aipProblem.cpp (with aipProblem.h) // aipDecision.cpp (with aipDecision.h) // aipEmotion.cpp (with aipEmotion.h) // aipPandemonium.cpp (with aipPandemonium.h) // aipBase.cpp (with aipBase.h) // aipGood.cpp (with aipGood.h) // //---------------------------------------------------------------------- // Data relationships in a rrproj Problem // // If A is joined to B by a line, and A is higher than B in the // diagram, there is a one-to-many relationship between A and B. // // angle brackets indicate output solution data // {abc} curly braces indicate virtual datasets // // [Problem]____[ProbWriter] // _________| | |__________ // | | | // {Position} {Project} | _{Employee}__________ // | | | | | | | | | | |_______ | // | | | [Pos_Day] | | | | | | | // | | | | | | | | [Emp_Dow] | | // | | | ____| | | | | // | | | | [Emp_Off] | | // | | |__________[Pos_Emp]_______| | | // | | | | // | |___________[Emp_Day_Pos]_____________________| | // | | // |_______________[Assignment]________________________| // // // // Pos_Day_Emp is the normalized output solution data. // Emp_Day_Pos is for (optional) historic data. // Other entities define the problem to be solved. // // If the problem is configured with an rppProbWriteWeek1 // for a [ProbWriter], the output solution data is // denormalized into weeks: // // [Problem]____[rppProbWriteWeek1] // _________| | |__________ // | | | // {Position} {Project} | {Employee} // | | | | | // [Pos_Day] <> | | // | | | | // | <>_| | // | | | // _______| // // // is the normalized solution data // is reordered noralized solution data // <> and <> are the solution data // by project-week: master and detail data // which are created from // // The data is stored in the order indicated by the name. // Input data can be provided in any order. // // In this software, Problem, Project, Position and Employee are // virtual datasets. They would be in a logical data model, but // they are not datasets in this software. // //---------------------------------------------------------------------- // Optionally providing historic Emp_Day_Pos data // // If an application expects the solver to consider data prior to // the start of the problem, historic Emp_Day_Pos data must // be supplied before the problem solve() function is called. // // Adding Emp_Day_Pos data is optional, and it may be added for // just the employees for which it matters. // // Examples requiring (some) historic data: // - It may be desirable to rotate through (some) employees // (ex. part-time employees, contractors, volunteers). // - It is desirable, for an employee who worked a position yesterday // to work the same position today (if the employee works today). // // Currently, the minimum desirable amount of historic Emp_Day_Pos // data is the last day worked (before the first day of the problem) // for each employee. Employees may be omitted if they worked // in the last couple of days before the problem. // // The solver does not get historic Emp_Day_Pos data; this data is // used to set attributes in entities that are part of the solver. // // The output solution data does not include the historic data. // //---------------------------------------------------------------------- // Using this software in an application // // The application will create an instance of class rppProblem // and call (some of) its public functions: // either: // - call functions to add the problem data to the rppProblem // or: // - call the problem read() function // call solve() to solve the problem // either: // - call get_rslt_desc() // - call get_pos_day_emp () to get the solution // or: // - create an instance of a subclass of rppProbWrite // - call the write() function of the instance // and then delete the instance of rrpProblem if required. // // The char* pointer returned from rrpProblem::get_rslt_desc() // must not be used after the rrpProblem is deleted. // // A subclass of rppProbWrite can denormalize the data in a // particular way - it customizes the solution data file // for one or more applications. // // The application developer does not have to know or care about: // - how rrpProblem stores data // - how the public functions work // - anything about any of the other classes in this file // //---------------------------------------------------------------------- // What rppProblem::solve() does (optional information) // // When solve() is called, it: // - creates an instance of rpsFactory // - uses the factory with the problem data to build a solver // - deletes the factory // - calls the solver solve() function // - copies the solution data to the problem data // - deletes the solver // //---------------------------------------------------------------------- #ifndef rrproj_prob_h_guard #define rrproj_prob_h_guard #include "aipPandemonium.h" #include "aipTime.h" #include //---------------------------------------------------------------------- // Local Constants const long Max_Str_Key_Len = 30; const long File_Date_Len = 10; // "yyyy-mm-dd" const long File_Time_Len = 5; // "hh:mm" const long Max_Log_Len = 300; const long Max_File_Name = 100; //---------------------------------------------------------------------- // Classes. Sub-classing is shown by indentation. // // class aipG is a typedef for aipGoodness. // // A class is a subclass of aipDemon (a list element) so that // a pointer to it can be added to pandemoniums (lists). // class aipBase; ( in aipBase.h ) class rppProblem; // rrproj problem // class aipDemon; ( in aipPandemonium.h ) class rppPosDay; // position on a day class rppEmpDow; // days-of-week the emp can work class rppEmpOff; // days an emp cannot work class rppPosEmp; // pos and an emp that can work it class rppAssignment; // an emp pre-assigned to a pos class rppEmpDayPos; // historic solution data class rppPosDayEmp; // normalized solution data class rppProjWeekEmpDay; // re-ordered normalized solution class rppProjWeek; // output master for a week class rppProjWeekEmp; // output detail for a week class rppProbWrite; // problem solution-writer class rppProbWriteWeek1; // week-based writer number 1 // class aipLogger; ( in aipBase.h ) // class aipStringLogger; ( in aipBase.h ) // class aipDemonItr; ( in aipPandemonium.h ) class rppPosDayItr; // position-day iterator class rppEmpItr; // employee iterator class rppEmpDowItr; // emp-off iterator class rppEmpOffItr; // emp-off iterator class rppPosEmpItr; // position-employee-pair iterator class rppAssgnItr; // assignment iterator class rppEmpDayPosItr; // historic emp-day-pos iterator class rppPosDayEmpItr; // normalized-output iterator class rppProjWeekEmpDayItr; // re-ordered output iterator class rppProjWeekItr; // output-master iterator class rppProjWeekEmpItr; // output-detail iterator class rppFileDate; // read/write dates in a file class rppFileTime; // read/write times in a file //====================================================================== // rppProblem - an rrproj problem as defined by the application class rppProblem : public aipBase { long m_start_yyyymmdd; long m_end_yyyymmdd; long m_week_start_day; // 0=Sunday long m_num_tries; aipStringLogger * m_logger; // for calls to log() aipPandemonium * m_posdays; // problem data aipPandemonium * m_empdows; aipPandemonium * m_empoffs; aipPandemonium * m_posemps; aipPandemonium * m_assignments; aipPandemonium * m_empdaypos; // historic solution data aipPandemonium * m_posdayemps; // output solution data rppPosDayEmpItr * m_posdayemp_itr; // for get_pos_day_emp() public: // Functions Used by the Application rppProblem (); virtual ~rppProblem (); int is_valid (); // returns true if valid virtual int read (const char *filename); // return 0 on failure // for problem data record - returns 0 on failure virtual int read_str (const char *x); virtual int write_str (char *x) const; virtual int write_input (const char *filename); const char * get_rslt_desc () const; // or i/o by calls void add_pos_day (long a_proj_id, long a_pos_id, long a_yyyymmdd, long a_num_emps_req); void add_emp_dow (long a_emp_id, long a_dow, long a_start_hhmm, long a_end_hhmm); void add_emp_off (long a_emp_id, long a_start_yyyymmdd, long a_end_yyyymmdd); void add_pos_emp (long a_pos_id, long a_emp_id); void add_assignment (long a_proj_id, long a_pos_id, long a_emp_id, long a_start_yyyymmdd, long a_end_yyyymmdd); void add_empdaypos (long a_emp_id, long a_yyyymmdd, // historic long a_proj_id, long a_pos_id); void add_posdayemp (long a_proj_id, long a_pos_id, // solution long a_yyyymmdd, long a_emp_id); virtual int solve(); // returns 1 (true) on success // get solution, returns 0 after last int get_pos_day_emp (long *a_proj_id, long *a_pos_id, long *a_yyyymmdd, long *a_emp_id); // Functions Used Internally long start_yyyymmdd () const { return m_start_yyyymmdd; } long end_yyyymmdd () const { return m_end_yyyymmdd; } long week_start_day () const { return m_week_start_day; } long num_tries () const { return m_num_tries; } rppPosDayItr posday_iterator () const; // get an iterator rppEmpDowItr empdow_iterator () const; rppEmpOffItr empoff_iterator () const; rppPosEmpItr posemp_iterator () const; rppAssgnItr assgn_iterator () const; rppEmpDayPosItr empdaypos_iterator () const; rppPosDayEmpItr posdayemp_iterator () const; }; //====================================================================== // rppPosDay - Position on a Day that requires Employees class rppPosDay : public aipDemon { long m_proj_id; long m_pos_id; long m_yyyymmdd; long m_num_emps_req; // employees required long m_start_hhmm; // 1430 is 2:30 pm long m_end_hhmm; public: rppPosDay () {} rppPosDay (long a_proj_id, long a_pos_id, long a_yyyymmdd, long a_num_emps_req, long a_start_hhmm =0, long a_end_hhmm =0); virtual ~rppPosDay (); virtual long num_keys (void) const { return 3; } virtual long key1 (void) const { return m_proj_id; } virtual long key2 (void) const { return m_pos_id; } virtual long key3 (void) const { return m_yyyymmdd; } // returns 0 on failure virtual int read_str (const char *x); virtual int write_str (char *x) const; long proj_id () const { return m_proj_id; } long pos_id () const { return m_pos_id; } long yyyymmdd () const { return m_yyyymmdd; } long num_emps_req () const { return m_num_emps_req; } long start_hhmm () const { return m_start_hhmm; } long end_hhmm () const { return m_end_hhmm; } }; //====================================================================== // rppEmpDow - Day-of-week on which the employee can work class rppEmpDow : public aipDemon { long m_emp_id; long m_dow; // ex: 0=sun, 1=mon, 2=tue, etc. long m_start_hhmm; // 1430 is 2:30 pm long m_end_hhmm; public: rppEmpDow () {} rppEmpDow (long a_emp_id, long a_dow, long a_start_hhmm, long a_end_hhmm); virtual ~rppEmpDow (); virtual long num_keys (void) const { return 2; } virtual long key1 (void) const { return m_emp_id; } virtual long key2 (void) const { return m_dow; } // returns 0 on failure virtual int read_str (const char *x); virtual int write_str (char *x) const; long emp_id () const { return m_emp_id; } long dow () const { return m_dow; } long start_hhmm () const { return m_start_hhmm; } long end_hhmm () const { return m_end_hhmm; } }; //====================================================================== // rppEmpOff - Days on which the employee cannot work class rppEmpOff : public aipDemon { long m_emp_id; long m_start_yyyymmdd; long m_end_yyyymmdd; aipTime m_start_day; // derived aipTime m_end_day; // derived protected: void derive_data(); public: rppEmpOff () {} rppEmpOff (long a_emp_id, long a_start_yyyymmdd, long a_end_yyyymmdd); virtual ~rppEmpOff (); virtual long num_keys (void) const { return 3; } virtual long key1 (void) const { return m_emp_id; } virtual long key2 (void) const { return m_start_yyyymmdd; } virtual long key3 (void) const { return m_end_yyyymmdd; } // returns 0 on failure virtual int read_str (const char *x); virtual int write_str (char *x) const; long emp_id () const { return m_emp_id; } long start_yyyymmdd () const { return m_start_yyyymmdd; } long end_yyyymmdd () const { return m_end_yyyymmdd; } aipTime start_day () const { return m_start_day; } aipTime end_day () const { return m_end_day; } }; //====================================================================== // rppPosEmp - Position and an Employee that can work it class rppPosEmp : public aipDemon { long m_pos_id; long m_emp_id; public: rppPosEmp () {} rppPosEmp (long a_pos_id, long a_emp_id); virtual ~rppPosEmp (); virtual long num_keys (void) const { return 2; } virtual long key1 (void) const { return m_pos_id; } virtual long key2 (void) const { return m_emp_id; } // returns 0 on failure virtual int read_str (const char *x); virtual int write_str (char *x) const; long pos_id () const { return m_pos_id; } long emp_id () const { return m_emp_id; } }; //====================================================================== // rppAssignment - Assignment of an emp to a pos for some day(s) class rppAssignment : public aipDemon { long m_proj_id; long m_pos_id; long m_emp_id; long m_start_yyyymmdd; long m_end_yyyymmdd; public: rppAssignment () {} rppAssignment (long a_proj_id, long a_pos_id, long a_emp_id, long a_start_yyyymmdd, long a_end_yyyymmdd); virtual ~rppAssignment (); virtual long num_keys (void) const { return 3; } virtual long key1 (void) const { return m_emp_id; } virtual long key2 (void) const { return m_pos_id; } virtual long key3 (void) const { return m_start_yyyymmdd;} // returns 0 on failure virtual int read_str (const char *x); virtual int write_str (char *x) const; long proj_id () const { return m_proj_id; } long pos_id () const { return m_pos_id; } long emp_id () const { return m_emp_id; } long start_yyyymmdd () const { return m_start_yyyymmdd; } long end_yyyymmdd () const { return m_end_yyyymmdd; } }; //====================================================================== // rppEmpDayPos - historic results of solving the problem class rppEmpDayPos : public aipDemon { long m_emp_id; long m_yyyymmdd; long m_proj_id; long m_pos_id; public: rppEmpDayPos () {} rppEmpDayPos (long a_emp_id, long a_yyyymmdd, long a_proj_id, long a_pos_id); virtual ~rppEmpDayPos (); virtual long num_keys (void) const { return 4; } virtual long key1 (void) const { return m_emp_id; } virtual long key2 (void) const { return m_yyyymmdd; } virtual long key3 (void) const { return m_proj_id; } virtual long key4 (void) const { return m_pos_id; } // returns 0 on failure virtual int read_str (const char *x); virtual int write_str (char *x) const; long emp_id () const { return m_emp_id; } long yyyymmdd () const { return m_yyyymmdd; } long proj_id () const { return m_proj_id; } long pos_id () const { return m_pos_id; } }; //====================================================================== // rppPosDayEmp - normalized solution data class rppPosDayEmp : public aipDemon { long m_proj_id; long m_pos_id; long m_yyyymmdd; long m_emp_id; public: rppPosDayEmp (long a_proj_id, long a_pos_id, long a_yyyymmdd, long a_emp_id); virtual ~rppPosDayEmp (); virtual long num_keys (void) const { return 3; } virtual long key1 (void) const { return m_proj_id; } virtual long key2 (void) const { return m_pos_id; } virtual long key3 (void) const { return m_yyyymmdd; } virtual int write_hdr (char *x) const; // returns 0 on failure virtual int write_str (char *x) const; // returns 0 on failure long proj_id () const { return m_proj_id; } long pos_id () const { return m_pos_id; } long yyyymmdd () const { return m_yyyymmdd; } long emp_id () const { return m_emp_id; } }; //====================================================================== // rppProjWeekEmpDay - re-ordered normalized output with week-number class rppProjWeekEmpDay : public aipDemon { long m_proj_id; long m_week_num; long m_emp_id; long m_week_pos; // 1 to 7 long m_yyyymmdd; long m_pos_id; public: rppProjWeekEmpDay (); virtual ~rppProjWeekEmpDay (); virtual long num_keys (void) const { return 4; } virtual long key1 (void) const { return m_proj_id; } virtual long key2 (void) const { return m_week_num; } virtual long key3 (void) const { return m_emp_id; } virtual long key4 (void) const { return m_week_pos; } void set (long a_proj_id, long a_week_num, long a_emp_id, long a_week_pos, long a_yyyymmdd, long a_pos_id); long proj_id () const { return m_proj_id; } long week_num () const { return m_week_num; } long emp_id () const { return m_emp_id; } long week_pos () const { return m_week_pos; } long yyyymmdd () const { return m_yyyymmdd; } long pos_id () const { return m_pos_id; } }; //====================================================================== // rppProjWeek - master for one week of the problem solution class rppProjWeek : public aipDemon { long m_proj_id; long m_week_num; long m_yyyymmdd[7]; // -1 = null = past end of problem public: rppProjWeek () { reset(); } virtual ~rppProjWeek () {} // currently not storing these objects in a pandemonium virtual long num_keys (void) const { return 2; } virtual long key1 (void) const { return m_proj_id; } virtual long key2 (void) const { return m_week_num; } void set_pw (long p, long w) { m_proj_id = p; m_week_num = w; } void set_day (long iday, long a_yyyymmdd) { m_yyyymmdd[iday-1] = a_yyyymmdd; } void reset (); virtual int write_hdr (char *x) const; // returns 0 on failure virtual int write_hdr (FILE *opf) const; // returns 0 on failure virtual int write_str (char *x) const; // returns 0 on failure virtual int write (FILE *ofp) const; // returns 0 on failure long proj_id () const { return m_proj_id; } long week_num () const { return m_week_num; } long yyyymmdd (long iday) const { if (iday < 1 || iday > 7) return -1; return m_yyyymmdd[iday-1]; } }; //====================================================================== // rppProjWeekEmp - details for one week of the problem solution class rppProjWeekEmp : public aipDemon { long m_proj_id; long m_week_num; long m_emp_id; long m_pos_id[7]; // -1 = null = not working that day public: rppProjWeekEmp () { reset(); } virtual ~rppProjWeekEmp () {} // currently not storing these objects in a pandemonium // virtual long num_keys (void) const { return 3; } // virtual long key1 (void) const { return m_proj_id; } // virtual long key2 (void) const { return m_week_num; } // virtual long key3 (void) const { return m_emp_id; } void set_pwe (long p, long w, long e) { m_proj_id = p; m_week_num = w; m_emp_id = e; } void set_day (long iday, long a_pos_id) { m_pos_id[iday-1] = a_pos_id ; } void reset (); virtual int write_hdr (char *x) const; // returns 0 on failure virtual int write_hdr (FILE *ofp) const; // returns 0 on failure virtual int write_str (char *x) const; // returns 0 on failure virtual int write (FILE *ofp) const; // returns 0 on failure long proj_id () const { return m_proj_id; } long week_num () const { return m_week_num; } long emp_id () const { return m_emp_id; } long pos_id (long iday) const { if (iday < 1 || iday > 7) return -1; return m_pos_id[iday-1]; } }; //====================================================================== // rppProbWrite - Solution-Writer for a problem // // Calling write(): // - opens the output file // - calls pre_proc() // - iterates through the problem Pos-Day-Emps, and for each: // - calls handle_pos_day_emp() // - calls post_proc() // - closes the output file // // This class may be used for its default behavior - writing the // solution data in the normalized form - or as a base class. class rppProbWrite : public aipBase { const rppProblem * m_prob; char m_out_file_name[Max_File_Name+1]; int m_any_pos_day_emp_written; protected: const rppProblem * prob () const { return m_prob; } virtual int pre_proc (FILE *ofp); virtual int handle_pos_day_emp (FILE *ofp, const rppPosDayEmp *pde); virtual int post_proc (FILE *ofp); int write_blank_line (FILE *ofp); public: rppProbWrite (const rppProblem *x, const char *out_file_name); virtual ~rppProbWrite (); int write (); // return zero on error }; //====================================================================== // rppProbWriteWeek1 - Week-based Solution-Writer number 1 // // m_proj_week_emp_days is the re-ordered normalized-solution-data // with a week-number. class rppProbWriteWeek1 : public rppProbWrite { aipTime m_week1_start_dt; // 1st, determine this aipPandemonium * m_proj_week_emp_days; // 2nd, reorder the data rppProjWeek * m_proj_week; int m_any_week_data; // 0=no rppProjWeekEmp * m_proj_week_emp; int m_any_emp_data; // 0=no protected: int is_valid () const; void reset (); void set_week1_start_dt (); virtual int week_dt (aipTime x, long *week_num, long *week_pos) const; virtual int handle_pos_day_emp (FILE *ofp, const rppPosDayEmp *pde); virtual int post_proc (FILE *ofp); virtual int set_proj_week (rppProjWeekEmpDay *x); virtual int add_to_proj_week_emp (rppProjWeekEmpDay *x); rppProjWeekEmpDayItr projweekempday_iterator () const; public: rppProbWriteWeek1 (const rppProblem *x, const char *out_file_name); virtual ~rppProbWriteWeek1 (); // int write() from parent }; //====================================================================== // rppPosDayItr - Iterator for Position-Days in a Problem class rppPosDayItr : public aipDemonItr { public: rppPosDayItr (aipPandemonium *p) { set_demon_itr(p,0); } rppPosDayItr (const rppPosDayItr& x) { set_demon_itr (x.pandemonium(), x.current()); } virtual ~rppPosDayItr () {} rppPosDayItr& operator = (const rppPosDayItr& x) { set_demon_itr(x.pandemonium(), x.current()); return *this; } rppPosDay * first () { return (rppPosDay*)aipDemonItr::first(); } rppPosDay * next () { return (rppPosDay*)aipDemonItr::next(); } }; //====================================================================== // rppEmpDowItr - Iterator for Emp-Days-of-week in a Problem class rppEmpDowItr : public aipDemonItr { public: rppEmpDowItr (aipPandemonium *p) { set_demon_itr(p,0); } rppEmpDowItr (const rppEmpDowItr& x) { set_demon_itr (x.pandemonium(), x.current()); } virtual ~rppEmpDowItr () {} rppEmpDowItr& operator = (const rppEmpDowItr& x) { set_demon_itr(x.pandemonium(), x.current()); return *this; } rppEmpDow * first () { return (rppEmpDow*)aipDemonItr::first(); } rppEmpDow * next () { return (rppEmpDow*)aipDemonItr::next(); } }; //====================================================================== // rppEmpOffItr - Iterator for Emp-Offs in a Problem class rppEmpOffItr : public aipDemonItr { public: rppEmpOffItr (aipPandemonium *p) { set_demon_itr(p,0); } rppEmpOffItr (const rppEmpOffItr& x) { set_demon_itr (x.pandemonium(), x.current()); } virtual ~rppEmpOffItr () {} rppEmpOffItr& operator = (const rppEmpOffItr& x) { set_demon_itr(x.pandemonium(), x.current()); return *this; } rppEmpOff * first () { return (rppEmpOff*)aipDemonItr::first(); } rppEmpOff * next () { return (rppEmpOff*)aipDemonItr::next(); } }; //====================================================================== // rppPosEmpItr - Iterator for Position-Employee pairs in a Problem class rppPosEmpItr : public aipDemonItr { public: rppPosEmpItr (aipPandemonium *p) { set_demon_itr(p,0); } rppPosEmpItr (const rppPosEmpItr& x) { set_demon_itr (x.pandemonium(), x.current()); } virtual ~rppPosEmpItr () {} rppPosEmpItr& operator = (const rppPosEmpItr& x) { set_demon_itr(x.pandemonium(), x.current()); return *this; } rppPosEmp * first () { return (rppPosEmp*)aipDemonItr::first(); } rppPosEmp * next () { return (rppPosEmp*)aipDemonItr::next(); } }; //====================================================================== // rppAssgnItr - Iterator for Assignments in a Problem class rppAssgnItr : public aipDemonItr { public: rppAssgnItr (aipPandemonium *p) { set_demon_itr(p,0); } rppAssgnItr (const rppAssgnItr& x) { set_demon_itr (x.pandemonium(), x.current()); } virtual ~rppAssgnItr () {} rppAssgnItr& operator = (const rppAssgnItr& x) { set_demon_itr(x.pandemonium(), x.current()); return *this; } rppAssignment * first () { return (rppAssignment*)aipDemonItr::first(); } rppAssignment * next () { return (rppAssignment*)aipDemonItr::next(); } }; //====================================================================== // rppEmpDayPosItr - Iterator for historic Emp-Day-Pos in a Problem class rppEmpDayPosItr : public aipDemonItr { public: rppEmpDayPosItr (aipPandemonium *p) { set_demon_itr(p,0); } rppEmpDayPosItr (const rppEmpDayPosItr& x) { set_demon_itr (x.pandemonium(), x.current()); } virtual ~rppEmpDayPosItr () {} rppEmpDayPosItr& operator = (const rppEmpDayPosItr& x) { set_demon_itr(x.pandemonium(), x.current()); return *this; } rppEmpDayPos * first () { return (rppEmpDayPos*)aipDemonItr::first(); } rppEmpDayPos * next () { return (rppEmpDayPos*)aipDemonItr::next(); } }; //====================================================================== // rppPosDayEmpItr - Iterator for normalized solution datam class rppPosDayEmpItr : public aipDemonItr { public: rppPosDayEmpItr (aipPandemonium *p) { set_demon_itr(p,0); } rppPosDayEmpItr (const rppPosDayEmpItr& x) { set_demon_itr (x.pandemonium(), x.current()); } virtual ~rppPosDayEmpItr () {} rppPosDayEmpItr& operator = (const rppPosDayEmpItr& x) { set_demon_itr(x.pandemonium(), x.current()); return *this; } rppPosDayEmp * first () { return (rppPosDayEmp*)aipDemonItr::first(); } rppPosDayEmp * next () { return (rppPosDayEmp*)aipDemonItr::next(); } }; //====================================================================== // rppProjWeekEmpDayItr - Iterator for re-ordered normalized solution // data, including a week-number class rppProjWeekEmpDayItr : public aipDemonItr { public: rppProjWeekEmpDayItr (aipPandemonium *p) { set_demon_itr(p,0); } rppProjWeekEmpDayItr (const rppProjWeekEmpDayItr& x) { set_demon_itr (x.pandemonium(), x.current()); } virtual ~rppProjWeekEmpDayItr () {} rppProjWeekEmpDayItr& operator = (const rppProjWeekEmpDayItr& x) { set_demon_itr(x.pandemonium(), x.current()); return *this; } rppProjWeekEmpDay * first () { return (rppProjWeekEmpDay*)aipDemonItr::first(); } rppProjWeekEmpDay * next () { return (rppProjWeekEmpDay*)aipDemonItr::next(); } }; //====================================================================== // rppProjWeekItr - Iterator for output-masters (weeks in a project) class rppProjWeekItr : public aipDemonItr { public: rppProjWeekItr (aipPandemonium *p) { set_demon_itr(p,0); } rppProjWeekItr (const rppProjWeekItr& x) { set_demon_itr (x.pandemonium(), x.current()); } virtual ~rppProjWeekItr () {} rppProjWeekItr& operator = (const rppProjWeekItr& x) { set_demon_itr(x.pandemonium(), x.current()); return *this; } rppProjWeek * first () { return (rppProjWeek*)aipDemonItr::first(); } rppProjWeek * next () { return (rppProjWeek*)aipDemonItr::next(); } }; //====================================================================== // rppProjWeekEmpItr - Iterator for output-details (Proj-Week Emps) class rppProjWeekEmpItr : public aipDemonItr { public: rppProjWeekEmpItr (aipPandemonium *p) { set_demon_itr(p,0); } rppProjWeekEmpItr (const rppProjWeekEmpItr& x) { set_demon_itr (x.pandemonium(), x.current()); } virtual ~rppProjWeekEmpItr () {} rppProjWeekEmpItr& operator = (const rppProjWeekEmpItr& x) { set_demon_itr(x.pandemonium(), x.current()); return *this; } rppProjWeekEmp * first () { return (rppProjWeekEmp*)aipDemonItr::first(); } rppProjWeekEmp * next () { return (rppProjWeekEmp*)aipDemonItr::next(); } }; //====================================================================== // rppFileDate - used to read/write dates to/from a file class rppFileDate { char m_yyyy_mm_dd[File_Date_Len+1]; long m_yyyymmdd; protected: void reset () { m_yyyy_mm_dd[0] = '\0'; m_yyyymmdd = 0; } public: rppFileDate (const char *a_yyyy_mm_dd); rppFileDate (long a_yyyymmdd); ~rppFileDate (); int is_valid (); const char * file_date () const { return m_yyyy_mm_dd; } long long_date () const { return m_yyyymmdd; } }; //====================================================================== // rppFileTime - used to read/write dates to/from a file class rppFileTime { char m_hh_mm[File_Time_Len+1]; long m_hhmm; protected: void reset () { m_hh_mm[0] = '\0'; m_hhmm = 3333; } public: rppFileTime (const char *a_hh_mm); rppFileTime (long a_hhmm); ~rppFileTime (); int is_valid (); const char * file_time () const { return m_hh_mm; } long long_time () const { return m_hhmm; } }; //====================================================================== #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. // //********************************************************************** rrproj_solv.cpp0000664000076400007640000003727011104463242013133 0ustar brianbrian//********************************************************************** // rrproj_solv.cpp - function bodies for rrproj_solv.h // // Copyright (c) 2008 Brian Marshall // // See the license at end of this file. // // Developers/Contributers: // [BRM] Brian Marshall - Calgary - bmarshal@agt.net // // 07/11/21 [BRM] began development // //---------------------------------------------------------------------- #include "rrproj_solv.h" #include #include using namespace std; //====================================================================== // rpsProblem // //---------------------------------------------------------------------- // Constructor rpsProblem::rpsProblem () { aipTime tmp("Jan 1 1900"); m_start_day = m_end_day = tmp; } //---------------------------------------------------------------------- // Destructor rpsProblem::~rpsProblem () {} //---------------------------------------------------------------------- // add_position void rpsProblem::add_position (rpsPosition *x) { add_requirement(x); } //---------------------------------------------------------------------- // add_employee void rpsProblem::add_employee (rpsEmployee *x) { add_resource(x); } //---------------------------------------------------------------------- // set_day_range void rpsProblem::set_day_range (long start_yyyymmdd, long end_yyyymmdd) { m_start_day = aipTime(start_yyyymmdd); m_end_day = aipTime(end_yyyymmdd); } //---------------------------------------------------------------------- // position_iterator - return an iterator: positions in the problem rpsPositionItr rpsProblem::position_iterator() const { rpsPositionItr i(requirement_pandemonium()); return i; } //---------------------------------------------------------------------- // employee_iterator - return an iterator: employees in the problem rpsEmployeeItr rpsProblem::employee_iterator() const { rpsEmployeeItr i(resource_pandemonium()); return i; } //---------------------------------------------------------------------- // decision_iterator - return an iterator: decisions in the problem rpsDecisionItr rpsProblem::decision_iterator() const { rpsDecisionItr i(decision_pandemonium()); return i; } //---------------------------------------------------------------------- // take_msg void rpsProblem::take_msg (aipMsg *mm) { aiprrProblem::take_msg(mm); } //---------------------------------------------------------------------- // Note that a decision has decided or failed to decide void rpsProblem::note_decide (aipHHDecision *dd, aipHHOption *oo) { if (!dd) return; aiprrProblem::note_decide (dd, oo); rpsDecision *d = (rpsDecision*)dd; rpsOption *o = (rpsOption*)oo; if (should_log_options()) log_decide (d, o); if (o) apply_emp_aspects (d, o); } //---------------------------------------------------------------------- // A decision has just chosen an option - apply some aspects to // some other unchosen options (by changing their dynamic goodness). // The aspects, here, are related to the employee of the chosen option. void rpsProblem::apply_emp_aspects (rpsDecision *d1, rpsOption *o1) { // regarding the emp, dcsn and chosen opt rpsPosDay * pd1 = d1->posday(); rpsPosition * p1 = pd1->pos(); rpsEmpDay * ed1 = o1->empday(); rpsEmployee * e1 = ed1->emp(); // regarding other dcsns and opts for this emp rpsDecision * d2; rpsOption * o2; rpsPosDay * pd2; rpsEmpDay * ed2; // good for an emp to work if has not worked for a while // but now emp has worked if ( e1->g_last_work() > aipNeutral) e1->reset_g_last_work(); rpsEmpDayItr editr = e1->empday_iterator(); // emp for ( ed2 = editr.first(); ed2; ed2 = editr.next() ) { // empday if (e1->g_last_work() == aipNeutral) { long day_diff = ed2->day().days_since(pd1->day()); if ( day_diff < -1 || day_diff > 2 ) continue; } rpsOptItr oitr = ed2->opt_iterator(); for ( o2 = oitr.first(); o2; o2 = oitr.next() ) { // opt if ( o2->is_chosen() ) continue; if ( o2->g_opt().is_forbidden() ) continue; d2 = (rpsDecision*)(o2->owner()); // dcsn if ( d2->is_decided() ) continue; pd2 = d2->posday(); // posday // emp cannot work overlapping posdays if ( pd2->end_time() > pd1->start_time() && pd2->start_time() < pd1->end_time() ) { o2->set_g_dynamic(aipForbidden); continue; } // good for an emp to work same pos as previous day if ( pd2->pos()->pos_id() == p1->pos_id() && pd2->proj_id() == pd1->proj_id() && pd2->day().days_since(pd1->day()) == 1 ) { o2->add_g_dynamic(aipGood); } } // end of loop through options in ed2 } // end of loop through empdays ed2 for the emp e1 } //---------------------------------------------------------------------- // Write about a decision being decided if enable_log_options // has been called void rpsProblem::log_decide (rpsDecision *d, rpsOption *o) { cout << "log: Decision for: " << " proj " << d->posday()->proj_id() << " pos " << d->posday()->pos_id() << " on " << d->posday()->yyyymmdd(); if (o) { cout << " emp: " << o->emp()->emp_id() << " g: " << o->g_opt_cmp().numeric_value(); } else { cout << " Failed"; } cout << "\n"; } //---------------------------------------------------------------------- // Write about the try to cout if enable_log_tries() has been called void rpsProblem::log_this_try () { aipHHSolveStat * ss = solve_stat(); long g_val = ss->g_usr_cur().numeric_value(); long g_val_bsf = ss->g_usr_bsf().numeric_value(); cout << "\n +++ A Try has ended - Goodness: " << g_val << " (Best-so-far: " << g_val_bsf << ")\n" << "+++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"; } //====================================================================== // rpsDecision // //---------------------------------------------------------------------- // constructor rpsDecision::rpsDecision (rpsPosDay *a_posday, long a_num_to_decide) : aiprrDecision(a_posday,a_num_to_decide) {} //---------------------------------------------------------------------- // destructor rpsDecision::~rpsDecision() {} //---------------------------------------------------------------------- // add_rps_option void rpsDecision::add_rps_option (rpsOption *x) { add_aiprr_option(x); } //---------------------------------------------------------------------- // take_msg - take and react to a message void rpsDecision::take_msg (aipMsg *mm) { aiprrDecision::take_msg(mm); // rpsMsg *m = (rpsMsg *)mm; // if (m->typ() == HH_Starting_New_Try) { // } } //====================================================================== // rpsOption - an employee working a position on a day // //---------------------------------------------------------------------- // constructor rpsOption::rpsOption(rpsEmpDay *a_empday, aipG g_constant) : aiprrOption(a_empday, g_constant) {} //---------------------------------------------------------------------- // destructor rpsOption::~rpsOption () {} //---------------------------------------------------------------------- // return emp, pos, day rpsEmployee * rpsOption::emp () const { return empday()->emp(); } rpsPosition * rpsOption::pos () const { return posday()->pos(); } aipTime rpsOption::day () const { return empday()->day(); } //---------------------------------------------------------------------- // take_msg - take and react to a message void rpsOption::take_msg (aipMsg *mm) { aiprrOption::take_msg(mm); // rpsMsg *m = (rpsMsg *)mm; // if (m->typ() == HH_Starting_New_Try) { // } } //---------------------------------------------------------------------- // g_opt - for making decisions aipG rpsOption::g_opt() { return ( aiprrOption::g_opt() + emp()->g_last_work() ); } //---------------------------------------------------------------------- // g_opt_cmp - for comparing solutions aipG rpsOption::g_opt_cmp() { return ( aiprrOption::g_opt_cmp() + emp()->g_last_work() ); } //---------------------------------------------------------------------- // g_opt_usr - for reporting to the user aipG rpsOption::g_opt_usr() { // for reporting to the user return ( aiprrOption::g_opt_usr() + emp()->g_last_work() ); } //====================================================================== // rpsPosition // //---------------------------------------------------------------------- // constructor rpsPosition::rpsPosition (long a_pos_id, rpsProblem *a_prob) : aiprrRequirement(a_pos_id, a_prob) { } //---------------------------------------------------------------------- // add_posday void rpsPosition::add_posday (rpsPosDay *x) { add_reqday(x); } //---------------------------------------------------------------------- // take_msg - take and react to a message void rpsPosition::take_msg (aipMsg *mm) { aiprrRequirement::take_msg(mm); // rpsMsg *m = (rpsMsg *)mm; } //---------------------------------------------------------------------- // Note that a decision for this position has been decided // void rpsPosition::note_rps_decide (rpsDecision *d, rpsOption *o) {} //---------------------------------------------------------------------- // return an iterator: position-days for the position rpsPosDayItr rpsPosition::posday_iterator() const { rpsPosDayItr i(reqday_pandemonium()); return i; } //====================================================================== // rpsPosDay // //---------------------------------------------------------------------- // constructor // // set_dcsn() must be called after the decision is created. rpsPosDay::rpsPosDay(rpsPosition *a_pos, long a_yyyymmdd, long a_proj_id, long a_start_hhmm, long a_end_hhmm) : aiprrReqDay(a_pos, a_yyyymmdd) { m_proj_id = a_proj_id; m_start_time = aipTime(a_yyyymmdd, a_start_hhmm); m_end_time = aipTime(a_yyyymmdd, a_end_hhmm); m_yyyymmdd = a_yyyymmdd; // if posday crosses midnight... if (m_end_time < m_start_time) m_end_time.add_days(1); } //---------------------------------------------------------------------- // Destructor rpsPosDay::~rpsPosDay() {} //---------------------------------------------------------------------- // take_msg - take and react to a message void rpsPosDay::take_msg (aipMsg *mm) { aiprrReqDay::take_msg(mm); // rpsMsg *m = (rpsMsg *)mm; } //---------------------------------------------------------------------- // Note that a decision for this position-day has been decided // void rpsPosDay::note_rps_decide (rpsDecision *d, rpsOption *o) { } //====================================================================== // rpsEmployee // //---------------------------------------------------------------------- // constructor rpsEmployee::rpsEmployee(long a_id, rpsProblem *a_prob) : aiprrResource(a_id, a_prob) { m_g_last_work = aipNeutral; m_start_last_work = aipTime(20010203); } //---------------------------------------------------------------------- // destructor rpsEmployee::~rpsEmployee() {} //---------------------------------------------------------------------- // set_start_last_work void rpsEmployee::set_start_last_work (aipTime x) { m_start_last_work = x; } //---------------------------------------------------------------------- // add_empday void rpsEmployee::add_empday (rpsEmpDay *x) { add_resday(x); } //---------------------------------------------------------------------- // take_msg - take and react to a message void rpsEmployee::take_msg (aipMsg *mm) { aiprrResource::take_msg(mm); rpsMsg *m = (rpsMsg *)mm; if (m->typ() == HH_Starting_New_Try) { aipTime limit_day(20020101); if (m_start_last_work < limit_day) { m_start_last_work = prob()->start_day(); } long days_since_work = prob()->start_day().days_since(m_start_last_work); if (days_since_work > 64) { m_g_last_work = aipGood; } else if (days_since_work > 32) { m_g_last_work = aipQuiteGood; } else if (days_since_work > 16) { m_g_last_work = aipPrettyGood; } else if (days_since_work > 8) { m_g_last_work = aipFairlyGood; } else if (days_since_work > 4) { m_g_last_work = aipSomewhatGood; } else if (days_since_work > 2) { m_g_last_work = aipSlightlyGood; } } } //---------------------------------------------------------------------- // Note that a decision involving this employee has been decided // void rpsEmployee::note_rps_decide (rpsDecision *d, rpsOption *o) { } //---------------------------------------------------------------------- // empday_iterator rpsEmpDayItr rpsEmployee::empday_iterator() const { rpsEmpDayItr i(resday_pandemonium()); return i; } //====================================================================== // rpsEmpDay // //---------------------------------------------------------------------- // constructor rpsEmpDay::rpsEmpDay(rpsEmployee *a_emp, long a_yyyymmdd, long a_start_hhmm, long a_end_hhmm) : aiprrResDay(a_emp, a_yyyymmdd) { m_start_time = aipTime(a_yyyymmdd, a_start_hhmm); m_end_time = aipTime(a_yyyymmdd, a_end_hhmm); m_yyyymmdd = a_yyyymmdd; // if empday crosses midnight... if (m_end_time < m_start_time) m_end_time.add_days(1); } //---------------------------------------------------------------------- // take_msg - take and react to a message void rpsEmpDay::take_msg (aipMsg *mm) { aiprrResDay::take_msg(mm); // rpsMsg *m = (rpsMsg *)mm; // if (m->typ() == HH_Starting_New_Try) { // } } //---------------------------------------------------------------------- // Note that a decision involving this empday has been decided // void rpsEmpDay::note_rps_decide (rpsDecision *d, rpsOption *o) { } //---------------------------------------------------------------------- // opt_iterator - return an iterator: options for an EmpDay rpsOptItr rpsEmpDay::opt_iterator() const { rpsOptItr i(opt_pandemonium()); return i; } //====================================================================== // Iterators // // All function bodies in 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. // //====================================================================== rrproj_solv.h0000664000076400007640000004601211104463242012572 0ustar brianbrian//********************************************************************** // rrproj_solv.h - Find a solution to an rrproj problem. // // An application should not directly include this file; // an application should include: rrproj_prob.h // // Copyright (c) 2008 Brian Marshall // // See the license at end of this file. // // An rrproj problem is the problem of assigning staff // and/or equipment to projects. // // Developers/Contributers: // [BRM] Brian Marshall - Calgary - bmarshal@agt.net // // 07/11/21 [BRM] began development // //---------------------------------------------------------------------- // About some of the classes... // // An rpsProblem is a subclass of aiprrProblem - it is a // specialization of assigning resources to requirements: // - a Position is a Requirement; a PosDay is a ReqDay // - an Employee is a Resource; an EmpDay is a ResDay // aiprrProblem is a subclass of aipHHProblem, and as such, // knows how to solve itself using the High-Hope technique. // See aipReqRes.h and aipHighHope.h // // A rpsProblem has a set of decisions, each of which has a set of // options. A Decision is associated with a Position-Day. // An Option refers to an Employee that can work the Position-Day. // //---------------------------------------------------------------------- // About the status... // // This is a very early version. The existing techniques can be // improved - mostly by improving how messages and calls to // problem.note_decide are handled. // // This software is currently very slow. It does loops within // loops where a match-merge techinique would be much faster. // This software will be made faster after enough alpha testing // has been done. // //---------------------------------------------------------------------- // A Note // // The program will frequently find the same solution a number of // times, one after another. This is a normal. Each try affects // object attributes provided by the aiParts artificial intelligence // software. // //---------------------------------------------------------------------- // Future Development // // If required... // repalce rpsEmployee.set_start_last_work () with a class of // stats about the current state of the employee // //---------------------------------------------------------------------- // Data Model // // Note that, except for the Problem, all the objects in the diagram // below are pandemoniums (ie. lists of objects). // // The Problem-owning-Decisions-owning-Options is implemented in // the Open Source aiParts software. See aipHighHope.h/cpp for // the general technique and aipReqRes.h/cpp which implements the // high-hope technique for requirement-resource problems. // The pandemonium of decisions in a problem is in aipProblem.h/cpp. // The pandemonium of options in a decision is in aipDecision.h/cpp. // // The objects in pandemoniums in this file do not have keys, // although they are ordered when created by rrproj_fctry.h/cpp. // The reason for this approach is that the pandemoniums of options // and decisions are defined in great-grandparent classes. // // A PosDay is associated with a Decision. If the PosDay requires // multiple employees, the Decision will be decided multiple times. // // [Employee]<<---[Problem]--->>[Position] // | | | // | v v // | v v // | [Decision]<-----[PosDay]<<---{Project} // | | // v v // v v // [EmpDay]----->>[Option] // // {Project} is only in the logical model - there is no project // object; we use proj_id as an optional attribute of Position. // //---------------------------------------------------------------------- #ifndef rrproj_solv_h_guard #define rrproj_solv_h_guard #include "aipReqRes.h" //---------------------------------------------------------------------- // Classes. Sub-classing is shown by indentation. // class aipG is a typedef for aipGoodness // class aipBase; ( in aipBase.h ) // class aipProblem; ( in aipProblem.h ) // class aipHHProblem; ( in aipHighHope.h ) // class aiprrProblem; ( in aipReqRes.h ) class rpsProblem; // rrproj solver problem // class aipDemon; ( in aipPandemonium.h ) // class aipDecision; ( in aipDecision.h ) // class aipHHDecision; ( in aipHighHope.h ) // class aiprrDecision; ( in aipReqRes.h ) class rpsDecision; // decision: emp to work position-day // class aipOption; ( in aipDecision.h ) // class aipHHOption; ( in aipHighHope.h ) // class aiprrOption; ( in aipReqRes.h ) class rpsOption; // option: an emp on a day // class aiprrRequirement; ( in aipReqRes.h ) class rpsPosition; // position requiring employees // class aiprrReqDay; ( in aipReqRes.h ) class rpsPosDay; // position on a day // class aiprrResource; ( in aipReqRes.h ) class rpsEmployee; // employee that can work positions // class aiprrResDay; ( in aipReqRes.h ) class rpsEmpDay; // employee on a day // class aipDemonItr; ( in aipPandemonium.h ) class rpsPositionItr; // position iterator class rpsPosDayItr; // posday iterator class rpsEmployeeItr; // employee iterator class rpsEmpDayItr; // empday iterator class rpsDecisionItr; // decision iterator class rpsOptItr; // opt iterator for empday // class aipMsg ( in aipBase.h ) // class aiprrMsg; ( in aipReqRes.h ) class rpsMsg; // message for rrproj solving //====================================================================== // rpsProblem - rrproj solver problem that can solve itself // // Parent classes provide: decisions // // solve_rr_problem () returns 1 (true) if a solution is found, // zero otherwise. // // aiprrProblem provides: // void set_num_try (long x); // int should_log_options () const; // void add_decision (aiprrDecision *x); // // m_start_day and m_end_day are set by the factory to // the range of days of the set of positions. class rpsProblem : public aiprrProblem { aipTime m_start_day; aipTime m_end_day; protected: virtual void apply_emp_aspects (rpsDecision *d, rpsOption *o); virtual void log_decide (rpsDecision *d, rpsOption *o); virtual void log_this_try (); public: rpsProblem (); virtual ~rpsProblem (); void add_position (rpsPosition *x); void add_employee (rpsEmployee *x); void set_day_range (long start_yyyymmdd, long end_yyyymmdd); aipTime start_day () const { return m_start_day; } aipTime end_day () const { return m_end_day; } rpsPositionItr position_iterator() const; rpsEmployeeItr employee_iterator() const; rpsDecisionItr decision_iterator() const; virtual void take_msg (aipMsg *m); virtual void note_decide (aipHHDecision *d, aipHHOption *o); virtual int solve_rr_problem () { return aiprrProblem::solve_rr_problem(); // 1 on success } }; //====================================================================== // rpsDecision - choosing an emp for a posday // // Parent classes provide: options, hope, importance class rpsDecision : public aiprrDecision { public: rpsDecision (rpsPosDay *a_posday, long num_to_decide =1); virtual ~rpsDecision(); void add_rps_option (rpsOption *x); virtual void take_msg (aipMsg *m); rpsProblem * prob () const { return (rpsProblem*)owner(); } rpsPosDay * posday () const { return (rpsPosDay*)reqday(); } rpsOption * rps_opt_cur () const { return (rpsOption*)aiprr_opt_cur(); } rpsOption * rps_opt_bsf () const { return (rpsOption*)aiprr_opt_bsf(); } }; //====================================================================== // rpsOption - an employee working a position on a day // // Parent classes provide: hope, importance, goodness variables class rpsOption : public aiprrOption { public: rpsOption(rpsEmpDay *a_empday, aipG g_constant); virtual ~rpsOption (); rpsDecision * rps_chooser () const { return (rpsDecision*)aiprr_chooser(); } virtual void take_msg (aipMsg *m); rpsEmpDay * empday () const { return (rpsEmpDay*)resday(); } rpsPosDay * posday () const { // we use owner instead of chooser, so an opt is always // for a posday. This depends on, in this file, options // are not shared between decisions. rpsDecision *d = (rpsDecision*)(hh_opt_owner()); return d ? d->posday() : 0; } rpsPosDay * bsf_posday () const { rpsDecision *d = (rpsDecision*)(aipHHOption::hh_bsf_chooser()); return d ? d->posday() : 0; } rpsEmployee * emp () const; rpsPosition * pos () const; aipTime day () const; virtual aipG g_opt(); // for making decisions virtual aipG g_opt_cmp(); // for comparing solutions virtual aipG g_opt_usr(); // for reporting to the user }; //====================================================================== // rpsPosition - a position requiring employees // // A position is owned by a problem; it owns position-days. // // Parent classes provide: id() // // A position is identified by: pos_id() class rpsPosition : public aiprrRequirement { public: rpsPosition(long a_pos_id, rpsProblem *a_prob); virtual ~rpsPosition() {} virtual long num_keys (void) const { return 1; } virtual long key1 (void) const { return aiprrRequirement::id(); } void add_posday (rpsPosDay *x); virtual void take_msg (aipMsg *m); // virtual void note_rps_decide (rpsDecision *d, rpsOption *o); rpsProblem * prob () const { return (rpsProblem*)(aiprrRequirement::prob()); } long pos_id () const { return aiprrRequirement::id(); } rpsPosDayItr posday_iterator() const; }; //====================================================================== // rpsPosDay - a position on a day (requiring one or more employees) // // Parent classes provide day() // // The proj_id is optional - if the solver is to pay attention // to it, it should be greater than zero. class rpsPosDay : public aiprrReqDay { long m_proj_id; aipTime m_start_time; aipTime m_end_time; long m_yyyymmdd; public: rpsPosDay(rpsPosition *a_pos, long a_yyyymmdd, long a_proj_id, long a_start_hhmm =800, long a_end_hhmm =1700); virtual ~rpsPosDay(); virtual long num_keys (void) const { return 3; } virtual long key1 (void) const { return aiprrReqDay::req()->id(); } virtual long key2 (void) const { return m_yyyymmdd; } virtual long key3 (void) const { return m_proj_id; } virtual void take_msg (aipMsg *m); // virtual void note_rps_decide (rpsDecision *d, rpsOption *o); rpsPosition * pos () const { return (rpsPosition*)(aiprrReqDay::req()); } long pos_id () const { return ( aiprrReqDay::req()->id() ); } long proj_id () const { return m_proj_id; } rpsDecision * dcsn () const { return (rpsDecision*)(aiprrReqDay::dcsn()); } aipTime start_time () const { return m_start_time; } aipTime end_time () const { return m_end_time; } long yyyymmdd () const { return m_yyyymmdd; } }; //====================================================================== // rpsEmployee - an employee that can work positions // // Parent classes provide id() class rpsEmployee : public aiprrResource { // this last-work logic is adequate for event-scheduling aipG m_g_last_work; aipTime m_start_last_work; // on first day of problem public: rpsEmployee(long a_id, rpsProblem *a_prob); virtual ~rpsEmployee(); virtual long num_keys (void) const { return 1; } virtual long key1 (void) const { return aiprrResource::id(); } void add_empday (rpsEmpDay *x); void set_start_last_work (aipTime x); void reset_g_last_work () { m_g_last_work = aipNeutral; } virtual void take_msg (aipMsg *m); // virtual void note_rps_decide (rpsDecision *d, rpsOption *o); rpsProblem * prob () const { return (rpsProblem*)(aiprrResource::prob()); } long emp_id () const { return aiprrResource::id(); } aipG g_last_work () const { return m_g_last_work; } rpsEmpDayItr empday_iterator() const; }; //====================================================================== // rpsEmpDay - an employee on a day // // Parent classes provide day() class rpsEmpDay : public aiprrResDay { aipTime m_start_time; aipTime m_end_time; long m_yyyymmdd; public: rpsEmpDay(rpsEmployee *a_emp, long a_yyyymmdd, long a_start_hhmm =800, long a_end_hhmm =2200); virtual ~rpsEmpDay() {} virtual long num_keys (void) const { return 2; } virtual long key1 (void) const { return aiprrResDay::res()->id(); } virtual long key2 (void) const { return m_yyyymmdd; } virtual void take_msg (aipMsg *m); // virtual void note_rps_decide (rpsDecision *d, rpsOption *o); rpsEmployee * emp () const { return (rpsEmployee*)(aiprrResDay::res()); } long emp_id () const { return (aiprrResDay::res()->id()); } aipTime start_time () const { return m_start_time; } aipTime end_time () const { return m_end_time; } long yyyymmdd () const { return m_yyyymmdd; } rpsOptItr opt_iterator() const; }; //====================================================================== // rpsPositionItr - Iterator for Positions in a Problem class rpsPositionItr : public aipDemonItr { public: rpsPositionItr (aipPandemonium *p) { set_demon_itr(p,0); } rpsPositionItr (const rpsPositionItr& x) { set_demon_itr (x.pandemonium(), x.current()); } virtual ~rpsPositionItr () {} rpsPositionItr& operator = (const rpsPositionItr& x) { set_demon_itr(x.pandemonium(), x.current()); return *this; } rpsPosition * first () { return (rpsPosition*)aipDemonItr::first(); } rpsPosition * next () { return (rpsPosition*)aipDemonItr::next(); } }; //====================================================================== // rpsPosDayItr - Iterator for PosDays of a Position class rpsPosDayItr : public aipDemonItr { public: rpsPosDayItr (aipPandemonium *p) { set_demon_itr(p,0); } rpsPosDayItr (const rpsPosDayItr& x) { set_demon_itr (x.pandemonium(), x.current()); } virtual ~rpsPosDayItr () {} rpsPosDayItr& operator = (const rpsPosDayItr& x) { set_demon_itr(x.pandemonium(), x.current()); return *this; } rpsPosDay * first () { return (rpsPosDay*)aipDemonItr::first(); } rpsPosDay * next () { return (rpsPosDay*)aipDemonItr::next(); } }; //====================================================================== // rpsEmployeeItr - Iterator for Employees in a Problem class rpsEmployeeItr : public aipDemonItr { public: rpsEmployeeItr (aipPandemonium *p) { set_demon_itr(p,0); } rpsEmployeeItr (const rpsEmployeeItr& x) { set_demon_itr (x.pandemonium(), x.current()); } virtual ~rpsEmployeeItr () {} rpsEmployeeItr& operator = (const rpsEmployeeItr& x) { set_demon_itr(x.pandemonium(), x.current()); return *this; } rpsEmployee * first () { return (rpsEmployee*)aipDemonItr::first(); } rpsEmployee * next () { return (rpsEmployee*)aipDemonItr::next(); } }; //====================================================================== // rpsEmpDayItr - Iterator for EmpDays of an Employee class rpsEmpDayItr : public aipDemonItr { public: rpsEmpDayItr (aipPandemonium *p) { set_demon_itr(p,0); } rpsEmpDayItr (const rpsEmpDayItr& x) { set_demon_itr (x.pandemonium(), x.current()); } virtual ~rpsEmpDayItr () {} rpsEmpDayItr& operator = (const rpsEmpDayItr& x) { set_demon_itr(x.pandemonium(), x.current()); return *this; } rpsEmpDay * first () { return (rpsEmpDay*)aipDemonItr::first(); } rpsEmpDay * next () { return (rpsEmpDay*)aipDemonItr::next(); } }; //====================================================================== // rpsDecisionItr - Iterator for Decisions in a Problem class rpsDecisionItr : public aipDemonItr { public: rpsDecisionItr (aipPandemonium *p) { set_demon_itr(p,0); } rpsDecisionItr (const rpsDecisionItr& x) { set_demon_itr (x.pandemonium(), x.current()); } virtual ~rpsDecisionItr () {} rpsDecisionItr& operator = (const rpsDecisionItr& x) { set_demon_itr(x.pandemonium(), x.current()); return *this; } rpsDecision * first () { return (rpsDecision*)aipDemonItr::first(); } rpsDecision * next () { return (rpsDecision*)aipDemonItr::next(); } }; //====================================================================== // rpsOptItr - Iterator for Opts for an EmpDay class rpsOptItr : public aipDemonItr { public: rpsOptItr (aipPandemonium *p) { set_demon_itr(p,0); } rpsOptItr (const rpsOptItr& x) { set_demon_itr (x.pandemonium(), x.current()); } virtual ~rpsOptItr () {} rpsOptItr& operator = (const rpsOptItr& x) { set_demon_itr(x.pandemonium(), x.current()); return *this; } rpsOption * first () { return (rpsOption*)aipDemonItr::first(); } rpsOption * next () { return (rpsOption*)aipDemonItr::next(); } }; //====================================================================== // rpsMsg - message for rrproj problem-solving class rpsMsg : public aiprrMsg { public: rpsMsg (rpsProblem *p, short typ) : aiprrMsg(p,typ) {} ~rpsMsg() {} }; //====================================================================== #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_a2b.cpp0000644000076400007640000003131011104463242012221 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.h0000755000076400007640000002456511104463242011707 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.cpp0000644000076400007640000004050711104463242013010 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. // // No geometrical or spatial information is used. // // 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) { if (!distance) return; cout << "\n" << problem->num_try() << " tries done.\n"; cout << "\nBest solution found (in try " << problem->solve_stat()->num_tries_to_best() << ") was:\n"; a2bSolutionItr itr = problem->solution_iterator(); for ( a2bNode *n = itr.first(); n; n = itr.next() ) { cout << n->label() << " "; } cout << " (distance = " << distance << ")\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.cpp0000644000076400007640000001465511104463242013367 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.cpp0000644000076400007640000001005011104463242013467 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.h0000644000076400007640000001351711104463242013147 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.cpp0000755000076400007640000000662111104463242012517 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.cpp0000644000076400007640000001476511104463242014110 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.cpp0000755000076400007640000001547411104463242012533 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; } //====================================================================== test_rrproj.bat0000775000076400007640000000052511104463243013110 0ustar brianbrian@echo off echo ... echo Testing the rrproj solver... echo Removing test_rrproj_out.dat if it exists... echo not_empty >test_rrproj_out.dat del test_rrproj_out.dat echo Running rrproj.exe... rrproj.exe test_rrproj_in.dat test_rrproj_out.dat fc test_rrproj_out.dat test_rrproj_out0.dat echo "Done test of the rrproj solver." echo " " test_rrproj_in.dat0000664000076400007640000000267311104463243013603 0ustar brianbrian# test1 - very simple test with multiple projects #rppProblem|start_yyyy_mm_dd|end_yyyy_mm_dd|week_start_day|num_tries rppProblem|2008-07-29|2008-07-30|1|20 #rppPosDay|proj_id|pos_id|yyyy_mm_dd|num_emps_req|start_hh_mm|end_hh_mm rppPosDay|1000|1|2008-07-29|1|18:00|21:00 rppPosDay|1000|1|2008-07-30|1|18:30|21:30 rppPosDay|1000|2|2008-07-29|2|18:00|21:00 rppPosDay|1000|2|2008-07-30|2|18:30|21:30 rppPosDay|2000|2|2008-07-29|1|19:00|20:00 rppPosDay|2000|2|2008-07-30|1|19:00|20:00 #rppPosEmp|pos_id|emp_id rppPosEmp|1|101 rppPosEmp|1|102 rppPosEmp|2|102 rppPosEmp|2|103 rppPosEmp|2|104 rppPosEmp|2|105 rppPosEmp|2|106 rppPosEmp|2|107 rppPosEmp|1|111 rppPosEmp|1|112 rppPosEmp|2|112 rppPosEmp|2|113 rppPosEmp|2|114 rppPosEmp|2|115 rppPosEmp|2|116 # dow (day of week) is days-since-Sunday # an emp with no dow data can work any time of any day #rppEmpDow|emp_id|dow|start_hh_mm|end_hh_mm rppEmpDow|102|0|08:00|23:00 rppEmpDow|102|6|08:00|23:00 rppEmpDow|103|6|08:30|23:30 rppEmpDow|104|0|08:00|00:00 #rppEmpOff|emp_id|start_yyyy_mm_dd|end_yyyy_mm_dd rppEmpOff|105|2008-07-28|2008-08-01 #rppAssignment|proj_id|pos_id|emp_id|start_yyyy_mm_dd|end_yyyy_mm_dd rppAssignment|1000|1|101|2008-07-29|2008-07-29 #rppEmpDayPos|emp_id|yyyy_mm_dd|proj_id|pos_id rppEmpDayPos|111|2008-07-23|3000|5 rppEmpDayPos|112|2008-07-19|3000|5 rppEmpDayPos|113|2008-07-15|3000|5 rppEmpDayPos|114|2008-07-02|3000|5 rppEmpDayPos|115|2008-03-10|3000|5 rppEmpDayPos|116|2008-02-05|3000|5 test_rrproj_out0.dat0000664000076400007640000000071511104463243014057 0ustar brianbrian# rrproj output file rppRsltDscr|Solution found. #rppProjWeek|proj|week|... rppProjWeek|1000|1|Jul|28|Mon| |29|Tue| |30|Wed| |31|Thu|Aug|1|Fri| |2|Sat| |3|Sun #rppProjWeekEmp|proj|week|emp|day1pos_id|day2pos_id|... rppProjWeekEmp|1000|1|101| |1|1| | | | rppProjWeekEmp|1000|1|115| |2|2| | | | rppProjWeekEmp|1000|1|116| |2|2| | | | rppProjWeek|2000|1|Jul|28|Mon| |29|Tue| |30|Wed| |31|Thu|Aug|1|Fri| |2|Sat| |3|Sun rppProjWeekEmp|2000|1|114| |2|2| | | | test_rrproj_out.dat0000664000076400007640000000071511104463243013777 0ustar brianbrian# rrproj output file rppRsltDscr|Solution found. #rppProjWeek|proj|week|... rppProjWeek|1000|1|Jul|28|Mon| |29|Tue| |30|Wed| |31|Thu|Aug|1|Fri| |2|Sat| |3|Sun #rppProjWeekEmp|proj|week|emp|day1pos_id|day2pos_id|... rppProjWeekEmp|1000|1|101| |1|1| | | | rppProjWeekEmp|1000|1|115| |2|2| | | | rppProjWeekEmp|1000|1|116| |2|2| | | | rppProjWeek|2000|1|Jul|28|Mon| |29|Tue| |30|Wed| |31|Thu|Aug|1|Fri| |2|Sat| |3|Sun rppProjWeekEmp|2000|1|114| |2|2| | | | test_rrproj.sh0000775000076400007640000000061311104463243012752 0ustar brianbrianecho " " echo "Testing the rrproj solver..." echo " removing test_rrproj_out.dat if it exists..." echo not_empty >test_rrproj_out.dat rm test_rrproj_out.dat echo " running rrproj..." rrproj test_rrproj_in.dat test_rrproj_out.dat echo " comparing test_rrproj_out.dat and test_rrproj_out0.dat..." cmp test_rrproj_out.dat test_rrproj_out0.dat echo "Done test of the rrproj solver." echo " "