Class: Bridge::TrickPlay

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

Overview

This class models the trick-taking phase of a game of bridge. This code is generalised, and could easily be adapted to support a variety of trick-taking card games.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(declarer, trump_suit) ⇒ TrickPlay

Returns a new instance of TrickPlay.

Parameters:

  • declarer:

    the declarer from the auction.

  • trump_suit:

    the trump suit from the auction.

Raises:

  • (TypeError)


14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/bridge/trick_play.rb', line 14

def initialize(declarer, trump_suit)
  raise TypeError, "Expected Direction, got #{declarer.inspect}" unless Direction[declarer]
  raise TypeError, "Expected Suit, got #{trump_suit.inspect}" if !trump_suit.nil? and Suit[trump_suit].nil?
  
  self.trumps = trump_suit
  self.declarer = declarer
  self.dummy = Direction[(declarer + 2) % 4]
  self.lho = Direction[(declarer + 1) % 4]
  self.rho = Direction[(declarer + 3) % 4]
  # Each trick corresponds to a cross-section of lists.
  self.played = {}
  self.history = []
  Direction.each do |position|
    self.played[position] = []
  end
  self.winners = []  # Winning player of each trick.
end

Instance Attribute Details

#declarerObject

Returns the value of attribute declarer.



6
7
8
# File 'lib/bridge/trick_play.rb', line 6

def declarer
  @declarer
end

#dummyObject

Returns the value of attribute dummy.



6
7
8
# File 'lib/bridge/trick_play.rb', line 6

def dummy
  @dummy
end

#historyObject

Returns the value of attribute history.



6
7
8
# File 'lib/bridge/trick_play.rb', line 6

def history
  @history
end

#lhoObject

Returns the value of attribute lho.



6
7
8
# File 'lib/bridge/trick_play.rb', line 6

def lho
  @lho
end

#playedObject

Returns the value of attribute played.



6
7
8
# File 'lib/bridge/trick_play.rb', line 6

def played
  @played
end

#rhoObject

Returns the value of attribute rho.



6
7
8
# File 'lib/bridge/trick_play.rb', line 6

def rho
  @rho
end

#trumpsObject

Returns the value of attribute trumps.



6
7
8
# File 'lib/bridge/trick_play.rb', line 6

def trumps
  @trumps
end

#winnersObject

Returns the value of attribute winners.



6
7
8
# File 'lib/bridge/trick_play.rb', line 6

def winners
  @winners
end

Instance Method Details

#complete?Boolean

Playing is complete if there are 13 complete tricks. @return: True if playing is complete, False if not.

Returns:

  • (Boolean)


34
35
36
# File 'lib/bridge/trick_play.rb', line 34

def complete?
  self.winners.size == 13
end

#get_current_trickObject

Returns the getTrick() tuple of the current trick. @return: a (leader, cards) trick tuple.



64
65
66
67
68
# File 'lib/bridge/trick_play.rb', line 64

def get_current_trick
  # Index of current trick is length of longest played list minus 1.
  index = [0, (self.played.map { |dir,cards| cards.size }.max - 1)].max
  self.get_trick(index)
end

#get_trick(index) ⇒ Object

A trick is a set of cards, one from each player’s hand. The leader plays the first card, the others play in clockwise order. @param: trick index, in range 0 to 12. @return: a trick object.

Raises:

  • (ArgumentError)


42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/bridge/trick_play.rb', line 42

def get_trick(index)
  raise ArgumentError unless 0 <= index and index < 13
  if index == 0  # First trick.
    leader = self.lho  # Leader is declarer's left-hand opponent.
  else  # Leader is winner of previous trick.
    leader = self.winners[index - 1]
  end
  
  cards = []
  
  Direction.each do |position|
    # If length of list exceeds index value, player's card in trick.
    if self.played[position].size > index
      cards[position] = self.played[position][index]
    end
  end
  
  Trick.new(:leader => leader, :cards => cards)
end

#get_trick_countObject

Returns the number of tricks won by declarer/dummy and by defenders. @return: the declarer trick count, the defender trick count. @rtype: tuple



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/bridge/trick_play.rb', line 73

def get_trick_count
  declarer_count, defender_count = 0, 0
  
  (0..self.winners.size-1).each do |i|
    trick = self.get_trick(i)
    winner = self.who_played?(self.winning_card(trick))
    if [self.declarer, self.dummy].include?(winner)
      declarer_count += 1
    else
      defender_count += 1
    end
  end
  
  [declarer_count, defender_count]
end

#get_tricksObject



89
90
91
92
93
# File 'lib/bridge/trick_play.rb', line 89

def get_tricks
  (0..self.winners.size-1).map do |i|
    self.get_trick(i)
  end
end

#play_card(card, player = nil, hand = nil) ⇒ Object

