Пример #1
0
    def getDiscard(self) -> CardObj:

        # Find deadwood of hand w/o each card.
        deadwoodArr = np.zeros(len(self.cards))
        for i in range(len(self.cards)):
            # Cannot draw and discard face up card.
            if self.cards[i] == self.drawnCard and self.drawnCard == self.faceUpCard:
                continue

            remainingCards = list(self.cards)
            remainingCards.remove(self.cards[i])
            bestMeldSets = GinRummyUtil.cardsToBestMeldSets(remainingCards)
            deadwood = GinRummyUtil.getDeadwoodPoints3(remainingCards) if len(bestMeldSets) == 0 \
                else GinRummyUtil.getDeadwoodPoints1(bestMeldSets[0], remainingCards)
            deadwoodArr[i] = deadwood


        # Find available melds for each card.
        meldsArr = np.zeros(len(self.cards)) # parallel

        # Look at cards pairwise for availability of melds.
        for i in range(len(self.cards)):
            for j in range(i + 1, len(self.cards)):
                card1 = self.cards[i]
                card2 = self.cards[j]
                ways = self._waysCompleteMeld(card1, card2)
                meldsArr[i] += ways
                meldsArr[j] += ways

        linComb = meldsArr * self.alpha + deadwoodArr * self.beta

        minArg = np.argmin(linComb)
        if self.cards[minArg] == self.drawnCard and self.drawnCard == self.faceUpCard:
            return self.cards[np.argsort(linComb)[1]]
        return self.cards[minArg]
    def getDiscard(self) -> Card:
        # Discard a random card (not just drawn face up) leaving minimal deadwood points.
        minDeadwood = float('inf')
        candidateCards = []
        for card in self.cards:
            # Cannot draw and discard face up card.
            if card == self.drawnCard and self.drawnCard == self.faceUpCard:
                continue
            # Disallow repeat of draw and discard.
            drawDiscard = [self.drawnCard, card]
            if GinRummyUtil.cardsToBitstring(
                    drawDiscard) in self.drawDiscardBitstrings:
                continue

            remainingCards = list(self.cards)
            remainingCards.remove(card)
            bestMeldSets = GinRummyUtil.cardsToBestMeldSets(remainingCards)
            deadwood = GinRummyUtil.getDeadwoodPoints3(remainingCards) if len(bestMeldSets) == 0 \
                else GinRummyUtil.getDeadwoodPoints1(bestMeldSets[0], remainingCards)
            if deadwood <= minDeadwood:
                if deadwood < minDeadwood:
                    minDeadwood = deadwood
                    candidateCards.clear()
                candidateCards.append(card)
        # Prevent future repeat of draw, discard pair.
        discard = candidateCards[randint(0, len(candidateCards) - 1)]
        drawDiscard = [self.drawnCard, discard]
        self.drawDiscardBitstrings.append(
            GinRummyUtil.cardsToBitstring(drawDiscard))
        return discard
    def getLinComb(self, cards, alpha):
        # Find deadwood of hand w/o each card.
        deadwoodArr = np.zeros(len(cards))
        for i in range(len(cards)):
            # Cannot draw and discard face up card.
            if cards[i] == self.drawnCard and self.drawnCard == self.faceUpCard:
                continue

            remainingCards = list(cards)
            remainingCards.remove(cards[i])
            bestMeldSets = GinRummyUtil.cardsToBestMeldSets(remainingCards)
            deadwood = GinRummyUtil.getDeadwoodPoints3(remainingCards) if len(bestMeldSets) == 0 \
                else GinRummyUtil.getDeadwoodPoints1(bestMeldSets[0], remainingCards)
            deadwoodArr[i] = deadwood


        # Find available melds for each card.
        meldsArr = np.zeros(len(cards)) # parallel

        probs = self._predictOpponentHand()

        # Look at cards pairwise for availability of melds.
        for i in range(len(cards)):
            for j in range(i + 1, len(cards)):
                card1 = cards[i]
                card2 = cards[j]
                ways = self._waysCompleteMeld(card1, card2, probs)
                meldsArr[i] += ways
                meldsArr[j] += ways

        return meldsArr * alpha + deadwoodArr * (1-alpha)
