hazard 1.3.0 → 1.3.1

Sign up to get free protection for your applications and to get access to all the features.
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
  - - ">="