hazard 1.3.0 → 1.3.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e1e25f4c424287018d994674779d8d269c7fa6b4
4
- data.tar.gz: 47c5f83f912a690514abdf59f6529412945ef82e
3
+ metadata.gz: ccb0217f3b63ffd8b667acb3ca8250145540120e
4
+ data.tar.gz: c0b892b16371e60d4e4a0c71e42c5319bba34ffb
5
5
  SHA512:
6
- metadata.gz: 04f3188932596ec902ab3cd2fb30bceaa197cdc4f3f01284b2bfd9b84150080686d7478cf586202c7295b3bce11196feade4a6872e9066aed94e7f7a58237069
7
- data.tar.gz: e370ad454d0865bf28ba73831695ffb19605a0b3a60b2c6e0c5a8194438fb34bdea7ba3fa321c38e31807cbda6ca2a6f0ea7d0ac8155241ba808d0ec4476bb7c
6
+ metadata.gz: 60f0e90c335352e3e2f9deac1c18f247bcd791727d751309fe25a41ce2f66d9f816e60e4c7c6bb9db9b3b2f90fffb2bd36a9463dd006e5373338a7944f20a955
7
+ data.tar.gz: 97cb2d1b0c0cad4616f7e0ab2b8d682fced257c545117b2bb488a66aad73f9a36f479926cb24c8b327e6102855aee9cc97cd75a99b9af519ccd114d718d44d0a
@@ -15,7 +15,6 @@ rvm:
15
15
  - 2.2
16
16
  - 2.1
17
17
  - 2.0
18
- - 1.9
19
18
 
20
19
  before_script:
21
20
  - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
data/README.md CHANGED
@@ -28,7 +28,7 @@ Hazard is a very simple dice library for ruby that allows you to :
28
28
  Add this line to your application's Gemfile:
29
29
 
30
30
  ```ruby
31
- gem 'hazard', '~> 1.2'
31
+ gem 'hazard', '~> 1.3', '>= 1.3.1'
32
32
  ```
33
33
 
34
34
  And then execute:
@@ -195,6 +195,21 @@ Examples :
195
195
  >> wt = WeightedTable.from_flat_table( :foo, :foo, :bar ]
196
196
  >> wt.sample
197
197
  # This ensure that you will get 66% foo and 33% bar
198
+
199
+ ### Weighted tables can also be used with floating points numbers
200
+
201
+ >> wt = WeightedTable.new( floating_points: true )
202
+ >> wt.from_weighted_table( [ <weight1>, <object1> ], [ <weight2>, <object2> ], ... ]
203
+ # Create a weighted table storing objects according to theire weights
204
+
205
+ >> wt.sample
206
+ # Return weighted random object
207
+
208
+ Examples :
209
+
210
+ >> wt = WeightedTable.new( floating_points: true )
211
+ >> wt.from_weighted_table( [ 2.6, :foo ], [ 1.5, :bar ] ]
212
+ >> wt.sample
198
213
 
199
214
  ### Saving and loading
200
215
 
@@ -28,6 +28,6 @@ Gem::Specification.new do |spec|
28
28
  spec.add_development_dependency 'rake', '~> 10.0'
29
29
  spec.add_development_dependency 'minitest', '~> 5.0'
30
30
 
31
- spec.required_ruby_version = '>= 1.9'
31
+ spec.required_ruby_version = '>= 2.0'
32
32
 
33
33
  end
@@ -1,3 +1,3 @@
1
1
  class Hazard
2
- VERSION = '1.3.0'
2
+ VERSION = '1.3.1'
3
3
  end
@@ -5,11 +5,14 @@ require 'yaml'
5
5
  # @author Cédric ZUGER
6
6
  class WeightedTable
7
7
 
8
- BASE_WEIGHT = 1
8
+ BASE_WEIGHT = 0
9
9
 
