示例#1
0
class Player():
    """Base class for a player in a Gin Rummy game."""
    def __init__(self, contestant_id: str = None, handtype: type = None):
        """Create a new Player using the [optionally] specified type of Hand.

        Args:
            contestant_id (str): [optional] ID value of the entity operating
                this player
            handtype (Hand subclass): [optional] Allows Hand classes with
                different behaviors (e.g. HandWithMelds) to be used by Player
        """
        self.game = None
        self.contestant_id = contestant_id
        if handtype is None:
            self.hand = Hand()
        else:
            if issubclass(handtype, Hand):
                self.hand = handtype()
            else:
                raise PylgrumInternalError(
                    "Type {} is not a ".format(handtype) + "subclass of Hand.")

    def join_game(self, game: 'game.Game'):
        """Join player to a game."""
        if not game:
            raise PylgrumError("Can't join game with None value")
        self.game = game

    def receive_card(self, card: Card) -> None:
        """Add a card to the hand."""
        self.hand.add(card)

    def turn_start(self, move: Move) -> None:
        """Called by a Game to begin a turn. (abstract)

        Args:
            move (Move): the Move object used to hold/transfer move details

        A move involves adding either the discard or the top of the draw
        pile to the player's hand, then discarding.

        This method handles the first part of that process, and populates
        the in-progress move with details that the game will execute. After
        this call, the Move must specify the card source (draw or discard
        pile) that the player has chosen.

        Note that while the last-discarded card is passed as an argument
        to this method, that is only as a convenience. If the player wants
        the discard, they do not directly "take" it from the argument here,
        but rather set a move with card_source==DISCARD_STACK.
        """

    def turn_finish(self, move: Move) -> None:
        """Called by a Game to finish a turn. (abstract)
示例#2
0
def hand_with_sets():
    """A hand with two sets.

    Should find 6 sets:
        (QH, QC, QD)
        (2S, 2H, 2D, 2C) - 5 permutations of this
    """
    hand = Hand()
    for card in Card.from_text(
        "JH", "QH", "QC", "QD", "2S",
        "3C", "2H", "6S", "2D", "2C"
    ):
        hand.add(card)

    yield hand
示例#3
0
def test_runs_must_be_same_suit():
    hand = Hand()

    hand.add(Card(rank=Rank.JACK, suit=Suit.HEART))
    hand.add(Card(rank=Rank.QUEEN, suit=Suit.HEART))
    hand.add(Card(rank=Rank.KING, suit=Suit.CLUB))

    md = MeldDetector(*hand.cards)
    md._detect_all_melds()
    assert(len(md._melds) == 0)
示例#4
0
    def __init__(self, contestant_id: str = None, handtype: type = None):
        """Create a new Player using the [optionally] specified type of Hand.

        Args:
            contestant_id (str): [optional] ID value of the entity operating
                this player
            handtype (Hand subclass): [optional] Allows Hand classes with
                different behaviors (e.g. HandWithMelds) to be used by Player
        """
        self.game = None
        self.contestant_id = contestant_id
        if handtype is None:
            self.hand = Hand()
        else:
            if issubclass(handtype, Hand):
                self.hand = handtype()
            else:
                raise PylgrumInternalError(
                    "Type {} is not a ".format(handtype) + "subclass of Hand.")
示例#5
0
def hand_with_simple_sets_and_runs():
    """A hand with non-overlapping sets and runs.

    Should find 6 melds:
        2C 2S 2H  (1 set)
        4D 5D 6D  (1 run)
        9S 10S JS (1 set)
        4C

    This hand should have 4 points of deadwood (1 deadwood card)
    """
    hand = Hand()
    for card in Card.from_text(
        "2C", "2S", "2H", "4D", "5D",
        "6D", "9S", "10S", "JS", "4C"
    ):
        hand.add(card)

    yield hand
示例#6
0
def hand_with_complex_sets_and_runs():
    """A hand with overlapping sets and runs.

    Should find 10 melds:
        2C 2S 2H 2D (5 permutations of set)
        3D 4D 5D    (3 perms of run (including 2 above): 234, 345, 2345)
        3S          (1 set - 333)
        3C AC       (1 run - A23 (2 is above))

    This hand should have 0 points of deadwood (0 deadwood cards)
    """
    hand = Hand()
    for card in Card.from_text(
        "2C", "2S", "2H", "2D",
        "3D", "4D", "5D", "3S",
        "3C", "AC"
    ):
        hand.add(card)

    yield hand
示例#7
0
def test_optimal_meld_scenario_8():
    h = Hand()
    for card in Card.from_text(
        "4C",
        "4S", "3S", "2S",
        "5H", "4H", "3H", "AH",
        "3D", "2D"
    ):
        h.add(card)

    ## in this scenario, there are two equally optimal outcomes
    optimal_expected_option1 = [
        Meld(Card.from_text("4C", "4S", "4H")),
        Meld(Card.from_text("3H", "3S", "3D"))
    ]
    optimal_expected_option2 = [
        Meld(Card.from_text("4S", "3S", "2S")),
        Meld(Card.from_text("5H", "4H", "3H"))
    ]
    md = MeldDetector(*h.cards)
    md.detect_optimal_melds()

    assert(len(md.optimal_hand.melds) == len(optimal_expected_option1)) # works for either

    is_option_1 = True
    is_option_2 = True

    for expected_meld in optimal_expected_option1:
        if expected_meld not in md.optimal_hand.melds:
            is_option_1 = False

    for expected_meld in optimal_expected_option2:
        if expected_meld not in md.optimal_hand.melds:
            is_option_2 = False

    assert(not (is_option_1 and is_option_2)) #highlander principle
    assert(is_option_1 or is_option_2)

    # b/c the two are equiv. this is true regardless of option
    assert(md.optimal_hand.deadwood_value == 10)
    assert(md.optimal_hand.deadwood_count == 4)
示例#8
0
def test_optimal_meld_scenario_9():
    # this is a relatively simple scenario - only one card is overused
    h = Hand()
    for card in Card.from_text(
        "6D", "6C", "6H",
        "2H", "3H", "4H",
        "2S", "2C"
    ):
        h.add(card)

    optimal_expected = [
        Meld(Card.from_text("6D", "6C", "6H")),
        Meld(Card.from_text("2H", "3H", "4H"))
    ]
    md = MeldDetector(*h.cards)
    md.detect_optimal_melds()

    assert(len(md.optimal_hand.melds) == len(optimal_expected))
    for expected_meld in optimal_expected:
        assert(expected_meld in md.optimal_hand.melds)
    assert(md.optimal_hand.deadwood_value == 4)
    assert(md.optimal_hand.deadwood_count == 2)
示例#9
0
def test_optimal_meld_scenario_6():
    h = Hand()
    for card in Card.from_text(
        "4S", "3S", "2S", "AS",
        "3H", "2H", "AH",
        "4D", "3D", "2D"
    ):
        h.add(card)

    optimal_expected = [
        Meld(Card.from_text("4S", "3S", "2S", "AS")),
        Meld(Card.from_text("3H", "2H", "AH")),
        Meld(Card.from_text("4D", "3D", "2D"))
    ]
    md = MeldDetector(*h.cards)
    md.detect_optimal_melds()

    assert(len(md.optimal_hand.melds) == len(optimal_expected))
    for expected_meld in optimal_expected:
        assert(expected_meld in md.optimal_hand.melds)
    assert(md.optimal_hand.deadwood_value == 0)
    assert(md.optimal_hand.deadwood_count == 0)
示例#10
0
def test_optimal_meld_scenario_1():
    h = Hand()
    for card in Card.from_text(
        "10S", "9S", "8S",
        "8H",
        "9C", "8C", "7C", "6C", "5C",
        "KD"
    ):
        h.add(card)

    optimal_expected = [
        Meld(Card.from_text("10S", "9S", "8S")),
        Meld(Card.from_text("9C", "8C", "7C", "6C", "5C"))
    ]
    md = MeldDetector(*h.cards)
    md.detect_optimal_melds()

    assert(len(md.optimal_hand.melds) == len(optimal_expected))
    for expected_meld in optimal_expected:
        assert(expected_meld in md.optimal_hand.melds)
    assert(md.optimal_hand.deadwood_value == 18)
    assert(md.optimal_hand.deadwood_count == 2)
示例#11
0
def test_small_hand_is_not_complete():
    hand = Hand()

    hand.add(Card(rank=Rank.JACK, suit=Suit.HEART))    # 0:  JH
    hand.add(Card(rank=Rank.QUEEN, suit=Suit.HEART))   # 1:  QH

    md = MeldDetector(*hand.cards)
    assert(md.is_complete_hand == False)
示例#12
0
def hand_with_overlapping_sets_and_runs():
    """A hand with obviously overlapping sets and runs.

    Should find 6 melds:
        2C 2S 2H 2D (5 permutations of set)
        4D 5D 6D    (1 run)
        8S
        10S
        JS

    This hand should have 28 points of deadwood (3 deadwood cards)

    The optimal sets from this hand are:
        2C 2S 2H 2D
        4D 5D 6D
    """
    hand = Hand()
    for card in Card.from_text(
        "2C", "2S", "2H", "2D", "4D", "5D", "6D", "8S", "10S", "JS"
    ):
        hand.add(card)

    yield hand
示例#13
0
def test_sets_must_be_three_long_to_count():
    hand = Hand()

    hand.add(Card(rank=Rank.JACK, suit=Suit.HEART))    # 0:  JH
    hand.add(Card(rank=Rank.JACK, suit=Suit.DIAMOND))   # 1:  JD

    md = MeldDetector(*hand.cards)
    md._detect_all_melds()
    assert(len(md._melds) == 0)
示例#14
0
    def test_too_many_cards(self):
        """Implicitly tests the add() override in Hand, too."""
        h = Hand()
        self.assertEqual(h.size(), 0)

        h.add(Card(rank=Rank.QUEEN, suit=Suit.HEART))  # 0 : QH
        h.add(Card(rank=Rank.JACK, suit=Suit.DIAMOND))  # 1 : JD
        h.add(Card(rank=Rank.ACE, suit=Suit.CLUB))  # 2 : AC
        h.add(Card(rank=Rank.KING, suit=Suit.SPADE))  # 3 : KS
        h.add(Card(rank=Rank.TWO, suit=Suit.HEART))  # 4 : 2H
        h.add(Card(rank=Rank.THREE, suit=Suit.DIAMOND))  # 5 : 3D
        h.add(Card(rank=Rank.FOUR, suit=Suit.CLUB))  # 6 : 4C
        h.add(Card(rank=Rank.FIVE, suit=Suit.SPADE))  # 7 : 5S
        h.add(Card(rank=Rank.TEN, suit=Suit.HEART))  # 8 : 10H
        h.add(Card(rank=Rank.NINE, suit=Suit.DIAMOND))  # 9 : 9D
        h.add(Card(rank=Rank.EIGHT, suit=Suit.CLUB))  # 10: 8C
        self.assertEqual(h.size(), 11)  ## a full hand

        with self.assertRaises(OverdealtHandError):
            h.add(Card(rank=Rank.SEVEN, suit=Suit.SPADE))
示例#15
0
def hand_with_complex_runs():
    """Multiple suits, not in order, overlapping runs

    Should find 7 runs:
        (3H, 4H, 5H),
        (4H, 5H, 6H),
        (5H, 6H, 7H),
        (3H, 4H, 5H, 6H),
        (4H, 5H, 6H, 7H),
        (3H, 4H, 5H, 6H, 7H),
        (9C, 10C, JC)
    """
    hand = Hand()

    hand.add(Card(rank=Rank.ACE, suit=Suit.DIAMOND))
    hand.add(Card(rank=Rank.ACE, suit=Suit.HEART))
    hand.add(Card(rank=Rank.THREE, suit=Suit.HEART))
    hand.add(Card(rank=Rank.SEVEN, suit=Suit.HEART))
    hand.add(Card(rank=Rank.FOUR, suit=Suit.HEART))
    hand.add(Card(rank=Rank.SIX, suit=Suit.HEART))
    hand.add(Card(rank=Rank.JACK, suit=Suit.CLUB))
    hand.add(Card(rank=Rank.FIVE, suit=Suit.HEART))
    hand.add(Card(rank=Rank.TEN, suit=Suit.SPADE))
    hand.add(Card(rank=Rank.NINE, suit=Suit.CLUB))
    hand.add(Card(rank=Rank.TEN, suit=Suit.CLUB))

    yield hand
示例#16
0
def hand_with_simple_runs():
    """A simple, same-suit, in-order hand with two runs.

    Should find two runs:
        (JH, QH, KH)
        (AS, 2S, 3S)
    """
    hand = Hand()

    hand.add(Card(rank=Rank.JACK, suit=Suit.HEART))    # 0:  JH
    hand.add(Card(rank=Rank.QUEEN, suit=Suit.HEART))   # 1:  QH
    hand.add(Card(rank=Rank.KING, suit=Suit.HEART))    # 2:  KH
    hand.add(Card(rank=Rank.KING, suit=Suit.SPADE))    # 3:  KS
    hand.add(Card(rank=Rank.TWO, suit=Suit.HEART))     # 4:  2H
    hand.add(Card(rank=Rank.THREE, suit=Suit.DIAMOND)) # 5:  3D
    hand.add(Card(rank=Rank.ACE, suit=Suit.SPADE))     # 6:  AS
    hand.add(Card(rank=Rank.TWO, suit=Suit.SPADE))     # 7:  2S
    hand.add(Card(rank=Rank.THREE, suit=Suit.SPADE))   # 8:  3S
    hand.add(Card(rank=Rank.NINE, suit=Suit.DIAMOND))  # 9:  9D

    yield hand