/* em-prims.cc -- the primitives (senses and actions) you find 
   called in the lap script files.  These mostly just call other libraries
   of functions in other files...

   $Log: em-prims.cc,v $
   Revision 1.5  1997/11/15 15:36:05  jo
   the "final" version for the paper

   Revision 1.1  1997/11/03 23:34:20  jo
   Initial revision


*/

#include <stdio.h>
#include <stream.h>
#include "em.hh"                // c++ stuff, see asm_decs.h below
#include "brain-objs.hh"

#include "asm_mydecs.h"         // tyrrell's code, can't include twice!
#include "animal_defs.h"        // #defs for actions, directions

#include "em-dirs.hh"           // must be after tyrrell's defs

extern int action;

static char rcsid[] = "$Id: em-prims.cc,v 1.5 1997/11/15 15:36:05 jo Exp jo $";

/*

When you add a primitive -- 
   1) add it to this list so it can be identified by people
   2) add the code (don't seem to need declarations, just definitions)
   3) add it to initprims at the bottom, so it gets set up

Remember that if a primitive action does not return SUCCESS, the rest
of a fap will not execute!  FAILURE indicates something _very_ drastic
happened, which should abort the next element.  Actually, it may not
make sense to abort except on sensing!

Notice that things that don't change "action" won't result in a time
step being taken, so we can use perceptual actions as an abstraction
without incurring cost (see also em.cc)
                          
*/

bool res; // commonly used...

// This was just my first test...

/*  ************  Basic Actions  ************ */
// # Since "life" has no goal, need an action that always fails
lapres nil () {
  return (FAILURE);}

lapres sleep () {
  action = SLEEPING;
  return (SUCCESS);
}

int getting_lost () {
  res = (variance > LIKELY)
    ? true : false;
  return((int) res);
}

int lost () {
  res = (variance > VERY_LIKELY)
    ? true : false;
  return((int) res);
}

// slight info on predator
int sniff_predator () {
  res = (max_p1_perc_stimulus > P1SNIFF || max_p2_perc_stimulus > P2SNIFF)
  	// || max_irr_perc_stimulus > TRIVIAL)
    ? true : false;
  return((int) res);
}

// slight info on predator
int predator1 () {
  res = (max_p1_perc_stimulus > max_p2_perc_stimulus)
    ? true : false;
  return((int) res);
}

// more certain of predator
int see_predator() {
  res = (max_p1_perc_stimulus > P1SEE || 
	 max_p2_perc_stimulus > P2SEE || 
	 (max_irr_perc_stimulus > LIKELY))
    ? true : false;
  return((int) res);
}

// action = look_around if biggest trace of predator is in our square
// -- assume that doesn't take careful discrimination to sort out and 
// we're just confused
lapres observe_predator () {
  action = Directions::observe_predator();
  return (SUCCESS);
}


int at_home() {
  res = (den_dir == SAME_SQUARE && max_den_perc_stimulus > CERTAIN)
    ? true : false;
  return((int) res);
}

// you can't get sheltered from cats or hoofs...
int covered() {
  res = (shelter_perc_stim[SAME_SQUARE] > VERY_LIKELY  && 
	 max_p1_perc_stimulus < TRIVIAL && max_irr_perc_stimulus < TRIVIAL)
    ? true : false;
  return((int) res);
}

lapres hold_still () {
  action = FREEZING;
  return (SUCCESS);
}

lapres pick_safe_dir () {
  Directions::pick_safe();
  return(SUCCESS);
}

lapres go_fast () {
  action = Directions::go_fast();
  return(SUCCESS);
}

lapres go () {
  action = Directions::go();
  if (action == RESTING && cleanliness < LIKELY)
    action = CLEANING;
  return(SUCCESS);
}

lapres look_around () {
  action = LOOKING_AROUND;
  return(SUCCESS);
}

lapres groom () {
  action = CLEANING;
  return(SUCCESS);
}

// should prob. not be the same as TRIVIAL, TWEAK! (but value is cubed!)
int late () {
  res = (night_prox > 0.75) ? true : false;
  return((int) res);
}

lapres go_home () {
  action = Directions::go_home();
  return(SUCCESS);
}

int day_time () {
  res = (night_prox < 0.90) ? true : false;
  return((int) res);
}

// mating will fail if health is < .1, am I being conservative?  TWEAK
// NB this was never really calibrated, it just got ignored
int fit_to_mate () {
  res = (animal_health > 0.2 && carbo_minus < 0.8 && water_minus < 0.8 &&
	 fat_minus < 0.8 && protein_minus < 0.8  && an_temp_plus < 0.8 && 
	 cleanliness > 0.3) ? true : false;
  return((int) res);
}
  
// TWEAK -- I don't understand the code for this -- don't think need to 
// check if mate's in square, but not sure about courtedness value
int courted_mate_here() {
  res = (mate_courtedness > TRIVIAL) ? true : false;
  return((int) res);
}

lapres copulate () {
  action = MATING;
  return(SUCCESS);
}

int mate_here() {
  res = (mate_dir == SAME_SQUARE && max_mate_perc_stimulus > TRIVIAL2)
    ? true : false;
  return((int) res);
}

lapres strut () {
  action = COURT_DISPLAYING;
  return(SUCCESS);
}

// should probably play with this parameter TWEAK!
int sniff_mate() {
  res = (max_mate_perc_stimulus > TRIVIAL)
    ? true : false;
  return((int) res);
}

int see_mate() {
  res = (max_mate_perc_stimulus > LIKELY)
    ? true : false;
  return((int) res);
}

lapres pick_mate_dir () {
  Directions::pick_mate();
  return(SUCCESS);
}

lapres sit_or_clean () {
  if (cleanliness < VERY_LIKELY)
    action = CLEANING;
  else
    action = RESTING;
  return(SUCCESS);
}