10
- def initialize
11
- @weights = {}
10
+ # Initialize a WeightedTable
11
+ # setting floating_points to true indicate that you will work with floating points rather than integers.
12
+ def initialize( floating_points: false )
13
+ @weights = []
12
14
  @max_weight = BASE_WEIGHT
15
+ @floating_points = floating_points
13
16
  end
14
17
 
15
18
  # Load a WeightedTable with data
@@ -28,13 +31,23 @@ class WeightedTable
28
31
  # @return [WeightedTable] the current WeightedTable
29
32
  def from_weighted_table( table )
30
33
  raise 'Table must contain at least one element' if table.empty?
34
+
35
+ # We may call this method many time for an existing object, so we must clear it
36
+ @weights.clear
37
+
31
38
  base = BASE_WEIGHT
32
- table.each do |t|
33
- w = base+t[0]
39
+ w = nil
40
+
41
+ table.each do |weight, data|
42
+ w = base + weight
43
+ @weights << [base, w, data]
34
44
  base = w
35
- @weights[ w ] = t[1]
36
- @max_weight = [ @max_weight, w-1 ].max
37
- end
45
+ end
46
+
47
+ @max_weight = w
48
+
49
+ # p @weights, @max_weight
50
+
38
51
  self
39
52
  end
40
53
 
@@ -51,16 +64,19 @@ class WeightedTable
51
64
  #
52
65
  # @return [Object] a random object given at the building of the table.
53
66
  def sample
54
- r = Kernel.rand( 1..@max_weight )
55
- keys = @weights.keys.sort
56
67
 
57
- low_mark = BASE_WEIGHT
68
+ # ... means that @max_weight is excluded
69
+ if @floating_points
70
+ r = Kernel.rand( 0.0...@max_weight.to_f )
71
+ else
72
+ r = Kernel.rand( 0...@max_weight )
73
+ end
58
74
 
59
- until keys.empty?
60
- high_mark = keys.shift
61
- return @weights[high_mark] if r >= low_mark && r < high_mark
62
- low_mark = high_mark
63
- end
75
+ # p r
76
+
77
+ @weights.each do |base_weight, max_weight, data|
78
+ return data if r >= base_weight && r < max_weight
79
+ end
64
80
 
65
81
  raise 'Rand not in key range'
66
82
  end
@@ -2,92 +2,162 @@ require 'test_helper'
2
2
 
3
3
  class HazardTest < Minitest::Test
4
4
 
