Skip to content

Module quickpython.examples.uno

/games/uno.py

Copyright (c) 2019 ShineyDev

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

View Source
"""

/games/uno.py

    Copyright (c) 2019 ShineyDev

    Licensed under the Apache License, Version 2.0 (the "License");

    you may not use this file except in compliance with the License.

    You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software

    distributed under the License is distributed on an "AS IS" BASIS,

    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

    See the License for the specific language governing permissions and

    limitations under the License.

"""

__authors__ = [("shineydev", "contact@shiney.dev")]

__maintainers__ = [("shineydev", "contact@shiney.dev")]

__version_info__ = (2, 0, 0, "final", 0)

__version__ = "{0}.{1}.{2}{3}{4}".format(

    *[str(n)[0] if (i == 3) else str(n) for (i, n) in enumerate(__version_info__)]

)

import os

import random

import colorama

import pyfiglet

CARD_COLORS = ["Blue", "Green", "Red", "Yellow"]

CARD_NAMES = [

    "0",

    "1",

    "1",

    "2",

    "2",

    "3",

    "3",

    "4",

    "4",

    "5",

    "5",

    "6",

    "6",

    "7",

    "7",

    "8",

    "8",

    "9",

    "9",

    "Reverse",

    "Skip",

    "+2",

]

START_HAND = 7

COLOR_FORMATS = {

    "Blue": colorama.Fore.BLUE,

    "Green": colorama.Fore.GREEN,

    "Red": colorama.Fore.RED,

    "Yellow": colorama.Fore.YELLOW,

}

class Card:

    def __init__(self, color: str, name: str):

        """

        initializes a `Card` object

        """

        self.color = color

        self.name = name

    def format(self) -> str:

        """

        formats the card with cli color codes

        returns:

            :: str :: the formatted card

        """

        if self.color != "Wild":

            return "{}{}{}".format(COLOR_FORMATS[self.color], self.short_name, colorama.Fore.RESET)

        return self.short_name

    def is_wild(self) -> bool:

        """

        checks whether the `Card` object is a wildcard

        returns:

            :: bool :: whether the `Card` object is a wildcard

        """

        if self.color == "Wild":

            return True

        return False

    @property

    def short_name(self) -> str:

        """

        generates a short name for the `Card` object

        returns:

            :: str :: the `Card` object's short name

        """

        return "{}{}".format(self.color[:1:], self.name[:1:])

class Deck:

    def __init__(self):

        """

        initializes a `Deck` object

        """

        self.deck = list()

        self.in_play = list()

        self.in_pile = list()

        # add color cards

        for color in CARD_COLORS:

            for name in CARD_NAMES:

                self.deck.append(Card(color, name))

        # add wildcards

        for i in range(4):

            for name in ["Change", "+4"]:

                self.deck.append(Card("Wild", name))

        self.shuffle()

    def flip(self):

        """

        flips `self.in_pile` back over into `self.deck`

        """

        if not self.deck:

            self.deck = self.in_pile

            self.in_pile = list()

    def is_empty(self) -> bool:

        """

        checks whether `self.deck` is empty

        returns:

            :: bool :: whether `self.deck` is empty

        """

        if len(self.deck) == 0:

            return True

        return False

    def reset(self):

        """

        re-builds `self.deck`, `self.in_play` and `self.in_pile` and calls `self.shuffle`

        """

        self.deck = list()

        self.in_play = list()

        self.in_pile = list()

        for color in CARD_COLORS:

            for name in CARD_NAMES:

                self.deck.append(Card(color, name))

        for i in range(4):

            for name in ["Change", "+4"]:

                self.deck.append(Card("Wild", name))

        self.shuffle()

    def shuffle(self):

        """

        shuffles `self.deck`

        """

        random.shuffle(self.deck)

class Hand:

    def __init__(self, deck: Deck):

        """

        initializes a `Hand` object

        """

        self.deck = deck

        self.hand = list()

    def __len__(self) -> int:

        """

        utilized by built-in `len` function

        """

        return len(self.hand)

    def generate(self):

        """

        fills `self.hand` up to len( `START_HAND` )

        """

        for i in range(START_HAND):

            card = self.deck.deck.pop()

            self.hand.append(card)

            self.deck.in_play.append(card)

    def reset(self):

        """

        sets `self.hand` to an empty list

        """

        self.hand = list()