Пример #4
0
 def getFinalMelds(self) -> List[List[CardObj]]:
     # Check if deadwood of maximal meld is low enough to go out.
     bestMeldSets = GinRummyUtil.cardsToBestMeldSets(self.cards) # List[List[List[Card]]]
     if not self.opponentKnocked and (len(bestMeldSets) == 0 or \
         GinRummyUtil.getDeadwoodPoints1(bestMeldSets[0], self.cards) > \
         GinRummyUtil.MAX_DEADWOOD):
         return None
     if len(bestMeldSets) == 0:
         return []
     return bestMeldSets[randint(0, len(bestMeldSets)-1)]
 def willDrawFaceUpCard(self, card: Card) -> bool:
     # Return true if card would be a part of a meld, false otherwise.
     self.faceUpCard = card
     newCards = list(self.cards)
     newCards.append(card)
     for meld in GinRummyUtil.cardsToAllMelds(newCards):
         if card in meld:
             return True
     return False
Пример #6
0
    def getDiscard(self) -> CardObj:
        # Discard a random card (not just drawn face up) leaving minimal deadwood points.
        minArg = -1
        minScore = 10**5
        candidateCards = np.zeros(len(self.cards)) # parallel

        # Look at cards pairwise for availability of melds.
        for i in range(len(self.cards)):
            for j in range(i + 1, len(self.cards)):
                card1 = self.cards[i]
                card2 = self.cards[j]
                ways = self._waysCompleteMeld(card1, card2)
                candidateCards[i] += ways
                candidateCards[j] += ways

        # Reward cards on belongingness to melds.
        bestMeldSets = GinRummyUtil.cardsToBestMeldSets(self.cards)
        if len(bestMeldSets) == 0:
            minArg = np.argmin(candidateCards)
            if self.cards[minArg] == self.drawnCard and self.drawnCard == self.faceUpCard:
                return self.cards[np.argsort(candidateCards)[1]]
            return self.cards[minArg]

        for bestMeldSet in bestMeldSets:
            candidateCardsTemp = np.copy(candidateCards)
            for meldSet in bestMeldSet:
                for k in range(len(self.cards)):
                    if self.cards[k] == self.drawnCard and self.drawnCard == self.faceUpCard:
                        candidateCardsTemp[k] += 1000
                    elif self.cards[k] in meldSet:
                        candidateCardsTemp[k] += 10
            currMin = np.amin(candidateCardsTemp)
            if currMin < minScore:
                minScore = currMin
                minArg = np.argmin(candidateCardsTemp)

        if self.cards[minArg] == self.drawnCard and self.drawnCard == self.faceUpCard:
            return self.cards[np.argsort(candidateCardsTemp)[1]]
        return self.cards[minArg]
    def getDiscard(self) -> CardObj:
        # Discard a random card (not just drawn face up) leaving minimal deadwood points.
        minDeadwood = float('inf')
        candidateCards = np.copy(self.ownCards)
        candidateCards *= 1000

        if self.drawnCard == self.faceUpCard:
            candidateCards[self.drawnCard.getId()] = 0

        bestMeldSets = GinRummyUtil.cardsToBestMeldSets(self.cards)
        if len(bestMeldSets) == 0:
            candidateCards = candidateCards / np.sum(candidateCards)
            discard = np.random.choice(52, 1, p=candidateCards)[0]
            for card in self.cards:
                if discard == card.getId():
                    return card

        # For each best meld set. Find the cards that do not belong to melds.
        for bestMeldSet in bestMeldSets:
            remainingCards = list(self.cards)
            for meldSet in bestMeldSet:
                for card in meldSet:
                    candidateCards[card.getId()] -= 5 # reward card for being in meld
                    remainingCards.remove(card)

            # Now, look at remaining cards pairwise.
            for i in range(len(remainingCards)):
                for j in range(i + 1, len(remainingCards)):
                    card1 = remainingCards[i]
                    card2 = remainingCards[j]
                    ways = self._waysCompleteMeld(card1, card2)
                    candidateCards[[card1.getId(), card2.getId()]] -= ways

        discard = np.argmax(candidateCards)
        for card in self.cards:
            if discard == card.getId():
                return card
