Hi. Before I start I'll just let you know that this isn't for school, I'm just doing python as a hobby which I have pretty much taught my self.

I'm working on a project, it is the "cheat!" card game written in python, currently CLI but when it is bug-free and clean I'll try making a GUI. I made this a while ago, by myself, and it never actually completely worked. Now I've written it a second time, much more efficient, cleaner and legible. But it still is in the debugging stage. Right now, the IDE I use (PyScripter, I recommend it) crashes when it gets to a certain point in it, and command prompt just stops and does nothing when it gets to that point, being when a player wins. I will attach the file as a .txt file and paste it here. Just save it and then rename it with a .py extension and then open it up in your IDE if you want to look at it.

I have no specific questions, but take a look if you want, maybe some suggestions and comments or questions, constructive criticism, if you're feeling very generous test and debug it to find what's wrong (not that I expect you to).

Thats all for now.

Here's the code:

# "cheat.py" Module
# Created on 14-June-2009
# compiled with python 3.0.1 -> www.python.org
# edited with PyScripter -> http://pyscripter.googlepages.com
# by Mitchell Kember
# Copyright (c) 2009. All rights reserved.

"""
Created on 14-June-2009

This python script is Cheat version 2, the "Cheat" card game. It
is played using the command line. Future versions will use a GUI.

@author: Mitchell Kember
"""

from random import randint
from random import shuffle
from re import split

class player:
    """A player in the cheat game."""

    def __init__(self, name):
        """Creates a player.
        params: name - The name of the player."""
        self.name = name
        self.hand = []

    def __eq__(self, obj):
        """How to comare two player objects for equality."""
        if not isinstance(obj, player):
            return False
        return self.name == obj.name and self.hand == obj.hand

    def __ne__(self, obj):
        """How to comare two player objects for inequality."""
        return not self.__eq__(obj)

    def __str__(self):
        """The string representation of a player object."""
        return self.name

class human(player):
    """A human player in the cheat game."""

    def __init__(self, name):
        """Creates a human (human) player.
           params: name - See player class"""
        super(human, self).__init__(name)

    def __eq__(self, obj):
        """How to compare two human objects for equality."""
        return super(human, self).__eq__(obj)

    def __ne__(self, obj):
        """How to compare two human objects for inequality."""
        return not self.__eq__(obj)

class computer(player):
    """A computer player in the cheat game."""

    def __init__(self, name, difficulty=randint(1, 3)):
        """Creates a player and then initializes computer-specific fields.
        params: name: See player class.
        [difficulty = randint(1, 3)] - The difficulty of the computer player."""
        super(computer, self).__init__(name)
        self.difficulty = difficulty

    def __eq__(self, obj):
        """How to compare two computer objects for equality."""
        return super(computer, self).__eq__
        (obj) and self.difficulty == obj.difficulty

    def __ne__(self, obj):
        """How to compare two computer objects for inequality."""
        return not self.__eq__(obj)

class diff:
    """Enum class for a computer player's difficulty."""

    unknown = 0
    EASY = 1
    MEDIUM = 2
    HARD = 3

    def get_name(difficulty):
        """Returns the name of the difficulty level."""
        return ['easy', 'medium', 'hard'][difficulty]

class deck:
    """Deck of 52 cards for the cheat game."""

    def __init__(self, shuffled=True):
        """Creates a new deck of 52 regular playing cards.
        params: [shuffled = true] - Whether deck will be shuffled or not."""
        self.cards = []
        for s in range(1, 5):
            for v in range(1, 14):
                self.cards.append(card(v, s))
        if shuffled:
            self.shuffle_deck()
    def shuffle_deck(self):
        """Shuffles the deck."""
        shuffle(self.cards)

    def sort_hand(hand):
        """Sorts a player's hand of cards by value (ace through king).
        params: hand - The list of card objects to sort."""
        vals = []
        for c in hand:
            vals.append((c, c.val))
        vals.sort(key=lambda a:a[1])
        return [c[0] for c in vals]

class card:
    """A card in that makes up a deck."""

    def __init__(self, value, suit):
        """Creates a card.
        params: value - The card's value. (see the value class)
                suit - The card's suit. (see the suit class)"""
        self.val = value
        self.su = suit

    def __str__(self):
        """Returns the name of the card. (Format: \"The [value] of [suit]\")"""
        return "the " + value.get_name(self.val) + " of " + \
            suit.get_name(self.su)

    def __eq__(self, obj):
        """How to compare two card objects for equality."""
        if not isinstance(obj, card):
            return False
        return self.val == obj.val and self.su == obj.su

    def __ne__(self, obj):
        """How to compare two card objects for inequality."""
        return not self.__eq__(obj)

    def __gt__(self, obj):
        """How to compare two objects for the greatest."""
        if not isinstance(obj, card):
            raise TypeError
        return self.val > obj.val

    def __ge__(self, obj):
        """How to compare two objects for the greatest or equality."""
        if not isinstance(obj, card):
            raise TypeError
        return self > obj or self.val == obj.val

class value:
    """Enum class for a card's value."""

    unknown = 0
    ACE = 1
    TWO = 2
    THREE = 3
    FOUR = 4
    FIVE = 5
    SIX = 6
    SEVEN = 7
    EIGHT = 8
    NINE = 9
    TEN = 10
    JACK = 11
    QUEEN = 12
    KING = 13

    def get_name(value):
        """Returns the name of a value.
        params: value - The value to get the name of."""
        return ['unknown', 'ace', 'two', 'three', 'four', 'five', 'six',
            'seven', 'eight', 'nine', 'ten', 'jack', 'queen', 'king'][value]

class suit:
    """Enum class for a card's suit."""

    unknown = 0
    SPADES = 1
    CLUBS = 2
    HEARTS = 3
    DIAMONDS = 4

    def get_name(suit):
        """Returns the name of a suit.
        params: suit - The suit to get the name of."""
        return ['unknown', 'spades', 'clubs', 'hearts', 'diamonds'][suit]

    def get_colour(suit):
        """Returns the colour of a suit.
        params: suit - The suit to get the colour of."""
        if suit == 1 or suit == 2:
            return 'black'
        elif suit == 3 or suit == 4:
            return 'red'
        else:
            return 'unknown'