class Player:

    def __init__(self, name: str, deck: Deck):

        """

        initializes a `Player` object

        """

        self.name = name

        self.deck = deck

        self.hand = Hand(self.deck)

    def is_winner(self) -> bool:

        """

        checks whether the `Player` has won the game

        returns:

            :: bool :: whether the `Player` has won the game

        """

        if len(self.hand) == 0:

            return True

        return False

class Uno:

    def __init__(self, player_count: int):

        """

        initializes an `Uno` object

        """

        self.player_count = player_count

    def game(self):

        """

        starts the game

        """

        self.deck = Deck()

        self.players = list()

        self.current_player = 0

        self.clockwise = True

        self.message = ""

        self.color = None

        for i in range(self.player_count):

            self.players.append(Player("P{}".format(i + 1), self.deck))

            self.players[i].hand.generate()

        card = self.deck.deck.pop()

        self.top_card = card

        self.deck.in_pile.append(card)

        if len(self.players) == 2:

            players = [self.players[0].name, self.players[1].name, "--", "--"]

        elif len(self.players) == 3:

            players = [

                self.players[0].name,

                self.players[1].name,

                self.players[2].name,

                "--",

            ]

        elif len(self.players) == 4:

            players = [

                self.players[0].name,

                self.players[1].name,

                self.players[2].name,

                self.players[3].name,

            ]

        while not any([player.is_winner() for player in self.players]):

            cls()

            if self.color:

                color = COLOR_FORMATS[self.color]

            else:

                color = ""

            if self.current_player == 0:

                pointers = ["/\\", " ", " ", " "]

            elif self.current_player == 1:

                pointers = [" ", ">", " ", " "]

            elif self.current_player == 2:

                pointers = [" ", " ", "\\/", " "]

            elif self.current_player == 3:

                pointers = [" ", " ", " ", "<"]

            print(

                """

            {4} left in deck

                     {0}

                     {8}

            {3} {11}     {5}{6}{7}     {9} {1}

                     {10}

                     {2}

            """.format(

                    *players,

                    len(self.deck.deck),

                    color,

                    self.top_card.format(),

                    colorama.Fore.RESET,

                    *pointers

                )

            )

            print()

            if self.message:

                print(self.message)

                print()

                self.message = ""

            print(self.players[self.current_player].name)

            for card in self.players[self.current_player].hand.hand:

                print(card.format(), end=" ")

            print()

            print()

            card = input("choose a card to place;\n> ").strip().upper()

            if card == "+":

                if self.deck.is_empty():

                    self.deck.flip()

                card = self.deck.deck.pop()

                self.players[self.current_player].hand.hand.append(card)

                self.deck.in_play.append(card)

                continue

            try:

                card_index = int(card)

                if card_index not in range(1, len(self.players[self.current_player].hand.hand) + 1):

                    self.message = (

                        "card must be an integer in range 1 - {0} or the card name".format(

                            len(self.players[self.current_player].hand.hand)

                        )

                    )

                    continue

                card = self.players[self.current_player].hand.hand[card_index - 1]

            except (ValueError) as e:

                if card not in [

                    card.short_name for card in self.players[self.current_player].hand.hand

                ]:

                    self.message = (

                        "card must be an integer in range 1 - {0} or the card name".format(

                            len(self.players[self.current_player].hand.hand)

                        )

                    )

                    continue

                card = self.players[self.current_player].hand.hand[

                    [card.short_name for card in self.players[self.current_player].hand.hand].index(

                        card

                    )

                ]

            if card.color == "Wild":

                pass

            elif self.top_card.color == "Wild":

                if card.color != self.color:

                    self.message = "you can't place that card"

                    continue

            elif card.color == self.top_card.color:

                pass

            elif card.name == self.top_card.name:

                pass

            else:

                self.message = "you can't place that card"

                continue

            card = self.players[self.current_player].hand.hand.pop(

                self.players[self.current_player].hand.hand.index(card)

            )

            self.top_card = card

            self.deck.in_play.remove(card)

            self.deck.in_pile.append(card)

            add_cards = 0

            skip_player = False

            reverse = False

            color = ""

            if self.top_card.name == "Change":

                self.color = ""

                while self.color not in ["Blue", "Green", "Red", "Yellow"]:

                    cls()

                    print(

                        """

            {4} left in deck

                     {0}

                     {8}

            {3} {11}     {5}{6}{7}     {9} {1}

                     {10}

                     {2}

                    """.format(

                            *players,

                            len(self.deck.deck),

                            color,

                            self.top_card.format(),

                            colorama.Fore.RESET,

                            *pointers

                        )

                    )

                    print()

                    if self.message:

                        print(self.message)

                        print()

                        self.message = ""

                    print(self.players[self.current_player].name)

                    for card in self.players[self.current_player].hand.hand:

                        print(card.format(), end=" ")

                    print()

                    print()

                    self.color = input("choose a color;\n> ").strip().capitalize()

            elif self.top_card.name == "+4":

                self.color = ""

                while self.color not in ["Blue", "Green", "Red", "Yellow"]:

                    cls()

                    print(

                        """

            {4} left in deck

                     {0}

                     {8}

            {3} {11}     {5}{6}{7}     {9} {1}

                     {10}

                     {2}

                    """.format(

                            *players,

                            len(self.deck.deck),

                            color,

                            self.top_card.format(),

                            colorama.Fore.RESET,

                            *pointers

                        )

                    )

                    print()

                    if self.message:

                        print(self.message)

                        print()

                        self.message = ""

                    print(self.players[self.current_player].name)

                    for card in self.players[self.current_player].hand.hand:

                        print(card.format(), end=" ")

                    print()

                    print()

                    self.color = input("choose a color;\n> ").strip().capitalize()

                add_cards = 4

                skip_player = True

            elif self.top_card.name == "+2":

                add_cards = 2

                skip_player = True

            elif self.top_card.name == "Skip":

                skip_player = True

            elif self.top_card.name == "Reverse":

                reverse = True

            if reverse:

                self.clockwise = not self.clockwise

                if len(self.players) == 2:

                    # we don't want to change the player

                    continue

            if self.clockwise:

                next_player = self.current_player + 1

            else:

                next_player = self.current_player - 1

            if next_player > (len(self.players) - 1):

                next_player -= len(self.players)

            elif next_player < 0:

                next_player += len(self.players)

            if add_cards:

                for i in range(add_cards):

                    if self.deck.is_empty():

                        self.deck.flip()

                    card = self.deck.deck.pop()

                    self.players[next_player].hand.hand.append(card)

                    self.deck.in_play.append(card)

            if skip_player:

                if self.clockwise:

                    next_player = self.current_player + 2

                else:

                    next_player = self.current_player - 2

                if next_player > (len(self.players) - 1):

                    next_player -= len(self.players)

                elif next_player < 0:

                    next_player += len(self.players)

            self.current_player = next_player

        winner = [player for player in self.players if player.is_winner()][0]

        cls()

        print()

        print(

            """

            {4} left in deck

                     {0}

                     {8}

            {3} {11}     {5}{6}{7}     {9} {1}

                     {10}

                     {2}

        """.format(

                *players,

                len(self.deck.deck),

                color,

                self.top_card.format(),

                colorama.Fore.RESET,

                *pointers

            )

        )

        print()

        print("{0} wins!".format(winner.name))

    def start(self):

        """

        calls `self.game` in a 'would you like to play again?' loop

        """

        choice = "y"

        while choice.startswith("y"):

            cls()

            print(pyfiglet.figlet_format("Uno"))

            print()

            input("enter to play\nctrl + c to quit to main menu\n\n")

            self.game()

            choice = input("\nwould you like to play again?\n> ").strip()

