# implementation of card game - Memory
# The emojis do not work on Codeskulptor. Use simplequi for emojis
try:
import simplegui
except ModuleNotFoundError:
import simplequi as simplegui
import random
# if you increase the pair count, then also pay attention to the Emoji count
CARD_PAIR_COUNT = 8
CARD_COUNT = CARD_PAIR_COUNT * 2
CARD_WIDTH = 90
CARD_HEIGHT = 100
FONT_SIZE = CARD_HEIGHT-25
FONT_POS_OFFSET_Y = -15
FONT_POS_OFFSET_X = 5
EMOJIS = ['🎄', '✨', '🔥', '🎁', '🐻', '🍔', '⚽', '🌇', '😊', '🥺', '✔️']
MODE_EMOJIS = 1
MODE_NUMBERS = 2
cards = []
exposed = []
state = 0
last_exposed_card_ids = []
turns = 0
mode = MODE_EMOJIS
# helper function to initialize globals
def new_game():
global cards, exposed, state, last_exposed_card_ids, turns
if mode == MODE_NUMBERS:
cards = list(range(CARD_PAIR_COUNT)) * 2
else:
cards = EMOJIS[0:CARD_PAIR_COUNT] * 2
# alternatively
# cards = list(range(CARD_PAIR_COUNT)) + list(range(CARD_PAIR_COUNT))
random.shuffle(cards)
exposed = [False] * CARD_COUNT
# alternatively
# exposed = [False for _ in range(CARD_COUNT)]
state = 0
last_exposed_card_ids = []
turns = 0
def card_id(pos_x):
""" Returns the card_id which the x position belongs to """
for i in range(CARD_COUNT):
if pos_x < CARD_WIDTH * (i+1):
return i
# define event handlers
def mouseclick(pos):
global exposed, last_exposed_card_ids, turns
# only check for x to determine the click position
clicked_card = card_id(pos[0])
# add game state logic here
# 0: start of the game
# 1: single exposed unpaired card
# 2: end of a turn
global state
if state == 0:
exposed[clicked_card] = True
last_exposed_card_ids.append(clicked_card)
state = 1
elif state == 1:
if not exposed[clicked_card]:
exposed[clicked_card] = True
last_exposed_card_ids.append(clicked_card)
state = 2
else:
# change state only if you click on an unexposed card
if not exposed[clicked_card]:
# only flip the last exposed cards over if they are not the same
if cards[last_exposed_card_ids[0]] != \
cards[last_exposed_card_ids[1]]:
exposed[last_exposed_card_ids[0]] = False
exposed[last_exposed_card_ids[1]] = False
exposed[clicked_card] = True
last_exposed_card_ids = [clicked_card]
turns += 1
label.set_text(f'Turns = {turns}')
state = 1
def change_mode():
global mode
mode = not mode
new_game()
# cards are logically 50x100 pixels in size
def draw(canvas: simplegui.Canvas):
for i, card in enumerate(cards):
# `enumerate()` is alternative to for card_index in range(len(cards))
# card borders of one pixel. For example for CARD_WIDTH = 50
# and CARD_HEIGHT = 100
# (0, 0), (49, 0), (49, 99), (0, 99)
nw = (0+i*CARD_WIDTH), 0 # northwest anchor point
ne = nw[0] + CARD_WIDTH - 1, 0 # northeast
se = ne[0], CARD_HEIGHT - 1 # southeast
sw = nw[0], se[1] # southwest
# draw face up or down
if exposed[i]:
text_pos = sw[0] + FONT_POS_OFFSET_X, sw[1] + FONT_POS_OFFSET_Y
canvas.draw_text(
str(card), text_pos, FONT_SIZE, 'yellow', 'monospace')
fill_color = None
else:
fill_color = 'green'
canvas.draw_polygon([nw, ne, se, sw], 1, 'gray', fill_color)
# create frame and add a button and labels
frame = simplegui.create_frame("Memory", CARD_WIDTH*CARD_COUNT, CARD_HEIGHT)
frame.add_button("Reset", new_game)
label = frame.add_label("Turns = 0")
change_mode_button = frame.add_button('Change mode', change_mode)
# register event handlers
frame.set_mouseclick_handler(mouseclick)
frame.set_draw_handler(draw)
# get things rolling
new_game()
frame.start()