dieroll 0.0.0 → 1.0.0

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/bin/dieroll CHANGED
@@ -7,17 +7,87 @@ $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
7
7
  require 'dieroll'
8
8
  require 'optparse'
9
9
 
10
- usage_string = "Usage: roll [-r] <roll-string>"
10
+ usage_string =
11
+ "Usage: roll [--ne --lt --le --eq --ge --gt]"+
12
+ " [--headers] [-v val] [-r] [-p] <roll-string>"
11
13
 
12
14
  options = {}
13
15
  OptionParser.new do |opts|
14
16
  opts.banner = usage_string
15
17
 
16
- options[:results] = false
17
- opts.on('-r','--results','Show full roll results' ) do
18
+ opts.separator ""
19
+
20
+ opts.on('-r','--results','Print full roll results' ) do
18
21
  options[:results] = true
19
22
  end
20
23
 
24
+ opts.separator ""
25
+
26
+ options[:comparisons] = []
27
+
28
+ opts.separator "Odds -"
29
+
30
+ opts.separator "Print chances of rolling:"
31
+
32
+ opts.on('--eq','--equal',
33
+ 'the given value') do
34
+ options[:comparisons] << :equal
35
+ end
36
+
37
+ opts.on('--ne','--not-equal',
38
+ 'any value EXCEPT the given value') do
39
+ options[:comparisons] << :not_equal
40
+ end
41
+
42
+ opts.on('--lt','--less-than',
43
+ 'less than the given value') do
44
+ options[:comparisons] << :less_than
45
+ end
46
+
47
+ opts.on('--le','--less-than-or-equal',
48
+ 'less than or equal the given value') do
49
+ options[:comparisons] << :less_than_or_equal
50
+ end
51
+
52
+ opts.on('--gt','--greater-than',
53
+ 'greater than the given value') do
54
+ options[:comparisons] << :greater_than
55
+ end
56
+
57
+ opts.on('--ge','--greater-than-or-equal',
58
+ 'greater than or equal the given value') do
59
+ options[:comparisons] << :greater_than_or_equal
60
+ end
61
+
62
+ opts.separator ""
63
+
64
+ opts.on('--headers',
65
+ 'Show headers on the Odds table') do
66
+ options[:headers] = true
67
+ end
68
+
69
+ opts.separator ""
70
+
71
+ opts.on('-v','--value VALUE',
72
+ 'Sets the value to be used for the Odds switches',
73
+ "Can be an int or a range 'X-Y'",
74
+ 'Will display all possible results if not set') do |value|
75
+ value =~ /(-?\d+)-?(-?\d*)/
76
+ unless($2.empty?)
77
+ options[:val] = ($1.to_i..$2.to_i)
78
+ else
79
+ options[:val] = value.to_i
80
+ end
81
+ end
82
+
83
+ opts.separator ""
84
+
85
+ opts.on('-p','--probability','Print statistical information for the roll') do
86
+ options[:probability] = true
87
+ end
88
+
89
+ opts.separator ""
90
+
21
91
  opts.on('-h','--help','Display this screen') do
22
92
  puts opts
23
93
  exit
@@ -31,9 +101,33 @@ else
31
101
  abort(usage_string)
32
102
  end
33
103
 
34
- dice.roll!
104
+ options.delete(:comparisons) if options[:comparisons].empty?
105
+ options.delete(:val) unless options[:comparisons]
106
+
35
107
  if options[:results]
108
+ dice.roll!
36
109
  puts dice.report
37
- else
38
- puts dice.total
110
+ puts ""
111
+ end
112
+
113
+ if options[:comparisons]
114
+ options[:val] ||= :all
115
+ table = dice.odds.table(options[:val],
116
+ options[:comparisons],
117
+ options[:headers])
118
+ table.each do |result_line|
119
+ puts result_line.join("\t")
120
+ end
121
+ puts ""
122
+ end
123
+
124
+ if options[:probability]
125
+ puts "Minumum Result:\t\t" + dice.odds.offset.to_s
126
+ puts "Maximum Result:\t\t" + dice.odds.max_result.to_s
127
+ puts "Average Result:\t\t" + dice.odds.mean.to_s
128
+ puts "Standard Deviation:\t" + dice.odds.standard_deviation.round(4).to_s
129
+ end
130
+
131
+ if options.empty?
132
+ puts dice.roll
39
133
  end
