<commit_before>/**
 * Eye sensor has a maximum range and senses walls
 * @class
 * @constructor {object} input
 */
var Eye = function(input) {
  console.log('Creating eye', input);
  this.name             = (input && input.name)?      input.name:'';
  this.angle            = (input && input.angle)?     input.angle:0;
  this.fov              = (input && input.fov)?       input.fov:(15*Math.PI/180); // Default 15deg.
  this.max_range        = (input && input.max_range)? input.max_range:4;
  this.sensed_proximity = this.max_range;
  this.sensed_type      = -1; // what does the eye see?

  // Extra sensor dealing exclusively with goals.
  // TODO: Refactor to binary, "nose". Could be interesting with forward turns.
  this.goal_range       = (input && input.goal_range)?input.goal_range:10;
  this.sensed_goal      = this.goal_range;

  // Watch for updates, syncing framerate to sensors.
  this.updated = false;

  // TODO: Watch for goal updates also?
};

// RatSLAM Goal log for rewarding distance.
var Goal = function(id, dis, rad) {
  //console.log('Creating goal', id, dis, rad);
  this.id  = id;
  this.dis = dis;
  this.rad = rad;
};

/**
 * A single agent
 * @class Agent
 * @param {Ros} ros
 * @param {array} eyes
 * @param {array} actions
 * @param {object} agent_opts
 * @param {object} brain_opts
 */
var Agent = function(ros, eyes, actions, agent_opts, brain_opts) {
  if (!ros) throw new Exception('ROS instance must be passed to RatSLAM.');
  this.ros = ros;

  // Initialise eyes from config passed in.
  this.eyes = [];
  for (var i = 0; i < eyes.length; i++) {
    if (typeof(eyes[i].angle) !== "undefined" && typeof(eyes[i].fov) !== "undefined") {
      this.eyes.push(new Eye(eyes[i]));
    }
  }

  this.actions = (actions)?actions:[
    // Default actions.
    [1.0,0.0],
    [1.0,-3.0],
    [1.0,3.0],
    [0.0,-4.0],
    [0.0,4.0]
  ];

  // Remember RatSLAM goals for rewarding distance.
  this.goals = [];

  var num_inputs      = agent_opts.num_inputs;
  var num_actions     = agent_opts.num_actions;
  var temporal_window = brain_opts.temporal_window; // amount of temporal memory. 0 = agent lives in-the-moment :)
  var network_size = num_inputs*temporal_window + num_actions*temporal_window + num_inputs;

  // the value function network computes a value of taking any of the possible actions
  // given an input state. Here we specify one explicitly the hard way
  // but user could also equivalently instead use opt.hidden_layer_sizes = [20,20]
  // to just insert simple relu hidden layers.
  var layer_defs = [];
  layer_defs.push({type: 'input', out_sx: 1, out_sy: 1, out_depth: network_size});
  layer_defs.push({type: 'fc', num_neurons: 80, activation: 'relu'});
  //layer_defs.push({type:'dropout', drop_prob:0.2}); // Uncertainty approximation.
  layer_defs.push({type: 'fc', num_neurons: 80, activation: 'relu'});
  layer_defs.push({type: 'regression', num_neurons: num_actions});

  brain_opts.layer_defs = layer_defs;

  var brain = new deepqlearn.Brain(num_inputs, num_actions, brain_opts); // woohoo

  this.brain = brain;

  this.reward_bonus = 0.0;
  this.digestion_signal = 0.0;

  // outputs on world
  this.linX = 0.0; // Linear velocity forwards.
  this.angZ = 0.0; // Angular velocity rotation.
};

