Class: Bridge::Auction

Inherits:
Object
  • Object
show all
Defined in:
lib/bridge/auction.rb

Overview

The auction (bidding phase) of a game of bridge.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(dealer) ⇒ Auction



12
13
14
15
16
# File 'lib/bridge/auction.rb', line 12

def initialize dealer
  self.dealer = dealer
  self.contract = nil
  self.calls = []
end

Instance Attribute Details

#callsObject

Returns the value of attribute calls.



8
9
10
# File 'lib/bridge/auction.rb', line 8

def calls
  @calls
end

#contractObject

Returns the value of attribute contract.



8
9
10
# File 'lib/bridge/auction.rb', line 8

def contract
  @contract
end

#dealerObject

Returns the value of attribute dealer.



8
9
10
# File 'lib/bridge/auction.rb', line 8

def dealer
  @dealer
end

Instance Method Details

#assert_call(callclass) ⇒ Object



173
174
175
# File 'lib/bridge/auction.rb', line 173

def assert_call callclass
  raise InvalidCallClassError unless [Bid, Pass, Double, Redouble].include?(callclass)
end

#complete?Boolean

Auction is complete if all players have called (ie. 4 or more calls) and the last 3 calls are Pass calls. @return: True if bidding is complete, False if not. @rtype: bool



22
23
24
25
# File 'lib/bridge/auction.rb', line 22

def complete?
  passes = self.calls.last(3).select { |c| c.is_a?(Pass) }.size
  self.calls.size >= 4 and passes == 3
end

#current_bidObject



161
162
163
# File 'lib/bridge/auction.rb', line 161

def current_bid
  get_current_call(Bid)
end

#current_doubleObject



165
166
167
# File 'lib/bridge/auction.rb', line 165

def current_double
  get_current_call(Double)
end

#current_redoubleObject



169
170
171
# File 'lib/bridge/auction.rb', line 169

def current_redouble
  get_current_call(Redouble)
end

#get_contractObject

When the bidding is complete, the contract is the last and highest bid, which may be doubled or redoubled. Hence, the contract represents the “final state” of the bidding. @return: a dict containing the keywords: on the contract, or None.



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/bridge/auction.rb', line 45

def get_contract
  if self.complete? and not self.passed_out?
    bid = self.get_current_call(Bid)
    double = self.get_current_call(Double)
    redouble = self.get_current_call(Redouble)
    declarer_bid = nil
    # Determine partnership.
    caller = self.who_called?(bid)
    partnership = [caller, Direction[(caller + 2) % 4]]
    # Determine declarer.
    self.calls.each do |call|
      if call.is_a?(Bid) and call.strain == bid.strain and partnership.include?(self.who_called?(call))
        declarer_bid = call
        break
      end
    end
              
    {
      :bid => bid,
      :declarer => declarer_bid.nil? ? nil : self.who_called?(declarer_bid),
      :double_by => double.nil? ? nil : self.who_called?(double),
      :redouble_by => redouble.nil? ? nil : self.who_called?(redouble) 
    }
  else
    nil  # Bidding passed out or not complete, no contract.
  end
end

#get_current_call(callclass) ⇒ Object

Returns most recent current call of specified class, or None. @return: most recent call matching type, or None.



148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/bridge/auction.rb', line 148

def get_current_call callclass
  assert_call(callclass)
  
  self.calls.reverse.each do |call|
    if call.is_a?(callclass)
      return call
    elsif call.is_a?(Bid)
      break  # Bids cancel all preceding calls.
    end
  end
  nil
end

#make_call(call, player = nil) ⇒ Object

Appends call from position to the calls list. Please note that call validity should be checked with isValidCall() before calling this method!

Raises:



77
78
79
80
81
82
83
84
85
86
87
# File 'lib/bridge/auction.rb', line 77

def make_call call, player = nil
  assert_call(call.class)
  # Calls must be distinct.
  raise InvalidCallError, "#{call.inspect} is invalid" unless self.valid_call?(call)

  self.calls << call
  if self.complete? and not self.passed_out?
    self.contract = Contract.new(self)
  end
  true
end

#passed_out?Boolean

Auction is passed out if each player has passed on their first turn. In this case, the bidding is complete, but no contract is established. @return: True if bidding is passed out, False if not. @rtype: bool



31
32
33
34
# File 'lib/bridge/auction.rb', line 31

def passed_out?
  passes = calls.select { |c| c.is_a?(Pass) }.size
  self.calls.size == 4 and passes == 4
end

#to_aObject



177
178
179
# File 'lib/bridge/auction.rb', line 177

def to_a
  self.calls
end

#valid_call?(call, position = nil) ⇒ Boolean

Check that call can be made, according to the rules of bidding. @return: True if call is available, False if not.



93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/bridge/auction.rb', line 93

def valid_call? call, position = nil
  # The bidding must not be complete.
  return false if complete?

  # Position's turn to play.
  return false if position and position != whose_turn

  # A pass is always available.
  return true if call.is_a?(Pass)

  # A bid must be greater than the current bid.
  if call.is_a?(Bid)
    return (!current_bid or call > current_bid)
  end

  # Doubles and redoubles only when a bid has been made.
  if current_bid
    bidder = who_called?(current_bid)
  
    # A double must be made on the current bid from opponents,
    # which has not been already doubled by partnership.
    if call.is_a?(Double)
      opposition = [Direction[(whose_turn + 1) % 4], Direction[(whose_turn + 3) % 4]]
      return (opposition.include?(bidder) and !current_double)
  
    # A redouble must be made on the current bid from partnership,
    # which has been doubled by an opponent.
    elsif call.is_a?(Redouble)
      partnership = [whose_turn, Direction[(whose_turn + 2) % 4]]
      return (partnership.include?(bidder) and current_double and !current_redouble)
    end
  end

  false  # Otherwise unavailable.
end

#who_called?(call) ⇒ Boolean

Returns the position from which the specified call was made. @return: the position of the player who made call, or None.

Raises:

  • (ArgumentError)


132
133
134
135
136
# File 'lib/bridge/auction.rb', line 132

def who_called? call
  raise ArgumentError, "#{call.inspect} is not a call" unless call.is_a?(Call)
  return nil unless calls.include?(call) # Call not made by any player.
  Direction[(self.dealer + calls.find_index(call)) % 4]
end

#whose_turnObject

Returns the position from which the next call should be made. @return: the next position to make a call, or None.



140
141
142
143
# File 'lib/bridge/auction.rb', line 140

def whose_turn
  return nil if complete?
  return Direction[(self.dealer + calls.size) % 4]
end