int safish() {
  res = (shelter_perc_stim[SAME_SQUARE] > LIKELY)     ? true : false;
  return((int) res);
}

// A bunch of cases...  more specific stuff below!
int needed_resource_available() {
  static double food_need;
  // don't hang out if going to get trodden
  if (max_irr_perc_stimulus > TRIVIAL)
    return ((int) false);
  food_need = carbo_minus + protein_minus + fat_minus / 3;
  res = (( food_perc_stim[SAME_SQUARE] > TRIVIAL && food_need > LIKELY) ||
	 (water_perc_stim[SAME_SQUARE] > TRIVIAL && water_minus > LIKELY) ||
	 (an_temp_minus > TRIVIAL && sqr_temp_plus > TRIVIAL) ||
	 (an_temp_plus > TRIVIAL && sqr_temp_minus > TRIVIAL) ||
	 (((cleanliness < VERY_LIKELY || animal_health < LIKELY) &&
	   shelter_perc_stim[SAME_SQUARE] > LIKELY)))     ? true : false;
  return((int) res);
}

// Similar needed_resource_available! assumes they've been found true
// (pick_dir also is a bit like this)
lapres exploit_resource() {
  static double food_need;
  food_need = carbo_minus + protein_minus + fat_minus / 3;

  //most transient && biggest reward...
  if (prey_in_animal_square && animal_health > 0.1)
    action = POUNCING;
  else {
    if (food_perc_stim[SAME_SQUARE] > TRIVIAL  && food_need > LIKELY) {
      if (cf_in_animal_square)
	action = EATING_CF;
      else
	action = EATING_FF;
    }
    else if (water_minus > LIKELY && water_perc_stim[SAME_SQUARE] > TRIVIAL)
      action = DRINKING;
    else if ((an_temp_minus && sqr_temp_minus) || 
	     shelter_perc_stim[SAME_SQUARE] < TRIVIAL) {
      // move to get warm! (or if exposed)
      Directions::pick();  action = Directions::go_fast();
    }   
    else if (cleanliness < CERTAIN)
      action = CLEANING;
    else
      action = RESTING;
  } // else not pouncing
  
  return(SUCCESS);
} // exploit_resource


// hungry or thirsty, actually.  May want to latch this like in 
// real animals -- variable that sets higher than it releases.
int hungry() {
  static double food_need;
  food_need = carbo_minus + protein_minus + fat_minus / 3;
  res = ((food_need > LIKELY) || (water_minus > LIKELY));
  return((int) res);
}

int cold() {
  res = (an_temp_minus > LIKELY);
  return((int) res);
}

int hot() {
  res = ((an_temp_plus > LIKELY));
  return((int) res);
}

int cool_spot() {
  res = ((sqr_temp_minus > TRIVIAL));
  return((int) res);
}

int dirty() {
  res = (cleanliness < VERY_LIKELY);
  return((int) res);
}

// there isn't really an independent tired factor...
int tired() {
  res = (animal_health < TRIVIAL);
  return((int) res);
}

lapres pick_sheltered_dir () {
  Directions::pick_sheltered();
  return(SUCCESS);
}

lapres pick_new_sheltered_dir () {
  Directions::pick_new_sheltered();
  return(SUCCESS);
}

lapres pick_forage_dir () {
  Directions::pick_forage();
  return(SUCCESS);
}

lapres pick_dir () {
  Directions::pick();
  return(SUCCESS);
}

lapres rest () {
  action = RESTING;
  return(SUCCESS);
}

/*---------*/


void
initprims() {
  // basic life
  Act::add_act("nil", nil);
  Act::add_act("sleep",sleep);
  Act::add_act("observe_predator",observe_predator);
  Act::add_act("hold_still",hold_still);
  Act::add_act("pick_safe_dir", pick_safe_dir);
  Act::add_act("go_fast",go_fast);
  Act::add_act("go",go);
  Act::add_act("groom",groom);
  Act::add_act("look_around",look_around);
  Act::add_act("go_home",go_home);
  Act::add_act("copulate",copulate);
  Act::add_act("strut",strut);
  Act::add_act("pick_mate_dir", pick_mate_dir);
  Act::add_act("sit_or_clean", sit_or_clean);
  Act::add_act("exploit_resource", exploit_resource);
  Act::add_act("pick_sheltered_dir",pick_sheltered_dir);
  Act::add_act("pick_new_sheltered_dir",pick_new_sheltered_dir);
  Act::add_act("pick_forage_dir",pick_forage_dir);
  Act::add_act("pick_dir",pick_dir);
  Act::add_act("rest", rest);
  Sense::add_sense("getting_lost",getting_lost);
  Sense::add_sense("lost",lost);
  Sense::add_sense("predator1",predator1);
  Sense::add_sense("see_predator",see_predator);
  Sense::add_sense("sniff_predator",sniff_predator);
  Sense::add_sense("at_home",at_home);
  Sense::add_sense("covered",covered);
  Sense::add_sense("late",late);
  Sense::add_sense("day_time", day_time);
  Sense::add_sense("fit_to_mate", fit_to_mate);
  Sense::add_sense("courted_mate_here", courted_mate_here);
  Sense::add_sense("mate_here", mate_here);
  Sense::add_sense("sniff_mate", sniff_mate);
  Sense::add_sense("see_mate", see_mate);
  Sense::add_sense("needed_resource_available", needed_resource_available);
  Sense::add_sense("hungry", hungry);
  Sense::add_sense("cold", cold);
  Sense::add_sense("hot", hot);
  Sense::add_sense("cool_spot", cool_spot);
  Sense::add_sense("dirty", dirty);
  Sense::add_sense("tired", tired);
  Sense::add_sense("safish", safish);
} // initprims

