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