What you can do is define a list, turned
to store cards that are turned over.
Here is an example of a Card
class:
class Card(turtle.Turtle):
def __init__(self, number):
super(Card, self).__init__()
self.number = number
def click(self, x, y):
if self in turned:
self.clear()
turned.remove(self)
else:
self.sety(self.ycor() - self.shapesize()[1] * 7)
self.write(self.number, align='center', font=('Arial', 20, 'bold'))
self.sety(self.ycor() + self.shapesize()[1] * 7)
turned.append(self)
The super(Card, self).__init__()
will give the Card
class all the attributes a turtle.Turtle
class has. Use self.number = number
to add a class variable to the Card
class.
In the click
function:
if self in turned:
self.clear()
turned.remove(self)
That will allow to user to unselect a card that is selected by removing it from the turned
list, and clearing the text, and
else:
self.sety(self.ycor() - self.shapesize()[1] * 7)
self.write(self.number, align='center', font=('Arial', 20, 'bold'))
self.sety(self.ycor() + self.shapesize()[1] * 7)
turned.append(self)
will write the text and append the card into the turned
list.
I also defined a Deck
class that will use the Card
class to create a whole grid of cards:
class Deck:
def __init__(self, rows, cols, width, height, x, y):
self.cards = []
for i in range(cols):
for j in range(rows):
card = Card(randint(2, 10))
card.shape("square")
card.color('black', 'white')
card.shapesize(height / 20, width / 20)
card.goto(i * width + x, j * height + y)
card.onclick(card.click)
self.cards.append(card)
Example:
import turtle
from random import randint
wn = turtle.Screen()
wn.tracer(0)
turned = []
class Card(turtle.Turtle):
def __init__(self, number):
super(Card, self).__init__()
self.number = number
def click(self, x, y):
if self in turned:
self.clear()
turned.remove(self)
else:
self.sety(self.ycor() - self.shapesize()[1] * 7)
self.write(self.number, align='center', font=('Arial', 20, 'bold'))
self.sety(self.ycor() + self.shapesize()[1] * 7)
turned.append(self)
print([card.number for card in turned]) # print to display the clicked cards
class Deck:
def __init__(self, rows, cols, width, height, x, y):
self.cards = []
for i in range(cols):
for j in range(rows):
card = Card(randint(2, 10))
card.shape("square")
card.color('black', 'white')
card.shapesize(height / 20, width / 20)
card.goto(i * width + x, j * height + y)
card.onclick(card.click)
self.cards.append(card)
deck = Deck(8, 8, 45, 62.5, -165, -210)
wn.update()
wn.mainloop()
Output:

The above code may seem simpler, but it used one global variable, turned
, and the numbers will only display for half a second with tracer
left on. And clicking directly on the displayed number will not change the status of the cards.
The below code corrects those flaws:
What you can do is define a class to be each card, and a class to be the deck of cards. In the deck class, define a class variable list, turned
to store cards that are turned over.
Here is an example of a Card
class:
class Card(turtle.Turtle):
def __init__(self, number):
super(Card, self).__init__()
self.number = number
self.penup()
def write_number(self, pen):
pen.goto(self.xcor(), self.ycor() - self.shapesize()[1] * 7)
pen.write(self.number, align='center', font=('Arial', 20, 'bold'))
def clicked(self, x, y):
h, w = self.shapesize()[:-1]
half_width = w * 10
half_height = h * 10
return self.xcor() + half_width > x > self.xcor() - half_width and \
self.ycor() + half_height > y > self.ycor() - half_height
The super(Card, self).__init__()
will give the Card
class all the attributes a turtle.Turtle
class has. Use self.number = number
to add a class variable to the Card
class.
The write_number
function will, as you can probably guess, display a number on the current turtle object using a separate turtle object so that the number will be display on the center of the card.
The clicked
function will basically take in two coordinates, and detect whether the coordinates are on the current turtle object. The reason I defined a clicked
function instead of using the built-in turtle.onclick
method, is because I want it to return a Boolean value rather than to execute a function.
The Deck
class that will use the Card
class to create a whole grid of cards.:
class Deck:
def __init__(self, rows, cols, width, height, x, y):
self.pen = turtle.Turtle(visible=False)
self.pen.penup()
self.pen.speed(0)
self.cards = []
self.numbers = []
self.turned = []
for i in range(cols):
for j in range(rows):
card = Card(randint(2, 10))
card.shape("square")
card.color('black', 'white')
card.shapesize(height / 20, width / 20)
card.goto(i * width + x, -j * height - y)
self.cards.append(card)
def click(self, x, y):
for card in self.cards:
if card.clicked(x, y):
if card in self.turned:
card.clear()
self.turned.remove(card)
else:
self.turned.append(card)
self.draw()
print([card.number for card in self.turned])
def draw(self):
for card in self.turned:
card.write_number(self.pen)
The self.cards
list store all the Card
objects, and the self.turned
stores the cards that are turned over.
The click
function will use the clicked
function from the Card
class to detect clicks on all the Card
objects inside the Deck
class variable, cards
.
Finally, the draw
function will display the number on all the cards in the turned
list, using the self.pen
defined in the Deck
.
Full working code:
import turtle
from random import randint
wn = turtle.Screen()
class Card(turtle.Turtle):
def __init__(self, number):
super(Card, self).__init__()
self.number = number
self.penup()
def write_number(self, pen):
pen.goto(self.xcor(), self.ycor() - self.shapesize()[1] * 7)
pen.write(self.number, align='center', font=('Arial', 20, 'bold'))
def clicked(self, x, y):
h, w = self.shapesize()[:-1]
half_width = w * 10
half_height = h * 10
return self.xcor() + half_width > x > self.xcor() - half_width and \
self.ycor() + half_height > y > self.ycor() - half_height
class Deck:
def __init__(self, rows, cols, width, height, x, y):
self.pen = turtle.Turtle(visible=False)
self.pen.penup()
self.pen.speed(0)
self.cards = []
self.numbers = []
self.turned = []
for i in range(cols):
for j in range(rows):
card = Card(randint(2, 10))
card.shape("square")
card.color('black', 'white')
card.shapesize(height / 20, width / 20)
card.goto(i * width + x, -j * height - y)
self.cards.append(card)
def click(self, x, y):
for card in self.cards:
if card.clicked(x, y):
if card in self.turned:
card.clear()
self.turned.remove(card)
else:
self.turned.append(card)
self.draw()
print([card.number for card in self.turned])
def draw(self):
for card in self.turned:
card.write_number(self.pen)
deck = Deck(8, 8, 45, 62.5, -165, -210)
wn.onscreenclick(deck.click)
wn.mainloop()