Esempio n. 1
0
    def setup_mode(self):

        # Sets up all of the structures that depend on the mode of Hokm
        # we're playing: seats, layouts, and (in 4p mode) partnerships.
        # Remember that seats in Hokm go the opposite direction of the
        # American/European standard.

        if self.mode == 4:

            self.seats = [
                Seat("North"),
                Seat("West"),
                Seat("South"),
                Seat("East"),
            ]

            self.seats[0].data.who = NORTH
            self.seats[1].data.who = WEST
            self.seats[2].data.who = SOUTH
            self.seats[3].data.who = EAST

            self.min_players = 4
            self.max_players = 4
            self.layout = FourPlayerCardGameLayout()

            # Set up the partnership structures.
            self.ns = Struct()
            self.ew = Struct()
            self.ns.score = 0
            self.ew.score = 0

        elif self.mode == 3:

            self.seats = [
                Seat("West"),
                Seat("South"),
                Seat("East"),
            ]

            self.seats[0].data.who = WEST
            self.seats[1].data.who = SOUTH
            self.seats[2].data.who = EAST

            self.west = self.seats[0]
            self.south = self.seats[1]
            self.east = self.seats[2]
            self.west.data.score = 0
            self.south.data.score = 0
            self.east.data.score = 0

            self.min_players = 3
            self.max_players = 3
            self.layout = ThreePlayerCardGameLayout()

        else:
            self.log_pre(
                "MAJOR ERROR: Hokm initialization with invalid mode %s!" %
                self.mode)
Esempio n. 2
0
    def __init__(self, server, table_name):

        super(FortyOne, self).__init__(server, table_name)

        self.game_display_name = "Forty-One"
        self.game_name = "fortyone"

        # Seat ordering is "Persian."
        self.seats = [
            Seat("North"),
            Seat("West"),
            Seat("South"),
            Seat("East"),
        ]

        self.min_players = 4
        self.max_players = 4
        self.state = State("need_players")
        self.prefix = "(^RForty-One^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name, self.game_display_name)

        # 41-specific stuff.
        self.goal = 41
        self.double = 7
        self.minimum = 11
        self.whist = False
        self.positive = True
        self.trick = None
        self.trump_suit = None
        self.led_suit = None
        self.turn = None
        self.dealer = None
        self.winner = None

        self.north = self.seats[0]
        self.west = self.seats[1]
        self.south = self.seats[2]
        self.east = self.seats[3]

        self.north.data.who = NORTH
        self.west.data.who = WEST
        self.south.data.who = SOUTH
        self.east.data.who = EAST

        self.north.data.partner = self.south
        self.west.data.partner = self.east
        self.south.data.partner = self.north
        self.east.data.partner = self.west

        self.layout = FourPlayerCardGameLayout()

        # Set everyone's score to zero.
        for seat in self.seats:
            seat.data.score = 0
Esempio n. 3
0
    def __init__(self, server, table_name):

        super(Whist, self).__init__(server, table_name)

        self.game_display_name = "Whist"
        self.game_name = "whist"
        self.seats = [
            Seat("North"),
            Seat("East"),
            Seat("South"),
            Seat("West"),
        ]

        self.min_players = 4
        self.max_players = 4
        self.state = State("need_players")
        self.prefix = "(^RWhist^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name,
                                       self.game_display_name)

        # Whist-specific guff.
        self.ns = Struct()
        self.ns.score = 0
        self.ew = Struct()
        self.ew.score = 0
        self.seats[0].data.who = NORTH
        self.seats[1].data.who = EAST
        self.seats[2].data.who = SOUTH
        self.seats[3].data.who = WEST

        self.goal = 5
        self.trick = None
        self.trump_suit = None
        self.led_suit = None
        self.turn = None
        self.dealer = None
        self.winner = None

        self.layout = FourPlayerCardGameLayout()
Esempio n. 4
0
    def __init__(self, server, table_name):

        super(Whist, self).__init__(server, table_name)

        self.game_display_name = "Whist"
        self.game_name = "whist"
        self.seats = [
            Seat("North"),
            Seat("East"),
            Seat("South"),
            Seat("West"),
        ]

        self.min_players = 4
        self.max_players = 4
        self.state = State("need_players")
        self.prefix = "(^RWhist^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name, self.game_display_name)

        # Whist-specific guff.
        self.ns = Struct()
        self.ns.score = 0
        self.ew = Struct()
        self.ew.score = 0
        self.seats[0].data.who = NORTH
        self.seats[1].data.who = EAST
        self.seats[2].data.who = SOUTH
        self.seats[3].data.who = WEST

        self.goal = 5
        self.trick = None
        self.trump_suit = None
        self.led_suit = None
        self.turn = None
        self.dealer = None
        self.winner = None

        self.layout = FourPlayerCardGameLayout()
