[SOLVED] Why doesn't my function for finding card combinations work for more than 2 cards?

Issue

I’m trying to make a cribbage game in python, and I have a pretty good things going except for when I’m trying to find combinations of cards that add up to 15 after the player discards 2 cards to the crib, it doesn’t work for combinations of 3 or greater. My code is below.

import math
import random
from itertools import *



deck = ['dA','d2','d3','d4','d5','d6','d7','d8','d9','dT','dJ','dQ','dK',

'hA','h2','h3','h4','h5','h6','h7','h8','h9','hT','hJ','hQ','hK',

'sA','s2','s3','s4','s5','s6','s7','s8','s9','sT','sJ','sQ','sK',

'cA','c2','c3','c4','c5','c6','c7','c8','c9','cT','cJ','cQ','cK']


newHand = []

CARD_SYMBOLS = {"d": "♦", "h": "♥", "s": "♠", "c": "♣"}

def show_hand(cards):
    print("┌─────┐\t"*len(cards))
    print( "\t".join([f"|  {card[1]}  |" for card in cards]))
    print( "\t".join([f"|  {CARD_SYMBOLS[card[0]]}  |" for card in cards]))
    print("└─────┘\t"*len(cards))

hand = []
handPoints = []
computerHand = []
crib = []
separator = ''


def cutCard():
  CD = random.choice(deck)
  newHand.append(CD)
  deck.remove(CD)

def dealCards():
  for x in range(6):
    card_dealt = random.choice(deck)
    hand.append(card_dealt)
    deck.remove(card_dealt)

    compCard_dealt = random.choice(deck)
    computerHand.append(compCard_dealt)
    deck.remove(compCard_dealt)

AtoK = {'2','3','4','5','6','7','8','9'}
tens = {'T','J','Q','K'}

twoCard15s = {'T5','J5','Q5','K5','78','96'}
threeCard15s = {'K4A','K32','Q4A','Q32','T4A','T32','95A','942','933','86A','852','843','77A','762','753','744','662','654','555'}
fourCard15s = {'K3AA','K22A','Q3AA','Q22A','J3AA','J22A','T3AA','T22A','94AA','932A','9222','85AA','842A','833A','8322','76AA','752A','7244','743A','7332','662A','6522','653A','6432','6441','6333','5433','5442','554A','555A','5532','4443'}

def countPoints():

  def count15s():
    #------2 card----this all works
    for num1 in range(0,4):
      for num2 in range(0,4):
        if(num1 != num2):
          combo = ''.join(newHand[num1][1]+newHand[num2][1])
          if(combo in twoCard15s):
            handPoints.append(combo)

   #-------3 card---works for about 3 combinations and I can't figure out why
    for numX in range(0,4):
      for numY in range(0,4):
        for numZ in range(0,4):
          if(numX != numY and numY != numZ and numX != numZ):
            combThree = ''.join(newHand[numX-1][1] + newHand[numY-1][1] + newHand[numZ-1][1])  
            if(combThree in threeCard15s):
              if(combThree not in handPoints):
                handPoints.append(combThree)

    #---------4 card---also doesn't work
        for numX in range(0,4):
          for numY in range(0,4):
            for numZ in range(0,4):
              for numA in range(0,4):
                if(numX != numY != numZ != numA):
                  combFour = ''.join(newHand[numX][1] + newHand[numY][1] + newHand[numZ][1] + newHand[numA][1])  
                  if(combFour in fourCard15s ):
                    if(combFour not in handPoints):
                      handPoints.append(combFour)


  count15s() #actually makes the function run
  print(handPoints) #prints the array of card combinations--I'm going to change this later so it actually counts points instead

def makeCrib():
  whichCards = input("Which cards do you want to put in the crib? (1-6) : ")
  if(whichCards[0] != whichCards[1]):
    if(len(whichCards) == 2):
      value1 = hand[int(whichCards[0]) - 1]
      value2 = hand[int(whichCards[1]) - 1]

      #chooses the inputed cards from the player's hand, subtracts 1 so it's more appealing to the eye, then adds the new cards to a separate array. 
      both = []
      both.append(value1)
      both.append(value2)
