Skip to content

Commit 0926c4f

Browse files
authored
main.py
0 parents  commit 0926c4f

File tree

1 file changed

+377
-0
lines changed

1 file changed

+377
-0
lines changed

main.py

Lines changed: 377 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,377 @@
1+
import pygame
2+
import random
3+
import math
4+
5+
pygame.init()
6+
7+
8+
# The `DrawInformation` class is used for storing and managing information related to drawing elements
9+
# on a window.
10+
class DrawInformation:
11+
BLACK = 0, 0, 0
12+
WHITE = 255, 255, 255
13+
GREEN = 0, 128, 0
14+
RED = 255, 0, 0
15+
BACKGROUND_COLOR = WHITE
16+
17+
GRADIENTS = [
18+
(0, 0, 255),
19+
(0, 0, 230),
20+
(0, 0, 205),
21+
]
22+
23+
SMALL_FONT = pygame.font.SysFont("timesnewroman", 15)
24+
FONT = pygame.font.SysFont("timesnewroman", 20)
25+
LARGE_FONT = pygame.font.SysFont("timesnewroman", 30)
26+
27+
SIDE_PAD = 100
28+
TOP_PAD = 150
29+
30+
def __init__(self, width, height, lst):
31+
self.width = width
32+
self.height = height
33+
34+
self.window = pygame.display.set_mode((width, height))
35+
pygame.display.set_caption("SortingWiz")
36+
self.set_list(lst)
37+
38+
def set_list(self, lst):
39+
self.lst = lst
40+
self.min_val = min(lst)
41+
self.max_val = max(lst)
42+
43+
self.block_width = round((self.width - self.SIDE_PAD) / len(lst))
44+
self.block_height = math.floor(
45+
(self.height - self.TOP_PAD) / (self.max_val - self.min_val)
46+
)
47+
self.start_x = self.SIDE_PAD // 2
48+
49+
50+
def draw(draw_info, algo_name, ascending):
51+
"""
52+
The `draw` function is responsible for rendering the sorting visualization on the screen, including
53+
the title, controls, sorting options, the list being sorted, and additional information.
54+
55+
:param draw_info: The `draw_info` parameter is an object that contains information about the drawing
56+
window and other drawing-related properties. It likely includes attributes such as `window` (the
57+
drawing window), `BACKGROUND_COLOR` (the background color of the window), `LARGE_FONT` (a large font
58+
for titles),
59+
:param algo_name: The `algo_name` parameter is a string that represents the name of the sorting
60+
algorithm being used. It will be displayed in the title of the window
61+
:param ascending: The `ascending` parameter is a boolean value that determines whether the sorting
62+
algorithm should sort the data in ascending order (`True`) or descending order (`False`)
63+
:param time_complexity: The time complexity of the current algorithm.
64+
:param space_complexity: The space complexity of the current algorithm.
65+
"""
66+
67+
draw_info.window.fill(draw_info.BACKGROUND_COLOR)
68+
69+
title_text = f"{algo_name} - {'Ascending' if ascending else 'Descending'}"
70+
title_font = draw_info.LARGE_FONT
71+
title_surface = title_font.render(title_text, 1, draw_info.GREEN)
72+
73+
# Calculate the position with padding
74+
title_x = draw_info.width / 2 - title_surface.get_width() / 2
75+
title_y = -30 # Padding before the title
76+
title_y += title_surface.get_height() + 0 # Padding after the title
77+
78+
draw_info.window.blit(title_surface, (title_x, title_y))
79+
80+
controls_text = "Q - Quit | R - Reset | SPACE - Start/Resume/Stop | A - Ascending | D - Descending"
81+
controls_font = draw_info.FONT
82+
controls_surface = controls_font.render(controls_text, 1, draw_info.BLACK)
83+
controls_x = draw_info.width / 2 - controls_surface.get_width() / 2
84+
controls_y = title_y + title_surface.get_height()
85+
draw_info.window.blit(controls_surface, (controls_x, controls_y))
86+
87+
sorting_text = (
88+
"I - Insertion Sort | B - Bubble Sort | S - Selection Sort | H - Heap Sort"
89+
)
90+
sorting_surface = draw_info.FONT.render(sorting_text, 1, draw_info.BLACK)
91+
sorting_x = draw_info.width / 2 - sorting_surface.get_width() / 2
92+
sorting_y = controls_y + controls_surface.get_height()
93+
draw_info.window.blit(sorting_surface, (sorting_x, sorting_y))
94+
95+
# Display time and space complexity based on the selected algorithm
96+
if algo_name == "Bubble Sort":
97+
time_complexity = "O(n^2)"
98+
space_complexity = "O(1)"
99+
elif algo_name == "Insertion Sort":
100+
time_complexity = "O(n^2)"
101+
space_complexity = "O(1)"
102+
elif algo_name == "Selection Sort":
103+
time_complexity = "O(n^2)"
104+
space_complexity = "O(1)"
105+
elif algo_name == "Heap Sort":
106+
time_complexity = "O(n log n)"
107+
space_complexity = "O(1)"
108+
else:
109+
time_complexity = "Not specified"
110+
space_complexity = "Not specified"
111+
112+
complexity_text = (
113+
f"Time Complexity: {time_complexity} | Space Complexity: {space_complexity}"
114+
)
115+
complexity_surface = draw_info.SMALL_FONT.render(
116+
complexity_text, 1, draw_info.BLACK
117+
)
118+
complexity_x = draw_info.width / 2 - complexity_surface.get_width() / 2
119+
complexity_y = sorting_y + sorting_surface.get_height() + 10 # Padding on top
120+
draw_info.window.blit(complexity_surface, (complexity_x, complexity_y))
121+
122+
draw_list(draw_info)
123+
124+
# Additional information
125+
additional_info_text = "[CS50 Fall 2023 Final Project - SortingWiz | Made by Arjun Vijay Prakash (@ArjunCodess)]"
126+
additional_info_surface = draw_info.SMALL_FONT.render(
127+
additional_info_text, 1, draw_info.BLACK
128+
)
129+
additional_info_x = draw_info.width / 2 - additional_info_surface.get_width() / 2
130+
additional_info_y = (
131+
complexity_y + complexity_surface.get_height() + 5
132+
) # Padding on bottom
133+
draw_info.window.blit(
134+
additional_info_surface, (additional_info_x, additional_info_y)
135+
)
136+
137+
pygame.display.update()
138+
139+
140+
def draw_list(draw_info, color_positions={}, clear_bg=False):
141+
"""
142+
The function `draw_list` takes in a list of values and draws rectangles on a window based on the
143+
values in the list.
144+
145+
:param draw_info: The `draw_info` parameter is an object that contains information needed to draw
146+
the list. It likely has the following attributes:
147+
:param color_positions: The `color_positions` parameter is a dictionary that specifies the positions
148+
in the list where you want to change the color of the blocks. The keys of the dictionary represent
149+
the positions in the list, and the values represent the color you want to assign to those positions
150+
:param clear_bg: The `clear_bg` parameter is a boolean value that determines whether the background
151+
of the drawing window should be cleared before drawing the list. If `clear_bg` is `True`, a
152+
rectangle covering the entire drawing window will be filled with the background color specified in
153+
`draw_info.BACKGROUND_COLOR`. If, defaults to False (optional)
154+
"""
155+
lst = draw_info.lst
156+
157+
if clear_bg:
158+
clear_rect = (
159+
draw_info.SIDE_PAD // 2,
160+
draw_info.TOP_PAD,
161+
draw_info.width - draw_info.SIDE_PAD,
162+
draw_info.height - draw_info.TOP_PAD,
163+
)
164+
pygame.draw.rect(draw_info.window, draw_info.BACKGROUND_COLOR, clear_rect)
165+
166+
for i, val in enumerate(lst):
167+
x = draw_info.start_x + i * draw_info.block_width
168+
y = draw_info.height - (val - draw_info.min_val) * draw_info.block_height
169+
170+
color = draw_info.GRADIENTS[i % 3]
171+
172+
if i in color_positions:
173+
color = color_positions[i]
174+
175+
pygame.draw.rect(
176+
draw_info.window, color, (x, y, draw_info.block_width, draw_info.height)
177+
)
178+
179+
if clear_bg:
180+
pygame.display.update()
181+
182+
183+
def generate_starting_list(n, min_val, max_val):
184+
"""
185+
The function generates a list of random integers within a given range.
186+
187+
:param n: The parameter "n" represents the number of elements you want in the list
188+
:param min_val: The minimum value that can be generated in the list
189+
:param max_val: The maximum value that can be generated in the list
190+
:return: a list of n random integers between min_val and max_val.
191+
"""
192+
lst = []
193+
194+
for _ in range(n):
195+
val = random.randint(min_val, max_val)
196+
lst.append(val)
197+
198+
return lst
199+
200+
201+
def bubble_sort(draw_info, ascending=True):
202+
lst = draw_info.lst
203+
204+
for i in range(len(lst) - 1):
205+
for j in range(len(lst) - 1 - i):
206+
num1 = lst[j]
207+
num2 = lst[j + 1]
208+
209+
if (num1 > num2 and ascending) or (num1 < num2 and not ascending):
210+
lst[j], lst[j + 1] = lst[j + 1], lst[j]
211+
draw_list(draw_info, {j: draw_info.GREEN, j + 1: draw_info.RED}, True)
212+
yield True
213+
214+
return lst
215+
216+
217+
def insertion_sort(draw_info, ascending=True):
218+
lst = draw_info.lst
219+
220+
for i in range(1, len(lst)):
221+
current = lst[i]
222+
223+
while True:
224+
ascending_sort = i > 0 and lst[i - 1] > current and ascending
225+
descending_sort = i > 0 and lst[i - 1] < current and not ascending
226+
227+
if not ascending_sort and not descending_sort:
228+
break
229+
230+
lst[i] = lst[i - 1]
231+
i = i - 1
232+
lst[i] = current
233+
draw_list(draw_info, {i - 1: draw_info.GREEN, i: draw_info.RED}, True)
234+
yield True
235+
236+
return lst
237+
238+
239+
def selection_sort(draw_info, ascending=True):
240+
lst = draw_info.lst
241+
242+
for i in range(len(lst)):
243+
min_idx = i
244+
for j in range(i + 1, len(lst)):
245+
if (lst[j] < lst[min_idx] and ascending) or (
246+
lst[j] > lst[min_idx] and not ascending
247+
):
248+
min_idx = j
249+
lst[i], lst[min_idx] = lst[min_idx], lst[i]
250+
draw_list(draw_info, {i: draw_info.GREEN, min_idx: draw_info.RED}, True)
251+
yield True
252+
253+
return lst
254+
255+
256+
def heapify(draw_info, lst, n, i, ascending):
257+
largest = i
258+
left = 2 * i + 1
259+
right = 2 * i + 2
260+
261+
if left < n and (
262+
(lst[left] > lst[largest] and ascending)
263+
or (lst[left] < lst[largest] and not ascending)
264+
):
265+
largest = left
266+
267+
if right < n and (
268+
(lst[right] > lst[largest] and ascending)
269+
or (lst[right] < lst[largest] and not ascending)
270+
):
271+
largest = right
272+
273+
if largest != i:
274+
lst[i], lst[largest] = lst[largest], lst[i]
275+
draw_list(draw_info, {i: draw_info.GREEN, largest: draw_info.RED}, True)
276+
yield True
277+
278+
yield from heapify(draw_info, lst, n, largest, ascending)
279+
280+
281+
def heap_sort(draw_info, ascending=True):
282+
lst = draw_info.lst
283+
n = len(lst)
284+
285+
# Build a max heap
286+
for i in range(n // 2 - 1, -1, -1):
287+
yield from heapify(draw_info, lst, n, i, ascending)
288+
289+
# Extract elements one by one
290+
for i in range(n - 1, 0, -1):
291+
lst[i], lst[0] = lst[0], lst[i]
292+
draw_list(draw_info, {i: draw_info.GREEN, 0: draw_info.RED}, True)
293+
yield True
294+
295+
yield from heapify(draw_info, lst, i, 0, ascending)
296+
297+
return lst
298+
299+
300+
def main():
301+
"""
302+
The main function controls the sorting visualization program, allowing the user to select different
303+
sorting algorithms and sort the list in ascending or descending order.
304+
"""
305+
run = True
306+
clock = pygame.time.Clock()
307+
308+
n = 100
309+
min_val = 0
310+
max_val = 200
311+
312+
lst = generate_starting_list(n, min_val, max_val)
313+
draw_info = DrawInformation(1200, 1000, lst)
314+
sorting = False
315+
ascending = True
316+
step_by_step = False
317+
sorting_paused = False
318+
319+
sorting_algorithm = bubble_sort
320+
sorting_algo_name = "Bubble Sort"
321+
sorting_algorithm_generator = None
322+
323+
while run:
324+
clock.tick(60)
325+
326+
if sorting and not sorting_paused:
327+
try:
328+
next(sorting_algorithm_generator)
329+
except StopIteration:
330+
sorting = False
331+
else:
332+
draw(draw_info, sorting_algo_name, ascending)
333+
334+
for event in pygame.event.get():
335+
if event.type == pygame.QUIT:
336+
run = False
337+
338+
if event.type == pygame.KEYDOWN:
339+
if event.key == pygame.K_r:
340+
lst = generate_starting_list(n, min_val, max_val)
341+
draw_info.set_list(lst)
342+
sorting = False
343+
elif event.key == pygame.K_SPACE and not sorting:
344+
sorting = True
345+
sorting_algorithm_generator = sorting_algorithm(
346+
draw_info, ascending
347+
)
348+
elif event.key == pygame.K_RETURN and not sorting:
349+
sorting_paused = False
350+
elif event.key == pygame.K_a and not sorting:
351+
ascending = True
352+
elif event.key == pygame.K_d and not sorting:
353+
ascending = False
354+
elif event.key == pygame.K_i and not sorting:
355+
sorting_algorithm = insertion_sort
356+
sorting_algo_name = "Insertion Sort"
357+
elif event.key == pygame.K_b and not sorting:
358+
sorting_algorithm = bubble_sort
359+
sorting_algo_name = "Bubble Sort"
360+
elif event.key == pygame.K_h and not sorting:
361+
sorting_algorithm = heap_sort
362+
sorting_algo_name = "Heap Sort"
363+
elif event.key == pygame.K_s and not sorting:
364+
sorting_algorithm = selection_sort
365+
sorting_algo_name = "Selection Sort"
366+
elif event.key == pygame.K_q:
367+
run = False
368+
elif event.key == pygame.K_SPACE and sorting and not sorting_paused:
369+
sorting_paused = True
370+
elif event.key == pygame.K_SPACE and sorting and sorting_paused:
371+
sorting_paused = False
372+
373+
pygame.quit()
374+
375+
376+
if __name__ == "__main__":
377+
main()

0 commit comments

Comments
 (0)