optionally use guarded alloc for tiles compositor, also replace allocation functions...
[blender.git] / source / gameengine / GameLogic / SCA_RandomActuator.cpp
1 /*
2  * Set random/camera stuff
3  *
4  *
5  * ***** BEGIN GPL LICENSE BLOCK *****
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
22  * All rights reserved.
23  *
24  * The Original Code is: all of this file.
25  *
26  * Contributor(s): none yet.
27  *
28  * ***** END GPL LICENSE BLOCK *****
29  */
30
31 /** \file gameengine/GameLogic/SCA_RandomActuator.cpp
32  *  \ingroup gamelogic
33  */
34
35
36 #include <stddef.h>
37 #include <math.h>
38
39 #include "BoolValue.h"
40 #include "IntValue.h"
41 #include "FloatValue.h"
42 #include "SCA_IActuator.h"
43 #include "SCA_RandomActuator.h"
44 #include "MT_Transform.h"
45
46 /* ------------------------------------------------------------------------- */
47 /* Native functions                                                          */
48 /* ------------------------------------------------------------------------- */
49
50 SCA_RandomActuator::SCA_RandomActuator(SCA_IObject *gameobj, 
51                                                                          long seed,
52                                                                          SCA_RandomActuator::KX_RANDOMACT_MODE mode,
53                                                                          float para1,
54                                                                          float para2,
55                                                                          const STR_String &propName)
56         : SCA_IActuator(gameobj, KX_ACT_RANDOM),
57           m_propname(propName),
58           m_parameter1(para1),
59           m_parameter2(para2),
60           m_distribution(mode)
61 {
62         m_base = new SCA_RandomNumberGenerator(seed);
63         m_counter = 0;
64         enforceConstraints();
65
66
67
68
69 SCA_RandomActuator::~SCA_RandomActuator()
70 {
71         m_base->Release();
72
73
74
75
76 CValue* SCA_RandomActuator::GetReplica()
77 {
78         SCA_RandomActuator* replica = new SCA_RandomActuator(*this);
79         // replication just copy the m_base pointer => common random generator
80         replica->ProcessReplica();
81         return replica;
82 }
83
84 void SCA_RandomActuator::ProcessReplica()
85 {
86         SCA_IActuator::ProcessReplica();
87         // increment reference count so that we can release the generator at the end
88         m_base->AddRef();
89 }
90
91
92
93 bool SCA_RandomActuator::Update()
94 {
95         //bool result = false;  /*unused*/
96         bool bNegativeEvent = IsNegativeEvent();
97
98         RemoveAllEvents();
99
100
101         CValue *tmpval = NULL;
102
103         if (bNegativeEvent)
104                 return false; // do nothing on negative events
105
106         switch (m_distribution) {
107         case KX_RANDOMACT_BOOL_CONST: {
108                 /* un petit peu filthy */
109                 bool res = !(m_parameter1 < 0.5);
110                 tmpval = new CBoolValue(res);
111         }
112         break;
113         case KX_RANDOMACT_BOOL_UNIFORM: {
114                 /* flip a coin */
115                 bool res; 
116                 if (m_counter > 31) {
117                         m_previous = m_base->Draw();
118                         res = ((m_previous & 0x1) == 0);
119                         m_counter = 1;
120                 } else {
121                         res = (((m_previous >> m_counter) & 0x1) == 0);
122                         m_counter++;
123                 }
124                 tmpval = new CBoolValue(res);
125         }
126         break;
127         case KX_RANDOMACT_BOOL_BERNOUILLI: {
128                 /* 'percentage' */
129                 bool res;
130                 res = (m_base->DrawFloat() < m_parameter1);
131                 tmpval = new CBoolValue(res);
132         }
133         break;
134         case KX_RANDOMACT_INT_CONST: {
135                 /* constant */
136                 tmpval = new CIntValue((int) floor(m_parameter1));
137         }
138         break;
139         case KX_RANDOMACT_INT_UNIFORM: {
140                 /* uniform (toss a die) */
141                 int res; 
142                 /* The [0, 1] interval is projected onto the [min, max+1] domain,    */
143                 /* and then rounded.                                                 */
144                 res = (int)floor( ((m_parameter2 - m_parameter1 + 1) * m_base->DrawFloat()) + m_parameter1);
145                 tmpval = new CIntValue(res);
146         }
147         break;
148         case KX_RANDOMACT_INT_POISSON: {
149                 /* poisson (queues) */
150                 /* If x_1, x_2, ... is a sequence of random numbers with uniform     */
151                 /* distribution between zero and one, k is the first integer for     */
152                 /* which the product x_1*x_2*...*x_k < exp(-\lamba).                 */
153                 float a, b;
154                 int res = 0;
155                 /* The - sign is important here! The number to test for, a, must be  */
156                 /* between 0 and 1.                                                  */
157                 a = exp(-m_parameter1);
158                 /* a quickly reaches 0.... so we guard explicitly for that.          */
159                 if (a < FLT_MIN) a = FLT_MIN;
160                 b = m_base->DrawFloat();
161                 while (b >= a) {
162                         b = b * m_base->DrawFloat();
163                         res++;
164                 };      
165                 tmpval = new CIntValue(res);
166         }
167         break;
168         case KX_RANDOMACT_FLOAT_CONST: {
169                 /* constant */
170                 tmpval = new CFloatValue(m_parameter1);
171         }
172         break;
173         case KX_RANDOMACT_FLOAT_UNIFORM: {
174                 float res = ((m_parameter2 - m_parameter1) * m_base->DrawFloat()) + m_parameter1;
175                 tmpval = new CFloatValue(res);
176         }
177         break;
178         case KX_RANDOMACT_FLOAT_NORMAL: {
179                 /* normal (big numbers): para1 = mean, para2 = std dev               */
180
181                 /* 
182
183                    070301 - nzc - Changed the termination condition. I think I 
184                    made a small mistake here, but it only affects distro's where
185                    the seed equals 0. In that case, the algorithm locks. Let's
186                    just guard that case separately.
187
188                 */
189
190                 float x = 0.0, y = 0.0, s = 0.0, t = 0.0;
191                 if (m_base->GetSeed() == 0) {
192                         /*
193
194                           070301 - nzc 
195                           Just taking the mean here seems reasonable.
196
197                          */
198                         tmpval = new CFloatValue(m_parameter1);
199                 } else {
200                         /*
201
202                           070301 - nzc 
203                           Now, with seed != 0, we will most assuredly get some
204                           sensible values. The termination condition states two 
205                           things: 
206                           1. s >= 0 is not allowed: to prevent the distro from 
207                                  getting a bias towards high values. This is a small
208                                  correction, really, and might also be left out.
209                           2. s == 0 is not allowed: to prevent a division by zero
210                                  when renormalising the drawn value to the desired
211                                  distribution shape. As a side effect, the distro will
212                                  never yield the exact mean. 
213                           I am not sure whether this is consistent, since the error 
214                           cause by #2 is of the same magnitude as the one 
215                           prevented by #1. The error introduced into the SD will be 
216                           improved, though. By how much? Hard to say... If you like
217                           the maths, feel free to analyse. Be aware that this is 
218                           one of the really old standard algorithms. I think the 
219                           original came in Fortran, was translated to Pascal, and 
220                           then someone came up with the C code. My guess it that
221                           this will be quite sufficient here.
222
223                          */
224                         do 
225                         {
226                                 x = 2.0 * m_base->DrawFloat() - 1.0;
227                                 y = 2.0 * m_base->DrawFloat() - 1.0;
228                                 s = x*x + y*y;
229                         } while ( (s >= 1.0) || (s == 0.0) );
230                         t = x * sqrt( (-2.0 * log(s)) / s);
231                         tmpval = new CFloatValue(m_parameter1 + m_parameter2 * t);
232                 }
233         }
234         break;
235         case KX_RANDOMACT_FLOAT_NEGATIVE_EXPONENTIAL: {
236                 /* 1st order fall-off. I am very partial to using the half-life as    */
237                 /* controlling parameter. Using the 'normal' exponent is not very     */
238                 /* intuitive...                                                       */
239                 /* tmpval = new CFloatValue( (1.0 / m_parameter1)                     */
240                 tmpval = new CFloatValue((m_parameter1)  * (-log(1.0 - m_base->DrawFloat())));
241
242         }
243         break;
244         default:
245         {
246                 /* unknown distribution... */
247                 static bool randomWarning = false;
248                 if (!randomWarning) {
249                         randomWarning = true;
250                         std::cout << "RandomActuator '" << GetName() << "' has an unknown distribution." << std::endl;
251                 }
252                 return false;
253         }
254         }
255
256         /* Round up: assign it */
257         CValue *prop = GetParent()->GetProperty(m_propname);
258         if (prop) {
259                 prop->SetValue(tmpval);
260         }
261         tmpval->Release();
262
263         return false;
264 }
265
266 void SCA_RandomActuator::enforceConstraints()
267 {
268         /* The constraints that are checked here are the ones fundamental to     */
269         /* the various distributions. Limitations of the algorithms are checked  */
270         /* elsewhere (or they should be... ).                                    */
271         switch (m_distribution) {
272         case KX_RANDOMACT_BOOL_CONST:
273         case KX_RANDOMACT_BOOL_UNIFORM:
274         case KX_RANDOMACT_INT_CONST:
275         case KX_RANDOMACT_INT_UNIFORM:
276         case KX_RANDOMACT_FLOAT_UNIFORM:
277         case KX_RANDOMACT_FLOAT_CONST:
278                 ; /* Nothing to be done here. We allow uniform distro's to have      */
279                 /* 'funny' domains, i.e. max < min. This does not give problems.     */
280                 break;
281         case KX_RANDOMACT_BOOL_BERNOUILLI: 
282                 /* clamp to [0, 1] */
283                 if (m_parameter1 < 0.0) {
284                         m_parameter1 = 0.0;
285                 } else if (m_parameter1 > 1.0) {
286                         m_parameter1 = 1.0;
287                 }
288                 break;
289         case KX_RANDOMACT_INT_POISSON: 
290                 /* non-negative */
291                 if (m_parameter1 < 0.0) {
292                         m_parameter1 = 0.0;
293                 }
294                 break;
295         case KX_RANDOMACT_FLOAT_NORMAL: 
296                 /* standard dev. is non-negative */
297                 if (m_parameter2 < 0.0) {
298                         m_parameter2 = 0.0;
299                 }
300                 break;
301         case KX_RANDOMACT_FLOAT_NEGATIVE_EXPONENTIAL: 
302                 /* halflife must be non-negative */
303                 if (m_parameter1 < 0.0) {
304                         m_parameter1 = 0.0;
305                 }
306                 break;
307         default:
308                 ; /* unknown distribution... */
309         }
310 }
311
312 #ifdef WITH_PYTHON
313
314 /* ------------------------------------------------------------------------- */
315 /* Python functions                                                          */
316 /* ------------------------------------------------------------------------- */
317
318 /* Integration hooks ------------------------------------------------------- */
319 PyTypeObject SCA_RandomActuator::Type = {
320         PyVarObject_HEAD_INIT(NULL, 0)
321         "SCA_RandomActuator",
322         sizeof(PyObjectPlus_Proxy),
323         0,
324         py_base_dealloc,
325         0,
326         0,
327         0,
328         0,
329         py_base_repr,
330         0,0,0,0,0,0,0,0,0,
331         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
332         0,0,0,0,0,0,0,
333         Methods,
334         0,
335         0,
336         &SCA_IActuator::Type,
337         0,0,0,0,0,0,
338         py_base_new
339 };
340
341 PyMethodDef SCA_RandomActuator::Methods[] = {
342         KX_PYMETHODTABLE(SCA_RandomActuator, setBoolConst),
343         KX_PYMETHODTABLE_NOARGS(SCA_RandomActuator, setBoolUniform),
344         KX_PYMETHODTABLE(SCA_RandomActuator, setBoolBernouilli),
345
346         KX_PYMETHODTABLE(SCA_RandomActuator, setIntConst),
347         KX_PYMETHODTABLE(SCA_RandomActuator, setIntUniform),
348         KX_PYMETHODTABLE(SCA_RandomActuator, setIntPoisson),
349
350         KX_PYMETHODTABLE(SCA_RandomActuator, setFloatConst),
351         KX_PYMETHODTABLE(SCA_RandomActuator, setFloatUniform),
352         KX_PYMETHODTABLE(SCA_RandomActuator, setFloatNormal),
353         KX_PYMETHODTABLE(SCA_RandomActuator, setFloatNegativeExponential),
354         {NULL,NULL} //Sentinel
355 };
356
357 PyAttributeDef SCA_RandomActuator::Attributes[] = {
358         KX_PYATTRIBUTE_FLOAT_RO("para1",SCA_RandomActuator,m_parameter1),
359         KX_PYATTRIBUTE_FLOAT_RO("para2",SCA_RandomActuator,m_parameter2),
360         KX_PYATTRIBUTE_ENUM_RO("distribution",SCA_RandomActuator,m_distribution),
361         KX_PYATTRIBUTE_STRING_RW_CHECK("propName",0,MAX_PROP_NAME,false,SCA_RandomActuator,m_propname,CheckProperty),
362         KX_PYATTRIBUTE_RW_FUNCTION("seed",SCA_RandomActuator,pyattr_get_seed,pyattr_set_seed),
363         { NULL }        //Sentinel
364 };      
365
366 PyObject* SCA_RandomActuator::pyattr_get_seed(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef)
367 {
368         SCA_RandomActuator* act = static_cast<SCA_RandomActuator*>(self);
369         return PyLong_FromSsize_t(act->m_base->GetSeed());
370 }
371
372 int SCA_RandomActuator::pyattr_set_seed(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
373 {
374         SCA_RandomActuator* act = static_cast<SCA_RandomActuator*>(self);
375         if (PyLong_Check(value))        {
376                 int ival = PyLong_AsSsize_t(value);
377                 act->m_base->SetSeed(ival);
378                 return PY_SET_ATTR_SUCCESS;
379         } else {
380                 PyErr_SetString(PyExc_TypeError, "actuator.seed = int: Random Actuator, expected an integer");
381                 return PY_SET_ATTR_FAIL;
382         }
383 }
384
385 /* 11. setBoolConst */
386 KX_PYMETHODDEF_DOC_VARARGS(SCA_RandomActuator, setBoolConst,
387 "setBoolConst(value)\n"
388 "\t- value: 0 or 1\n"
389 "\tSet this generator to produce a constant boolean value.\n") 
390 {
391         int paraArg;
392         if (!PyArg_ParseTuple(args, "i:setBoolConst", &paraArg)) {
393                 return NULL;
394         }
395         
396         m_distribution = KX_RANDOMACT_BOOL_CONST;
397         m_parameter1 = (paraArg) ? 1.0 : 0.0;
398         
399         Py_RETURN_NONE;
400 }
401 /* 12. setBoolUniform, */
402 KX_PYMETHODDEF_DOC_NOARGS(SCA_RandomActuator, setBoolUniform,
403 "setBoolUniform()\n"
404 "\tSet this generator to produce true and false, each with 50%% chance of occurring\n") 
405 {
406         /* no args */
407         m_distribution = KX_RANDOMACT_BOOL_UNIFORM;
408         enforceConstraints();
409         Py_RETURN_NONE;
410 }
411 /* 13. setBoolBernouilli,  */
412 KX_PYMETHODDEF_DOC_VARARGS(SCA_RandomActuator, setBoolBernouilli,
413 "setBoolBernouilli(value)\n"
414 "\t- value: a float between 0 and 1\n"
415 "\tReturn false value * 100%% of the time.\n")
416 {
417         float paraArg;
418         if (!PyArg_ParseTuple(args, "f:setBoolBernouilli", &paraArg)) {
419                 return NULL;
420         }
421         
422         m_distribution = KX_RANDOMACT_BOOL_BERNOUILLI;
423         m_parameter1 = paraArg; 
424         enforceConstraints();
425         Py_RETURN_NONE;
426 }
427 /* 14. setIntConst,*/
428 KX_PYMETHODDEF_DOC_VARARGS(SCA_RandomActuator, setIntConst,
429 "setIntConst(value)\n"
430 "\t- value: integer\n"
431 "\tAlways return value\n") 
432 {
433         int paraArg;
434         if (!PyArg_ParseTuple(args, "i:setIntConst", &paraArg)) {
435                 return NULL;
436         }
437         
438         m_distribution = KX_RANDOMACT_INT_CONST;
439         m_parameter1 = paraArg;
440         enforceConstraints();
441         Py_RETURN_NONE;
442 }
443 /* 15. setIntUniform,*/
444 KX_PYMETHODDEF_DOC_VARARGS(SCA_RandomActuator, setIntUniform,
445 "setIntUniform(lower_bound, upper_bound)\n"
446 "\t- lower_bound: integer\n"
447 "\t- upper_bound: integer\n"
448 "\tReturn a random integer between lower_bound and\n"
449 "\tupper_bound. The boundaries are included.\n")
450 {
451         int paraArg1, paraArg2;
452         if (!PyArg_ParseTuple(args, "ii:setIntUniform", &paraArg1, &paraArg2)) {
453                 return NULL;
454         }
455         
456         m_distribution = KX_RANDOMACT_INT_UNIFORM;
457         m_parameter1 = paraArg1;
458         m_parameter2 = paraArg2;
459         enforceConstraints();
460         Py_RETURN_NONE;
461 }
462 /* 16. setIntPoisson,           */
463 KX_PYMETHODDEF_DOC_VARARGS(SCA_RandomActuator, setIntPoisson,
464 "setIntPoisson(value)\n"
465 "\t- value: float\n"
466 "\tReturn a Poisson-distributed number. This performs a series\n"
467 "\tof Bernouilli tests with parameter value. It returns the\n"
468 "\tnumber of tries needed to achieve succes.\n")
469 {
470         float paraArg;
471         if (!PyArg_ParseTuple(args, "f:setIntPoisson", &paraArg)) {
472                 return NULL;
473         }
474         
475         m_distribution = KX_RANDOMACT_INT_POISSON;
476         m_parameter1 = paraArg; 
477         enforceConstraints();
478         Py_RETURN_NONE;
479 }
480 /* 17. setFloatConst */
481 KX_PYMETHODDEF_DOC_VARARGS(SCA_RandomActuator, setFloatConst,
482 "setFloatConst(value)\n"
483 "\t- value: float\n"
484 "\tAlways return value\n")
485 {
486         float paraArg;
487         if (!PyArg_ParseTuple(args, "f:setFloatConst", &paraArg)) {
488                 return NULL;
489         }
490         
491         m_distribution = KX_RANDOMACT_FLOAT_CONST;
492         m_parameter1 = paraArg; 
493         enforceConstraints();
494         Py_RETURN_NONE;
495 }
496 /* 18. setFloatUniform, */
497 KX_PYMETHODDEF_DOC_VARARGS(SCA_RandomActuator, setFloatUniform,
498 "setFloatUniform(lower_bound, upper_bound)\n"
499 "\t- lower_bound: float\n"
500 "\t- upper_bound: float\n"
501 "\tReturn a random integer between lower_bound and\n"
502 "\tupper_bound.\n")
503 {
504         float paraArg1, paraArg2;
505         if (!PyArg_ParseTuple(args, "ff:setFloatUniform", &paraArg1, &paraArg2)) {
506                 return NULL;
507         }
508         
509         m_distribution = KX_RANDOMACT_FLOAT_UNIFORM;
510         m_parameter1 = paraArg1;
511         m_parameter2 = paraArg2;
512         enforceConstraints();
513         Py_RETURN_NONE;
514 }
515 /* 19. setFloatNormal, */
516 KX_PYMETHODDEF_DOC_VARARGS(SCA_RandomActuator, setFloatNormal,
517 "setFloatNormal(mean, standard_deviation)\n"
518 "\t- mean: float\n"
519 "\t- standard_deviation: float\n"
520 "\tReturn normal-distributed numbers. The average is mean, and the\n"
521 "\tdeviation from the mean is characterized by standard_deviation.\n")
522 {
523         float paraArg1, paraArg2;
524         if (!PyArg_ParseTuple(args, "ff:setFloatNormal", &paraArg1, &paraArg2)) {
525                 return NULL;
526         }
527         
528         m_distribution = KX_RANDOMACT_FLOAT_NORMAL;
529         m_parameter1 = paraArg1;
530         m_parameter2 = paraArg2;
531         enforceConstraints();
532         Py_RETURN_NONE;
533 }
534 /* 20. setFloatNegativeExponential, */
535 KX_PYMETHODDEF_DOC_VARARGS(SCA_RandomActuator, setFloatNegativeExponential, 
536 "setFloatNegativeExponential(half_life)\n"
537 "\t- half_life: float\n"
538 "\tReturn negative-exponentially distributed numbers. The half-life 'time'\n"
539 "\tis characterized by half_life.\n")
540 {
541         float paraArg;
542         if (!PyArg_ParseTuple(args, "f:setFloatNegativeExponential", &paraArg)) {
543                 return NULL;
544         }
545         
546         m_distribution = KX_RANDOMACT_FLOAT_NEGATIVE_EXPONENTIAL;
547         m_parameter1 = paraArg; 
548         enforceConstraints();
549         Py_RETURN_NONE;
550 }
551
552 #endif
553
554 /* eof */