class game:
    """Cheat cardgame."""

    # game.gameplay: The rules of cheat and how it is played.
    gameplay = """                         Cheat gameplay:
\tThe object of the game is to get rid of all your cards. The winner is the first
person to get rid of all their cards.

\tTo start the game, the entire deck of 52 cards is dealt evenly to everyone. Now,
the player with the ace of spades will start. On this first turn, he or she is
allowed to put down all of his or her aces. He or she will take the cards, place
them face-down on the pile of cards and say the number of cards they are putting
down and that turn's card (ex \"2 twos\", \"1 jack\"). The next player will put
down twos, the next three, etc. until king, then it goes back to aces.

\tOn each player's turn, they will have to cheat if they do not have
any of the correct card. This means they will put down cards other than the correct
ones. You are NOT allowed to put down your cards and say that you are putting down
a different number of cards (ex say \"1 king\" but actually put down two). So
you can put down an ace and an eight when you are supposed to put down kings
and say \"2 kings\". You can also cheat even if you don't have to, if you are
supposed to put down fours and you have 2 fours, you could put the fours down
and also put a queen and say \"3 fours\", or even put down a jack and say
\"1 four\", and not even put down the fours that you have at all!

\tAfter any player's turn, any other player can challenge them by accusing
\"Cheat!\". If the player cheated, they must take up the entire pile. If the
player hasn't cheated, the challenger must add the entire pile to their hand.

\tThis will continue until a player wins, and then the other players may
keep playing to get a 2nd place winner, and 3rd place winner, etc."""
    # Ranks for the winners
    ranks = ['first', 'second', 'third', 'fourth', 'fifth', 'sixth',
        'seventh', 'eighth']

    def __init__(self, *players):
        """Creates a new game.
        params: *players - the players in this game."""
        self.players = list(players)  # (So that it is mutable)
        # Make sure there are no more than eight players:
        if len(self.players) > 8:
            print("There cannot be more than 8 players.")
            return
        # Make sure all players are instances of the class player
        for p in self.players:
            if not isinstance(p, player):
                print('Error: players are not player objects.')
                return
        # self.c_player: The player whose turn it is.
        # self.c_value: This turn's card value (ace through king).
        self.winners = []
        self.deck = deck()
        self.pile = []
        self.player_iter = iter(self.players)
        self.c_player = None
        self.value_iter = iter(range(1, 14))
        self.c_value = 0

        self.deal_cards()
        # Move the player with the ace of spades to the front of the list
        for ind, p in enumerate(self.players):
            for c in p.hand:
                if c == card(value.ACE, suit.SPADES):
                    self.players[0], self.players[ind] = \
                        self.players[ind], self.players[0]
        self.next_turn()

    def reset_player_iter(self):
        """Resets the player iterator by re-assigning it."""
        self.player_iter = iter(self.players)

    def reset_value_iter(self):
        """Resets the value iterator by re-assigning it."""
        self.value_iter = iter(range(1, 14))

    def deal_cards(self):
        """Deals the deck to each player."""
        i = 0
        for c in self.deck.cards:
            if i == len(self.players):
                i = 0
            self.players[i].hand.append(c)
            i += 1
        # Delete self.deck, and use self.pile instead so to not have to use
        # self.deck.cards.
        del self.deck

    def update_current(self):
        """Updates the current player and current value instance variables."""
        while True:
            # Go to the next player in the self.players list
            try:
                self.c_player = next(self.player_iter)
            # If at the end of the list, go back to the start by calling
            # self.reset_player_iter().
            except StopIteration:
                self.reset_player_iter()
                self.c_player = next(self.player_iter)
            if self.c_player not in self.winners:
                break
        # Go to the next card value in the list of numbers 1 through 14
        # (enum values of the value class).
        try:
            self.c_value = next(self.value_iter)
        # If at the end of the list, go back to the start by calling
        # self.reset_value_iter().
        except StopIteration:
            self.reset_value_iter()
            self.c_value = next(self.value_iter)

    def next_turn(self):
        """Goes to the next turn in the game."""
        # Check to see if there is only one player left (game is over).
        # If there is, then print the ranks and end.
        if len([p for p in self.players if p not in self.winners]) == 1:
            print("The game is over.\n\nThe ranks:\n")
            for place, p in enumerate(self.winners):
                print("{0}: {1}".format(game.ranks[place], p))
            print("{0}. {1}".format(game.ranks[len(self.winners)], self.c_player))
            return
        # Update self.c_player and self.c_value instance variables.
        self.update_current()
        print("  {0}  ".format(len(self.c_player.hand))*10)
        print("It's {0}'s turn. {0}, put down your {1}s.".format(
            self.c_player, value.get_name(self.c_value)), end=' '*6)
        input("Press enter to continue...")
        # Go on to either human_turn() or computer_turn()
        if isinstance(self.c_player, computer):
            self.computer_turn()
        elif isinstance(self.c_player, human):
            self.human_turn()
        else:
            print("Error: Players are not human or computer objects.")

    def human_turn(self):
        """Executed when it is a human player's turn."""
        # Sort the player's cards by values, so that they go from ace to king.
        self.c_player.hand = deck.sort_hand(self.c_player.hand)
        print("{0}, here are your cards.".format(self.c_player) + \
            "Type the number keys of the cards you want to play" , end=' '*6)
        input("Press enter to continue...")
        # Print the player's cards, and
        # if the card's value is the self_c_value, then print it in CAPS.
        for ind, c in enumerate(self.c_player.hand):
            print("{0} : ".format(ind), end='')
            if c.val == self.c_value:
                print(str(c).upper())
            else:
                print(c)
        # Loop until atleast one valid card is chosen by the human
        while True:
            # del_indexes: list to contain indexes of elements in card_indexes to
            # delete. They can't be deleted in the for loop because it would
            # mess it up if they are deleted while it is iterating them.
            del_indexes = []
            # card_indexes: The indexes of the cards put down.
            # The indexes can be entered seperated by spaces or commas.
            card_indexes = split('\W+', input("Enter the number keys: "))
            for ind, c in enumerate(card_indexes):
                # Make sure that each index is a number (digit).
                if not c.isdigit():
                    del_indexes.append(ind)
                    continue
                # Make sure the index is valid, that it is one of the indexes
                # in the player's hand of cards.
                if int(c) >= len(self.c_player.hand) or int(c) < 0:
                    del_indexes.append(ind)
            # Delete the indexes that were appended to del_indexes
            for ind in del_indexes:
                del card_indexes[ind]
            # Make sure at least one card is chosen.
            if not card_indexes:
                print("You have to chose at least one card.")
            else:
                break
        del del_indexes
        self.finish_turn(card_indexes)

    def computer_turn(self):
        """Executed when it is a computer player's turn."""
        card_indexes = []
        skip = False
        # If the player only has one card then just chose that one.
        if len(self.c_player.hand) == 1:
            card_indexes = [0]
            skip = True
        # If the computer player's difficulty is not diff.EASY, or if it is
        # and a random number from 1 to 3 is 1 (33% chance), then find all
        # the cards that the computer player has and can put down without
        # cheating.
        if (self.c_player.difficulty != diff.EASY or randint(1, 3) == 1) \
                                                            and not skip:
            for ind, c in enumerate(self.c_player.hand):
                if c.val == self.c_value:
                    card_indexes.append(ind)
        # What the EASY computer player does:
        if self.c_player.difficulty == diff.EASY and not skip:
            # If the computer player already put down some cards from the
            # 33% chance above, half the time leave it and don't add any more.
            # Add either 1, 2, 3 or 4 more random cards
            if not card_indexes or randint(1, 2) == 1:
                for i in range(randint(1, 4)):
                    while True:
                        rand_num = randint(0, len(self.c_player.hand)-1)
                        if rand_num not in card_indexes:
                            card_indexes.append(rand_num)
                            break
        # What the MEDIUM computer player does:
        elif self.c_player.difficulty == diff.MEDIUM and not skip:
            # It will add up to 3 cards if to the card_indexes if it is empy,
            # or 1/5 of the time when card_indexes is not empty
            if not card_indexes or randint(1, 5) == 1:
                for i in range(randint(1, 3)):
                    while True:
                        rand_num = randint(0, len(self.c_player.hand)-1)
                        if rand_num not in card_indexes:
                            card_indexes.append(rand_num)
                            break
        # What the HARD computer player does:
        elif self.c_player.difficulty == diff.HARD and not skip:
            pass
        self.finish_turn(card_indexes)

    def finish_turn(self, card_indexes):
        """Finishes the turn, human or computer.
        params: card_indexes - The indexes of the cards that are chosen."""
        print("{0} put down {1} {2}(s).".format(
            self.c_player, len(card_indexes), value.get_name(self.c_value)))
        # cards: The actual card objects that were put down.
        cards = [self.c_player.hand[int(i)] for i in card_indexes]
        # challenges: Get the challenges from other players.
        challenges = self.get_challenges(len(card_indexes))
        cheat = False
        # If there are any challenges:
        if challenges:
            # Maybe later make this only include one challenge
            for ch in challenges:
                if ch == challenges[-1]:
                    print("Cheat! called by {0}.".format(ch.name),
                        end=' '*6)
                else:
                    print("Cheat! called by {0}.".format(ch.name))
            input("Press enter to continue...")
            for c in cards:
                # If the player cheated...
                if c.val != self.c_value:
                    cheat = True
                    break
            # If the player has cheated:
            if cheat:
                print("{0} Cheated! {0} gets to pick up the pile!".format(
                    self.c_player), end=' '*6)
                self.c_player.hand += self.pile
                self.pile = []
            # If the player has not cheated:
            else:
                print("{0} is innocent! {1} gets to pick up the pile!".format(
                    self.c_player, challenges[0]), end=' '*6)
                for c in cards:
                    self.c_player.hand.remove(c)
                    self.pile.append(c)
                challenges[0].hand += self.pile
                self.pile = []
        # If the player has not been challenged:
        else:
            print("No one has accused {0} of cheating".format(self.c_player), end=' '*6)
            for c in cards:
                self.c_player.hand.remove(c)
                self.pile.append(c)
        input("Press enter to continue...")
        # If the player has won (has no cards left):
        if not self.c_player.hand:
            print("{0} won in {1} place!".format(self.c_player, game.ranks[len(self.winners)]))
            print("The game will continue.")
            input("Press enter to continue...")
            self.winners.append(self.c_player)
        self.next_turn()

    def get_challenges(self, cards_len):
        """Gets the challenges from other players.
        params: cards_len - The amount of cards the player put down."""
        if randint(1, 2) == 1:
            return self.human_challenge(cards_len) + self.computer_challenge(cards_len)
        else:
            return self.computer_challenge(cards_len) + self.human_challenge(cards_len)

    def human_challenge(self, cards_len):
        """Asks the human players if they would like to challenge.
        params: cards_len - The amount of cards the player put down."""
        # Get the human instances from the self.players list
        human_players = [p for p in self.players if isinstance(p, human) and p not in self.winners]
        # If the player whose turn it is is a human, remove him/her from the
        # list (if the player isn't a human, he/she wouldn't be in the list
        # anyway).
        if isinstance(self.c_player, human):
            human_players.remove(self.c_player)
        if not human_players:
            return []
        shuffle(human_players)
        human_challenges = []
        # Go through each player in human_players and ask if they want
        # to challenge, and give them information like how many cards the
        # the player put down, and how many this human already has.
        for h in human_players:
            num_cards = 0
            for c in h.hand:
                if c.val == self.c_value:
                    num_cards += 1
            print("{0}: Do you think {1} is cheating?".format(
                h, self.c_player), end=' '*6)
            input("Press enter for more info...")
            print("{0} claims to have put down {1} {2}(s).".format(
                self.c_player, cards_len, value.get_name(self.c_value)), end=' '*6)
            input("Press enter for more info...")
            print("You have {0} {1}(s).".format(num_cards, value.get_name(self.c_value)))
            challenge = input('Enter Cheat, or press enter to pass... ')
            if 'cheat' in challenge.lower():
                human_challenges.append(h)
        return human_challenges

    def computer_challenge(self, cards_len):
        """Gets the challengese from the computer players.
        params: cards_len - The amount of cards the player put down."""
        # Get the computer instances from the self.players list
        computer_players = [p for p in self.players if isinstance(p, computer) and p not in self.winners]
        # If the player whose turn it is is a computer player, remove him/her
        # from the list (if the player isn't a computer player, he/she
        # wouldn't be in the list anyway).
        if isinstance(self.c_player, computer):
            computer_players.remove(self.c_player)
        if not computer_players:
            return []
        shuffle(computer_players)
        computer_challenges = []
        for c in computer_players:
            # unless the computer player's difficulty is diff.EASY, challenge
            # if the number of cards the player put down plus the number of
            # this computer player has is more then four.
            if c.difficulty != diff.EASY:
                num_cards = 0
                for ca in c.hand:
                    if ca.val == self.c_value:
                        num_cards += 1
                if num_cards + cards_len > 4:
                    computer_challenges.append(c)
                    continue
            # Easy computer player challenges 1/5 of the time.
            if c.difficulty == diff.EASY:
                if randint(1, 5) == 1:
                    computer_challenges.append(c)
            elif c.difficulty == diff.MEDIUM:
                pass
            elif c.difficulty == diff.HARD:
                pass
        return computer_challenges

if __name__ == "__main__":
    game(computer('Bob', diff.EASY), computer('Bill', diff.EASY), computer('Tristan', diff.EASY))

sorry that its so long.. maybe I shouldn't havae posted it all..

Attachments
# "cheat.py" Module
# Created on 14-June-2009
# compiled with python 3.0.1 -> www.python.org
# edited with PyScripter -> http://pyscripter.googlepages.com
# by Mitchell Kember
# Copyright (c) 2009. All rights reserved.

