hazard 1.0.4 → 1.1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d73a6ac407a313337a1309b8f85680c2acdf9a36
4
- data.tar.gz: f26d0073ed98e7d10483815e6b16f6456e582142
3
+ metadata.gz: f9ae47cda73390cc30cd6d81a594879a0f142308
4
+ data.tar.gz: 7eb73440367d32076ddbef3b9d2ded4ecb772fe5
5
5
  SHA512:
6
- metadata.gz: 9b6aa19cf68606574fca81bbb5c3242fbabc44833806078de8a883de43fba72c16df8a10f42b163a0c18e829d70d873bb3e3450634cf30115c08254e43fe4fa4
7
- data.tar.gz: 4decd66c91587ad5a1eba94d94d575432c874589801fd9a087e7ba88c50930e6feb1ca1d11a8dfe8d40bdf7cbb221393d0dcc531b6785febcb02f915bbbcd960
6
+ metadata.gz: 7678295f955f7d2ddc157951b5da6d91746092eaf4f6eaad5c10dfbba7b19505bc676aa3d0baef9be415804e2ee5427d78802d45965ba486d94459d4453ada2d
7
+ data.tar.gz: 9c000fc08c2bd6debd467784dd728cd106f87958add39c9d8ab28d4b51d24a3f65930e22b55c9d80dee3e3f6b5f77a865b6a427a966552f3cf51da717ac976bb
data/README.md CHANGED
@@ -5,8 +5,23 @@
5
5
  [![Code Climate](https://codeclimate.com/github/czuger/hazard/badges/gpa.svg)](https://codeclimate.com/github/czuger/hazard)
6
6
  [![Test Coverage](https://codeclimate.com/github/czuger/hazard/badges/coverage.svg)](https://codeclimate.com/github/czuger/hazard/coverage)
7
7
 
8
- Hazard is a very simple dice library for ruby (see [usage](#usage)).
9
-
8
+ Hazard is a very simple dice library for ruby that allows you to :
9
+ * Roll dice and get the sum
10
+ * Roll dice and work with the detail
11
+ * Random pick elements from weighted lists
12
+
13
+ 1. [Installation](#installation)
14
+ 1. [Basic Usage](#basic-usage)
15
+ 1. [Roll a simple die](#roll-a-simple-die)
16
+ 1. [Roll multiple dice](#roll-multiple-dice)
17
+ 1. [Advanced Usage](#advanced-usage)
18
+ 1. [Roll dice and get the details](#roll-dice-and-get-the-details)
19
+ 1. [Some real cases](#some-real-cases)
20
+ 1. [Weighted Tables](#weighted-Tables)
21
+ 1. [If you have the weights](#if-you-have-the-weights)
22
+ 1. [If you don't have the weights (or are to lazy to get them)](#if-you-dont-have-the-weights-or-are-to-lazy-to-get-them)
23
+ 1. [Saving and loading](#saving-and-loading)
24
+
10
25
  ## Installation
11
26
 
12
27
  Add this line to your application's Gemfile:
@@ -27,9 +42,9 @@ If needed :
27
42
 
28
43
  $ require 'hazard'
29
44
 
30
- ## Usage
45
+ ## Basic Usage
31
46
 
32
- **Roll a simple die**
47
+ ### Roll a simple die
33
48
 
34
49
  >> Hazard.d<n> # where n is an number
35
50
  => Roll a n-sided die
@@ -46,7 +61,7 @@ Examples :
46
61
  => 38
47
62
 
48
63
 
49
- **Roll multiple dice**
64
+ ### Roll multiple dice
50
65
 
51
66
  >> Hazard.r<m>d<n> # where m and n are numbers
52
67
  => Roll m n-sided dice and return the sum
@@ -72,8 +87,9 @@ Examples :
72
87
  >> Hazard.d2d6
73
88
  => 8
74
89
 
90
+ ## Advanced Usage
75
91
 
76
- **Roll dice and get the details**
92
+ ### Roll dice and get the details
77
93
 
78
94
  >> Hazard.s<m>d<n> # where m and n are numbers
79
95
  => Roll m n-sided dice and return a RolledDice object
@@ -100,9 +116,9 @@ Examples :
100
116
  >> Hazard.s2d6
101
117
  => #<RolledDice:0x007f62e55a0010 @rolls=[1, 6], @result=7>
102
118
 
103
- **Some real cases**
119
+ ### Some real cases
104
120
 
105
- # Assuming you are playing DD Next
121
+ Assuming you are playing DD Next
106
122
 
107
123
  # You may want to roll 2 d20 dice with advantage (take the greatest)
108
124
  # This will rolls 2 d20, get the rolls and get the best of them
@@ -120,8 +136,50 @@ Examples :
120
136
  # Should you have the Elemental Adept feat, which mean that you treat all 1 as 2
121
137
  # If you cast a fireball, this will do the trick :
122
138
  >> Hazard.s8d6.rolls.map{ |d| d == 1 ? 2 : d }.reduce(:+)
123
- => 24
139
+ => 24
140
+
141
+ ## Weighted Tables
124
142
 
143
+ Weighted tables are object that allow to get weighted random.
144
+ Example : if you have two time foo and one time bar in your table.
145
+
146
+ ### If you have the weights
147
+
148
+ >> wt = WeightedTable.from_weighted_table( [ <weight1>, <object1> ], [ <weight2>, <object2> ], ... ]
149
+ # Create a weighted table storing objects according to theire weights
150
+
151
+ >> wt.sample
152
+ # Return weighted random object
153
+
154
+ Examples :
155
+
156
+ >> wt = WeightedTable.new.from_weighted_table( [ 2, :foo ], [ 1, :bar ] ]
157
+ >> wt.sample
158
+ # This ensure that you will get 66% foo and 33% bar
159
+
160
+
161
+ ### If you don't have the weights (or are to lazy to get them)
162
+
163
+ >> wt = WeightedTable.from_flat_table( <object1>, <object1>, <object2>, ... ]
164
+ # Create a weighted table storing objects computing the weight of the objects according to theire occurences
165
+
166
+ >> wt.sample
167
+ # Return weighted random object
168
+
169
+ Examples :
170
+
171
+ >> wt = WeightedTable.new.from_flat_table( :foo, :foo, :bar ]
172
+ >> wt.sample
173
+ # This ensure that you will get 66% foo and 33% bar
174
+
175
+ ### Saving and loading
176
+
177
+ # You can save your builded table for future usage
178
+ >> wt.to_file( filename )
179
+
180
+ # And load it
181
+ >> wt = WeightedTable.from_file( filename )
182
+
125
183
  ## Contributing
126
184
 
127
185
  Bug reports and pull requests are welcome on GitHub at https://github.com/czuger/hazard. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
@@ -1,3 +1,3 @@
1
1
  class Hazard
2
- VERSION = '1.0.4'
2
+ VERSION = '1.1.0'
3
3
  end
data/lib/hazard.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require_relative 'rolled_dice'
2
+ require_relative 'weighted_table'
2
3
 
3
4
  class Hazard
4
5
 
@@ -0,0 +1,88 @@
1
+ require 'yaml'
2
+
3
+ class WeightedTable
4
+
5
+ BASE_WEIGHT = 1
6
+
7
+ def initialize
8
+ @weights = {}
9
+ @max_weight = BASE_WEIGHT
10
+ end
11
+
12
+ # Load a WeightedTable with data
13
+ # Data format must be : [ [ weight, data ], [ weight, data ], ... ]
14
+ # Example : [ [ 2, :foo ], [ 1, :bar ] ]
15
+ #
16
+ # @return [WeightedTable] the current WeightedTable
17
+ def self.from_weighted_table( table )
18
+ WeightedTable.new.from_weighted_table( table )
19
+ end
20
+
21
+ # Load a WeightedTable with data
22
+ # Data format must be : [ [ weight, data ], [ weight, data ], ... ]
23
+ # Example : [ [ 2, :foo ], [ 1, :bar ] ]
24
+ #
25
+ # @return [WeightedTable] the current WeightedTable
26
+ def from_weighted_table( table )
27
+ raise 'Table must contain at least one element' if table.empty?
28
+ base = BASE_WEIGHT
29
+ table.each do |t|
30
+ w = base+t[0]
31
+ base = w
32
+ @weights[ w ] = t[1]
33
+ @max_weight = [ @max_weight, w-1 ].max
34
+ end
35
+ self
36
+ end
37
+
38
+ # Load a WeightedTable with data
39
+ # Data format must be : [ data, data, data ]
40
+ # Example : [ :foo, :foo, :bar ]
41
+ #
42
+ # @return [WeightedTable] the current WeightedTable
43
+ def self.from_flat_table( table )
44
+ from_weighted_table( table.group_by{ |e| e }.map{ |k, v| [ v.count, k ] } )
45
+ end
46
+
47
+ # Return a random item from a WeightedTable
48
+ #
49
+ # @return [Object] a random object given at the building of the table.
50
+ def sample
51
+ r = Kernel.rand( 1..@max_weight )
52
+ keys = @weights.keys.sort
53
+
54
+ low_mark = BASE_WEIGHT
55
+
56
+ until keys.empty?
57
+ high_mark = keys.shift
58
+ return @weights[high_mark] if r >= low_mark && r < high_mark
59
+ low_mark = high_mark
60
+ end
61
+
62
+ raise 'Rand not in key range'
63
+ end
64
+
65
+ # Load a WeightedTable with data
66
+ # Data format must be : [ data, data, data ]
67
+ # Example : [ :foo, :foo, :bar ]
68
+ #
69
+ # @return [WeightedTable] the current WeightedTable
70
+ def from_flat_table( table )
71
+ from_weighted_table( table.group_by{ |e| e }.map{ |k, v| [ v.count, k ] } )
72
+ end
73
+
74
+ # Save the table to a file
75
+ # @param filename [String] the filename where to save the table
76
+ def to_file( filename )
77
+ File.open( filename, 'w' ) do |f|
78
+ f.write( to_yaml )
79
+ end
80
+ end
81
+
82
+ # Read the table from a file
83
+ # @param filename [String] the filename from which to read the table
84
+ def self.from_file( filename )
85
+ YAML.load_file( filename )
86
+ end
87
+
88
+ end
data/test/test_helper.rb CHANGED
@@ -7,4 +7,5 @@ require 'hazard'
7
7
 
8
8
  require 'minitest/autorun'
9
9
  require 'mocha/mini_test'
10
+ require 'pp'
10
11
 
@@ -0,0 +1,73 @@
1
+ require 'test_helper'
2
+
3
+ class HazardTest < Minitest::Test
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 do
43
+ WeightedTable.from_weighted_table []
44
+ end
45
+ assert_raises do
46
+ WeightedTable.from_flat_table []
47
+ end
48
+ end
49
+
50
+ def test_statistics
51
+
52
+ wt = WeightedTable.from_weighted_table [ [ 2, :foo ], [ 1, :bar ] ]
53
+ results = { foo: 0, bar: 0 }
54
+ 1.upto(100).each do
55
+ results[ wt.sample ] += 1
56
+ end
57
+
58
+ assert_in_delta 0.33, (results[:bar]*0.01), 0.1
59
+ assert_in_delta 0.66, (results[:foo]*0.01), 0.1
60
+ end
61
+
62
+ def test_save_and_load
63
+ wt = WeightedTable.from_weighted_table [ [ 2, :foo ], [ 1, :bar ] ]
64
+ wt.to_file( 'wt.yaml' )
65
+ wt = nil
66
+ wt = WeightedTable.from_file( 'wt.yaml' )
67
+ Kernel.stubs( :rand ).returns( 1 )
68
+ assert_equal :foo, wt.sample
69
+ assert_equal :foo, wt.sample
70
+ `rm wt.yaml`
71
+ end
72
+
73
+ 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.0.4
4
+ version: 1.1.0
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: 2018-03-20 00:00:00.000000000 Z
11
+ date: 2018-04-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -71,8 +71,10 @@ files:
71
71
  - lib/hazard.rb
72
72
  - lib/hazard/version.rb
73
73
  - lib/rolled_dice.rb
74
+ - lib/weighted_table.rb
74
75
  - test/hazard_test.rb
75
76
  - test/test_helper.rb
77
+ - test/weighted_table_test.rb
76
78
  homepage: https://github.com/czuger/hazard
77
79
  licenses:
78
80
  - MIT
@@ -100,3 +102,4 @@ summary: Dice library for ruby
100
102
  test_files:
101
103
  - test/hazard_test.rb
102
104
  - test/test_helper.rb
105
+ - test/weighted_table_test.rb