if __name__ == "__main__":

    players = None

    while not isinstance(players, int):

        cls()

        players = input("players;\n> ")

        try:

            players = int(players)

        except (ValueError) as e:

            pass

    game = Uno(players)

    game.start()

Variables

CARD_COLORS
CARD_NAMES
COLOR_FORMATS
START_HAND

Classes

Card

class Card(
    color: str,
    name: str
)
View Source
class Card:

    def __init__(self, color: str, name: str):

        """

        initializes a `Card` object

        """

        self.color = color

        self.name = name

    def format(self) -> str:

        """

        formats the card with cli color codes

        returns:

            :: str :: the formatted card

        """

        if self.color != "Wild":

            return "{}{}{}".format(COLOR_FORMATS[self.color], self.short_name, colorama.Fore.RESET)

        return self.short_name

    def is_wild(self) -> bool:

        """

        checks whether the `Card` object is a wildcard

        returns:

            :: bool :: whether the `Card` object is a wildcard

        """

        if self.color == "Wild":

            return True

        return False

    @property

    def short_name(self) -> str:

        """

        generates a short name for the `Card` object

        returns:

            :: str :: the `Card` object's short name

        """

        return "{}{}".format(self.color[:1:], self.name[:1:])

Instance variables

short_name

generates a short name for the Card object

