Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Python

Challenge - OOP building a memory game!

Hello everyone!

I usually try to challenge myself of completing the challenge before I watch all of the tutorial-videos of how the teachers solves the challenge. This is my try on the Memory Game challenge that @AmendolaMegan teaches. I choose to put all of my methods and attributes in one class (I understand that you could create a card class but I decided not to cause it's a small project). I will be honest, i took me few weeks to wrap my head around OOP since the previous tutorials had me thinking in another way of programming. I tried many days and got stuck almost everyday. I made some progress but usually got stuck after awhile and after some time I just ended up in bed thinking of how I, hmm how should I put it.. 'how I should think of solving the problem'. The problem I had wasn't writing the code inside of the metods or lack of knowledge in how to use different data types or the logic and arithmetic functions and dunder methods and those alike. No the problem was the difficulty of thinking in an OOP kind of way. Anyway, here is my take on the Memory Game challenge. Please have a look and I gladly like some feedback where i could improve. Cheers!

import random
import os

class Game:

    def __init__(self, game_size):
        self.game_size = game_size
        self.grid = []
        self.words = []
        self.cards = {}
        self.correct_guesses = []
        self.row_headers = [1,2,3,4]
        self.column_headers = ["1", "2", "3", "4"]

        for x in range(self.game_size):
            for y in range(self.game_size):
                self.grid.append((x,y))

    def cls(self):
        os.system('cls' if os.name=='nt' else 'clear')

    def generate_words(self):
        with open("words.txt", "r") as f:
            card_words = f.read().split()
        while True:
            cards = set(random.choices(card_words, k=8))
            if len(cards) < 8:
                continue
            else:
                break
        cards = list(cards) * 2
        random.shuffle(cards)
        return cards

    def set_cards(self, words):
        new_dict = dict()
        idx = 0
        for location in self.grid:
            x,y = location
            if x == 0:
                new_dict[(x,y)] = " "
            elif y == 0:
                new_dict[(x,y)] = " "
            else:
                new_dict[(x,y)] = self.words[idx]
                idx += 1
        return new_dict

    def draw_grid(self, guesses = []):
        for cell in self.grid:
            x,y = cell
            if (x,y) in self.correct_guesses or (x,y) in guesses:
                fill = self.cards[(x,y)]
            else:
                fill = " " * len(self.cards[(x,y)])
            if y == 4:
                if x == 0:
                    print(self.column_headers[y-1] + " " * (len(max(self.words, key=len))-1) + "|", end="\n")
                else:
                    print(f"{fill}" + " " * (len(max(self.words, key=len)) - len(fill))  + "|", end="\n")
            elif y == 0:
                if x > 0:
                    print(str(self.row_headers[x-1]) + "|", end="")
                else:          
                    print(" |", end="")
            else:
                if x == 0:
                    print(self.column_headers[y-1] + " " * (len(max(self.words, key=len))-1) + "|", end="")
                else:    
                    print(f"{fill}" + " " * (len(max(self.words, key=len)) - len(self.cards[(x,y)])) + "|", end="")

    def guess(self, time):
        guess = tuple(map(int, input(f"location of your {time} guess?  ")))
        if guess in self.grid and guess not in self.correct_guesses:
            return guess
        else:
            raise ValueError


if __name__ == '__main__':

    game = Game(5)
    game.words = game.generate_words()
    game.cards = game.set_cards(game.words)
    is_playing = True

    while is_playing:
        game.cls()
        game.draw_grid()
        try:
            guess1 = game.guess("first")
            guess2 = game.guess("second")
            if guess1 == guess2:
                raise ValueError
        except ValueError:
            print("Invalid input, it should look like this: '11', '24', '41'")
            input()
        else:
            if game.cards[guess1] == game.cards[guess2]:
                game.correct_guesses.extend([guess1, guess2])
                game.draw_grid()
                print("It's a match!")
                input("Press enter to continue... ")
            else:
                guesses = [guess1, guess2]
                game.cls()
                game.draw_grid(guesses)
                print(f"location {guess1} {game.cards[guess1]} did not match with location {guess2} {game.cards[guess2]}")
                input("Press enter to continue.. ")

            if len(game.correct_guesses) == len(game.words):
                game.cls()
                game.draw_grid()
                print("You matched all the cards, congratulations!")
                input("Press Enter to continue... ")
                is_playing = False

I realized that I use an external source fot the words I was generating (see the generate_words method). So I will write a sample of some words which I used (the sample I had was much larger).

a sample from the file words.txt

depressed funny houses miss obsequious skip callous quixotic zesty illustrious mug gullible oil rampant property vanish preserve test crayon powder dashing hulking remain wish acid stereotyped bomb venomous prevent thundering

1 Answer

Hey, Isak Landström, I just took a look at your game and it runs nice and smoothly! It catches all errors and makes excellent use of OOP! OOP is tough to grasp at first, but the more you work with it, rewatch videos, take notes, the easier it gets. It looks like you've got a good handle on classes, methods, and how to use them. I love that you implemented an external word list. It's more organized and would make it much easier to add or remove words from the list rather than having a long list in your program. Nicely done!

Side note: I recommend always using either a linter or a pep8 checker to make sure your code complies with the styling guidelines. There are a few codes firing when I enter your code in the checker. Your code looks excellent and running through the list line by line will tidy up your code a bit further.

A little bonus mission could be asking the user if they want to play again and setting the value of is_playing to whatever their input is, just to add a nice little loop that allows them to play the game for as long as they would like.

Excellent job! Keep up the great work :D

Hey Mel and thank you for the reply!

The pep8 checker is a very useful tool to use so I can style my code according to the guidelines, thank you for the link, I will be sure to use it. Yeah my code could use some tidying up but I was excited to get it 'out there' and to show you guys so sometimes my eagerness makes me post before checking that my code follow the conventional styling guidelines. I will work on improving the readability and nit-picking my code for styling errors.

I will try to make the dungeon game that was suggested in the course. I will post that one when I have finished it.

Cheers, Isak.