# for each card in the hand, if the card selected isn't in the array of selected cards, push the card to the newHand (scored)
      for x in range(0,6):
        if(hand[x] not in both):
          newHand.append(hand[x])
      



#makes it appealing to the eye 
print('Your Hand: \n   1       2       3       4       5       6')


dealCards() #randomizes the cards and deals them
show_hand(hand) #prints the card art for the hand
makeCrib()
cutCard()
show_hand(newHand)

countPoints()

I guess my main question is why does this work for 2 card combinations, and how can I make it so it works for 3-5?

What it ends up printing is something like this:
picture
The combination of 8 6 and A should be printed in the array at the very bottom but it doesn’t.

Also is there an easier/more efficient way to check every combination other than a quad nested for loop? Thank you in advance to anyone who can help!

Solution

You should use a dictionary to map cards to their numeric value and compute the sum of values for combinations (rather than searching for pre-defined combinations).

Also, you should use the combinations function from itertools instead of nested loops. (your loops are combining the some cards with themselves and also repeating permutations of the same combinations which requires extra work to compensate)

from itertools import combinations

kinds      = "dhsc"
faces      = "A23456789TJQK"
cardValues = {kind+face:min(10,value) for kind in kinds
              for value,face in enumerate(faces,1)}

def count15s(cards):
    points = 0
    combos = []
    for size in (2,3,4,5):
        for combo in combinations(cards,size):
            if sum(map(cardValues.get,combo)) == 15:
                points += 2
                combos.append(combo)
    return points,combos


print(*count15s(['s7','dA','h6','h8','d5'])) 
# 4 [('s7', 'h8'), ('dA', 'h6', 'h8')]

print(*count15s(['s2','d7','h3','h5','dK']))
# 6 [('h5', 'dK'), ('s2', 'h3', 'dK'), ('d7', 'h3', 'h5')]

print(*count15s(['s2','d5','h3','h5','dK']))
# 8 [('d5', 'dK'), ('h5', 'dK'), ('s2', 'h3', 'dK'), ('s2', 'd5', 'h3', 'h5')]

print(*count15s(['s5','d5','h5','c5','dK']))
# 16 [('s5', 'dK'), ('d5', 'dK'), ('h5', 'dK'), ('c5', 'dK'), 
      ('s5', 'd5', 'h5'), ('s5', 'd5', 'c5'), ('s5', 'h5', 'c5'),
      ('d5', 'h5', 'c5')]

The same approach can be used to count sequences:

cardOrder = {kind+face:order for kind in kinds
              for order,face in enumerate(faces)}

def countSeq(cards):
    cards  = sorted(cards,key=cardOrder.get)
    points = 0
    combos = []
    for size in (5,4,3):
        for combo in combinations(cards,size):
            sequence = "".join(card[1] for card in combo)
            if sequence in faces:
                points += size
                combos.append(combo)
        if points: break
    return points,combos


print(*countSeq(['s7','d8','h8','h9','d9'])) 
# 12 [('s7', 'd8', 'h9'), ('s7', 'd8', 'd9'), 
      ('s7', 'h8', 'h9'), ('s7', 'h8', 'd9')]

print(*countSeq(['sA','d2','h2','c2','d3']))
# 9 [('sA', 'd2', 'd3'), ('sA', 'h2', 'd3'), ('sA', 'c2', 'd3')]        

print(*countSeq(['sT','dJ','hQ','cK','dK']))
# 8 [('sT', 'dJ', 'hQ', 'cK'), ('sT', 'dJ', 'hQ', 'dK')]

print(*countSeq(['sT','d9','hQ','cJ','dK']))
# 5 [('d9', 'sT', 'cJ', 'hQ', 'dK')]

Answered By – Alain T.

Answer Checked By – Mildred Charles (BugsFixing Admin)

Leave a Reply

Your email address will not be published.