5
\$\begingroup\$

I've been learning Python for a couple of months, and I decided to have a go at a small project that I felt able to complete on my own: a version of the card game UNO, using PyGame for the graphical interface. I've settled with a version that mostly works. I've had a great time building it, and certainly my child of 4 has a great time playing it :) It's nothing fancy: a two-player game against a dumb AI, but I guess it's nice for a start.

Now, the code is over 600 lines long, and as I was adding features, I knew there were far better ways to write it, some of which I won't have even heard of, but I wanted to get as far as possible. So I would be pleased to get some code reviews, the harsher the better.

Below I've left the full code. There are a number of comments in Spanish, but that's mostly for my own consumption. If anyone does take the time to review and destroy it, I will be eternally thankful.

import pygame
import random
import os
import sys

main_dir = os.path.split(os.path.abspath(__file__))[0]
os.environ['SDL_VIDEO_CENTERED'] = '1'


class Button:  # Con esta clase crearemos una colección de botones con el color, coordenadas y texto que definamos

    def __init__(self, x, y, w, h):
        self.x = x
        self.y = y
        self.w = w
        self.h = h
        self.rect = (self.x, self.y, self.w, self.h)
        self.color = []
        self.text = []

    def contains_point(self, point):
        """Return True if my sprite rectangle contains point pt """
        (my_x, my_y) = self.x, self.y
        my_width = self.w
        my_height = self.h
        (x, y) = point
        return my_x <= x < my_x + my_width and my_y <= y < my_y + my_height


class Text:

    def __init__(self, x, y, text):
        self.font = pygame.font.SysFont("Fixedsys Excelsior", 48)
        self.x = x
        self.y = y
        self.position = (self.x, self.y)
        self.text = self.font.render(text.format(), True, (255, 255, 255))


class CardSprite:  # Vamos a cargar una imagen que contiene todas las cartas.
    # Esta imagen es un objeto diferente a la carta, por tanto tendrá su propia clase

    def __init__(self):  # Cargamos una lista de coordenadas donde están las cartas
        self.sheet = pygame.image.load(os.path.join(main_dir, 'UNO', "Copia UNO.jpg"))
        self.x = self.sheet.get_width()  # 800
        self.y = self.sheet.get_height()  # 882

    def load_grid_images(self):
        sheet_rect = self.sheet.get_rect()
        sheet_width, sheet_height = sheet_rect.size
        sprite_rects = []
        cardheighty = 0
        cardwidthx = 0
        cardsizex = self.x // 10
        cardsizey = 125
        for card in range(56):  # El rango sería mejor definirlo en función del número de cartas del mazo, revisar
            card_coordinates = [cardwidthx, cardheighty, cardsizex, cardsizey]
            sprite_rects.append(card_coordinates)
            cardwidthx += cardsizex
            if cardwidthx >= self.x:
                cardwidthx = 0
                cardheighty += cardsizey
        return self.images_at(sprite_rects)

    def image_at(self, rectangle, colorkey=None):
        """Load a specific image from a specific rectangle."""
        # Loads image from x, y, x+offset, y+offset.
        rect = pygame.Rect(rectangle)
        image = pygame.Surface(rect.size)
        image.blit(self.sheet, (0, 0), rect)
        if colorkey is not None:
            if colorkey == -1:
                colorkey = image.get_at((0, 0))
            image.set_colorkey(colorkey, pygame.RLEACCEL)
        return image

    def images_at(self, rects, colorkey=None):
        """Load a whole bunch of images and return them as a list."""
        return [self.image_at(rect, colorkey) for rect in rects]


