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

//**********************************************************************
//  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 <string.h>
#include <iostream>
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.
//
//======================================================================