itachi
itachi

Reputation: 53

Memory game not matching tiles

I am making a memory game and I can't get the program to check if the tiles match and if they do match get the tiles to stay exposed, and keep the tiles exposed for one second if the tiles don't match. lines 116-122 I think that part of the program is supposed to check but it never returns true for is_matching(). I feel like I am supposed to check if the actual tiles themselves are equal to each other but to me, that seems counterproductive because the tile is just a location on the grid and the image is what's drawn on the tile?? thanks in advance

# Code Example 2
#

import pygame
import random
import time


# User-defined functions

def main():
    # initialize all pygame modules (some need initialization)
    pygame.init()
    # create a pygame display window
    pygame.display.set_mode((500, 400))
    # set the title of the display window
    pygame.display.set_caption('Tic Tac Toe')
    # get the display surface
    w_surface = pygame.display.get_surface()
    # create a game object
    game = Game(w_surface)
    # start the main game loop by calling the play method on the game object
    game.play()
    # quit pygame and clean up the pygame window
    pygame.quit()


# User-defined classes

class Game:
    # An object in this class represents a complete game.

    def __init__(self, surface):
        # Initialize a Game.
        # - self is the Game to initialize
        # - surface is the display window surface object

        # === objects that are part of every game that we will discuss
        self.surface = surface
        self.bg_color = pygame.Color('black')

        self.FPS = 60
        self.game_Clock = pygame.time.Clock()
        self.close_clicked = False
        self.continue_game = True

        # === game specific objects
        self.board = []
        self.score = [0]
        self.board_size = 4
        self.create_board()
        self.click = 0
        self.exposed = 0
        self.first_image= None
        self.second_image= None
        self.tile1 = None
        self.tile2 = None
        self.match = None

    def create_board(self):
        Tile.set_surface(self.surface)
        # width = self.surface.get_width()//self.board_size
        # height = self.surface.get_height() // self.board_size
        # image is of type surface
        self.images = []
        new_image =['image1.bmp','image2.bmp','image3.bmp','image4.bmp', 'image5.bmp','image6.bmp','image7.bmp',
                    'image8.bmp','image1.bmp','image2.bmp','image3.bmp','image4.bmp', 'image5.bmp','image6.bmp',
                    'image7.bmp','image8.bmp']
        for file_name in new_image:
            image = pygame.image.load(file_name)
            self.images.append(image)
        # random.shuffle(self.images)
        cover = pygame.image.load('image0.bmp')
        #image1 = pygame.image.load(images_list)
        width = image.get_width()
        height = image.get_height()
        for row_index in range(0, self.board_size):
            row = []
            for col_index in range(0, self.board_size):
                x = width * col_index
                y = height * row_index
                imageindex = row_index * self.board_size + col_index
                image = self.images[imageindex]
                tile = Tile(x,y,image,cover)
                row.append(tile)
            self.board.append(row)

    def play(self):
        # Play the game until the player presses the close box.
        # - self is the Game that should be continued or not.

        while not self.close_clicked:  # until player clicks close box
            # play frame
            self.handle_events()
            self.draw()
            if self.continue_game:
                self.update()
                self.decide_continue()
            self.game_Clock.tick(self.FPS)  # run at most with FPS Frames Per Second

    def handle_events(self):
        # Handle each user event by changing the game state appropriately.
        # - self is the Game whose events will be handled

        events = pygame.event.get()
        for event in events:
            if event.type == pygame.QUIT:
                self.close_clicked = True
            if event.type == pygame.MOUSEBUTTONUP and self.continue_game == True:
                self.handle_mouse_up(event)

    def update(self):
        # Update the game objects for the next frame.
        # - self is the Game to update
        self.score[0] = pygame.time.get_ticks() // 1000
        if self.first_image != None and self.match:
            print(self.check_matching())
            self.first_image = None
            self.tile1 = None
            self.second_image = None
            self.tile2 = None
            self.exposed += 1
            print(self.exposed)
        if self.first_image != None:
            if self.second_image != None and not self.match:
                # time.sleep(1)
                print(self.check_matching())
                self.tile1.hide_tile()
                self.tile2.hide_tile()
                self.second_image = None
                self.tile2 = None
                self.first_image = None
                self.tile1 = None

    def handle_mouse_up(self,event):
        for row in self.board:
            for tile in row:
                valid_click = tile.select(event.pos)
                if valid_click == True:
                    # self.number_exposed += 1
                    # time.sleep(1)
                    tile.expose_tile()
                    print(self.click)
                    if self.click == 0:
                        self.first_image = tile.image
                        self.tile1 = tile
                    elif self.click == 1:
                        self.second_image = tile.image
                        self.tile2 = tile
                    self.click += 1
                    print(self.first_image)
                    print(self.second_image)
                    if self.click > 1:
                        self.click = 0

    def draw(self):
        # Draw all game objects.
        # - self is thae Game to draw
        # draw tiles
        self.surface.fill(self.bg_color)  # clear the display surface first
        for each_row in self.board:
            for each_tile in each_row:
                each_tile.draw()
        self.draw_score()

        pygame.display.update()  # make the updated surface appear on the display

    def draw_score(self):
        # 1. Set the color
        size = self.surface.get_width()
        fg_color = pygame.Color('white')
        # 2.create the font object
        font = pygame.font.SysFont('', 70)
        # 3 Create a text box by rendering the font
        text_string = '' + str(self.score[0])
        text_box = font.render(text_string, True, fg_color, self.bg_color)
        surface_height = self.surface.get_width()
        text_box_height = text_box.get_width()
        location = (surface_height - text_box_height, 0)
        # 4 Compute the location of the text box
        #location = (430, 0)
        # 5 Blit or pin the text box on the surface
        self.surface.blit(text_box, location)

    def decide_continue(self):
        if self.exposed >= 1:
            self.continue_game = False

    def check_matching(self):
        self.match = self.first_image == self.second_image
        return self.match


        # Check and remember if the game should continue
        # - self is the Game to check


