Script for Sisyphus-I



void function main ()
{
  check_if_already_placed_employees();
  if (possible_to_place_the_group_head())
  { if (possible_to_place_the_secretaries())
    { if (possible_to_place_the_managers())
      { if (possible_to_place_the_heads_of_large_projects())
        { print; print "----- Placing simple researchers";
          try_to_place_simple_researchers_by_pairs();
          try_to_place_remaining_employees_alone();
        }
      }
    }
  }
}


void function check_if_already_placed_employees()
{ 
  spec [Employee:?]->(In)->[Office] | set Placed_employees;
  if ($Placed_employees)
  { print "Some employees are already placed in the given data set";
    spec [Employee]->(In)->[Office];  //list the relevant CGs
  }
  spec Employee : ?  |  set The_employees;
  subtract $The_employees $Placed_employees | set Employees_to_place;
}




/* ---------------- Functions for placing the group head ------------------ */

boolean function possible_to_place_the_group_head()
{
  print; print "----- Placing the head of group";
  spec Group_head : ?  | set The_group_head;
  spec "[Employee:$The_group_head]->(In)->[Office:?]" | set Office_of_group_head;
  if ($Office_of_group_head == "")
  { if (! possible_to_place($The_group_head,Large_central_office))
    { if (! possible_to_place($The_group_head,Small_central_office))
      { if (! possible_to_place($The_group_head,Large_and_not_central_office))
        { if (! possible_to_place($The_group_head,Small_and_not_central_office))
          { print "$The_group_head cannot be placed: no office left"; 
            return false;
          }
        }
      }
    }
    spec "[Employee:$The_group_head]->(In)->[Office:?]" | set Office_of_group_head;
  }
  return true;
}


boolean function possible_to_place (employee, kind_of_office)
{ 
  spec $kind_of_office : ?  | set offices_of_this_kind;
  for o in $offices_of_this_kind
  { spec "[Employee]->(In)->[Office:$o]" | set occupied_office;
    if ($occupied_office == "") //$o is empty, the employee can be alone
    { place_employee_in($employee,$o); return true; }
  }
  return false;
}


void function place_employee_in (employee, office)
{
  name "#$employee$office" "[Employee: $employee]->(In)->[Office: $office]";
  print "$employee is placed in $office";
  subtractFrom Employees_to_place $employee;
}





/* --------- Functions for placing the secretaries, the managers
                       and the head of large projects             --------- */

boolean function possible_to_place_the_secretaries()
{
  print; print "----- Placing the secretaries";
  spec Secretary : ?  | set The_secretaries;
  for s in $The_secretaries
  { spec "[Employee:$s]->(In)->[Office]" | set office_of_this_employee;
    if ($office_of_this_employee == "")  
    { if (! possible_to_place_with_another_secretary($s))
      { if (! possible_to_place_nearest_group_head($s,Large_office))
        { if (! possible_to_place_nearest_group_head($s,Small_office))
          { print "The secretary $s cannot be placed close to
                $Office_of_group_head, the office of head of group $The_group_head";
            return false;
          }
        }
      }
    }
  }
  return true;
}


boolean function possible_to_place_the_managers()
{
  print; print "----- Placing the managers";
  spec Manager : ?  | set the_managers;
  for m in $the_managers
  { spec "[Employee:$m]->(In)->[Office]" | set office_of_this_employee;
    if ($office_of_this_employee == "")
    { if (! possible_to_place_nearest_group_head($m,Small_central_office))
      { if (! possible_to_place_nearest_group_head($m,Small_and_not_central_office))
        { if (! possible_to_place_nearest_group_head($m,Large_central_office))
          { if (! possible_to_place_nearest_group_head($m,Large_and_not_central_office))
            { print "The manager $m cannot be placed close to
                 $Office_of_group_head, the office of head of group $The_group_head";
              return false;
            }
          }
        }
      }
    }
  }
  return true;
}