"""
Created on 14-June-2009

This python script is Cheat version 2, the "Cheat" card game. It
is played using the command line. Future versions will use a GUI.

@author: Mitchell Kember
"""

from random import randint
from random import shuffle
from re import split

class player:
    """A player in the cheat game."""

    def __init__(self, name):
        """Creates a player.
        params: name - The name of the player."""
        self.name = name
        self.hand = []

    def __eq__(self, obj):
        """How to comare two player objects for equality."""
        if not isinstance(obj, player):
            return False
        return self.name == obj.name and self.hand == obj.hand

    def __ne__(self, obj):
        """How to comare two player objects for inequality."""
        return not self.__eq__(obj)

    def __str__(self):
        """The string representation of a player object."""
        return self.name

class human(player):
    """A human player in the cheat game."""

    def __init__(self, name):
        """Creates a human (human) player.
           params: name - See player class"""
        super(human, self).__init__(name)

    def __eq__(self, obj):
        """How to compare two human objects for equality."""
        return super(human, self).__eq__(obj)

    def __ne__(self, obj):
        """How to compare two human objects for inequality."""
        return not self.__eq__(obj)

class computer(player):
    """A computer player in the cheat game."""

    def __init__(self, name, difficulty=randint(1, 3)):
        """Creates a player and then initializes computer-specific fields.
        params: name: See player class.
        [difficulty = randint(1, 3)] - The difficulty of the computer player."""
        super(computer, self).__init__(name)
        self.difficulty = difficulty

    def __eq__(self, obj):
        """How to compare two computer objects for equality."""
        return super(computer, self).__eq__
        (obj) and self.difficulty == obj.difficulty

    def __ne__(self, obj):
        """How to compare two computer objects for inequality."""
        return not self.__eq__(obj)

class diff:
    """Enum class for a computer player's difficulty."""

    unknown = 0
    EASY = 1
    MEDIUM = 2
    HARD = 3

    def get_name(difficulty):
        """Returns the name of the difficulty level."""
        return ['easy', 'medium', 'hard'][difficulty]

class deck:
    """Deck of 52 cards for the cheat game."""

    def __init__(self, shuffled=True):
        """Creates a new deck of 52 regular playing cards.
        params: [shuffled = true] - Whether deck will be shuffled or not."""
        self.cards = []
        for s in range(1, 5):
            for v in range(1, 14):
                self.cards.append(card(v, s))
        if shuffled:
            self.shuffle_deck()
    def shuffle_deck(self):
        """Shuffles the deck."""
        shuffle(self.cards)

    def sort_hand(hand):
        """Sorts a player's hand of cards by value (ace through king).
        params: hand - The list of card objects to sort."""
        vals = []
        for c in hand:
            vals.append((c, c.val))
        vals.sort(key=lambda a:a[1])
        return [c[0] for c in vals]

class card:
    """A card in that makes up a deck."""

    def __init__(self, value, suit):
        """Creates a card.
        params: value - The card's value. (see the value class)
                suit - The card's suit. (see the suit class)"""
        self.val = value
        self.su = suit

    def __str__(self):
        """Returns the name of the card. (Format: \"The [value] of [suit]\")"""
        return "the " + value.get_name(self.val) + " of " + \
            suit.get_name(self.su)

    def __eq__(self, obj):
        """How to compare two card objects for equality."""
        if not isinstance(obj, card):
            return False
        return self.val == obj.val and self.su == obj.su

    def __ne__(self, obj):
        """How to compare two card objects for inequality."""
        return not self.__eq__(obj)

    def __gt__(self, obj):
        """How to compare two objects for the greatest."""
        if not isinstance(obj, card):
            raise TypeError
        return self.val > obj.val

    def __ge__(self, obj):
        """How to compare two objects for the greatest or equality."""
        if not isinstance(obj, card):
            raise TypeError
        return self > obj or self.val == obj.val

class value:
    """Enum class for a card's value."""

    unknown = 0
    ACE = 1
    TWO = 2
    THREE = 3
    FOUR = 4
    FIVE = 5
    SIX = 6
    SEVEN = 7
    EIGHT = 8
    NINE = 9
    TEN = 10
    JACK = 11
    QUEEN = 12
    KING = 13

    def get_name(value):
        """Returns the name of a value.
        params: value - The value to get the name of."""
        return ['unknown', 'ace', 'two', 'three', 'four', 'five', 'six',
            'seven', 'eight', 'nine', 'ten', 'jack', 'queen', 'king'][value]

class suit:
    """Enum class for a card's suit."""

    unknown = 0
    SPADES = 1
    CLUBS = 2
    HEARTS = 3
    DIAMONDS = 4

    def get_name(suit):
        """Returns the name of a suit.
        params: suit - The suit to get the name of."""
        return ['unknown', 'spades', 'clubs', 'hearts', 'diamonds'][suit]

    def get_colour(suit):
        """Returns the colour of a suit.
        params: suit - The suit to get the colour of."""
        if suit == 1 or suit == 2:
            return 'black'
        elif suit == 3 or suit == 4:
            return 'red'
        else:
            return 'unknown'

class game:
    """Cheat cardgame."""

    # game.gameplay: The rules of cheat and how it is played.
    gameplay = """                         Cheat gameplay:
\tThe object of the game is to get rid of all your cards. The winner is the first
person to get rid of all their cards.

\tTo start the game, the entire deck of 52 cards is dealt evenly to everyone. Now,
the player with the ace of spades will start. On this first turn, he or she is
allowed to put down all of his or her aces. He or she will take the cards, place
them face-down on the pile of cards and say the number of cards they are putting
down and that turn's card (ex \"2 twos\", \"1 jack\"). The next player will put
down twos, the next three, etc. until king, then it goes back to aces.

\tOn each player's turn, they will have to cheat if they do not have
any of the correct card. This means they will put down cards other than the correct
ones. You are NOT allowed to put down your cards and say that you are putting down
a different number of cards (ex say \"1 king\" but actually put down two). So
you can put down an ace and an eight when you are supposed to put down kings
and say \"2 kings\". You can also cheat even if you don't have to, if you are
supposed to put down fours and you have 2 fours, you could put the fours down
and also put a queen and say \"3 fours\", or even put down a jack and say
\"1 four\", and not even put down the fours that you have at all!

\tAfter any player's turn, any other player can challenge them by accusing
\"Cheat!\". If the player cheated, they must take up the entire pile. If the
player hasn't cheated, the challenger must add the entire pile to their hand.

\tThis will continue until a player wins, and then the other players may
keep playing to get a 2nd place winner, and 3rd place winner, etc."""
    # Ranks for the winners
    ranks = ['first', 'second', 'third', 'fourth', 'fifth', 'sixth',
        'seventh', 'eighth']

    def __init__(self, *players):
        """Creates a new game.
        params: *players - the players in this game."""
        self.players = list(players)  # (So that it is mutable)
        # Make sure there are no more than eight players:
        if len(self.players) > 8:
            print("There cannot be more than 8 players.")
            return
        # Make sure all players are instances of the class player
        for p in self.players:
            if not isinstance(p, player):
                print('Error: players are not player objects.')
                return
        # self.c_player: The player whose turn it is.
        # self.c_value: This turn's card value (ace through king).
        self.winners = []
        self.deck = deck()
        self.pile = []
        self.player_iter = iter(self.players)
        self.c_player = None
        self.value_iter = iter(range(1, 14))
        self.c_value = 0

        self.deal_cards()
        # Move the player with the ace of spades to the front of the list
        for ind, p in enumerate(self.players):
            for c in p.hand:
                if c == card(value.ACE, suit.SPADES):
                    self.players[0], self.players[ind] = \
                        self.players[ind], self.players[0]
        self.next_turn()

    def reset_player_iter(self):
        """Resets the player iterator by re-assigning it."""
        self.player_iter = iter(self.players)

    def reset_value_iter(self):
        """Resets the value iterator by re-assigning it."""
        self.value_iter = iter(range(1, 14))

    def deal_cards(self):
        """Deals the deck to each player."""
        i = 0
        for c in self.deck.cards:
            if i == len(self.players):
                i = 0
            self.players[i].hand.append(c)
            i += 1
        # Delete self.deck, and use self.pile instead so to not have to use
        # self.deck.cards.
        del self.deck

    def update_current(self):
        """Updates the current player and current value instance variables."""
        while True:
            # Go to the next player in the self.players list
            try:
                self.c_play

Aw hell nah. I'm not gonna debug that for you. But, is this supposed to be a game? Why isn't it interactive? If it is, how do I play it? I actually love this silly game.

Comments
quick reply

I didn't actually expect you to debug it :p It is interactive.. Look at the human_turn and human_challenge methods of the game class. To play it just save it as a .py, and in the if __name__ == "__main__":, just create an instance of game with 1 - 8 'player' object arguments (either 'human' or 'computer', not directly player). For example:

if __name__ == "__main__":
    game(human('scru'), computer('Mike', diff.EASY), computer('Jack', diff.HARD))

The code actually isn't completely done, I haven't done MEDIUM or HARD difficulties yet, only EASY. The whole module doesn't need to be debugged, only the 'game' class does (EDIT- which is only, uhh, 355 lines hehe). Like I said, it crashes when a player wins. I'll let you know of anything I figure out.

I didn't actually expect you to debug it :p It is interactive.. Look at the human_turn and human_challenge methods of the game class. To play it just save it as a .py, and in the if __name__ == "__main__":, just create an instance of game with 1 - 8 'player' object arguments (either 'human' or 'computer', not directly player). For example:

if __name__ == "__main__":
    game(human('scru'), computer('Mike', diff.EASY), computer('Jack', diff.HARD))

The code actually isn't completely done, I haven't done MEDIUM or HARD difficulties yet, only EASY. The whole module doesn't need to be debugged, only the 'game' class does (EDIT- which is only, uhh, 355 lines hehe). Like I said, it crashes when a player wins. I'll let you know of anything I figure out.

