MFMv2.0.10
Movable Feast Machine Simulator 2.0.10
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
Element_Boids.h
1 #ifndef ELEMENT_BOIDS_H /* -*- C++ -*- */
2 #define ELEMENT_BOIDS_H
3 
4 #include "Element.h"
5 #include "EventWindow.h"
6 #include "ElementTable.h"
7 #include "Element_Empty.h"
8 #include "Element_Res.h"
9 #include "itype.h"
10 #include "FXP.h"
11 #include "P1Atom.h"
12 
13 namespace MFM
14 {
15 
110  template <class CC>
111  class Element_Boids : public Element<CC>
112  {
113  // Extract short names for parameter types
114  typedef typename CC::ATOM_TYPE T;
115  typedef typename CC::PARAM_CONFIG P;
116  enum { R = P::EVENT_WINDOW_RADIUS };
117 
118  class Vector {
119  FXP16 x;
120  FXP16 y;
121  public:
122 
123  Vector() : x(0), y(0) { }
124  Vector(FXP16 x, FXP16 y) : x(x), y(y) { }
125  Vector(const SPoint p) : x(p.GetX()), y(p.GetY()) { }
126  Vector(const UPoint p) : x(p.GetX()), y(p.GetY()) { }
127 
128  FXP16 GetX() const { return x; }
129 
130  FXP16 GetY() const { return y; }
131 
132  void Print(ByteSink & f) {
133  f.Printf("(%06x,%06x)",x.asInt(),y.asInt());
134  }
135 
136  friend FXP16 GetDistSq(const Vector & v1, const Vector & v2) {
137  return (v1.x-v2.x)*(v1.x-v2.x)+(v1.y-v2.y)*(v1.y-v2.y);
138  }
139 
140  friend Vector operator+(const Vector & v1, const Vector & v2) {
141  return Vector(v1.x+v2.x,v1.y+v2.y);
142  }
143  friend Vector operator-(const Vector & v1) {
144  return Vector(-v1.x,-v1.y);
145  }
146  friend Vector operator-(const Vector & v1, const Vector & v2) {
147  return v1+-v2;
148  }
149  friend FXP16 operator*(const Vector & v1,const Vector v2) {
150  return v1.x*v2.x+v1.y*v2.y;
151  }
152  friend Vector operator*(const FXP16 num, const Vector & v1) {
153  return Vector(v1.x*num,v1.y*num);
154  }
155  friend Vector operator*(const Vector & v1,const FXP16 num) {
156  return num*v1;
157  }
158  friend Vector operator/(const Vector & v1,const FXP16 num) {
159  return 1.0/num*v1;
160  }
161 
162  Vector & operator=(const Vector & v1) {
163  this->x = v1.x;
164  this->y = v1.y;
165  return *this;
166  }
167 
168  Vector & operator+=(const Vector & v1) {
169  *this = *this + v1;
170  return *this;
171  }
172 
173  Vector & operator-=(const Vector & v1) {
174  *this = *this - v1;
175  return *this;
176  }
177 
178  Vector & operator*=(const FXP16 num) {
179  *this = *this * num;
180  return *this;
181  }
182 
183  Vector & operator/=(const FXP16 num) {
184  *this = *this / num;
185  return *this;
186  }
187 
188  };
189 
190  template <u32 TVBITS>
191  static u32 toSignMag(FXP16 value) {
192  const u32 SIGN_BIT = 1<<(TVBITS-1);
193  const u32 MAX = SIGN_BIT-1;
194  u32 sign = 0;
195  if (value < 0) {
196  sign = SIGN_BIT;
197  value = -value;
198  }
199  if (value > MAX)
200  value = MAX;
201  u32 val = (u32) value;
202  return sign|val;
203  }
204 
205  template <u32 TVBITS>
206  static FXP16 fromSignMag(const u32 value) {
207  const u32 SIGN_BIT = 1<<(TVBITS-1);
208  const u32 MASK = SIGN_BIT-1;
209  FXP16 val = value&MASK;
210  if (value & SIGN_BIT)
211  val = -val;
212  return val;
213  }
214 
215  template <u32 TVBITS>
216  static u32 toTinyVector(const Vector v) {
217  u32 x = toSignMag<TVBITS>(v.x);
218  u32 y = toSignMag<TVBITS>(v.y);
219  return (x<<TVBITS)|y;
220  }
221 
222  template <u32 TVBITS>
223  static Vector toVector(const u32 bits) {
224  const u32 MASK = (1<<TVBITS)-1;
225 
226  FXP16 x = fromSignMag<TVBITS>((bits>>TVBITS)&MASK);
227  FXP16 y = fromSignMag<TVBITS>(bits&MASK);
228  return Vector(x,y);
229  }
230 
231  public:
232 
233  static const u32 TYPE = 0xbd;
234  static const u32 TYPE_BITS = 8;
235  static const u32 TYPE_MASK = (1<<TYPE_BITS)-1;
236  static bool IsBoidType(u32 type) {
237  return (type&TYPE_MASK)==TYPE;
238  }
239 
240  static const u32 BITS_PER_DIM = 4; // including 1 sign bit
241 
242  static const u32 STATE_HEADING_IDX = 0;
243  static const u32 STATE_HEADING_LEN = 2 * BITS_PER_DIM;
244  static const u32 STATE_BITS = STATE_HEADING_IDX + STATE_HEADING_LEN;
245 
246  Element_Boids() { }
247 
248  Vector GetHeading(const T &atom) const {
249  if (!IsBoidType(atom.GetType()))
250  FAIL(ILLEGAL_STATE);
251  return toVector<BITS_PER_DIM>(atom.GetStateField(STATE_HEADING_IDX,STATE_HEADING_LEN));
252  }
253 
254  void SetHeading(const T &atom, const Vector v) const {
255  if (!IsBoidType(atom.GetType()))
256  FAIL(ILLEGAL_STATE);
257  atom.SetStateField(STATE_HEADING_IDX,STATE_HEADING_LEN,toTinyVector<BITS_PER_DIM>(v));
258  }
259 
260  SPoint PickPossibleDestination(EventWindow<CC>& window, const Vector & target, Random & random) const
261  {
262  SPoint dest;
263  FXP16 totWgt = 0;
264 
265  const FXP16 MAX_DIST = R;
266  const FXP16 MAX_DIST_WGT = R;
267  const FXP16 WGT_PER_DIST = MAX_DIST_WGT / MAX_DIST;
268 
269  const MDist<R> md = MDist<R>::get();
270  for (u32 idx = md.GetFirstIndex(0); idx <= md.GetLastIndex(2); ++idx) {
271  const SPoint sp = md.GetPoint(idx);
272 
273  // Allow corners but not two steps away
274  if (sp.GetMaximumLength() > 1) continue;
275 
276  Vector spVec(sp);
277  const T other = window.GetRelativeAtom(sp);
278 
279  // Compute weight of this choice:
280  //
281  // (1) Begin with dist from spVec to target: Closer is bigger,
282  // from MAX_DIST_WGT at dist 0 falling linearly to 0 at
283  // MAX_DIST
284  //
285  // (2) But check if the location is empty. If not, cut that
286  // weight in half
287  //
288  // (3) Then add that weight to the total.
289  //
290  // (4) Then the dest is empty or the center, throw the
291  // weighted die and select if win
292 
293  FXP16 dist = GetDistSq(spVec,target);
294  FXP16 weight = 0;
295  if (dist < MAX_DIST)
296  weight = MAX_DIST_WGT-dist*WGT_PER_DIST;
297  if (other.GetType() != Element_Empty<CC>::TYPE)
298  weight /= 2;
299 
300  if (weight > 0) {
301  totWgt += weight;
302  if (random.OddsOf(weight,totWgt)) {
303  dest = sp;
304  }
305  }
306  }
307  return dest;
308  }
309 
310  virtual void Behavior(EventWindow<CC>& window) const
311  {
312  const double TARGET_SPACING = 1.5;
313  Random & random = window.GetRandom();
314 
315  T self = window.GetCenterAtom();
316  const u32 selfType = self.GetType();
317 
318  if (!IsBoidType(selfType))
319  FAIL(ILLEGAL_STATE);
320 
321  //Vector myHead = GetHeading(self);
322  // myHead.Print(stderr);
323 
324  Vector avgHeading;
325  Vector avgPosition;
326  u32 countNgbr = 0;
327 
328  u32 minDist = R+1;
329 
330  u32 resCount = 0;
331  SPoint resAt;
332 
333  const MDist<R> md = MDist<R>::get();
334 
335  // Scan event window outside self
336  for (u32 idx = md.GetFirstIndex(1); idx <= md.GetLastIndex(R); ++idx) {
337  const SPoint sp = md.GetPoint(idx);
338  const T other = window.GetRelativeAtom(sp);
339 
340  const u32 otherType = other.GetType();
341 
342  if (otherType == Element_Res<CC>::TYPE) {
343  if (random.OneIn(++resCount)) resAt = sp;
344  continue;
345  }
346 
347  if (otherType != selfType) continue;
348 
349  u32 dist = sp.GetManhattanLength();
350  if (dist < minDist)
351  minDist = dist;
352 
353  Vector theirHead = GetHeading(other);
354 
355  avgHeading += theirHead;
356  avgPosition += Vector(sp);
357 
358  ++countNgbr;
359  }
360 
361  if (countNgbr > 0 && countNgbr < 3 && resCount > 0) {
362  // Whatever else we do, if we're not alone and not too crowded
363  // and there's res, recruit it. The scan is done though, so
364  // we don't consider this guy in our heading calculations
365  window.SetRelativeAtom(resAt, self); // Clone bombs away
366  }
367 
368  if (countNgbr == 0) {
369  // We're alone, ono! avgHeading and avgPosition are worthless!
370  // With medium prob: Do random increment to heading
371  // With lower prob: Suicide
372  if (random.OneIn(3)) {
373  window.SetCenterAtom(Element_Empty<CC>::THE_INSTANCE.GetDefaultAtom()); // I can't stand it
374  }
375 
376  } else if (countNgbr == 1 && minDist > TARGET_SPACING) {
377  // Try to step closer to avgPosition (regardless of heading?)
378  SPoint dest = PickPossibleDestination(window,avgPosition,random);
379 
380  window.SwapAtoms(SPoint(0,0),dest);
381  return; // No diffusion if swap
382 
383  } else {
384  avgHeading /= countNgbr;
385  avgPosition /= countNgbr;
386 
387  // avgPosition.Print(stderr);
388  // fprintf(stderr," %d\n",countNgbr);
389  }
390 
391  this->Diffuse(window);
392 
393  }
394 
395  };
396 
397 }
398 
399 #endif /* ELEMENT_BOIDS_H */
u32 GetFirstIndex(const u32 radius) const
Definition: MDist.h:112
Definition: Element_Boids.h:111
void Diffuse(EventWindow< CC > &window) const
Definition: Element.tcc:73
Definition: Element_Empty.h:41
Definition: Random.h:45
u32 GetLastIndex(const u32 radius) const
Definition: MDist.h:129
void SetCenterAtom(const T &atom)
Definition: EventWindow.h:220
virtual void Behavior(EventWindow< CC > &window) const
Definition: Element_Boids.h:310
bool OneIn(u32 odds)
Definition: Random.h:96
T GetY() const
Definition: Point.tcc:40
Random & GetRandom()
Definition: EventWindow.h:122
const T & GetRelativeAtom(const SPoint &offset) const
Definition: EventWindow.tcc:26
u32 GetManhattanLength() const
Definition: Point.tcc:46
bool OddsOf(u32 thisMany, u32 outOfThisMany)
Definition: Random.h:183
const T & GetCenterAtom() const
Definition: EventWindow.h:209
virtual const T & GetDefaultAtom() const
Definition: Element.h:382
Definition: ByteSink.h:47
u32 GetMaximumLength() const
Definition: Point.tcc:52
Definition: MDist.h:69
static MDist< R > & get()
Definition: MDist.tcc:193
Definition: Element_Res.h:48
Definition: ElementTable.h:43
bool SetRelativeAtom(const SPoint &offset, const T &atom)
Definition: EventWindow.tcc:15
Definition: Atom.h:43
void SwapAtoms(const SPoint &locA, const SPoint &locB)
Definition: EventWindow.tcc:40
T GetX() const
Definition: Point.tcc:34