boolean function possible_to_place_the_heads_of_large_projects()
{
  print; print "----- Placing the heads of large projects";
  spec Large_project_head : ?  | set the_heads_of_large_projects;
  for h in $the_heads_of_large_projects
  { spec "[Employee:$h]->(In)->[Office]" | set office_of_this_employee;
    if ($office_of_this_employee == "")
    { if (! possible_to_place_nearest_group_head($h,Small_office))
      { if (! possible_to_place_nearest_group_head($h,Large_office))
        { print "The head of large project $h cannot be placed close to
              $Office_of_group_head, the office of head of group $The_group_head";
          return false;
        }
      }
    }
  }
  return true;
}

boolean function possible_to_place_with_another_secretary (employee)
{ 
  for s2 in $The_secretaries
  { spec "[Employee:$s2]->(In)->[Office:?]" | set offices_with_a_secretary;
    for o in $offices_with_a_secretary
    { if (office_not_fully_occupied($o))
      { place_employee_in($employee,$o); return true; }
    }
  }
  return false;
}


boolean function office_not_fully_occupied (office)
{
  spec "[Employee:?]->(In)->[Office:$office]" | count | set number_of_occupants;
  if ($number_of_occupants == 0) { return true; }
  ? Large_office : $office | set office_is_large;
  if ($office_is_large)
  { if ($number_of_occupants < 2) { return true; } }
  return false;
}


boolean function possible_to_place_nearest_group_head (employee,kind_of_office)
{
  set near_chain "->(Near)->";  //first, try very close offices
  while (true)                  //the loop progressively increases $near_chain
  { spec "[Office:?]$near_chain[Office:$Office_of_group_head]"
        | set offices_near_group_head;
    if ($offices_near_group_head == "") { break; } //no more office at such distance
    for o in $offices_near_group_head
    { ? $kind_of_office : $o  | set office_is_suitable;
      if ($office_is_suitable)
      { spec "[Employee]->(In)->[Office:$o]" | set occupied_office;
        if ("$occupied_office" == "") //$o is empty
        { place_employee_in($employee,$o); return true; }
      }
    }
    set near_chain "$near_chain[Office]->(Near)->";
  }
  return false;
}




/* ---------------- Functions for placing simple researchers ------------------ */


void function try_to_place_simple_researchers_by_pairs()
{
  if ($Employees_to_place == "") { return; }

  spec Large_office : ?  | set large_offices;
  spec [Employee]->(In)->[Office:?] | set occupied_offices;
  subtract $large_offices $occupied_offices | set Unoccupied_large_offices;

  spec [Simple_researcher:?]- { (Agent)<-[Smoke]; }  | set smokers;
  spec [Simple_researcher:?]- { (Agent)<-[Hack];  }  | set hackers;
  spec [Simple_researcher:?]- { (Agent)<-[Hack];
                                (Agent)<-[Smoke]; }  | set hackers_smokers;

  //echo "Smokers: $smokers.";  echo "Hackers: $hackers.";
  //echo "Hackers_smokers: $hackers_smokers.";
  subtract "$smokers" "$hackers_smokers" | set smokers_non_hackers;
  subtract "$hackers" "$hackers_smokers" | set hackers_non_smokers;

  spec Simple_researcher : ?  | set the_simple_researchers;
  subtract "$the_simple_researchers" "$smokers" | set non_smokers;
  subtract "$non_smokers" "$hackers_non_smokers" | set non_hackers_non_smokers;

  //print "  1- hackers_smokers ---------------------------------------------";
  if ($hackers_smokers)    { try_to_place_pairs_of(hackers_smokers); }
  //print "  2- smokers_non_hackers -----------------------------------------";
  if ($Employees_to_place) { try_to_place_pairs_of(smokers_non_hackers); }
  //print "  3- hackers_non_smokers -----------------------------------------";
  if ($Employees_to_place) { try_to_place_pairs_of(hackers_non_smokers); }
  //print "  4- non_hackers_non_smokers -------------------------------------";
  if ($Employees_to_place) { try_to_place_pairs_of(non_hackers_non_smokers); }

  //print "  5- smokers -----------------------------------------------------";
  if ($Employees_to_place) { try_to_place_pairs_of(smokers); }
  //print "  6- non_smokers -------------------------------------------------";
  if ($Employees_to_place) { try_to_place_pairs_of(non_smokers); }
}