Agent.prototype = {
  /**
   * Add RatSLAM goal to memory for later reward
   * @method addGoal
   * @param {integer} id
   * @param {float} dis
   * @param {float} rad
   */
  addGoal: function(id, dis, rad) {
    // TODO: If Goal ID has changed, publish an "eat" reward?
    // Does it only change when eaten? Not really, it changes when shortcut too.
    this.goals.push(new Goal(id, dis, rad));
    // Truncate log, only need a few to gauge getting closer.
    this.goals = this.goals.slice(-2);

    return goal;
  },
  forward: function() {
    // in forward pass the agent simply behaves in the environment
    // create input to brain
    // TODO: Signal when loop closure, so net can correlate difference from goal eyes improvement.
    var num_eyes = this.eyes.length;
    var input_array = new Array(num_eyes * 1); // FIXME: ``* 1` Shouldn't it be the size?
    for (var i=0; i<num_eyes; i++) {
      var e = this.eyes[i];
      input_array[i*4] = 1.0; // Wall
      input_array[i*4+1] = 1.0; // Type 1
      input_array[i*4+2] = 1.0; // Type 2
      input_array[i*4+3] = Math.min(e.goal_range, e.sensed_goal)/e.goal_range; // Goal, normalize to [0,1]
      if (e.sensed_type !== -1) {
        // sensed_type is 0 for wall, 1 for food and 2 for poison.
        // lets do a 1-of-k encoding into the input array
        input_array[i*4 + e.sensed_type] = e.sensed_proximity/e.max_range; // normalize to [0,1]
        /*
        console.log('input0: ' + input_array[i*4]);
        console.log('input1: ' + input_array[i*4+1]);<commit_msg>No need to return goal<commit_after>/**
 * Eye sensor has a maximum range and senses walls
 * @class
 * @constructor {object} input
 */
var Eye = function(input) {
  console.log('Creating eye', input);
  this.name             = (input && input.name)?      input.name:'';
  this.angle            = (input && input.angle)?     input.angle:0;
  this.fov              = (input && input.fov)?       input.fov:(15*Math.PI/180); // Default 15deg.
  this.max_range        = (input && input.max_range)? input.max_range:4;
  this.sensed_proximity = this.max_range;
  this.sensed_type      = -1; // what does the eye see?

  // Extra sensor dealing exclusively with goals.
  // TODO: Refactor to binary, "nose". Could be interesting with forward turns.
  this.goal_range       = (input && input.goal_range)?input.goal_range:10;
  this.sensed_goal      = this.goal_range;

  // Watch for updates, syncing framerate to sensors.
  this.updated = false;

  // TODO: Watch for goal updates also?
};

// RatSLAM Goal log for rewarding distance.
var Goal = function(id, dis, rad) {
  //console.log('Creating goal', id, dis, rad);
  this.id  = id;
  this.dis = dis;
  this.rad = rad;
};

/**
 * A single agent
 * @class Agent
 * @param {Ros} ros
 * @param {array} eyes
 * @param {array} actions
 * @param {object} agent_opts
 * @param {object} brain_opts
 */
var Agent = function(ros, eyes, actions, agent_opts, brain_opts) {
  if (!ros) throw new Exception('ROS instance must be passed to RatSLAM.');
  this.ros = ros;

  // Initialise eyes from config passed in.
  this.eyes = [];
  for (var i = 0; i < eyes.length; i++) {
    if (typeof(eyes[i].angle) !== "undefined" && typeof(eyes[i].fov) !== "undefined") {
      this.eyes.push(new Eye(eyes[i]));
    }
  }

  this.actions = (actions)?actions:[
    // Default actions.
    [1.0,0.0],
    [1.0,-3.0],
    [1.0,3.0],
    [0.0,-4.0],
    [0.0,4.0]
  ];

  // Remember RatSLAM goals for rewarding distance.
  this.goals = [];

  var num_inputs      = agent_opts.num_inputs;
  var num_actions     = agent_opts.num_actions;
  var temporal_window = brain_opts.temporal_window; // amount of temporal memory. 0 = agent lives in-the-moment :)
  var network_size = num_inputs*temporal_window + num_actions*temporal_window + num_inputs;

  // the value function network computes a value of taking any of the possible actions
  // given an input state. Here we specify one explicitly the hard way
  // but user could also equivalently instead use opt.hidden_layer_sizes = [20,20]
  // to just insert simple relu hidden layers.
  var layer_defs = [];
  layer_defs.push({type: 'input', out_sx: 1, out_sy: 1, out_depth: network_size});
  layer_defs.push({type: 'fc', num_neurons: 80, activation: 'relu'});
  //layer_defs.push({type:'dropout', drop_prob:0.2}); // Uncertainty approximation.
  layer_defs.push({type: 'fc', num_neurons: 80, activation: 'relu'});
  layer_defs.push({type: 'regression', num_neurons: num_actions});

  brain_opts.layer_defs = layer_defs;

  var brain = new deepqlearn.Brain(num_inputs, num_actions, brain_opts); // woohoo

  this.brain = brain;

  this.reward_bonus = 0.0;
  this.digestion_signal = 0.0;

  // outputs on world
  this.linX = 0.0; // Linear velocity forwards.
  this.angZ = 0.0; // Angular velocity rotation.
};

Agent.prototype = {
  /**
   * Add RatSLAM goal to memory for later reward
   * @method addGoal
   * @param {integer} id
   * @param {float} dis
   * @param {float} rad
   */
  addGoal: function(id, dis, rad) {
    // TODO: If Goal ID has changed, publish an "eat" reward?
    // Does it only change when eaten? Not really, it changes when shortcut too.
    this.goals.push(new Goal(id, dis, rad));
    // Truncate log, only need a few to gauge getting closer.
    this.goals = this.goals.slice(-2);
  },
  forward: function() {
    // in forward pass the agent simply behaves in the environment
    // create input to brain
    // TODO: Signal when loop closure, so net can correlate difference from goal eyes improvement.
    var num_eyes = this.eyes.length;
    var input_array = new Array(num_eyes * 1); // FIXME: ``* 1` Shouldn't it be the size?
    for (var i=0; i<num_eyes; i++) {
      var e = this.eyes[i];
      input_array[i*4] = 1.0; // Wall
      input_array[i*4+1] = 1.0; // Type 1
      input_array[i*4+2] = 1.0; // Type 2
      input_array[i*4+3] = Math.min(e.goal_range, e.sensed_goal)/e.goal_range; // Goal, normalize to [0,1]
      if (e.sensed_type !== -1) {
        // sensed_type is 0 for wall, 1 for food and 2 for poison.
        // lets do a 1-of-k encoding into the input array
        input_array[i*4 + e.sensed_type] = e.sensed_proximity/e.max_range; // normalize to [0,1]
        /*
        console.log('input0: ' + input_array[i*4]);
        console.log('input1: ' + input_array[i*4+1]);
{'fraction_non_alphanumeric': 0.14202354902573722, 'fraction_numerical': 0.01833906429092425, 'mean_word_length': 2.9303849303849305, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3162314', 'n_tokens_mistral': 3445, 'n_tokens_neox': 3275, 'n_words': 951}