Open LGTrader opened 5 months ago
Hi LGTrader,
Here is a small example solution to your problem:
from endplay import *
d = Deal("N:AJ975.4.987.AQ54 K642.J53.KT52.73 T8.A976.J43.JT62 Q3.KQT82.AQ6.K98")
table = calc_dd_table(d)
# ♣ ♦ ♥ ♠ NT
# N 8 5 4 7 6
# S 8 5 4 7 6
# E 4 7 9 4 5
# W 5 8 9 6 7
# let's see how west makes 9 tricks in hearts
d.first = Player.south
d.trump = Denom.hearts
d1 = d.copy()
sequence = []
while len(d1.curhand) > 0:
# solve board returns (card, tricks) tuples in decreasing number of tricks, so the
# first element is the optimal one. as we don't care about the number of tricks (
# we already worked it out in the double dummy table) or any other cards we discard
# all these values by assigning them to _
(card, _), *_ = solve_board(d1)
# add the optimal card to the play sequence
sequence.append(card)
# play the card so that the next iteration analyses the next player's move
d1.play(card)
print(sequence)
# confirm it is a double dummy sequence
assert all(x == 9 for x in analyse_play(d, sequence))
If you have any questions feel free to let me know :). I'm afraid I only really get time on weekends to help out though!
Dominic, THANKS! Excellent starting example. I rearranged a couple of things to make the output more readable to me and then tested a number of contracts. They all work and they are all executing DD paths to completion to that's great.
Clearly endplay knows whats going on during play, but imagine that where I display the play of each trick that I also wanted to document who lead the first card and who won the trick. How do I gather that information?
I think it would be helpful to add more examples like this to the package.
And I totally understand only having time on weekends. I spent most of my life in that state!
from endplay import *
Watch_Play = False
d = Deal("N:AJ975.4.987.AQ54 K642.J53.KT52.73 T8.A976.J43.JT62 Q3.KQT82.AQ6.K98")
print(d)
print()
table = calc_dd_table(d)
# ♣ ♦ ♥ ♠ NT
# N 8 5 4 7 6
# S 8 5 4 7 6
# E 4 7 9 4 5
# W 5 8 9 6 7
table.pprint()
print()
# let's see how west makes 9 tricks in hearts
d.first = Player.west
d.trump = Denom.hearts
dd_tricks = 8
d1 = d.copy()
sequence = []
while len(d1.curhand) > 0:
# solve board returns (card, tricks) tuples in decreasing number of tricks, so the
# first element is the optimal one. as we don't care about the number of tricks (
# we already worked it out in the double dummy table) or any other cards we discard
# all these values by assigning them to _
(card, _), *_ = solve_board(d1)
# add the optimal card to the play sequence
sequence.append(card)
if Watch_Play:
print(d1.curplayer, ' ', d1.curtrick)
print(card)
# play the card so that the next iteration analyses the next player's move
d1.play(card)
#d1.pprint()
#print()
# Print the original deal
print()
d.pprint()
print()
# Review the card play order
print()
print('Order the cards were played')
print()
for i in range(0,13):
print(sequence[4*i + 0], sequence[4*i + 1], sequence[4*i + 2], sequence[4*i + 3])
# Show that deal d1 is now empty - all cards played
print()
d1.pprint()
print()
#print(sequence)
print()
# Demonstrate that all steps were optimum
print(analyse_play(d, sequence))
# confirm it is a double dummy sequence
assert all(x == dd_tricks for x in analyse_play(d, sequence))
Hi,
No worries! The player on lead to the current trick is always stored in deal.first
(the player who is next to play a card is stored in deal.curplayer
). The player who won the previous trick will be the player who leads the next trick, so you can just check the value of deal.first
after the last card to the trick is played. You could do, for example:
from dataclasses import dataclass
from endplay import *
@dataclass
class DoubleDummyTrick:
cards: list[Card]
first: Player
winner: Player
def __str__(self):
res = []
for i, player in enumerate(Player.iter_from(self.first)):
prefix = "*" if player == self.winner else " "
res += [f"{prefix}{player.abbr}: {self.cards[i]}"]
return " ".join(res)
def get_double_dummy_line(deal: Deal, trump: Denom, declarer: Player):
deal = deal.copy() # copy so we don't mutate the input deal
deal.trump = trump
deal.first = declarer.lho # first card is played by declarer's lho
tricks: list[DoubleDummyTrick] = []
for _ in range(13):
# initialise trick with no cards and a default value for winner which will
# be updated at the end
trick = DoubleDummyTrick([], deal.first, Player.north)
for _ in range(4):
(card, _), *_ = solve_board(deal)
trick.cards.append(card)
deal.play(card)
# the person on lead after the trick is played is the player who won the last trick
trick.winner = deal.first
tricks.append(trick)
return tricks
d = Deal("N:AJ975.4.987.AQ54 K642.J53.KT52.73 T8.A976.J43.JT62 Q3.KQT82.AQ6.K98")
line = get_double_dummy_line(d, Denom.hearts, Player.west)
for trick in line:
print(trick)
# Output:
# *N: ♠A E: ♠2 S: ♠8 W: ♠3
# N: ♠5 *E: ♠K S: ♠T W: ♠Q
# E: ♦2 S: ♦3 *W: ♦Q N: ♦7
# W: ♦6 N: ♦8 *E: ♦K S: ♦4
# E: ♦5 S: ♦J *W: ♦A N: ♦9
# W: ♣8 N: ♣4 E: ♣3 *S: ♣T
# S: ♣2 W: ♣9 *N: ♣Q E: ♣7
# N: ♣A *E: ♥3 S: ♣6 W: ♣K
# E: ♥5 *S: ♥A W: ♥2 N: ♥4
# S: ♥6 W: ♥8 N: ♣5 *E: ♥J
# E: ♠4 S: ♥7 *W: ♥T N: ♠7
# *W: ♥Q N: ♠9 E: ♠6 S: ♥9
# *W: ♥K N: ♠J E: ♦T S: ♣J
Here I've made a function which calculates a double dummy line, the dataclass is just a little convenience class to store the results for each trick with a __str__
method so that it prints nicely, displaying the cards in the order they were played (so the first entry is by the player on lead) and an asterix before the player who wins that trick.
Thanks. That's very informative and addresses an additional question I had with
deal.first = declarer.lho
The example runs fine here so again I think this sort of thing would help newer users of the library if it's in the examples package.
My personal use is to hopefully become better at card play. I'm relatively new to duplicate bridge. My partner says my bidding is better than many of his long-term partners but my card play needs work.
Sorry up front for opening an issue that I would normally ask in a forum so if there's a better way to approach this question please let me know.
I've been learning python and using endplay to possibly improve my duplicate bridge play. One example of using the library that I haven't found or figured out on my own is how to get endplay to show the card play for a specific hand and contract that matches a double dummy result.
Is there an example of this sort of code using endplay?
At a high level I understand that I could make random card choices and then use (I think) analyse_play or solve_board and look to minimize future tricks to the opponent but that feels a little heavy handed so I thought I'd ask before I start writing code.
Thanks for considering this question.