260619:

mass had to grow with age, but then slow down. tried logarithmic growth, but that wasn’t proper (as growth needs to have a ceiling). could have used constrain, but that’s unnecessary computation.

found this: https://math.stackexchange.com/questions/2821035/exponential-something-what-is-the-name-of-that-asymptotic-exponential-functi (thanks to jagi).

equation:

which can be implemented like so:

    const a = 100; //max. 
    const b = 50; //how quickly max is achieved.
    const base = 50; //to ensure base of graph doesn't shift.
 
    y = base + a * (1 - Math.exp(-(x - base) / b));

plotted like this:


260625:

got these getters right:

get_mass() {
    //for a given current age, calculate mass.
 
    //mass is an asymptotic-exponential-growth graph.
 
    const a = 10; //max.
    const b = 18; //in how many steps is max achieved.
 
    return (a * (1 - Math.exp(-5 * (this.curr_age / b)))) / (1 - Math.exp(-5));
  }
  get_energy() {
    //energy is like a bell curve.
 
    /*
    https://www.desmos.com/calculator/3iioyvma2l
 
    f(x) = y = e^(-((x-a)^2) / b).
    */
 
    const m = 10;
 
    return (
      (1 / (1 + Math.exp(-(this.curr_age - 18) / 2))) *
      (1 / (1 + Math.exp((this.curr_age - 35) / 5))) *
      m
    );
  }

the simulation works well. need to refactor a little bit to clean it up, and make things more variable to have a basic simulation going.


fixed scheduling:

  /*
  for given age, get a schedule based on the busyness (the older you are, the more busy you get).
  */
  get_schedule(age) {
    let avl_time = Array.from({ length: day_length }, (_, i) => i);
    let schedule = [];
 
    //calculate available time slots based on age.
    const min = 2;
    const max = 12;
    const peak = 25;
    const sigma = 10; //spread-width.
 
    const g = Math.exp(-Math.pow(age - peak, 2) / (2 * sigma * sigma));
    const mean_busyness = min + (max - min) * g;
 
    const spread = 1.25 + (1 - g) * 3.5;
 
    let busyness = Math.round(randomGaussian(mean_busyness, spread));
    busyness = constrain(busyness, min, max);
 
    //^ this is the length of our to-be-created schedule array.
 
    let i = 0;
 
    while (i < busyness) {
      i = (i + Math.floor(Math.random() * 6)) % avl_time.length;
      let a = avl_time.splice(i, 1)[0],
        b = avl_time.splice(i, 1)[0];
      schedule.push(
        schedule.length ? [schedule[schedule.length - 1][1], a] : [a, b],
      );
    }
 
    schedule.push([schedule[schedule.length - 1][1], schedule[0][0]]);
 
    this.get_new_destinations(schedule);
 
    return schedule;
  }

wrote a small algorithm distribute points in space — somewhat randomly — but with a minimum distance between them.

was inspired by the poisson-disk-sampling algorithm.

but mine is simpler.

/*
select hotspots away from each other, recursively, by drawing circles. 
*/
 
function setup() {
  createCanvas(800, 800);
  background(255);
 
  // let spots = find_spots(width, height, 5, 20);
  let spots = find_spots(width, height, 5, 100);
 
  // console.log(spots);
}
 
/*
for a canvas of width w & height h, and a min-radius specified between points, 
return tuples of x, y coordinates such that; 
coordinates are away by min-radius, 
and are somewhat uniformally distributed across space. 
*/
function find_spots(w, h, n, min_spacing) {
  let posis = Array.from({ length: n }, () => []);
  let size = min_spacing;
  //check if co-centric circles min_spacing apart can fit onto the space:
  const can_fit = Math.floor(Math.min(w, h) / min_spacing) >= n;
 
  //we now do two branches:
  const origin = createVector(width / 2, height / 2);
  if (can_fit) {
    //randomly plot them on the circles (they will always be at-least min-spacing apart).
    for (let i = 0; i < n; i++) {
      let theta = random(TWO_PI);
      let x = origin.x + (size / 2) * cos(theta);
      let y = origin.y + (size / 2) * sin(theta);
      noFill();
      stroke(0);
      strokeWeight(0.5);
      // circle(origin.x, origin.y, size);
      strokeWeight(10);
      stroke(255, 0, 0);
      point(x, y);
      size += min_spacing;
    }
  } else {
    let circle_count = 0;
    while (origin.x + size / 2 < w && origin.y + size / 2 < h) {
      noFill();
      stroke(0);
      strokeWeight(0.5);
      circle(origin.x, origin.y, size);
      size += min_spacing;
      circle_count++;
    }
 
    let spots_on_each = Math.ceil(n / circle_count);
    //reset size:
    size = min_spacing;
 
    let drawn = 0;
 
    for (let i = 0; i < circle_count; i++) {
      //draw the original circle:
      noFill();
      stroke(0);
      strokeWeight(0.5);
      // circle(origin.x, origin.y, size);
 
      let start_theta = random(TWO_PI);
      let inc = TWO_PI / circle_count;
      let theta = start_theta;
 
      for (let j = 0; j < spots_on_each && drawn < n; j++) {
        let x = origin.x + (size / 2) * cos(theta);
        let y = origin.y + (size / 2) * sin(theta);
        stroke(255, 0, 0);
        strokeWeight(10);
        point(x, y);
        theta += inc;
 
        drawn++;
      }
 
      size += min_spacing;
    }
  }
 
  return posis;
}
 
 

explanation:

 
given a 2-d surface of dimensions {w,h}, 
 
we draw cocentric circles from an origin, w units apart (w == min spacing required between points) until: 
 
a) # of concentric circles == n (number of points we want)
b) cocentric circle x + r >= width or height of surface. 
 
in case (a): 
 
	we select a random theta, and draw a point for each of the circles so that: 
		x = origin + size/2 * cos(theta)
		y = origin + size/2 * sin(theta)
 
in case (b)*
	for each co-centric circle, we draw multiple points d degrees apart such that: 
		d == n (number of points we wanted) / # of cocentric circles. 
		
 
* in case b, it is not possible (with this approach) to have n points w units apart. so, we do the best we can.

i considered returning vectors and not tuples, so that you could access, say, spots[n].x && spots[n].y, but decided against it (more operation required). in this case, an array works better.