Oh wow this is a neat framework you have here.

Anyhow the gameplay may need some work. It's a little too verbose for my liking, and I have to press enter a LOT. I mean, I don't know how I would do this any better (blame it on my long day; too tired to think).

There are some nice subtleties in your UI though. For instance when printing my hand, the suite I'm being asked to put down is highlighted, if you may, so it catches my eye right away.

You have a bug. It's quite amusing actually.

Jack claims to have put down 1 three(s). Press enter for more info...
You have 1 three(s).
Enter Cheat, or press enter to pass...
Cheat! called by Jack. Press enter to continue...
Jack Cheated! Jack gets to pick up the pile! Press enter to continue...

Thanks. Yeah I can change the press enters later. The real goal of this is making a nice GUI with it so the command line won't matter much then. But I'm open to suggestions. Later I'll add something like this:

if __name__ == "__main__":
    humans = input("how many human players?")
    ..... -make sure its valid blah blah
    computers = input("how many computer players?")
    diffs = input("Enter the difficulty:")
    yesno = input("Would you like to see the rules?")
    . . . .  . . .  . .
    for someting in something:
        var = human('..')
        var = computer('...', vardifficulty)
   . . . 
     game(h1, h2, c1, c2)

Something like that, you get what I mean, just lets you get the game set up without editing the file.

I am pretty happy with how I've set up all the other classes like player, human, computer, card, deck, etc...

I've made some progress in the debugging! I fixed an infinite loop involving random numbers that have always already been chosen, and figured out the source of the only other problem that I think there is but yet to figure out how to fix it. I'll keep you posted :).

EDIT:

You have a bug. It's quite amusing actually.


Jack claims to have put down 1 three(s). Press enter for more info...
You have 1 three(s).
Enter Cheat, or press enter to pass...
Cheat! called by Jack. Press enter to continue...
Jack Cheated! Jack gets to pick up the pile! Press enter to continue...

Wow I thought that I made sure that couldn't happen... Ok thanks I'll check that out. Thanks.

WOOOT! The bugs have been exterminated! Not sure how I did it, I don't even think that I changed anything but anyway the first time ever that the whole game went through fine. I will polish it all up a bit (the comments, interface) then post the code. Then I will improve with people's suggestions, and then tackle the GUI. Thanks for the encouraging replies :).

I'll keep everyone up to date on the project.

EDIT: not relevant at all, but I've been wondering: When should i subclass object? Java does this for every class, but I've seen some python classes that subclass object. What is the purpose of it? Should my player, deck and card classes do it? Thanks.

Still not answered:

not relevant at all, but I've been wondering: When should i subclass object? Java does this for every class, but I've seen some python classes that subclass object. What is the purpose of it? Should my player, deck and card classes do it? Thanks

Ok, I've cleaned up the code, thinned out the press enter to continue's, debugged it, added code to let you set up the game, improved the interface.
Now My goals are:
- Improve the computer players. Make them more intelligent.
- Allow more players by allowing multiple decks of cards
- the GUI
Its official name for now is Pythonic CHEAT! (version 2.2). I'll attach the file with txt extension (note - why can't you attach .py files?). Save it, rename with .py extension. Try it out, check out the code, suggestions, comments, constructive criticism are welcome.

Attachments
#!/usr/bin/env python
# Module "cheat.py"
# Pythonic CHEAT! version 2.0
# Created on 14-June-2009
# compiled with python 3.0.1 -> www.python.org
# edited with PyScripter -> http://pyscripter.googlepages.com
# by Mitchell Kember
# Copyright (c) 2009. All rights reserved.

"""
Created on 14-June-2009

Pythonic CHEAT! version 2.2

    This is the card game, \"cheat!\", coded in python and uses
the command line for user interactions. I plan to make a
Graphical User Interface for the game, and to make computer
players more intelligent, such as realizing a player never cheats,
etc, and to allow multiple decks.

    Easy and Medium difficulties are almost finished, but
Hard difficulty cannot be used yet.

@author: Mitchell Kember
"""
# Imports
from random import randint
from random import shuffle
from re import split
from time import sleep

__version__ = 2.2

class player:
    """A player in the cheat game."""

    def __init__(self, name):
        """Creates a player with a name and a hand of cards (initially empty).
        params: name - The name of the player."""
        self.name = name
        self.hand = []

    def __str__(self):
        """The string representation of a player object, its name property."""
        return self.name

class human(player):
    """A human in the cheat game, derived from class player"""

    def __init__(self, name):
        """Creates a (human) player.
           params: name - See player class"""
        super(human, self).__init__(name)

class computer(player):
    """A computer player in the cheat game, derived from class player."""

    def __init__(self, name, difficulty=randint(1, 3)):
        """Creates a (computer) player and initializes its difficulty.
        params: name: See player class.
        [difficulty = randint(1, 3)] - The difficulty of the computer player."""
        super(computer, self).__init__(name)
        self.difficulty = difficulty

class diff:
    """Enum class for a computer player's difficulty."""

    unknown = 0
    EASY = 1
    MEDIUM = 2
    HARD = 3

    def get_name(difficulty):
        """Returns the name of the difficulty level."""
        return ['easy', 'medium', 'hard'][difficulty]

class deck:
    """Deck of 52 card objects for the cheat game."""

    def __init__(self, shuffled=True):
        """Creates a new deck of 52 regular playing cards.
        params: [shuffled = true] - Whether deck will be shuffled or not."""
        self.cards = []
        for s in range(1, 5):
            for v in range(1, 14):
                self.cards.append(card(v, s))
        if shuffled:
            self.shuffle_deck()
    def shuffle_deck(self):
        """Shuffles the deck."""
        shuffle(self.cards)

    def sort_hand(hand):
        """Sorts a player's hand of cards by value (ace through king).
        params: hand - The list of card objects to sort."""
        vals = []
        for c in hand:
            vals.append((c, c.val))
        vals.sort(key=lambda a:a[1])
        return [c[0] for c in vals]

class card:
    """A playing card that makes up a deck."""

    def __init__(self, value, suit):
        """Creates a card with a value (ace through king) and
        a suit (spades, clubs, hearts or diamonds).
        params: value - The card's value. (see the value class)
                suit - The card's suit. (see the suit class)"""
        self.val = value
        self.su = suit

    def __str__(self):
        """Returns the name of the card. (Format: \"The [value] of [suit]\")"""
        return "the " + value.get_name(self.val) + " of " + \
            suit.get_name(self.su)

    def __eq__(self, obj):
        """How to compare two card objects for equality."""
        if not isinstance(obj, card):
            return False
        return self.val == obj.val and self.su == obj.su

    def __ne__(self, obj):
        """How to compare two card objects for inequality."""
        return not self.__eq__(obj)

    def __gt__(self, obj):
        """How to compare two card objects for greatness."""
        if not isinstance(obj, card):
            raise TypeError
        return self.val > obj.val

    def __ge__(self, obj):
        """How to compare two card objects for greatness or equality."""
        if not isinstance(obj, card):
            raise TypeError
        return self > obj or self.val == obj.val

class value:
    """Enum class for a card's value."""

    unknown = 0
    ACE = 1
    TWO = 2
    THREE = 3
    FOUR = 4
    FIVE = 5
    SIX = 6
    SEVEN = 7
    EIGHT = 8
    NINE = 9
    TEN = 10
    JACK = 11
    QUEEN = 12
    KING = 13

    def get_name(value):
        """Returns the name of a value.
        params: value - The value to get the name of."""
        return ['unknown', 'ace', 'two', 'three', 'four', 'five', 'six',
            'seven', 'eight', 'nine', 'ten', 'jack', 'queen', 'king'][value]

class suit:
    """Enum class for a card's suit."""

    unknown = 0
    SPADES = 1
    CLUBS = 2
    HEARTS = 3
    DIAMONDS = 4

    def get_name(suit):
        """Returns the name of a suit.
        params: suit - The suit to get the name of."""
        return ['unknown', 'spades', 'clubs', 'hearts', 'diamonds'][suit]

    def get_colour(suit):
        """Returns the colour of a suit.
        params: suit - The suit to get the colour of."""
        if suit == 1 or suit == 2:
            return 'black'
        elif suit == 3 or suit == 4:
            return 'red'
        else:
            return 'unknown'

