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()