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