Skip to content

Instantly share code, notes, and snippets.

@henryiii henryiii/cards.py
Last active Aug 29, 2015

Embed
What would you like to do?
base for card games
# -*- coding: utf-8 -*-
"""
Created on Sun Jul 20 17:27:33 2014
@author: henryiii
"""
# http://en.wikipedia.org/wiki/Playing_cards_in_Unicode
from __future__ import unicode_literals, division
import random
from collections import Counter
from functools import total_ordering
import pickle
import socket
try:
input = raw_input
except NameError:
pass
SUITS = ('None','Clubs','Diamonds','Hearts','Spades')
SUITS_SIMPLE = 'xCDHS'
SUITS_UNICODE = 'x♣♦♥♠'
NAMES = ('None','A','2','3','4',
'5','6','7','8',
'9','10','J','Q','K','?')
POKERHANDS = (
'High',
'One pair',
'Two pair',
'Three of a kind',
'Straight',
'Flush',
'Full house',
'Four of a kind',
'Straight flush',
'Royal flush',
'Five of a kind')
@total_ordering
class Card(object):
def __init__(self, rank, suit=None):
if suit is not None:
self._num = 0
self.rank = rank
self.suit = suit
else:
if rank > 52 or rank < 0:
raise IndexError('Not valid card')
self._num = rank
@classmethod
def fromstr(cls, string):
self = cls(0)
string = string.upper()
for n in range(1,5):
if SUITS_UNICODE[n] in string or SUITS_SIMPLE[n] in string:
self.suit = n
string = string.replace(SUITS_UNICODE[n],'')
string = string.replace(SUITS_SIMPLE[n],'')
for n,rank in enumerate(NAMES):
if rank in string:
self.rank = n
return self
raise ValueError()
@property
def suit(self):
return self._num % 4 + 1
@suit.setter
def suit(self,val):
assert 1 <= val <= 4
self._num = (self._num//4)*4 + val - 1
@property
def rank(self):
val = self._num // 4 + 1
return val if val != 1 else 14
@rank.setter
def rank(self,val):
assert 1 <= val <= 14
val = val if val != 14 else 1
self._num = self._num%4 + (val-1)*4
@property
def lowrank(self):
val = self.rank
return val if val != 14 else 1
@property
def color(self):
return int(1 < self.suit < 4)
def __str__(self):
return NAMES[self.lowrank] + SUITS_UNICODE[self.suit]
def __repr__(self):
return '{}({},{})'.format(
self.__class__.__name__,
self.rank, self.suit)
def __eq__(self, other):
return self._num == other._num
def __lt__(self, other):
return (self.rank < other.rank
if self.rank != other.rank
else self.suit < other.suit)
def __int__(self):
return self._num
class Deck(object):
def __init__(self):
self.cards = [Card(i) for i in range(52)]
self.shuffle()
def shuffle(self):
random.shuffle(self.cards)
def draw(self):
return self.cards.pop()
def reput(self, card):
self.cards.insert(0,card)
def __str__(self):
return ('[' + ' '.join(map(str,self.cards)) + ']')
class Hand (object):
def __init__(self, deck=None, handnum = 0):
self.cards = []
self.deck = deck
self.handnum = handnum
def draw(self, num=5):
for i in range(num):
self.cards.append(self.deck.draw())
self.cards.sort()
def add_cards(self, *cards):
for card in cards:
self.cards.append(card)
@classmethod
def fake_hand(cls, cardstr):
self = cls()
self.add_cards(*cards_from_string(cardstr))
return self
def discard(self,*cards):
for card in cards:
if self.deck is not None:
self.deck.reput(card)
self.cards.remove(card)
def redraw(self, *cards):
self.discard(*cards)
self.draw(len(cards))
def __str__(self):
return ('[' + ' '.join(map(str,self.cards)) + ']')
@total_ordering
class PokerHand (Hand):
def score(self):
return self.match()['score']
def match(self):
return matchhand(self.cards)
def name(self):
return POKERHANDS[self.match()['type']-1]
def __lt__(self, other):
return self.score() < other.score()
def __eq__(self, other):
return self.score() == other.score()
def __str__(self):
return ('[' + ' '.join(map(str,self.match()['cards'])) + ']')
class PokerGame(object):
def __init__(self, hands=2, money=100):
self.deck = Deck()
self.hands = [None]*hands
self.restart()
self.moneys = [money]*hands
self.anti = 5
def restart(self):
self.deck = Deck()
for n in range(len(self.hands)):
self.hands[n] = PokerHand(self.deck, n)
self.hands[n].draw()
def winner(self):
return [hand.handnum for hand in sorted(self.hands,reverse=True)]
def __str__(self):
return '\n'.join('Hand {}: {} {}'.format(hand.handnum + 1, str(hand), hand.name()) for hand in sorted(self.hands,reverse=True))
def matchhand(cards):
values = dict()
cards = list(sorted(cards, reverse=True))
# Counting groups
ranks = Counter(c.rank for c in cards).most_common()
suits = Counter(c.suit for c in cards).most_common()
# Ordering ranks
for rank, freq in ranks:
if freq > 1:
positions = [i for i in range(len(cards)) if cards[i].rank == rank]
for n,v in enumerate(positions):
cards.insert(n,cards.pop(v))
values['cards'] = cards
# Straight checking vals
vals = set(c.lowrank for c in cards)
values['flush'] = len(suits) == 1
values['runs'] = ranks
values['group'] = ranks[0][1]
values['twogroup'] = len(ranks)==3 and ranks[1][0]==2
values['royal']=vals=={1,10,11,12,13}
values['straight'] = len(ranks) == 5 and (min(vals)+4 == max(vals) or values['royal'])
if values['flush']:
if values['straight']:
if values['royal']:
values['type']=10
else:
values['type']=9
else:
values['type']=6
elif values['straight']:
values['type']=5
elif values['twogroup']:
if ranks[0][1] == 3:
values['type'] = 7
else:
values['type'] = 3
else:
values['type'] = (1,2,4,8,11)[ranks[0][1]-1]
#Score:
# (type)(r1)(r2)...(s1)...
# where each digit is rank
# A is 14; vals are 1 hex digits each
# last five are suits
values['score'] = values['type'] * 16**10
for n,card in enumerate(cards):
values['score'] += card.rank * 16**(9-n)
values['score'] += card.suit * 16**(4-n)
return values
def pprint_values(values):
print '''Hand: {v[cards][0]} {v[cards][1]} {v[cards][2]} {v[cards][3]} {v[cards][4]}
Runs: {v[runs]}
R: {v[royal]}, \
F: {v[flush]}, \
S: {v[straight]}
Group: {v[group]}, \
Second group?: {v[twogroup]}
Type: {t}
Score: {v[score]:X}\
'''.format(v=values,t=POKERHANDS[values['type']-1])
def cards_from_string(strings):
return [Card.fromstr(cstr) for cstr in strings.split()]
def poker_play_console():
players = int(input('How many players? '))
game = PokerGame(players)
pot = 0
bet = 0
for player in range(players):
print "Player {}'s turn.".format(player + 1)
game.moneys[player] -= game.anti
pot += game.anti
print "Anti: ${}".format(game.anti, game.moneys[player])
print 'Your cards: {} {}, ${}'.format(game.hands[player], game.hands[player].name(), game.moneys[player])
betstr = input('What would you like to bet? 0 to call, f to fold')
if betstr.lower() != 'f':
if betstr!='':
bet += int(betstr)
game.moneys[player] -= bet
pot += bet
for player in range(players):
print "Player {}'s turn.".format(player + 1)
print 'Your cards: {} {}, ${}'.format(game.hands[player], game.hands[player].name(), game.moneys[player])
cards = cards_from_string(input('What cards shall you discard? '))
game.hands[player].redraw(*cards)
print 'Final hand: {} {}'.format(game.hands[player], game.hands[player].name())
bet = int(input('What would you like to bet? '))
game.moneys[player] -= bet
pot += bet
print "Winner: Player {}".format(game.winner()[0] + 1)
print game
game.moneys[game.winner()[0]] += pot
return game
if __name__ == '__main__':
game = poker_play_console()
def test_simple():
deck = Deck()
hand1 = PokerHand(deck)
hand1.draw()
hand2 = PokerHand(deck)
hand2.draw()
print
print '-'*35
print
print hand1
pprint_values(hand1.match())
print
print hand2
pprint_values(hand2.match())
print 'hand2 > hand1?', hand2 > hand1
hand3 = PokerHand.fake_hand('4♥ 5♣ 6♣ 7♥ 8♠')
pprint_values(hand3.match())
hand4 = PokerHand.fake_hand('A♥ q♣ K♣ 10♥ J♠')
pprint_values(hand4.match())
hand5 = PokerHand.fake_hand('A♥ Q♥ K♥ 10♥ J♥')
pprint_values(hand5.match())
hand5 = PokerHand.fake_hand('3♥ Q♥ K♥ 10♥ J♥')
pprint_values(hand5.match())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.