Class: GamesDice::Probabilities

Inherits:
Object
  • Object
show all
Defined in:
lib/games_dice/marshal.rb,
ext/games_dice/probabilities.c

Overview

This class models probability distributions for dice systems.

An object of this class represents a single distribution, which might be the result of a complex combination of dice.

Examples:

Distribution for a six-sided die

probs = GamesDice::Probabilities.for_fair_die( 6 )
probs.min # => 1
probs.max # => 6
probs.expected # => 3.5
probs.p_ge( 4 ) # => 0.5

Adding two distributions

pd6 = GamesDice::Probabilities.for_fair_die( 6 )
probs = GamesDice::Probabilities.add_distributions( pd6, pd6 )
probs.min # => 2
probs.max # => 12
probs.expected # => 7.0
probs.p_ge( 10 ) # => 0.16666666666666669

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(probs, offset) ⇒ GamesDice::Probabilities

Creates new instance of GamesDice::Probabilities.

Parameters:

  • probs (Array<Float>)

    Each entry in the array is the probability of getting a result

  • offset (Integer)

    The result associated with index of 0 in the array



584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
# File 'ext/games_dice/probabilities.c', line 584

VALUE probabilities_initialize( VALUE self, VALUE arr, VALUE offset ) {
  int i, o, s;
  double error, p_item;
  ProbabilityList *pl;
  double *pr;

  o = NUM2INT(offset);
  Check_Type( arr, T_ARRAY );
  s = FIX2INT( rb_funcall( arr, rb_intern("count"), 0 ) );
  pl = get_probability_list( self );
  pl->offset = o;
  pr = alloc_probs( pl, s );
  for(i=0; i<s; i++) {
    p_item = NUM2DBL( rb_ary_entry( arr, i ) );
    if ( p_item < 0.0 ) {
      rb_raise( rb_eArgError, "Negative probability not allowed" );
    } else if ( p_item > 1.0 ) {
      rb_raise( rb_eArgError, "Probability must be in range 0.0..1.0" );
    }
    pr[i] = p_item;
  }
  error = calc_cumulative( pl ) - 1.0;
  if ( error < -1.0e-8 ) {
    rb_raise( rb_eArgError, "Total probabilities are less than 1.0" );
  } else if ( error > 1.0e-8 ) {
    rb_raise( rb_eArgError, "Total probabilities are greater than 1.0" );
  }
  return self;
}

Class Method Details

.add_distributions(pd_a, pd_b) ⇒ GamesDice::Probabilities

Combines two distributions to create a third, that represents the distribution created when adding results together.

Parameters:

Returns:



889
890
891
892
893
894
895
896
897
# File 'ext/games_dice/probabilities.c', line 889

VALUE probabilities_add_distributions( VALUE self, VALUE gdpa, VALUE gdpb ) {
  ProbabilityList *pl_a = get_probability_list( gdpa );
  ProbabilityList *pl_b = get_probability_list( gdpb );
  assert_value_wraps_pl( gdpa );
  assert_value_wraps_pl( gdpb );
  pl_a = get_probability_list( gdpa );
  pl_b = get_probability_list( gdpb );
  return pl_as_ruby_class( pl_add_distributions( pl_a, pl_b ), Probabilities );
}

.add_distributions_mult(m_a, pd_a, m_b, pd_b) ⇒ GamesDice::Probabilities

Combines two distributions with multipliers to create a third, that represents the distribution created when adding weighted results together.

Parameters:

Returns:



909
910
911
912
913
914
915
916
917
918
919
920
921
# File 'ext/games_dice/probabilities.c', line 909

VALUE probabilities_add_distributions_mult( VALUE self, VALUE m_a, VALUE gdpa, VALUE m_b, VALUE gdpb ) {
  int mul_a, mul_b;
  ProbabilityList *pl_a;
  ProbabilityList *pl_b;

  assert_value_wraps_pl( gdpa );
  assert_value_wraps_pl( gdpb );
  mul_a = NUM2INT( m_a );
  pl_a = get_probability_list( gdpa );
  mul_b = NUM2INT( m_b );
  pl_b = get_probability_list( gdpb );
  return pl_as_ruby_class( pl_add_distributions_mult( mul_a, pl_a, mul_b, pl_b ), Probabilities );
}

.for_fair_die(sides) ⇒ GamesDice::Probabilities

Distribution for a die with equal chance of rolling 1..N

Parameters:

  • sides (Integer)

    Number of sides on die

Returns:



826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
# File 'ext/games_dice/probabilities.c', line 826

VALUE probabilities_for_fair_die( VALUE self, VALUE sides ) {
  int s = NUM2INT( sides );
  VALUE obj;
  ProbabilityList *pl;

  if ( s < 1 ) {
    rb_raise( rb_eArgError, "Number of sides should be 1 or more" );
  }
  if ( s > 100000 ) {
    rb_raise( rb_eArgError, "Number of sides should be less than 100001" );
  }
  obj = pl_alloc( Probabilities );
  pl = get_probability_list( obj );
  pl->offset = 1;
  alloc_probs_iv( pl, s, 1.0/s );
  return obj;
}

.from_h(prob_hash) ⇒ GamesDice::Probabilities

Creates new instance of GamesDice::Probabilities.

Parameters:

  • prob_hash (Hash)

    A hash representation of the distribution, each key is an integer result, and the matching value is probability of getting that result

Returns:



851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
# File 'ext/games_dice/probabilities.c', line 851

VALUE probabilities_from_h( VALUE self, VALUE hash ) {
  VALUE obj;
  ProbabilityList *pl;
  double error;

  Check_Type( hash, T_HASH );

  obj = pl_alloc( Probabilities );
  pl = get_probability_list( obj );


  // Set these up so that they get adjusted during hash iteration
  pl->offset = 0x7fffffff;
  pl->slots = 0;
  // First iteration establish min/max and validate all key/values
  rb_hash_foreach( hash, validate_key_value, obj );

  alloc_probs_iv( pl, pl->slots, 0.0 );
  // Second iteration copy key/value pairs into structure
  rb_hash_foreach( hash, copy_key_value, obj );

  error = calc_cumulative( pl ) - 1.0;
  if ( error < -1.0e-8 ) {
    rb_raise( rb_eArgError, "Total probabilities are less than 1.0" );
  } else if ( error > 1.0e-8 ) {
    rb_raise( rb_eArgError, "Total probabilities are greater than 1.0" );
  }
  return obj;
}

Instance Method Details

#each {|result, probability| ... } ⇒ GamesDice::Probabilities

Iterates through value, probability pairs

Yield Parameters:

  • result (Integer)

    A result that may be possible in the dice scheme

  • probability (Float)

    Probability of result, in range 0.0..1.0

Returns:



805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
# File 'ext/games_dice/probabilities.c', line 805

VALUE probabilities_each( VALUE self ) {
  ProbabilityList *pl = get_probability_list( self );
  int i;
  double *pr = pl->probs;
  int o = pl->offset;
  for ( i = 0; i < pl->slots; i++ ) {
    if ( pr[i] > 0.0 ) {
      VALUE a = rb_ary_new2( 2 );
      rb_ary_store( a, 0, INT2NUM( i + o ));
      rb_ary_store( a, 1, DBL2NUM( pr[i] ));
      rb_yield( a );
    }
  }
  return self;
}

#expectedFloat

Expected value of distribution.

Returns:

  • (Float)


728
729
730
# File 'ext/games_dice/probabilities.c', line 728

VALUE probabilites_expected( VALUE self ) {
  return DBL2NUM( pl_expected( get_probability_list( self ) ) );
}

#given_ge(target) ⇒ GamesDice::Probabilities

Probability distribution derived from this one, where we know (or are only interested in situations where) the result is greater than or equal to target.

Parameters:

  • target (Integer)

Returns:



738
739
740
741
742
# File 'ext/games_dice/probabilities.c', line 738

VALUE probabilities_given_ge( VALUE self, VALUE target ) {
  int t = NUM2INT(target);
  ProbabilityList *pl = get_probability_list( self );
  return pl_as_ruby_class( pl_given_ge( pl, t ), Probabilities );
}

#given_le(target) ⇒ GamesDice::Probabilities

Probability distribution derived from this one, where we know (or are only interested in situations where) the result is less than or equal to target.

Parameters:

  • target (Integer)

Returns:



750
751
752
753
754
# File 'ext/games_dice/probabilities.c', line 750

VALUE probabilities_given_le( VALUE self, VALUE target ) {
  int t = NUM2INT(target);
  ProbabilityList *pl = get_probability_list( self );
  return pl_as_ruby_class( pl_given_le( pl, t ), Probabilities );
}

#cloneGamesDice::Probabilities

Cloning an object of this class creates a deep copy of the probabilities hash.



619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
# File 'ext/games_dice/probabilities.c', line 619

VALUE probabilities_initialize_copy( VALUE copy, VALUE orig ) {
  ProbabilityList *pl_copy;
  ProbabilityList *pl_orig;
  double *pr;

  if (copy == orig) return copy;
  pl_copy = get_probability_list( copy );
  pl_orig = get_probability_list( orig );

  pr = alloc_probs( pl_copy, pl_orig->slots );
  pl_copy->offset = pl_orig->offset;
  memcpy( pr, pl_orig->probs, pl_orig->slots * sizeof(double) );
  memcpy( pl_copy->cumulative, pl_orig->cumulative, pl_orig->slots * sizeof(double) );;

  return copy;
}

#maxInteger

Maximum result in the distribution

Returns:

  • (Integer)


673
674
675
# File 'ext/games_dice/probabilities.c', line 673

VALUE probabilities_max( VALUE self ) {
  return INT2NUM( pl_max( get_probability_list( self ) ) );
}

#minInteger

Minimum result in the distribution

Returns:

  • (Integer)