returns: :: str :: the Card object's short name

Methods

format
def format(
    self
) -> str

formats the card with cli color codes

returns: :: str :: the formatted card

View Source
    def format(self) -> str:

        """

        formats the card with cli color codes

        returns:

            :: str :: the formatted card

        """

        if self.color != "Wild":

            return "{}{}{}".format(COLOR_FORMATS[self.color], self.short_name, colorama.Fore.RESET)

        return self.short_name
is_wild
def is_wild(
    self
) -> bool

checks whether the Card object is a wildcard

returns: :: bool :: whether the Card object is a wildcard

View Source
    def is_wild(self) -> bool:

        """

        checks whether the `Card` object is a wildcard

        returns:

            :: bool :: whether the `Card` object is a wildcard

        """

        if self.color == "Wild":

            return True

        return False

Deck

class Deck(

)
View Source
class Deck:

    def __init__(self):

        """

        initializes a `Deck` object

        """

        self.deck = list()

        self.in_play = list()

        self.in_pile = list()

        # add color cards

        for color in CARD_COLORS:

            for name in CARD_NAMES:

                self.deck.append(Card(color, name))

        # add wildcards

        for i in range(4):

            for name in ["Change", "+4"]:

                self.deck.append(Card("Wild", name))

        self.shuffle()

    def flip(self):

        """

        flips `self.in_pile` back over into `self.deck`

        """

        if not self.deck:

            self.deck = self.in_pile

            self.in_pile = list()

    def is_empty(self) -> bool:

        """

        checks whether `self.deck` is empty

        returns:

            :: bool :: whether `self.deck` is empty

        """

        if len(self.deck) == 0:

            return True

        return False

    def reset(self):

        """

        re-builds `self.deck`, `self.in_play` and `self.in_pile` and calls `self.shuffle`

        """

        self.deck = list()

        self.in_play = list()

        self.in_pile = list()

        for color in CARD_COLORS:

            for name in CARD_NAMES:

                self.deck.append(Card(color, name))

        for i in range(4):

            for name in ["Change", "+4"]:

                self.deck.append(Card("Wild", name))

        self.shuffle()

    def shuffle(self):

        """

        shuffles `self.deck`

        """

        random.shuffle(self.deck)

Methods

flip
def flip(
    self
)

flips self.in_pile back over into self.deck

View Source
    def flip(self):

        """

        flips `self.in_pile` back over into `self.deck`

        """

        if not self.deck:

            self.deck = self.in_pile

            self.in_pile = list()
is_empty
def is_empty(
    self
) -> bool

checks whether self.deck is empty

returns: :: bool :: whether self.deck is empty

View Source
    def is_empty(self) -> bool:

        """

        checks whether `self.deck` is empty

        returns:

            :: bool :: whether `self.deck` is empty

        """

        if len(self.deck) == 0:

            return True

        return False
reset
def reset(
    self
)

re-builds self.deck, self.in_play and self.in_pile and calls self.shuffle

View Source
    def reset(self):

        """

        re-builds `self.deck`, `self.in_play` and `self.in_pile` and calls `self.shuffle`

        """

        self.deck = list()

        self.in_play = list()

        self.in_pile = list()

        for color in CARD_COLORS:

            for name in CARD_NAMES:

                self.deck.append(Card(color, name))

        for i in range(4):

            for name in ["Change", "+4"]:

                self.deck.append(Card("Wild", name))

        self.shuffle()
shuffle
def shuffle(
    self
)

shuffles self.deck