5
- def test_empty_table
6
- assert_raises do
7
- WeightedTable.from_weighted_table []
8
- end
9
- assert_raises do
10
- WeightedTable.from_flat_table []
11
- end
12
- end
13
-
14
- def test_one_element_table
15
- assert_equal :foo, WeightedTable.from_weighted_table( [ [ 3, :foo ] ] ).sample
16
- assert_equal :foo, WeightedTable.from_flat_table( [ :foo, :foo, :foo ] ).sample
17
- end
18
-
19
- def test_two_element_table
20
- Kernel.stubs( :rand ).returns( 0 )
21
- assert_raises do
22
- WeightedTable.from_weighted_table []
23
- end
24
- assert_raises do
25
- WeightedTable.from_flat_table []
26
- end
27
-
28
- Kernel.stubs( :rand ).returns( 1 )
29
- assert_equal :foo, WeightedTable.from_weighted_table( [ [ 3, :foo ], [ 1, :bar ] ] ).sample
30
- assert_equal :foo, WeightedTable.from_flat_table( [ :foo, :foo, :foo, :bar ] ).sample
31
- Kernel.stubs( :rand ).returns( 2 )
32
- assert_equal :foo, WeightedTable.from_weighted_table( [ [ 3, :foo ], [ 1, :bar ] ] ).sample
33
- assert_equal :foo, WeightedTable.from_flat_table( [ :foo, :foo, :foo, :bar ] ).sample
34
- Kernel.stubs( :rand ).returns( 3 )
35
- assert_equal :foo, WeightedTable.from_weighted_table( [ [ 3, :foo ], [ 1, :bar ] ] ).sample
36
- assert_equal :foo, WeightedTable.from_flat_table( [ :foo, :foo, :foo, :bar ] ).sample
37
- Kernel.stubs( :rand ).returns( 4 )
38
- assert_equal :bar, WeightedTable.from_weighted_table( [ [ 3, :foo ], [ 1, :bar ] ] ).sample
39
- assert_equal :bar, WeightedTable.from_flat_table( [ :foo, :foo, :foo, :bar ] ).sample
40
-
41
- Kernel.stubs( :rand ).returns( 5 )
42
- assert_raises 'Rand not in key range' do
43
- WeightedTable.from_weighted_table []
44
- end
45
- assert_raises 'Rand not in key range' do
46
- WeightedTable.from_flat_table []
47
- end
48
- end
49
-
50
- def test_three_element_table
51
-
52
- Kernel.stubs( :rand ).returns( 1 )
53
- assert_equal :foo, WeightedTable.from_weighted_table( [ [ 3, :foo ], [ 1, :bar ], [ 1, :foobar ] ] ).sample
54
- assert_equal :foo, WeightedTable.from_flat_table( [ :foo, :foo, :foo, :bar, :foobar ] ).sample
55
-
56
- Kernel.stubs( :rand ).returns( 5 )
57
- assert_equal :foobar, WeightedTable.from_weighted_table( [ [ 3, :foo ], [ 1, :bar ], [ 1, :foobar ] ] ).sample
58
- assert_equal :foobar, WeightedTable.from_flat_table( [ :foo, :foo, :foo, :bar, :foobar ] ).sample
59
-
60
- end
61
-
62
- def test_statistics
63
- wt = WeightedTable.from_weighted_table [ [ 2, :foo ], [ 1, :bar ] ]
64
- results = { foo: 0, bar: 0 }
65
-
66
- rounds = 1000
67
-
68
- 1.upto(rounds).each do
69
- results[ wt.sample ] += 1
70
- end
71
-
72
- assert_in_delta 0.33, (results[:bar].to_f/rounds), 0.1
73
- assert_in_delta 0.66, (results[:foo].to_f/rounds), 0.1
74
- end
75
-
76
- def test_save_and_load
77
- wt = WeightedTable.from_weighted_table [ [ 2, :foo ], [ 1, :bar ] ]
78
- wt.to_file( 'wt.yaml' )
79
- wt = nil
80
- wt = WeightedTable.from_file( 'wt.yaml' )
81
- Kernel.stubs( :rand ).returns( 1 )
82
- assert_equal :foo, wt.sample
83
- assert_equal :foo, wt.sample
84
- `rm wt.yaml`
85
- end
86
-
87
- def test_out_of_range
88
- Kernel.stubs( :rand ).returns( 99 )
89
- assert_raises do
90
- WeightedTable.from_weighted_table( [ [ 3, :foo ], [ 1, :bar ], [ 1, :foobar ] ] ).sample
91
- end
92
- end
5
+ def test_empty_table
6
+ assert_raises do
7
+ WeightedTable.from_weighted_table []
8
+ end
9
+ assert_raises do
10
+ WeightedTable.from_flat_table []
11
+ end
12
+ end
13
+
14
+ def test_one_element_table
15
+ assert_equal :foo, WeightedTable.from_weighted_table( [ [ 3, :foo ] ] ).sample
16
+ assert_equal :foo, WeightedTable.from_flat_table( [ :foo, :foo, :foo ] ).sample
17
+ end
18
+
19
+ def test_two_element_table
20
+ Kernel.stubs( :rand ).returns( 0 )
21
+ assert_raises do
22
+ WeightedTable.from_weighted_table []
23
+ end
24
+ assert_raises do
25
+ WeightedTable.from_flat_table []
26
+ end
27
+
28
+ Kernel.stubs( :rand ).returns( 0 )
29
+ assert_equal :foo, WeightedTable.from_weighted_table( [ [ 3, :foo ], [ 1, :bar ] ] ).sample
30
+ assert_equal :foo, WeightedTable.from_flat_table( [ :foo, :foo, :foo, :bar ] ).sample
31
+ Kernel.stubs( :rand ).returns( 1 )
32
+ assert_equal :foo, WeightedTable.from_weighted_table( [ [ 3, :foo ], [ 1, :bar ] ] ).sample
33
+ assert_equal :foo, WeightedTable.from_flat_table( [ :foo, :foo, :foo, :bar ] ).sample
34
+ Kernel.stubs( :rand ).returns( 2 )
35
+ assert_equal :foo, WeightedTable.from_weighted_table( [ [ 3, :foo ], [ 1, :bar ] ] ).sample
36
+ assert_equal :foo, WeightedTable.from_flat_table( [ :foo, :foo, :foo, :bar ] ).sample
37
+ Kernel.stubs( :rand ).returns( 3 )
38
+ assert_equal :bar, WeightedTable.from_weighted_table( [ [ 3, :foo ], [ 1, :bar ] ] ).sample
39
+ assert_equal :bar, WeightedTable.from_flat_table( [ :foo, :foo, :foo, :bar ] ).sample
40
+
41
+ Kernel.stubs( :rand ).returns( 4 )
42
+ assert_raises 'Rand not in key range' do
43
+ WeightedTable.from_weighted_table []
44
+ end
45
+ assert_raises 'Rand not in key range' do
46
+ WeightedTable.from_flat_table []
47
+ end
48
+ end
49
+
50
+ def test_three_element_table
51
+
52
+ Kernel.stubs( :rand ).returns( 0 )
53
+ assert_equal :foo, WeightedTable.from_weighted_table( [ [ 3, :foo ], [ 1, :bar ], [ 1, :foobar ] ] ).sample
54
+ assert_equal :foo, WeightedTable.from_flat_table( [ :foo, :foo, :foo, :bar, :foobar ] ).sample
55
+
56
+ Kernel.stubs( :rand ).returns( 4 )
57
+ assert_equal :foobar, WeightedTable.from_weighted_table( [ [ 3, :foo ], [ 1, :bar ], [ 1, :foobar ] ] ).sample
58
+ assert_equal :foobar, WeightedTable.from_flat_table( [ :foo, :foo, :foo, :bar, :foobar ] ).sample
59
+ end
60
+
61
+ def test_floating_points
62
+ # 1 -> 4.5 => :foo
63
+ # 4.5 -> 6 => :bar
64
+ # 6 -> 7.5 => :foobar
65
+ wt = WeightedTable.from_weighted_table( [ [ 3.5, :foo ], [ 1.5, :bar ], [ 1.5, :foobar ] ] )
66
+
67
+ Kernel.stubs( :rand ).returns( 0.5 )
68
+ assert_raises 'Rand not in key range' do
69
+ WeightedTable.from_weighted_table []
70
+ end
71
+
72
+ Kernel.stubs( :rand ).returns( 1 )
73
+ assert_equal :foo, wt.sample
74
+
75
+ Kernel.stubs( :rand ).returns( 1.5 )
76
+ assert_equal :foo, wt.sample
77
+
78
+ Kernel.stubs( :rand ).returns( 4.5 )
79
+ assert_equal :bar, wt.sample
80
+
81
+ Kernel.stubs( :rand ).returns( 4.6 )
82
+ assert_equal :bar, wt.sample
83
+
84
+ Kernel.stubs( :rand ).returns( 6 )
85
+ assert_equal :foobar, wt.sample
86
+
87
+ Kernel.stubs( :rand ).returns( 6.3 )
88
+ assert_equal :foobar, wt.sample
89
+
90
+ Kernel.stubs( :rand ).returns( 7.1 )
91
+ assert_raises 'Rand not in key range' do
92
+ WeightedTable.from_weighted_table []
93
+ end
94
+ end
95
+
96
+ def test_floating_point_statistics
97
+ wt = WeightedTable.new( floating_points: true )
98
+
99
+ tests_arrays = [
100
+ [[ 2.3, :foo ], [ 1.6, :bar ]], [[ 6.4, :foo ], [ 8.2, :bar ]], [[ 35.7, :foo ], [ 678.9, :bar ]],
101
+ [[ 6.4, :foo ], [ 8.7, :bar ], [ 4.1, :foobar ]], [[ 56, :foo ], [ 4.9, :bar ], [ 665.4, :foobar ]]
102
+ ]
103
+
104
+ tests_arrays.each do |t_a|
105
+
106
+ wt.from_weighted_table t_a
107
+ results = Hash[t_a.map{ |_, elem| [ elem, 0 ] }]
108
+
109
+ rounds = 100000
110
+ 1.upto(rounds).each do
111
+ results[ wt.sample ] += 1
112
+ end
113
+
114
+ weights_sum = t_a.map{ |weight, elem| weight }.inject( :+ ).to_f
115
+
116
+ t_a.each do |weight, elem|
117
+ assert_in_delta weight/weights_sum, (results[elem].to_f/rounds), 0.01
118
+ end
119
+ end
120
+ end
121
+
122
+ def test_statistics
123
+ tests_arrays = [
124
+ [[ 2, :foo ], [ 1, :bar ]], [[ 6, :foo ], [ 8, :bar ]], [[ 35, :foo ], [ 678, :bar ]],
125
+ [[ 6, :foo ], [ 8, :bar ], [ 4, :foobar ]], [[ 56, :foo ], [ 4, :bar ], [ 665, :foobar ]]
126
+ ]
127
+
128
+ tests_arrays.each do |t_a|
129
+ wt = WeightedTable.from_weighted_table t_a
130
+ results = Hash[t_a.map{ |_, elem| [ elem, 0 ] }]
131
+
132
+ rounds = 100000
133
+
134
+ 1.upto(rounds).each do
135
+ results[ wt.sample ] += 1
136
+ end
137
+
138
+ weights_sum = t_a.map{ |weight, elem| weight }.inject( :+ ).to_f
139
+
140
+ t_a.each do |weight, elem|
141
+ assert_in_delta weight/weights_sum, (results[elem].to_f/rounds), 0.01
142
+ end
143
+ end
144
+ end
145
+
146
+ def test_save_and_load
147
+ wt = WeightedTable.from_weighted_table [ [ 2, :foo ], [ 1, :bar ] ]
148
+ wt.to_file( 'wt.yaml' )
149
+ wt = nil
150
+ wt = WeightedTable.from_file( 'wt.yaml' )
151
+ Kernel.stubs( :rand ).returns( 1 )
152
+ assert_equal :foo, wt.sample
153
+ assert_equal :foo, wt.sample
154
+ `rm wt.yaml`
155
+ end
156
+
157
+ def test_out_of_range
158
+ Kernel.stubs( :rand ).returns( 99 )
159
+ assert_raises do
160
+ WeightedTable.from_weighted_table( [ [ 3, :foo ], [ 1, :bar ], [ 1, :foobar ] ] ).sample
161
+ end
162
+ end
93
163
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hazard
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 1.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cédric ZUGER
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-05-06 00:00:00.000000000 Z
11
+ date: 2019-11-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -73,7 +73,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
73
73
  requirements:
74
74
  - - ">="
75
75
  - !ruby/object:Gem::Version
76
- version: '1.9'
76
+ version: '2.0'
77
77
  required_rubygems_version: !ruby/object:Gem::Requirement
78
78
  requirements:
79
79
  - - ">="