I'm working on different Project Euler problems through writing code on my own and watching Youtube videos.
While working on problem 54, the code that the author of the Youtube video wrote while defining a function in Python 2.7 is
def __init__(self, *args):
self.cards = sorted(args, key=lambda c: -c.rank)
self.duplicates = {}
for card in self.cards:
if card.rank in self.duplicates:
continue
_duplicates = 0
for card2 in self.cards:
if card.rank == card2.rank:
_duplicates += 1
self.duplicates[card.rank] = _duplicates
which seems to work fine for him. When I run this code on Python 3.0, my Pycharm compiler returns
File "C:/Users/Matthew/PycharmProjects/Project_Euler/Euler54.py", line 51, in __init__
self.cards = sorted(args, key=lambda c: -c.rank)
File "C:/Users/Matthew/PycharmProjects/Project_Euler/Euler54.py", line 51, in <lambda>
self.cards = sorted(args, key=lambda c: -c.rank)
AttributeError: 'function' object has no attribute 'rank'
I have tried changing
self.cards = sorted(args, key=lambda c: -c.rank)
to
self.cards = sorted(args, key=lambda c: -c.rank())
but this appears to be an incorrect fix. I'm not sure how to resolve this error on my own. I could look into writing a different solution, as the strategy shown by the author of the video wasn't friendly to a beginner. The full program is pasted below (I'm not sure if I should include this as I'm not formally looking for a code review. I'm trying to get the program to run so that I can complete the exercise).
import time
from collections import namedtuple
start = time.time()
Card = namedtuple('Card', ['rank', 'suit'])
Ace = 14
King = 13
Queen = 12
Jack = 11
Ten = 10
def map_rank(rank_to_map):
if rank_to_map == 'A':
return Ace
if rank_to_map == 'K':
return King
if rank_to_map == 'Q':
return Queen
if rank_to_map == 'J':
return Jack
if rank_to_map == 'T':
return Ten
return int(rank_to_map)
def map_suit(suit_to_map):
if suit_to_map == 'H':
return 1
if suit_to_map == 'D':
return 2
if suit_to_map == 'C':
return 3
# S = Spades
return 4
def map_card(card_to_map):
return Card(rank=map_rank(card_to_map[0]), suit=map_suit(card_to_map[1]))
class Hand(object):
def __init__(self, *args):
self.cards = sorted(args, key=lambda c: -c.rank)
self.duplicates = {}
for card in self.cards:
if card.rank in self.duplicates:
continue
_duplicates = 0
for card2 in self.cards:
if card.rank == card2.rank:
_duplicates += 1
self.duplicates[card.rank] = _duplicates
def __cmp__(self, other):
for rule in self.rules():
rule = getattr(self, rule)
val = rule(other)
if val:
return val
def rules(self):
return []
def _straight(self):
last_card_rank = False
for card in self.cards:
if last_card_rank and last_card_rank != card.rank + 1:
return False
last_card_rank = card.rank
return True
def _flush(self):
last_card_suit = False
for card in self.cards:
if last_card_suit and last_card_suit != card.suit:
return False
last_card_suit = card.suit
return True
def _duplicate_count(self, c):
for rank, value in self.duplicates.items():
if value == c:
yield rank
class PokerHand(Hand):
def rules(self):
rules = super(PokerHand, self).rules()
rules.append('royal_flush')
rules.append('straight_flush')
rules.append('four_of_a_kind')
rules.append('full_house')
rules.append('flush')
rules.append('straight')
rules.append('three_of_a_kind')
rules.append('two_pair')
rules.append('highest_card')
return rules
def royal_flush(self, other):
h1 = self.cards[0].rank == Ace and self._flush() and self._straight()
h2 = other.cards[0].rank == Ace and other._flush() and other._straight()
if h1 and not h2:
return 1
if h2 and not h1:
return -1
if h1 and h2:
return 0
def straight_flush(self, other):
h1 = self._flush() and self._straight()
h2 = other._flush() and other._straight()
if h1 and not h2:
return 1
if h2 and not h1:
return -1
if h1 and h2:
return self.highest_card(other)
def four_of_a_kind(self, other):
return self._of_a_kind(other, 4)
def three_of_a_kind(self, other):
return self._of_a_kind(other, 3)
def _of_a_kind(self, other, value):
h1 = [r for r in self._duplicate_count(value)]
h2 = [r for r in other._duplicate_count(value)]
if len(h1) > len(h2):
return 1
if len(h2) > len(h1):
return -1
for h1_c, h2_c in zip(h1, h2):
if h1_c > h2_c:
return 1
if h2_c > h1_c:
return -1
def two_pair(self, other):
return self._of_a_kind(other, 2)
def full_house(self, other):
h1_3 = [r for r in self._duplicate_count(3)]
h2_3 = [r for r in other._duplicate_count(3)]
h1_2 = [r for r in self._duplicate_count(2)]
h2_2 = [r for r in other._duplicate_count(2)]
if h1_3 and h1_2 and not (h2_3 and h2_2):
return 1
if h2_3 and h2_2 and not (h1_3 and h1_2):
return -1
if h1_3 and h1_2 and h2_3 and h2_2:
for h1_c, h2_c in zip(h1_3, h2_3):
if h1_c > h2_c:
return 1
if h2_c > h1_c:
return -1
for h1_c, h2_c in zip(h1_2, h2_2):
if h1_c > h2_c:
return 1
if h2_c > h1_c:
return -1
def flush(self, other):
h1 = self._flush
h2 = other._flush
if h1 and not h2:
return 1
if h2 and not h1:
return -1
if h1 and h2:
return self.highest_card(other)
def straight(self, other):
h1 = self._straight
h2 = other._straight
if h1 and not h2:
return 1
if h2 and not h1:
return -1
if h1 and h2:
return self.highest_card(other)
def highest_card(self, other):
for c1,c2 in zip(self.cards, other.cards):
if c1.rank != c2.rank:
return 1 if c1.rank > c2.rank else -1
raise Exception("tie in highest_card")
def gen_hands(s):
cards = [map_card for c in s.split(' ')]
return PokerHand(*cards[0:5]), PokerHand(*cards[5:])
p1_wins = 0
with open('poker.txt') as f:
for line in f:
h1, h2 = gen_hands(line.strip())
if h1 > h2:
p1_wins += 1
print(p1_wins)
The program won't run as pasted without adding the poker.txt file provided here. Please let me know if it was a mistake to create a post for this sort of attribute error on SE. I wasn't sure if I should post code for Project Euler on this site.
On line 195,
cards = [map_card for c in s.split(' ')]
in
def gen_hands(s):
cards = [map_card for c in s.split(' ')]
return PokerHand(*cards[0:5]), PokerHand(*cards[5:])
generates a list containing a large amount of references to the map_card
function. This was mistakenly written as map_card
instead of map_card(c)
. It should be
def gen_hands(s):
cards = [map_card(c) for c in s.split(' ')]
return PokerHand(*cards[0:5]), PokerHand(*cards[5:])