View Source
    def shuffle(self):

        """

        shuffles `self.deck`

        """

        random.shuffle(self.deck)

Hand

class Hand(
    deck: quickpython.examples.uno.Deck
)
View Source
class Hand:

    def __init__(self, deck: Deck):

        """

        initializes a `Hand` object

        """

        self.deck = deck

        self.hand = list()

    def __len__(self) -> int:

        """

        utilized by built-in `len` function

        """

        return len(self.hand)

    def generate(self):

        """

        fills `self.hand` up to len( `START_HAND` )

        """

        for i in range(START_HAND):

            card = self.deck.deck.pop()

            self.hand.append(card)

            self.deck.in_play.append(card)

    def reset(self):

        """

        sets `self.hand` to an empty list

        """

        self.hand = list()

Methods

generate
def generate(
    self
)

fills self.hand up to len( START_HAND )

View Source
    def generate(self):

        """

        fills `self.hand` up to len( `START_HAND` )

        """

        for i in range(START_HAND):

            card = self.deck.deck.pop()

            self.hand.append(card)

            self.deck.in_play.append(card)
reset
def reset(
    self
)

sets self.hand to an empty list

View Source
    def reset(self):

        """

        sets `self.hand` to an empty list

        """

        self.hand = list()

Player

class Player(
    name: str,
    deck: quickpython.examples.uno.Deck
)
View Source
class Player:

    def __init__(self, name: str, deck: Deck):

        """

        initializes a `Player` object

        """

        self.name = name

        self.deck = deck

        self.hand = Hand(self.deck)

    def is_winner(self) -> bool:

        """

        checks whether the `Player` has won the game

        returns:

            :: bool :: whether the `Player` has won the game

        """

        if len(self.hand) == 0:

            return True

        return False

Methods

is_winner
def is_winner(
    self
) -> bool

checks whether the Player has won the game

returns: :: bool :: whether the Player has won the game

View Source
    def is_winner(self) -> bool:

        """

        checks whether the `Player` has won the game

        returns:

            :: bool :: whether the `Player` has won the game

        """

        if len(self.hand) == 0:

            return True

        return False

Uno