class game:
    """Cheat cardgame. Instantiating game will start the game."""

    # gameplay: The rules of cheat and how it is played.
    gameplay = """                         Cheat gameplay:
\tThe object of the game is to get rid of all your cards. The winner is the first
person to get rid of all their cards.

\tTo start the game, the entire deck of 52 cards is dealt evenly to everyone. Now,
the player with the ace of spades will start. On this first turn, he or she is
allowed to put down all of his or her aces. He or she will take the cards, place
them face-down on the pile of cards and say the number of cards they are putting
down and that turn's card (ex \"2 twos\", \"1 jack\"). The next player will put
down twos, the next three, etc. until king, then it goes back to aces.

\tOn each player's turn, they will have to cheat if they do not have
any of the correct card. This means they will put down cards other than the correct
ones. You are NOT allowed to put down your cards and say that you are putting down
a different number of cards (ex say \"1 king\" but actually put down two). So
you can put down an ace and an eight when you are supposed to put down kings
and say \"2 kings\". You can also cheat even if you don't have to, if you are
supposed to put down fours and you have 2 fours, you could put the fours down
and also put a queen and say \"3 fours\", or even put down a jack and say
\"1 four\", and not even put down the fours that you have at all!

\tAfter any player's turn, any other player can challenge them by accusing
\"Cheat!\". If the player cheated, they must take up the entire pile. If the
player hasn't cheated, the challenger must add the entire pile to their hand.

\tThis will continue until a player wins, and then the other players may
keep playing to get a 2nd place winner, and 3rd place winner, etc."""
    # Ranks for the winners
    ranks = ['first', 'second', 'third', 'fourth', 'fifth', 'sixth',
        'seventh', 'eighth']

    def __init__(self, *players):
        """Creates a new game and begins it immediately.
        params: *players - the players in this game."""
        self.players = list(players)  # (So that it is mutable)
        # Make sure there are no more than eight players:
        if len(self.players) > 8:
            print("There cannot be more than 8 players.")
            return
        # Make sure all players are instances of the class player
        for p in self.players:
            if not isinstance(p, player):
                print('Error: players are not player objects.')
                raise TypeError
                return
        # self.c_player: The player whose turn it is.
        # self.c_value: This turn's card value (ace through king).
        self.winners = []
        self.deck = deck()
        self.pile = []
        self.player_iter = iter(self.players)
        self.c_player = None
        self.value_iter = iter(range(1, 14))
        self.c_value = 0

        self.deal_cards()
        # Move the player with the ace of spades to the front of self.players.
        for ind, p in enumerate(self.players):
            for c in p.hand:
                if c == card(value.ACE, suit.SPADES):
                    self.players[0], self.players[ind] = \
                        self.players[ind], self.players[0]
        # Begin the chain of turns.
        self.next_turn()

    def reset_player_iter(self):
        """Resets the player iterator by re-assigning it."""
        self.player_iter = iter(self.players)

    def reset_value_iter(self):
        """Resets the value iterator by re-assigning it."""
        self.value_iter = iter(range(1, 14))

    def deal_cards(self):
        """Deals the deck to each player."""
        i = 0
        for c in self.deck.cards:
            if i == len(self.players):
                i = 0
            self.players[i].hand.append(c)
            i += 1
        # Delete self.deck, and use self.pile instead so to not have to use
        # self.deck.cards.
        del self.deck

    def update_current(self):
        """Updates the current player and current value properties."""
        while True:
            # Go to the next player in the self.players list
            try:
                self.c_player = next(self.player_iter)
            # If at the end of the list, go back to the start.
            except StopIteration:
                self.reset_player_iter()
                self.c_player = next(self.player_iter

You should always subclass object, except when subclassing another class that subclasses object (which technically, is still subclassing object). Why? Because the difference between declaring an old style class and a new style class is that new style classes subclass object.

We would prefer if you use new-style classes in all your OO code.

I'm going through all the code, changing the names after reading PEP 8, and making sure they are all new style (subclass object, unless already subclassing another class that I made which subclasses object). I read somewhere that even though it does nothing its important to do for example in class Player(object): in the __init__ method, to do super(Player, self).__init__() . I already did that with my human and computer classes, ex super(Human, self).__init__(name) . Is that right? I'm also using an Enumeration class to do the value, suit and difficulty enums. Do you have to have cls (or any name) as first argument in a class method? Because I've been not doing that and it works fine, but I read your suposed to (in PEP 8). Thanks.

Your first argument for a classmethod must represent the class (as the first of a normal method represents an instance). It doesn't literally have to be named "cls", but it must represent the class.

Note that class methods are not the same as static methods, which have no other arguments than the ones you specify.

I have no idea about the super() function as I never use it, but there are certain python features that are accessible from new style classes and not old styles ones.

Further info on new style classes:
http://www.python.org/doc/newstyle/

Note: old style classes have been removed from python 3.0

scru, I'm pretty sure that now in python 3.0.1 all classes are new-style and you don't need to subclass object... Am I right?

EDIT: just saw your post. So now that classic style classes are removed, I don't need to bother subclassing object, right?

EDIT: I must be right because I tested class c: pass and then issubclass(c, object) and it returned True, so its already subclassing object without writing class c[B](object)[/B]: pass I'm still working on this and I'll post a new update of it soon. I'm almost done what could will be called version 2.3. Right now I'm going over all the docstrings.
This project has come a long way, and I've learnt a lot :). I think I won't start the GUI (which will be Tkinter unless wxpython releases a version for python 3.0.1 by then) until after I've perfected the command line version.

You can try pyQT which has a python 3 version, if tkinter's archaic UI doesn't suit you. Only, you'd have to gpl your code. If you don't mind that, then I suppose you wouldn't mind my offer of assistance? I've grown really fond of this program.

Thanks very much, I'll check PyQT, and accept any help. What is "gpl"-ing your code? How do I do it?
EDIT: Ohh is it like make it an open source project? (G eneral P ublic L icense right?)

Yeah, it is. The GPL is a strong copyleft: if you use code that is gpl, your code must be gpl too (pyQT is GPL). Two implications of using your pyQT:

1. You have to open source your code under gpl
2. People using your code have to gpl theirs as well.

Ok Pythonic CHEAT! version 2.5 is finished! Pay particular close attention to the docstrings, I worked all day on the documentation, frequently checking PEP 257. I also made the computers a bit smarter.

The code is *much* nicer now. Okay, so where can I download this PyQt, and how do I gpl my code?

You can try pyQT which has a python 3 version, if tkinter's archaic UI doesn't suit you. Only, you'd have to gpl your code. If you don't mind that, then I suppose you wouldn't mind my offer of assistance? I've grown really fond of this program.

Thanks, what do you mean by assistance, like what would you do? Of course I'll accept any help :p.

Yeah, it is. The GPL is a strong copyleft: if you use code that is gpl, your code must be gpl too (pyQT is GPL). Two implications of using your pyQT:

1. You have to open source your code under gpl
2. People using your code have to gpl theirs as well.

the GPL is strong copyleft: What does copyleft mean? Nevermind, http://en.wikipedia.org/wiki/Copyleft told me.
I'll still get credit if anyone decides to distribute it since I made it, even if they change it a bit right?

And will the user have to install some PyQt thing along with Python to run the program?

Thanks for keeping up with me in this project :).

And here is Pythonic CHEAT! version 2.5! (If you can come up with a more original name let me now :o)

Attachments
#!/usr/bin/env python
# Python Module "cheat.py"
# Pythonic CHEAT! version 2.5
# Created on 14-June-2009
# compiled with python 3.0.1 -> www.python.org
# edited with PyScripter -> http://pyscripter.googlepages.com
# by Mitchell Kember
# Copyright (c) 2009. All rights reserved.

"""
Created on 14-June-2009

Pythonic CHEAT! version 2.5

  This is the card game, \"Cheat!\", coded in python 3.0.1 and uses
the command line for user interaction. I plan to make a
Graphical User Interface for the game, and to make computer
players more intelligent, such as realizing a player never cheats,
etc, and to allow multiple decks. Easy and medium difficulties are almost
finished, but hard difficulty cannot be used yet.

Classes:
Enumeration
Player
Human(Player)
Computer(Player)
Deck
Card
Game

Enumeration Globals:
Difficulty
Value
Suit

@author: Mitchell Kember

"""

# Imports
from random import randint
from random import shuffle
from re import split
from time import sleep

# Enum-creating class
class Enumeration:

    """A simple Enumeration.

    Example:
    >>> Seasons = Enumeration("Spring", "Summer", "Autumn", "Winter")
    >>> print(Season.Spring > Season.Winter)
    False
    >>> print(Season.Autumn)
    3

    Instantiating Enumeration assigns a numerical value
    starting at one and incrementing by one to each
    string argument.

    Keyword Arguments:
    *names -- a variable amount of str objects, which are the
            reference names of the instance attributes created

    """

    def __init__(self, *names):
        super().__init__()
        for number, name in enumerate(list(names)):
            setattr(self, name, number + 1)

# Globals
version = 2.5
Difficulty = Enumeration('Easy', 'Medium', 'Hard')
Value = Enumeration('Ace', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven',
                    'Eight', 'Nine', 'Ten', 'Jack', 'Queen', 'King')
Suit = Enumeration('Spades', 'Clubs', 'Hearts', 'Diamonds')

# Functions
def difficulty_name(difficulty):
    """Returns the name of the difficulty level."""
    return ['unknown', 'easy', 'medium', 'hard'][difficulty]

def value_name(value):
    """Returns the name of the value."""
    return ['unknown', 'ace', 'two', 'three', 'four', 'five', 'six',
        'seven', 'eight', 'nine', 'ten', 'jack', 'queen', 'king'][value]

def suit_name(suit):
    """Returns the name of the suit."""
    return ['unknown', 'spades', 'clubs', 'hearts', 'diamonds'][suit]

def suit_colour(suit):
    """Returns the colour of the suit."""
    return ['unknown', 'black', 'black', 'red', 'red'][suit]

# Classes
class Player:

    """A player in the cheat game.

    A player in the cheat game with a name and
    a hand of cards. A player can be converted to
    a string with str(instance_of_player).

    Attributes:
        self.name -- str
        self.hand -- list

    Keyword Arguments:
    name -- the player's name

    """

    def __init__(self, name):
        super().__init__()
        self.name = name
        self.hand = []

    def __str__(self):
        """The string representation of a Player object, the name attribute."""
        return self.name

class Human(Player):

    """A human player, derived from Player.

    A human player for the cheat game, derived from the
    class PLayer. Currently, it is exactly the same as
    its superclass, and is used only for the name Human.
    The human player has a name and a hand of cards.

    Inherited Attributes:
    self.name -- str
    self.hand -- list

    Keyword Arguments:
    name -- the human player's name

    """

    def __init__(self, name):
        super().__init__(name)

class Computer(Player):

    """A computer player, derived from Player.

    A computer player for the cheat game, derived from the
    class PLayer. It is a Player (which has a name and a hand
    of cards) with a level of difficulty.

    Inherited Attributes:
    self.name -- str
    self.hand -- list

    New Attributes:
    self.difficulty - Enumeration value (1, 2 or 3)

    Keyword Arguments:
    name -- the computer player's name
    [difficulty] -- the computer player's level of difficulty,
        Difficulty.Easy, Difficulty.Medium or Difficulty.Hard
        Default: a random choice out of the three

    """

    def __init__(self, name, difficulty=randint(1, 3)):
        super().__init__(name)
        self.difficulty = difficulty