class Card:
    suits = ["Rojo", "Amarillo", "Verde", "Azul", "Comodín"]  # suit es un atributo de clase
    ranks = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0",
             "pierdeturno", "cambiasentido", "robados", "eligecolor", "robacuatro"]  # rank es otro atributo de clase

    def __init__(self, suit=0,
                 rank=0):  # __init__ crea una instancia de la clase Card; cada carta tiene un suit y un rank
        self.suit = suit
        self.rank = rank
        self.position = []
        self.image = None

    def contains_point(self, point):
        """ Return True if my sprite rectangle contains point pt """
        (my_x, my_y) = self.position
        my_width = self.image.get_width()
        my_height = self.image.get_height()
        (x, y) = point
        return my_x <= x < my_x + my_width and my_y <= y < my_y + my_height


class Deck:
    def __init__(self):
        self.cards = []  # Creamos el atributo cards. Recordar que los atributos en __init__, aparte de self, son opcionales
        for suit in range(4):
            for rank in range(0, 10):
                self.cards.append(Card(suit, rank))
        for suit in range(4):
            for rank in range(10, 13):
                self.cards.append(Card(suit, rank))
        for suit in range(4, 5):
            for rank in range(13, 15):
                self.cards.append(Card(suit, rank))
                self.cards.append(Card(suit, rank))

        sprite = CardSprite()
        images = sprite.load_grid_images()

        i = 0
        for image in images:
            self.cards[i].image = image
            i += 1

        questionmark = sprite.image_at([720, 750, 80, 125])
        self.image = questionmark
        self.position = (650, 256)

    def shuffle(self):
        rng = random.Random()
        rng.shuffle(self.cards)

    def remove(self, card):
        if card in self.cards:
            self.cards.remove(card)  # Usamos el método remove
            return True
        return False

    def pop(self):
        return self.cards.pop()  # Pop toma la última carta y la reparte

    def is_empty(self):
        return self.cards == []  # True si no quedan cartas en el mazo

    def deal(self, hands, num_cards=999):
        num_hands = len(hands)
        for i in range(num_cards):
            if self.is_empty():
                break
            card = self.pop()
            hand = hands[i % num_hands]
            hand.add(card)

    def contains_point(self, point):
        """ Return True if my sprite rectangle contains point pt """
        (my_x, my_y) = self.position
        my_width = self.image.get_width()
        my_height = self.image.get_height()
        (x, y) = point
        return my_x <= x < my_x + my_width and my_y <= y < my_y + my_height


class Hand(Deck):
    pass

    def __init__(self, name=""):
        super().__init__()
        self.cards = []
        self.name = name
        if name == "AI":
            self.status = 1  # jugador controlado por el ordenador
        else:
            self.status = 0  # jugador humano

    def add(self, card):
        self.cards.append(card)