663
664
665
# File 'ext/games_dice/probabilities.c', line 663

VALUE probabilities_min( VALUE self ) {
  return INT2NUM( pl_min(  get_probability_list( self ) ) );
}

#p_eql(target) ⇒ Float

Probability of result equalling specific target

Parameters:

  • target (Integer)

Returns:

  • (Float)

    in range (0.0..1.0)



682
683
684
# File 'ext/games_dice/probabilities.c', line 682

VALUE probabilites_p_eql( VALUE self, VALUE target ) {
  return DBL2NUM( pl_p_eql( get_probability_list( self ), NUM2INT(target) ) );
}

#p_ge(target) ⇒ Float

Probability of result being equal to or greater than specific target

Parameters:

  • target (Integer)

Returns:

  • (Float)

    in range (0.0..1.0)



700
701
702
# File 'ext/games_dice/probabilities.c', line 700

VALUE probabilites_p_ge( VALUE self, VALUE target ) {
  return DBL2NUM( pl_p_ge( get_probability_list( self ), NUM2INT(target) ) );
}

#p_gt(target) ⇒ Float

Probability of result being greater than specific target

Parameters:

  • target (Integer)

Returns:

  • (Float)

    in range (0.0..1.0)



691
692
693
# File 'ext/games_dice/probabilities.c', line 691

VALUE probabilites_p_gt( VALUE self, VALUE target ) {
  return DBL2NUM( pl_p_gt( get_probability_list( self ), NUM2INT(target) ) );
}

#p_le(target) ⇒ Float

Probability of result being equal to or less than specific target

Parameters:

  • target (Integer)

Returns:

  • (Float)

    in range (0.0..1.0)



709
710
711
# File 'ext/games_dice/probabilities.c', line 709

VALUE probabilites_p_le( VALUE self, VALUE target ) {
  return DBL2NUM( pl_p_le( get_probability_list( self ), NUM2INT(target) ) );
}

#p_lt(target) ⇒ Float

Probability of result being less than specific target

Parameters:

  • target (Integer)

Returns:

  • (Float)

    in range (0.0..1.0)



718
719
720
# File 'ext/games_dice/probabilities.c', line 718

VALUE probabilites_p_lt( VALUE self, VALUE target ) {
  return DBL2NUM( pl_p_lt( get_probability_list( self ), NUM2INT(target) ) );
}

#repeat_n_sum_k(n, k, kmode = :keep_best) ⇒ GamesDice::Probabilities

Calculates distribution generated by summing best k results of n iterations of the distribution.

Parameters:

  • n (Integer)

    Number of repetitions, must be at least 1

  • k (Integer)

    Number of best results to keep and sum

Returns:



777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
# File 'ext/games_dice/probabilities.c', line 777

VALUE probabilities_repeat_n_sum_k( int argc, VALUE* argv, VALUE self ) {
  VALUE nsum, nkeepers, kmode;
  int keep_best, n, k;
  ProbabilityList *pl;

  rb_scan_args( argc, argv, "21", &nsum, &nkeepers, &kmode );

  keep_best = 1;
  if (NIL_P(kmode)) {
    keep_best = 1;
  } else if ( rb_intern("keep_worst") == SYM2ID(kmode) ) {
    keep_best = 0;
  } else if ( rb_intern("keep_best") != SYM2ID(kmode) ) {
    rb_raise( rb_eArgError, "Keep mode not recognised" );
  }

  n = NUM2INT(nsum);
  k = NUM2INT(nkeepers);
  pl = get_probability_list( self );
  return pl_as_ruby_class( pl_repeat_n_sum_k( pl, n, k, keep_best ), Probabilities );
}

#repeat_sum(n) ⇒ GamesDice::Probabilities

Adds a distribution to itself repeatedly, to simulate a number of dice results being summed.

Parameters:

  • n (Integer)

    Number of repetitions, must be at least 1

Returns:



763
764
765
766
767
# File 'ext/games_dice/probabilities.c', line 763

VALUE probabilities_repeat_sum( VALUE self, VALUE nsum ) {
  int n = NUM2INT(nsum);
  ProbabilityList *pl = get_probability_list( self );
  return pl_as_ruby_class( pl_repeat_sum( pl, n ), Probabilities );
}

#to_hHash

A hash representation of the distribution. Each key is an integer result, and the matching value is probability of getting that result. A new hash is generated on each call to this method.

Returns:

  • (Hash)


642
643
644
645
646
647
648
649
650
651
652
653
654
655
# File 'ext/games_dice/probabilities.c', line 642

VALUE probabilities_to_h( VALUE self ) {
  ProbabilityList *pl = get_probability_list( self );
  VALUE h = rb_hash_new();
  double *pr = pl->probs;
  int s = pl->slots;
  int o = pl->offset;
  int i;
  for(i=0; i<s; i++) {
    if ( pr[i] > 0.0 ) {
      rb_hash_aset( h, INT2FIX( o + i ), DBL2NUM( pr[i] ) );
    }
  }
  return h;
}