Compare commits
28 commits
Author | SHA1 | Date | |
---|---|---|---|
|
f87e6dd836 | ||
|
c8020398a9 | ||
|
3df378dd21 | ||
|
b14c6d2660 | ||
|
4ed1f12107 | ||
|
76a43c94b4 | ||
|
d3ab01ff02 | ||
|
3249c2d2e9 | ||
|
bc04eaf0c8 | ||
|
562623c713 | ||
|
7bdebab0f5 | ||
|
5b284ee2bb | ||
|
32508d1738 | ||
|
ab0589ddf2 | ||
|
01306f9ee0 | ||
|
8d147ad1df | ||
|
3b266b2169 | ||
|
0ca1eca5a3 | ||
|
0068baa68e | ||
|
4a03d31439 | ||
|
aec64b7408 | ||
|
bdcb1dbe55 | ||
|
985dc40ab1 | ||
|
8a34f10a2e | ||
|
d1c265eac9 | ||
|
f95288736c | ||
|
67e6a83952 | ||
|
de30a74c6d |
9 changed files with 167 additions and 25 deletions
24
README.md
24
README.md
|
@ -1,5 +1,23 @@
|
||||||
## Memory Game
|
# Memory Game
|
||||||
|
### Benjamin Zimmerman
|
||||||
|
|
||||||
This is my final project (option 1) for ITSE-1479.
|
---
|
||||||
Instead of using the provided cardback.png image, I used a photo of one of my turtles as the card back.
|
|
||||||
|
|
||||||
|
Info for users:
|
||||||
|
- This is my final project (option 1) for ITSE-1479 (Intro to Scripting Languages)
|
||||||
|
- It is a memory game with a graphical interface.
|
||||||
|
- You can click on a card to flip it. Match 2 cards correctly and the cards will stay flipped. Match all cards to win.
|
||||||
|
- Scoring:
|
||||||
|
- When you make an incorrect match, you lose 1 point.
|
||||||
|
- When you make a correct match, you gain 5 points.
|
||||||
|
|
||||||
|
Technical info:
|
||||||
|
- This program is written in Python 3, using the turtle module for graphics, and the pygame module for sound and music.
|
||||||
|
- Instead of using the provided cardback.png image, I used a photo of one of my turtles as the card back.
|
||||||
|
- Notable game behavior: If you make an incorrect match, the cards will flip back over after 1 second. However, if you click on a card while the incorrect match is still face up, nothing will happen.
|
||||||
|
- Just wait until the cards flip back over.
|
||||||
|
|
||||||
|
Future plans:
|
||||||
|
- Change the game behavior to a queue of cards to flip over, 1 pair at a time.
|
||||||
|
- Expand the game to include a stopwatch.
|
||||||
|
- Expand score to include number of correct and incorrect matches.
|
||||||
|
|
58
audio.py
Normal file
58
audio.py
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
from pygame import mixer
|
||||||
|
|
||||||
|
|
||||||
|
class Audio:
|
||||||
|
background_music = 0
|
||||||
|
click_sound = 0
|
||||||
|
match_made_sound = 0
|
||||||
|
no_match_sound = 0
|
||||||
|
game_success_sound = 0
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def start_audio():
|
||||||
|
mixer.init()
|
||||||
|
|
||||||
|
# Background music is Fisticuffs in Frederick Street, by Christophe Saunière
|
||||||
|
mixer.music.load('audio/background.wav')
|
||||||
|
|
||||||
|
# Click sound... I don't remember where I got it. Sorry for no link.
|
||||||
|
Audio.click_sound = mixer.Sound('audio/mouse_click.wav')
|
||||||
|
|
||||||
|
# Match made sound
|
||||||
|
# https://pixabay.com/?utm_source=link-attribution&utm_medium=referral&utm_campaign=music&utm_content=43637
|
||||||
|
Audio.match_made_sound = mixer.Sound('audio/match_made.wav')
|
||||||
|
|
||||||
|
# no_match sound is from https://freesound.org/people/distillerystudio/sounds/327738/
|
||||||
|
Audio.no_match_sound = mixer.Sound('audio/no_match.wav')
|
||||||
|
|
||||||
|
# game_success_sound is TMNT turtle Michaelangelo saying "Cowabunga!"
|
||||||
|
Audio.game_success_sound = mixer.Sound('audio/game_success.wav')
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def play_background_music():
|
||||||
|
mixer.music.play(-1)
|
||||||
|
mixer.music.set_volume(0.5)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def pause_music():
|
||||||
|
mixer.music.pause()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def unpause_background_music():
|
||||||
|
mixer.music.unpause()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def click():
|
||||||
|
Audio.click_sound.play()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def match_made():
|
||||||
|
Audio.match_made_sound.play()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def no_match():
|
||||||
|
Audio.no_match_sound.play()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def game_success():
|
||||||
|
Audio.game_success_sound.play()
|
BIN
audio/background.wav
Normal file
BIN
audio/background.wav
Normal file
Binary file not shown.
BIN
audio/game_success.wav
Normal file
BIN
audio/game_success.wav
Normal file
Binary file not shown.
BIN
audio/match_made.wav
Normal file
BIN
audio/match_made.wav
Normal file
Binary file not shown.
BIN
audio/mouse_click.wav
Normal file
BIN
audio/mouse_click.wav
Normal file
Binary file not shown.
BIN
audio/no_match.wav
Normal file
BIN
audio/no_match.wav
Normal file
Binary file not shown.
30
card.py
30
card.py
|
@ -3,30 +3,46 @@ from tkinter import PhotoImage
|
||||||
|
|
||||||
|
|
||||||
class Card(turtle.Turtle):
|
class Card(turtle.Turtle):
|
||||||
|
card_count = 0
|
||||||
|
|
||||||
def __init__(self, image_path):
|
def __init__(self, image_path):
|
||||||
"""
|
"""
|
||||||
Initializes Card object.
|
Initializes Card object.
|
||||||
:param image_path: Path to image
|
:param image_path: Path to image for card_front
|
||||||
"""
|
"""
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
# self.size = 150 # desired image height and width (in pixels)
|
self.image_path = image_path
|
||||||
|
self.card_id = Card.card_count
|
||||||
|
Card.card_count += 1
|
||||||
self.penup()
|
self.penup()
|
||||||
self.speed(8)
|
self.speed(8)
|
||||||
self.smaller_back = PhotoImage(file='images/turtle.png').subsample(4, 4)
|
self.back = PhotoImage(file='images/turtle.png').subsample(4, 4)
|
||||||
turtle.addshape('card_back', turtle.Shape('image', self.smaller_back))
|
turtle.addshape('card_back', turtle.Shape('image', self.back))
|
||||||
self.smaller_front = PhotoImage(file=image_path).subsample(4, 4)
|
self.front = PhotoImage(file=image_path).subsample(4, 4)
|
||||||
turtle.addshape('card_front', turtle.Shape('image', self.smaller_front))
|
turtle.addshape(f'card_front{self.card_id}', turtle.Shape('image', self.front))
|
||||||
self.shape('card_back')
|
self.shape('card_back')
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
"""
|
||||||
|
Checks if two cards have the same front image.
|
||||||
|
:param other: Another Card object
|
||||||
|
:return: True if cards have the same front image, False otherwise.
|
||||||
|
"""
|
||||||
|
if type(other) is not Card:
|
||||||
|
raise TypeError('Can only compare Card objects to other Card objects.')
|
||||||
|
else:
|
||||||
|
return self.image_path == other.image_path
|
||||||
|
|
||||||
def to_front(self):
|
def to_front(self):
|
||||||
self.shape('card_front')
|
self.shape(f'card_front{self.card_id}')
|
||||||
|
|
||||||
def to_back(self):
|
def to_back(self):
|
||||||
self.shape('card_back')
|
self.shape('card_back')
|
||||||
|
|
||||||
def is_mouse_over(self, x, y):
|
def is_mouse_over(self, x, y):
|
||||||
# Collision code reused from D. Atkinson's Turtle Crossing program, with some minor modifications.
|
# Collision code reused from D. Atkinson's Turtle Crossing program, with some minor modifications.
|
||||||
|
# http://tiny.cc/ShortCodeLink
|
||||||
top_edge = self.ycor() + 103
|
top_edge = self.ycor() + 103
|
||||||
bottom_edge = self.ycor() - 103
|
bottom_edge = self.ycor() - 103
|
||||||
car_left_edge = self.xcor() - 103
|
car_left_edge = self.xcor() - 103
|
||||||
|
|
80
main.py
80
main.py
|
@ -1,3 +1,7 @@
|
||||||
|
from tkinter import PhotoImage
|
||||||
|
|
||||||
|
from audio import Audio
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import os
|
import os
|
||||||
import random
|
import random
|
||||||
|
@ -14,6 +18,24 @@ try:
|
||||||
screen = turtle.Screen()
|
screen = turtle.Screen()
|
||||||
turtle.bgcolor('#46a38d')
|
turtle.bgcolor('#46a38d')
|
||||||
screen.setup(WIDTH, HEIGHT)
|
screen.setup(WIDTH, HEIGHT)
|
||||||
|
screen.title('Turtle Match - Benjamin Zimmerman')
|
||||||
|
|
||||||
|
success_t = turtle.Turtle()
|
||||||
|
success_t.hideturtle()
|
||||||
|
success_t.penup()
|
||||||
|
turtle.register_shape('success_turtle', turtle.Shape('image', PhotoImage(file='images/turtle.png')))
|
||||||
|
success_t.shape('success_turtle')
|
||||||
|
success_t.goto(-379, 0)
|
||||||
|
|
||||||
|
Audio.start_audio()
|
||||||
|
Audio.play_background_music()
|
||||||
|
|
||||||
|
# Scoreboard
|
||||||
|
score_text = turtle.Turtle()
|
||||||
|
score_text.hideturtle()
|
||||||
|
score_text.penup()
|
||||||
|
score_text.goto((WIDTH - HEIGHT) // 2, HEIGHT * .25)
|
||||||
|
score_text.write('Score: 0', font=('Arial', 24, 'bold'))
|
||||||
|
|
||||||
|
|
||||||
def coord_translation(x, y):
|
def coord_translation(x, y):
|
||||||
|
@ -26,13 +48,17 @@ try:
|
||||||
return x - (WIDTH / 2), y - (HEIGHT / 2)
|
return x - (WIDTH / 2), y - (HEIGHT / 2)
|
||||||
|
|
||||||
|
|
||||||
# Creates list of images, doubles it, and shuffles it
|
|
||||||
image_files = os.listdir('images')
|
image_files = os.listdir('images')
|
||||||
|
|
||||||
|
# Creates list of images, doubles it, and shuffles it
|
||||||
image_files.remove('turtle.png')
|
image_files.remove('turtle.png')
|
||||||
image_files.extend(image_files)
|
image_files.extend(image_files)
|
||||||
random.shuffle(image_files)
|
random.shuffle(image_files)
|
||||||
|
|
||||||
cards = [Card(f'images/{file}') for file in image_files]
|
cards = []
|
||||||
|
for file in image_files:
|
||||||
|
path = f'images/{file}'
|
||||||
|
cards.append(Card(path))
|
||||||
|
|
||||||
# Move sprites
|
# Move sprites
|
||||||
for i in range(16):
|
for i in range(16):
|
||||||
|
@ -42,36 +68,60 @@ try:
|
||||||
x, y = coord_translation((210 * (i % 4)) + 105, (210 * int(i / 4)) + 105)
|
x, y = coord_translation((210 * (i % 4)) + 105, (210 * int(i / 4)) + 105)
|
||||||
cards[i].goto(x, y)
|
cards[i].goto(x, y)
|
||||||
|
|
||||||
|
score = 0
|
||||||
|
matches = 0
|
||||||
|
game_is_running = True
|
||||||
|
end_routine_done = False
|
||||||
|
clicked_cards = []
|
||||||
|
|
||||||
|
|
||||||
def clicked_card(x, y):
|
def clicked_card(x, y):
|
||||||
"""
|
"""
|
||||||
:return: The card which was clicked
|
Appends the card which was clicked on to the list clicked_cards.
|
||||||
"""
|
"""
|
||||||
|
global clicked_cards
|
||||||
|
if len(clicked_cards) == 2:
|
||||||
|
return None
|
||||||
for card in cards:
|
for card in cards:
|
||||||
if card.is_mouse_over(x, y):
|
if card.is_mouse_over(x, y) and card.shape()[:10] != 'card_front':
|
||||||
print(cards.index(card))
|
|
||||||
card.to_front()
|
card.to_front()
|
||||||
clicked_cards.append(card)
|
clicked_cards.append(card)
|
||||||
|
Audio.click()
|
||||||
|
|
||||||
|
|
||||||
|
def update_score():
|
||||||
|
score_text.clear()
|
||||||
|
score_text.write(f'Score: {score}', font=('Arial', 24, 'bold'))
|
||||||
|
|
||||||
|
|
||||||
screen.onclick(fun=clicked_card)
|
screen.onclick(fun=clicked_card)
|
||||||
|
|
||||||
game_is_running = True
|
|
||||||
clicked_cards = []
|
|
||||||
score = 0
|
|
||||||
|
|
||||||
while game_is_running:
|
while game_is_running:
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
|
if not end_routine_done:
|
||||||
|
if matches >= 8:
|
||||||
|
success_t.showturtle()
|
||||||
|
Audio.pause_music()
|
||||||
|
Audio.game_success()
|
||||||
|
time.sleep(3.1)
|
||||||
|
Audio.unpause_background_music()
|
||||||
|
end_routine_done = True
|
||||||
if len(clicked_cards) == 2:
|
if len(clicked_cards) == 2:
|
||||||
if clicked_cards[0].shape() != clicked_cards[1].shape():
|
if clicked_cards[0] == clicked_cards[1]:
|
||||||
time.sleep(2)
|
time.sleep(0.5)
|
||||||
|
clicked_cards[0].hideturtle()
|
||||||
|
clicked_cards[1].hideturtle()
|
||||||
|
score += 5
|
||||||
|
update_score()
|
||||||
|
Audio.match_made()
|
||||||
|
matches += 1
|
||||||
|
else:
|
||||||
|
time.sleep(1)
|
||||||
clicked_cards[0].to_back()
|
clicked_cards[0].to_back()
|
||||||
clicked_cards[1].to_back()
|
clicked_cards[1].to_back()
|
||||||
print('Wrong!')
|
|
||||||
score -= 1
|
score -= 1
|
||||||
else:
|
update_score()
|
||||||
print('Correct!')
|
Audio.no_match()
|
||||||
score += 5
|
|
||||||
clicked_cards = []
|
clicked_cards = []
|
||||||
screen.update()
|
screen.update()
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue