games_dice 0.3.3 → 0.3.5

Sign up to get free protection for your applications and to get access to all the features.
data/.travis.yml CHANGED
@@ -6,3 +6,4 @@ rvm:
6
6
  - rbx-18mode
7
7
  - rbx-19mode
8
8
  - ree
9
+
data/Rakefile CHANGED
@@ -3,6 +3,11 @@ require "rspec/core/rake_task"
3
3
  require 'rake/extensiontask'
4
4
  require "yard"
5
5
 
6
+ def can_compile_extensions
7
+ return false if RUBY_DESCRIPTION =~ /jruby/
8
+ return true
9
+ end
10
+
6
11
  desc "GamesDice unit tests"
7
12
  RSpec::Core::RakeTask.new(:test) do |t|
8
13
  t.pattern = "spec/*_spec.rb"
@@ -22,4 +27,14 @@ Rake::ExtensionTask.new do |ext|
22
27
  ext.gem_spec = gemspec
23
28
  end
24
29
 
25
- task :default => [:compile, :test]
30
+ task :delete_compiled_ext do |t|
31
+ `rm lib/games_dice/games_dice.*`
32
+ end
33
+
34
+ task :pure_test => [:delete_compiled_ext, :test]
35
+
36
+ if can_compile_extensions
37
+ task :default => [:compile, :test]
38
+ else
39
+ task :default => [:test]
40
+ end
@@ -21,7 +21,7 @@ VALUE Probabilities = Qnil;
21
21
  // General utils
22
22
  //
23
23
 