Plays card to current trick. Card validity should be checked with isValidPlay() beforehand.

Parameters:

  • card:

    the Card object to be played from player’s hand.

  • player:

    the player of card, or None.

  • hand:

    the hand of player, or [].

Raises:

  • (ArgumentError)


100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/bridge/trick_play.rb', line 100

def play_card(card, player=nil, hand=nil)
  Bridge.assert_card(card)
  
  player = player || self.whose_turn
  hand = hand || [card]  # Skip hand check.
  
  raise ArgumentError, 'Not valid play' unless self.valid_play?(card, player, hand)
  self.played[player] << card
  self.history << card
  
  # If trick is complete, determine winner.
  trick = self.get_current_trick
  if trick.cards.compact.size == 4
    winner = self.who_played?(self.winning_card(trick))
    self.winners << winner
  end
  return true
end

#to_aObject



202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
# File 'lib/bridge/trick_play.rb', line 202

def to_a
  trick_counts = self.get_trick_count
  {
    trumps: self.trumps,
    declarer: self.declarer,
    dummy: self.dummy,
    lho: self.lho,
    rho: self.rho,
    played: self.played,
    winners: self.winners,
    declarer_trick_count: trick_counts.first,
    defender_trick_count: trick_counts.last,
    tricks: self.get_tricks
  }
end

#valid_play?(card, player = nil, hand = []) ⇒ Boolean

Card is playable if and only if:

  • Play session is not complete.

  • Direction is on turn to play.

  • Card exists in hand.

  • Card has not been previously played.

In addition, if the current trick has an established lead, then card must follow lead suit OR hand must be void in lead suit. Specification of player and hand are required for verification.

Returns:

  • (Boolean)


127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/bridge/trick_play.rb', line 127

def valid_play?(card, player=nil, hand=[])
  Bridge.assert_card(card)
  
  if self.complete?
    return false
  elsif hand and !hand.include?(card)
    return false  # Playing a card not in hand.
  elsif player and self.whose_turn != self.dummy and player != self.whose_turn
    return false  # Playing out of turn.
  elsif self.who_played?(card)
    return false  # Card played previously.
  end
  trick = self.get_current_trick
  # 0 if start of playing, 4 if complete trick.
  if [0, 4].include?(trick.cards.compact.size)
    return true # Card will be first in next trick.
  else # Current trick has an established lead: check for revoke.
    leadcard = trick.leader_card
    # Cards in hand that match suit of leadcard.
    followers = hand.select { |c| c.suit == leadcard.suit and !self.who_played?(c) }
    # Hand void in lead suit or card follows lead suit.
    return (followers.size == 0 or followers.include?(card))
  end
end

#who_played?(card) ⇒ Boolean

Returns the player who played the specified card. @return: the player who played card.

Parameters:

  • card:

    a Card.

Returns:

  • (Boolean)


155
156
157
158
159
160
161
# File 'lib/bridge/trick_play.rb', line 155

def who_played?(card)
  Bridge.assert_card(card) unless card.nil?
  self.played.each do |player,cards|
    return player if cards.include?(card)
  end
  false
end

#whose_turnObject

If playing is not complete, returns the player who is next to play. @return: the player next to play.



165
166
167
168
169
170
171
172
173
174
175
# File 'lib/bridge/trick_play.rb', line 165

def whose_turn
  unless self.complete?
    trick = self.get_current_trick
    if trick.cards.compact.size == 4  # If trick is complete, trick winner's turn.
      return self.who_played?(self.winning_card(trick))
    else  # Otherwise, turn is next (clockwise) player in trick.
      return Direction[(trick.leader + trick.cards.compact.size) % 4]
    end
  end
  return false
end

#winning_card(trick) ⇒ Object

Determine which card wins the specified trick:

  • In a trump contract, the highest ranked trump card wins.

  • Otherwise, the highest ranked card of the lead suit wins.

@param: a complete (leader, cards) trick tuple. @return: the Card object which wins the trick.



182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/bridge/trick_play.rb', line 182

def winning_card(trick)
  if trick.cards.compact.size == 4  # Trick is complete.
    if self.trumps  # Suit contract.
      trumpcards = trick.cards.compact.select { |c| c.suit_i == self.trumps }
      if trumpcards.size > 0 # Highest ranked trump.
        return trumpcards.max 
      else # we re in trump contract but play didn't have a trump.
        followers = trick.cards.compact.select { |c| c.suit == trick.leader_card.suit }
        return followers.max # Highest ranked card in lead suit.
      end
    else
      # No Trump contract, or no trump cards played.
      followers = trick.cards.compact.select { |c| c.suit == trick.leader_card.suit }
      return followers.max # Highest ranked card in lead suit.
    end
  else
    return false
  end
end