@@ -1,7 +1,8 @@
1
1
  module Dieroll
2
2
  class DiceSet
3
3
 
4
- def initialize(number_of_dice, sides, sign='+', drop_string)
4
+ #Create DiceSet object
5
+ def initialize(number_of_dice, sides, sign='+', drop_string=nil)
5
6
  @number_of_dice, @sides, @sign = number_of_dice, sides, sign
6
7
  @drop_string = drop_string
7
8
  @drops = @drop_string.scan(/[l|h]/) if !!@drop_string
@@ -11,32 +12,48 @@ module Dieroll
11
12
  @dice << Dieroll::Die.new(@sides)
12
13
  end
13
14
 
14
- @last_result = []
15
+ @last_results = []
15
16
  @last_total = nil
16
17
  end
17
18
 
18
- def roll!
19
- @last_result = []
19
+ #Rolls the DiceSet. Returns the result.
20
+ def roll(save=false)
21
+ results = []
20
22
  @dice.each do |die|
21
- @last_result << die.roll!
23
+ if save
24
+ results << die.roll!
25
+ else
26
+ results << die.roll
27
+ end
22
28
  end
23
29
 
24
- @last_result.sort!
25
- @last_non_dropped = @last_result.dup
30
+ results.sort!
31
+ last_non_dropped = results.dup
26
32
  if !!@drops
27
33
  @drops.each do |drop|
28
- @last_non_dropped.shift if drop == 'l'
29
- @last_non_dropped.pop if drop == 'h'
34
+ last_non_dropped.shift if drop == 'l'
35
+ last_non_dropped.pop if drop == 'h'
30
36
  end
31
37
  end
32
38
 
33
- @last_total = @last_non_dropped.inject(0){|sum, element| sum + element}
34
- @last_total *= -1 if @sign == '-'
39
+ total = last_non_dropped.inject(0){|sum, element| sum + element}
40
+ total *= -1 if @sign == '-'
41
+
42
+ @last_results = results if save
43
+ @last_non_dropped = last_non_dropped if save
44
+ @last_total = total if save
35
45
 
36
- @last_total
46
+ total
37
47
  end
38
48
 
39
- def to_s
49
+ #Rolls the DiceSet. Returns the result.
50
+ #Updates @last_total, @last_result, @last_non_dropped
51
+ def roll!
52
+ roll(true)
53
+ end
54
+
55
+ #Returns a string with details of the last roll.
56
+ def report
40
57
  output = "#{@sign}#{@number_of_dice}d#{@sides}"
41
58
  output += "/#{@drop_string}" if !!@drop_string
42
59
  output += ": "
@@ -47,5 +64,61 @@ module Dieroll
47
64
  output
48
65
  end
49
66
 