class Deck:

    """Deck of 52 cards.

    A deck of 52 regular playing cards for the cheat
    game, each card being an instance of the Card class
    (see the Card class documentation for more details).

    Attributes:
    self.cards -- list

    Methods:
    self.shuffle_deck -- returns None
    cls.sort_hand -- returns list

    Keyword Arguments:
    [shuffled] -- shuffles the deck after creating if True
        default: True

    """

    def __init__(self, shuffled=True):
        super().__init__()
        self.cards = []
        # Loop through the four suits and the 13 values
        # And create each card (52)
        for s in range(1, 5):
            for v in range(1, 14):
                self.cards.append(Card(v, s))
        if shuffled:
            self.shuffle_deck()
    def shuffle_deck(self):
        """Shuffles the deck."""
        shuffle(self.cards)

    def sort_hand(cls, hand):
        """Sorts a Player's hand of cards by value (ace through king)."""
        vals = []
        for c in hand:
            vals.append((c, c.val))
        # Sort by the second element in each tuple (the value)
        vals.sort(key=lambda a:a[1])
        return [c[0] for c in vals]

class Card:
    """A regular playing card.

    A regular playing Card, each with a value
    (ace through king) and a suit (spades, clubs,
    hearts or diamonds). 52 Card objects make up
    a Deck object. Card objects can be compared
    with ==, !=, <=, >=, > or < and can be converted
    to a string with str(instance_of_Card).

    Attributes:
    value -- Enumeration value (numbers 1 - 13)
    suit -- Enumeration value (numbers 1 - 4)

    Keyword Arguments:
    value -- the card's value, a number from 1 to 13 (inclusive)
        use Value.Ace, Value.Two, etc. where Value is an
        instance of Enumeration with the 13 values as values.
    suit -- the card's suit, a number from 1 to 4 (inclusive)
        use Suit.Spades, Suit.Hearts, etc. where Suit is an
        instance of Enumeration with the four suits as values

    """

    def __init__(self, value, suit):
        super().__init__()
        self.value = value
        self.suit = suit

    def __str__(self):
        """Returns the name of the Card. (Format: \"The [value] of [suit]\")"""
        return "the " + value_name(self.value) + " of " + \
            suit_name(self.suit)

    def __eq__(self, obj):
        """How to compare two Card objects for equality."""
        if not isinstance(obj, Card):
            return False
        return self.value == obj.value and self.suit == obj.suit

    def __ne__(self, obj):
        """How to compare two Card objects for inequality."""
        return not self.__eq__(obj)

    def __gt__(self, obj):
        """How to compare two Card objects for greatness."""
        if not isinstance(obj, Card):
            raise TypeError
        return self.value > obj.value

    def __ge__(self, obj):
        """How to compare two Card objects for greatness or equality."""
        if not isinstance(obj, Card):
            raise TypeError
        return self > obj or self.value == obj.value

class Game:
    """Cheat card game.

    A Pythonic CHEAT! game. Instatiating Game will start
    the game immediately. There can be a maximum of 8 players
    in the game. The Game class controls the user interface.

    Attributes:
    cls.gameplay -- str
    self.players -- list
    self.winners -- list
    self.deck -- Deck
    self.pile -- list
    self.player_iter -- iter()
    self.c_player -- Player
    self.value_iter -- iter()
    self.c_value -- Enumeration value (numbers 1 through 13)

    Methods:
    self.reset_player_iter -- returns None
    self.reset_value_iter -- returns None
    self.deal_cards -- returns None
    self.update_current -- returns None
    self.next_turn -- returns None
    self.human_turn -- returns None
    self.computer_turn -- returns None
    self.finish_turn -- returns None
    self.get_challenges -- returns list
    self.human_challenge -- returns list
    self.computer_challenge -- returns list

    Keyword Arguments:
    *players -- a variable amount of Player objects (max: 8)

    """

    # gameplay: The rules of cheat and how it is played.
    gameplay = """                         Cheat gameplay:
\tThe object of the game is to get rid of all your cards. The winner is the
first person to get rid of all their cards.

\tTo start the game, the entire deck of 52 cards is dealt evenly to everyone.
Now, the Player with the ace of spades will start. On this first turn, he or
she is allowed to put down all of his or her aces. He or she will take the
cards, place them face-down on the pile of cards and say the number of cards
they are putting down and that turn's card (ex \"2 twos\", \"1 jack\"). The
next Player will put down twos, the next three, etc. until king, then it goes
back to aces.

\tOn each Player's turn, they will have to cheat if they do not have
any of the correct card. This means they will put down cards other than the
correct ones. You are NOT allowed to put down your cards and say that you are
putting down a Difficultyerent number of cards (ex say \"1 king\" but actually
put down two). So you can put down an ace and an eight when you are supposed to
put down kings and say \"2 kings\". You can also cheat even if you don't have
to, if you are supposed to put down fours and you have 2 fours, you could put
the fours down and also put a queen and say \"3 fours\", or even put down a jack
and say \"1 four\", and not even put do

Sorry 2.5 still had some bugs in it because I changed the capitalization of some names and stuff, heres 2.6:

Attachments
#!/usr/bin/env python
# Python Module "cheat.py"
# Pythonic CHEAT! version 2.6
# Created on 14-June-2009
# compiled with python 3.0.1 -> www.python.org
# edited with PyScripter -> http://pyscripter.googlepages.com
# by Mitchell Kember
# Copyright (c) 2009. All rights reserved.

"""
Created on 14-June-2009

Pythonic CHEAT! version 2.6

  This is the card game, \"Cheat!\", coded in python 3.0.1 and uses
the command line for user interaction. I plan to make a
Graphical User Interface for the game, and to make computer
players more intelligent, such as realizing a player never cheats,
etc, and to allow multiple decks. Easy and medium difficulties are almost
finished, but hard difficulty cannot be used yet.

Classes:
Enumeration
Player
Human(Player)
Computer(Player)
Deck
Card
Game

Enumeration Globals:
Difficulty
Value
Suit

@author: Mitchell Kember

"""

# Imports
from random import randint
from random import shuffle
from re import split
from time import sleep

# Enum-creating class
class Enumeration:

    """A simple Enumeration.

    Example:
    >>> Seasons = Enumeration("Spring", "Summer", "Autumn", "Winter")
    >>> print(Season.Spring > Season.Winter)
    False
    >>> print(Season.Autumn)
    3

    Instantiating Enumeration assigns a numerical value
    starting at one and incrementing by one to each
    string argument.

    Keyword Arguments:
    *names -- a variable amount of str objects, which are the
            reference names of the instance attributes created

    """

    def __init__(self, *names):
        super().__init__()
        for number, name in enumerate(list(names)):
            setattr(self, name, number + 1)

# Globals
version = 2.6
Difficulty = Enumeration('Easy', 'Medium', 'Hard')
Value = Enumeration('Ace', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven',
                    'Eight', 'Nine', 'Ten', 'Jack', 'Queen', 'King')
Suit = Enumeration('Spades', 'Clubs', 'Hearts', 'Diamonds')

# Functions
def difficulty_name(difficulty):
    """Returns the name of the difficulty level."""
    return ['unknown', 'easy', 'medium', 'hard'][difficulty]

def value_name(value):
    """Returns the name of the value."""
    return ['unknown', 'ace', 'two', 'three', 'four', 'five', 'six',
        'seven', 'eight', 'nine', 'ten', 'jack', 'queen', 'king'][value]

def suit_name(suit):
    """Returns the name of the suit."""
    return ['unknown', 'spades', 'clubs', 'hearts', 'diamonds'][suit]

def suit_colour(suit):
    """Returns the colour of the suit."""
    return ['unknown', 'black', 'black', 'red', 'red'][suit]

# Classes
class Player:

    """A player in the cheat game.

    A player in the cheat game with a name and
    a hand of cards. A player can be converted to
    a string with str(instance_of_player).

    Attributes:
        self.name -- str
        self.hand -- list

    Keyword Arguments:
    name -- the player's name

    """

    def __init__(self, name):
        super().__init__()
        self.name = name
        self.hand = []

    def __str__(self):
        """The string representation of a Player object, the name attribute."""
        return self.name

class Human(Player):

    """A human player, derived from Player.

    A human player for the cheat game, derived from the
    class PLayer. Currently, it is exactly the same as
    its superclass, and is used only for the name Human.
    The human player has a name and a hand of cards.

    Inherited Attributes:
    self.name -- str
    self.hand -- list

    Keyword Arguments:
    name -- the human player's name

    """

    def __init__(self, name):
        super().__init__(name)

class Computer(Player):

    """A computer player, derived from Player.

    A computer player for the cheat game, derived from the
    class PLayer. It is a Player (which has a name and a hand
    of cards) with a level of difficulty.

    Inherited Attributes:
    self.name -- str
    self.hand -- list

    New Attributes:
    self.difficulty - Enumeration value (1, 2 or 3)

    Keyword Arguments:
    name -- the computer player's name
    [difficulty] -- the computer player's level of difficulty,
        Difficulty.Easy, Difficulty.Medium or Difficulty.Hard
        Default: a random choice out of the three

    """

    def __init__(self, name, difficulty=randint(1, 3)):
        super().__init__(name)
        self.difficulty = difficulty

class Deck:

    """Deck of 52 cards.

    A deck of 52 regular playing cards for the cheat
    game, each card being an instance of the Card class
    (see the Card class documentation for more details).

    Attributes:
    self.cards -- list

    Methods:
    self.shuffle_deck -- returns None
    cls.sort_hand -- returns list

    Keyword Arguments:
    [shuffled] -- shuffles the deck after creating if True
        default: True

    """

    def __init__(self, shuffled=True):
        super().__init__()
        self.cards = []
        # Loop through the four suits and the 13 values
        # And create each card (52)
        for s in range(1, 5):
            for v in range(1, 14):
                self.cards.append(Card(v, s))
        if shuffled:
            self.shuffle_deck()
    def shuffle_deck(self):
        """Shuffles the deck."""
        shuffle(self.cards)

    def sort_hand(cls, hand):
        """Sorts a Player's hand of cards by value (ace through king)."""
        vals = []
        for c in hand:
            vals.append((c, c.value))
        # Sort by the second element in each tuple (the value)
        vals.sort(key=lambda a:a[1])
        return [c[0] for c in vals]
    sort_hand = classmethod(sort_hand)