Esempio n. 5
0
class Whist(SeatedGame):
    """A Whist game table implementation.  Whist came about sometime in the
    18th century.  This implementation does not (currently) score honours,
    because honours are boring.
    """

    def __init__(self, server, table_name):

        super(Whist, self).__init__(server, table_name)

        self.game_display_name = "Whist"
        self.game_name = "whist"
        self.seats = [
            Seat("North"),
            Seat("East"),
            Seat("South"),
            Seat("West"),
        ]

        self.min_players = 4
        self.max_players = 4
        self.state = State("need_players")
        self.prefix = "(^RWhist^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name, self.game_display_name)

        # Whist-specific guff.
        self.ns = Struct()
        self.ns.score = 0
        self.ew = Struct()
        self.ew.score = 0
        self.seats[0].data.who = NORTH
        self.seats[1].data.who = EAST
        self.seats[2].data.who = SOUTH
        self.seats[3].data.who = WEST

        self.goal = 5
        self.trick = None
        self.trump_suit = None
        self.led_suit = None
        self.turn = None
        self.dealer = None
        self.winner = None

        self.layout = FourPlayerCardGameLayout()

    def show_help(self, player):

        super(Whist, self).show_help(player)
        player.tell_cc("\nWHIST SETUP PHASE:\n\n")
        player.tell_cc("          ^!setup^., ^!config^., ^!conf^.     Enter setup phase.\n")
        player.tell_cc("            ^!goal^. <num>, ^!score^.     Set the goal score to <num>.\n")
        player.tell_cc("            ^!ready^., ^!done^., ^!r^., ^!d^.     End setup phase.\n")
        player.tell_cc("\nWHIST PLAY:\n\n")
        player.tell_cc("              ^!play^. <card>, ^!pl^.     Play <card> from your hand.\n")
        player.tell_cc("                 ^!hand^., ^!inv^., ^!i^.     Look at the cards in your hand.\n")

    def display(self, player):

        player.tell_cc("%s" % self.layout)

    def get_score_str(self):
        return "          ^RNorth/South^~: %d    ^MEast/West^~: %d\n" % (self.ns.score, self.ew.score)

    def get_color_code(self, seat):

        if seat == self.seats[0] or seat == self.seats[2]:
            return "^R"
        else:
            return "^M"

    def get_sp_str(self, seat):

        return "^G%s^~ (%s%s^~)" % (seat.player_name, self.get_color_code(seat), seat)

    def get_metadata(self):

        to_return = "\n\n"
        if self.turn:
            to_return += "It is ^Y%s^~'s turn (%s%s^~).  Trumps are ^C%s^~.\n" % (self.turn.player_name, self.get_color_code(self.turn), self.turn, self.trump_suit)
            to_return += "Tricks:   ^RNorth/South^~: %d    ^MEast/West^~: %d\n" % (self.ns.tricks, self.ew.tricks)
        to_return += "The goal score for this game is ^C%s^~.\n" % get_plural_str(self.goal, "point")
        to_return += self.get_score_str()

        return to_return

    def show(self, player):
        self.display(player)
        player.tell_cc(self.get_metadata())

    def set_goal(self, player, goal_str):

        if not goal_str.isdigit():
            self.tell_pre(player, "You didn't even send a number!\n")
            return False

        new_goal = int(goal_str)
        if new_goal < 1:
            self.tell_pre(player, "The goal must be at least one point.\n")
            return False

        # Got a valid goal.
        self.goal = new_goal
        self.bc_pre("^M%s^~ has changed the goal to ^G%s^~.\n" % (player, get_plural_str(new_goal, "point")))

    def clear_trick(self):

        # Set the current trick to an empty hand...
        self.trick = Hand()
        self.led_suit = None

        # ...and set everyone's played card to None.
        for seat in self.seats:
            seat.data.card = None

        # Clear the layout as well.
        self.layout.clear()

    def new_deal(self):

        dealer_name = self.dealer.player_name

        self.bc_pre("^R%s^~ (%s%s^~) gives the cards a good shuffle...\n" % (dealer_name, self.get_color_code(self.dealer), self.dealer))
        deck = new_deck()
        deck.shuffle()

        # Deal out all of the cards.  We'll flip the last one; that determines
        # the trump suit for the hand.
        self.bc_pre("^R%s^~ deals the cards out to all the players.\n" % dealer_name)
        for seat in self.seats:
            seat.data.hand = Hand()
        for i in range(13):
            for seat in self.seats:
                seat.data.hand.add(deck.discard())

        # Flip the dealer's last card; it determines the trump suit.
        last_card = self.dealer.data.hand[-1]
        self.bc_pre("^R%s^~ flips their last card; it is ^C%s^~.\n" % (dealer_name,
           card_to_str(last_card, LONG)))
        self.trump_suit = last_card.suit

        # Sort everyone's hands.
        for seat in self.seats:
            seat.data.hand = sorted_hand(seat.data.hand, self.trump_suit)

        # Show everyone their hands.
        self.show_hands()

        # Set the trick counts to zero.
        self.ns.tricks = 0
        self.ew.tricks = 0

    def show_hand(self, player):

        seat = self.get_seat_of_player(player)

        if not seat:
            self.tell_pre(player, "You're not playing!\n")
            return

        print_str = "Your current hand:\n   "
        print_str += hand_to_str(seat.data.hand, self.trump_suit)
        print_str += "\n"
        self.tell_pre(player, print_str)

    def show_hands(self):

        for seat in self.seats:
            if seat.player:
                self.show_hand(seat.player)

    def play(self, player, play_str):

        seat = self.get_seat_of_player(player)
        if not seat:
            self.tell_pre(player, "You're not playing!\n")
            return False

        elif seat != self.turn:
            self.tell_pre(player, "It's not your turn!\n")
            return False

        # Translate the play string into an actual card.
        potential_card = str_to_card(play_str)

        if not potential_card:
            self.tell_pre(player, "That's not a valid card!\n")
            return False

        # Do they even have this card?
        if potential_card not in seat.data.hand:
            self.tell_pre(player, "You don't have that card!\n")
            return False

        # Okay, it's a card in their hand.  First, let's do the "follow the
        # led suit" business.
        action_str = "^Wplays^~"
        if self.led_suit:

            this_suit = potential_card.suit
            if (this_suit != self.led_suit and
               hand_has_suit(seat.data.hand, self.led_suit)):

                # You can't play off-suit if you can match the led suit.
                self.tell_pre(player, "You can't throw off; you have the led suit.\n")
                return False

        else:

            # No led suit; they're the leader.
            action_str = "^Yleads^~ with"
            self.led_suit = potential_card.suit

        # They either matched the led suit, didn't have any of it, or they
        # are themselves the leader.  Nevertheless, their play is valid.
        seat.data.card = potential_card
        self.trick.add(seat.data.hand.discard_specific(potential_card))
        trump_str = ""
        if potential_card.suit == self.trump_suit:
            trump_str = ", a ^Rtrump^~"
        self.bc_pre("%s %s ^C%s^~%s.\n" % (self.get_sp_str(seat), action_str, card_to_str(potential_card, LONG), trump_str))
        self.layout.place(seat.data.who, potential_card)
        return potential_card

    def tick(self):

        # If all seats are full and active, autostart.
        if (self.state.get() == "need_players" and self.seats[0].player and
           self.seats[1].player and self.seats[2].player and
           self.seats[3].player and self.active):
            self.state.set("playing")
            self.bc_pre("The game has begun.\n")

            # Initialize everything by clearing the (non-existent) trick.
            self.clear_trick()

            # Make a new deal.
            self.dealer = self.seats[0]
            self.new_deal()

            # Eldest leads to the first trick.
            self.turn = self.next_seat(self.dealer)
            self.layout.change_turn(self.turn.data.who)

    def handle(self, player, command_str):

        # Handle common commands.
        handled = self.handle_common_commands(player, command_str)

        if not handled:

            state = self.state.get()

            command_bits = command_str.split()
            primary = command_bits[0].lower()

            if state == "setup":

                if primary in ("goal", "score", "sc", "g",):
                    if len(command_bits) == 2:
                        self.set_goal(player, command_bits[1])
                    else:
                        self.tell_pre(player, "Invalid goal command.\n")
                    handled = True

                elif primary in ("done", "ready", "d", "r",):
                    self.bc_pre("The game is now looking for players.\n")
                    self.state.set("need_players")
                    handled = True

            elif state == "need_players":

                if primary in ("config", "setup", "conf",):
                    self.state.set("setup")
                    self.bc_pre("^R%s^~ has switched the game to setup mode.\n" % player)
                    handled = True

            elif state == "playing":

                card_played = False
                if primary in ("hand", "inventory", "inv", "i",):
                    self.show_hand(player)
                    handled = True

                elif primary in ("play", "move", "pl", "mv",):
                    if len(command_bits) == 2:
                        card_played = self.play(player, command_bits[1])
                    else:
                        self.tell_pre(player, "Invalid play command.\n")
                    handled = True

                if card_played:

                    # A card hit the table.  We need to do stuff.
                    if len(self.trick) == 4:

                        # Finish the trick up.
                        self.finish_trick()

                        # Is that the last trick of this hand?
                        if self.ns.tricks + self.ew.tricks == 13:

                            # Yup.  Finish the hand up.
                            self.finish_hand()

                            # Did someone win the overall game?
                            winner = self.find_winner()
                            if winner:

                                # Yup.  Finish.
                                self.resolve(winner)
                                self.finish()

                            else:

                                # Nope.  Pass the deal to the next dealer...
                                self.dealer = self.next_seat(self.dealer)

                                # Deal and set up the first player.
                                self.new_deal()
                                self.turn = self.next_seat(self.dealer)
                                self.layout.change_turn(self.turn.data.who)

                    else:

                        # Trick not over.  Rotate.
                        self.turn = self.next_seat(self.turn)
                        self.layout.change_turn(self.turn.data.who)
                        if self.turn.player:
                            self.show_hand(self.turn.player)

        if not handled:
            self.tell_pre(player, "Invalid command.\n")

    def finish_trick(self):

        # Okay, we have a trick with four cards.  Which card won?
        winner = handle_trick(self.trick, self.trump_suit)

        # This /should/ just return one seat...
        winning_seat_list = [x for x in self.seats if x.data.card == winner]

        if len(winning_seat_list) != 1:
            self.server.log.log(self.log_prefix + "Something went horribly awry; trick ended without a finish.")
            self.bc_pre("Something went horribly wrong; no one won the trick!  Tell the admin.\n")
            return

        winning_seat = winning_seat_list[0]

        # Print information about the winning card.
        self.bc_pre("%s wins the trick with ^C%s^~.\n" % (self.get_sp_str(winning_seat), card_to_str(winner, LONG)))

        # Give the trick to the correct partnership.
        if winning_seat == self.seats[0] or winning_seat == self.seats[2]:
            self.ns.tricks += 1
        else:
            self.ew.tricks += 1

        # Clear the trick.
        self.clear_trick()

        # Set the next leader to the player who won.
        self.turn = winning_seat
        self.layout.change_turn(self.turn.data.who)
        if self.turn.player:
            self.show_hand(self.turn.player)

    def finish_hand(self):

        # Which side won more than 6 tricks?
        if self.ns.tricks > 6:
            winning_side = "^RNorth/South^~"
            addend = self.ns.tricks - 6
            self.ns.score += addend
        else:
            winning_side = "^MEast/West^~"
            addend = self.ew.tricks - 6
            self.ew.score += addend

        # Let everyone know.
        self.bc_pre("%s wins the hand and gains ^C%s^~.\n" % (winning_side, get_plural_str(addend, "point")))
        self.bc_pre(self.get_score_str())

    def find_winner(self):

        # Easy: has one of the sides reached a winning score?
        if self.ns.score >= self.goal:
            return self.ns
        elif self.ew.score >= self.goal:
            return self.ew

        return None

    def resolve(self, winning_partnership):

        if self.ns == winning_partnership:
            name_one = self.seats[0].player_name
            name_two = self.seats[2].player_name
        else:
            name_one = self.seats[1].player_name
            name_two = self.seats[3].player_name
        self.bc_pre("^G%s^~ and ^G%s^~ win!\n" % (name_one, name_two))
Esempio n. 6
0
class Whist(SeatedGame):
    """A Whist game table implementation.  Whist came about sometime in the
    18th century.  This implementation does not (currently) score honours,
    because honours are boring.
    """
    def __init__(self, server, table_name):

        super(Whist, self).__init__(server, table_name)

        self.game_display_name = "Whist"
        self.game_name = "whist"
        self.seats = [
            Seat("North"),
            Seat("East"),
            Seat("South"),
            Seat("West"),
        ]

        self.min_players = 4
        self.max_players = 4
        self.state = State("need_players")
        self.prefix = "(^RWhist^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name,
                                       self.game_display_name)

        # Whist-specific guff.
        self.ns = Struct()
        self.ns.score = 0
        self.ew = Struct()
        self.ew.score = 0
        self.seats[0].data.who = NORTH
        self.seats[1].data.who = EAST
        self.seats[2].data.who = SOUTH
        self.seats[3].data.who = WEST

        self.goal = 5
        self.trick = None
        self.trump_suit = None
        self.led_suit = None
        self.turn = None
        self.dealer = None
        self.winner = None

        self.layout = FourPlayerCardGameLayout()

    def show_help(self, player):

        super(Whist, self).show_help(player)
        player.tell_cc("\nWHIST SETUP PHASE:\n\n")
        player.tell_cc(
            "          ^!setup^., ^!config^., ^!conf^.     Enter setup phase.\n"
        )
        player.tell_cc(
            "            ^!goal^. <num>, ^!score^.     Set the goal score to <num>.\n"
        )
        player.tell_cc(
            "            ^!ready^., ^!done^., ^!r^., ^!d^.     End setup phase.\n"
        )
        player.tell_cc("\nWHIST PLAY:\n\n")
        player.tell_cc(
            "              ^!play^. <card>, ^!pl^.     Play <card> from your hand.\n"
        )
        player.tell_cc(
            "                 ^!hand^., ^!inv^., ^!i^.     Look at the cards in your hand.\n"
        )

    def display(self, player):

        player.tell_cc("%s" % self.layout)

    def get_score_str(self):
        return "          ^RNorth/South^~: %d    ^MEast/West^~: %d\n" % (
            self.ns.score, self.ew.score)

    def get_color_code(self, seat):

        if seat == self.seats[0] or seat == self.seats[2]:
            return "^R"
        else:
            return "^M"

    def get_sp_str(self, seat):

        return "^G%s^~ (%s%s^~)" % (seat.player_name,
                                    self.get_color_code(seat), seat)

    def get_metadata(self):

        to_return = "\n\n"
        if self.turn:
            to_return += "It is ^Y%s^~'s turn (%s%s^~).  Trumps are ^C%s^~.\n" % (
                self.turn.player_name, self.get_color_code(
                    self.turn), self.turn, self.trump_suit)
            to_return += "Tricks:   ^RNorth/South^~: %d    ^MEast/West^~: %d\n" % (
                self.ns.tricks, self.ew.tricks)
        to_return += "The goal score for this game is ^C%s^~.\n" % get_plural_str(
            self.goal, "point")
        to_return += self.get_score_str()

        return to_return

    def show(self, player):
        self.display(player)
        player.tell_cc(self.get_metadata())

    def set_goal(self, player, goal_str):

        if not goal_str.isdigit():
            self.tell_pre(player, "You didn't even send a number!\n")
            return False

        new_goal = int(goal_str)
        if new_goal < 1:
            self.tell_pre(player, "The goal must be at least one point.\n")
            return False

        # Got a valid goal.
        self.goal = new_goal
        self.bc_pre("^M%s^~ has changed the goal to ^G%s^~.\n" %
                    (player, get_plural_str(new_goal, "point")))

    def clear_trick(self):

        # Set the current trick to an empty hand...
        self.trick = Hand()
        self.led_suit = None

        # ...and set everyone's played card to None.
        for seat in self.seats:
            seat.data.card = None

        # Clear the layout as well.
        self.layout.clear()

    def new_deal(self):

        dealer_name = self.dealer.player_name

        self.bc_pre(
            "^R%s^~ (%s%s^~) gives the cards a good shuffle...\n" %
            (dealer_name, self.get_color_code(self.dealer), self.dealer))
        deck = new_deck()
        deck.shuffle()

        # Deal out all of the cards.  We'll flip the last one; that determines
        # the trump suit for the hand.
        self.bc_pre("^R%s^~ deals the cards out to all the players.\n" %
                    dealer_name)
        for seat in self.seats:
            seat.data.hand = Hand()
        for i in range(13):
            for seat in self.seats:
                seat.data.hand.add(deck.discard())

        # Flip the dealer's last card; it determines the trump suit.
        last_card = self.dealer.data.hand[-1]
        self.bc_pre("^R%s^~ flips their last card; it is ^C%s^~.\n" %
                    (dealer_name, card_to_str(last_card, LONG)))
        self.trump_suit = last_card.suit

        # Sort everyone's hands.
        for seat in self.seats:
            seat.data.hand = sorted_hand(seat.data.hand, self.trump_suit)

        # Show everyone their hands.
        self.show_hands()

        # Set the trick counts to zero.
        self.ns.tricks = 0
        self.ew.tricks = 0

    def show_hand(self, player):

        seat = self.get_seat_of_player(player)

        if not seat:
            self.tell_pre(player, "You're not playing!\n")
            return

        print_str = "Your current hand:\n   "
        print_str += hand_to_str(seat.data.hand, self.trump_suit)
        print_str += "\n"
        self.tell_pre(player, print_str)

    def show_hands(self):

        for seat in self.seats:
            if seat.player:
                self.show_hand(seat.player)

    def play(self, player, play_str):

        seat = self.get_seat_of_player(player)
        if not seat:
            self.tell_pre(player, "You're not playing!\n")
            return False

        elif seat != self.turn:
            self.tell_pre(player, "It's not your turn!\n")
            return False

        # Translate the play string into an actual card.
        potential_card = str_to_card(play_str)

        if not potential_card:
            self.tell_pre(player, "That's not a valid card!\n")
            return False

        # Do they even have this card?
        if potential_card not in seat.data.hand:
            self.tell_pre(player, "You don't have that card!\n")
            return False

        # Okay, it's a card in their hand.  First, let's do the "follow the
        # led suit" business.
        action_str = "^Wplays^~"
        if self.led_suit:

            this_suit = potential_card.suit
            if (this_suit != self.led_suit
                    and hand_has_suit(seat.data.hand, self.led_suit)):

                # You can't play off-suit if you can match the led suit.
                self.tell_pre(player,
                              "You can't throw off; you have the led suit.\n")
                return False

        else:

            # No led suit; they're the leader.
            action_str = "^Yleads^~ with"
            self.led_suit = potential_card.suit

        # They either matched the led suit, didn't have any of it, or they
        # are themselves the leader.  Nevertheless, their play is valid.
        seat.data.card = potential_card
        self.trick.add(seat.data.hand.discard_specific(potential_card))
        trump_str = ""
        if potential_card.suit == self.trump_suit:
            trump_str = ", a ^Rtrump^~"
        self.bc_pre("%s %s ^C%s^~%s.\n" %
                    (self.get_sp_str(seat), action_str,
                     card_to_str(potential_card, LONG), trump_str))
        self.layout.place(seat.data.who, potential_card)
        return potential_card

    def tick(self):

        # If all seats are full and active, autostart.
        if (self.state.get() == "need_players" and self.seats[0].player
                and self.seats[1].player and self.seats[2].player
                and self.seats[3].player and self.active):
            self.state.set("playing")
            self.bc_pre("The game has begun.\n")

            # Initialize everything by clearing the (non-existent) trick.
            self.clear_trick()

            # Make a new deal.
            self.dealer = self.seats[0]
            self.new_deal()

            # Eldest leads to the first trick.
            self.turn = self.next_seat(self.dealer)
            self.layout.change_turn(self.turn.data.who)

    def handle(self, player, command_str):

        # Handle common commands.
        handled = self.handle_common_commands(player, command_str)

        if not handled:

            state = self.state.get()

            command_bits = command_str.split()
            primary = command_bits[0].lower()

            if state == "setup":

                if primary in (
                        "goal",
                        "score",
                        "sc",
                        "g",
                ):
                    if len(command_bits) == 2:
                        self.set_goal(player, command_bits[1])
                    else:
                        self.tell_pre(player, "Invalid goal command.\n")
                    handled = True

                elif primary in (
                        "done",
                        "ready",
                        "d",
                        "r",
                ):
                    self.bc_pre("The game is now looking for players.\n")
                    self.state.set("need_players")
                    handled = True

            elif state == "need_players":

                if primary in (
                        "config",
                        "setup",
                        "conf",
                ):
                    self.state.set("setup")
                    self.bc_pre(
                        "^R%s^~ has switched the game to setup mode.\n" %
                        player)
                    handled = True

            elif state == "playing":

                card_played = False
                if primary in (
                        "hand",
                        "inventory",
                        "inv",
                        "i",
                ):
                    self.show_hand(player)
                    handled = True

                elif primary in (
                        "play",
                        "move",
                        "pl",
                        "mv",
                ):
                    if len(command_bits) == 2:
                        card_played = self.play(player, command_bits[1])
                    else:
                        self.tell_pre(player, "Invalid play command.\n")
                    handled = True

                if card_played:

                    # A card hit the table.  We need to do stuff.
                    if len(self.trick) == 4:

                        # Finish the trick up.
                        self.finish_trick()

                        # Is that the last trick of this hand?
                        if self.ns.tricks + self.ew.tricks == 13:

                            # Yup.  Finish the hand up.
                            self.finish_hand()

                            # Did someone win the overall game?
                            winner = self.find_winner()
                            if winner:

                                # Yup.  Finish.
                                self.resolve(winner)
                                self.finish()

                            else:

                                # Nope.  Pass the deal to the next dealer...
                                self.dealer = self.next_seat(self.dealer)

                                # Deal and set up the first player.
                                self.new_deal()
                                self.turn = self.next_seat(self.dealer)
                                self.layout.change_turn(self.turn.data.who)

                    else:

                        # Trick not over.  Rotate.
                        self.turn = self.next_seat(self.turn)
                        self.layout.change_turn(self.turn.data.who)
                        if self.turn.player:
                            self.show_hand(self.turn.player)

        if not handled:
            self.tell_pre(player, "Invalid command.\n")

    def finish_trick(self):

        # Okay, we have a trick with four cards.  Which card won?
        winner = handle_trick(self.trick, self.trump_suit)

        # This /should/ just return one seat...
        winning_seat_list = [x for x in self.seats if x.data.card == winner]

        if len(winning_seat_list) != 1:
            self.server.log.log(
                self.log_prefix +
                "Something went horribly awry; trick ended without a finish.")
            self.bc_pre(
                "Something went horribly wrong; no one won the trick!  Tell the admin.\n"
            )
            return

        winning_seat = winning_seat_list[0]

        # Print information about the winning card.
        self.bc_pre("%s wins the trick with ^C%s^~.\n" %
                    (self.get_sp_str(winning_seat), card_to_str(winner, LONG)))

        # Give the trick to the correct partnership.
        if winning_seat == self.seats[0] or winning_seat == self.seats[2]:
            self.ns.tricks += 1
        else:
            self.ew.tricks += 1

        # Clear the trick.
        self.clear_trick()

        # Set the next leader to the player who won.
        self.turn = winning_seat
        self.layout.change_turn(self.turn.data.who)
        if self.turn.player:
            self.show_hand(self.turn.player)

    def finish_hand(self):

        # Which side won more than 6 tricks?
        if self.ns.tricks > 6:
            winning_side = "^RNorth/South^~"
            addend = self.ns.tricks - 6
            self.ns.score += addend
        else:
            winning_side = "^MEast/West^~"
            addend = self.ew.tricks - 6
            self.ew.score += addend

        # Let everyone know.
        self.bc_pre("%s wins the hand and gains ^C%s^~.\n" %
                    (winning_side, get_plural_str(addend, "point")))
        self.bc_pre(self.get_score_str())

    def find_winner(self):

        # Easy: has one of the sides reached a winning score?
        if self.ns.score >= self.goal:
            return self.ns
        elif self.ew.score >= self.goal:
            return self.ew

        return None

    def resolve(self, winning_partnership):

        if self.ns == winning_partnership:
            name_one = self.seats[0].player_name
            name_two = self.seats[2].player_name
        else:
            name_one = self.seats[1].player_name
            name_two = self.seats[3].player_name
        self.bc_pre("^G%s^~ and ^G%s^~ win!\n" % (name_one, name_two))