67
+ #Returns the Odds for the DiceSet. Creates Odds if it doesn't exist.
68
+ def odds
69
+ calculate_odds unless !!@odds
70
+
71
+ @odds
72
+ end
73
+
74
+ #Returns @last_total as a string.
75
+ def to_s
76
+ @last_total.to_s
77
+ end
78
+
79
+ private
80
+
81
+ #Creates a new Odds object for the DiceSet.
82
+ def calculate_odds
83
+ if !@drops
84
+ @odds = @dice[0].odds ** @number_of_dice
85
+ if(@sign == '-')
86
+ @odds.offset = @sides * @number_of_dice * -1
87
+ end
88
+ else
89
+ possibilities = []
90
+ num_possibilities = @sides ** @number_of_dice
91
+
92
+ current_side = 1
93
+
94
+ @number_of_dice.times do |dice|
95
+ possibilities.sort!
96
+ num_possibilities.times do |possibility|
97
+ possibilities[possibility] ||= []
98
+ possibilities[possibility] << current_side
99
+ current_side += 1
100
+ current_side = 1 if current_side > @sides
101
+ end
102
+ end
103
+
104
+ combinations_array = []
105
+ possibilities.each do |possibility|
106
+ possibility.sort!
107
+ @drops.each do |drop|
108
+ possibility.shift if drop == 'l'
109
+ possibility.pop if drop == 'h'
110
+ end
111
+ total = possibility.inject(0) {|sum, element| sum + element}
112
+ combinations_array[total] ||= 0
113
+ combinations_array[total] += 1
114
+ end
115
+ offset = @number_of_dice - @drops.size
116
+ offset.times do
117
+ combinations_array.shift
118
+ end
119
+ @odds = Dieroll::Odds.new(combinations_array, offset)
120
+ end
121
+ end
122
+
50
123
  end
51
124
  end
data/lib/dieroll/die.rb CHANGED
@@ -1,19 +1,48 @@
1
1
  module Dieroll
2
2
  class Die
3
3
 
4
+ #Create Die object
4
5
  def initialize(sides)
5
6
  @sides = sides
6
7
  @last_result = nil
7
8
  end
8
9
 
10
+ #Roll the Die. Returns the result.
11
+ def roll(save=false)
12
+ result = rand(1..@sides)
13
+ @last_result = result if save
14
+
15
+ result
16
+ end
17
+
18
+ #Roll the Die. Returns the result. Saves last_result.
9
19
  def roll!
10
- @last_result = rand(1..@sides)
20
+ roll(true)
21
+ end
22
+
23
+ #Returns the Die's Odds object. Creates Odds if it doesn't exist.
24
+ def odds
25
+ calculate_odds unless !!@odds
11
26
 
12
- @last_result
27
+ @odds
13
28
  end
14
29
 
30
+ #Return last_result as a string
15
31
  def to_s
16
- "#{@last_result}"
32
+ @last_result.to_s
17
33
  end
34
+
35
+ private
36
+
37
+ #Create a new Odds object for the Die.
38
+ def calculate_odds
39
+ combinations_array = []
40
+
41
+ @sides.times do
42
+ combinations_array << 1
43
+ end
44
+ @odds = Dieroll::Odds.new(combinations_array)
45
+ end
46
+
18
47
  end
19
48
  end