Пример #8
0
    def play(self):
        scores = [0, 0]
        hands = []
        hands.extend([[], []])

        startingPlayer = random.randrange(2)

        # while game not over
        while scores[0] < GinRummyUtil.GOAL_SCORE and scores[
                1] < GinRummyUtil.GOAL_SCORE:

            currentPlayer = startingPlayer
            opponent = (1 if currentPlayer == 0 else 0)

            # get shuffled deck and deal cards
            deck = Deck.getShuffle(random.randrange(10**8))
            hands[0] = []
            hands[1] = []
            for i in range(2 * GinRummyGame.HAND_SIZE):
                hands[i % 2] += [deck.pop()]
            for i in range(2):
                GinRummyGame.players[i].startGame(i, startingPlayer, hands[i])
                if GinRummyGame.playVerbose:
                    print("Player %d is dealt %s.\n" % (i, hands[i]))
            if GinRummyGame.playVerbose:
                print("Player %d starts.\n" % (startingPlayer))
            discards = []
            discards.append(deck.pop())
            if GinRummyGame.playVerbose:
                print("The initial face up card is %s.\n" %
                      (discards[len(discards) - 1]))
            firstFaceUpCard = discards[len(discards) - 1]
            turnsTaken = 0
            knockMelds = None

            # tracking_pastdiscards = {0: np.zeros(52), 1:  np.zeros(52)}
            # tracking_pastpickups = {0: np.zeros(52), 1:  np.zeros(52)}
            # tracking_pastnonpickups = {0: np.zeros(52), 1:  np.zeros(52)}
            # while the deck has more than two cards remaining, play round
            while len(deck) > 2:
                # DRAW
                drawFaceUp = False
                faceUpCard = discards[len(discards) - 1]

                # offer draw face-up iff not 3rd turn with first face up card (decline automatically in that case)
                if not (turnsTaken == 2 and faceUpCard == firstFaceUpCard):
                    # both players declined and 1st player must draw face down
                    drawFaceUp = GinRummyGame.players[
                        currentPlayer].willDrawFaceUpCard(faceUpCard)
                    if GinRummyGame.playVerbose and not drawFaceUp and faceUpCard == firstFaceUpCard and turnsTaken < 2:
                        print("Player %d declines %s.\n" %
                              (currentPlayer, firstFaceUpCard))

                if not (not drawFaceUp and turnsTaken < 2
                        and faceUpCard == firstFaceUpCard):
                    # continue with turn if not initial declined option
                    drawCard = discards.pop() if drawFaceUp else deck.pop()
                    for i in range(2):
                        to_report = drawCard if i == currentPlayer or drawFaceUp else None
                        GinRummyGame.players[i].reportDraw(
                            currentPlayer, to_report)
                        # TRACKING
                        # if i != currentPlayer: # player i is tracking currentPlayer
                        #     if drawFaceUp:
                        #         tracking_pastpickups[i][faceUpCard.getId()] = 1
                        #     else:
                        #         tracking_pastnonpickups[i][faceUpCard.getId()] = 1
                    if GinRummyGame.playVerbose:
                        print("Player %d draws %s.\n" %
                              (currentPlayer, drawCard))
                    hands[currentPlayer].append(drawCard)

                    # DISCARD
                    discardCard = GinRummyGame.players[
                        currentPlayer].getDiscard()
                    if not discardCard in hands[
                            currentPlayer] or discardCard == faceUpCard:
                        print(
                            "Player %d discards %s illegally and forfeits.\n" %
                            (currentPlayer, discardCard))
                        return opponent

                    hands[currentPlayer].remove(discardCard)
                    for i in range(2):
                        GinRummyGame.players[i].reportDiscard(
                            currentPlayer, discardCard)
                        # TRACKING
                        # if i != currentPlayer: # player i is tracking currentPlayer
                        #     tracking_pastdiscards[i][discardCard.getId()] = 1
                        #     tracking_pastpickups[i][discardCard.getId()] = 0
                        #     tracking_hand = one_hot(GinRummyGame.players[currentPlayer].cards)
                        #     tracking_hand[discardCard.getId()] = 0
                        #     tracking_hands.append(tracking_hand)
                        #     tracking_states.append(np.array([tracking_pastdiscards[i], tracking_pastpickups[i], tracking_pastnonpickups[i]]))
                        #     tracking_states2.append(np.array([tracking_pastdiscards[i], tracking_pastpickups[i], tracking_pastnonpickups[i], one_hot(GinRummyGame.players[i].cards)]))
                    if GinRummyGame.playVerbose:
                        print("Player %d discards %s.\n" %
                              (currentPlayer, discardCard))
                    discards.append(discardCard)
                    if GinRummyGame.playVerbose:
                        unmeldedCards = hands[currentPlayer].copy()
                        bestMelds = GinRummyUtil.cardsToBestMeldSets(
                            unmeldedCards)
                        if len(bestMelds) == 0:
                            print(
                                "Player %d has %s with %d deadwood.\n" %
                                (currentPlayer, unmeldedCards,
                                 GinRummyUtil.getDeadwoodPoints3(unmeldedCards)
                                 ))
                        else:
                            melds = bestMelds[0]
                            for meld in melds:
                                for card in meld:
                                    unmeldedCards.remove(card)
                            melds.extend(unmeldedCards)
                            print(
                                "Player %d has %s with %d deadwood.\n" %
                                (currentPlayer, melds,
                                 GinRummyUtil.getDeadwoodPoints3(unmeldedCards)
                                 ))

                    # CHECK FOR KNOCK
                    knockMelds = GinRummyGame.players[
                        currentPlayer].getFinalMelds()
                    if knockMelds != None:
                        # player knocked; end of round
                        break

                turnsTaken += 1
                currentPlayer = 1 if currentPlayer == 0 else 0
                opponent = 1 if currentPlayer == 0 else 0

            if knockMelds != None:
                # round didn't end due to non-knocking and 2 cards remaining in draw pile
                # check legality of knocking meld
                handBitstring = GinRummyUtil.cardsToBitstring(
                    hands[currentPlayer])
                unmelded = handBitstring
                for meld in knockMelds:
                    meldBitstring = GinRummyUtil.cardsToBitstring(meld)
                    if (not meldBitstring
                            in GinRummyUtil.getAllMeldBitstrings()) or (
                                (meldBitstring & unmelded) != meldBitstring):
                        # non-meld or meld not in hand
                        print("Player %d melds %s illegally and forfeits.\n" %
                              (currentPlayer, knockMelds))
                        return opponent
                    unmelded &= ~meldBitstring  # remove successfully melded cards from

                # compute knocking deadwood
                knockingDeadwood = GinRummyUtil.getDeadwoodPoints1(
                    knockMelds, hands[currentPlayer])
                if knockingDeadwood > GinRummyUtil.MAX_DEADWOOD:
                    print(
                        "Player %d melds %s with greater than %d deadwood and forfeits.\n"
                        % (currentPlayer, knockMelds, knockingDeadwood))
                    return opponent

                meldsCopy = []
                for meld in knockMelds:
                    meldsCopy.append(meld.copy())
                for i in range(2):
                    GinRummyGame.players[i].reportFinalMelds(
                        currentPlayer, meldsCopy)
                if GinRummyGame.playVerbose:
                    if knockingDeadwood > 0:
                        print(
                            "Player %d melds %s with %d deadwood from %s.\n" %
                            (currentPlayer, knockMelds, knockingDeadwood,
                             GinRummyUtil.bitstringToCards(unmelded)))
                    else:
                        print("Player %d goes gin with melds %s.\n" %
                              (currentPlayer, knockMelds))

                # get opponent meld
                opponentMelds = GinRummyGame.players[opponent].getFinalMelds()
                meldsCopy = []
                for meld in opponentMelds:
                    meldsCopy.append(meld.copy())
                for i in range(2):
                    GinRummyGame.players[i].reportFinalMelds(
                        opponent, meldsCopy)

                # check legality of opponent meld
                opponentHandBitstring = GinRummyUtil.cardsToBitstring(
                    hands[opponent])
                opponentUnmelded = opponentHandBitstring
                for meld in opponentMelds:
                    meldBitstring = GinRummyUtil.cardsToBitstring(meld)
                    if (meldBitstring
                            not in GinRummyUtil.getAllMeldBitstrings()) or (
                                (meldBitstring & opponentUnmelded) !=
                                meldBitstring):
                        # non-meld or meld not in hand
                        print("Player %d melds %s illegally and forfeits.\n" %
                              (opponent, opponentMelds))
                        return currentPlayer
                    opponentUnmelded &= ~meldBitstring  # remove successfully melded cards from

                if GinRummyGame.playVerbose:
                    print("Player %d melds %s.\n" % (opponent, opponentMelds))

                # lay off on knocking meld (if not gin)
                unmeldedCards = GinRummyUtil.bitstringToCards(opponentUnmelded)
                if knockingDeadwood > 0:
                    # knocking player didn't go gin
                    cardWasLaidOff = False
                    while True:
                        # attempt to lay each card off
                        cardWasLaidOff = False
                        layOffCard = None
                        layOffMeld = None
                        for card in unmeldedCards:
                            for meld in knockMelds:
                                newMeld = meld.copy()
                                newMeld.append(card)
                                newMeldBitstring = GinRummyUtil.cardsToBitstring(
                                    newMeld)
                                if newMeldBitstring in GinRummyUtil.getAllMeldBitstrings(
                                ):
                                    layOffCard = card
                                    layOffMeld = meld
                                    break
                            if layOffCard != None:
                                if GinRummyGame.playVerbose:
                                    print("Player %d lays off %s on %s.\n" %
                                          (opponent, layOffCard, layOffMeld))
                                for i in range(2):
                                    GinRummyGame.players[i].reportLayoff(
                                        opponent, layOffCard,
                                        layOffMeld.copy())
                                unmeldedCards.remove(layOffCard)
                                layOffMeld.append(layOffCard)
                                cardWasLaidOff = True
                                break
                        if not cardWasLaidOff:
                            break

                opponentDeadwood = 0
                for card in unmeldedCards:
                    opponentDeadwood += GinRummyUtil.getDeadwoodPoints2(card)
                if GinRummyGame.playVerbose:
                    print("Player %d has %d deadwood with %s\n" %
                          (opponent, opponentDeadwood, unmeldedCards))

                # compare deadwood and compute new scores
                if knockingDeadwood == 0:
                    # gin round win
                    scores[
                        currentPlayer] += GinRummyUtil.GIN_BONUS + opponentDeadwood
                    if GinRummyGame.playVerbose:
                        print("Player %d scores the gin bonus of %d plus opponent deadwood %d for %d total points.\n" % \
                        (currentPlayer, GinRummyUtil.GIN_BONUS, opponentDeadwood, GinRummyUtil.GIN_BONUS + opponentDeadwood))

                elif knockingDeadwood < opponentDeadwood:
                    # non-gin round win:
                    scores[
                        currentPlayer] += opponentDeadwood - knockingDeadwood
                    if GinRummyGame.playVerbose:
                        print(
                            "Player %d scores the deadwood difference of %d.\n"
                            % (currentPlayer,
                               opponentDeadwood - knockingDeadwood))

                else:
                    # undercut win for opponent
                    scores[
                        opponent] += GinRummyUtil.UNDERCUT_BONUS + knockingDeadwood - opponentDeadwood
                    if GinRummyGame.playVerbose:
                        print("Player %d undercuts and scores the undercut bonus of %d plus deadwood difference of %d for %d total points.\n" % \
                        (opponent, GinRummyUtil.UNDERCUT_BONUS, knockingDeadwood - opponentDeadwood, GinRummyUtil.UNDERCUT_BONUS + knockingDeadwood - opponentDeadwood))

                startingPlayer = 1 if startingPlayer == 0 else 0  # starting player alternates

            # If the round ends due to a two card draw pile with no knocking, the round is cancelled.
            else:
                if GinRummyGame.playVerbose:
                    print(
                        "The draw pile was reduced to two cards without knocking, so the hand is cancelled."
                    )

            # report final hands
            for i in range(2):
                for j in range(2):
                    GinRummyGame.players[i].reportFinalHand(j, hands[j].copy())

            # score reporting
            if GinRummyGame.playVerbose:
                print("Player\tScore\n0\t%d\n1\t%d\n" % (scores[0], scores[1]))
            for i in range(2):
                GinRummyGame.players[i].reportScores(scores.copy())

        if GinRummyGame.playVerbose:
            print("Player %s wins.\n" % (0 if scores[0] > scores[1] else 1))
        return 0 if scores[0] >= GinRummyUtil.GOAL_SCORE else 1