Esempio n. 7
0
class FortyOne(SeatedGame):
    """An implementation of Forty-One, a quirky combination of solo and
    partnership trick-taking that is apparently popular in Syria.  The only
    real differences from the rules I've seen online are that this version
    supports a 'whist' mode rather than always having Hearts as trump and it
    forces play to continue if winning scores are tied.
    """

    def __init__(self, server, table_name):

        super(FortyOne, self).__init__(server, table_name)

        self.game_display_name = "Forty-One"
        self.game_name = "fortyone"

        # Seat ordering is "Persian."
        self.seats = [
            Seat("North"),
            Seat("West"),
            Seat("South"),
            Seat("East"),
        ]

        self.min_players = 4
        self.max_players = 4
        self.state = State("need_players")
        self.prefix = "(^RForty-One^~): "
        self.log_prefix = "%s/%s: " % (self.table_display_name, self.game_display_name)

        # 41-specific stuff.
        self.goal = 41
        self.double = 7
        self.minimum = 11
        self.whist = False
        self.positive = True
        self.trick = None
        self.trump_suit = None
        self.led_suit = None
        self.turn = None
        self.dealer = None
        self.winner = None

        self.north = self.seats[0]
        self.west = self.seats[1]
        self.south = self.seats[2]
        self.east = self.seats[3]

        self.north.data.who = NORTH
        self.west.data.who = WEST
        self.south.data.who = SOUTH
        self.east.data.who = EAST

        self.north.data.partner = self.south
        self.west.data.partner = self.east
        self.south.data.partner = self.north
        self.east.data.partner = self.west

        self.layout = FourPlayerCardGameLayout()

        # Set everyone's score to zero.
        for seat in self.seats:
            seat.data.score = 0

    def show_help(self, player):

        super(FortyOne, self).show_help(player)
        player.tell_cc("\nFORTY-ONE SETUP PHASE:\n\n")
        player.tell_cc("          ^!setup^., ^!config^., ^!conf^.     Enter setup phase.\n")
        player.tell_cc("            ^!goal^. <num>, ^!score^.     Set the goal score to <num>.\n")
        player.tell_cc("           ^!double^. <num>, ^!doub^.     Set the lowest doubling to <num>.\n")
        player.tell_cc("           ^!minimum^. <num>, ^!min^.     Set the minimum deal bid to <num>.\n")
        player.tell_cc("         ^!positive^. on|off, ^!pos^.     Require positive partners for wins.\n")
        player.tell_cc("             ^!whist^. on|off, ^!wh^.     Enable whist mode for trumps.\n")
        player.tell_cc("            ^!ready^., ^!done^., ^!r^., ^!d^.     End setup phase.\n")
        player.tell_cc("\nFORTY-ONE PLAY:\n\n")
        player.tell_cc("            ^!choose^. <suit>, ^!ch^.     Declare <suit> as trumps.  Hakem only.\n")
        player.tell_cc("                 ^!bid^. <num>, ^!b^.     Bid to win <num> tricks.\n")
        player.tell_cc("              ^!play^. <card>, ^!pl^.     Play <card> from your hand.\n")
        player.tell_cc("                 ^!hand^., ^!inv^., ^!i^.     Look at the cards in your hand.\n")

    def display(self, player):

        player.tell_cc("%s" % self.layout)

    def get_color_code(self, seat):
        if seat == self.north or seat == self.south:
            return "^R"
        else:
            return "^M"

    def get_sp_str(self, seat):

        return "^G%s^~ (%s%s^~)" % (seat.player_name, self.get_color_code(seat), seat)

    def get_player_num_str(self, seat, num):

        return "%s%s^~: %d" % (self.get_color_code(seat), seat.player_name, num)

    def get_score_str(self):
        return_str = "       "
        for seat in self.seats:
            return_str += "   %s" % self.get_player_num_str(seat, seat.data.score)
        return_str += "\n"

        return return_str

    def get_trick_str(self):
        return_str = "^CTricks^~:"
        for seat in self.seats:
            return_str += "   %s" % self.get_player_num_str(seat, seat.data.tricks)
        return_str += "\n"

        return return_str

    def get_bid_str(self):
        return_str = "  ^GBids^~:"
        for seat in self.seats:
            return_str += "   %s" % self.get_player_num_str(seat, seat.data.bid)
        return_str += "\n"

        return return_str

    def get_metadata(self):

        to_return = "\n\n"
        if self.turn:
            seat_color = self.get_color_code(self.turn)

            to_return += "It is ^Y%s^~'s turn (%s%s^~)." % (self.turn.player_name, seat_color, self.turn)
            if self.whist:
                to_return += "  Trumps are ^C%s^~." % self.trump_suit
            to_return += "\n" + self.get_trick_str()
            to_return += self.get_bid_str()
        to_return += "\nThe goal score for this game is ^C%s^~.\n" % get_plural_str(self.goal, "point")
        to_return += self.get_score_str()
        if self.positive:
            partner_str = "^Ymust have"
        else:
            partner_str = "^ydo not need to have"
        to_return += "(Both partners %s^~ a positive score to win.)\n" % partner_str
        if self.double != 7:
            if self.double:
                to_return += "Bids double value at ^C%s^~.\n" % get_plural_str(self.double, "trick")
            else:
                to_return += "Bids ^Rnever^~ double their value.\n"
        if self.minimum != 11:
            to_return += "The minimum bid for a deal is ^G%s^~.\n" % get_plural_str(self.minimum, "point")

        return to_return

    def show(self, player):
        self.display(player)
        player.tell_cc(self.get_metadata())

    def set_goal(self, player, goal_str):

        if not goal_str.isdigit():
            self.tell_pre(player, "You didn't even send a number!\n")
            return False

        new_goal = int(goal_str)
        if new_goal < 1:
            self.tell_pre(player, "The goal must be at least one point.\n")
            return False

        # Got a valid goal.
        self.goal = new_goal
        self.bc_pre("^M%s^~ has changed the goal to ^G%s^~.\n" % (player, get_plural_str(new_goal, "point")))

    def set_double(self, player, double_str):

        if not double_str.isdigit():
            self.tell_pre(player, "You didn't even send a number!\n")
            return False

        new_double = int(double_str)

        if new_double > 13:
            self.tell_pre(player, "The doubling value must be at most 13 tricks.\n")
            return False

        # Got a valid double value.
        self.double = new_double
        if self.double:
            self.bc_pre("^M%s^~ has changed the doubling value to ^G%s^~.\n" % (player, get_plural_str(new_double, "trick")))
        else:
            self.bc_pre("^M%s^~ has ^Rdisabled^~ doubling values.\n" % (player,))

    def set_minimum(self, player, minimum_str):

        if not minimum_str.isdigit():
            self.tell_pre(player, "You didn't even send a number!\n")
            return False

        new_minimum = int(minimum_str)

        # It doesn't make sense for the minimum to be below 4, as that's the
        # lowest possible bid anyhow.  13 is a plausible maximum.
        if new_minimum < 4:
            self.tell_pre(player, "The minimum must be at least 4 points.\n")
            return False

        if new_minimum > 13:
            self.tell_pre(player, "The minimum must be at most 13 points.\n")
            return False

        # Got a valid minimum.
        self.minimum = new_minimum
        self.bc_pre("^M%s^~ has changed the minimum to ^G%s^~.\n" % (player, get_plural_str(new_minimum, "point")))

    def set_positive(self, player, positive_bits):

        positive_bool = booleanize(positive_bits)
        if positive_bool:
            if positive_bool > 0:
                self.positive = True
                display_str = "^Crequires^~"
            elif positive_bool < 0:
                self.positive = False
                display_str = "^cdoes not require^~"
            self.bc_pre("^R%s^~ sets it so that winning %s positive scores.\n" % (player, display_str))
        else:
            self.tell_pre(player, "Not a valid boolean!\n")

    def set_whist(self, player, whist_bits):

        whist_bool = booleanize(whist_bits)
        if whist_bool:
            if whist_bool > 0:
                self.whist = True
                display_str = "^Con^~"
            elif whist_bool < 0:
                self.whist = False
                display_str = "^coff^~"
            self.bc_pre("^R%s^~ has turned whist-style trumps %s.\n" % (player, display_str))
        else:
            self.tell_pre(player, "Not a valid boolean!\n")

    def clear_trick(self):

        # Set the current trick to an empty hand...
        self.trick = Hand()
        self.led_suit = None

        # ...and set everyone's played card to None.
        for seat in self.seats:
            seat.data.card = None

        # Clear the layout as well.
        self.layout.clear()

    def new_deal(self):

        # Set tricks and bids to zero.
        for seat in self.seats:
            seat.data.tricks = 0
            seat.data.bid = 0

        dealer_name = self.dealer.player_name

        self.bc_pre("^R%s^~ (%s%s^~) gives the cards a good shuffle...\n" % (dealer_name, self.get_color_code(self.dealer), self.dealer))
        deck = new_deck()
        deck.shuffle()

        # Deal out all of the cards.
        self.bc_pre("^R%s^~ deals the cards out to all of the players.\n" % dealer_name)
        for seat in self.seats:
            seat.data.hand = Hand()
        for _ in range(13):
            for seat in self.seats:
                seat.data.hand.add(deck.discard())

        # If we're in whist mode, flip the dealer's last card to determine
        # trumps.
        if self.whist:
            last_card = self.dealer.data.hand[-1]
            self.bc_pre("^R%s^~ flips their last card; it is ^C%s^~.\n" % (dealer_name, card_to_str(last_card, LONG)))
            self.trump_suit = last_card.suit
        else:

            # Hearts forever.
            self.trump_suit = HEARTS

        # Sort everyone's hands and show them to everyone.
        for seat in self.seats:
            seat.data.hand = sorted_hand(seat.data.hand, self.trump_suit)
        self.show_hands()

    def show_hand(self, player):

        seat = self.get_seat_of_player(player)

        if not seat:
            self.tell_pre(player, "You're not playing!\n")
            return

        print_str = "Your current hand:\n   "
        print_str += hand_to_str(seat.data.hand, self.trump_suit)
        print_str += "\n"
        self.tell_pre(player, print_str)

    def show_hands(self):

        for seat in self.seats:
            if seat.player:
                self.show_hand(seat.player)

    def bid(self, player, bid_str):

        seat = self.get_seat_of_player(player)
        if not seat:
            self.tell_pre(player, "You're not playing!\n")
            return False

        elif seat != self.turn:
            self.tell_pre(player, "It's not your turn!\n")
            return False

        if not bid_str.isdigit():
            self.tell_pre(player, "You didn't even send a number!\n")
            return False

        bid = int(bid_str)

        if bid < 1:
            self.tell_pre(player, "You must bid at least one point.\n")
            return False

        if bid > 13:
            self.tell_pre(player, "You can't bid more tricks than there are!\n")
            return False

        # Got a valid bid.
        seat.data.bid = bid
        self.bc_pre("%s has bid ^G%s^~.\n" % (self.get_sp_str(seat), get_plural_str(bid, "trick")))
        return True

    def play(self, player, play_str):

        seat = self.get_seat_of_player(player)
        if not seat:
            self.tell_pre(player, "You're not playing!\n")
            return False

        elif seat != self.turn:
            self.tell_pre(player, "It's not your turn!\n")
            return False

        # Translate the play string into an actual card.
        potential_card = str_to_card(play_str)

        if not potential_card:
            self.tell_pre(player, "That's not a valid card!\n")
            return False

        # Do they even have this card?
        if potential_card not in seat.data.hand:
            self.tell_pre(player, "You don't have that card!\n")
            return False

        # Okay, it's a card in their hand.  First, let's do the "follow the
        # led suit" business.
        action_str = "^Wplays^~"
        if self.led_suit:

            this_suit = potential_card.suit
            if this_suit != self.led_suit and hand_has_suit(seat.data.hand, self.led_suit):

                # You can't play off-suit if you can match the led suit.
                self.tell_pre(player, "You can't throw off; you have the led suit.\n")
                return False

        else:

            # No led suit; they're the leader.
            action_str = "^Yleads^~ with"
            self.led_suit = potential_card.suit

        # They either matched the led suit, didn't have any of it, or they
        # are themselves the leader.  Nevertheless, their play is valid.
        seat.data.card = potential_card
        self.trick.add(seat.data.hand.discard_specific(potential_card))
        trump_str = ""
        if potential_card.suit == self.trump_suit:
            trump_str = ", a ^Rtrump^~"
        self.bc_pre("%s %s ^C%s^~%s.\n" % (self.get_sp_str(seat), action_str, card_to_str(potential_card, LONG), trump_str))
        self.layout.place(seat.data.who, potential_card)
        return potential_card

    def tick(self):

        # If all seats are full and active, autostart.
        active_seats = [x for x in self.seats if x.player]
        if self.state.get() == "need_players" and len(active_seats) == 4 and self.active:
            self.state.set("bidding")
            self.bc_pre("The game has begun.\n")

            # Initialize everything by clearing the (non-existent) trick.
            self.clear_trick()

            # Pick a starting dealer at random.
            self.dealer = random.choice(self.seats)
            self.bc_pre("Fate has spoken, and the starting dealer is %s!\n" % self.get_sp_str(self.dealer))
            self.new_deal()

            # Eldest opens bidding, per usual.
            self.turn = self.next_seat(self.dealer)
            self.layout.change_turn(self.turn.data.who)
            if self.turn.player:
                self.tell_pre(self.turn.player, "It is your turn to bid.\n")

    def handle(self, player, command_str):

        # Handle common commands.
        handled = self.handle_common_commands(player, command_str)

        if not handled:

            state = self.state.get()

            command_bits = command_str.split()
            primary = command_bits[0].lower()

            if state == "setup":

                if primary in ("goal", "score", "sc", "g",):
                    if len(command_bits) == 2:
                        self.set_goal(player, command_bits[1])
                    else:
                        self.tell_pre(player, "Invalid goal command.\n")
                    handled = True

                elif primary in ("positive", "pos", "po", "p",):
                    if len(command_bits) == 2:
                        self.set_positive(player, command_bits[1])
                    else:
                        self.tell_pre(player, "Invalid positive command.\n")
                    handled = True

                elif primary in ("double", "doub",):
                    if len(command_bits) == 2:
                        self.set_double(player, command_bits[1])
                    else:
                        self.tell_pre(player, "Invalid double command.\n")
                    handled = True

                elif primary in ("minimum", "min",):
                    if len(command_bits) == 2:
                        self.set_minimum(player, command_bits[1])
                    else:
                        self.tell_pre(player, "Invalid minimum command.\n")
                    handled = True

                elif primary in ("whist", "wh",):
                    if len(command_bits) == 2:
                        self.set_whist(player, command_bits[1])
                    else:
                        self.tell_pre(player, "Invalid whist command.\n")
                    handled = True

                elif primary in ("done", "ready", "d", "r",):
                    self.bc_pre("The game is now looking for players.\n")
                    self.state.set("need_players")
                    handled = True

            elif state == "need_players":

                if primary in ("config", "setup", "conf",):
                    self.state.set("setup")
                    self.bc_pre("^R%s^~ has switched the game to setup mode.\n" % player)
                    handled = True

            elif state == "bidding":

                if primary in ("hand", "inventory", "inv", "i",):
                    self.show_hand(player)
                    handled = True

                elif primary in ("bid", "b",):
                    if len(command_bits) == 2:
                        bid_made = self.bid(player, command_bits[1])
                    else:
                        self.tell_pre(player, "Invalid bid command.\n")
                    handled = True

                if bid_made:

                    bid_list = [x for x in self.seats if x.data.bid]
                    if len(bid_list) == 4:

                        # Bidding is complete.  Are enough tricks bid?
                        bid_total = 0
                        point_total = 0
                        for seat in self.seats:
                            bid = seat.data.bid
                            bid_total += bid
                            point_total += bid

                            # Bids of self.double or more count double, if set.
                            if self.double:
                                if bid >= self.double:
                                    point_total += bid

                        bid_str = get_plural_str(bid_total, "trick")
                        point_str = get_plural_str(point_total, "point")
                        if point_total >= self.minimum:

                            # Enough indeed.  Start the game proper.
                            self.bc_pre("With ^W%s^~ bid for a total of ^C%s^~, play begins!\n" % (bid_str, point_str))
                            self.state.set("playing")
                            self.turn = self.next_seat(self.turn)
                            if self.turn.player:
                                self.show_hand(self.turn.player)

                        else:

                            # Not enough.  Throw hands in and deal fresh.
                            self.bc_pre("With only ^R%s^~ bid, everyone throws in their hand.\n" % bid_str)
                            self.dealer = self.next_seat(self.dealer)

                            # Deal and set up the first player to bid.
                            self.new_deal()
                            self.turn = self.next_seat(self.dealer)
                            if self.turn.player:
                                self.tell_pre(self.turn.player, "It is your turn to bid.\n")

                    else:

                        # Still need more bids.
                        self.turn = self.next_seat(self.turn)
                        if self.turn.player:
                            self.tell_pre(self.turn.player, "It is your turn to bid.\n")
                            self.show_hand(self.turn.player)

                    # No matter what happened, update the layout.
                    self.layout.change_turn(self.turn.data.who)

            elif state == "playing":

                card_played = False
                if primary in ("hand", "inventory", "inv", "i",):
                    self.show_hand(player)
                    handled = True

                elif primary in ("play", "move", "pl", "mv",):
                    if len(command_bits) == 2:
                        card_played = self.play(player, command_bits[1])
                    else:
                        self.tell_pre(player, "Invalid play command.\n")
                    handled = True

                if card_played:

                    # A card hit the table.  We need to do stuff.
                    if len(self.trick) == 4:

                        # Finish the trick up.
                        self.finish_trick()

                        # Is that the last trick?
                        if self.north.data.tricks + self.west.data.tricks + self.south.data.tricks + self.east.data.tricks == 13:

                            # Resolve the hand...
                            self.resolve_hand()

                            # And look for a winner.
                            winner = self.find_winner()
                            if winner:

                                # Found a winner.  Finish.
                                self.resolve(winner)
                                self.finish()

                            else:

                                # No winner.  Pass the deal to the next player...
                                self.dealer = self.next_seat(self.dealer)

                                # Deal and set up the first player to bid.
                                self.new_deal()
                                self.turn = self.next_seat(self.dealer)
                                self.layout.change_turn(self.turn.data.who)
                                self.state.set("bidding")
                                if self.turn.player:
                                    self.tell_pre(self.turn.player, "It is your turn to bid.\n")

                    else:

                        # Trick not over.  Rotate.
                        self.turn = self.next_seat(self.turn)
                        self.layout.change_turn(self.turn.data.who)
                        if self.turn.player:
                            self.show_hand(self.turn.player)

        if not handled:
            self.tell_pre(player, "Invalid command.\n")

    def finish_trick(self):

        # Okay, we have a trick with four cards.  Which card won?
        winner = handle_trick(self.trick, self.trump_suit)

        # This /should/ just return one seat...
        winning_seat_list = [x for x in self.seats if x.data.card == winner]

        if len(winning_seat_list) != 1:
            self.server.log.log(self.log_prefix + "Something went horribly awry; trick ended without a finish.")
            self.bc_pre("Something went horribly wrong; no one won the trick!  Tell the admin.\n")
            return

        winning_seat = winning_seat_list[0]

        # Print information about the winning card.
        self.bc_pre("%s wins the trick with ^C%s^~.\n" % (self.get_sp_str(winning_seat), card_to_str(winner, LONG)))

        # Give the trick to the correct seat.
        winning_seat.data.tricks += 1

        # Clear the trick.
        self.clear_trick()

        # Set the next leader to the player who won.
        self.turn = winning_seat
        self.layout.change_turn(self.turn.data.who)
        if self.turn.player:
            self.show_hand(self.turn.player)

    def resolve_hand(self):

        for seat in self.seats:

            # Determine the score delta; if the bid is < self.double (or
            # doubling is disabled) it's just the bid, otherwise it's double.
            bid = seat.data.bid
            if bid < self.double or not self.double:
                score_delta = bid
            else:
                score_delta = bid * 2
            score_delta_str = get_plural_str(score_delta, "point")

            result_str = "%s bid ^G%s^~ and " % (self.get_sp_str(seat), get_plural_str(bid, "trick"))

            if bid <= seat.data.tricks:

                # Woot, gain points!
                seat.data.score += score_delta
                result_str += "took ^W%d^~, gaining ^G%s^~.\n" % (seat.data.tricks, score_delta_str)

            else:

                # Aw, lost points.
                seat.data.score -= score_delta
                result_str += "only took ^W%d^~, losing ^R%s^~.\n" % (seat.data.tricks, score_delta_str)

            self.bc_pre(result_str)

        self.bc_pre(self.get_score_str())

    def find_winner(self):

        # The rules don't define what happens if both sides win in the same
        # deal.  I've arbitrarily decided that highest score wins, and if
        # both sides have equivalent high scores, there isn't a winner yet.
        high_count = -1
        high_list = None
        for seat in self.seats:
            if seat.data.score >= self.goal:
                if seat.data.score > high_count:
                    high_count = seat.data.score
                    high_list = [seat]
                elif seat.data.score == high_count:
                    high_list.append(seat)

        # If we're in positive mode, strip entries from the list if their
        # partners don't have a positive score.
        if self.positive:
            high_list = [x for x in high_list if x.data.partner.data.score > 0]

        # If the list is empty, we can flat-out bail.
        if not high_list:
            return None

        # If the list has multiple entries, and they're from different teams,
        # we also don't have a winner.
        if (self.north in high_list or self.south in high_list) and (self.west in high_list or self.east in high_list):

            # This is at least worth reporting.
            self.bc_pre("Both sides are ^Ctied for the win^~!  The game continues...\n")
            return None

        # It's fine if both members of a team won at the same time, but we
        # don't care which, since a partnership wins.
        return high_list[0]

    def resolve(self, winning_seat):

        if self.north == winning_seat or self.south == winning_seat:
            name_one = self.north.player_name
            name_two = self.south.player_name
        else:
            name_one = self.west.player_name
            name_two = self.east.player_name

        self.bc_pre("^G%s^~ and ^G%s^~ win!\n" % (name_one, name_two))