Home Page Downloads Contact Us

Free Open Source Artificial Intelligence C++ Parts

aiParts Open Source Project  

Home Page
Open Source
Contact
Free Downloads
Releases and Credits
Software Status
License
v0.9.3 README File
About rrproj
assigning people and/or equipment to projects
High-Hope Technique
History of the AI
View Source Files
and how they are organized
Making Programs
Development Notes
Using aiParts
Your Application
Support and Services

aiParts Source File

//**********************************************************************
//  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.
//
//**********************************************************************