class UNOGame:

    def __init__(self):
        self.all_buttons = []
        self.hands = []
        self.colors = ([234, 26, 39], [248, 224, 0], [0, 164, 78], [2, 149, 216], [255, 165, 0],
                       [0, 0, 0])  # Rojo, amarillo, verde, azul, naranja, negro
        surface_sizex = 1280  # Ancho del tablero, en píxeles
        surface_sizey = 640  # Alto del tablero, en píxeles
        self.main_surface = pygame.display.set_mode((surface_sizex, surface_sizey))  # Creamos el tablero
        self.play_area = (320, 264)
        self.surface_color = (19, 136, 8)  # Red/Green/Blue; la superficie del tablero será de color verde oscuro
        self.deck = Deck()
        self.deck.shuffle()


    def place_cards(self):
        # Definimos un patrón para pegar las cartas una junto a otra
        [paste_x, paste_y] = [64, 480]
        [paste_xAI, paste_yAI] = [64, 32]

        # Definimos la posición que ocuparán las cartas sobre el tablero al empezar la partida
        for hand in self.hands:
            for card in hand.cards:
                card.hidden = self.deck.image  # Para el atributo hidden usamos la carta con el signo de interrogación,
                # que anteriormente asignamos al atributo image del mazo
                if hand.name == "AI":
                    card.position = [paste_xAI, paste_yAI]
                    paste_xAI += 85
                else:
                    card.position = [paste_x, paste_y]
                    paste_x += 85

    def place_buttons(self):
        size = 45
        x = self.play_area[0]
        y = self.play_area[1]
        # Definimos los botones para elegir entre los cuatro colores, así como el botón de pasar turno y el del color en juego
        for n in range(4):
            a_button = Button(x - 55, y + 145, size, size)
            x += 50
            self.all_buttons.append(a_button)

        coordinates = [850, 275, 125, 25], [750, 325, 330, 25], [225, 400, 175, 30], [575, 400, 175, 30]
        for c in coordinates:
            a_button = Button(c[0], c[1], c[2], c[3])
            self.all_buttons.append(a_button)

        c = 0
        for button in self.all_buttons:
            button.color = self.colors[c]
            c += 1
            if c == 6:
                break

        self.all_buttons[4].text = self.font.render("Pasar turno".format(), True, (0, 0, 0))
        self.all_buttons[5].text = self.font.render("El color en juego es el {0}".format(self.color_in_play), True,
                                                    (255, 255, 255))
        self.all_buttons[6].text = self.font.render("Volver a jugar".format(), True, (0, 0, 0))
        self.all_buttons[6].color = self.colors[0]
        self.all_buttons[7].text = self.font.render("Salir del juego".format(), True, (0, 0, 0))
        self.all_buttons[7].color = self.colors[3]

    def blit_buttons(self, i=0, j=6):
        for n in range(i, j):
            button_color = self.all_buttons[n].color
            button_rect = (self.all_buttons[n].x, self.all_buttons[n].y, self.all_buttons[n].w, self.all_buttons[n].h)
            if n == 5:
                self.all_buttons[n].text = self.font.render("El color en juego es el {0}".format(self.color_in_play),
                                                            True, (255, 255, 255))
            button_text = self.all_buttons[n].text
            self.main_surface.fill(button_color, button_rect)
            try:
                self.main_surface.blit(button_text, button_rect)
            except:
                TypeError

    def update_cards(self):
        # Ponemos las cartas sobre el tablero
        for hand in self.hands:
            for card in hand.cards:
                if hand.name == "AI":
                    self.main_surface.blit(card.hidden,
                                           card.position)  # Si es una carta de la IA, elegimos la imagen del signo de interrogación
                else:
                    self.main_surface.blit(card.image, card.position)
        self.main_surface.blit(self.deck.image, self.deck.position)
        self.main_surface.blit(self.card_in_play.image, self.play_area)

    def update_surface(self):
        # last_play = Text(600, 200, "La carta en juego es " + self.card_in_play.suits[self.color] + self.card_in_play.ranks[self.number])
        # Pintamos la superficie            
        self.main_surface.fill(self.surface_color)
        self.update_cards()
        self.blit_buttons(4, 6)
        pygame.display.flip()

    def play_discard(self):
        self.deck.cards = self.discard_deck.cards  # Asignamos al mazo las cartas del mazo de descartes
        self.deck.shuffle()  # Barajamos el nuevo mazo
        self.discard_deck.cards = []  # Vaciamos el mazo de descartes para reiniciar el ciclo

    def start_UNO(self):  # Creamos la partida del juego de cartas UNO, que es un tipo de juego de cartas

        # Iniciamos el módulo pygame
        pygame.mixer.pre_init(44100, -16, 2, 2048)  # setup mixer to avoid sound lag
        pygame.init()
        pygame.mixer.init()
        pygame.mixer.music.load(os.path.join(main_dir, 'UNO', "lobby.mp3"))
        pygame.mixer.music.play(-1)

        # Creamos una superficie de juego, donde colocar las cartas

        pygame.display.set_caption('UNO')
        self.main_surface.fill(self.surface_color)
        self.font = pygame.font.SysFont("Fixedsys Excelsior", 32)

        # Cargamos el logo del juego para la carga inicial
        logo = pygame.image.load(os.path.join(main_dir, 'UNO', "UNO_logo_small.png"))
        h = logo.get_height()
        w = logo.get_width()
        welcome = Text(345, 580, "Pulsa cualquier tecla para continuar")
        surface_sizex = 1280  # Ancho del tablero, en píxeles
        surface_sizey = 640  # Alto del tablero, en píxeles
        self.main_surface.blit(logo, (surface_sizex // 2 - w // 2, surface_sizey // 2 - h // 2))
        self.main_surface.blit(welcome.text, welcome.position)
        pygame.display.flip()

        while True:
            event = pygame.event.poll()
            if event.type == pygame.KEYDOWN:  # Hemos pulsado una tecla
                break
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()

        # Permitimos al jugador introducir su nombre
        name = Text(100, 100, "Introduce tu nombre (pulsa enter cuando hayas terminado):")
        self.main_surface.fill(self.surface_color)
        self.main_surface.blit(name.text, name.position)
        pygame.display.flip()

        self.player_name = ''
        font = pygame.font.SysFont("Fixedsys Excelsior", 48)
        enter = 0
        while enter == 0:
            event = pygame.event.poll()  # Buscar eventos y asignárselos a la variable event
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_BACKSPACE:
                    self.player_name = self.player_name[:-1]
                elif event.key == pygame.K_RETURN:
                    enter = 1
                else:
                    self.player_name += event.unicode
                self.main_surface.fill(self.surface_color)
                txt_surface = font.render(self.player_name, True, pygame.Color('dodgerblue2'))
                self.main_surface.blit(txt_surface, (100, 200))
                self.main_surface.blit(name.text, name.position)
                pygame.display.flip()

        self.main_surface.fill(self.surface_color)
        tachan = Text(100, 300, "¡Bienvenido, " + self.player_name + "! La partida comenzará en unos segundos")
        self.main_surface.blit(tachan.text, tachan.position)
        pygame.display.flip()

        self.names = [self.player_name, "AI"]
        self.main_surface.fill(self.surface_color)

    def play_UNO(self):  # Iniciamos la partida de UNO
        # Creamos las manos del juego
        pygame.mixer.music.load(os.path.join(main_dir, 'UNO', "lobby.mp3"))
        pygame.mixer.music.play(-1)
        for name in self.names:
            self.hands.append(Hand(name))

        # Repartimos las cartas
        self.deck.deal(self.hands, 7 * len(self.names))  # Tomamos el objeto mazo que pertenece al objeto juego (self),
        # y repartimos siete cartas a cada jugador

        # Colocamos las cartas sobre el tablero            
        self.place_cards()

        # Sacamos una carta para empezar a jugar
        self.card_in_play = self.deck.pop()
        self.card_in_play.position = self.play_area
        self.color = self.card_in_play.suit  # Me interesa separar el color de la carta, para poder implementar el comodín eligecolor
        self.number = self.card_in_play.rank

        # Creamos algunas cositas más parabotones extra
        self.color_in_play = self.card_in_play.suits[self.color]

        turn = 0
        pierdeturno = 0
        self.has_drawn = 2  # Para cubrir el caso en que la carta inicial sea una carta de efectos
        i = 0

        self.place_buttons()
        self.update_surface()
        self.discard_deck = Deck()  # Creamos un mazo de descartes
        self.discard_deck.cards = []  # Vaciamos el mazo de descartes

        while True:
            # Comenzamos el bucle estableciendo los efectos en función de la carta en juego (self.card_in_play):

            if self.card_in_play.rank == 10:  # "pierdeturno": # El turno pasará al otro jugador
                if self.has_drawn == 0:
                    pierdeturno += 1
                    self.has_drawn = 1

            elif self.card_in_play.rank == 11:  # "cambiasentido":
                self.has_drawn = 1  # Esto es simplemente para poder simplificar la fórmula de efectos y expresarla como 10 <= rank <= 14

            elif self.card_in_play.rank == 12:  # "robados":
                if self.has_drawn != 1:  # No queremos que al comenzar el siguiente turno se vuelvan a robar dos cartas
                    for n in range(2):
                        if self.deck.is_empty():  # Si no quedan cartas en el mazo
                            self.play_discard()
                        self.hands[(i + 1) % len(self.hands)].cards.append(
                            self.deck.pop())  # Robo dos cartas y las añado a mi mano
                    self.has_drawn = 1

            elif self.card_in_play.rank == 13:  # "eligecolor":
                if self.has_drawn != 1:  # No queremos que al final de este turno se vuelvan a robar cuatro cartas
                    # Aquí hay que añadir un árbol de decisión según sea jugador IA o jugador humano, para elegir el color
                    if self.hands[i].status == 1:  # si el jugador es IA
                        rng = random.Random()
                        self.color = rng.randrange(0, 4)
                    else:  # si el jugador es humano
                        self.blit_buttons(0, 4)
                        pygame.display.flip()
                        has_picked_color = 0
                        while has_picked_color == 0:
                            for event in pygame.event.get():
                                if event.type == pygame.MOUSEBUTTONDOWN:  # Hemos hecho click
                                    place_of_click = event.dict["pos"]
                                    b = 0
                                    for button in self.all_buttons[0:4]:
                                        if button.contains_point(place_of_click):
                                            has_picked_color = 1
                                            self.color = b
                                        b += 1
                    self.card_in_play.suit = self.color
                    self.color_in_play = self.card_in_play.suits[self.color]
                    self.has_drawn = 1

            elif self.card_in_play.rank == 14:  # "robacuatro":
                if self.has_drawn != 1:  # No queremos que al final de este turno se vuelvan a robar cuatro cartas
                    for n in range(4):
                        if self.deck.is_empty():  # Si no quedan cartas en el mazo
                            self.play_discard()
                        self.hands[(i + 1) % len(self.hands)].cards.append(
                            self.deck.pop())  # Robo cuatro cartas y las añado a mi mano
                    # Aquí hay que añadir un árbol de decisión según sea jugador IA o jugador humano, para elegir el color
                    if self.hands[i].status == 1:  # si el jugador es IA
                        rng = random.Random()
                        self.color = rng.randrange(0, 4)
                    else:  # si el jugador es humano
                        self.blit_buttons(0, 4)
                        pygame.display.flip()
                        has_picked_color = 0
                        while has_picked_color == 0:
                            for event in pygame.event.get():
                                if event.type == pygame.MOUSEBUTTONDOWN:  # Hemos hecho click
                                    place_of_click = event.dict["pos"]
                                    b = 0
                                    for button in self.all_buttons[0:4]:
                                        if button.contains_point(place_of_click):
                                            has_picked_color = 1
                                            self.color = b
                                        b += 1
                    self.card_in_play.suit = self.color
                    self.color_in_play = self.card_in_play.suits[self.color]
                    self.has_drawn = 1

            # Actualizamos las cartas
            self.place_cards()
            self.update_surface()

            # Los efectos ya están consolidados (esperemos). Ahora hay que definir el cambio de turno
            turn += 1
            i = (turn + pierdeturno) % len(
                self.hands)  # 0 en el primer turno, salvo que la primera carta en juego sea pierdeturno
            self.playable_cards = []
            suits_in_hand = []
            hand = self.hands[i].cards  # Llamamos hand a la mano que está jugando, para que el código sea más legible

            # Comienza el siguiente turno, propiamente dicho
            # Determinamos si tenemos cartas en la mano que podamos jugar:
            for card in hand:
                suits_in_hand.append(
                    card.suit)  # Anotamos el color de cada carta, para comprobar después si podemos usar el comodín robacuatro
                if card.suit == self.color or card.rank == self.card_in_play.rank or card.rank == 13:  # "eligecolor":
                    # Si la carta comparte color o número con la que está en juego, o si es el comodín de elegir color
                    self.playable_cards.append(card)

            if self.color not in suits_in_hand:  # Si no tenemos ninguna carta del mismo color que la que está en el área de juego
                for card in hand:
                    if card.rank == 14:  # "robacuatro":
                        self.playable_cards.append(card)  # Lo añadimos a las cartas que podemos jugar

            if self.hands[i].status == 1:  # si el jugador es IA
                self.AI_plays(hand)

            else:  # si el jugador es humano
                self.human_plays(hand)

            # Consideraciones de final de turno, a continuación

            self.color = self.card_in_play.suit
            self.color_in_play = self.card_in_play.suits[self.color]

            if self.deck.is_empty():  # Si no quedan cartas en el mazo
                self.play_discard()

            self.place_cards()
            self.update_surface()

            if self.hands[i].is_empty():  # Si no quedan cartas en la mano
                end_game = 0
                self.main_surface.fill(self.surface_color)
                if self.hands[i].name == self.player_name:  # Ha ganado el jugador humano
                    pygame.mixer.music.load(os.path.join(main_dir, 'UNO', "win.mp3"))
                    pygame.mixer.music.play(-1)
                    win = Text(100, 300, "¡Enhorabuena, " + self.player_name + "! Has ganado la partida")
                    self.main_surface.blit(win.text, win.position)
                else:  # Ha ganado la IA
                    pygame.mixer.music.load(os.path.join(main_dir, 'UNO', "loss.mp3"))
                    pygame.mixer.music.play(-1)
                    loss = Text(100, 300, "¡Lo sentimos, " + self.player_name + "! El ordenador ha ganado la partida")
                    self.main_surface.blit(loss.text, loss.position)
                self.blit_buttons(6, 8)
                pygame.display.flip()
                while end_game == 0:
                    for event in pygame.event.get():
                        if event.type == pygame.QUIT:
                            pygame.quit()
                            sys.exit()
                        if event.type == pygame.MOUSEBUTTONDOWN:  # Hemos hecho click
                            place_of_click = event.dict["pos"]
                            for button in self.all_buttons[6:8]:
                                if button.contains_point(place_of_click):
                                    if button == self.all_buttons[6]:
                                        end_game = 1
                                        self.deck = Deck()
                                        self.deck.shuffle()
                                        self.play_UNO()
                                    else:
                                        pygame.quit()
                                        sys.exit()

            pygame.display.flip()

    def AI_plays(self, hand):
        if not self.playable_cards:  # No podemos jugar ninguna carta, tenemos que robar
            drawn_card = self.deck.pop()
            if drawn_card.suit == self.color or drawn_card.rank == self.card_in_play.rank or drawn_card.suit == 4:  # "Comodín":
                # Si la carta robada comparte color o número con la que está en juego, o si es un comodín
                self.card_in_play = drawn_card  # Jugamos la carta robada
                self.has_drawn = 0
                # hand.remove(drawn_card) ¡No es necesario, porque no llega a añadir la carta a la mano! Dará error
                self.discard_deck.cards.append(drawn_card)
            else:
                hand.append(drawn_card)  # Añadimos la carta robada a nuestra mano

        else:  # No nos hace falta robar, porque tenemos en la mano cartas que podemos jugar
            rng = random.Random()
            selected_card = self.playable_cards[
                rng.randrange(0, len(self.playable_cards))]  # La IA elige la carta que jugar
            self.card_in_play = selected_card  # La carta elegida será la próxima carta en juego
            self.has_drawn = 0
            self.card_in_play.position = self.play_area  # Adjudicamos a la carta elegida la zona de juego
            hand.remove(selected_card)
            self.discard_deck.cards.append(selected_card)

    def human_plays(self, hand):
        self.has_drawn = 0
        has_played = 0
        has_playable_cards = 0
        while self.has_drawn == 0:
            if not self.playable_cards:  # No podemos jugar ninguna carta, tenemos que robar
                for event in pygame.event.get():            #event = pygame.event.poll()  # Buscar eventos y asignárselos a la variable event
                    if event.type == pygame.QUIT:
                        pygame.quit()
                        sys.exit()
                    if event.type == pygame.MOUSEBUTTONDOWN:  # Hemos hecho click
                        place_of_click = event.dict["pos"]
                        if self.deck.contains_point(place_of_click):
                            drawn_card = self.deck.pop()
                            if drawn_card.suit == self.color or drawn_card.rank == self.card_in_play.rank or drawn_card.suit == 4:  # "Comodín":
                                self.playable_cards.append(drawn_card)
                                has_playable_cards = 1
                            else:
                                pygame.display.flip()
                            self.has_drawn = 1
                            hand.append(drawn_card)
                            # Actualizamos las cartas
                            self.place_cards()
                            self.update_surface()
            else:
                has_playable_cards = 1
                self.has_drawn = 1

        while has_played == 0:
            if has_playable_cards == 1:  # Podemos jugar
                for event in pygame.event.get():
                    if event.type == pygame.QUIT:
                        pygame.quit()
                        sys.exit()
                    if event.type == pygame.MOUSEBUTTONDOWN:  # Hemos hecho click
                        place_of_click = event.dict["pos"]
                        for card in self.playable_cards:
                            if card.contains_point(place_of_click):
                                self.card_in_play = card
                                hand.remove(card)
                                self.discard_deck.cards.append(card)
                                has_played = 1
                                self.has_drawn = 0

            else:  # Tenemos que pasar turno
                for event in pygame.event.get():
                    if event.type == pygame.QUIT:
                        pygame.quit()
                        sys.exit()
                    if event.type == pygame.MOUSEBUTTONDOWN:  # Hemos hecho click
                        place_of_click = event.dict["pos"]
                        if self.all_buttons[4].contains_point(place_of_click):
                            has_played = 1


def main():
    game = UNOGame()
    game.start_UNO()
    game.play_UNO()


# call the "main" function if running this script
if __name__ == '__main__': main()
\$\endgroup\$
0

1 Answer 1

2
\$\begingroup\$

Documentation

The PEP 8 style guide recommends adding docstrings for classes and functions. For example, you can convert the following comment:

class Button:  # Con esta clase crearemos una colección de botones con el color, coordenadas y texto que definamos

to a docstring:

class Button():
    """
    Con esta clase crearemos una colección de botones con el color,
    coordenadas y texto que definamos
    """

This also makes the code more readable since the one very long line is split up into several lines. Converting to English like your other docstrings would be more consistent.

I was hoping to run the code and play the game, but I can't because it relies on several external files. You should add a header doctrsing which summarizes the purpose of the code and notifies the user of external files and where they should be located. Alternately, you could create a version of your code that does not rely on external files.

Comments

I can't really assess whether your comments are helpful or not because I am not fluent in Spanish. However, I'm pretty sure the following comment can be deleted since it simply repeats what the code is doing:

self.cards.remove(card)  # Usamos el método remove

Good comments should explain why the code was written a certain way.

The following comments in the Hand class:

if name == "AI":
    self.status = 1  # jugador controlado por el ordenador
else:
    self.status = 0  # jugador humano

would not be needed if you chose your variable names and types differently. Every time the status variable appears in your code, you need to explain it with a comment. "status" is too generic. Also, since you only set the variable to 1 and 0, it could be a boolean type instead of an integer type. In fact, you may be able to combine the 2 variables into one boolean:

is_ai

Then checks like:

if self.hands[i].status == 1:  # si el jugador es IA

are simplified as:

if self.hands[i].is_ai:

There is no need to compare against 1, and there is no need for the comment. Similar logic applies for lines like:

if hand.name == "AI":

The variable has_played would be better as a boolean as well because it more clearly demonstrates the intent. You only set it to 0 and 1, so you may as well set it to False and True instead.

Simpler

I don't think pass is needed here:

class Hand(Deck):
    pass

In class Deck, this line:

for suit in range(4, 5):

is simpler as:

suit = 4

Layout

This line:

if __name__ == '__main__': main()

is typically split into two lines:

if __name__ == '__main__':
    main()
\$\endgroup\$

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.