Merge of itasc branch. Project files, scons and cmake should be working. Makefile...
[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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, 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 "BoolValue.h"
33 #include "IntValue.h"
34 #include "FloatValue.h"
35 #include "SCA_IActuator.h"
36 #include "SCA_RandomActuator.h"
37 #include "math.h"
38 #include "MT_Transform.h"
39
40 #ifdef HAVE_CONFIG_H
41 #include <config.h>
42 #endif
43
44 /* ------------------------------------------------------------------------- */
45 /* Native functions                                                          */
46 /* ------------------------------------------------------------------------- */
47
48 SCA_RandomActuator::SCA_RandomActuator(SCA_IObject *gameobj, 
49                                                                          long seed,
50                                                                          SCA_RandomActuator::KX_RANDOMACT_MODE mode,
51                                                                          float para1,
52                                                                          float para2,
53                                                                          const STR_String &propName)
54         : SCA_IActuator(gameobj, KX_ACT_RANDOM),
55           m_propname(propName),
56           m_parameter1(para1),
57           m_parameter2(para2),
58           m_distribution(mode)
59 {
60         m_base = new SCA_RandomNumberGenerator(seed);
61         m_counter = 0;
62         enforceConstraints();
63
64
65
66
67 SCA_RandomActuator::~SCA_RandomActuator()
68 {
69         m_base->Release();
70
71
72
73
74 CValue* SCA_RandomActuator::GetReplica()
75 {
76         SCA_RandomActuator* replica = new SCA_RandomActuator(*this);
77         // replication just copy the m_base pointer => common random generator
78         replica->ProcessReplica();
79         return replica;
80 }
81
82 void SCA_RandomActuator::ProcessReplica()
83 {
84         SCA_IActuator::ProcessReplica();
85         // increment reference count so that we can release the generator at the end
86         m_base->AddRef();
87 }
88
89
90
91 bool SCA_RandomActuator::Update()
92 {
93         //bool result = false;  /*unused*/
94         bool bNegativeEvent = IsNegativeEvent();
95
96         RemoveAllEvents();
97
98
99         CValue *tmpval = NULL;
100
101         if (bNegativeEvent)
102                 return false; // do nothing on negative events
103
104         switch (m_distribution) {
105         case KX_RANDOMACT_BOOL_CONST: {
106                 /* un petit peu filthy */
107                 bool res = !(m_parameter1 < 0.5);
108                 tmpval = new CBoolValue(res);
109         }
110         break;
111         case KX_RANDOMACT_BOOL_UNIFORM: {
112                 /* flip a coin */
113                 bool res; 
114                 if (m_counter > 31) {
115                         m_previous = m_base->Draw();
116                         res = ((m_previous & 0x1) == 0);
117                         m_counter = 1;
118                 } else {
119                         res = (((m_previous >> m_counter) & 0x1) == 0);
120                         m_counter++;
121                 }
122                 tmpval = new CBoolValue(res);
123         }
124         break;
125         case KX_RANDOMACT_BOOL_BERNOUILLI: {
126                 /* 'percentage' */
127                 bool res;
128                 res = (m_base->DrawFloat() < m_parameter1);
129                 tmpval = new CBoolValue(res);
130         }
131         break;
132         case KX_RANDOMACT_INT_CONST: {
133                 /* constant */
134                 tmpval = new CIntValue((int) floor(m_parameter1));
135         }
136         break;
137         case KX_RANDOMACT_INT_UNIFORM: {
138                 /* uniform (toss a die) */
139                 int res; 
140                 /* The [0, 1] interval is projected onto the [min, max+1] domain,    */
141                 /* and then rounded.                                                 */
142                 res = (int) floor( ((m_parameter2 - m_parameter1 + 1) * m_base->DrawFloat())
143                                                    + m_parameter1);
144                 tmpval = new CIntValue(res);
145         }
146         break;
147         case KX_RANDOMACT_INT_POISSON: {
148                 /* poisson (queues) */
149                 /* If x_1, x_2, ... is a sequence of random numbers with uniform     */
150                 /* distribution between zero and one, k is the first integer for     */
151                 /* which the product x_1*x_2*...*x_k < exp(-\lamba).                 */
152                 float a = 0.0, b = 0.0;
153                 int res = 0;
154                 /* The - sign is important here! The number to test for, a, must be  */
155                 /* between 0 and 1.                                                  */
156                 a = exp(-m_parameter1);
157                 /* a quickly reaches 0.... so we guard explicitly for that.          */
158                 if (a < FLT_MIN) a = FLT_MIN;
159                 b = m_base->DrawFloat();
160                 while (b >= a) {
161                         b = b * m_base->DrawFloat();
162                         res++;
163                 };      
164                 tmpval = new CIntValue(res);
165         }
166         break;
167         case KX_RANDOMACT_FLOAT_CONST: {
168                 /* constant */
169                 tmpval = new CFloatValue(m_parameter1);
170         }
171         break;
172         case KX_RANDOMACT_FLOAT_UNIFORM: {
173                 float res = ((m_parameter2 - m_parameter1) * m_base->DrawFloat())
174                         + 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) 
241                                                                   * (-log(1.0 - m_base->DrawFloat())) );
242
243         }
244         break;
245         default:
246         {
247                 /* unknown distribution... */
248                 static bool randomWarning = false;
249                 if (!randomWarning) {
250                         randomWarning = true;
251                         std::cout << "RandomActuator '" << GetName() << "' has an unknown distribution." << std::endl;
252                 }
253                 return false;
254         }
255         }
256
257         /* Round up: assign it */
258         CValue *prop = GetParent()->GetProperty(m_propname);
259         if (prop) {
260                 prop->SetValue(tmpval);
261         }
262         tmpval->Release();
263
264         return false;
265 }
266
267 void SCA_RandomActuator::enforceConstraints() {
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 /* ------------------------------------------------------------------------- */
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 /* eof */