Donkey.bas in Python in 170 lines
Introduction
To be honest, I've wanted to write some kind of game for a long time and I even had experience writing them in this language, but they were all console games 🙁
But a few days ago I had a great idea: to write a remake of some old game in a minimum number of lines of code and using only standard Python libraries, namely: Tkinter, Time, Random And Winsound.
Yes, yes, none. Pygame'a. Ever since my school Computer Science lessons I didn't like easy ways to write programs in Pascal 🙂
I chose a toy for writing Donkeywritten back in 1981 for IBM PC DOS Bill Gates himself.
Something like this)
How was everything written?
First, I create a window for the game. Here I set the icon and title of the window, and also calculate the size and position so that the window appears in the center of the screen.
I fix the window size so that it cannot be changed, preparing the environment for further game development:
import tkinter as tk
import time
from random import randint
import winsound
# Create the main application window
window = tk.Tk()
# Setting the window icon
window.iconbitmap('resources\icon.ico')
# Setting the window title
window.title('DonkeyPy 1.0')
# Get the width and height of the screen
screen_width = window.winfo_screenwidth()
screen_height = window.winfo_screenheight()
# Set the width and height of the window
window_width = 718
window_height = 418
# Calculate coordinates for placing the window in the centre of the screen
x = (screen_width // 2) - (window_width // 2)
y = (screen_height // 2) - (window_height // 2)
# Apply window size and position
window.geometry(f'{window_width}x{window_height}+{x}+{y}')
# Prohibit window resizing
window.resizable(False, False)
I add a background image with text labels. The screen displays a message that you need to press a key to exit Escand also a donkey account is kept, which is updated using a special function.
Also, I upload an image showing when the donkey wins, and the same is done with the car:
# Set the background of the window
window.image = tk.PhotoImage(file="resources\zf.png")
bg = tk.Label(window, image=window.image)
bg.grid(row=0, column=0)
bg.config(bg='#555555')
# Escape key label
esc_lbl = tk.Label(window, text="Press Esc to exit", bg='#555555', fg='#C0C0C0', font=('Comic Sans MS', 16, 'bold'))
esc_lbl.place(x=500, y=345)
# Window closing function
def exit(event):
if event.keysym == 'Escape':
window.destroy()
window.bind('<KeyPress-Escape>', exit)
# Donkey labels
donkey_lbl = tk.Label(window, text="Donkey", bg='#555555', fg='#C0C0C0', font=('Comic Sans MS', 16, 'bold'))
donkey_lbl.place(x=26, y=40)
donkey_count = tk.Label(window, text=0, bg='#555555', fg='#C0C0C0', font=('Comic Sans MS', 16, 'bold'))
donkey_count.place(x=26, y=90)
donkey_loses = tk.Label(window, text="Donkey loses!", bg='#555555', fg='#C0C0C0', font=('Comic Sans MS', 16, 'bold'))
donkey_loses.place(x=1000, y=1000)
# Donkey scoring function
def donkey_points_count():
donkey_count['text'] = int(donkey_count['text']) + 1
donkey_wins = tk.PhotoImage(file="resources\donkey_wins.png")
donkey_wins_label = tk.Label(window)
donkey_wins_label.image = donkey_wins
donkey_wins_label['image'] = donkey_wins_label.image
donkey_wins_label.place(x=1000, y=1000)
donkey_wins_label.config(bg='#555555')
# Car labels
car_lbl = tk.Label(window, text="Driver", bg='#555555', fg='#C0C0C0', font=('Comic Sans MS', 16, 'bold'))
car_lbl.place(x=500, y=40)
car_count = tk.Label(window, text=0, bg='#555555', fg='#C0C0C0', font=('Comic Sans MS', 16, 'bold'))
car_count.place(x=500, y=90)
driver_loses = tk.Label(window, text="Driver loses!", bg='#555555', fg='#C0C0C0', font=('Comic Sans MS', 16, 'bold'))
driver_loses.place(x=1000, y=1000)
# Driver scoring function
def driver_points_count():
car_count['text'] = int(car_count['text']) + 1
driver_wins = tk.PhotoImage(file="resources\driver_wins.png")
driver_wins_label = tk.Label(window)
driver_wins_label.image = driver_wins
driver_wins_label['image'] = driver_wins_label.image
driver_wins_label.place(x=1000, y=1000)
driver_wins_label.config(bg='#555555')
I create an image of a car and add it to the window. Then I define a function that moves the car when keys are pressed. Right And Leftand I set the game restart function:
# Uploading car image
car = tk.PhotoImage(file="resources\car.png")
car_label = tk.Label(window)
car_label.image = car
car_label['image'] = car_label.image
car_y = 280
car_label.place(x=250, y=car_y)
car_label.config(bg='#555555')
car_y_initial = 280
# Car move function
def move_car(event):
if car_y == 100:
return
else:
if event.keysym == 'Right':
car_label.place(x=380)
elif event.keysym == 'Left':
car_label.place(x=250)
winsound.PlaySound('sounds\move_car.wav', 1)
window.bind('<KeyPress-Right>', move_car)
window.bind('<KeyPress-Left>', move_car)
# Game restart function
def restart_game():
global car_y, car_y_initial
car_y = car_y_initial
car_label.place(x=250)
donkey_loses.place(x=1000, y=1000)
driver_wins_label.place(x=1000, y=1000)
if car_count['text'] == 10:
car_count['text'] = int(car_count['text']) * 0
donkey_count['text'] = int(donkey_count['text']) * 0
change_road()
This code snippet loads an image of a donkey into the program using the Tkinter library, I place it on the screen and set some display options for it.
The code also contains functions for restarting the game (there are 3 of them in total) and hiding the driver's loss mark:
# Uploading donkey image
donkey = tk.PhotoImage(file="resources\donkey.png")
donkey_label = tk.Label(window)
donkey_label.image = donkey
donkey_label['image'] = donkey_label.image
donkey_x = 365
donkey_y = -40
donkey_label.place(x=donkey_x, y=donkey_y)
donkey_label.config(bg='#555555')
donkey_y_initial = -1340
# Game restart function in case of a donkey win
def restart_game_2():
global car_y, car_y_initial, donkey_y, donkey_y_initial
car_y = car_y_initial
car_label.place(x=250, y=car_y)
donkey_y = donkey_y_initial
# Label hiding function
def driver_loses_f():
driver_loses.place(x=1000, y=1000)
Then I wrote a function that checks for a collision between two objects: a car and a donkey.
The function uses the winfo_rootx() and winfo_rooty() methods to get the coordinates of objects on the screen. It then compares these coordinates to determine whether a collision has occurred:
# Function for checking image collision
def check_collision():
car_x = car_label.winfo_rootx()
car_y = car_label.winfo_rooty()
donkey_x = donkey_label.winfo_rootx()
donkey_y = donkey_label.winfo_rooty()
# Condition, if the donkey wins
if car_x >= donkey_x and car_x <= donkey_x + donkey.width() and \
car_y >= donkey_y and car_y <= donkey_y + donkey.height():
donkey_points_count()
winsound.PlaySound('sounds\image_collision.wav', 1)
if donkey_count['text'] < 10:
driver_loses.place(x=26, y=140)
restart_game_2()
window.after(2500, driver_loses_f)
else:
donkey_wins_label.place(x=498, y=225)
restart_game_3()
window.after(2500, donkey_wins_f)
This describes the function that makes the donkey move.
I check if a move is already in progress, and if not, I set the is_moving flag to True. Then I increment the coordinate. Y donkey at 50 and check its collision with the car:
is_moving = False
# Donkey move function
def move_donkey():
global is_moving, donkey_y, car_y
# If the function is already in progress, exit
if is_moving:
return
# Set the flag that the function is running
is_moving = True
donkey_y += 50
donkey_label.place(y=donkey_y)
# Checking for collision
window.after(1000, check_collision)
# Condition, if the donkey reaches a certain y-coordinate
if donkey_y == 360:
donkey_x = 365 if randint(1, 2) == 1 else 230
donkey_y = -40
donkey_label.place(x=donkey_x, y=donkey_y)
car_y -= 20
car_label.place(y=car_y)
# Condition, if the driver wins
if car_y == 100:
driver_points_count()
donkey_x = 1000
donkey_label.place(x=donkey_x)
if car_count['text'] < 10:
donkey_loses.place(x=500, y=140)
window.after(2500, move_donkey)
window.after(2500, restart_game)
else:
driver_wins_label.place(x=498, y=225)
window.after(2500, move_donkey)
window.after(2500, restart_game)
else:
window.after(110, move_donkey)
else:
window.after(110, move_donkey)
# Reset flag on function termination
is_moving = False
In this code snippet I create an object that will represent the road:
# Create one Label for the road
road_label = tk.Label(window)
road_label.place(x=308, y=5)
Then I define a function that is responsible for animating the road markings. The function changes the image of the road_label using different image files and updates it every 10 milliseconds:
# The function responsible for animating road markings
def change_road():
if car_y == 100:
return
else:
current_time = (int(time.time() * 20) % 3) + 1
road = tk.PhotoImage(file="resources\doroga_{}.png".format(current_time))
road_label.image = road
road_label['image'] = road_label.image
road_label.config(bg='#555555')
window.after(10, change_road)
Final code:
import tkinter as tk
import time
from random import randint
import winsound
window = tk.Tk()
window.iconbitmap('resources\icon.ico')
window.title('DonkeyPy 1.0')
screen_width = window.winfo_screenwidth()
screen_height = window.winfo_screenheight()
window_width = 718
window_height = 418
x = (screen_width // 2) - (window_width // 2)
y = (screen_height // 2) - (window_height // 2)
window.geometry(f'{window_width}x{window_height}+{x}+{y}')
window.resizable(False, False)
window.image = tk.PhotoImage(file="resources\zf.png")
bg = tk.Label(window, image=window.image)
bg.grid(row=0, column=0)
bg.config(bg='#555555')
esc_lbl = tk.Label(window, text="Press Esc to exit", bg='#555555', fg='#C0C0C0', font=('Comic Sans MS', 16, 'bold'))
esc_lbl.place(x=500, y=345)
def exit(event):
if event.keysym == 'Escape':
window.destroy()
window.bind('<KeyPress-Escape>', exit)
donkey_lbl = tk.Label(window, text="Donkey", bg='#555555', fg='#C0C0C0', font=('Comic Sans MS', 16, 'bold'))
donkey_lbl.place(x=26, y=40)
donkey_count = tk.Label(window, text=0, bg='#555555', fg='#C0C0C0', font=('Comic Sans MS', 16, 'bold'))
donkey_count.place(x=26, y=90)
donkey_loses = tk.Label(window, text="Donkey loses!", bg='#555555', fg='#C0C0C0', font=('Comic Sans MS', 16, 'bold'))
donkey_loses.place(x=1000, y=1000)
def donkey_points_count():
donkey_count['text'] = int(donkey_count['text']) + 1
donkey_wins = tk.PhotoImage(file="resources\donkey_wins.png")
donkey_wins_label = tk.Label(window)
donkey_wins_label.image = donkey_wins
donkey_wins_label['image'] = donkey_wins_label.image
donkey_wins_label.place(x=1000, y=1000)
donkey_wins_label.config(bg='#555555')
car_lbl = tk.Label(window, text="Driver", bg='#555555', fg='#C0C0C0', font=('Comic Sans MS', 16, 'bold'))
car_lbl.place(x=500, y=40)
car_count = tk.Label(window, text=0, bg='#555555', fg='#C0C0C0', font=('Comic Sans MS', 16, 'bold'))
car_count.place(x=500, y=90)
driver_loses = tk.Label(window, text="Driver loses!", bg='#555555', fg='#C0C0C0', font=('Comic Sans MS', 16, 'bold'))
driver_loses.place(x=1000, y=1000)
def driver_points_count():
car_count['text'] = int(car_count['text']) + 1
driver_wins = tk.PhotoImage(file="resources\driver_wins.png")
driver_wins_label = tk.Label(window)
driver_wins_label.image = driver_wins
driver_wins_label['image'] = driver_wins_label.image
driver_wins_label.place(x=1000, y=1000)
driver_wins_label.config(bg='#555555')
car = tk.PhotoImage(file="resources\car.png")
car_label = tk.Label(window)
car_label.image = car
car_label['image'] = car_label.image
car_y = 280
car_label.place(x=250, y=car_y)
car_label.config(bg='#555555')
car_y_initial = 280
def move_car(event):
if car_y == 100:
return
else:
if event.keysym == 'Right':
car_label.place(x=380)
elif event.keysym == 'Left':
car_label.place(x=250)
winsound.PlaySound('sounds\move_car.wav', 1)
window.bind('<KeyPress-Right>', move_car)
window.bind('<KeyPress-Left>', move_car)
def restart_game():
global car_y, car_y_initial
car_y = car_y_initial
car_label.place(x=250)
donkey_loses.place(x=1000, y=1000)
driver_wins_label.place(x=1000, y=1000)
if car_count['text'] == 10:
car_count['text'] = int(car_count['text']) * 0
donkey_count['text'] = int(donkey_count['text']) * 0
change_road()
donkey = tk.PhotoImage(file="resources\donkey.png")
donkey_label = tk.Label(window)
donkey_label.image = donkey
donkey_label['image'] = donkey_label.image
donkey_x = 365
donkey_y = -40
donkey_label.place(x=donkey_x, y=donkey_y)
donkey_label.config(bg='#555555')
donkey_y_initial = -1340
def restart_game_2():
global car_y, car_y_initial, donkey_y, donkey_y_initial
car_y = car_y_initial
car_label.place(x=250, y=car_y)
donkey_y = donkey_y_initial
def driver_loses_f():
driver_loses.place(x=1000, y=1000)
def restart_game_3():
global car_y, car_y_initial, donkey_y, donkey_y_initial
car_y = car_y_initial
car_label.place(x=250, y=car_y)
donkey_y = donkey_y_initial
if donkey_count['text'] == 10:
donkey_count['text'] = int(donkey_count['text']) * 0
car_count['text'] = int(car_count['text']) * 0
def donkey_wins_f():
donkey_wins_label.place(x=1000, y=1000)
def check_collision():
car_x = car_label.winfo_rootx()
car_y = car_label.winfo_rooty()
donkey_x = donkey_label.winfo_rootx()
donkey_y = donkey_label.winfo_rooty()
if car_x >= donkey_x and car_x <= donkey_x + donkey.width() and \
car_y >= donkey_y and car_y <= donkey_y + donkey.height():
donkey_points_count()
winsound.PlaySound('sounds\image_collision.wav', 1)
if donkey_count['text'] < 10:
driver_loses.place(x=26, y=140)
restart_game_2()
window.after(2500, driver_loses_f)
else:
donkey_wins_label.place(x=498, y=225)
restart_game_3()
window.after(2500, donkey_wins_f)
is_moving = False
def move_donkey():
global is_moving, donkey_y, car_y
if is_moving:
return
is_moving = True
donkey_y += 50
donkey_label.place(y=donkey_y)
window.after(1000, check_collision)
if donkey_y == 360:
donkey_x = 365 if randint(1, 2) == 1 else 230
donkey_y = -40
donkey_label.place(x=donkey_x, y=donkey_y)
car_y -= 20
car_label.place(y=car_y)
if car_y == 100:
driver_points_count()
donkey_x = 1000
donkey_label.place(x=donkey_x)
if car_count['text'] < 10:
donkey_loses.place(x=500, y=140)
window.after(2500, move_donkey)
window.after(2500, restart_game)
else:
driver_wins_label.place(x=498, y=225)
window.after(2500, move_donkey)
window.after(2500, restart_game)
else:
window.after(110, move_donkey)
else:
window.after(110, move_donkey)
is_moving = False
road_label = tk.Label(window)
road_label.place(x=308, y=5)
def change_road():
if car_y == 100:
return
else:
current_time = (int(time.time() * 20) % 3) + 1
road = tk.PhotoImage(file="resources\doroga_{}.png".format(current_time))
road_label.image = road
road_label['image'] = road_label.image
road_label.config(bg='#555555')
window.after(10, change_road)
move_donkey()
change_road()
window.mainloop()
If you remove all spaces and comments, you get 172 lines.
Screenshot of the running game:
Conclusion
I have an idea for the future development of the project:
Well, I think it's worth finishing this article here. Poke around the game's source code or download the already assembled version you can on GitHub.
Yura_FX was with you. Thank you for reading this article to the end. Don't forget to share your opinion in the comments 🙂