PJI Homepage 6
Picture of me.
class Deck(object):
	'''Deck: An object that represents a deck of cards. Subclass of object. 
	Defaults to a standard poker deck.'''
	# attributes
	cards = []
	numberOfSuits = 4
	suitNames = {3:'Spades', 2:'Hearts', 1:'Diamonds', 0:'Clubs'}
	suitLowCard = 2
	suitHighCard = 14
	suitCardNames = {14:'Ace', 13:'King', 12:'Queen', 11:'Jack'}
	specialCards = 0
	specialCardNames = {}
	
	
	# populate the deck with cards
	def __init__(self):
		for suit in range(self.numberOfSuits):
			for num in range(self.suitLowCard, (self.suitHighCard + 1)):
				self.cards.append([num, suit])
		
		if self.specialCards:
			for num in range(self.specialCards):
				self.cards.append([num, self.numberOfSuits])
				
		print 'Deck made.'
	
	
	# shuffle the deck
	def shuffle(self):
		if self.cards:
			import random
			random.shuffle(self.cards)
			print 'Deck shuffled.'
		else:
			print 'Sorry, dude. No cards to shuffle. Must use deck.__init__ first.'
	
	
	# empties the deck
	def clearDeck(self):
		self.cards = []
		print 'Deck cleared.'
	
	
	# draws, by default, the top card
	def draw(self, card=0):
		return self.cards.pop(card)
	
	
	# draws the bottom card
	def drawFromBottom(self):
		return self.cards.pop()
	
	
	# cuts the deck at the location given, defaults to middle
	def cut(self, loc=26):
		self.cards = self.cards[loc:] + self.cards[:loc]
		return 'Deck cut.'
	
	
	# reorganize the deck
	def order(self):
		# flip num and suit so the sort will order by suit
		for card in self.cards:
			card.reverse()
		
		self.cards.sort()
		self.cards.reverse()
		
		# flip back num and suit
		for card in self.cards:
			card.reverse()
			
		return 'Deck reorganized.'
	
	
	# stack the deck
	def stackDeck(self, card, new_loc):
		self.insert(self.draw(card), new_loc)
		return 'Nothing to see here.'
	
	
	# find a card, returning its index
	def findCard(self, card_num=14, card_suit=3):
		if type(card_num) == type(int()):
			if type(card_suit) == type(str()):
				for suit in self.suitNames.iteritems():
					if suit[1].lower() == card_suit.lower():
						return self.cards.index([card_num, suit[0]])
					elif suit[1][:-1].lower() == card_suit.lower():
						return self.cards.index([card_num, suit[0]])
				else:
					return "Suit doesn't exist."
			elif type(card_suit) == type(int()):
				return self.cards.index([card_num, card_suit])
			else:
				return "Suit must be either an int or string."
		else:
			return "Card number must be an int."


class TarotDeck(Deck):
	'''TarotDeck: An object that represents a deck of tarot cards. Subclass 
	of Deck.'''
	# attributes
	cards = []
	reverseRate = .5
	numberOfSuits = 4
	suitNames = {3:'Swords', 2:'Wands', 1:'Pentacles', 0:'Cups'}
	suitLowCard = 1
	suitHighCard = 14
	suitCardNames = {14:'King',
					 13:'Queen',
					 12:'Knight',
					 11:'Page',
					 10:'Ten',
					 9:'Nine',
					 8:'Eight',
					 7:'Seven',
					 6:'Six',
					 5:'Five',
					 4:'Four',
					 3:'Three',
					 2:'Two',
					 1:'Ace'}
	specialCards = 22
	specialCardNames = {0: 'The Fool',
						1: 'The Magician',
						2: 'The High Priestess',
						3: 'The Empress',
						4: 'The Emperor',
						5: 'The Hierophant',
						6: 'The Lovers',
						7: 'The Chariot',
						8: 'Strength',
						9: 'The Hermit',
						10: 'Wheel of Fortune',
						11: 'Justice',
						12: 'The Hanged Man',
						13: 'Death',
						14: 'Temperance',
						15: 'The Devil',
						16: 'The Tower',
						17: 'The Star',
						18: 'The Moon',
						19: 'The Sun',
						20: 'Judgement',
						21: 'The World'}
	
	
	# draw is overridden to give each card a reverse attribute
	def draw(self, card=0):
		import random
		if random.random() < self.reverseRate:
			rev = 0
		else:
			rev = 1		
		aCard = self.cards.pop(card)
		aCard.append(rev)
		return aCard
	
	
	# returns description of the card
	def describe(self, card):
		if card[1] < self.numberOfSuits:
			name = self.suitCardNames[card[0]] + ' of ' + self.suitNames[card[1]]
		else:
			name = self.specialCardNames[card[0]]
		if card[2] == 1:
			name = name + ' Reversed'
		return name
		

class Hand(object):
	'''Hand: A object to represent a hand of cards. Subclass 
	of object. Defaults to a Texas Hold 'em hand.'''
	# attributes
	cards = []
	overflowCards = []
	sleeve = []
	size = 2
	
	
	# init built-in
	def __init__(self):
		if self.cards: self.cards = []
		return None
	
	
	# take a card into the hand
	def take(self, card=0):
		if card:
			if len(self.cards) < self.size:
				self.cards.append(card)
				print card, 'taken'
			else:
				self.overflowCards.append(card)
				print 'Yo, I can only have ' + str(self.size) + ' cards in my hand.'
		else:
			print 'Um, there is no card to take.'
	
	
	# order cards, defaults to by num
	def order(self, orderBy=0):
		if self.cards:
			if orderBy: self.swapCardInfo()
			self.cards.sort()
			self.cards.reverse()
			if orderBy: self.swapCardInfo()
			print 'Hand ordered.'
		else:
			print 'Sorry, man, but there is no hand to order. You need to have one delt to you.'

	
	# swaps the order of the num and the suit
	# on the cards in the hand
	def swapCardInfo(self):
		if self.cards:
			for card in self.cards:
				card.reverse()
			return 0
		else:
			return "Dude, gotta have cards to swap card's info."
	
	
	# order cards by suit
	def orderBySuit(self):
		if self.cards:
			self.swapCardInfo()
			self.order()
			self.swapCardInfo()
			return 0
		else:
			return "Hey, no cards, dangit!"
	
	
	# get number of each card as list
	def getNums(self):
		self.numList = [card[0] for card in self.cards]
		
		
	# get the suits of each card as list
	def getSuits(self):
		self.suitList = [card[1] for card in self.cards]
		
	
	# discard a card from the hand
	def discard(self, card=0):
		return self.cards.pop(card)
		
	
	# discard all
	def discardHand(self):
		discards = []
		while self.cards:
			discards.append(self.cards.pop())
		return discards
	
	
	# discard a card from the overflow
	def discardOverflow(self, card=0):
		return self.cards.pop(card)
		
		
	# put a card up your sleeve
	def upSleeve(self, card=[14, 's']):
		self.sleeve.append(card)
		return 'Shhhh!'
	
	
	# drop a card from your sleeve into your hand
	def sleeveSwap(self, handCard, sleeveCard):
		buff = self.cards[handCard]
		self.cards[handCard] = self.sleeve[sleeveCard]
		self.sleeve[sleeveCard]
		return "Didn't see nothin'!"


class DiscardPile(Hand):
	'''DiscardPile: An object that represents a discard pile used 
	in most card games. Subclass of Hand. Size defaults to 
	being large enough to discard an entire standard poker deck.'''
	cards = []
	size = 52


class UpPile(Hand):
	'''UpPile: An object that represents the face up cards in a card 
	game like Texas Hold 'Em, but could represent something like a 
	Tarot reading. Subclass of Hand. Size defaults for Texas 
	Hold 'Em'''
	cards = []
	size = 5


