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



613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
# File 'ext/games_dice/probabilities.c', line 613

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:



918
919
920
921
922
923
924
925
926
927
# File 'ext/games_dice/probabilities.c', line 918

VALUE probabilities_add_distributions( VALUE self, VALUE gdpa, VALUE gdpb ) {
  ProbabilityList *pl_a;
  ProbabilityList *pl_b;

  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:



939
940
941
942
943
944
945
946
947
948
949
950
951
# File 'ext/games_dice/probabilities.c', line 939

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:



855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
# File 'ext/games_dice/probabilities.c', line 855

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:



880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
# File 'ext/games_dice/probabilities.c', line 880

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:



834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
# File 'ext/games_dice/probabilities.c', line 834

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)


757
758
759
# File 'ext/games_dice/probabilities.c', line 757

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:



767
768
769
770
771
# File 'ext/games_dice/probabilities.c', line 767

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:



779
780
781
782
783
# File 'ext/games_dice/probabilities.c', line 779

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.



648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
# File 'ext/games_dice/probabilities.c', line 648

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)


702
703
704
# File 'ext/games_dice/probabilities.c', line 702

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

#minInteger

Minimum result in the distribution

Returns:

  • (Integer)


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

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)



711
712
713
# File 'ext/games_dice/probabilities.c', line 711

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)



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

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)



720
721
722
# File 'ext/games_dice/probabilities.c', line 720

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)



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

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)



747
748
749
# File 'ext/games_dice/probabilities.c', line 747

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:



806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
# File 'ext/games_dice/probabilities.c', line 806

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:



792
793
794
795
796
# File 'ext/games_dice/probabilities.c', line 792

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)


671
672
673
674
675
676
677
678
679
680
681
682
683
684
# File 'ext/games_dice/probabilities.c', line 671

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;
}