class Card:
    """A regular playing card.

    A regular playing Card, each with a value
    (ace through king) and a suit (spades, clubs,
    hearts or diamonds). 52 Card objects make up
    a Deck object. Card objects can be compared
    with ==, !=, <=, >=, > or < and can be converted
    to a string with str(instance_of_Card).

    Attributes:
    value -- Enumeration value (numbers 1 - 13)
    suit -- Enumeration value (numbers 1 - 4)

    Keyword Arguments:
    value -- the card's value, a number from 1 to 13 (inclusive)
        use Value.Ace, Value.Two, etc. where Value is an
        instance of Enumeration with the 13 values as values.
    suit -- the card's suit, a number from 1 to 4 (inclusive)
        use Suit.Spades, Suit.Hearts, etc. where Suit is an
        instance of Enumeration with the four suits as values

    """

    def __init__(self, value, suit):
        super().__init__()
        self.value = value
        self.suit = suit

    def __str__(self):
        """Returns the name of the Card. (Format: \"The [value] of [suit]\")"""
        return "the " + value_name(self.value) + " of " + \
            suit_name(self.suit)

    def __eq__(self, obj):
        """How to compare two Card objects for equality."""
        if not isinstance(obj, Card):
            return False
        return self.value == obj.value and self.suit == obj.suit

    def __ne__(self, obj):
        """How to compare two Card objects for inequality."""
        return not self.__eq__(obj)

    def __gt__(self, obj):
        """How to compare two Card objects for greatness."""
        if not isinstance(obj, Card):
            raise TypeError
        return self.value > obj.value

    def __ge__(self, obj):
        """How to compare two Card objects for greatness or equality."""
        if not isinstance(obj, Card):
            raise TypeError
        return self > obj or self.value == obj.value

class Game:
    """Cheat card game.

    A Pythonic CHEAT! game. Instatiating Game will start
    the game immediately. There can be a maximum of 8 players
    in the game. The Game class controls the user interface.

    Attributes:
    cls.gameplay -- str
    self.players -- list
    self.winners -- list
    self.deck -- Deck
    self.pile -- list
    self.player_iter -- iter()
    self.c_player -- Player
    self.value_iter -- iter()
    self.c_value -- Enumeration value (numbers 1 through 13)

    Methods:
    self.reset_player_iter -- returns None
    self.reset_value_iter -- returns None
    self.deal_cards -- returns None
    self.update_current -- returns None
    self.next_turn -- returns None
    self.human_turn -- returns None
    self.computer_turn -- returns None
    self.finish_turn -- returns None
    self.get_challenges -- returns list
    self.human_challenge -- returns list
    self.computer_challenge -- returns list

    Keyword Arguments:
    *players -- a variable amount of Player objects (max: 8)

    """

    # gameplay: The rules of cheat and how it is played.
    gameplay = """                         Cheat gameplay:
\tThe object of the game is to get rid of all your cards. The winner is the
first person to get rid of all their cards.

\tTo start the game, the entire deck of 52 cards is dealt evenly to everyone.
Now, the Player with the ace of spades will start. On this first turn, he or
she is allowed to put down all of his or her aces. He or she will take the
cards, place them face-down on the pile of cards and say the number of cards
they are putting down and that turn's card (ex \"2 twos\", \"1 jack\"). The
next Player will put down twos, the next three, etc. until king, then it goes
back to aces.

\tOn each Player's turn, they will have to cheat if they do not have
any of the correct card. This means they will put down cards other than the
correct ones. You are NOT allowed to put down your cards and say that you are
putting down a Difficultyerent number of cards (ex say \"1 king\" but actually
put down two). So you can put down an ace and an eight when you are supposed to
put down kings and say \"2 kings\". You can also cheat even if you don't have
to, if you are supposed to put down fours and you have 2 fours, you could put
the fours down and also put a queen and say \"3 fours\", or even put down a jac

Only, you'd have to gpl your code

This is not true. You can GPL your code if you want but it is not a requirement. Your code automatically comes under the GPL only when it modifies a program that is already GPL'd. You would have to provide a link to the GPL part of the code, i.e. the QT website (generally in the comments at the beginning of the program), but your code is your code. One of the mobile phone manufactures, I think it is Nokia but I'm not sure, uses QT but has had a closed operating system for years. If it is Nokia, they have recently open-sourced, but it was closed for years even though the QT tool kit was used.

This is not true. You can GPL your code if you want but it is not a requirement. Your code automatically comes under the GPL only when it modifies a program that is already GPL'd. You would have to provide a link to the GPL part of the code, i.e. the QT website (generally in the comments at the beginning of the program), but your code is your code. One of the mobile phone manufactures, I think it is Nokia but I'm not sure, uses QT but has had a closed operating system for years. If it is Nokia, they have recently open-sourced, but it was closed for years even though the QT tool kit was used.

I don't think you understand the GPL very well. Yes, he does have to GPL his code, just for using code that is gpled (whether he modifies it or not). That's why it's called a copyleft. Also note that pyQT's license is not the same as QT's, and the LGPL license for QT does not apply.

Ok Pythonic CHEAT! version 2.5 is finished! Pay particular close attention to the docstrings, I worked all day on the documentation, frequently checking PEP 257. I also made the computers a bit smarter.

The code is *much* nicer now. Okay, so where can I download this PyQt, and how do I gpl my code?

Thanks, what do you mean by assistance, like what would you do? Of course I'll accept any help :p.

the GPL is strong copyleft: What does copyleft mean? Nevermind, http://en.wikipedia.org/wiki/Copyleft told me.
I'll still get credit if anyone decides to distribute it since I made it, even if they change it a bit right?

And will the user have to install some PyQt thing along with Python to run the program?

Thanks for keeping up with me in this project :).

And here is Pythonic CHEAT! version 2.5! (If you can come up with a more original name let me now :o)

I'm not sure about how the credit works.

Yes the user would have to install pyQT.

I would help you with the GUI part of your program. If you decide to use pyQT, then I'll help you with that. If you decide not to, you still have options that I can also help with (I can go straight Win32 for you with ctypes), but my code would be BSD in such a case (allows you to license your code however you like, but my code must remain BSD).

Take a look at the GPL FAQ, especially the sections on modifing existing GPL programs vs. linking to GPL binaries. We are talking about an aggregate, specifically different programs with different licenses in the same package.

An “aggregate” consists of a number of separate programs, distributed together on the same CD-ROM or other media. The GPL permits you to create and distribute an aggregate, even when the licenses of the other software are non-free or GPL-incompatible. The only condition is that you cannot release the aggregate under a license that prohibits users from exercising rights that each program's individual license would grant them.

So they have certain rights for the GPL portion, which you can not prohit them from using, and different rights for any non-GPL code that you also can not prohibit in any way. The bottom line is that you own any program that you create (without copying from others). In most countries today, no one can force you to do anything do don't want to do with it

I think you may have mis-interpreted what is meant by "aggregate." Notice that quote mentions nothing about whether any software in the "aggregate" directly uses to a GPLed program (I'm telling you, you have to GPL in this case).

I went to the pyQT website in their licensing section. While woooee is wrong that GPLed code does not infect other software for simply using it, PyQT seems to allow software using it to be licensed under a restricted set of licenses as a special exception:

If you use the GPL version of PyQt then any code you develop with it must be distributed under a compatable license. Like the GPL version of Qt, PyQt grants an exception to the GPL that specifies the list of licenses that you may use. The full text of the exception can be found ... and a copy is included in the PyQt source package.

Here is the page the above quote refers to:
http://doc.trolltech.com/4.4/license-gpl-exceptions.html

Can you give me a link to doiwnload PyQt? And how do I gpl my code?

Also, what do you think is better: Global variables and global functions or class attributes and static methods of the class they are mostly associated with?

http://www.riverbankcomputing.co.uk/software/pyqt/download

http://www.gnu.org/licenses/gpl-howto.html

Make sure you read and understand the GPL (daunting, I know; I'm not really a big GPL fan myself).


EDIT:

I almost never use global names (I hate the term variable for Python) unless they are constants. I prefer that my functions never modify any names not belonging to its local namespace (I instead prefer to pass any "global" names to and from my functions). Global functions are okay, and I only ever use static methods when the routine directly relates to the class.

I have a few more questions: When you make a class attribute, is it like this:

Class A:     # Python 3.0.1 so it auto-subclasses object
    the_class_attribute = 23

or like this:

Class A:
    A.the_class_attribute = 23

Because
I've been using the first and it works fine but I think I've seen the second. Also in python 3, do you still need to use

class A:
    def staticdef():
        print("I'm static")
    staticdef = staticmethod(staticdef)

Because When I omit the staticdef = staticmethod(staticdef) , it still works exactly the same (but not if I try to do a class method that way).
And about the global name vs class attribute, I'm wondering if I should make it a global name again, not class attribute because its an instance of my Enumeration class, and it used to be a class by itself, without using Enumeration, and it looks just like a class when you're using it, so I don't think it should be in another class, but that the class should just use it, like I has before... I'm actually thinking I should get rid of the Enumeration class and just make them like I had before, that way the functions can go in there too.. I'll post the next version of this soon.

Comments
+1 for having an appetite for learning

Okay, one at a time.


The first method of defining a class attribute is the correct one (nobody reply to this to bitch about "oh, there is no correct way to program" or I'll steal all your peanut butter). The second method only works because of Python's late binding; it's not the syntax.

When you omit staticdef = staticmethod(staticdef), it may still work, but you don't necessarily have a static method; I can for instance still call the method from an instance (and not the class). In this case it will raise an error because of your lack of arguments, but what about static methods which will have arguments? Use the correct syntax!

A shorthand way with decorators:

@staticmethod
def staticdef():
    ....

Try not to make assumptions about Python 3 and what it supports, what omissions it allows etc. beyond what is outlined in the documentation. In fact, one of the core principles of Python grammar is explicit is better than implicit. Always mark methods you intend to be used statically as static methods.

Finally, this is a judgment call. If doing so will result in code than is easier to read and maintain, it won't hurt*.

*I accept no liability for any unforeseen damages, physical or otherwise.

P.S. Python decorators are fun. Not only to use, but to create. If you want to have some good fun with Python, I suggest you take a look at descriptors and decorators (together), generators, list comprehensions, and the data model (customizing your types with __specialfunc__).

Here's version 2.8! I really improved a lot of the code, but I'm still isn't ready to make the gui yet. I almost have 1k lines of code now. More then half is documentation I'd say, I spent a lot of time on that. I'd appreciate if you took a look at it.

Oh, and I only need to gpl my code when I use PyQt if I distribute it, right? Like if it never leaves my computer, it won't matter anyway, right?

Attachments
#!/usr/bin/env python
# Python Module "cheat.py"
# Pythonic CHEAT! version 2.8
# Created on 14-June-2009
# compiled with python 3.0.1 -> www.python.org
# edited with PyScripter 1.9.9.7 -> http://pyscripter.googlepages.com
# by Mitchell Kember
# Copyright (c) 2009. All rights reserved.

"""
Created on 14-June-2009