class Uno(
    player_count: int
)
View Source
class Uno:

    def __init__(self, player_count: int):

        """

        initializes an `Uno` object

        """

        self.player_count = player_count

    def game(self):

        """

        starts the game

        """

        self.deck = Deck()

        self.players = list()

        self.current_player = 0

        self.clockwise = True

        self.message = ""

        self.color = None

        for i in range(self.player_count):

            self.players.append(Player("P{}".format(i + 1), self.deck))

            self.players[i].hand.generate()

        card = self.deck.deck.pop()

        self.top_card = card

        self.deck.in_pile.append(card)

        if len(self.players) == 2:

            players = [self.players[0].name, self.players[1].name, "--", "--"]

        elif len(self.players) == 3:

            players = [

                self.players[0].name,

                self.players[1].name,

                self.players[2].name,

                "--",

            ]

        elif len(self.players) == 4:

            players = [

                self.players[0].name,

                self.players[1].name,

                self.players[2].name,

                self.players[3].name,

            ]

        while not any([player.is_winner() for player in self.players]):

            cls()

            if self.color:

                color = COLOR_FORMATS[self.color]

            else:

                color = ""

            if self.current_player == 0:

                pointers = ["/\\", " ", " ", " "]

            elif self.current_player == 1:

                pointers = [" ", ">", " ", " "]

            elif self.current_player == 2:

                pointers = [" ", " ", "\\/", " "]

            elif self.current_player == 3:

                pointers = [" ", " ", " ", "<"]

            print(

                """

            {4} left in deck

                     {0}

                     {8}

            {3} {11}     {5}{6}{7}     {9} {1}

                     {10}

                     {2}

            """.format(

                    *players,

                    len(self.deck.deck),

                    color,

                    self.top_card.format(),

                    colorama.Fore.RESET,

                    *pointers

                )

            )

            print()

            if self.message:

                print(self.message)

                print()

                self.message = ""

            print(self.players[self.current_player].name)

            for card in self.players[self.current_player].hand.hand:

                print(card.format(), end=" ")

            print()

            print()

            card = input("choose a card to place;\n> ").strip().upper()

            if card == "+":

                if self.deck.is_empty():

                    self.deck.flip()

                card = self.deck.deck.pop()

                self.players[self.current_player].hand.hand.append(card)

                self.deck.in_play.append(card)

                continue

            try:

                card_index = int(card)

                if card_index not in range(1, len(self.players[self.current_player].hand.hand) + 1):

                    self.message = (

                        "card must be an integer in range 1 - {0} or the card name".format(

                            len(self.players[self.current_player].hand.hand)

                        )

                    )

                    continue

                card = self.players[self.current_player].hand.hand[card_index - 1]

            except (ValueError) as e:

                if card not in [

                    card.short_name for card in self.players[self.current_player].hand.hand

                ]:

                    self.message = (

                        "card must be an integer in range 1 - {0} or the card name".format(

                            len(self.players[self.current_player].hand.hand)

                        )

                    )

                    continue

                card = self.players[self.current_player].hand.hand[

                    [card.short_name for card in self.players[self.current_player].hand.hand].index(

                        card

                    )

                ]

            if card.color == "Wild":

                pass

            elif self.top_card.color == "Wild":

                if card.color != self.color:

                    self.message = "you can't place that card"

                    continue

            elif card.color == self.top_card.color:

                pass

            elif card.name == self.top_card.name:

                pass

            else:

                self.message = "you can't place that card"

                continue

            card = self.players[self.current_player].hand.hand.pop(

                self.players[self.current_player].hand.hand.index(card)

            )

            self.top_card = card

            self.deck.in_play.remove(card)

            self.deck.in_pile.append(card)

            add_cards = 0

            skip_player = False

            reverse = False

            color = ""

            if self.top_card.name == "Change":

                self.color = ""

                while self.color not in ["Blue", "Green", "Red", "Yellow"]:

                    cls()

                    print(

                        """

            {4} left in deck

                     {0}

                     {8}

            {3} {11}     {5}{6}{7}     {9} {1}

                     {10}

                     {2}

                    """.format(

                            *players,

                            len(self.deck.deck),

                            color,

                            self.top_card.format(),

                            colorama.Fore.RESET,

                            *pointers

                        )

                    )

                    print()

                    if self.message:

                        print(self.message)

                        print()

                        self.message = ""

                    print(self.players[self.current_player].name)

                    for card in self.players[self.current_player].hand.hand:

                        print(card.format(), end=" ")

                    print()

                    print()

                    self.color = input("choose a color;\n> ").strip().capitalize()

            elif self.top_card.name == "+4":

                self.color = ""

                while self.color not in ["Blue", "Green", "Red", "Yellow"]:

                    cls()

                    print(

                        """

            {4} left in deck

                     {0}

                     {8}

            {3} {11}     {5}{6}{7}     {9} {1}

                     {10}

                     {2}

                    """.format(

                            *players,

                            len(self.deck.deck),

                            color,

                            self.top_card.format(),

                            colorama.Fore.RESET,

                            *pointers

                        )

                    )

                    print()

                    if self.message:

                        print(self.message)

                        print()

                        self.message = ""

                    print(self.players[self.current_player].name)

                    for card in self.players[self.current_player].hand.hand:

                        print(card.format(), end=" ")

                    print()

                    print()

                    self.color = input("choose a color;\n> ").strip().capitalize()

                add_cards = 4

                skip_player = True

            elif self.top_card.name == "+2":

                add_cards = 2

                skip_player = True

            elif self.top_card.name == "Skip":

                skip_player = True

            elif self.top_card.name == "Reverse":

                reverse = True

            if reverse:

                self.clockwise = not self.clockwise

                if len(self.players) == 2:

                    # we don't want to change the player

                    continue

            if self.clockwise:

                next_player = self.current_player + 1

            else:

                next_player = self.current_player - 1

            if next_player > (len(self.players) - 1):

                next_player -= len(self.players)

            elif next_player < 0:

                next_player += len(self.players)

            if add_cards:

                for i in range(add_cards):

                    if self.deck.is_empty():

                        self.deck.flip()

                    card = self.deck.deck.pop()

                    self.players[next_player].hand.hand.append(card)

                    self.deck.in_play.append(card)

            if skip_player:

                if self.clockwise:

                    next_player = self.current_player + 2

                else:

                    next_player = self.current_player - 2

                if next_player > (len(self.players) - 1):

                    next_player -= len(self.players)

                elif next_player < 0:

                    next_player += len(self.players)

            self.current_player = next_player

        winner = [player for player in self.players if player.is_winner()][0]

        cls()

        print()

        print(

            """

            {4} left in deck

                     {0}

                     {8}

            {3} {11}     {5}{6}{7}     {9} {1}

                     {10}

                     {2}

        """.format(

                *players,

                len(self.deck.deck),

                color,

                self.top_card.format(),

                colorama.Fore.RESET,

                *pointers

            )

        )

        print()

        print("{0} wins!".format(winner.name))

    def start(self):

        """

        calls `self.game` in a 'would you like to play again?' loop

        """

        choice = "y"

        while choice.startswith("y"):

            cls()

            print(pyfiglet.figlet_format("Uno"))

            print()

            input("enter to play\nctrl + c to quit to main menu\n\n")

            self.game()

            choice = input("\nwould you like to play again?\n> ").strip()

