1
\$\begingroup\$

I've made a memory match game, where the users can draw each card. When the program is ran, there would be a canvas that allows the user to draw in. When the user is done drawing, they can press ENTER, and another fresh canvas will appear.

The user may repeat the process, drawing as many card designs as needed, and when enough card designs are drawn, press ENTER without drawing anything, and the memory match game will begin.

Here is a sped-up demonstration how it works:

enter image description here

My code:

import pygame
from time import sleep
from random import shuffle, choice

ROWS = 5
COLUMNS = 8

pygame.init()
wn = pygame.display.set_mode((435, 500))

class CreateShape:
    def __init__(self, x, y, w, h):
        self.rect = pygame.Rect(x, y, w, h)
        self.drawing = False
        self.color = (255, 0, 0)
        self.cors = []

    def under(self, pos):
        return self.rect.collidepoint(pos)

    def draw(self):
        pygame.draw.rect(wn, (255, 255, 255), self.rect)
        for cor in self.cors:
            if len(cor) > 2:
                pygame.draw.polygon(wn, self.color, cor)
    def create(self):
        while True:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    pygame.quit()
                elif event.type == pygame.MOUSEBUTTONDOWN:
                    if shape.under(event.pos):
                        shape.cors.append([])
                        shape.drawing = True
                elif event.type == pygame.MOUSEBUTTONUP:
                    shape.drawing = False
                elif event.type == pygame.MOUSEMOTION:
                    if shape.under(event.pos):
                        if shape.drawing:
                            shape.cors[-1].append(event.pos)
                elif event.type == pygame.KEYDOWN:
                    return self.cors
            wn.fill((0, 0, 0))
            shape.draw()
            pygame.display.update()

class Card:
    def __init__(self, x, y, w, h, shape, original):
        self.x = x
        self.y = y
        self.w = w
        self.h = h
        self.card_color = (200, 200, 200)
        self.shape_color = (255, 0, 0)
        self.rect = pygame.Rect(x, y, w, h)
        self.shape = shape
        self.original = original
        self.turned = False

    def flip(self):
        self.turned = not self.turned

    def clicked(self, pos):
        return self.rect.collidepoint(pos)

    def draw(self):
        pygame.draw.rect(wn, self.card_color, self.rect)
        if self.turned:
            for shape in self.shape:
                pygame.draw.polygon(wn, self.shape_color, shape)


class Deck:
    def __init__(self, x, y, w, h, rows, cols, score_keeper, shapes=[], space=5):
        shapes *= 2
        self.turned = []
        self.cards = []
        index = 0
        while len(shapes) < rows * cols:
            shapes += [choice(shapes)] * 2
        shuffle(shapes)
        for i in range(rows):
            for j in range(cols):
                shapey = [[[c[0] / 2, c[1] / 2] for c in s] for s in shapes[index]]
                card_x, card_y = x + j * (w + space), y + i * (h + space)
                cors_x = sorted([cor[0] for shape in shapey for cor in shape])
                cors_y = sorted([cor[1] for shape in shapey for cor in shape])
                max_x, min_x = cors_x[0], cors_x[-1]
                max_y, min_y = cors_y[0], cors_y[-1]
                shaper = [[(i + card_x - min_x + (w - max_x + min_x) / 2,
                            j + card_y - min_y + (h - max_y + min_y) / 2) for i, j in shape] for shape in shapey]
                self.cards.append(Card(card_x, card_y, w, h, shaper, shapes[index]))
                index += 1
                
    def check_equal(self, c1, c2):
        for shape1, shape2 in zip(c1.original, c2.original):
            for s1, s2 in zip(shape1, shape2):
                for cor1, cor2 in zip(s1, s2):
                    if cor1 != cor2:
                        return False
        return True
    
    def clicked(self, pos):
        for card in self.cards:
            if card.clicked(pos):
                card.flip()
                if card.turned:
                    self.turned.append(card)
                else:
                    self.turned.remove(card)
        if len(self.turned) == 2:
            self.draw()
            pygame.display.update()
            sleep(0.5)
            if self.check_equal(*self.turned):
                for card in self.turned:
                    self.cards.remove(card)
                score.add()
            else:
                for card in self.turned:
                    card.flip()
                score.remove()
            self.turned.clear()
    def draw(self):
        for card in self.cards:
            card.draw()

class Score:
    def __init__(self, x, y, size=40):
        self.x = x
        self.y = y
        self.size = size
        self.font = pygame.font.SysFont('Arial', size)
        self.total = 0

    def add(self, amt=10):
        self.total += amt

    def remove(self, amt=5):
        self.total -= amt

    def draw(self):
        text = self.font.render(f'Score: {self.total}', False, (255, 255, 255))
        wn.blit(text, (self.x, self.y))

shapes = []
for i in range(ROWS * COLUMNS // 2):
    shape = CreateShape(168, 200, 100, 100)
    my_shape = shape.create()
    if not my_shape:
        break
    shapes.append(my_shape)

score = Score(20, 20)
deck = Deck(20, 90, 45, 65, ROWS, COLUMNS, score, shapes)

while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
        elif event.type == pygame.MOUSEBUTTONDOWN:
            deck.clicked(event.pos)
    wn.fill((0, 0, 0))
    deck.draw()
    score.draw()
    pygame.display.update()

I believe my check_equal function can be greatly simplified, as all it does is return wehther two lists (c1 and c2) of lists of tuples are equal:

def check_equal(self, c1, c2):
    for shape1, shape2 in zip(c1.original, c2.original):
        for s1, s2 in zip(shape1, shape2):
            for cor1, cor2 in zip(s1, s2):
                if cor1 != cor2:
                    return False
    return True

Can you please point out where in my code can be simpliflied, and where can I improve the efficiency of it?

Bug reports will also be greatly appreciated!

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

check_equal() can be greatly simplified:

def check_equal(self, c1, c2):
    return c1.original is c2.original

The main code creates a list of shapes. Deck.__init__() uses shapes *= 2 to double the list of shapes. That is, shapes now contains two copies of each shape. Actually, it contains two references to the same shape. Then a Card is made from each shape, which stores the shape in Card.original. So you can use is to check if the cars have the same shape.

\$\endgroup\$
1
  • \$\begingroup\$ Awesome! ------ \$\endgroup\$
    – Chocolate
    Commented Dec 3, 2020 at 13:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.