@@ -0,0 +1,164 @@
1
+ module Dieroll class Odds
2
+ attr_reader :combinations_array, :max_result, :variance,
3
+ :standard_deviation, :mean, :offset
4
+
5
+ def initialize(combinations_array, offset=1)
6
+ @combinations_array = combinations_array
7
+ @offset = offset
8
+ sum_combinations
9
+ calculate_odds
10
+ calculate_statistics
11
+ end
12
+
13
+ def *(other)
14
+ result_array = []
15
+ @combinations_array.each_with_index do |combination_one, index_one|
16
+ other.combinations_array.each_with_index do |combination_two, index_two|
17
+ result_array[index_one + index_two] ||= 0
18
+ result_array[index_one + index_two] +=
19
+ combination_one * combination_two
20
+ end
21
+ end
22
+
23
+ offset = @offset + other.offset
24
+
25
+ Odds.new(result_array, offset)
26
+ end
27
+
28
+ def **(number)
29
+ original = Odds.new(@combinations_array)
30
+ result = Odds.new(@combinations_array)
31
+ number -= 1
32
+ number.times do
33
+ result *= original
34
+ end
35
+
36
+ result
37
+ end
38
+
39
+ def +(mod)
40
+ mod += @offset
41
+
42
+ Odds.new(@combinations_array, mod)
43
+ end
44
+
45
+ def -(mod)
46
+ mod = @offset - mod
47
+
48
+ Odds.new(@combinations_array, mod)
49
+ end
50
+
51
+ def offset=(offset)
52
+ @offset = offset
53
+ calculate_statistics
54
+ end
55
+
56
+
57
+ def equal(result)
58
+ if(result-@offset >= 0 && !!@odds_array[result-@offset])
59
+ @odds_array[result-@offset]
60
+ else
61
+ 0
62
+ end
63
+ end
64
+
65
+ def not_equal(result)
66
+ 1 - equal(result)
67
+ end
68
+
69
+ def greater_than(result)
70
+ total_chance = 0
71
+ @odds_array.each_with_index do |chance, index|
72
+ if(index+@offset > result)
73
+ total_chance += chance
74
+ end
75
+ end
76
+
77
+ total_chance
78
+ end
79
+
80
+ def greater_than_or_equal(result)
81
+ self.greater_than(result) + self.equal(result)
82
+ end
83
+
84
+ def less_than(result)
85
+ 1 - greater_than_or_equal(result)
86
+ end
87
+
88
+ def less_than_or_equal(result)
89
+ 1 - greater_than(result)
90
+ end
91
+
92
+ def to_s
93
+ "#{@odds_array}"
94
+ end
95
+
96
+ def table(value, comparison_array, header=false)
97
+ valid_comparisons = [:not_equal, :equal,
98
+ :less_than, :less_than_or_equal,
99
+ :greater_than_or_equal, :greater_than]
100
+ if value == :all
101
+ range = (@offset..@max_result)
102
+ else
103
+ if value.kind_of?(Range)
104
+ range = value
105
+ else
106
+ range = [value]
107
+ end
108
+ end
109
+
110
+ result_lines = []
111
+ if header
112
+ result_lines << comparison_array
113
+ result_lines[0].unshift "result"
114
+ end
115
+
116
+ range.each do |result|
117
+ result_line = [result]
118
+ comparison_array.each do |comparison|
119
+ if valid_comparisons.include?(comparison)
120
+ result_line << self.send(comparison, result).round(4)
121
+ end
122
+ end
123
+ result_lines << result_line
124
+ end
125
+
126
+ result_lines
127
+ end
128
+
129
+ private
130
+
131
+ def sum_combinations
132
+ @combinations_total = @combinations_array.inject(0) do |sum, element|
133
+ sum + element
134
+ end
135
+ end
136
+
137
+ def calculate_odds
138
+ @odds_array = @combinations_array.map do |combination|
139
+ (combination.to_f / @combinations_total.to_f)
140
+ end
141
+ end
142
+
143
+ def calculate_statistics
144
+ @max_result = @combinations_array.size + @offset - 1
145
+
146
+ results_sum = 0
147
+ total_results = 0
148
+ (@offset..@max_result).each do |result|
149
+ results_sum += @combinations_array[result-@offset] * result
150
+ total_results += @combinations_array[result-@offset]
151
+ end
152
+ @mean = results_sum.to_f / total_results
153
+
154
+ variance_sum = 0
155
+ (@offset..@max_result).each do |result|
156
+ variance_sum += (result - @mean)**2 *
157
+ @combinations_array[result - @offset]
158
+ end
159
+ @variance = (variance_sum.to_f / @combinations_total)
160
+
161
+ @standard_deviation = (Math.sqrt @variance)
162
+ end
163
+ end
164
+ end
@@ -8,7 +8,7 @@ module::Dieroll
8
8
  rand(1..sides)
9
9
  end
10
10
 
11
- # Roll arbitrary 'XdY+Z' string
11
+ # Roll arbitrary dice notation string
12
12
  def self.from_string(string)
13
13
  rolls = [0]
14
14
  sets = s_to_set(string)
@@ -24,7 +24,7 @@ module::Dieroll
24
24
 
25
25
  rolls
26
26
  end
27
-
27
+
28
28
  # Create roller object
29
29
  def initialize(string)
30
30
  @string = string
@@ -44,45 +44,81 @@ module::Dieroll
44
44
  end
45
45
  end
46
46
 
47
- # Determine results of roll
48
- def roll!
49
- @total = 0
47
+ # Updates the object to use a new dice notation string
48
+ def string=(string)
49
+ @string = string
50
+ @odds = nil
51
+ initialize(@string)
52
+ end
53
+
54
+ # Rolls the Roller. Returns the total.
55
+ def roll(save=false)
56
+ total = 0
50
57
  @dice_sets.each do |set|
51
- @total += set.roll!
58
+ if save
59
+ total += set.roll!
60
+ else
61
+ total += set.roll
62
+ end
52
63
  end
53
64
  @mods.each do |mod|
54
- @total += mod
65
+ total += mod
55
66
  end
56
67
 
57
- @total
68
+ @total = total if save
69
+
70
+ total
71
+ end
72
+
73
+ # Rolls the Roller. Returns the total. Sets @total.
74
+ def roll!
75
+ roll(true)
58
76
  end
59
77
 
60
78
  # Return roll result as string
61
79
  def report
62
- output = "#{@total}\n"
63
- output += "#{@string}:\n"
80
+ output = @total.to_s + "\n"
81
+ output += @string.to_s + ":\n"
64
82
  @dice_sets.each do |set|
65
- output += "#{set.to_s}\n"
83
+ output += set.report + "\n"
66
84
  end
67
85
  @mods.each do |mod|
68
86
  output += "+" if mod >= 0
69
- output += "#{mod}\n"
87
+ output += mod.to_s + "\n"
70
88
  end
71
89
 
72
90
  output
73
91
  end
74
-
75
- def to_s
76
- "#{@total}"
92
+
93
+ # Returns @odds. Creates an Odds object if !@odds.
94
+ def odds
95
+ calculate_odds unless !!@odds
96
+
97
+ @odds
77
98
  end
78
99
 
79
- def string=(string)
80
- @string = string
81
- initialize(@string)
100
+ # Returns @total as a string
101
+ def to_s
102
+ @total.to_s
82
103
  end
83
104
 
84
105
  private
85
106
 
107
+ # Creates the the Odds object for the roller.
108
+ def calculate_odds
109
+ @dice_sets.each_with_index do |set, index|
110
+ if(index == 0)
111
+ @odds = set.odds
112
+ else
113
+ @odds *= set.odds
114
+ end
115
+ end
116
+
117
+ @mods.each do |mod|
118
+ @odds += mod
119
+ end
120
+ end
121
+
86
122
  def self.roll(num, sides)
87
123
  total = 0
88
124
  dice = []
@@ -91,7 +127,8 @@ module::Dieroll
91
127
  end
92
128
  Dieroll::Result.new(sides, dice)
93
129
  end
94
-
130
+
131
+ # Parse str and return an array to create DiceSets.
95
132
  def self.s_to_set(str)
96
133
  sets = []
97
134
  set_strings = str.scan(/^[^+|-]+|[+|-][^+|-]+/)
data/lib/dieroll.rb CHANGED
@@ -2,3 +2,4 @@ require 'dieroll/roller'
2
2
  require 'dieroll/result'
3
3
  require 'dieroll/die'
4
4
  require 'dieroll/diceset'
5
+ require 'dieroll/odds'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dieroll
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.0
4
+ version: 1.0.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,9 +9,9 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-03-17 00:00:00.000000000 Z
12
+ date: 2012-03-25 00:00:00.000000000 Z
13
13
  dependencies: []
14
- description: Dieroll allows for rolling dice described by 'XdY+Z' strings
14
+ description: Dieroll allows for rolling dice described by dice notation
15
15
  email:
16
16
  executables:
17
17
  - dieroll
@@ -19,6 +19,7 @@ extensions: []
19
19
  extra_rdoc_files: []
20
20
  files:
21
21
  - lib/dieroll/roller.rb
22
+ - lib/dieroll/odds.rb
22
23
  - lib/dieroll/result.rb
23
24
  - lib/dieroll/diceset.rb
24
25
  - lib/dieroll/die.rb