bridge 0.1.4 → 0.2.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 +7 -0
- data/.gitignore +1 -0
- data/.travis.yml +6 -0
- data/Gemfile +2 -3
- data/README.md +56 -0
- data/Rakefile +3 -4
- data/bridge.gemspec +19 -18
- data/lib/bridge.rb +3 -1
- data/lib/bridge/auction.rb +77 -0
- data/lib/bridge/bid.rb +1 -1
- data/lib/bridge/card.rb +2 -2
- data/lib/bridge/constants.rb +42 -24
- data/lib/bridge/deal.rb +5 -10
- data/lib/bridge/play.rb +80 -0
- data/lib/bridge/score.rb +53 -47
- data/lib/bridge/trick.rb +3 -3
- data/lib/bridge/version.rb +1 -1
- data/test/auction_test.rb +133 -0
- data/test/bid_test.rb +135 -0
- data/test/bridge_test.rb +118 -0
- data/test/{test_card.rb → card_test.rb} +12 -12
- data/test/{test_chicago.rb → chicago_test.rb} +11 -11
- data/test/deal_test.rb +225 -0
- data/test/{test_duplicate.rb → duplicate_test.rb} +9 -9
- data/test/helper.rb +3 -6
- data/test/play_test.rb +102 -0
- data/test/score_test.rb +215 -0
- data/test/{test_trick.rb → trick_test.rb} +10 -10
- metadata +80 -73
- data/Gemfile.lock +0 -12
- data/README.rdoc +0 -62
- data/test/test_bid.rb +0 -135
- data/test/test_bridge.rb +0 -62
- data/test/test_deal.rb +0 -225
- data/test/test_score.rb +0 -330
data/lib/bridge/score.rb
CHANGED
@@ -1,41 +1,33 @@
|
|
1
1
|
module Bridge
|
2
2
|
class Score
|
3
|
-
attr_reader :
|
4
|
-
alias :vulnerable? :vulnerable
|
5
|
-
|
6
|
-
# Checks contract with result, i.e. "1NTX-1", "2S=", "6SXX+1"
|
7
|
-
# on Ruby >= 1.9 there are named groups :contract and :result
|
8
|
-
# on Ruby < 1.9 there are: contract on $1 and result on $5
|
9
|
-
if RUBY_VERSION >= "1.9"
|
10
|
-
REGEXP = Regexp.new %q{\A(?<contract>([1-7])([CDHS]|NT)(X{1,2})?)(?<result>=|\+[1-6]|-([1-9]|1[0-3]))\Z}
|
11
|
-
else
|
12
|
-
REGEXP = Regexp.new %q{\A(([1-7])([CDHS]|NT)(X{1,2})?)(=|\+[1-6]|-([1-9]|1[0-3]))\Z}
|
13
|
-
end
|
3
|
+
attr_reader :contract, :vulnerable, :tricks_number
|
14
4
|
|
15
5
|
# Creates new score object
|
16
6
|
#
|
17
7
|
# ==== Example
|
18
|
-
# Bridge::Score.new(
|
19
|
-
def initialize(
|
20
|
-
@contract
|
21
|
-
@
|
22
|
-
|
23
|
-
|
8
|
+
# Bridge::Score.new("7SXXN", "NS", 0)
|
9
|
+
def initialize(contract, vulnerable, tricks)
|
10
|
+
@contract = contract
|
11
|
+
@vulnerable = vulnerable
|
12
|
+
@tricks_number = calculate_tricks(tricks)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Returns all possible contracts with given points
|
16
|
+
def self.with_points(points)
|
17
|
+
all_contracts.select { |contract, result| result == points }.keys.sort
|
24
18
|
end
|
25
19
|
|
26
20
|
# Returns nr of overtricks or undertricks. 0 if contract was made without them
|
27
21
|
def result
|
28
|
-
|
22
|
+
tricks_number - tricks_to_make_contract
|
29
23
|
end
|
30
24
|
|
31
25
|
# Returns string with nr of tricks relative to contract level
|
32
26
|
def result_string
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
else
|
38
|
-
result.to_s
|
27
|
+
case
|
28
|
+
when result > 0 then "+#{result}"
|
29
|
+
when result == 0 then "="
|
30
|
+
when result < 0 then result.to_s
|
39
31
|
end
|
40
32
|
end
|
41
33
|
|
@@ -49,24 +41,44 @@ module Bridge
|
|
49
41
|
made? ? (made_contract_points + overtrick_points + bonus) : undertrick_points
|
50
42
|
end
|
51
43
|
|
52
|
-
# Returns
|
53
|
-
def
|
54
|
-
|
55
|
-
|
44
|
+
# Returns if declarer side is vulnerable
|
45
|
+
def vulnerable?
|
46
|
+
case vulnerable
|
47
|
+
when "BOTH" then true
|
48
|
+
when "NONE" then false
|
49
|
+
else
|
50
|
+
vulnerable.split("").include?(declarer)
|
51
|
+
end
|
56
52
|
end
|
57
53
|
|
58
|
-
|
54
|
+
private
|
55
|
+
|
56
|
+
def contract_bid
|
57
|
+
@contract_bid ||= Bid.new(contract.match(Bridge::CONTRACT_REGEXP)[:bid])
|
58
|
+
end
|
59
|
+
|
60
|
+
def declarer
|
61
|
+
@declarer ||= contract.match(Bridge::CONTRACT_REGEXP)[:direction]
|
62
|
+
end
|
59
63
|
|
60
64
|
def tricks_to_make_contract
|
61
|
-
|
65
|
+
@tricks_to_make_contract ||= contract_bid.level.to_i + 6
|
62
66
|
end
|
63
67
|
|
64
68
|
def doubled?
|
65
|
-
|
69
|
+
modifier == 2
|
66
70
|
end
|
67
71
|
|
68
72
|
def redoubled?
|
69
|
-
|
73
|
+
modifier == 4
|
74
|
+
end
|
75
|
+
|
76
|
+
def modifier
|
77
|
+
case contract.match(Bridge::CONTRACT_REGEXP)[:modifier]
|
78
|
+
when nil then 1
|
79
|
+
when "X" then 2
|
80
|
+
when "XX" then 4
|
81
|
+
end
|
70
82
|
end
|
71
83
|
|
72
84
|
def bonus
|
@@ -84,7 +96,7 @@ module Bridge
|
|
84
96
|
end
|
85
97
|
|
86
98
|
def grand_slam_bonus
|
87
|
-
if made? and
|
99
|
+
if made? and contract_bid.grand_slam?
|
88
100
|
vulnerable? ? 1500 : 1000
|
89
101
|
else
|
90
102
|
0
|
@@ -92,7 +104,7 @@ module Bridge
|
|
92
104
|
end
|
93
105
|
|
94
106
|
def small_slam_bonus
|
95
|
-
if made? and
|
107
|
+
if made? and contract_bid.small_slam?
|
96
108
|
vulnerable? ? 750 : 500
|
97
109
|
else
|
98
110
|
0
|
@@ -108,11 +120,11 @@ module Bridge
|
|
108
120
|
end
|
109
121
|
|
110
122
|
def first_trick_points
|
111
|
-
|
123
|
+
contract_bid.no_trump? ? 40 : single_trick_points
|
112
124
|
end
|
113
125
|
|
114
126
|
def single_trick_points
|
115
|
-
|
127
|
+
contract_bid.minor? ? 20 : 30
|
116
128
|
end
|
117
129
|
|
118
130
|
def undertrick_points
|
@@ -120,7 +132,7 @@ module Bridge
|
|
120
132
|
end
|
121
133
|
|
122
134
|
def made_contract_points
|
123
|
-
first_trick_points *
|
135
|
+
first_trick_points * modifier + (contract_bid.level.to_i - 1) * single_trick_points * modifier
|
124
136
|
end
|
125
137
|
|
126
138
|
def overtrick_points
|
@@ -136,7 +148,7 @@ module Bridge
|
|
136
148
|
# TODO: do some refactoring
|
137
149
|
def vulnerable_undertrick_points
|
138
150
|
if !made?
|
139
|
-
p = -100 *
|
151
|
+
p = -100 * modifier
|
140
152
|
if result < -1
|
141
153
|
return p += (result + 1) * 300 if doubled?
|
142
154
|
return p += (result + 1) * 600 if redoubled?
|
@@ -150,7 +162,7 @@ module Bridge
|
|
150
162
|
|
151
163
|
def not_vulnerable_undertrick_points
|
152
164
|
if !made?
|
153
|
-
p = -50 *
|
165
|
+
p = -50 * modifier
|
154
166
|
if [-3, -2].include?(result)
|
155
167
|
return p += (result + 1) * 200 if doubled?
|
156
168
|
return p += (result + 1) * 400 if redoubled?
|
@@ -180,21 +192,15 @@ module Bridge
|
|
180
192
|
end
|
181
193
|
end
|
182
194
|
|
183
|
-
def split_contract(contract)
|
184
|
-
contract = contract.gsub(/(X+)/, "")
|
185
|
-
modifier = $1.nil? ? 1 : $1.to_s.size * 2
|
186
|
-
[Bridge::Bid.new(contract), modifier]
|
187
|
-
end
|
188
|
-
|
189
195
|
def self.all_contracts
|
190
196
|
result = {}
|
191
197
|
contracts = %w(1 2 3 4 5 6 7).inject([]) do |bids, level|
|
192
198
|
bids += ["H/S", "C/D", "NT"].map { |suit| level + suit }
|
193
199
|
end
|
194
200
|
(contracts + contracts.map { |c| c + "X" } + contracts.map { |c| c + "XX" } ).each do |contract|
|
195
|
-
[
|
201
|
+
["BOTH", "NONE"].each do |vulnerable|
|
196
202
|
(0..13).each do |tricks|
|
197
|
-
score = new(
|
203
|
+
score = new(contract.sub("H/S", "S").sub("C/D", "C").sub("NT", "NT") + "N", vulnerable, tricks)
|
198
204
|
result[contract + score.result_string + (score.vulnerable? ? "v" : "")] = score.points
|
199
205
|
end
|
200
206
|
end
|
data/lib/bridge/trick.rb
CHANGED
@@ -8,11 +8,11 @@ module Bridge
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def winner(trump = nil)
|
11
|
-
winner_in_suit(trump) || winner_in_suit(
|
11
|
+
winner_in_suit(trump) || winner_in_suit(suit)
|
12
12
|
end
|
13
13
|
|
14
14
|
def complete?
|
15
|
-
|
15
|
+
cards.size == 4
|
16
16
|
end
|
17
17
|
|
18
18
|
def incomplete?
|
@@ -22,7 +22,7 @@ module Bridge
|
|
22
22
|
private
|
23
23
|
|
24
24
|
def winner_in_suit(suit)
|
25
|
-
|
25
|
+
cards.select { |c| c.suit == suit }.max
|
26
26
|
end
|
27
27
|
end
|
28
28
|
end
|
data/lib/bridge/version.rb
CHANGED
@@ -0,0 +1,133 @@
|
|
1
|
+
require "helper"
|
2
|
+
|
3
|
+
describe Bridge::Auction do
|
4
|
+
it "raises on invalid bids" do
|
5
|
+
assert_raises ArgumentError do
|
6
|
+
Bridge::Auction.new("N", ["8NT"])
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "directions" do
|
11
|
+
it "returns empty array when no bids" do
|
12
|
+
auction = Bridge::Auction.new("N", [])
|
13
|
+
assert_equal [], auction.directions
|
14
|
+
end
|
15
|
+
|
16
|
+
it "returns directions" do
|
17
|
+
auction = Bridge::Auction.new("N", ["1NT", "PASS", "PASS", "X", "PASS"])
|
18
|
+
assert_equal ["N", "E", "S", "W", "N"], auction.directions
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "#next_direction" do
|
23
|
+
it "returns next direction" do
|
24
|
+
auction = Bridge::Auction.new("N", ["1NT", "PASS", "PASS", "X", "PASS"])
|
25
|
+
assert_equal "E", auction.next_direction
|
26
|
+
end
|
27
|
+
|
28
|
+
it "returns dealer direction when no bids" do
|
29
|
+
auction = Bridge::Auction.new("S", [])
|
30
|
+
assert_equal "S", auction.next_direction
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "#finished?" do
|
35
|
+
it "returns false when not finished" do
|
36
|
+
refute Bridge::Auction.new("N", ["2NT"]).finished?
|
37
|
+
end
|
38
|
+
|
39
|
+
it "returns true when finished" do
|
40
|
+
assert Bridge::Auction.new("N", ["2NT", "PASS", "PASS", "PASS"]).finished?
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "#contract" do
|
45
|
+
it "returns nil when no contract" do
|
46
|
+
assert_nil Bridge::Auction.new("N", ["PASS"]).contract
|
47
|
+
end
|
48
|
+
|
49
|
+
it "returns current contract" do
|
50
|
+
assert_equal "2SN", Bridge::Auction.new("N", ["2S", "PASS"]).contract
|
51
|
+
end
|
52
|
+
|
53
|
+
it "returns current contract with modifier" do
|
54
|
+
assert_equal "2SXN", Bridge::Auction.new("N", ["2S", "X", "PASS"]).contract
|
55
|
+
end
|
56
|
+
|
57
|
+
it "returns current contract without modifier" do
|
58
|
+
assert_equal "2NTS", Bridge::Auction.new("N", ["2S", "X", "2NT", "PASS"]).contract
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe "#declarer" do
|
63
|
+
it "returns declarer direction" do
|
64
|
+
assert_equal "W", Bridge::Auction.new("E", ["2S", "X", "2NT", "PASS", "PASS", "PASS"]).declarer
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe "#bid_allowed?" do
|
69
|
+
it "returns false if finished" do
|
70
|
+
refute Bridge::Auction.new("N", ["1NT", "PASS", "PASS", "PASS"]).bid_allowed?("PASS")
|
71
|
+
end
|
72
|
+
|
73
|
+
it "returns true if PASS" do
|
74
|
+
assert Bridge::Auction.new("N", ["1NT", "PASS", "PASS"]).bid_allowed?("PASS")
|
75
|
+
end
|
76
|
+
|
77
|
+
it "returns false if bid is lower than previous" do
|
78
|
+
refute Bridge::Auction.new("N", ["1NT", "PASS"]).bid_allowed?("1S")
|
79
|
+
end
|
80
|
+
|
81
|
+
it "returns true if bid is higher than previous" do
|
82
|
+
assert Bridge::Auction.new("N", ["1NT", "PASS", "X"]).bid_allowed?("2C")
|
83
|
+
end
|
84
|
+
|
85
|
+
it "returns true if bid is first contract" do
|
86
|
+
assert Bridge::Auction.new("N", ["PASS"]).bid_allowed?("2C")
|
87
|
+
end
|
88
|
+
|
89
|
+
it "returns true if bid is double on previous contract" do
|
90
|
+
assert Bridge::Auction.new("N", ["1C"]).bid_allowed?("X")
|
91
|
+
end
|
92
|
+
|
93
|
+
it "returns true if bid is double on previous 2 bids contract" do
|
94
|
+
assert Bridge::Auction.new("N", ["1C", "PASS", "PASS"]).bid_allowed?("X")
|
95
|
+
end
|
96
|
+
|
97
|
+
it "returns false if bid is double on partner contract" do
|
98
|
+
refute Bridge::Auction.new("N", ["1C", "PASS"]).bid_allowed?("X")
|
99
|
+
end
|
100
|
+
|
101
|
+
it "returns false if bid is double on PASS" do
|
102
|
+
refute Bridge::Auction.new("N", ["PASS"]).bid_allowed?("X")
|
103
|
+
end
|
104
|
+
|
105
|
+
it "returns false if bid is double on doubled contract" do
|
106
|
+
refute Bridge::Auction.new("N", ["1C", "X", "PASS"]).bid_allowed?("X")
|
107
|
+
end
|
108
|
+
|
109
|
+
it "returns true if bid is redouble on doubled previous contract" do
|
110
|
+
assert Bridge::Auction.new("N", ["1C", "X"]).bid_allowed?("XX")
|
111
|
+
end
|
112
|
+
|
113
|
+
it "returns true if bid is redouble on 2 bids previous dobuled contract" do
|
114
|
+
assert Bridge::Auction.new("N", ["1C", "X", "PASS", "PASS"]).bid_allowed?("XX")
|
115
|
+
end
|
116
|
+
|
117
|
+
it "returns false if bid is redouble on partners double" do
|
118
|
+
refute Bridge::Auction.new("N", ["1C", "X", "PASS"]).bid_allowed?("XX")
|
119
|
+
end
|
120
|
+
|
121
|
+
it "returns false if bid is redouble on not doubled contract" do
|
122
|
+
refute Bridge::Auction.new("N", ["1C", "PASS", "PASS"]).bid_allowed?("XX")
|
123
|
+
end
|
124
|
+
|
125
|
+
it "returns false if bid is redouble on redoubled contract" do
|
126
|
+
refute Bridge::Auction.new("N", ["1C", "X", "XX", "PASS"]).bid_allowed?("XX")
|
127
|
+
end
|
128
|
+
|
129
|
+
it "returns true if 2 PASS in the auction" do
|
130
|
+
assert Bridge::Auction.new("N", ["PASS", "1D", "PASS", "PASS", "1H"]).bid_allowed?("PASS")
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
data/test/bid_test.rb
ADDED
@@ -0,0 +1,135 @@
|
|
1
|
+
require "helper"
|
2
|
+
|
3
|
+
describe Bridge::Bid do
|
4
|
+
it "pas is not a valid bid" do
|
5
|
+
assert_raises(ArgumentError) do
|
6
|
+
Bridge::Bid.new("pas")
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
it "case doesn't matter in bid" do
|
11
|
+
assert_equal "PASS", Bridge::Bid.new("pass").to_s
|
12
|
+
assert_equal "X", Bridge::Bid.new("x").to_s
|
13
|
+
assert_equal "XX", Bridge::Bid.new("xx").to_s
|
14
|
+
assert_equal "1NT", Bridge::Bid.new("1nt").to_s
|
15
|
+
end
|
16
|
+
|
17
|
+
it "pass is a valid bid" do
|
18
|
+
bid = Bridge::Bid.new("PASS")
|
19
|
+
assert bid.pass?
|
20
|
+
refute bid.double?
|
21
|
+
refute bid.redouble?
|
22
|
+
refute bid.modifier?
|
23
|
+
refute bid.contract?
|
24
|
+
assert_nil bid.level
|
25
|
+
assert_nil bid.suit
|
26
|
+
end
|
27
|
+
|
28
|
+
it "double is a valid bid" do
|
29
|
+
bid = Bridge::Bid.new("X")
|
30
|
+
refute bid.pass?
|
31
|
+
assert bid.double?
|
32
|
+
refute bid.redouble?
|
33
|
+
assert bid.modifier?
|
34
|
+
refute bid.contract?
|
35
|
+
assert_nil bid.level
|
36
|
+
assert_nil bid.suit
|
37
|
+
end
|
38
|
+
|
39
|
+
it "redouble is a valid bid" do
|
40
|
+
bid = Bridge::Bid.new("XX")
|
41
|
+
refute bid.pass?
|
42
|
+
refute bid.double?
|
43
|
+
assert bid.redouble?
|
44
|
+
assert bid.modifier?
|
45
|
+
refute bid.contract?
|
46
|
+
assert_nil bid.level
|
47
|
+
assert_nil bid.suit
|
48
|
+
end
|
49
|
+
|
50
|
+
it "1H is a valid bid" do
|
51
|
+
bid = Bridge::Bid.new("1H")
|
52
|
+
refute bid.pass?
|
53
|
+
refute bid.double?
|
54
|
+
refute bid.redouble?
|
55
|
+
refute bid.modifier?
|
56
|
+
assert bid.contract?
|
57
|
+
assert_equal "1", bid.level
|
58
|
+
assert_equal "H", bid.suit
|
59
|
+
end
|
60
|
+
|
61
|
+
it "7NT is a valid bid" do
|
62
|
+
bid = Bridge::Bid.new("7NT")
|
63
|
+
refute bid.pass?
|
64
|
+
refute bid.double?
|
65
|
+
refute bid.redouble?
|
66
|
+
refute bid.modifier?
|
67
|
+
assert bid.contract?
|
68
|
+
assert_equal "7", bid.level
|
69
|
+
assert_equal "NT", bid.suit
|
70
|
+
end
|
71
|
+
|
72
|
+
it "7NT is greater than 1C" do
|
73
|
+
assert Bridge::Bid.new("7NT") > Bridge::Bid.new("1C")
|
74
|
+
refute Bridge::Bid.new("7NT") < Bridge::Bid.new("1C")
|
75
|
+
refute Bridge::Bid.new("7NT") == Bridge::Bid.new("1C")
|
76
|
+
end
|
77
|
+
|
78
|
+
it "1S is greater than 1H" do
|
79
|
+
assert Bridge::Bid.new("1S") > Bridge::Bid.new("1H")
|
80
|
+
refute Bridge::Bid.new("1S") < Bridge::Bid.new("1H")
|
81
|
+
refute Bridge::Bid.new("1S") == Bridge::Bid.new("1H")
|
82
|
+
end
|
83
|
+
|
84
|
+
it "comparison of PASS and 1S raises an error" do
|
85
|
+
assert_raises(ArgumentError) do
|
86
|
+
Bridge::Bid.new("PASS") > Bridge::Bid.new("1S")
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
it "comparison of X and PASS raises an error" do
|
91
|
+
assert_raises(ArgumentError) do
|
92
|
+
Bridge::Bid.new("X") > Bridge::Bid.new("PASS")
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
it "PASS and XX are not equal" do
|
97
|
+
refute_equal Bridge::Bid.new("PASS"), Bridge::Bid.new("XX")
|
98
|
+
end
|
99
|
+
|
100
|
+
it "1S returns S trump" do
|
101
|
+
assert_equal "S", Bridge::Bid.new("1S").trump
|
102
|
+
end
|
103
|
+
|
104
|
+
it "5NT returns nil trump" do
|
105
|
+
assert_nil Bridge::Bid.new("5NT").trump
|
106
|
+
end
|
107
|
+
|
108
|
+
it "1H is a major bid" do
|
109
|
+
assert Bridge::Bid.new("1H").major?
|
110
|
+
end
|
111
|
+
|
112
|
+
it "5S is a major bid" do
|
113
|
+
assert Bridge::Bid.new("5S").major?
|
114
|
+
end
|
115
|
+
|
116
|
+
it "2C is a minor bid" do
|
117
|
+
assert Bridge::Bid.new("2C").minor?
|
118
|
+
end
|
119
|
+
|
120
|
+
it "6D is a minor bid" do
|
121
|
+
assert Bridge::Bid.new("6D").minor?
|
122
|
+
end
|
123
|
+
|
124
|
+
it "1NT is a nt bid" do
|
125
|
+
assert Bridge::Bid.new("1NT").nt?
|
126
|
+
end
|
127
|
+
|
128
|
+
it "6NT is a small slam" do
|
129
|
+
assert Bridge::Bid.new("6NT").small_slam?
|
130
|
+
end
|
131
|
+
|
132
|
+
it "7NT is a grand slam" do
|
133
|
+
assert Bridge::Bid.new("7NT").grand_slam?
|
134
|
+
end
|
135
|
+
end
|