class PokerHand(Hand):
	'''PokerHand: An object that represents and evaluates a poker 
	hand. Subclass of Hand. Defaults to a best five of seven 
	game like Seven Card Stud or Texas Hold 'Em.'''
	# attributes
	size = 7
	handsDict = {0: 'Straight flush',
				 1: 'Four of a kind',
				 2: 'Full house',
				 3: 'Straight',
				 4: 'Flush',
				 5: 'Three of a kind',
				 6: 'Two pair',
				 7: 'Pair',
				 8: 'High'}
	
	
	# overrides x = y
	def __eq__(self, other):
		scoreSelf, cardsSelf = self.eval(self.cards[:])
		scoreOther, cardsOther = other.eval(other.cards[:])
		if scoreSelf != scoreOther:
			return False
		else:
			for i in range(len(cardsSelf)):
				if cardsSelf[i][0] != cardsOther[i][0]:
					return False
			else:
				return True


	# overrides x >= y
	def __ge__(self, other):
		if self.__gt__(other) or self.__eq__(other):
			return True
		else:
			return False


	# overrides x > y
	def __gt__(self, other):
		scoreSelf, cardsSelf = self.eval(self.cards[:])
		scoreOther, cardsOther = other.eval(other.cards[:])
		# remember lower score is better
		if scoreSelf < scoreOther:
			return True
		elif scoreSelf > scoreOther:
			return False
		else:
			for i in range(len(cardsSelf)):
				if cardsSelf[i][0] > cardsOther[i][0]:
					return True
				elif cardsSelf[i][0] < cardsOther[i][0]:
					return False
			else:
				return False


	# overrides x <= y
	def __le__(self, other):
		if self.__lt__(other) or self.__eq__(other):
			return True
		else:
			return False


	# overrides x < y
	def __lt__(self, other):
		scoreSelf, cardsSelf = self.eval(self.cards[:])
		scoreOther, cardsOther = other.eval(other.cards[:])
		# remember lower score is better
		if scoreSelf > scoreOther:
			return True
		elif scoreSelf < scoreOther:
			return False
		else:
			for i in range(len(cardsSelf)):
				if cardsSelf[i][0] < cardsOther[i][0]:
					return True
				elif cardsSelf[i][0] > cardsOther[i][0]:
					return False
			else:
				return False


	# overrides x != y
	def __ne__(self, other):
		if self.__eq__(other):
			return False
		else:
			return True


	# evaluates the hand, returning the best and it's score
	def eval(self, hand):
		# returns indices of cards that match match in a list
		def match(match=14, hand=[], attr=0):
			result = []
			for i in range(len(hand)):
				if hand[i][attr] == match[attr]:
					result.append(i)
			return result
	
	
		# given a list of values, returns a list of runs
		def runs(numList, inc=1):
			buff = numList.pop()
			if numList:
				result = runs(numList, inc)
				if result[-1][-1] + inc != buff:
					result.append([])
				result[-1].append(buff)
			else:
				result =[[buff],]
			return result
	
	
		# returns a list of the sets
		def getSets(hand=[], attr=0):
			result = {}
			for card in hand:
				if not result.has_key(card[attr]):
					result[card[attr]] = match(match=card, hand=hand[:], attr=attr)
			return result
		
		
		# returns a list of the cards of the runs
		def getRuns(hand=[], attr=0):
			# handle the variable ace
			for card in hand:
				if card[0] == 14:
					hand.append([1, card[1]])
			
			# find the runs in the hand
			result = []
			setsDict = getSets(hand, attr)
			if setsDict:
				uniqNums = setsDict.keys()
				uniqNums.sort()
				runsList = runs(uniqNums[:], 1)
		
				# return the cards in the run
				result = []
				for run in runsList:
					result.append([],)
					for num in run:
						iList = setsDict[num]
						# if multiple permutations of the run, put a list 
						# of the card that can fit in a slot in that slot
						if len(iList)==1:
							result[-1].append(hand[iList[0]])
						else:
							result[-1].append([],)
							for card in iList:
								result[-1][-1].append(hand[card])
				for run in result: run.reverse()
				result.reverse()
			return result
	
	
		# returns list of suits
		def getSuits(hand, attr=1):
			return getSets(hand, attr)
			

		# given a dict of lists and an int, removes dict 
		# members when len of list doesn't = int
		def filterLen(theDict, size):
			for k in theDict.keys():
				if len(theDict[k]) != size:
					theDict.pop(k)
			return theDict
		
		
		# given of dict of lists and an int, removes dict
		# members when len < int
		def filterLenLT(theDict, size):
			for k in theDict.keys():
				if len(theDict[k]) < size:
					theDict.pop(k)
			return theDict
		
		
		# returns the hand there is a size of a kind
		def isOfAKind(hand, size, handSize=5):
			hand.sort()
			hand.reverse()
			setsDict = getSets(hand[:])
			setsDict = filterLen(setsDict, size)
			if setsDict:
				sets = setsDict.keys()
				sets.sort()
				result = []
				setsDict[sets[-1]].sort()
				setsDict[sets[-1]].reverse()
				for i in setsDict[sets[-1]]:
					result.insert(0, hand.pop(i))
				result.extend(hand)
				return result[:handSize]
			else:
				return None
		
		
		# when all else fails, returns the high card hand
		def isHigh(hand, size=5):
			hand.sort()
			hand.reverse()
			return hand[:size]
	
	
		# returns hand if there is a pair
		def isPair(hand, size=5):
			result = isOfAKind(hand[:], 2)	
			return result
			
		
		# returns hand if there are two pair
		def is2Pair(hand, size=5):
			hand.sort()
			setsDict = getSets(hand[:])
			setsDict = filterLen(setsDict, 2)
			if len(setsDict) >= 2:
				result = []
				popList = []
				pairs = setsDict.keys()
				pairs.sort()
				for i in setsDict[pairs[-1]]:
					result.insert(0, hand[i])
					popList.append(i)
				for j in setsDict[pairs[-2]]:
					result.insert(0, hand[j])
					popList.append(j)
				result.reverse()
				hand.reverse()
				for x in range(len(hand)):
					if not popList.count(x): 
						result.insert(0, hand[x])
						break
				result.reverse()
				return result[:size]
			else:
				return None
		
		
		# returns hand if there are trips
		def isTrip(hand, size=5):
			result = isOfAKind(hand[:], 3)
			return result
		
		
		# returns hand if a straight
		def isStraight(hand, size=5):
			result = []
			theRuns = getRuns(hand[:])
			for aRun in theRuns:
				if len(aRun) >= size:
					for theNum in aRun:
						if type(theNum[0]) == type(list()):
							result.append(theNum[0])
						else:
							result.append(theNum)
					break
			return result[:size]
		
		
		# returns hand if a flush
		def isFlush(hand, size=5):
			hand.sort()
			hand.reverse()
			setsDict = getSets(hand[:], 1)
			setsDict = filterLenLT(setsDict, size)
			if setsDict:
				sets = setsDict.keys()
				sets.sort()
								
				# compare the sets to find the highest
				compSet = setsDict.pop(sets.pop())
				while setsDict:
					buff = setsDict.pop(sets.pop())
					for card in range(size):
						if hand[compSet[card]][0] > hand[buff[card]][0]:
							break
						elif hand[compSet[card]][0] < hand[buff[card]][0]:
							compSet = buff
							break
				
				# construct the best hand
				compSet.reverse()
				result = []
				for i in compSet:
					result.insert(0, hand.pop(i))
				if len(result) < size: result.extend(hand)
				return result[:size]
			else:
				return None
							
		
		# returns hand if full house
		def isBoat(hand, size=5):
			hand.sort()
			setsDict = getSets(hand[:])
			tripsDict = filterLen(setsDict.copy(), 3)
			pairsDict = filterLen(setsDict.copy(), 2)
			if (len(tripsDict) >= 2) or (tripsDict and pairsDict):
				result = []
				popList = []
				trips = tripsDict.keys()
				trips.sort()
				
				# add the trip to the hand
				for i in tripsDict.pop(trips.pop()):
					result.append(hand[i])
					popList.append(i)
				
				# add the pair to the hand
				for k in trips:
					pairsDict[k] = tripsDict[k]
				pairs = pairsDict.keys()
				pairs.sort()
				thePair = pairsDict.pop(pairs.pop())
				for j in range(2):
					result.append(hand[thePair[j]])
					popList.append(thePair[j])
				
				# construct rest of hand
				if len(result) < size:
					print result
					[hand.pop(x) for x in popList]
					hand.reverse()
					result.extend(hand)					
				return result[:size]
			else:
				return None
			
		
		# returns hand if quads
		def isQuads(hand, size=5):
			result = isOfAKind(hand[:], 4)
			return result
			
		
		# returns hand if straight flush
		def isStraightFlush(hand, size=5):
			result = None
			flushDict = getSuits(hand[:])
			flushDict = filterLenLT(flushDict.copy(), size)
			if flushDict:
				flushHand = []
				for suit in flushDict.iterkeys():
					[flushHand.append(hand[card]) for card in flushDict[suit]]
				result = isStraight(flushHand[:])
			return result
				
		
		# module main line
		result = None
		handTypes = [isStraightFlush, 
					 isQuads, 
					 isBoat, 
					 isFlush, 
					 isStraight, 
					 isTrip, 
					 is2Pair, 
					 isPair, 
					 isHigh]
		for i in range(len(handTypes)):
			result = handTypes[i](hand[:])
			if result: break
		return i, result


class Test(PokerHand):
	size = 10
	cards = [[14, 0],
			 [2, 0],
			 [3, 0],
			 [4, 0],
			 [5, 0],
			 [14, 2],
			 [2, 2],
			 [3, 2],
			 [4, 2],
			 [5, 2]]
	
	def __init__(self): pass


class TarotHand(Hand):
	'''TarotHand: This represents a traditional tarot spread used for 
	divination. It defaults to the Celtic Cross. Subclass of Hand. 
	Note: This object in no way claims any supernatural ability.'''
	size = 10
	cardPosition = {0: 'Signifier',
					1: 'Problem',
					2: 'Problem Foundation',
					3: 'Immediate Past',
					4: 'Best Outcome',
					5: 'Immediate Future',
					6: 'Querent Approach',
					7: 'Querent Surrounding',
					8: 'Hopes and Fears',
					9: 'Final Outcome'}
		

def take5(hand, deck, x=5):
	'''take5: a function that quickly fills a hand from a deck.
	defaults to taking 5 cards, hence the name.'''
	for i in range(x):
		hand.take(deck.draw())