void function try_to_place_pairs_of (kind_of_employee)
{ 
  set suitable_co-occupants $$kind_of_employee; //placed or not
  countIn $suitable_co-occupants | set number_of_suitable_co-occupants;
  if ($number_of_suitable_co-occupants < 2) { return; }

  for e1 in $suitable_co-occupants
  { spec "[Employee:$e1]->(In)->[Office: ?]"  | set o1;
    if ($o1 == "") //$e1 not placed
    { spec "[Employee:$e1]->(Project)->[Project:?]" | set p1; //$p1: project of $e1
      for e2 in $suitable_co-occupants
      { if ($e1 != $e2)
        { //echo "  $e1 != $e2.";
          spec "[Employee:$e2]->(Project)->[Project:?]"  | set p2;
          if ($p1 != $p2)
          { spec "[Employee:$e2]->(In)->[Office: ?]"  | set o2;
            if ($o2) //e2 is already placed
            { if (office_not_fully_occupied($o2))
              { place_employee_in($e1,$o2); break; }
            }
            else
            { subtractFirstFrom Unoccupied_large_offices | set o2;
              if ($o2)
              { place_employee_in($e1,$o2); place_employee_in($e2,$o2);
                print "  Note: $e1 ($p1) and $e2 ($p2) are $kind_of_employee";
                break;
              }
            }
          }
        }
      }
    }
  }
  if ($Employees_to_place == "") {return;}
  //For non placed employees, idem without the constraint of a different project
  for e1 in $suitable_co-occupants
  { spec "[Employee:$e1]->(In)->[Office: ?]"  | set o1;
    if ($o1 == "") //$e1 not placed
    { for e2 in $suitable_co-occupants
      { if ($e1 != $e2)
        { //echo " $e1 != $e2.";
          spec "[Employee:$e2]->(In)->[Office: ?]"  | set o2;
          if ($o2) //e2 is already placed
          { if (office_not_fully_occupied($o2))
            { place_employee_in($e1,$o2); break; }
          }
          else
          { subtractFirstFrom Unoccupied_large_offices | set o2;
            if ($o2)
            { place_employee_in($e1,$o2); place_employee_in($e2,$o2);
              print "  Note: $e1 ($p1) and $e2 ($p2) are $kind_of_employee";
              break;
            }
          }
        }
      }
    }
  }
}


void function try_to_place_remaining_employees_alone()
{
  spec Office : ?  | set the_offices;

  for e in $Employees_to_place
  { spec [Employee]->(In)->[Office:?] | set occupied_offices;
    subtract $the_offices $occupied_offices | set unoccupied_offices;
    if ($unoccupied_offices)
    { for o in $unoccupied_offices { place_employee_in($e,$o); } }
    else
    { print "$e cannot be placed: no office left"; return; }
  }
}


/* //place_pair_in ($e1,$p1, $e2,$p2, $kind_of_employee, office);
void function place_pair_in (empl1,proj1, empl2,proj2, kind_of_empl, office)
{
  name "#$empl1$office" "[Employee: $empl1]->(In)->[Office: $office]";
  name "#$empl2$office" "[Employee: $empl2]->(In)->[Office: $office]";
  print "$empl1 and $empl2 are placed in $office";
  print -n "They are $kind_of_empl, $empl1 is in the project $proj1";
  print "$empl2 is in the project $proj2";
  subtractFrom Employees_to_place $empl1;
  subtractFrom Employees_to_place $empl2;
}
*/


main();  //run the program