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.
- checksums.yaml +7 -0
- data/AUTHORS +13 -0
- data/Gemfile +16 -0
- data/Gemfile.lock +32 -0
- data/MIT-LICENSE +20 -0
- data/README.md +115 -0
- data/Rakefile +37 -0
- data/lib/hand_record_utility.rb +695 -0
- data/lib/hand_record_utility/version.rb +3 -0
- data/lib/tasks/hand_record_utility_tasks.rake +4 -0
- data/spec/hand_record_utility_spec.rb +219 -0
- data/spec/spec_helper.rb +6 -0
- metadata +88 -0
checksums.yaml
ADDED
@@ -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
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'
|
data/Gemfile.lock
ADDED
@@ -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
|
data/MIT-LICENSE
ADDED
@@ -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.
|
data/README.md
ADDED
@@ -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
|
+
|
data/Rakefile
ADDED
@@ -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,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
|
data/spec/spec_helper.rb
ADDED
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: []
|