games_dice 0.3.3 → 0.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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