24
- static inline int max( int *a, int n ) {
24
+ inline int max( int *a, int n ) {
25
25
  int m = -1000000000;
26
26
  int i;
27
27
  for ( i=0; i < n; i++ ) {
@@ -30,7 +30,7 @@ static inline int max( int *a, int n ) {
30
30
  return m;
31
31
  }
32
32
 
33
- static inline int min( int *a, int n ) {
33
+ inline int min( int *a, int n ) {
34
34
  int m = 1000000000;
35
35
  int i;
36
36
  for ( i=0; i < n; i++ ) {
@@ -46,7 +46,7 @@ static inline int min( int *a, int n ) {
46
46
  //
47
47
 
48
48
  // There is no point calculating these, a cache of them is just fine.
49
- static double nfact[171] = {
49
+ double nfact[171] = {
50
50
  1.0, 1.0, 2.0, 6.0,
51
51
  24.0, 120.0, 720.0, 5040.0,
52
52
  40320.0, 362880.0, 3628800.0, 39916800.0,
@@ -91,7 +91,7 @@ static double nfact[171] = {
91
91
  3.287218585534296e+293, 5.423910666131589e+295, 9.003691705778438e+297, 1.503616514864999e+300,
92
92
  2.5260757449731984e+302, 4.269068009004705e+304, 7.257415615307999e+306 };
93
93
 
94
- static double num_arrangements( int *args, int nargs ) {
94
+ double num_arrangements( int *args, int nargs ) {
95
95
  int sum = 0;
96
96
  double div_by = 1.0;
97
97
  int i;
@@ -110,7 +110,7 @@ static double num_arrangements( int *args, int nargs ) {
110
110
  // Probability List basics - create, delete, copy
111
111
  //
112
112
 
113
- static ProbabilityList *create_probability_list() {
113
+ ProbabilityList *create_probability_list() {
114
114
  ProbabilityList *pl;
115
115
  pl = malloc (sizeof(ProbabilityList));
116
116
  if ( pl == NULL ) {
@@ -123,20 +123,22 @@ static ProbabilityList *create_probability_list() {
123
123
  return pl;
124
124
  }
125
125
 
126
- static void destroy_probability_list( ProbabilityList *pl ) {
126
+ void destroy_probability_list( ProbabilityList *pl ) {
127
127
  xfree( pl->cumulative );
128
128
  xfree( pl->probs );
129
129
  xfree( pl );
130
130
  return;
131
131
  }
132
132
 
133
- static double *alloc_probs( ProbabilityList *pl, int slots ) {
133
+ double *alloc_probs( ProbabilityList *pl, int slots ) {
134
+ double *pr;
135
+
134
136
  if ( slots < 1 || slots > 1000000 ) {
135
137
  rb_raise(rb_eArgError, "Bad number of probability slots");
136
138
  }
137
139
  pl->slots = slots;
138
140
 
139
- double *pr = ALLOC_N( double, slots );
141
+ pr = ALLOC_N( double, slots );
140
142
  pl->probs = pr;
141
143
 
142
144
  pl->cumulative = ALLOC_N( double, slots );
@@ -144,7 +146,7 @@ static double *alloc_probs( ProbabilityList *pl, int slots ) {
144
146
  return pr;
145
147
  }
146
148
 
147
- static double calc_cumulative( ProbabilityList *pl ) {
149
+ double calc_cumulative( ProbabilityList *pl ) {
148
150
  double *c = pl->cumulative;
149
151
  double *pr = pl->probs;
150
152
  int i;
@@ -156,12 +158,14 @@ static double calc_cumulative( ProbabilityList *pl ) {
156
158
  return t;
157
159
  }
158
160
 
159
- static double *alloc_probs_iv( ProbabilityList *pl, int slots, double iv ) {
161
+ double *alloc_probs_iv( ProbabilityList *pl, int slots, double iv ) {
162
+ double *pr;
163
+ int i;
164
+
160
165
  if ( iv < 0.0 || iv > 1.0 ) {
161
166
  rb_raise(rb_eArgError, "Bad single probability value");
162
167
  }
163
- double *pr = alloc_probs( pl, slots );
164
- int i;
168
+ pr= alloc_probs( pl, slots );
165
169
  for(i=0; i<slots; i++) {
166
170
  pr[i] = iv;
167
171
  }
@@ -169,16 +173,17 @@ static double *alloc_probs_iv( ProbabilityList *pl, int slots, double iv ) {
169
173
  return pr;
170
174
  }
171
175
 
172
- static ProbabilityList *copy_probability_list( ProbabilityList *orig ) {
176
+ ProbabilityList *copy_probability_list( ProbabilityList *orig ) {
173
177
  ProbabilityList *pl = create_probability_list();
174
- double *pr = alloc_probs( pl, orig->slots );
178
+ double *pr;
179
+ pr = alloc_probs( pl, orig->slots );
175
180
  pl->offset = orig->offset;
176
181
  memcpy( pr, orig->probs, orig->slots * sizeof(double) );
177
182
  memcpy( pl->cumulative, orig->cumulative, orig->slots * sizeof(double) );
178
183
  return pl;
179
184
  }
180
185
 
181
- static inline ProbabilityList *new_basic_pl( int nslots, double iv, int o ) {
186
+ inline ProbabilityList *new_basic_pl( int nslots, double iv, int o ) {
182
187
  ProbabilityList *pl = create_probability_list();
183
188
  alloc_probs_iv( pl, nslots, iv );
184
189
  pl->offset = o;
@@ -190,22 +195,23 @@ static inline ProbabilityList *new_basic_pl( int nslots, double iv, int o ) {
190
195
  // Probability List core "native" methods
191
196
  //
192
197
 
193
- static inline int pl_min( ProbabilityList *pl ) {
198
+ inline int pl_min( ProbabilityList *pl ) {
194
199
  return pl->offset;
195
200
  }
196
201
 
197
- static inline int pl_max( ProbabilityList *pl ) {
202
+ inline int pl_max( ProbabilityList *pl ) {
198
203
  return pl->offset + pl->slots - 1;
199
204
  }
200
205
 
201
- static ProbabilityList *pl_add_distributions( ProbabilityList *pl_a, ProbabilityList *pl_b ) {
206
+ ProbabilityList *pl_add_distributions( ProbabilityList *pl_a, ProbabilityList *pl_b ) {
207
+ double *pr;
202
208
  int s = pl_a->slots + pl_b->slots - 1;
203
209
  int o = pl_a->offset + pl_b->offset;
204
210
  int i,j;
205
211
 
206
212
  ProbabilityList *pl = create_probability_list();
207
213
  pl->offset = o;
208
- double *pr = alloc_probs_iv( pl, s, 0.0 );
214
+ pr = alloc_probs_iv( pl, s, 0.0 );
209
215
  for ( i=0; i < pl_a->slots; i++ ) { for ( j=0; j < pl_b->slots; j++ ) {
210
216
  pr[ i + j ] += (pl_a->probs)[i] * (pl_b->probs)[j];
211
217
  } }
@@ -213,21 +219,22 @@ static ProbabilityList *pl_add_distributions( ProbabilityList *pl_a, Probability
213
219
  return pl;
214
220
  }
215
221
 
216
- static ProbabilityList *pl_add_distributions_mult( int mul_a, ProbabilityList *pl_a, int mul_b, ProbabilityList *pl_b ) {
222
+ ProbabilityList *pl_add_distributions_mult( int mul_a, ProbabilityList *pl_a, int mul_b, ProbabilityList *pl_b ) {
217
223
  int pts[4] = {
218
224
  mul_a * pl_min( pl_a ) + mul_b * pl_min( pl_b ),
219
225
  mul_a * pl_max( pl_a ) + mul_b * pl_min( pl_b ),
220
226
  mul_a * pl_min( pl_a ) + mul_b * pl_max( pl_b ),
221
227
  mul_a * pl_max( pl_a ) + mul_b * pl_max( pl_b ) };
222
228
 
229
+ double *pr;
223
230
  int combined_min = min( pts, 4 );
224
231
  int combined_max = max( pts, 4 );
225
232
  int s = 1 + combined_max - combined_min;
233
+ int i,j;
226
234
 
227
235
  ProbabilityList *pl = create_probability_list();
228
236
  pl->offset = combined_min;
229
- double *pr = alloc_probs_iv( pl, s, 0.0 );
230
- int i,j;
237
+ pr = alloc_probs_iv( pl, s, 0.0 );
231
238
  for ( i=0; i < pl_a->slots; i++ ) { for ( j=0; j < pl_b->slots; j++ ) {
232
239
  int k = mul_a * (i + pl_a->offset) + mul_b * (j + pl_b->offset) - combined_min;
233
240
  pr[ k ] += (pl_a->probs)[i] * (pl_b->probs)[j];
@@ -236,7 +243,7 @@ static ProbabilityList *pl_add_distributions_mult( int mul_a, ProbabilityList *p
236
243
  return pl;
237
244
  }
238
245
 
239
- static inline double pl_p_eql( ProbabilityList *pl, int target ) {
246
+ inline double pl_p_eql( ProbabilityList *pl, int target ) {
240
247
  int idx = target - pl->offset;
241
248
  if ( idx < 0 || idx >= pl->slots ) {
242
249
  return 0.0;
@@ -244,15 +251,15 @@ static inline double pl_p_eql( ProbabilityList *pl, int target ) {
244
251
  return (pl->probs)[idx];
245
252
  }
246
253
 
247
- static inline double pl_p_gt( ProbabilityList *pl, int target ) {
254
+ inline double pl_p_gt( ProbabilityList *pl, int target ) {
248
255
  return 1.0 - pl_p_le( pl, target );
249
256
  }
250
257
 
251
- static inline double pl_p_lt( ProbabilityList *pl, int target ) {
258
+ inline double pl_p_lt( ProbabilityList *pl, int target ) {
252
259
  return pl_p_le( pl, target - 1 );
253
260
  }
254
261
 
255
- static inline double pl_p_le( ProbabilityList *pl, int target ) {
262
+ inline double pl_p_le( ProbabilityList *pl, int target ) {
256
263
  int idx = target - pl->offset;
257
264
  if ( idx < 0 ) {
258
265
  return 0.0;
@@ -263,11 +270,11 @@ static inline double pl_p_le( ProbabilityList *pl, int target ) {
263
270
  return (pl->cumulative)[idx];
264
271
  }
265
272
 
266
- static inline double pl_p_ge( ProbabilityList *pl, int target ) {
273
+ inline double pl_p_ge( ProbabilityList *pl, int target ) {
267
274
  return 1.0 - pl_p_le( pl, target - 1 );
268
275
  }
269
276
 
270
- static inline double pl_expected( ProbabilityList *pl ) {
277
+ inline double pl_expected( ProbabilityList *pl ) {
271
278
  double t = 0.0;
272
279
  int o = pl->offset;
273
280
  int s = pl->slots;
@@ -279,24 +286,30 @@ static inline double pl_expected( ProbabilityList *pl ) {
279
286
  return t;
280
287
  }
281
288
 
282
- static ProbabilityList *pl_given_ge( ProbabilityList *pl, int target ) {
289
+ ProbabilityList *pl_given_ge( ProbabilityList *pl, int target ) {
283
290
  int m = pl_min( pl );
291
+ double p, mult;
292
+ double *pr;
293
+ double *new_pr;
294
+ int o,i,s;
295
+ ProbabilityList *new_pl;
296
+
284
297
  if ( m > target ) {
285
298
  target = m;
286
299
  }
287
- double p = pl_p_ge( pl, target );
300
+ p = pl_p_ge( pl, target );
288
301
  if ( p <= 0.0 ) {
289
302
  rb_raise( rb_eRuntimeError, "Cannot calculate given probabilities, divide by zero" );
290
303
  }
291
- double mult = 1.0/p;
292
- int s = pl->slots + pl->offset - target;
293
- double *pr = pl->probs;
304
+ mult = 1.0/p;
305
+ s = pl->slots + pl->offset - target;
306
+ pr = pl->probs;
294
307
 
295
- ProbabilityList *new_pl = create_probability_list();
308
+ new_pl = create_probability_list();
296
309
  new_pl->offset = target;
297
- double *new_pr = alloc_probs( new_pl, s );
298
- int o = target - pl->offset;
299
- int i;
310
+ new_pr = alloc_probs( new_pl, s );
311
+ o = target - pl->offset;
312
+
300
313
  for ( i = 0; i < s; i++ ) {
301
314
  new_pr[i] = pr[o + i] * mult;
302
315
  }
@@ -304,23 +317,29 @@ static ProbabilityList *pl_given_ge( ProbabilityList *pl, int target ) {
304
317
  return new_pl;
305
318
  }
306
319
 
307
- static ProbabilityList *pl_given_le( ProbabilityList *pl, int target ) {
320
+ ProbabilityList *pl_given_le( ProbabilityList *pl, int target ) {
308
321
  int m = pl_max( pl );
322
+ double p, mult;
323
+ double *pr;
324
+ double *new_pr;
325
+ int i,s;
326
+ ProbabilityList *new_pl;
327
+
309
328
  if ( m < target ) {
310
329
  target = m;
311
330
  }
312
- double p = pl_p_le( pl, target );
331
+ p = pl_p_le( pl, target );
313
332
  if ( p <= 0.0 ) {
314
333
  rb_raise( rb_eRuntimeError, "Cannot calculate given probabilities, divide by zero" );
315
334
  }
316
- double mult = 1.0/p;
317
- int s = target - pl->offset + 1;
318
- double *pr = pl->probs;
335
+ mult = 1.0/p;
336
+ s = target - pl->offset + 1;
337
+ pr = pl->probs;
319
338
 
320
- ProbabilityList *new_pl = create_probability_list();
339
+ new_pl = create_probability_list();
321
340
  new_pl->offset = pl->offset;
322
- double *new_pr = alloc_probs( new_pl, s );
323
- int i;
341
+ new_pr = alloc_probs( new_pl, s );
342
+
324
343
  for ( i = 0; i < s; i++ ) {
325
344
  new_pr[i] = pr[i] * mult;
326
345
  }
@@ -328,7 +347,12 @@ static ProbabilityList *pl_given_le( ProbabilityList *pl, int target ) {
328
347
  return new_pl;
329
348
  }
330
349
 
331
- static ProbabilityList *pl_repeat_sum( ProbabilityList *pl, int n ) {
350
+ ProbabilityList *pl_repeat_sum( ProbabilityList *pl, int n ) {
351
+ ProbabilityList *pd_power = NULL;
352
+ ProbabilityList *pd_result = NULL;
353
+ ProbabilityList *pd_next = NULL;
354
+ int power = 1;
355
+
332
356
  if ( n < 1 ) {
333
357
  rb_raise( rb_eRuntimeError, "Cannot calculate repeat_sum when n < 1" );
334
358
  }
@@ -336,10 +360,8 @@ static ProbabilityList *pl_repeat_sum( ProbabilityList *pl, int n ) {
336
360
  rb_raise( rb_eRuntimeError, "Too many probability slots" );
337
361
  }
338
362
 
339
- ProbabilityList *pd_power = copy_probability_list( pl );
340
- ProbabilityList *pd_result = NULL;
341
- ProbabilityList *pd_next = NULL;
342
- int power = 1;
363
+ pd_power = copy_probability_list( pl );
364
+
343
365
  while ( 1 ) {
344
366
  if ( power & n ) {
345
367
  if ( pd_result ) {
@@ -362,7 +384,7 @@ static ProbabilityList *pl_repeat_sum( ProbabilityList *pl, int n ) {
362
384
  }
363
385
 
364
386
  // Assigns { p_rejected, p_maybe, p_kept } to buffer
365
- static void calc_p_table( ProbabilityList *pl, int q, int kbest, double *buffer ) {
387
+ void calc_p_table( ProbabilityList *pl, int q, int kbest, double *buffer ) {
366
388
  if ( kbest ) {
367
389
  buffer[2] = pl_p_gt( pl, q );
368
390
  buffer[1] = pl_p_eql( pl, q );
@@ -376,12 +398,12 @@ static void calc_p_table( ProbabilityList *pl, int q, int kbest, double *buffer
376
398
  }
377
399
 
378
400
  // Assigns a list of pl variants to a buffer
379
- static void calc_keep_distributions( ProbabilityList *pl, int k, int q, int kbest, ProbabilityList **pl_array ) {
380
- // Init array
401
+ void calc_keep_distributions( ProbabilityList *pl, int k, int q, int kbest, ProbabilityList **pl_array ) {
402
+ ProbabilityList *pl_kd;
403
+
381
404
  int n;
382
405
  for ( n=0; n<k; n++) { pl_array[n] = NULL; }
383
406
  pl_array[0] = new_basic_pl( 1, 1.0, q * k );
384
- ProbabilityList *pl_kd;
385
407
 
386
408
  if ( kbest ) {
387
409
  if ( pl_p_gt( pl, q ) > 0.0 && k > 1 ) {
@@ -404,7 +426,7 @@ static void calc_keep_distributions( ProbabilityList *pl, int k, int q, int kbes
404
426
  return;
405
427
  }
406
428
 
407
- static inline void clear_pl_array( int k, ProbabilityList **pl_array ) {
429
+ inline void clear_pl_array( int k, ProbabilityList **pl_array ) {
408
430
  int n;
409
431
  for ( n=0; n<k; n++) {
410
432
  if ( pl_array[n] != NULL ) {
@@ -414,7 +436,20 @@ static inline void clear_pl_array( int k, ProbabilityList **pl_array ) {
414
436
  return;
415
437
  }
416
438
 
417
- static ProbabilityList *pl_repeat_n_sum_k( ProbabilityList *pl, int n, int k, int kbest ) {
439
+ ProbabilityList *pl_repeat_n_sum_k( ProbabilityList *pl, int n, int k, int kbest ) {
440
+ // Table of probabilities ( reject, maybe, keep ) for each "pivot point"
441
+ double p_table[3];
442
+ int keep_combos[3];
443
+ // Table of distributions for each count of > pivot point (vs == pivot point)
444
+ ProbabilityList *keep_distributions[171];
445
+ ProbabilityList *kd;
446
+ ProbabilityList *pl_result = NULL;
447
+
448
+ double *pr;
449
+ int d = n - k;
450
+ int i, j, q, dn, kn, mn, kdq;
451
+ double p_sequence;
452
+
418
453
  if ( n < 1 ) {
419
454
  rb_raise( rb_eRuntimeError, "Cannot calculate repeat_n_sum_k when n < 1" );
420
455
  }
@@ -432,21 +467,10 @@ static ProbabilityList *pl_repeat_n_sum_k( ProbabilityList *pl, int n, int k, in
432
467
  }
433
468
 
434
469
  // Init target
435
- ProbabilityList *pl_result = create_probability_list();
436
- double *pr = alloc_probs_iv( pl_result, 1 + k * (pl->slots - 1), 0.0 );
470
+ pl_result = create_probability_list();
471
+ pr = alloc_probs_iv( pl_result, 1 + k * (pl->slots - 1), 0.0 );
437
472
  pl_result->offset = pl->offset * k;
438
473
 
439
- // Table of probabilities ( reject, maybe, keep ) for each "pivot point"
440
- double p_table[3];
441
- int keep_combos[3];
442
- // Table of distributions for each count of > pivot point (vs == pivot point)
443
- ProbabilityList *keep_distributions[171];
444
- ProbabilityList *kd;
445
-
446
- int d = n - k;
447
- int i, j, q, dn, kn, mn, kdq;
448
- double p_sequence;
449
-
450
474
  for ( i = 0; i < pl->slots; i++ ) {
451
475
  if ( ! pl->probs[i] > 0.0 ) continue;
452
476
 
@@ -491,21 +515,21 @@ static ProbabilityList *pl_repeat_n_sum_k( ProbabilityList *pl, int n, int k, in
491
515
  // Ruby integration
492
516
  //
493
517
 
494
- static inline VALUE pl_as_ruby_class( ProbabilityList *pl, VALUE klass ) {
518
+ inline VALUE pl_as_ruby_class( ProbabilityList *pl, VALUE klass ) {
495
519
  return Data_Wrap_Struct( klass, 0, destroy_probability_list, pl );
496
520
  }
497
521
 
498
- static VALUE pl_alloc(VALUE klass) {
522
+ VALUE pl_alloc(VALUE klass) {
499
523
  return pl_as_ruby_class( create_probability_list(), klass );
500
524
  }
501
525
 
502
- inline static ProbabilityList *get_probability_list( VALUE obj ) {
526
+ inline ProbabilityList *get_probability_list( VALUE obj ) {
503
527
  ProbabilityList *pl;
504
528
  Data_Get_Struct( obj, ProbabilityList, pl );
505
529
  return pl;
506
530
  }
507
531
 
508
- static void assert_value_wraps_pl( VALUE obj ) {
532
+ void assert_value_wraps_pl( VALUE obj ) {
509
533
  if ( TYPE(obj) != T_DATA ||
510
534
  RDATA(obj)->dfree != (RUBY_DATA_FUNC)destroy_probability_list) {
511
535
  rb_raise( rb_eTypeError, "Expected a Probabilities object, but got something else" );
@@ -515,8 +539,14 @@ static void assert_value_wraps_pl( VALUE obj ) {
515
539
  // Validate key/value from hash, and adjust object properties as required
516
540
  int validate_key_value( VALUE key, VALUE val, VALUE obj ) {
517
541
  int k = NUM2INT( key );
518
- double v = NUM2DBL( val );
519
542
  ProbabilityList *pl = get_probability_list( obj );
543
+ // Not assigned (to avoid "unused" warning), but this throws execption if val cannot be coerced to double
544
+ NUM2DBL( val );
545
+
546
+ if ( k > 0x7fffffff ) {
547
+ rb_raise( rb_eArgError, "Result too large" );
548
+ }
549
+
520
550
  if ( k < pl->offset ) {
521
551
  if ( pl->slots < 1 ) {
522
552
  pl->slots = 1;
@@ -544,16 +574,20 @@ int copy_key_value( VALUE key, VALUE val, VALUE obj ) {
544
574
  // Ruby class and instance methods for Probabilities
545
575
  //
546
576
 
547
- static VALUE probabilities_initialize( VALUE self, VALUE arr, VALUE offset ) {
548
- int o = NUM2INT(offset);
577
+ VALUE probabilities_initialize( VALUE self, VALUE arr, VALUE offset ) {
578
+ int i, o, s;
579
+ double error, p_item;
580
+ ProbabilityList *pl;
581
+ double *pr;
582
+
583
+ o = NUM2INT(offset);
549
584
  Check_Type( arr, T_ARRAY );
550
- int s = FIX2INT( rb_funcall( arr, rb_intern("count"), 0 ) );
551
- ProbabilityList *pl = get_probability_list( self );
585
+ s = FIX2INT( rb_funcall( arr, rb_intern("count"), 0 ) );
586
+ pl = get_probability_list( self );
552
587
  pl->offset = o;
553
- int i;
554
- double *pr = alloc_probs( pl, s );
588
+ pr = alloc_probs( pl, s );
555
589
  for(i=0; i<s; i++) {
556
- double p_item = NUM2DBL( rb_ary_entry( arr, i ) );
590
+ p_item = NUM2DBL( rb_ary_entry( arr, i ) );
557
591
  if ( p_item < 0.0 ) {
558
592
  rb_raise( rb_eArgError, "Negative probability not allowed" );
559
593
  } else if ( p_item > 1.0 ) {
@@ -561,7 +595,7 @@ static VALUE probabilities_initialize( VALUE self, VALUE arr, VALUE offset ) {
561
595
  }
562
596
  pr[i] = p_item;
563
597
  }
564
- double error = calc_cumulative( pl ) - 1.0;
598
+ error = calc_cumulative( pl ) - 1.0;
565
599
  if ( error < -1.0e-8 ) {
566
600
  rb_raise( rb_eArgError, "Total probabilities are less than 1.0" );
567
601
  } else if ( error > 1.0e-8 ) {
@@ -571,12 +605,16 @@ static VALUE probabilities_initialize( VALUE self, VALUE arr, VALUE offset ) {
571
605
  }
572
606
 
573
607
 
574
- static VALUE probabilities_initialize_copy( VALUE copy, VALUE orig ) {
608
+ VALUE probabilities_initialize_copy( VALUE copy, VALUE orig ) {
609
+ ProbabilityList *pl_copy;
610
+ ProbabilityList *pl_orig;
611
+ double *pr;
612
+
575
613
  if (copy == orig) return copy;
576
- ProbabilityList *pl_copy = get_probability_list( copy );
577
- ProbabilityList *pl_orig = get_probability_list( orig );
614
+ pl_copy = get_probability_list( copy );
615
+ pl_orig = get_probability_list( orig );
578
616
 
579
- double *pr = alloc_probs( pl_copy, pl_orig->slots );
617
+ pr = alloc_probs( pl_copy, pl_orig->slots );
580
618
  pl_copy->offset = pl_orig->offset;
581
619
  memcpy( pr, pl_orig->probs, pl_orig->slots * sizeof(double) );
582
620
  memcpy( pl_copy->cumulative, pl_orig->cumulative, pl_orig->slots * sizeof(double) );;
@@ -649,10 +687,14 @@ VALUE probabilities_repeat_sum( VALUE self, VALUE nsum ) {
649
687
  return pl_as_ruby_class( pl_repeat_sum( pl, n ), Probabilities );
650
688
  }
651
689
 
652
- static VALUE probabilities_repeat_n_sum_k( int argc, VALUE* argv, VALUE self ) {
690
+ VALUE probabilities_repeat_n_sum_k( int argc, VALUE* argv, VALUE self ) {
653
691
  VALUE nsum, nkeepers, kmode;
692
+ int keep_best, n, k;
693
+ ProbabilityList *pl;
694
+
654
695
  rb_scan_args( argc, argv, "21", &nsum, &nkeepers, &kmode );
655
- int keep_best = 1;
696
+
697
+ keep_best = 1;
656
698
  if (NIL_P(kmode)) {
657
699
  keep_best = 1;
658
700
  } else if ( rb_intern("keep_worst") == SYM2ID(kmode) ) {
@@ -661,9 +703,9 @@ static VALUE probabilities_repeat_n_sum_k( int argc, VALUE* argv, VALUE self ) {
661
703
  rb_raise( rb_eArgError, "Keep mode not recognised" );
662
704
  }
663
705
 
664
- int n = NUM2INT(nsum);
665
- int k = NUM2INT(nkeepers);
666
- ProbabilityList *pl = get_probability_list( self );
706
+ n = NUM2INT(nsum);
707
+ k = NUM2INT(nkeepers);
708
+ pl = get_probability_list( self );
667
709
  return pl_as_ruby_class( pl_repeat_n_sum_k( pl, n, k, keep_best ), Probabilities );
668
710
  }
669
711
 
@@ -685,14 +727,17 @@ VALUE probabilities_each( VALUE self ) {
685
727
 
686
728
  VALUE probabilities_for_fair_die( VALUE self, VALUE sides ) {
687
729
  int s = NUM2INT( sides );
730
+ VALUE obj;
731
+ ProbabilityList *pl;
732
+
688
733
  if ( s < 1 ) {
689
734
  rb_raise( rb_eArgError, "Number of sides should be 1 or more" );
690
735
  }
691
736
  if ( s > 100000 ) {
692
737
  rb_raise( rb_eArgError, "Number of sides should be less than 100001" );
693
738
  }
694
- VALUE obj = pl_alloc( Probabilities );
695
- ProbabilityList *pl = get_probability_list( obj );
739
+ obj = pl_alloc( Probabilities );
740
+ pl = get_probability_list( obj );
696
741
  pl->offset = 1;
697
742
  alloc_probs_iv( pl, s, 1.0/s );
698
743
  return obj;
@@ -701,17 +746,19 @@ VALUE probabilities_for_fair_die( VALUE self, VALUE sides ) {
701
746
  VALUE probabilities_from_h( VALUE self, VALUE hash ) {
702
747
  VALUE obj = pl_alloc( Probabilities );
703
748
  ProbabilityList *pl = get_probability_list( obj );
749
+ double error;
750
+
704
751
  // Set these up so that they get adjusted during hash iteration
705
- pl->offset = 2000000000;
752
+ pl->offset = 0x7fffffff;
706
753
  pl->slots = 0;
707
754
  // First iteration establish min/max and validate all key/values
708
755
  rb_hash_foreach( hash, validate_key_value, obj );
709
756
 
710
- double *pr = alloc_probs_iv( pl, pl->slots, 0.0 );
757
+ alloc_probs_iv( pl, pl->slots, 0.0 );
711
758
  // Second iteration copy key/value pairs into structure
712
759
  rb_hash_foreach( hash, copy_key_value, obj );
713
760
 
714
- double error = calc_cumulative( pl ) - 1.0;
761
+ error = calc_cumulative( pl ) - 1.0;
715
762
  if ( error < -1.0e-8 ) {
716
763
  rb_raise( rb_eArgError, "Total probabilities are less than 1.0" );
717
764
  } else if ( error > 1.0e-8 ) {
@@ -721,23 +768,33 @@ VALUE probabilities_from_h( VALUE self, VALUE hash ) {
721
768
  }
722
769
 
723
770
  VALUE probabilities_add_distributions( VALUE self, VALUE gdpa, VALUE gdpb ) {
724
- assert_value_wraps_pl( gdpa );
725
- assert_value_wraps_pl( gdpb );
726
771
  ProbabilityList *pl_a = get_probability_list( gdpa );
727
772
  ProbabilityList *pl_b = get_probability_list( gdpb );
773
+ assert_value_wraps_pl( gdpa );
774
+ assert_value_wraps_pl( gdpb );
775
+ pl_a = get_probability_list( gdpa );
776
+ pl_b = get_probability_list( gdpb );
728
777
  return pl_as_ruby_class( pl_add_distributions( pl_a, pl_b ), Probabilities );
729
778
  }
730
779
 
731
780
  VALUE probabilities_add_distributions_mult( VALUE self, VALUE m_a, VALUE gdpa, VALUE m_b, VALUE gdpb ) {
781
+ int mul_a, mul_b;
782
+ ProbabilityList *pl_a;
783
+ ProbabilityList *pl_b;
784
+
732
785
  assert_value_wraps_pl( gdpa );
733
786
  assert_value_wraps_pl( gdpb );
734
- int mul_a = NUM2INT( m_a );
735
- ProbabilityList *pl_a = get_probability_list( gdpa );
736
- int mul_b = NUM2INT( m_b );
737
- ProbabilityList *pl_b = get_probability_list( gdpb );
787
+ mul_a = NUM2INT( m_a );
788
+ pl_a = get_probability_list( gdpa );
789
+ mul_b = NUM2INT( m_b );
790
+ pl_b = get_probability_list( gdpb );
738
791
  return pl_as_ruby_class( pl_add_distributions_mult( mul_a, pl_a, mul_b, pl_b ), Probabilities );
739
792
  }
740
793
 
794
+ VALUE probabilities_implemented_in( VALUE self ) {
795
+ return ID2SYM( rb_intern("c") );
796
+ }
797
+
741
798
  ///////////////////////////////////////////////////////////////////////////////////////////////////
742
799
  //
743
800
  // Setup Probabilities class for Ruby interpretter
@@ -765,6 +822,7 @@ void init_probabilities_class( VALUE ParentModule ) {
765
822
  rb_define_singleton_method( Probabilities, "for_fair_die", probabilities_for_fair_die, 1 );
766
823
  rb_define_singleton_method( Probabilities, "add_distributions", probabilities_add_distributions, 2 );
767
824
  rb_define_singleton_method( Probabilities, "add_distributions_mult", probabilities_add_distributions_mult, 4 );
825
+ rb_define_singleton_method( Probabilities, "implemented_in", probabilities_implemented_in, 0 );
768
826
  rb_define_singleton_method( Probabilities, "from_h", probabilities_from_h, 1 );
769
827
  return;
770
828
  }
@@ -16,32 +16,32 @@ typedef struct _pd {
16
16
  double *cumulative;
17
17
  } ProbabilityList;
18
18
 
19
- static inline int pl_min( ProbabilityList *pl );
19
+ inline int pl_min( ProbabilityList *pl );
20
20
 
21
- static inline int pl_max( ProbabilityList *pl );
21
+ inline int pl_max( ProbabilityList *pl );
22
22
 
23
- static ProbabilityList *pl_add_distributions( ProbabilityList *pl_a, ProbabilityList *pl_b );
23
+ ProbabilityList *pl_add_distributions( ProbabilityList *pl_a, ProbabilityList *pl_b );
24
24
 
25
- static ProbabilityList *pl_add_distributions_mult( int mul_a, ProbabilityList *pl_a, int mul_b, ProbabilityList *pl_b );
25
+ ProbabilityList *pl_add_distributions_mult( int mul_a, ProbabilityList *pl_a, int mul_b, ProbabilityList *pl_b );
26
26
 
27
- static inline double pl_p_eql( ProbabilityList *pl, int target );
27
+ inline double pl_p_eql( ProbabilityList *pl, int target );
28
28
 
29
- static inline double pl_p_gt( ProbabilityList *pl, int target );
29
+ inline double pl_p_gt( ProbabilityList *pl, int target );
30
30
 
31
- static inline double pl_p_lt( ProbabilityList *pl, int target );
31
+ inline double pl_p_lt( ProbabilityList *pl, int target );
32
32
 
33
- static inline double pl_p_le( ProbabilityList *pl, int target );
33
+ inline double pl_p_le( ProbabilityList *pl, int target );
34
34
 
35
- static inline double pl_p_ge( ProbabilityList *pl, int target );
35
+ inline double pl_p_ge( ProbabilityList *pl, int target );
36
36
 
37
- static inline double pl_expected( ProbabilityList *pl );
37
+ inline double pl_expected( ProbabilityList *pl );
38
38
 
39
- static ProbabilityList *pl_given_ge( ProbabilityList *pl, int target );
39
+ ProbabilityList *pl_given_ge( ProbabilityList *pl, int target );
40
40
 
41
- static ProbabilityList *pl_given_le( ProbabilityList *pl, int target );
41
+ ProbabilityList *pl_given_le( ProbabilityList *pl, int target );
42
42
 
43
- static ProbabilityList *pl_repeat_sum( ProbabilityList *pl, int n );
43
+ ProbabilityList *pl_repeat_sum( ProbabilityList *pl, int n );
44
44
 
45
- static ProbabilityList *pl_repeat_n_sum_k( ProbabilityList *pl, int n, int k, int kbest );
45
+ ProbabilityList *pl_repeat_n_sum_k( ProbabilityList *pl, int n, int k, int kbest );
46
46
 
47
47
  #endif
data/games_dice.gemspec CHANGED
@@ -12,25 +12,25 @@ Gem::Specification.new do |gem|
12
12
  gem.summary = %q{Simulates and explains dice rolls from simple "1d6" to complex "roll 7 ten-sided dice, take best 3,
13
13
  results of 10 roll again and add on".}
14
14
  gem.homepage = "https://github.com/neilslater/games_dice"
15
+ gem.license = "MIT"
15
16
 
16
17
  gem.add_development_dependency "rspec", ">= 2.13.0"
17
18
  gem.add_development_dependency "rake", ">= 1.9.1"
18
19
  gem.add_development_dependency "yard", ">= 0.8.6"
20
+ gem.add_development_dependency "rake-compiler"
19
21
 
22
+ # Red Carpet has a C extension, and v3.0.0 is does not compile for 1.8.7
20
23
  if RUBY_VERSION < "1.9.0"
21
- # Red Carpet v3.0.0 does not compile for 1.8.7
22
24
  gem.add_development_dependency "redcarpet", ">=2.3.0", "<3.0.0"
23
25
  else
24
26
  gem.add_development_dependency "redcarpet", ">=2.3.0"
25
27
  end
26
28
 
27
- gem.add_development_dependency "rake-compiler"
28
-
29
29
  gem.add_dependency "parslet", ">= 1.5.0"
30
30
 
31
31
  gem.files = `git ls-files`.split($/)
32
32
  gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
33
- gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
34
33
  gem.extensions = gem.files.grep(%r{/extconf\.rb$})
34
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
35
35
  gem.require_paths = ["lib"]
36
36
  end
data/lib/games_dice.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  require "games_dice/version"
2
2
  begin
3
3
  require "games_dice/games_dice"
4
- rescue
4
+ rescue LoadError
5
5
  require "games_dice/probabilities"
6
6
  end
7
7
  require "games_dice/constants"
@@ -26,7 +26,7 @@ class GamesDice::Probabilities
26
26
  # @return [GamesDice::Probabilities]
27
27
  def initialize( probs = [1.0], offset = 0 )
28
28
  # This should *probably* be validated in future, but that would impact performance
29
- @probs = probs
29
+ @probs = check_probs_array probs.clone
30
30
  @offset = offset
31
31
  end
32
32
 
@@ -218,6 +218,13 @@ class GamesDice::Probabilities
218
218
  GamesDice::Probabilities.new( new_probs, combined_min )
219
219
  end
220
220
 
221
+ # Returns a symbol for the language name that this class is implemented in. The C version of the
222
+ # code is noticeably faster when dealing with larger numbers of possible results.
223
+ # @return [Symbol] Either :c or :ruby
224
+ def self.implemented_in
225
+ :ruby
226
+ end
227
+
221
228
  # Adds a distribution to itself repeatedly, to simulate a number of dice
222
229
  # results being summed.
223
230
  # @param [Integer] n Number of repetitions, must be at least 1
@@ -286,6 +293,20 @@ class GamesDice::Probabilities
286
293
 
287
294
  private
288
295
 
296
+ def check_probs_array probs_array
297
+ probs_array.map!{ |n| Float(n) }
298
+ total = probs_array.inject(0.0) do |t,x|
299
+ if x < 0.0 || x > 1.0
300
+ raise ArgumentError, "Found probability value #{x} which is not in range 0.0..1.0"
301
+ end
302
+ t+x
303
+ end
304
+ if (total-1.0).abs > 1e-6
305
+ raise ArgumentError, "Total probabilities too far from 1.0 for a valid distribution"
306
+ end
307
+ probs_array
308
+ end
309
+
289
310
  def calc_keep_distributions k, q, kmode
290
311
  if kmode == :keep_best
291
312
  keep_distributions = [ GamesDice::Probabilities.new( [1.0], q * k ) ]
@@ -1,3 +1,3 @@
1
1
  module GamesDice
2
- VERSION = "0.3.3"
2
+ VERSION = "0.3.5"
3
3
  end
@@ -104,6 +104,13 @@ describe GamesDice::Probabilities do
104
104
  end
105
105
  end
106
106
 
107
+ describe "#implemented_in" do
108
+ it "should be either :c or :ruby" do
109
+ lang = GamesDice::Probabilities.implemented_in
110
+ lang.should be_a Symbol
111
+ [:c, :ruby].member?( lang ).should be_true
112
+ end
113
+ end
107
114
  end # describe "class methods"
108
115
 
109
116
  describe "instance methods" do
metadata CHANGED
@@ -1,134 +1,121 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: games_dice
3
- version: !ruby/object:Gem::Version
4
- hash: 21
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.5
5
5
  prerelease:
6
- segments:
7
- - 0
8
- - 3
9
- - 3
10
- version: 0.3.3
11
6
  platform: ruby
12
- authors:
7
+ authors:
13
8
  - Neil Slater
14
9
  autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
-
18
- date: 2013-07-11 00:00:00 Z
19
- dependencies:
20
- - !ruby/object:Gem::Dependency
12
+ date: 2013-07-14 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
21
15
  name: rspec
22
- prerelease: false
23
- requirement: &id001 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
24
17
  none: false
25
- requirements:
26
- - - ">="
27
- - !ruby/object:Gem::Version
28
- hash: 59
29
- segments:
30
- - 2
31
- - 13
32
- - 0
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
33
21
  version: 2.13.0
34
22
  type: :development
35
- version_requirements: *id001
36
- - !ruby/object:Gem::Dependency
37
- name: rake
38
23
  prerelease: false
39
- requirement: &id002 !ruby/object:Gem::Requirement
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 2.13.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
40
33
  none: false
41
- requirements:
42
- - - ">="
43
- - !ruby/object:Gem::Version
44
- hash: 49
45
- segments:
46
- - 1
47
- - 9
48
- - 1
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
49
37
  version: 1.9.1
50
38
  type: :development
51
- version_requirements: *id002
52
- - !ruby/object:Gem::Dependency
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: 1.9.1
46
+ - !ruby/object:Gem::Dependency
53
47
  name: yard
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: 0.8.6
54
+ type: :development
54
55
  prerelease: false
55
- requirement: &id003 !ruby/object:Gem::Requirement
56
+ version_requirements: !ruby/object:Gem::Requirement
56
57
  none: false
57
- requirements:
58
- - - ">="
59
- - !ruby/object:Gem::Version
60
- hash: 51
61
- segments:
62
- - 0
63
- - 8
64
- - 6
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
65
61
  version: 0.8.6
62
+ - !ruby/object:Gem::Dependency
63
+ name: rake-compiler
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
66
70
  type: :development
67
- version_requirements: *id003
68
- - !ruby/object:Gem::Dependency
69
- name: redcarpet
70
71
  prerelease: false
71
- requirement: &id004 !ruby/object:Gem::Requirement
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: redcarpet
80
+ requirement: !ruby/object:Gem::Requirement
72
81
  none: false
73
- requirements:
74
- - - ">="
75
- - !ruby/object:Gem::Version
76
- hash: 3
77
- segments:
78
- - 2
79
- - 3
80
- - 0
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
81
85
  version: 2.3.0
82
- - - <
83
- - !ruby/object:Gem::Version
84
- hash: 7
85
- segments:
86
- - 3
87
- - 0
88
- - 0
89
- version: 3.0.0
90
86
  type: :development
91
- version_requirements: *id004
92
- - !ruby/object:Gem::Dependency
93
- name: rake-compiler
94
87
  prerelease: false
95
- requirement: &id005 !ruby/object:Gem::Requirement
88
+ version_requirements: !ruby/object:Gem::Requirement
96
89
  none: false
97
- requirements:
98
- - - ">="
99
- - !ruby/object:Gem::Version
100
- hash: 3
101
- segments:
102
- - 0
103
- version: "0"
104
- type: :development
105
- version_requirements: *id005
106
- - !ruby/object:Gem::Dependency
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: 2.3.0
94
+ - !ruby/object:Gem::Dependency
107
95
  name: parslet
108
- prerelease: false
109
- requirement: &id006 !ruby/object:Gem::Requirement
96
+ requirement: !ruby/object:Gem::Requirement
110
97
  none: false
111
- requirements:
112
- - - ">="
113
- - !ruby/object:Gem::Version
114
- hash: 3
115
- segments:
116
- - 1
117
- - 5
118
- - 0
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
119
101
  version: 1.5.0
120
102
  type: :runtime
121
- version_requirements: *id006
122
- description: A library for simulating dice. Use it to construct dice-rolling systems used in role-playing and board games.
123
- email:
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: 1.5.0
110
+ description: A library for simulating dice. Use it to construct dice-rolling systems
111
+ used in role-playing and board games.
112
+ email:
124
113
  - slobo777@gmail.com
125
114
  executables: []
126
-
127
- extensions:
115
+ extensions:
128
116
  - ext/games_dice/extconf.rb
129
117
  extra_rdoc_files: []
130
-
131
- files:
118
+ files:
132
119
  - .gitignore
133
120
  - .travis.yml
134
121
  - Gemfile
@@ -164,39 +151,38 @@ files:
164
151
  - spec/readme_spec.rb
165
152
  - spec/reroll_rule_spec.rb
166
153
  homepage: https://github.com/neilslater/games_dice
167
- licenses: []
168
-
154
+ licenses:
155
+ - MIT
169
156
  post_install_message:
170
157
  rdoc_options: []
171
-
172
- require_paths:
158
+ require_paths:
173
159
  - lib
174
- required_ruby_version: !ruby/object:Gem::Requirement
160
+ required_ruby_version: !ruby/object:Gem::Requirement
175
161
  none: false
176
- requirements:
177
- - - ">="
178
- - !ruby/object:Gem::Version
179
- hash: 3
180
- segments:
162
+ requirements:
163
+ - - ! '>='
164
+ - !ruby/object:Gem::Version
165
+ version: '0'
166
+ segments:
181
167
  - 0
182
- version: "0"
183
- required_rubygems_version: !ruby/object:Gem::Requirement
168
+ hash: -335297493340390599
169
+ required_rubygems_version: !ruby/object:Gem::Requirement
184
170
  none: false
185
- requirements:
186
- - - ">="
187
- - !ruby/object:Gem::Version
188
- hash: 3
189
- segments:
171
+ requirements:
172
+ - - ! '>='
173
+ - !ruby/object:Gem::Version
174
+ version: '0'
175
+ segments:
190
176
  - 0
191
- version: "0"
177
+ hash: -335297493340390599
192
178
  requirements: []
193
-
194
179
  rubyforge_project:
195
180
  rubygems_version: 1.8.24
196
181
  signing_key:
197
182
  specification_version: 3
198
- summary: Simulates and explains dice rolls from simple "1d6" to complex "roll 7 ten-sided dice, take best 3, results of 10 roll again and add on".
199
- test_files:
183
+ summary: Simulates and explains dice rolls from simple "1d6" to complex "roll 7 ten-sided
184
+ dice, take best 3, results of 10 roll again and add on".
185
+ test_files:
200
186
  - spec/bunch_spec.rb
201
187
  - spec/complex_die_spec.rb
202
188
  - spec/dice_spec.rb