Pythonic CHEAT! version 2.8

This module is a console based card game, called \"Pythonic CHEAT!\",
based on the real card game, \"Cheat!\". It is a sole executable script,
written in python 3.0.1.

State:
- all difficulties except hard can be used
- most things are finished except for
  the computer players

Future Improvements:
- GUI
- more intelligent computer players

Classes:
Player
Human(Player)
Computer(Player)
Difficulty
Deck
Card
Value
Suit
Game

Functions:
game_setup

-- Documentation Help --

Words in the brackets are not part of documentation,
but are explaining it to you. \"classname\" is substitued
by the classes name, as are method_name and attribute_name.
\"return_type\" is substituted by the type returned, but
\"returns\", \"self\" and \"cls\" are literally that. Square
brackets indicate that the words inside are only present
if the class has a superclass which has methods/attributes
that the subclass inherits.

Class Docstring:
===============

Summary of documentation.

More elaborate documentation, spanning
several lines. .....................
...................................
.............................

[Inherited Attributes]:                 (only if the class subclasses another)
classname.attribute_name -- type        (inherited class attribute)
self.attribute_name -- type             (inherited instance attribute)

[New] Attributes:               (\"New\" if there are also inherited attributes)
same as inherited attributes.

Inherited Methods:                        (only if the class subclasses another)
self.method_name -- returns return_type           (inherited instance method)
class_name.method_name -- returns return_type     (inherited staticmethod)
cls.method_name -- returns return_type            (inherited classmethod)

[New] Methods:                     (\"New\" if there are also inherited methods)
same as inherited methods.

Keyword Arguments:              (explains arguments for instantiating the class)
arg1 -- description - type

Function/Method Docstring:
=========================

Summary of documentation.

More elaborate documentation, spanning
several lines. .....................
...................................
.............................

Keyword Arguments:      (explains the arguments for calling the method/function)
arg1 -- description - type

-----------------------------

@author: Mitchell Kember

"""

# Imports
from random import randint
from random import shuffle
from re import split
from time import sleep

# Global names
version = 2.8

# Functions
def game_setup():
    """Sets up a game with user input.

    Sets up a game by asking the user how many players,
    their names, difficulties, asks if they would like
    to see the rules, etc.

    """
    print("Pythonic CHEAT! version", version, " by Mitchell Kember")
    # Get the amount of human players.
    while True:
        h = input("How many human players will there be?").strip()
        try:
            h = int(h)
        except ValueError:
            print("Please type a number this time.")
        else:
            break
    # Get the amount of computer players.
    while True:
        c = input("How many computer players will there be?").strip()
        try:
            c = int(c)
        except ValueError:
            print("Please type a number this time.")
        else:
            break
    # Get the names of the human players.
    humans = []
    for num in range(h):
        while True:
            name = input("Type the {0} human's name:".format(Game.ranks[num])) \
                    .strip()
            if not (name.isspace() or not name):
                break
        humans.append(Human(name))
    # Get the names and levels of difficulty of the computer players.
    computers = []
    for num in range(c):
        while True:
            name = input("Type the {0} computer player's name:".format(
                Game.ranks[num])).strip()
            if not (name.isspace() or not name):
                break
        while True:
            d = input("Enter {0}'s difficulty (Easy = 1, Medium = 2, Hard = 3):"
                .format(name)).strip()
            try:
                d = int(d)
            except ValueError:
                print("Please type a number this time.")
            else:
                break
        computers.append(Computer(name, d))
    players = humans + computers
    # Ask if they want to see the rules.
    if 'y' in input("Would you like to see the rules of Pythonic CHEAT! ? (Y/N):").lower():
        print(Game.gameplay)
    input("Press enter to start the game...")
    # Begin the game
    Game(*players)

# Classes
class Player:

    """A player in the game.

    A player in the game with a name and
    a hand of cards. A player can be converted to
    a string with str(instance_of_player), which
    returns their name attribute.

    Attributes:
    self.name -- str
    self.hand -- list

    Keyword Arguments:
    name -- the player's name - str

    """

    def __init__(self, name):
        super().__init__()
        self.name = name
        self.hand = []

    def __str__(self):
        """The string representation of a Player object, the name attribute."""
        return self.name

class Human(Player):

    """A human player, derived from class Player.

    A human player in the game, derived from the
    class PLayer. Currently, it is exactly the same as
    its superclass, and is used only for the name \"Human\".
    A human player has a name and a hand of cards.

    Inherited Attributes:
    self.name -- str
    self.hand -- list

    Keyword Arguments:
    name -- the human player's name - str

    """

    def __init__(self, name):
        super().__init__(name)

class Computer(Player):

    """A computer player, derived from class Player.

    A computer player for the cheat game, derived from the
    class PLayer. It is a Player (which has a name and a hand
    of cards) with a level of difficulty.

    Inherited Attributes:
    self.name -- str
    self.hand -- list

    New Attributes:
    self.difficulty - use class Difficulty (numbers 1 to 3)

    Keyword Arguments:
    name -- the computer player's name
    [difficulty] -- the computer player's level of difficulty,
        Difficulty.easy, Difficulty.medium or Difficulty.hard
        Default: a random choice out of the three

    """

    def __init__(self, name, difficulty=randint(1, 3)):
        super().__init__(name)
        if difficulty < 1 or difficulty > 3:
            raise ValueError
        self.difficulty = difficulty

class Difficulty:

    """An enum class for a computer player's difficulty.

    An enum class to define a computer player's level
    of difficulty, either 1 (Difficulty.easy), 2
    (Difficulty.medium) or 3 (Difficulty.hard). Difficulty
    is not to be instatiated.

    Attributes:
    Difficulty.unknown -- 0
    Difficulty.easy -- 1
    Difficulty.medium -- 2
    Difficulty.hard -- 3

    Methods:
    Difficulty.name -- returns str

    """

    unknown, easy, \
    medium, hard = range(4)

    @staticmethod
    def name(difficulty):
        """Returns the name of the difficulty level."""
        return ['unknown', 'easy', 'medium', 'hard'][difficulty]

class Deck:

    """A Deck of 52 cards.

    A deck of 52 regular playing cards for the game, each
    card being an instance of the Card class (see the Card
    class documentation for more details).

    Attributes:
    self.cards -- list

    Methods:
    self.shuffle_deck -- returns None
    Deck.sort_hand -- returns list

    Keyword Arguments:
    [shuffled] -- shuffles the deck after creating if True - bool
        default: True

    """

    def __init__(self, shuffled=True):
        super().__init__()
        self.cards = []
        # Loop through the four suits and the 13 values
        # And create each card (52)
        for s in range(1, 5):
            for v in range(1, 14):
                self.cards.append(Card(v, s))
        if shuffled:
            self.shuffle_deck()
    def shuffle_deck(self):
        """Shuffles the deck."""
        shuffle(self.cards)

    @staticmethod
    def sort_hand(hand):
        """Sorts a Player's hand of cards by value (ace through king)."""
        vals = []
        for c in hand:
            vals.append((c, c.value))
        # Sort by the second element in each tuple (the value)
        vals.sort(key=lambda a:a[1])
        return [c[0] for c in vals]

class Card:

    """A regular playing card.

    A regular playing Card, each with a value
    (ace through king) and a suit (spades, clubs,
    hearts or diamonds). 52 Card objects make up
    a Deck object. Card objects can be compared
    with ==, !=, <=, >=, > or < and can be converted
    to a string with str(instance_of_Card).

    Attributes:
    self.value -- the card's value (ace, four, queen, etc.)
    self.suit -- the card's suit (spades, diamonds, etc.)

    Keyword Arguments:
    value -- the card's value, a number from 1 to 13.
        use Value.ace, Value.two, etc. Value is
        a class with the 13 values
    suit -- the card's suit, a number from 1 to 4.
        use Suit.spades, Suit.hearts, etc. Suit is a
        class with the 4 suits

    """

    def __init__(self, value, suit):
        super().__init__()
        self.value = value
        self.suit = suit

    def __str__(self):
        """Returns the name of the Card. (Format: \"The [value] of [suit]\")"""
        return "the " + Value.name(self.value) + " of " + \
            Suit.name(self.suit)

    def __eq

Whichever you prefer. Designer can save you a lot of time, but it generates some nasty python code. I personally prefer coding (all) my gui by hand. It's a matter of preference.

I think it's time you took a look at modules though. You can split your program down across different files and make it a breeze to read.

This article has been dead for over six months. Start a new discussion instead.