bridge 0.1.4 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|