Wednesday, September 17, 2014

Memory (Python / Pygame)

I highly recommend the Python tutorials by sentdex on Youtube. When I finished his series on pygame I went out looking for more projects.

I came across the Making Games with Python and Pygame textbook / e-book combo by Albert Sweigert. I've enjoyed the first few chapters so much that I want to add this to my bookshelf soon. I haven't read his section on Memory yet, but I decided to give it a try using what I know from sentdex's tutorials.

This game looks like this:
The player clicks on a white tile to expose its symbol. (Ints now, but those could be used as indices to an array of images.) The symbol is text drawn on a surface whose top left corner is lined up with the tile's. (I will center it later.) Each time a tile is revealed it is added to a list. When a tile was clicked on twice, it led to a nasty bug. But I think that's fixed with the check to see if the tile's index is in the list.

Here's the code:
import time
import random
import pygame

pad = 20
nsymb = 4
row,col = 4,4
ntiles = row*col
tile_h,tile_w = 100,100

white = (255,255,255)

disp_h = (row+1) * pad + row * tile_w
disp_w = (col+1) * pad + col * tile_h
disp = pygame.display.set_mode( (disp_h, disp_w) )

pygame.font.init()
font = pygame.font.Font('freesansbold.ttf',115)
clock = pygame.time.Clock()

class Tile:
    def __init__(self,i,j,symb):
        self.symb = symb
        self.reveal = False
        self.matched = False
        x = (i+1) * pad + i*tile_w
        y = (j+1) * pad + j*tile_h
        self.rect = pygame.Rect(x,y,tile_w,tile_h)
    def hide(self):
        self.reveal = False
    def show(self):
        self.reveal = True
        textSurf = font.render(self.__repr__(),True,(255,0,0))
        disp.blit(textSurf,self.rect.topleft)
    def __repr__(self):
        return str(self.symb) if self.reveal else "*"

def refresh(tiles,start=False):
    for tile in tiles:
        if start or tile.reveal and not tile.matched:
            tile.hide()
            pygame.draw.rect(disp,white,tile.rect)

def keep(shown,tile,index):
    shown.append(index)
    tile.matched = True

def end():    
    pygame.quit()
    quit()
    
def game_loop():
    c,r,s = range(col),range(row),range(nsymb)
    symbs = [i for i in s for j in range(int(ntiles/nsymb))]
    random.shuffle(symbs)
    tiles = [Tile(i,j,symbs[i+j*col]) for j in r for i in c]
    shown = []
    refresh(tiles,True)
    while len(shown) < ntiles:    
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                end()
            elif event.type == pygame.MOUSEBUTTONDOWN:
                loc = event.__dict__['pos']
                for i,tile in enumerate(tiles):
                    if tile.rect.collidepoint(loc):
                        tile.show()
                        pygame.display.update()
                        if i not in shown:
                            if len(shown) % 2 == 1:
                                j = shown.pop()
                                prev = tiles[j]
                                if tile.symb == prev.symb:
                                    keep(shown,tile,i)
                                    keep(shown,prev,j)
                                else:
                                    time.sleep(2)
                                    refresh(tiles)
                            else:
                                shown.append(i)
        pygame.display.update()
        clock.tick(30)
    end()
game_loop()

No comments:

Post a Comment