class Tile:

    surface = None
    border_size = 3
    border_color = pygame.Color('black')
    # An object in this class represents a Dot that moves
    @classmethod
    def set_surface(cls,game_surface):
        cls.surface = game_surface
    # instance method
    def __init__(self,x , y, image, cover):
        self.image = image
        self.cover = cover
        self.covered = True
        width = self.image.get_width()
        height = self.image.get_height()

        self.rect = pygame.Rect(x, y, width, height)

    def draw(self):
        pygame.draw.rect(Tile.surface,Tile.border_color,self.rect,Tile.border_size)
        Tile.surface.blit(self.image,self.rect)
        if self.covered:
            Tile.surface.blit(self.cover, self.rect)
        else:
            Tile.surface.blit(self.image, self.rect)

        # Draw the dot on the surface
        # - self is the Dot

    def select(self, position):
        valid_click = False

        if self.rect.collidepoint(position):
            if self.covered:
                valid_click = True
                self.expose_tile()
            else:
                valid_click = False

        return valid_click

    def expose_tile(self):
        # if a tile is clicked this method will show the picture underneath that tile
        self.covered = False

    def hide_tile(self):
        self.covered = True

    def __eq__(self, other_tile):
        if self.first_image == other_tile.image:
            return True
        else:
            return False

main()

Upvotes: 3

Views: 1205

Answers (1)

Rabbid76
Rabbid76

Reputation: 210909

The tiles do not match, because what you actually do is to compare image objects:

self.match = self.first_image == self.second_image

but each image is loaded twice. For each image are generated to different objects, so they will never match.

Load each image once and use it for 2 matching tiles:

# define unique image names
new_image =['image1.bmp','image2.bmp','image3.bmp','image4.bmp', 
            'image5.bmp','image6.bmp','image7.bmp','image8.bmp']

# load each unique image
for file_name in new_image:
    image = pygame.image.load(file_name)
    self.images.append(image)

# create a list where each loaded image object is used twice
self.images = self.images + self.images

Furthermore, since the image names just differ in the number, the definition of the name list can be simplified:

new_image = ['image' + str(i) + '.bmp' for i in range(1,9)]

Extension according to the commend

what about the time delay

Completely remove self.first_image and self.second_image. Adapt Tile:

class Tile:

    # [...]
    def __eq__(self, other_tile):
        return self.image == other_tile.image

Once tiles have been clicked then keep them stated in self.tile1 and self.tile2. When the 1st click occurs, then hide the exposed tiles, if they do not match:

if self.click == 0:
    if self.tile1 != self.tile2:
        if self.tile1:
            self.tile1.hide_tile()
        if self.tile2:
            self.tile2.hide_tile()

When the 2nd click occurs then set the time (e.g. pygame.time.get_ticks() + 2000) when they have to be covered automatically:

elif self.click == 1:
    self.tile2 = tile
    self.hide_time = pygame.time.get_ticks() + 2000

Evaluate if the tiles have to be covered in update:

ticks = pygame.time.get_ticks()
if self.tile1 and self.tile2:
    if self.tile1 != self.tile2 and self.hide_time > 0 and ticks > self.hide_time:
        self.tile1.hide_tile()
        self.tile2.hide_tile()

Changes to your code:

class Game:

    def __init__(self, surface):
        # [...]

        self.hide_time = 0

    # [...]

        def update(self):
        ticks = pygame.time.get_ticks()
        self.score[0] = ticks // 1000
        if self.tile1 and self.tile2:
            if self.tile1 != self.tile2 and self.hide_time > 0 and ticks > self.hide_time:
                self.tile1.hide_tile()
                self.tile2.hide_tile()

    def handle_mouse_up(self,event):
        self.hide_time = 0
        if self.click == 0:
            if self.tile1 != self.tile2:
                if self.tile1:
                    self.tile1.hide_tile()
                if self.tile2:
                    self.tile2.hide_tile()

        tiles = [t for row in self.board for t in row if t.select(event.pos) and not t.covered]
        if any(tiles):
            tile = tiles[0]
            if self.click == 0:
                self.tile1 = tile
            elif self.click == 1:
                self.tile2 = tile
                self.hide_time = pygame.time.get_ticks() + 2000
            tile.expose_tile()
            self.click += 1
            if self.click > 1:
                self.click = 0

    # [...]

    def check_matching(self):
        self.match = self.tile1.image == self.tile2.image
        return self.match

Upvotes: 2

Related Questions