hand_record_utility 1.0.2

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: db3a370a62468dc2340f6d38b71b8abb2e306dc8
4
+ data.tar.gz: cf95f78402510dcd2408f4a3900799f029569485
5
+ SHA512:
6
+ metadata.gz: 3ce5b36d978516bf2ab4a7d0a7f08821439e2acd41077cb2651ece428900ab7b4c5f23e7657829fd44ab5bc239ae8f7c2e75f8dabefcfd2ff37bc680327ff92b
7
+ data.tar.gz: 18a97da6cb633aa608b1635cb7a69b24021b83286fd9573acb37aaeee44a5064f0d8241b6f4e5d6f390c9924f745f8a0fa769dfc9b19ac3af701c4ef933a410c
data/AUTHORS ADDED
@@ -0,0 +1,13 @@
1
+ Main developer and maintainer:
2
+ ==
3
+
4
+ Nicolas Hammond
5
+
6
+
7
+ Contributors:
8
+ ==
9
+
10
+ Richard Pavlicek - author of the algorithm.
11
+ Greg Humphries - wrote Andrews code.
12
+
13
+
data/Gemfile ADDED
@@ -0,0 +1,16 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Declare your gem's dependencies in hand_record_utility.gemspec.
4
+ # Bundler will treat runtime dependencies like base dependencies, and
5
+ # development dependencies will be added by default to the :development group.
6
+ gemspec
7
+
8
+ gem 'combinatorics'
9
+
10
+ # Declare any dependencies that are still in development here instead of in
11
+ # your gemspec. These might include edge Rails or gems from your path or
12
+ # Git. Remember to move these dependencies to your gemspec before releasing
13
+ # your gem to rubygems.org.
14
+
15
+ # To use debugger
16
+ # gem 'debugger'
@@ -0,0 +1,32 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ hand_record_utility (1.0.2)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ combinatorics (0.4.3)
10
+ diff-lcs (1.2.5)
11
+ rake (10.3.2)
12
+ rspec (3.1.0)
13
+ rspec-core (~> 3.1.0)
14
+ rspec-expectations (~> 3.1.0)
15
+ rspec-mocks (~> 3.1.0)
16
+ rspec-core (3.1.4)
17
+ rspec-support (~> 3.1.0)
18
+ rspec-expectations (3.1.2)
19
+ diff-lcs (>= 1.2.0, < 2.0)
20
+ rspec-support (~> 3.1.0)
21
+ rspec-mocks (3.1.2)
22
+ rspec-support (~> 3.1.0)
23
+ rspec-support (3.1.1)
24
+
25
+ PLATFORMS
26
+ ruby
27
+
28
+ DEPENDENCIES
29
+ combinatorics
30
+ hand_record_utility!
31
+ rake
32
+ rspec
@@ -0,0 +1,20 @@
1
+ Copyright 2014 Nicolas Hammond
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,115 @@
1
+ Hand Record Utility
2
+ ==
3
+
4
+ Utility for hand records, primarily for use in Bridge. Can be applied to other games with 4x13 card hands.
5
+
6
+ This code takes a hand record (4 hands from a Bridge table) and converts it to a unique number using Richard Pavlicek's alogrithm. See http://www.rpbridge.net/7z68.htm for details on Pavlicek's algorithm.
7
+
8
+ Being extended to include other algorithms, e.g. Thomas Andrews. See http://bridge.thomasoandrews.com/impossible/
9
+
10
+ Install
11
+ ==
12
+
13
+ To install
14
+
15
+ git clone https://github.com/njhammond/hand_record_utility
16
+ cd hand_record_utility
17
+ bundle install
18
+
19
+ Test
20
+ ==
21
+
22
+ To test
23
+
24
+ rake
25
+
26
+ Expect to see 0 failures. There may be some diagnostics about invalid numbers, this is all part of the test. There are also standalone tests in the ```./standalone_tests``` directory. These contain some of the debug code. Use these for code examples.
27
+
28
+ Given a unique number, either use the debug tools in this code to display it,
29
+ or use Thomas Andrews' web site,
30
+ [http://bridge.thomasoandrews.com/impossible]
31
+ (http://bridge.thomasoandrews.com/impossible).
32
+
33
+ Use, can also test
34
+
35
+ rspec
36
+
37
+ Usage
38
+ ==
39
+
40
+ The code is written in Ruby.
41
+
42
+ The maximum number of deals is HandRecordUtility::D or
43
+ 53644737765488792839237440000. D is the name that Pavlicek assigns to the maximum number of deals.
44
+
45
+ To create a random deal, and convert back
46
+
47
+ ```
48
+ i = rand(HandRecordUtility::D) + 1
49
+ board = HandRecordUtility.pavlicek_number_to_board(i)
50
+ HandRecordUtility.to_pavlicek_number(board)
51
+ ```
52
+
53
+ Board is a hash with elements, :north, :east, :south, :west.
54
+
55
+ There are some debug options to pretty print the data.
56
+
57
+ ```
58
+ # Print a short form of a board
59
+ HandRecordUtility.debug_board_short_form(board)
60
+ # Print a hand record of a board
61
+ HandRecordUtility.debug_board(board)
62
+ ```
63
+
64
+ To do the same for Andrews numbers
65
+
66
+ ```
67
+ i = rand(HandRecordUtility::D) + 1
68
+ board = HandRecordUtility.andrews_number_to_board(i)
69
+ HandRecordUtility.to_andrews_number(board)
70
+ # Print a short form of a board
71
+ HandRecordUtility.debug_board_short_form(board)
72
+ # Print a hand record of a board
73
+ HandRecordUtility.debug_board(board)
74
+ ```
75
+
76
+
77
+ Notes
78
+ ==
79
+
80
+ Externally the range is 1..D, within the code the range is 0..D-1.
81
+
82
+ Definitions
83
+ ==
84
+
85
+ A hand has 13 cards. A hand record has 4 hands.
86
+
87
+ There are 53,644,737,765,488,792,839,237,440,000 possible bridge deals.
88
+
89
+ Credits
90
+ ==
91
+
92
+ Richard Pavlicek has given his approval for use of his algorithm subject to proper accreditiation. Thank you Richard.
93
+
94
+ Thomas Andrews given his approval for use of his algorithm subject to proper accreditiation. Thank you Thomas.
95
+
96
+ Purpose
97
+ ==
98
+
99
+ The original purpose for putting this code out in the public domain is to try to spur some interest in creating Open Source code that can be used in Bridge projects.
100
+
101
+ Developers
102
+ ==
103
+
104
+ See AUTHORS for list of developers.
105
+
106
+
107
+ License
108
+ ==
109
+
110
+ This project uses the MIT-LICENSE.
111
+
112
+ Please make sure to credit Richard Pavlicek as original author of the algorithm as well.
113
+
114
+ Please make sure to credit Thomas Andrews as original author of his algorithm.
115
+
@@ -0,0 +1,37 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+ require 'rspec/core/rake_task'
9
+
10
+ RDoc::Task.new(:rdoc) do |rdoc|
11
+ rdoc.rdoc_dir = 'rdoc'
12
+ rdoc.title = 'HandRecordUtility'
13
+ rdoc.options << '--line-numbers'
14
+ rdoc.rdoc_files.include('README.rdoc')
15
+ rdoc.rdoc_files.include('lib/**/*.rb')
16
+ end
17
+
18
+
19
+ Bundler::GemHelper.install_tasks
20
+
21
+ require 'rake/testtask'
22
+
23
+ Rake::TestTask.new(:test) do |t|
24
+ t.libs << 'lib'
25
+ t.libs << 'test'
26
+ # t.pattern = 'test/**/*_test.rb'
27
+ t.pattern = 'test/*_test.rb'
28
+ t.verbose = false
29
+ end
30
+
31
+ RSpec::Core::RakeTask.new(:spec) do |config|
32
+ # Need to fix. Get errors about pattern.
33
+ config.pattern = "spec/*_spec.rb"
34
+ end
35
+
36
+ #task default: :test
37
+ task :default => :spec
@@ -0,0 +1,695 @@
1
+ # Provides a set of utilities for manipulating hand records.
2
+ # Primarily for use for Bridge, the card game.
3
+ #
4
+ # A hand record/board contains four hands: ":north", ":south", ":west", ":east"
5
+ # Each hand is a string[17] in the format
6
+ # example hand="SKQH5432DAQJCT954"
7
+ # where T=Ten (we rarely use 10 when working on internal strings).
8
+ # Because of the implementation you can remove suits which are not used, e.g.
9
+ # example hand="SAKQJT9H8765432"
10
+ # but this is discouraged.
11
+ #
12
+ # This gem assumes that the hand record and hand are well formed.
13
+ # Little/no error checking on input values.
14
+ #
15
+ # See http://www.rpbridge.net/7z68.htm for Pavlicek algorithm.
16
+ # Richard has given his permission to use the algorithm with proper attribution.
17
+ #
18
+ # This algorithm uses 0..D-1 internally, for a UI and externally we use 1..D
19
+ #
20
+ # See bridge.thomasandrews.com/impossible
21
+ # for a web site that displays Pavlicek hands.
22
+ #
23
+ # We provide routines to go both ways, from a unique number to a hand record,
24
+ # from a hand record to a unique number.
25
+ #
26
+ # Code now supports Andrews numbers as well.
27
+ #
28
+ # Code added to convert from a large number (up to 29 digits, see D below)
29
+ # to hex (up to 24 digits) and to base 64 (up to 16 characters).
30
+ # The latter has a translation algorithm to ensure that the 64 characters are
31
+ # regular ASCII characters.
32
+ # Also can convert the other way.
33
+ # This shortens the number for transmission, e.g. URLs
34
+ #
35
+ # Code best viewed with a tab stop of 2.
36
+ # 345678901234567890123456789012345678901234567890123456789012345678901234567890
37
+
38
+ # Used for some factorial math
39
+ require 'combinatorics/choose'
40
+
41
+ module HandRecordUtility
42
+
43
+ # Constants. D is a 29 digit number
44
+ # 22222222221111111111000000000
45
+ # 98765432109876543210987654321
46
+ D = 53644737765488792839237440000
47
+ # Different directions
48
+ DIR_N = 0
49
+ DIR_E = 1
50
+ DIR_S = 2
51
+ DIR_W = 3
52
+ DIRECTIONS = [
53
+ "North",
54
+ "East",
55
+ "South",
56
+ "West",
57
+ ]
58
+
59
+ # 64 characters so can map
60
+ CHAR_TO_BITS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ{}"
61
+ # Given the ascii characters, map to a number. Subtract 48 from the char
62
+ BITS_TO_CHAR = [
63
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, # 0123456789
64
+ -1, -1, -1, -1, -1, -1, -1, 36, 37, 38, # :;<=>?@ABC
65
+ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, # DEFGHIJKLM
66
+ 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, # NOPQRSTUVW
67
+ 59, 60, 61, -1, -1, -1, -1, -1, -1, 10, # XYZ[/]^_'a
68
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, # bcdefghijk
69
+ 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, # lmnopqrstu
70
+ 31, 32, 33, 34, 35, 62, -1, 63 # vwxyz{|}
71
+ ]
72
+
73
+ # Using the Ranked order, the order of cards is AS, AH, AD, AC
74
+ # Using the Suited order, the order of cards is AS, KS, QS, ...
75
+ # Always used Ranked
76
+ PAVLICEK_RANKED = 1
77
+ PAVLICEK_SUITED = 2
78
+
79
+ # Used in Andrews numbers
80
+ S_MAX = Combinatorics::Choose.C(26,13)
81
+ E_MAX = Combinatorics::Choose.C(39,13)
82
+
83
+ # Default is suited
84
+ # Pavlicek web site uses the ranked order, Andrews reference is to the suited
85
+ # order.
86
+ # Using Andrews web site to generate the numbers, the suited is preferred.
87
+ # The number is assumed to be 1..D, however the algorithm on Pavlicek's page
88
+ # is 0..D-1. Therefore in a later routine we subtract 1.
89
+ def self.pavlicek_number_to_board(number)
90
+ pavlicek_suited_number_to_board(number)
91
+ end
92
+
93
+ # Uses the ranked Pavlicek method
94
+ def self.pavlicek_ranked_number_to_board(number)
95
+ pavlicek_ranked_or_suited_number_to_board(number, PAVLICEK_RANKED)
96
+ end
97
+
98
+ # Uses the suited Pavlicek method (default)
99
+ def self.pavlicek_suited_number_to_board(number)
100
+ pavlicek_ranked_or_suited_number_to_board(number, PAVLICEK_SUITED)
101
+ end
102
+
103
+ # Algorithm:
104
+ # 1. N=E=S=W=13; C=52; K=D
105
+ # 2. X=K*N/C; If I < X then N=N-1, go to 6
106
+ # 3. I=I-X; X=K*E/C; If I < X then E=E-1, go to 6
107
+ # 4. I=I-X; X=K*S/C; If I < X then S=S-1, go to 6
108
+ # 5. I=I-X; X=K*W/C; W=W-1
109
+ # 6. K=X; C=C-1, loop if not zero to 2
110
+ # Returns a board
111
+ def self.pavlicek_ranked_or_suited_number_to_board(number, ranked_or_suited)
112
+ # Common theme to help with debugging. Define routine name for debug code.
113
+ # Sometimes also define a short routine name.
114
+ # Use a variable to turn debug on or off in the code
115
+ # Similar code through most of this gem
116
+ routine_name = "pavlicek_ranked_or_suited_number_to_board"
117
+ short_routine_name = "prsntb"
118
+ debug_routine = 0
119
+
120
+ # Error check
121
+ if (number <= 0) then
122
+ puts "Error in #{routine_name}. Invalid number #{number}. Must be 1 or higher."
123
+ return nil
124
+ end
125
+
126
+ if (number > HandRecordUtility::D) then
127
+ puts "Error in #{routine_name}. Invalid number #{number} is too big."
128
+ return nil
129
+ end
130
+
131
+ # Using variable names from http://www.rpbridge.net/7z68.htm
132
+ n = 13
133
+ e = 13
134
+ s = 13
135
+ w = 13
136
+ c = 52
137
+ k = D
138
+ # hands[NESW][HCDS][0..12 where 0=A, 1=K, 2=Q, ...12=2]
139
+ hands = Array.new(4) { Array.new(4) { Array.new } }
140
+
141
+ # The algorithm assumes that the space is 0..D-1
142
+ # But the UI assumes 1..D, so decrement by 1.
143
+ i = number - 1
144
+
145
+ (0..51).each do |card_count|
146
+ # if (ranked_or_suited == PAVLICEK_RANKED) then
147
+ # suit = card_count % 4
148
+ # # 0=A, 1=K, 2=Q, .... 12=2
149
+ # rank = (card_count / 4).to_i
150
+ # else
151
+ suit = (card_count / 13).to_i
152
+ # 0=A, 1=K, 2=Q, .... 12=2
153
+ rank = card_count - (suit * 13)
154
+ # end
155
+
156
+ # Make sure do it this way and not
157
+ # x = (k / c) * n
158
+ x = (k * n) / c
159
+ if (i < x) then
160
+ n = n - 1
161
+ dir = DIR_N
162
+ if (debug_routine == 1) then
163
+ puts "#{short_routine_name}: dir=#{dir} suit=#{suit} rank=#{rank} i=#{i} x=#{x} n=#{n} e=#{e} s=#{s} w=#{w}"
164
+ end
165
+ hands[dir][suit] << rank
166
+ else
167
+ i = i - x
168
+ x = (k * e) / c
169
+ if (i < x) then
170
+ e = e - 1
171
+ dir = DIR_E
172
+ if (debug_routine == 1) then
173
+ puts "#{short_routine_name}: dir=#{dir} suit=#{suit} rank=#{rank} i=#{i} x=#{x} n=#{n} e=#{e} s=#{s} w=#{w}"
174
+ end
175
+ hands[DIR_E][suit] << rank
176
+ else
177
+ i = i - x
178
+ x = (k * s) / c
179
+ if (i < x) then
180
+ s = s - 1
181
+ dir = DIR_S
182
+ if (debug_routine == 1) then
183
+ puts "#{short_routine_name}: dir=#{dir} suit=#{suit} rank=#{rank} i=#{i} x=#{x} n=#{n} e=#{e} s=#{s} w=#{w}"
184
+ end
185
+ hands[dir][suit] << rank
186
+ else
187
+ # This is West
188
+ i = i - x
189
+ x = (k * w) / c
190
+ if ((i < x) || ((i == 0) && (x == 0))) then
191
+ w = w - 1
192
+ dir = DIR_W
193
+ if (debug_routine == 1) then
194
+ puts "#{short_routine_name}: dir=#{dir} suit=#{suit} rank=#{rank} i=#{i} x=#{x} n=#{n} e=#{e} s=#{s} w=#{w}"
195
+ end
196
+ hands[dir][suit] << rank
197
+ else
198
+ puts "Error. card_count=#{card_count} i=#{i} x=#{x} k=#{k} c=#{c} n=#{n} e=#{e} s=#{s} w=#{w}"
199
+ end
200
+ end
201
+ end
202
+ end
203
+ k = x
204
+ c = c - 1
205
+ end
206
+
207
+ if (debug_routine == 1) then
208
+ puts "#{routine_name}: end. n=#{n} e=#{e} s=#{s} w=#{w}"
209
+ end
210
+
211
+ return pavlicek_arrays_to_board(hands)
212
+ end
213
+
214
+ # pavlicek_arrays_to_board takes four arrays and returns the entire board.
215
+ def self.pavlicek_arrays_to_board(hands)
216
+ board = Hash.new
217
+ board[:north] = pavlicek_array_to_hand(hands[DIR_N])
218
+ board[:east] = pavlicek_array_to_hand(hands[DIR_E])
219
+ board[:south] = pavlicek_array_to_hand(hands[DIR_S])
220
+ board[:west] = pavlicek_array_to_hand(hands[DIR_W])
221
+ return board
222
+ end
223
+
224
+
225
+ # pav_hand is an array of cards
226
+ # Returns a 17 char string with the hand record.
227
+ def self.pavlicek_array_to_hand(pav_hand_array)
228
+ s = "S" + pavlicek_hand_array_to_suit(pav_hand_array[0])
229
+ s = s + "H" + pavlicek_hand_array_to_suit(pav_hand_array[1])
230
+ s = s + "D" + pavlicek_hand_array_to_suit(pav_hand_array[2])
231
+ s = s + "C" + pavlicek_hand_array_to_suit(pav_hand_array[3])
232
+ return s
233
+ end
234
+
235
+ # Given an array holding cards in a suit, return a string with that suit
236
+ def self.pavlicek_hand_array_to_suit(pav_hand_array)
237
+ routine_name = "pavlicek_hand_array_to_suit"
238
+ s = ""
239
+ return s if (pav_hand_array.nil?)
240
+
241
+ pav_hand_array.each do |card|
242
+ case card
243
+ when 0
244
+ s = s + "A"
245
+ when 1
246
+ s = s + "K"
247
+ when 2
248
+ s = s + "Q"
249
+ when 3
250
+ s = s + "J"
251
+ when 4
252
+ s = s + "T"
253
+ when 5..12
254
+ c = 14 - card
255
+ s = s + c.to_s
256
+ else
257
+ puts "Invalid value in #{routine_name}. card=#{card}"
258
+ # Error
259
+ end
260
+ end
261
+ return s
262
+ end
263
+
264
+ # Converts from a board to a Pavlicek number
265
+ # See http://www.rpbridge.net/7z68.htm
266
+ # Pavlicek ranks the cards in this order:
267
+ # AS, AH, AD, AC, KS.... 2C
268
+ # Algorithm from Richard's web site
269
+ # 1. N=E=S=W=13; C=52; K=D; I=0
270
+ # 2. X=K*N/C; If N card then N=N-1, go to 6
271
+ # 3. I=I+X; X=K*E/C; If E card then E=E-1, go to 6
272
+ # 4. I=I+X; X=K*S/C; If S card then S=S-1, go to 6
273
+ # 5. I=I+X; X=K*W/C; W=W-1
274
+ # 6. K=X; C=C-1, loop if not zero to 2
275
+ def self.to_pavlicek_number(board)
276
+ to_pavlicek_suited_number(board)
277
+ end
278
+
279
+ def self.to_pavlicek_suited_number(board)
280
+ routine_name = "to_pavlicek_suited_number"
281
+ debug_routine = 0
282
+
283
+ if (debug_routine == 1) then
284
+ puts "#{routine_name}: Board=#{board}"
285
+ end
286
+
287
+ pav_array = board_to_pavlicek_array(board, PAVLICEK_SUITED)
288
+ # debug_pavlicek_array(pav_array)
289
+ pav_number = pavlicek_array_to_number(pav_array)
290
+ # debug_pavlicek_array(pav_array)
291
+
292
+ return pav_number
293
+ end
294
+
295
+ # Given a hand, converts to a Pavlicek array
296
+ def self.to_pavlicek_ranked_number(board)
297
+ routine_name = "to_pavlicek_ranked_number"
298
+ debug_routine = 1
299
+
300
+ if (debug_routine == 1) then
301
+ puts "#{routine_name}: Board=#{board}"
302
+ end
303
+
304
+ pav_array = board_to_pavlicek_array(board, PAVLICEK_RANKED)
305
+ pav_number = pavlicek_array_to_number(pav_array)
306
+ # debug_pavlicek_array(pav_array)
307
+
308
+ return pav_number
309
+ end
310
+
311
+ # Given a hand, converts to a Pavlicek array
312
+ # This is a new term I have created.
313
+ # Returns an array [0..51] where 0 is AS, 1 is AH, 2 is AD.... 51 is 2C
314
+ # The values in the array are [0123] (n, e, s, w)
315
+ # In other words, for each card we know which hand it is in
316
+ def self.board_to_pavlicek_array(board, ranked_or_suited)
317
+ # Initialize an array of size 52. Set all values to -1 (as an error check)
318
+ pav_array = Array.new(52, -1)
319
+
320
+ put_hand_in_pavlicek_array(pav_array, board[:north], DIR_N, ranked_or_suited)
321
+ put_hand_in_pavlicek_array(pav_array, board[:east], DIR_E, ranked_or_suited)
322
+ put_hand_in_pavlicek_array(pav_array, board[:south], DIR_S, ranked_or_suited)
323
+ put_hand_in_pavlicek_array(pav_array, board[:west], DIR_W, ranked_or_suited)
324
+ # Return the array
325
+ pav_array
326
+ end
327
+
328
+ # Given a hand in format "SKQH5432DAQJCT954"
329
+ # Fill out the pavlicek array
330
+ def self.put_hand_in_pavlicek_array(pav_array, hand, direction, ranked_or_suited)
331
+ suit = 0
332
+ hand.each_char do |value|
333
+ if ((value == "S") || (value == "H") || (value == "D") || (value == "C")) then
334
+ case value
335
+ when "S"
336
+ suit = 0
337
+ when "H"
338
+ suit = 1
339
+ when "D"
340
+ suit = 2
341
+ when "C"
342
+ suit = 3
343
+ end
344
+ else
345
+ # We have a card in a suit
346
+ card = 14
347
+ case value
348
+ when "A"
349
+ card_value = 0
350
+ when "K"
351
+ card_value = 1
352
+ when "Q"
353
+ card_value = 2
354
+ when "J"
355
+ card_value = 3
356
+ when "T"
357
+ card_value = 4
358
+ else
359
+ # Map "9" -> 4, "8" -> 5, ... "2" -> 12
360
+ card_value = 62 - value.ord # 50=2, 57=9
361
+ # card_value = 14 - card_int
362
+ if (card_value <= 0) then
363
+ puts "Error in card. card_value=#{card_value} value=#{value}"
364
+ end
365
+ end
366
+
367
+ #Error check?
368
+ # AS=0, AH=1, AD=2, AC=3, ... 2C=51
369
+ if (ranked_or_suited == PAVLICEK_RANKED) then
370
+ card = (card_value * 4) + suit
371
+ else
372
+ card = (suit * 13) + card_value
373
+ end
374
+ # card should be 0..51
375
+ # puts "hand=#{hand} value=#{value} card=#{card} direction=#{direction}"
376
+ pav_array[card] = direction
377
+ end
378
+ end
379
+ # No return value
380
+ end
381
+
382
+ # Given a Pavlicek array, convert to a number
383
+ # Using variable names from http://www.rpbridge.net/7z68.htm
384
+ def self.pavlicek_array_to_number(pav_array)
385
+ routine_name = "pavlicek_array_to_number"
386
+ debug_routine = 0
387
+ n = 13
388
+ e = 13
389
+ s = 13
390
+ w = 13
391
+ c = 52
392
+ k = D
393
+ i = 0
394
+ card_count = 1
395
+
396
+ # Go through the 52 cards
397
+ (0..51).each do |card_count|
398
+ hand_has_card = pav_array[card_count]
399
+ if (debug_routine == 1) then
400
+ # Use a shortened routine name
401
+ puts "pa2n: count=#{card_count} hand_has_card=#{hand_has_card} c=#{c} k=#{k} i=#{i} n=#{n} e=#{e} s=#{s} w=#{w}"
402
+ end
403
+
404
+ x = (k * n) / c
405
+ if (hand_has_card == DIR_N) then
406
+ n = n - 1
407
+ else
408
+ i = i + x
409
+ x = (k * e) / c
410
+ if (hand_has_card == DIR_E) then
411
+ e = e - 1
412
+ else
413
+ i = i + x
414
+ x = (k * s) / c
415
+ if (hand_has_card == DIR_S) then
416
+ s = s - 1
417
+ else
418
+ i = i + x
419
+ x = (k * w) / c
420
+ if (hand_has_card == DIR_W) then
421
+ w = w - 1
422
+ else
423
+ # Invalid card
424
+ puts "Invalid direction in HandRecordUtility. direction=#{hand_has_card} count=#{count} value=#{hand_has_card}"
425
+ end
426
+ end
427
+ end
428
+ end
429
+
430
+ if (debug_routine == 1) then
431
+ puts "pa2n: end i=#{i} k=#{k} x=#{x} c=#{c} n=#{n} e=#{e} s=#{s} w=#{s}"
432
+ end
433
+ k = x
434
+ c = c - 1
435
+ end
436
+
437
+ # The algorithm uses 0..D-1,
438
+ # The UI uses 1..D so need to increase by 1
439
+ return i + 1
440
+ end
441
+
442
+ ##############
443
+ # Andrews code
444
+ ##############
445
+ # Convert board to Andrews number.
446
+ # http://bridge.thomasoandrews.com/impossible/algorithm.html
447
+
448
+ def self.pavlicek_array_to_andrews_sequences(pav_array)
449
+ sequences = Array.new( 3 ) { Array.new( 13, -1 ) }
450
+ (0..2).each do |j|
451
+ count = 0
452
+ loc = 0
453
+ (0..51).each do |i|
454
+ if (j == pav_array[i]) then
455
+ sequences[j][loc] = count
456
+ loc = loc + 1
457
+ end
458
+ if (j <= pav_array[i]) then
459
+ count = count + 1
460
+ end
461
+ end
462
+ end
463
+ sequences
464
+ end
465
+
466
+ def self.encode_increasing_sequence(sequence)
467
+ sum = 0
468
+ sequence.each_with_index do |val,index|
469
+ if val > index then
470
+ sum = sum + Combinatorics::Choose.C(val,index+1)
471
+ end
472
+ end
473
+ sum
474
+ end
475
+
476
+ def self.to_andrews_number(board)
477
+ pav_array = board_to_pavlicek_array(board, PAVLICEK_SUITED)
478
+ sequences = pavlicek_array_to_andrews_sequences(pav_array)
479
+
480
+ seqN = encode_increasing_sequence(sequences[0])
481
+ seqE = encode_increasing_sequence(sequences[1])
482
+ seqS = encode_increasing_sequence(sequences[2])
483
+
484
+ # Need to add 1. Internally we use 0..D-1, externally 1..D
485
+ i = seqS + S_MAX * (seqE + (E_MAX * seqN)) + 1
486
+ return i
487
+ end
488
+
489
+ def self.decode_to_increasing_sequence(index)
490
+ length = 13
491
+ result = Array.new( length, -1 )
492
+
493
+ 13.downto(1) do |i|
494
+ last_value = 0
495
+ num = i
496
+ next_value = Combinatorics::Choose.C(num,i)
497
+ while index >= next_value do
498
+ num += 1
499
+ last_value=next_value
500
+ next_value = Combinatorics::Choose.C(num,i)
501
+ end
502
+ result[i-1] = num-1
503
+ index = index - last_value
504
+ end
505
+ result
506
+ end
507
+
508
+ # Convert from an Andrews number to a board
509
+ def self.andrews_number_to_board(number)
510
+ routine_name = "andrews_number_to_board"
511
+
512
+ # Error check
513
+ if (number <= 0) then
514
+ puts "Error in #{routine_name}. Invalid number #{number}. Must be 1 or higher."
515
+ return nil
516
+ end
517
+
518
+ if (number > HandRecordUtility::D) then
519
+ puts "Error in #{routine_name}. Invalid number #{number} is too big."
520
+ return nil
521
+ end
522
+
523
+ # Need to subtract 1 because the UI is 1..D, code is 0..D-1
524
+ number = number - 1
525
+
526
+ # Using variable names from http://www.rpbridge.net/7z68.htm
527
+ s_index = number % S_MAX
528
+ quotient = number / S_MAX
529
+
530
+ e_index = quotient % E_MAX
531
+ n_index = quotient / E_MAX
532
+
533
+ north_sequence = decode_to_increasing_sequence(n_index)
534
+ east_sequence = decode_to_increasing_sequence(e_index)
535
+ south_sequence = decode_to_increasing_sequence(s_index)
536
+
537
+ # initialize all cards to west since anything missing must be there!
538
+
539
+ pav_array = Array.new(52,DIR_W)
540
+
541
+ [[north_sequence,DIR_N],
542
+ [east_sequence,DIR_E],
543
+ [south_sequence,DIR_S]].each do |sequence,direction|
544
+
545
+ hand_loc = 0
546
+ sequence_loc = 0
547
+ pav_array.each_with_index do |val,idx|
548
+ if (val == DIR_W) then
549
+ if (hand_loc == sequence[sequence_loc]) then
550
+ pav_array[idx] = direction
551
+ sequence_loc = sequence_loc + 1
552
+ end
553
+ hand_loc = hand_loc + 1
554
+ end
555
+ end
556
+ end
557
+
558
+ nsew = Array.new(4) { Array.new(4) { Array.new } }
559
+ pav_array.each_with_index do |dir,idx|
560
+ suit = idx / 13
561
+ rank = idx % 13
562
+ nsew[dir][suit].push(rank)
563
+ end
564
+
565
+ pavlicek_arrays_to_board(nsew)
566
+ end
567
+
568
+ ##########
569
+ # Hex code
570
+ ##########
571
+ # Given a number, convert to a 24 character hex string.
572
+ # A number should be a maximum of 96 bits.
573
+ # A hex character has 4 bits, so should be a maximum of 24 characters.
574
+ # This routine does no error checking on the input.
575
+ def self.number_to_hex(i)
576
+ i.to_s(16)
577
+ end
578
+
579
+ # From hex back to int
580
+ # This routine does no error checking on the input.
581
+ def self.hex_to_number(s)
582
+ s.to_i(16)
583
+ end
584
+
585
+ ##########
586
+ # Base 64 code
587
+ ##########
588
+ # From number to character string
589
+ # This routine does no error checking on the input.
590
+ def self.number_to_string_64(i)
591
+ return "0" if (i == 0)
592
+ s = ""
593
+ while (i > 0)
594
+ digit = i % 64
595
+ s = CHAR_TO_BITS[digit] + s
596
+ i = i / 64
597
+ end
598
+ s
599
+ end
600
+
601
+ # Converts the string to an int.
602
+ # This routine does no error checking on the input.
603
+ def self.string_64_to_number(s)
604
+ i = 0
605
+ return i if (s.nil? || s.empty?)
606
+ s.each_char do |c|
607
+ chr = c.ord - 48
608
+ # Validity check
609
+ return -1 if ((chr < 0) || (chr > 77))
610
+ n = BITS_TO_CHAR[chr]
611
+ # Validity check
612
+ return -1 if (n == -1)
613
+ i = (i * 64) + n
614
+ end
615
+ i
616
+ end
617
+
618
+ ##########
619
+ # Debug code
620
+ ##########
621
+ # Debug utility to print values of the pav_array
622
+ def self.debug_pavlicek_array(pav_array)
623
+ (0..4).each do |tens|
624
+ printf "%2d: ", tens
625
+ (0..9).each do |digit|
626
+ num = (tens * 10) + digit
627
+ printf "%d ", pav_array[num]
628
+ end
629
+ puts "\n"
630
+ end
631
+ printf "%2d: %d %d \n", 5, pav_array[50], pav_array[51]
632
+ end
633
+
634
+ # Print a board in short form
635
+ def self.debug_board_short_form(board)
636
+ debug_board_short_form_suit("N", board[:north])
637
+ debug_board_short_form_suit("S", board[:south])
638
+ debug_board_short_form_suit("E", board[:east])
639
+ debug_board_short_form_suit("W", board[:west])
640
+ end
641
+
642
+ # Helper routine to print a hand in short form
643
+ def self.debug_board_short_form_suit(hand_name, hand)
644
+ puts " #{hand_name}: #{hand}"
645
+ end
646
+
647
+ # Print out in format normally associated with bridge journalism
648
+ def self.debug_board(board)
649
+ debug_board_ns(board[:north])
650
+ sw = debug_board_hand_suit(board[:west], "S")
651
+ se = debug_board_hand_suit(board[:east], "S")
652
+ printf "S: %-14s S: %s\n", sw, se
653
+ sw = debug_board_hand_suit(board[:west], "H")
654
+ se = debug_board_hand_suit(board[:east], "H")
655
+ printf "H: %-14s H: %s\n", sw, se
656
+ sw = debug_board_hand_suit(board[:west], "D")
657
+ se = debug_board_hand_suit(board[:east], "D")
658
+ printf "D: %-14s D: %s\n", sw, se
659
+ sw = debug_board_hand_suit(board[:west], "C")
660
+ se = debug_board_hand_suit(board[:east], "C")
661
+ printf "C: %-14s C: %s\n", sw, se
662
+ debug_board_ns(board[:south])
663
+ end
664
+
665
+ # Prints the North or South hand
666
+ def self.debug_board_ns(hand)
667
+ printf " S: %s\n", debug_board_hand_suit(hand, "S")
668
+ printf " H: %s\n", debug_board_hand_suit(hand, "H")
669
+ printf " D: %s\n", debug_board_hand_suit(hand, "D")
670
+ printf " C: %s\n", debug_board_hand_suit(hand, "C")
671
+ end
672
+
673
+ # Returns the cards in a suit for a hand
674
+ # Assumes hand is well formed
675
+ def self.debug_board_hand_suit(hand, suit)
676
+ s = ""
677
+ case suit
678
+ when "S"
679
+ s = hand[/S.*H/]
680
+ s = s[1, s.length - 2]
681
+ when "H"
682
+ s = hand[/H.*D/]
683
+ s = s[1, s.length - 2]
684
+ when "D"
685
+ s = hand[/D.*C/]
686
+ s = s[1, s.length - 2]
687
+ when "C"
688
+ s = hand[/C.*/]
689
+ s = s[1, s.length - 1]
690
+ end
691
+ return s
692
+ end
693
+
694
+
695
+ end
@@ -0,0 +1,3 @@
1
+ module HandRecordUtility
2
+ VERSION = "1.0.2"
3
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :hand_record_utility do
3
+ # # Task goes here
4
+ # end
@@ -0,0 +1,219 @@
1
+ require 'spec_helper'
2
+ require 'hand_record_utility'
3
+
4
+ describe HandRecordUtility do
5
+
6
+ # Set up board
7
+ # The format of a hand should be 17 characters, but we can shorten
8
+ # @board[:north] = "SAKQJT98765432"
9
+ # is the same as
10
+ # @board[:north] = "SAKQJT98765432HDC"
11
+ before do
12
+ @board = Hash.new
13
+ # @board[:north] = "SAKQJT98765432"
14
+ # @board[:east] = "HAKQJT98765432"
15
+ # @board[:south] = "DAKQJT98765432"
16
+ # @board[:west] = "CAKQJT98765432"
17
+ end
18
+
19
+ # Default first test
20
+ # it "should generate a board" do
21
+ # pending "do it"
22
+ # end
23
+
24
+ ###################
25
+ # Pavlicek tests
26
+ ###################
27
+ # Bounds test - success
28
+ it "should test Pavlicek 1" do
29
+ i = 1
30
+ @board[:north] = "SAKQJT98765432"
31
+ @board[:east] = "HAKQJT98765432"
32
+ @board[:south] = "DAKQJT98765432"
33
+ @board[:west] = "CAKQJT98765432"
34
+ expect(HandRecordUtility.to_pavlicek_number(@board)).to eq(i)
35
+ end
36
+
37
+ it "should test Pavlicek 12345678901234567890123456789" do
38
+ i = 12345678901234567890123456789
39
+ @board = HandRecordUtility.pavlicek_number_to_board(i)
40
+ expect(HandRecordUtility.to_pavlicek_number(@board)).to eq(i)
41
+ end
42
+
43
+ # Test for end
44
+ it "should test Pavlicek 53644737765488792839237440000" do
45
+ # D =53644737765488792839237440000
46
+ i = HandRecordUtility::D
47
+ @board[:north] = "CAKQJT98765432"
48
+ @board[:east] = "DAKQJT98765432"
49
+ @board[:south] = "HAKQJT98765432"
50
+ @board[:west] = "SAKQJT98765432"
51
+ j = HandRecordUtility.to_pavlicek_number(@board)
52
+ expect(j).to eq(i)
53
+ end
54
+
55
+ ntimes = 10
56
+ it "should test 10 random Pavlicek numbers converting back and forth" do
57
+ (1..ntimes).each do |count|
58
+ i = rand(HandRecordUtility::D) + 1
59
+ @board = HandRecordUtility.pavlicek_number_to_board(i)
60
+ j = HandRecordUtility.to_pavlicek_number(@board)
61
+ expect(j).to eq(i)
62
+ end
63
+ end
64
+
65
+ # Bounds test - failure
66
+ it "should fail with number 0 and print an error message" do
67
+ i = 0
68
+ @board = HandRecordUtility.pavlicek_number_to_board(i)
69
+ expect(@board).to eq(nil)
70
+ end
71
+
72
+ it "should fail with number D+1 and print an error message" do
73
+ i = HandRecordUtility::D + 1
74
+ @board = HandRecordUtility.pavlicek_number_to_board(i)
75
+ expect(@board).to eq(nil)
76
+ end
77
+
78
+ ###################
79
+ # int<->hex tests
80
+ ###################
81
+ it "should convert 0 to hex 0x0" do
82
+ i = 0
83
+ s = HandRecordUtility.number_to_hex(i)
84
+ expect(s).to eq("0")
85
+ j = HandRecordUtility.hex_to_number(s)
86
+ expect(j).to eq(i)
87
+ end
88
+
89
+ it "should convert 1 to hex 0x1" do
90
+ i = 1
91
+ s = HandRecordUtility.number_to_hex(i)
92
+ expect(s).to eq("1")
93
+ j = HandRecordUtility.hex_to_number(s)
94
+ expect(j).to eq(i)
95
+ end
96
+
97
+ it "should convert 10 to hex a" do
98
+ i = 10
99
+ s = HandRecordUtility.number_to_hex(i)
100
+ j = HandRecordUtility.hex_to_number(s)
101
+ puts "i=#{i} s=#{s} j=#{j}"
102
+ expect(s).to eq "a"
103
+ expect(j).to eq(i)
104
+ end
105
+
106
+ it "should convert 53644737765488792839237440000 to hex 0xad55e315634dda658bf49200" do
107
+ i = 53644737765488792839237440000
108
+ s = HandRecordUtility.number_to_hex(i)
109
+ expect(s).to eq("ad55e315634dda658bf49200")
110
+ j = HandRecordUtility.hex_to_number(s)
111
+ expect(j).to eq(i)
112
+ end
113
+
114
+ ntimes = 10
115
+ it "should convert convert 10 random numbers to/from hex" do
116
+ (1..ntimes).each do |count|
117
+ i = rand(HandRecordUtility::D) + 1
118
+ s = HandRecordUtility.number_to_hex(i)
119
+ j = HandRecordUtility.hex_to_number(s)
120
+ expect(j).to eq(i)
121
+ end
122
+ end
123
+
124
+ ###################
125
+ # int<->string_64 tests
126
+ ###################
127
+ it "should test all characters to map string 64 format" do
128
+ (0..63).each do |number|
129
+ c = HandRecordUtility::CHAR_TO_BITS[number]
130
+ i = HandRecordUtility.string_64_to_number(c)
131
+ expect(number).to eq(i)
132
+ end
133
+ end
134
+
135
+ it "should convert 0 to string 64 0" do
136
+ i = 0
137
+ s = HandRecordUtility.number_to_string_64(i)
138
+ j = HandRecordUtility.string_64_to_number(s)
139
+ expect(s).to eq "0"
140
+ expect(j).to eq(i)
141
+ end
142
+
143
+ it "should convert 1 to string 64 1" do
144
+ i = 1
145
+ s = HandRecordUtility.number_to_string_64(i)
146
+ j = HandRecordUtility.string_64_to_number(s)
147
+ expect(s).to eq "1"
148
+ expect(j).to eq(i)
149
+ end
150
+
151
+ it "should convert 64 to string 64 40" do
152
+ i = 64
153
+ s = HandRecordUtility.number_to_string_64(i)
154
+ j = HandRecordUtility.string_64_to_number(s)
155
+ expect(s).to eq "10"
156
+ expect(j).to eq(i)
157
+ end
158
+
159
+ ntimes = 10
160
+ it "should convert convert 10 random numbers to/from 64 bit string" do
161
+ (1..ntimes).each do |count|
162
+ i = rand(HandRecordUtility::D) + 1
163
+ s = HandRecordUtility.number_to_string_64(i)
164
+ j = HandRecordUtility.string_64_to_number(s)
165
+ expect(j).to eq(i)
166
+ end
167
+ end
168
+
169
+ ###################
170
+ # Andrews tests
171
+ ###################
172
+ # 1 is the same hand record for Andrews as Pavlicek
173
+ # Test edge case
174
+ it "should test Andrews number of 1" do
175
+ i = 1
176
+ @board[:north] = "SAKQJT98765432"
177
+ @board[:east] = "HAKQJT98765432"
178
+ @board[:south] = "DAKQJT98765432"
179
+ @board[:west] = "CAKQJT98765432"
180
+ j = HandRecordUtility.to_andrews_number(@board)
181
+ expect(j).to eq(i)
182
+ end
183
+
184
+ # Test edge case
185
+ it "should test Andrews number of 53644737765488792839237440000" do
186
+ i = 53644737765488792839237440000
187
+ @board[:north] = "CAKQJT98765432"
188
+ @board[:east] = "DAKQJT98765432"
189
+ @board[:south] = "HAKQJT98765432"
190
+ @board[:west] = "SAKQJT98765432"
191
+ j = HandRecordUtility.to_andrews_number(@board)
192
+ expect(j).to eq(i)
193
+ end
194
+
195
+ # Test random cases
196
+ ntimes = 10
197
+ it "should test 10 random Andrew numbers converting back and forth" do
198
+ (1..ntimes).each do |count|
199
+ i = rand(HandRecordUtility::D) + 1
200
+ @board = HandRecordUtility.andrews_number_to_board(i)
201
+ j = HandRecordUtility.to_andrews_number(@board)
202
+ expect(j).to eq(i)
203
+ end
204
+ end
205
+ #
206
+ # Bounds test - failure
207
+ it "should fail with number 0 and print an error message" do
208
+ i = 0
209
+ @board = HandRecordUtility.andrews_number_to_board(i)
210
+ expect(@board).to eq(nil)
211
+ end
212
+
213
+ it "should fail with number D+1 and print an error message" do
214
+ i = HandRecordUtility::D + 1
215
+ @board = HandRecordUtility.andrews_number_to_board(i)
216
+ expect(@board).to eq(nil)
217
+ end
218
+
219
+ end
@@ -0,0 +1,6 @@
1
+ require 'hand_record_utility'
2
+
3
+ RSpec.configure do |config|
4
+ # config.pattern = '**/*_spec'
5
+ # Optional config here.
6
+ end
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hand_record_utility
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Nicolas Hammond
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-01-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: |
42
+ Takes a hand record (4 hands of 13 cards) and converts to a unique number using Richard Pavlicek's algorithm. The reverse also applies. Also works with the Andrews algorithm. The number will be in the range of [1..53644737765488792839237440000].
43
+
44
+ See Richard Pavlicek's web site at http://rpbridge.net/7z68.htm.
45
+
46
+ See Thomas Andrews' web site at http://bridge.thomasoandrews.com/impossible/bin/impossible.cgi.
47
+ email:
48
+ - gems@hammondsoftware.com
49
+ executables: []
50
+ extensions: []
51
+ extra_rdoc_files: []
52
+ files:
53
+ - AUTHORS
54
+ - Gemfile
55
+ - Gemfile.lock
56
+ - MIT-LICENSE
57
+ - README.md
58
+ - Rakefile
59
+ - lib/hand_record_utility.rb
60
+ - lib/hand_record_utility/version.rb
61
+ - lib/tasks/hand_record_utility_tasks.rake
62
+ - spec/hand_record_utility_spec.rb
63
+ - spec/spec_helper.rb
64
+ homepage: https://github.com/njhammond/hand_record_utility
65
+ licenses:
66
+ - MIT
67
+ metadata: {}
68
+ post_install_message:
69
+ rdoc_options: []
70
+ require_paths:
71
+ - lib
72
+ required_ruby_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ required_rubygems_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ requirements: []
83
+ rubyforge_project:
84
+ rubygems_version: 2.3.0
85
+ signing_key:
86
+ specification_version: 4
87
+ summary: Converts a hand record to/from a unique number using various algorithms.
88
+ test_files: []