Methods

game
def game(
    self
)

starts the game

View Source
    def game(self):

        """

        starts the game

        """

        self.deck = Deck()

        self.players = list()

        self.current_player = 0

        self.clockwise = True

        self.message = ""

        self.color = None

        for i in range(self.player_count):

            self.players.append(Player("P{}".format(i + 1), self.deck))

            self.players[i].hand.generate()

        card = self.deck.deck.pop()

        self.top_card = card

        self.deck.in_pile.append(card)

        if len(self.players) == 2:

            players = [self.players[0].name, self.players[1].name, "--", "--"]

        elif len(self.players) == 3:

            players = [

                self.players[0].name,

                self.players[1].name,

                self.players[2].name,

                "--",

            ]

        elif len(self.players) == 4:

            players = [

                self.players[0].name,

                self.players[1].name,

                self.players[2].name,

                self.players[3].name,

            ]

        while not any([player.is_winner() for player in self.players]):

            cls()

            if self.color:

                color = COLOR_FORMATS[self.color]

            else:

                color = ""

            if self.current_player == 0:

                pointers = ["/\\", " ", " ", " "]

            elif self.current_player == 1:

                pointers = [" ", ">", " ", " "]

            elif self.current_player == 2:

                pointers = [" ", " ", "\\/", " "]

            elif self.current_player == 3:

                pointers = [" ", " ", " ", "<"]

            print(

                """

            {4} left in deck

                     {0}

                     {8}

            {3} {11}     {5}{6}{7}     {9} {1}

                     {10}

                     {2}

            """.format(

                    *players,

                    len(self.deck.deck),

                    color,

                    self.top_card.format(),

                    colorama.Fore.RESET,

                    *pointers

                )

            )

            print()

            if self.message:

                print(self.message)

                print()

                self.message = ""

            print(self.players[self.current_player].name)

            for card in self.players[self.current_player].hand.hand:

                print(card.format(), end=" ")

            print()

            print()

            card = input("choose a card to place;\n> ").strip().upper()

            if card == "+":

                if self.deck.is_empty():

                    self.deck.flip()

                card = self.deck.deck.pop()

                self.players[self.current_player].hand.hand.append(card)

                self.deck.in_play.append(card)

                continue

            try:

                card_index = int(card)

                if card_index not in range(1, len(self.players[self.current_player].hand.hand) + 1):

                    self.message = (

                        "card must be an integer in range 1 - {0} or the card name".format(

                            len(self.players[self.current_player].hand.hand)

                        )

                    )

                    continue

                card = self.players[self.current_player].hand.hand[card_index - 1]

            except (ValueError) as e:

                if card not in [

                    card.short_name for card in self.players[self.current_player].hand.hand

                ]:

                    self.message = (

                        "card must be an integer in range 1 - {0} or the card name".format(

                            len(self.players[self.current_player].hand.hand)

                        )

                    )

                    continue

                card = self.players[self.current_player].hand.hand[

                    [card.short_name for card in self.players[self.current_player].hand.hand].index(

                        card

                    )

                ]

            if card.color == "Wild":

                pass

            elif self.top_card.color == "Wild":

                if card.color != self.color:

                    self.message = "you can't place that card"

                    continue

            elif card.color == self.top_card.color:

                pass

            elif card.name == self.top_card.name:

                pass

            else:

                self.message = "you can't place that card"

                continue

            card = self.players[self.current_player].hand.hand.pop(

                self.players[self.current_player].hand.hand.index(card)

            )

            self.top_card = card

            self.deck.in_play.remove(card)

            self.deck.in_pile.append(card)

            add_cards = 0

            skip_player = False

            reverse = False

            color = ""

            if self.top_card.name == "Change":

                self.color = ""

                while self.color not in ["Blue", "Green", "Red", "Yellow"]:

                    cls()

                    print(

                        """

            {4} left in deck

                     {0}

                     {8}

            {3} {11}     {5}{6}{7}     {9} {1}

                     {10}

                     {2}

                    """.format(

                            *players,

                            len(self.deck.deck),

                            color,

                            self.top_card.format(),

                            colorama.Fore.RESET,

                            *pointers

                        )

                    )

                    print()

                    if self.message:

                        print(self.message)

                        print()

                        self.message = ""

                    print(self.players[self.current_player].name)

                    for card in self.players[self.current_player].hand.hand:

                        print(card.format(), end=" ")

                    print()

                    print()

                    self.color = input("choose a color;\n> ").strip().capitalize()

            elif self.top_card.name == "+4":

                self.color = ""

                while self.color not in ["Blue", "Green", "Red", "Yellow"]:

                    cls()

                    print(

                        """

            {4} left in deck

                     {0}

                     {8}

            {3} {11}     {5}{6}{7}     {9} {1}

                     {10}

                     {2}

                    """.format(

                            *players,

                            len(self.deck.deck),

                            color,

                            self.top_card.format(),

                            colorama.Fore.RESET,

                            *pointers

                        )

                    )

                    print()

                    if self.message:

                        print(self.message)

                        print()

                        self.message = ""

                    print(self.players[self.current_player].name)

                    for card in self.players[self.current_player].hand.hand:

                        print(card.format(), end=" ")

                    print()

                    print()

                    self.color = input("choose a color;\n> ").strip().capitalize()

                add_cards = 4

                skip_player = True

            elif self.top_card.name == "+2":

                add_cards = 2

                skip_player = True

            elif self.top_card.name == "Skip":

                skip_player = True

            elif self.top_card.name == "Reverse":

                reverse = True

            if reverse:

                self.clockwise = not self.clockwise

                if len(self.players) == 2:

                    # we don't want to change the player

                    continue

            if self.clockwise:

                next_player = self.current_player + 1

            else:

                next_player = self.current_player - 1

            if next_player > (len(self.players) - 1):

                next_player -= len(self.players)

            elif next_player < 0:

                next_player += len(self.players)

            if add_cards:

                for i in range(add_cards):

                    if self.deck.is_empty():

                        self.deck.flip()

                    card = self.deck.deck.pop()

                    self.players[next_player].hand.hand.append(card)

                    self.deck.in_play.append(card)

            if skip_player:

                if self.clockwise:

                    next_player = self.current_player + 2

                else:

                    next_player = self.current_player - 2

                if next_player > (len(self.players) - 1):

                    next_player -= len(self.players)

                elif next_player < 0:

                    next_player += len(self.players)

            self.current_player = next_player

        winner = [player for player in self.players if player.is_winner()][0]

        cls()

        print()

        print(

            """

            {4} left in deck

                     {0}

                     {8}

            {3} {11}     {5}{6}{7}     {9} {1}

                     {10}

                     {2}

        """.format(

                *players,

                len(self.deck.deck),

                color,

                self.top_card.format(),

                colorama.Fore.RESET,

                *pointers

            )

        )

        print()

        print("{0} wins!".format(winner.name))
start
def start(
    self
)

calls self.game in a 'would you like to play again?' loop

View Source
    def start(self):

        """

        calls `self.game` in a 'would you like to play again?' loop

        """

        choice = "y"

        while choice.startswith("y"):

            cls()

            print(pyfiglet.figlet_format("Uno"))

            print()

            input("enter to play\nctrl + c to quit to main menu\n\n")

            self.game()

            choice = input("\nwould you like to play again?\n> ").strip()