TouseefAhmad commited on
Commit
0bf3b02
·
verified ·
1 Parent(s): a1de3c9

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +430 -271
app.py CHANGED
@@ -1,285 +1,375 @@
1
  import gradio as gr
2
  import matplotlib.pyplot as plt
3
  import numpy as np
4
- from matplotlib.animation import FuncAnimation
5
  from io import BytesIO
6
- import time
7
  import networkx as nx
8
  from collections import deque
 
9
 
10
  class DSAVisualizer:
11
  def __init__(self):
 
 
 
12
  self.current_data = []
13
  self.steps = []
14
  self.current_step = 0
15
- self.pseudocode = ""
16
  self.active_lines = []
17
- self.graph = None
18
- self.graph_type = ""
19
-
20
  def generate_data(self, data_size=10, data_type="Random"):
21
- if data_type == "Random":
22
- self.current_data = np.random.randint(1, 100, data_size)
23
- elif data_type == "Ascending":
24
- self.current_data = np.arange(1, data_size + 1)
25
- elif data_type == "Descending":
26
- self.current_data = np.arange(data_size, 0, -1)
27
- elif data_type == "Nearly Sorted":
28
- self.current_data = np.arange(1, data_size + 1)
29
- for _ in range(max(1, data_size // 10)):
30
- i, j = np.random.randint(0, data_size, 2)
31
- self.current_data[i], self.current_data[j] = self.current_data[j], self.current_data[i]
32
- return list(self.current_data)
 
 
 
 
 
 
 
 
 
33
 
34
  def bubble_sort(self, arr):
35
- n = len(arr)
36
- steps = [arr.copy()]
37
- pseudocode = [
38
- "procedure bubbleSort(A : list)",
39
- " n = length(A)",
40
- " repeat",
41
- " swapped = false",
42
- " for i from 1 to n-1:",
43
- " if A[i-1] > A[i]:",
44
- " swap(A[i-1], A[i])",
45
- " swapped = true",
46
- " until not swapped"
47
- ]
48
- active_line = [0]
49
-
50
- swapped = True
51
- while swapped:
52
- swapped = False
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
  for j in range(1, n):
54
- active_line.append(5)
55
- if steps[-1][j-1] > steps[-1][j]:
 
 
 
 
 
 
 
56
  active_line.append(6)
57
- new_step = steps[-1].copy()
58
- new_step[j-1], new_step[j] = new_step[j], new_step[j-1]
59
- steps.append(new_step)
60
- swapped = True
61
- active_line.append(7)
62
- active_line.append(4)
63
- active_line.append(3)
64
-
65
- return steps, pseudocode, active_line
 
66
 
67
  def dfs(self, graph_dict, start):
68
- graph = self.parse_graph(graph_dict)
69
- visited = set()
70
- stack = [start]
71
- steps = []
72
- pseudocode = [
73
- "procedure DFS(G, start):",
74
- " visited = set()",
75
- " stack = [start]",
76
- " while stack not empty:",
77
- " vertex = stack.pop()",
78
- " if vertex not in visited:",
79
- " visited.add(vertex)",
80
- " for neighbor in G[vertex]:",
81
- " if neighbor not in visited:",
82
- " stack.push(neighbor)"
83
- ]
84
- active_line = [0]
85
-
86
- steps.append({"visited": set(), "current": None, "stack": stack.copy(), "graph": graph.copy()})
87
- active_line.append(1)
88
-
89
- while stack:
90
- active_line.append(3)
91
- vertex = stack.pop()
92
- active_line.append(4)
93
- if vertex not in visited:
94
- active_line.append(5)
95
- visited.add(vertex)
96
- active_line.append(6)
97
- for neighbor in graph.get(vertex, []):
98
- active_line.append(7)
99
- if neighbor not in visited:
100
- stack.append(neighbor)
101
- steps.append({"visited": visited.copy(),
102
- "current": vertex,
103
- "stack": stack.copy(),
104
- "graph": graph.copy()})
105
  active_line.append(3)
106
-
107
- return steps, pseudocode, active_line
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
 
109
  def bfs(self, graph_dict, start):
110
- graph = self.parse_graph(graph_dict)
111
- visited = set()
112
- queue = deque([start])
113
- steps = []
114
- pseudocode = [
115
- "procedure BFS(G, start):",
116
- " visited = set()",
117
- " queue = deque([start])",
118
- " while queue not empty:",
119
- " vertex = queue.popleft()",
120
- " if vertex not in visited:",
121
- " visited.add(vertex)",
122
- " for neighbor in G[vertex]:",
123
- " if neighbor not in visited:",
124
- " queue.append(neighbor)"
125
- ]
126
- active_line = [0]
127
-
128
- steps.append({"visited": set(), "current": None, "queue": list(queue), "graph": graph.copy()})
129
- active_line.append(1)
130
-
131
- while queue:
132
- active_line.append(3)
133
- vertex = queue.popleft()
134
- active_line.append(4)
135
- if vertex not in visited:
136
- active_line.append(5)
137
- visited.add(vertex)
138
- active_line.append(6)
139
- for neighbor in graph.get(vertex, []):
140
- active_line.append(7)
141
- if neighbor not in visited:
142
- queue.append(neighbor)
143
- steps.append({"visited": visited.copy(),
144
- "current": vertex,
145
- "queue": list(queue),
146
- "graph": graph.copy()})
147
  active_line.append(3)
148
-
149
- return steps, pseudocode, active_line
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
150
 
151
- def visualize_sorting(self, algorithm, arr):
152
- if algorithm == "Bubble Sort":
153
- self.steps, self.pseudocode, self.active_lines = self.bubble_sort(arr)
154
- # Add other sorting algorithms here
155
- self.current_step = 0
156
- return self.create_plot(), self.create_pseudocode(0)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
 
158
  def visualize_graph(self, algorithm, graph_input, start):
159
- if algorithm == "DFS":
160
- self.steps, self.pseudocode, self.active_lines = self.dfs(graph_input, start)
161
- elif algorithm == "BFS":
162
- self.steps, self.pseudocode, self.active_lines = self.bfs(graph_input, start)
163
- self.current_step = 0
164
- return self.create_graph_plot(), self.create_pseudocode(0)
 
 
 
 
165
 
166
  def create_plot(self):
167
- fig, ax = plt.subplots(figsize=(10, 6))
168
- if self.steps:
169
- current_data = self.steps[self.current_step]
170
- colors = ['#1f77b4' for _ in current_data]
171
-
172
- # Highlight recently swapped elements
173
- if self.current_step > 0:
174
- prev = self.steps[self.current_step-1]
175
- for i in range(len(current_data)):
176
- if i < len(prev) and current_data[i] != prev[i]:
177
- colors[i] = '#ff7f0e'
178
-
179
- # Highlight current comparison
180
- if self.current_step < len(self.active_lines):
181
- line_num = self.active_lines[self.current_step]
182
- if line_num == 5 or line_num == 6:
183
- if self.current_step > 0:
184
- # Find the indices being compared
185
- prev = self.steps[self.current_step-1]
186
- current = self.steps[self.current_step]
187
- diff_indices = [i for i in range(min(len(prev), len(current)))
188
- if prev[i] != current[i]]
189
- if diff_indices:
190
- i = diff_indices[0]
191
- colors[i] = '#d62728'
192
- if i+1 < len(colors):
193
- colors[i+1] = '#d62728'
194
 
195
- ax.bar(range(len(current_data)), current_data, color=colors)
196
- ax.set_title(f'Sorting Visualization - Step {self.current_step}/{len(self.steps)-1}')
197
- ax.set_xlabel('Index')
198
- ax.set_ylabel('Value')
199
- ax.grid(axis='y', linestyle='--', alpha=0.7)
200
- else:
201
- ax.text(0.5, 0.5, "No data to visualize",
202
- ha='center', va='center', fontsize=16)
203
-
204
- buf = BytesIO()
205
- plt.savefig(buf, format='png', dpi=100)
206
- plt.close(fig)
207
- return buf.getvalue()
208
 
209
  def create_graph_plot(self):
210
- if not self.steps:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
211
  fig, ax = plt.subplots(figsize=(10, 8))
212
- ax.text(0.5, 0.5, "No graph data to visualize",
213
- ha='center', va='center', fontsize=16)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
214
  buf = BytesIO()
215
- plt.savefig(buf, format='png')
216
  plt.close(fig)
217
  return buf.getvalue()
 
 
 
 
 
 
 
 
218
 
219
- state = self.steps[self.current_step]
220
- graph_dict = state["graph"]
221
-
222
- G = nx.Graph()
223
- for node, neighbors in graph_dict.items():
224
- for neighbor in neighbors:
225
- G.add_edge(node, neighbor)
226
-
227
- pos = nx.spring_layout(G, seed=42)
228
- fig, ax = plt.subplots(figsize=(10, 8))
229
-
230
- node_colors = []
231
- for node in G.nodes():
232
- if node == state['current']:
233
- node_colors.append('#d62728') # Current node
234
- elif node in state['visited']:
235
- node_colors.append('#2ca02c') # Visited nodes
236
- elif 'stack' in state and node in state['stack']:
237
- node_colors.append('#ff7f0e') # Nodes in stack
238
- elif 'queue' in state and node in state['queue']:
239
- node_colors.append('#ff7f0e') # Nodes in queue
240
  else:
241
- node_colors.append('#1f77b4') # Unexplored nodes
242
-
243
- nx.draw_networkx_nodes(G, pos, node_size=800,
244
- node_color=node_colors, alpha=0.9, ax=ax)
245
- nx.draw_networkx_edges(G, pos, width=1.5, alpha=0.5, ax=ax)
246
- nx.draw_networkx_labels(G, pos, font_size=12,
247
- font_weight='bold', font_color='white', ax=ax)
248
-
249
- algorithm = "DFS" if 'stack' in state else "BFS"
250
- title = f"{algorithm} Traversal - Step {self.current_step}/{len(self.steps)-1}\n"
251
- title += f"Current: {state['current']} | "
252
- if 'stack' in state:
253
- title += f"Stack: {state['stack']}"
254
- else:
255
- title += f"Queue: {state['queue']}"
256
 
257
- ax.set_title(title, fontsize=14)
258
- ax.set_axis_off()
259
-
260
- buf = BytesIO()
261
- plt.savefig(buf, format='png', dpi=100, bbox_inches='tight')
262
- plt.close(fig)
263
- return buf.getvalue()
264
-
265
- def create_pseudocode(self, step):
266
- if not self.pseudocode or not self.active_lines:
267
  return ""
268
-
269
- # Find the active line for this step
270
- if step < len(self.active_lines):
271
- active_line = self.active_lines[step]
272
- else:
273
- active_line = self.active_lines[-1] if self.active_lines else 0
274
-
275
- html = "<div style='font-family: \"Fira Code\", monospace; background: #2d2d2d; color: #f8f8f2; padding: 15px; border-radius: 8px; line-height: 1.5;'>"
276
- for i, line in enumerate(self.pseudocode):
277
- if i == active_line:
278
- html += f"<div style='background: #44475a; padding: 8px; border-left: 3px solid #bd93f9;'><b>{line}</b></div>"
279
- else:
280
- html += f"<div style='padding: 8px;'>{line}</div>"
281
- html += "</div>"
282
- return html
283
 
284
  def next_step(self):
285
  if self.current_step < len(self.steps) - 1:
@@ -292,25 +382,39 @@ class DSAVisualizer:
292
  return self.update_display()
293
 
294
  def update_display(self):
295
- if not self.steps:
296
- return None, ""
297
-
298
- if isinstance(self.steps[0], list): # Sorting visualization
299
- plot = self.create_plot()
300
- else: # Graph visualization
301
- plot = self.create_graph_plot()
302
-
303
- return plot, self.create_pseudocode(self.current_step)
 
 
 
304
 
305
  def parse_graph(self, graph_str):
306
- graph = {}
307
- for line in graph_str.strip().split('\n'):
308
- if ':' in line:
309
- node, neighbors = line.split(':', 1)
310
- node = node.strip()
311
- neighbors = [n.strip() for n in neighbors.split(',') if n.strip()]
312
- graph[node] = neighbors
313
- return graph
 
 
 
 
 
 
 
 
 
 
 
314
 
315
  # Initialize visualizer
316
  visualizer = DSAVisualizer()
@@ -323,6 +427,8 @@ custom_css = """
323
  --accent: #ff4081;
324
  --dark: #2d2d2d;
325
  --light: #f8f8f8;
 
 
326
  }
327
 
328
  body {
@@ -355,6 +461,7 @@ h1, h2, h3 {
355
  text-transform: uppercase !important;
356
  letter-spacing: 0.5px !important;
357
  transition: all 0.3s ease !important;
 
358
  }
359
 
360
  .btn-primary:hover {
@@ -374,6 +481,25 @@ h1, h2, h3 {
374
  border-radius: 12px;
375
  padding: 20px;
376
  border: 1px solid rgba(255,255,255,0.1);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
377
  }
378
 
379
  footer {
@@ -400,6 +526,10 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="purple"),
400
  </div>
401
  """)
402
 
 
 
 
 
403
  # Main Content
404
  with gr.Tab("🧮 Sorting Algorithms"):
405
  with gr.Row():
@@ -412,12 +542,12 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="purple"),
412
  data_size = gr.Slider(5, 50, value=10, step=1, label="Data Size")
413
  gen_btn = gr.Button("♻️ Generate Data", variant="primary")
414
  algorithm = gr.Dropdown(
415
- ["Bubble Sort", "Merge Sort", "Quick Sort", "Insertion Sort"],
416
  value="Bubble Sort", label="Algorithm"
417
  )
418
  sort_btn = gr.Button("▶️ Run Algorithm", variant="primary")
419
 
420
- data_output = gr.Textbox(label="Generated Data", interactive=False)
421
 
422
  gen_btn.click(
423
  visualizer.generate_data,
@@ -438,7 +568,7 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="purple"),
438
  sort_btn.click(
439
  visualizer.visualize_sorting,
440
  inputs=[algorithm, data_output],
441
- outputs=[plot_output, pseudocode]
442
  )
443
 
444
  with gr.Tab("📊 Graph Algorithms"):
@@ -473,7 +603,7 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="purple"),
473
  graph_btn.click(
474
  visualizer.visualize_graph,
475
  inputs=[algorithm, graph_input, start_node],
476
- outputs=[graph_plot, graph_pseudocode]
477
  )
478
 
479
  with gr.Tab("📚 Complexity Analysis"):
@@ -483,6 +613,7 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="purple"),
483
  | Algorithm | Time (Best) | Time (Avg) | Time (Worst) | Space | Use Cases |
484
  |-----------------|---------------|---------------|---------------|---------------|-------------------------------|
485
  | **Bubble Sort** | O(n) | O(n²) | O(n²) | O(1) | Educational, small datasets |
 
486
  | **Merge Sort** | O(n log n) | O(n log n) | O(n log n) | O(n) | General purpose, stable |
487
  | **Quick Sort** | O(n log n) | O(n log n) | O(n²) | O(log n) | Large datasets, in-place |
488
  | **DFS** | O(V+E) | O(V+E) | O(V+E) | O(V) | Pathfinding, cycle detection |
@@ -492,7 +623,7 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="purple"),
492
  - **Sorting Algorithms**:
493
  - Use **QuickSort** for average-case performance
494
  - Use **MergeSort** for guaranteed O(n log n) performance
495
- - **BubbleSort** is educational but impractical for large data
496
 
497
  - **Graph Algorithms**:
498
  - **DFS** is better for pathfinding in deep graphs
@@ -513,11 +644,15 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="purple"),
513
  - Multiple algorithm implementations
514
  - Pseudocode with execution highlighting
515
  - Customizable input data
 
516
  - Complexity analysis reference
517
 
 
 
 
 
518
  **Future Enhancements**:
519
- - Add more algorithms (Dijkstra, A*, etc.)
520
- - Code generation for implementations
521
  - Speed control for animations
522
  - Comparison mode for algorithms
523
  - Export visualizations as GIF
@@ -526,17 +661,41 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="purple"),
526
  """)
527
 
528
  # Navigation events
529
- next_btn.click(visualizer.next_step, outputs=[plot_output, pseudocode])
530
- prev_btn.click(visualizer.prev_step, outputs=[plot_output, pseudocode])
531
- graph_next.click(visualizer.next_step, outputs=[graph_plot, graph_pseudocode])
532
- graph_prev.click(visualizer.prev_step, outputs=[graph_plot, graph_pseudocode])
 
 
 
 
 
 
 
 
 
 
 
 
533
 
534
  # Footer
535
  gr.Markdown("""
536
  <footer>
537
- <p>AlgoViz Pro v1.0 | Created with Gradio | For educational purposes</p>
538
  </footer>
539
  """)
 
 
 
 
 
 
 
 
 
 
 
 
540
 
541
  if __name__ == "__main__":
542
  demo.launch()
 
1
  import gradio as gr
2
  import matplotlib.pyplot as plt
3
  import numpy as np
 
4
  from io import BytesIO
 
5
  import networkx as nx
6
  from collections import deque
7
+ import traceback
8
 
9
  class DSAVisualizer:
10
  def __init__(self):
11
+ self.reset_state()
12
+
13
+ def reset_state(self):
14
  self.current_data = []
15
  self.steps = []
16
  self.current_step = 0
17
+ self.pseudocode = []
18
  self.active_lines = []
19
+ self.graph_dict = {}
20
+ self.error_message = ""
21
+
22
  def generate_data(self, data_size=10, data_type="Random"):
23
+ try:
24
+ if data_size < 5:
25
+ data_size = 5
26
+ elif data_size > 50:
27
+ data_size = 50
28
+
29
+ if data_type == "Random":
30
+ self.current_data = np.random.randint(1, 100, data_size)
31
+ elif data_type == "Ascending":
32
+ self.current_data = np.arange(1, data_size + 1)
33
+ elif data_type == "Descending":
34
+ self.current_data = np.arange(data_size, 0, -1)
35
+ elif data_type == "Nearly Sorted":
36
+ self.current_data = np.arange(1, data_size + 1)
37
+ for _ in range(max(1, data_size // 10)):
38
+ i, j = np.random.randint(0, data_size, 2)
39
+ self.current_data[i], self.current_data[j] = self.current_data[j], self.current_data[i]
40
+ return list(self.current_data)
41
+ except Exception as e:
42
+ self.error_message = f"Data generation error: {str(e)}"
43
+ return []
44
 
45
  def bubble_sort(self, arr):
46
+ try:
47
+ arr = [int(x) for x in arr] # Ensure integers
48
+ n = len(arr)
49
+ steps = [arr.copy()]
50
+ pseudocode = [
51
+ "procedure bubbleSort(A : list)",
52
+ " n = length(A)",
53
+ " repeat",
54
+ " swapped = false",
55
+ " for i from 1 to n-1:",
56
+ " if A[i-1] > A[i]:",
57
+ " swap(A[i-1], A[i])",
58
+ " swapped = true",
59
+ " until not swapped"
60
+ ]
61
+ active_line = [0]
62
+
63
+ swapped = True
64
+ while swapped:
65
+ swapped = False
66
+ for j in range(1, n):
67
+ active_line.append(5)
68
+ if steps[-1][j-1] > steps[-1][j]:
69
+ active_line.append(6)
70
+ new_step = steps[-1].copy()
71
+ new_step[j-1], new_step[j] = new_step[j], new_step[j-1]
72
+ steps.append(new_step)
73
+ swapped = True
74
+ active_line.append(7)
75
+ active_line.append(4)
76
+ active_line.append(3)
77
+
78
+ return steps, pseudocode, active_line
79
+ except Exception as e:
80
+ self.error_message = f"Bubble sort error: {str(e)}"
81
+ return [], [], []
82
+
83
+ def insertion_sort(self, arr):
84
+ try:
85
+ arr = [int(x) for x in arr] # Ensure integers
86
+ steps = [arr.copy()]
87
+ pseudocode = [
88
+ "procedure insertionSort(A : list)",
89
+ " for j from 1 to length(A)-1:",
90
+ " key = A[j]",
91
+ " i = j-1",
92
+ " while i >= 0 and A[i] > key:",
93
+ " A[i+1] = A[i]",
94
+ " i = i-1",
95
+ " A[i+1] = key"
96
+ ]
97
+ active_line = [0]
98
+
99
+ arr = arr.copy()
100
+ n = len(arr)
101
  for j in range(1, n):
102
+ active_line.append(1)
103
+ key = arr[j]
104
+ active_line.append(2)
105
+ i = j-1
106
+ active_line.append(3)
107
+ while i >= 0 and arr[i] > key:
108
+ active_line.append(5)
109
+ arr[i+1] = arr[i]
110
+ steps.append(arr.copy())
111
  active_line.append(6)
112
+ i = i-1
113
+ active_line.append(3)
114
+ active_line.append(7)
115
+ arr[i+1] = key
116
+ steps.append(arr.copy())
117
+
118
+ return steps, pseudocode, active_line
119
+ except Exception as e:
120
+ self.error_message = f"Insertion sort error: {str(e)}"
121
+ return [], [], []
122
 
123
  def dfs(self, graph_dict, start):
124
+ try:
125
+ graph = self.parse_graph(graph_dict)
126
+ if not graph:
127
+ return [], [], []
128
+
129
+ visited = set()
130
+ stack = [start]
131
+ steps = []
132
+ pseudocode = [
133
+ "procedure DFS(G, start):",
134
+ " visited = set()",
135
+ " stack = [start]",
136
+ " while stack not empty:",
137
+ " vertex = stack.pop()",
138
+ " if vertex not in visited:",
139
+ " visited.add(vertex)",
140
+ " for neighbor in G[vertex]:",
141
+ " if neighbor not in visited:",
142
+ " stack.push(neighbor)"
143
+ ]
144
+ active_line = [0]
145
+
146
+ steps.append({"visited": set(), "current": None, "stack": stack.copy(), "graph": graph})
147
+ active_line.append(1)
148
+
149
+ while stack:
 
 
 
 
 
 
 
 
 
 
 
150
  active_line.append(3)
151
+ vertex = stack.pop()
152
+ active_line.append(4)
153
+ if vertex not in visited:
154
+ active_line.append(5)
155
+ visited.add(vertex)
156
+ active_line.append(6)
157
+ for neighbor in graph.get(vertex, []):
158
+ active_line.append(7)
159
+ if neighbor not in visited:
160
+ stack.append(neighbor)
161
+ steps.append({"visited": visited.copy(),
162
+ "current": vertex,
163
+ "stack": stack.copy(),
164
+ "graph": graph})
165
+ active_line.append(3)
166
+
167
+ return steps, pseudocode, active_line
168
+ except Exception as e:
169
+ self.error_message = f"DFS error: {str(e)}"
170
+ return [], [], []
171
 
172
  def bfs(self, graph_dict, start):
173
+ try:
174
+ graph = self.parse_graph(graph_dict)
175
+ if not graph:
176
+ return [], [], []
177
+
178
+ visited = set()
179
+ queue = deque([start])
180
+ steps = []
181
+ pseudocode = [
182
+ "procedure BFS(G, start):",
183
+ " visited = set()",
184
+ " queue = deque([start])",
185
+ " while queue not empty:",
186
+ " vertex = queue.popleft()",
187
+ " if vertex not in visited:",
188
+ " visited.add(vertex)",
189
+ " for neighbor in G[vertex]:",
190
+ " if neighbor not in visited:",
191
+ " queue.append(neighbor)"
192
+ ]
193
+ active_line = [0]
194
+
195
+ steps.append({"visited": set(), "current": None, "queue": list(queue), "graph": graph})
196
+ active_line.append(1)
197
+
198
+ while queue:
 
 
 
 
 
 
 
 
 
 
 
199
  active_line.append(3)
200
+ vertex = queue.popleft()
201
+ active_line.append(4)
202
+ if vertex not in visited:
203
+ active_line.append(5)
204
+ visited.add(vertex)
205
+ active_line.append(6)
206
+ for neighbor in graph.get(vertex, []):
207
+ active_line.append(7)
208
+ if neighbor not in visited:
209
+ queue.append(neighbor)
210
+ steps.append({"visited": visited.copy(),
211
+ "current": vertex,
212
+ "queue": list(queue),
213
+ "graph": graph})
214
+ active_line.append(3)
215
+
216
+ return steps, pseudocode, active_line
217
+ except Exception as e:
218
+ self.error_message = f"BFS error: {str(e)}"
219
+ return [], [], []
220
 
221
+ def visualize_sorting(self, algorithm, data):
222
+ try:
223
+ # Convert data to list of integers
224
+ if isinstance(data, str):
225
+ data = [int(x.strip()) for x in data.strip('[]').split(',') if x.strip()]
226
+ elif not isinstance(data, list):
227
+ data = list(data)
228
+
229
+ if algorithm == "Bubble Sort":
230
+ self.steps, self.pseudocode, self.active_lines = self.bubble_sort(data)
231
+ elif algorithm == "Insertion Sort":
232
+ self.steps, self.pseudocode, self.active_lines = self.insertion_sort(data)
233
+ else:
234
+ self.error_message = f"Algorithm '{algorithm}' not implemented yet"
235
+ return None, self.create_pseudocode(0), self.error_message
236
+
237
+ self.current_step = 0
238
+ return self.create_plot(), self.create_pseudocode(0), ""
239
+ except Exception as e:
240
+ self.error_message = f"Visualization error: {str(e)}"
241
+ return None, "", self.error_message
242
 
243
  def visualize_graph(self, algorithm, graph_input, start):
244
+ try:
245
+ self.steps, self.pseudocode, self.active_lines = (
246
+ self.dfs(graph_input, start) if algorithm == "DFS"
247
+ else self.bfs(graph_input, start)
248
+ )
249
+ self.current_step = 0
250
+ return self.create_graph_plot(), self.create_pseudocode(0), ""
251
+ except Exception as e:
252
+ self.error_message = f"Graph visualization error: {str(e)}"
253
+ return None, "", self.error_message
254
 
255
  def create_plot(self):
256
+ try:
257
+ fig, ax = plt.subplots(figsize=(10, 6))
258
+ if self.steps and self.current_step < len(self.steps):
259
+ current_data = self.steps[self.current_step]
260
+ colors = ['#1f77b4' for _ in current_data]
261
+
262
+ # Highlight recently swapped elements
263
+ if self.current_step > 0 and self.current_step < len(self.steps):
264
+ prev = self.steps[self.current_step-1]
265
+ for i in range(len(current_data)):
266
+ if i < len(prev) and current_data[i] != prev[i]:
267
+ colors[i] = '#ff7f0e'
268
+
269
+ ax.bar(range(len(current_data)), current_data, color=colors)
270
+ ax.set_title(f'Step {self.current_step}/{len(self.steps)-1}')
271
+ ax.set_xlabel('Index')
272
+ ax.set_ylabel('Value')
273
+ ax.grid(axis='y', linestyle='--', alpha=0.7)
274
+ else:
275
+ ax.text(0.5, 0.5, "No data to visualize",
276
+ ha='center', va='center', fontsize=16)
 
 
 
 
 
 
277
 
278
+ buf = BytesIO()
279
+ plt.savefig(buf, format='png', dpi=100, bbox_inches='tight')
280
+ plt.close(fig)
281
+ return buf.getvalue()
282
+ except Exception as e:
283
+ self.error_message = f"Plot creation error: {str(e)}"
284
+ return None
 
 
 
 
 
 
285
 
286
  def create_graph_plot(self):
287
+ try:
288
+ if not self.steps or self.current_step >= len(self.steps):
289
+ fig, ax = plt.subplots(figsize=(10, 8))
290
+ ax.text(0.5, 0.5, "No graph data to visualize",
291
+ ha='center', va='center', fontsize=16)
292
+ buf = BytesIO()
293
+ plt.savefig(buf, format='png')
294
+ plt.close(fig)
295
+ return buf.getvalue()
296
+
297
+ state = self.steps[self.current_step]
298
+ graph_dict = state["graph"]
299
+
300
+ G = nx.Graph()
301
+ for node, neighbors in graph_dict.items():
302
+ for neighbor in neighbors:
303
+ if neighbor in graph_dict: # Ensure neighbor exists
304
+ G.add_edge(node, neighbor)
305
+
306
+ # Add isolated nodes
307
+ for node in graph_dict:
308
+ if node not in G:
309
+ G.add_node(node)
310
+
311
+ pos = nx.spring_layout(G, seed=42)
312
  fig, ax = plt.subplots(figsize=(10, 8))
313
+
314
+ node_colors = []
315
+ for node in G.nodes():
316
+ if node == state.get('current'):
317
+ node_colors.append('#d62728') # Current node
318
+ elif node in state.get('visited', set()):
319
+ node_colors.append('#2ca02c') # Visited nodes
320
+ elif node in state.get('stack', []) or node in state.get('queue', []):
321
+ node_colors.append('#ff7f0e') # Nodes in stack/queue
322
+ else:
323
+ node_colors.append('#1f77b4') # Unexplored nodes
324
+
325
+ nx.draw_networkx_nodes(G, pos, node_size=800,
326
+ node_color=node_colors, alpha=0.9, ax=ax)
327
+ nx.draw_networkx_edges(G, pos, width=1.5, alpha=0.5, ax=ax)
328
+ nx.draw_networkx_labels(G, pos, font_size=12,
329
+ font_weight='bold', font_color='white', ax=ax)
330
+
331
+ algorithm = "DFS" if 'stack' in state else "BFS"
332
+ title = f"{algorithm} Traversal - Step {self.current_step}/{len(self.steps)-1}\n"
333
+ title += f"Current: {state.get('current', 'None')} | "
334
+
335
+ if 'stack' in state:
336
+ title += f"Stack: {state['stack']}"
337
+ elif 'queue' in state:
338
+ title += f"Queue: {state['queue']}"
339
+
340
+ ax.set_title(title, fontsize=14)
341
+ ax.set_axis_off()
342
+
343
  buf = BytesIO()
344
+ plt.savefig(buf, format='png', dpi=100, bbox_inches='tight')
345
  plt.close(fig)
346
  return buf.getvalue()
347
+ except Exception as e:
348
+ self.error_message = f"Graph plot error: {str(e)}"
349
+ return None
350
+
351
+ def create_pseudocode(self, step):
352
+ try:
353
+ if not self.pseudocode or not self.active_lines:
354
+ return ""
355
 
356
+ # Find the active line for this step
357
+ if step < len(self.active_lines):
358
+ active_line = self.active_lines[step]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
359
  else:
360
+ active_line = self.active_lines[-1] if self.active_lines else 0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
361
 
362
+ html = "<div style='font-family: monospace; background: #2d2d2d; color: #f8f8f2; padding: 15px; border-radius: 8px; line-height: 1.5;'>"
363
+ for i, line in enumerate(self.pseudocode):
364
+ if i == active_line:
365
+ html += f"<div style='background: #44475a; padding: 8px; border-left: 3px solid #bd93f9;'><b>{line}</b></div>"
366
+ else:
367
+ html += f"<div style='padding: 8px;'>{line}</div>"
368
+ html += "</div>"
369
+ return html
370
+ except Exception as e:
371
+ self.error_message = f"Pseudocode error: {str(e)}"
372
  return ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
373
 
374
  def next_step(self):
375
  if self.current_step < len(self.steps) - 1:
 
382
  return self.update_display()
383
 
384
  def update_display(self):
385
+ try:
386
+ if not self.steps:
387
+ return None, "", "No steps available"
388
+
389
+ if isinstance(self.steps[0], list): # Sorting visualization
390
+ plot = self.create_plot()
391
+ else: # Graph visualization
392
+ plot = self.create_graph_plot()
393
+
394
+ return plot, self.create_pseudocode(self.current_step), ""
395
+ except Exception as e:
396
+ return None, "", f"Update error: {str(e)}"
397
 
398
  def parse_graph(self, graph_str):
399
+ try:
400
+ graph = {}
401
+ for line in graph_str.strip().split('\n'):
402
+ if ':' in line:
403
+ node, neighbors = line.split(':', 1)
404
+ node = node.strip()
405
+ if node not in graph:
406
+ graph[node] = []
407
+ neighbors = [n.strip() for n in neighbors.split(',') if n.strip()]
408
+ graph[node].extend(neighbors)
409
+
410
+ # Add neighbors that might not be defined yet
411
+ for neighbor in neighbors:
412
+ if neighbor not in graph:
413
+ graph[neighbor] = []
414
+ return graph
415
+ except Exception as e:
416
+ self.error_message = f"Graph parse error: {str(e)}"
417
+ return {}
418
 
419
  # Initialize visualizer
420
  visualizer = DSAVisualizer()
 
427
  --accent: #ff4081;
428
  --dark: #2d2d2d;
429
  --light: #f8f8f8;
430
+ --success: #4CAF50;
431
+ --error: #f44336;
432
  }
433
 
434
  body {
 
461
  text-transform: uppercase !important;
462
  letter-spacing: 0.5px !important;
463
  transition: all 0.3s ease !important;
464
+ color: white !important;
465
  }
466
 
467
  .btn-primary:hover {
 
481
  border-radius: 12px;
482
  padding: 20px;
483
  border: 1px solid rgba(255,255,255,0.1);
484
+ min-height: 500px;
485
+ }
486
+
487
+ .error-box {
488
+ background: var(--error) !important;
489
+ color: white !important;
490
+ padding: 10px;
491
+ border-radius: 8px;
492
+ margin-top: 10px;
493
+ font-weight: bold;
494
+ }
495
+
496
+ .success-box {
497
+ background: var(--success) !important;
498
+ color: white !important;
499
+ padding: 10px;
500
+ border-radius: 8px;
501
+ margin-top: 10px;
502
+ font-weight: bold;
503
  }
504
 
505
  footer {
 
526
  </div>
527
  """)
528
 
529
+ # Error display
530
+ error_output = gr.HTML(visible=False, elem_classes=["error-box"])
531
+ success_output = gr.HTML(visible=False, elem_classes=["success-box"])
532
+
533
  # Main Content
534
  with gr.Tab("🧮 Sorting Algorithms"):
535
  with gr.Row():
 
542
  data_size = gr.Slider(5, 50, value=10, step=1, label="Data Size")
543
  gen_btn = gr.Button("♻️ Generate Data", variant="primary")
544
  algorithm = gr.Dropdown(
545
+ ["Bubble Sort", "Insertion Sort"],
546
  value="Bubble Sort", label="Algorithm"
547
  )
548
  sort_btn = gr.Button("▶️ Run Algorithm", variant="primary")
549
 
550
+ data_output = gr.Textbox(label="Generated Data", interactive=True)
551
 
552
  gen_btn.click(
553
  visualizer.generate_data,
 
568
  sort_btn.click(
569
  visualizer.visualize_sorting,
570
  inputs=[algorithm, data_output],
571
+ outputs=[plot_output, pseudocode, error_output]
572
  )
573
 
574
  with gr.Tab("📊 Graph Algorithms"):
 
603
  graph_btn.click(
604
  visualizer.visualize_graph,
605
  inputs=[algorithm, graph_input, start_node],
606
+ outputs=[graph_plot, graph_pseudocode, error_output]
607
  )
608
 
609
  with gr.Tab("📚 Complexity Analysis"):
 
613
  | Algorithm | Time (Best) | Time (Avg) | Time (Worst) | Space | Use Cases |
614
  |-----------------|---------------|---------------|---------------|---------------|-------------------------------|
615
  | **Bubble Sort** | O(n) | O(n²) | O(n²) | O(1) | Educational, small datasets |
616
+ | **Insertion Sort** | O(n) | O(n²) | O(n²) | O(1) | Small datasets, nearly sorted|
617
  | **Merge Sort** | O(n log n) | O(n log n) | O(n log n) | O(n) | General purpose, stable |
618
  | **Quick Sort** | O(n log n) | O(n log n) | O(n²) | O(log n) | Large datasets, in-place |
619
  | **DFS** | O(V+E) | O(V+E) | O(V+E) | O(V) | Pathfinding, cycle detection |
 
623
  - **Sorting Algorithms**:
624
  - Use **QuickSort** for average-case performance
625
  - Use **MergeSort** for guaranteed O(n log n) performance
626
+ - **BubbleSort** and **InsertionSort** are efficient for small datasets
627
 
628
  - **Graph Algorithms**:
629
  - **DFS** is better for pathfinding in deep graphs
 
644
  - Multiple algorithm implementations
645
  - Pseudocode with execution highlighting
646
  - Customizable input data
647
+ - Comprehensive error handling
648
  - Complexity analysis reference
649
 
650
+ **Algorithms Implemented**:
651
+ - Sorting: Bubble Sort, Insertion Sort
652
+ - Graph Traversal: DFS, BFS
653
+
654
  **Future Enhancements**:
655
+ - Add more algorithms (Merge Sort, Dijkstra, etc.)
 
656
  - Speed control for animations
657
  - Comparison mode for algorithms
658
  - Export visualizations as GIF
 
661
  """)
662
 
663
  # Navigation events
664
+ next_btn.click(
665
+ visualizer.next_step,
666
+ outputs=[plot_output, pseudocode, error_output]
667
+ )
668
+ prev_btn.click(
669
+ visualizer.prev_step,
670
+ outputs=[plot_output, pseudocode, error_output]
671
+ )
672
+ graph_next.click(
673
+ visualizer.next_step,
674
+ outputs=[graph_plot, graph_pseudocode, error_output]
675
+ )
676
+ graph_prev.click(
677
+ visualizer.prev_step,
678
+ outputs=[graph_plot, graph_pseudocode, error_output]
679
+ )
680
 
681
  # Footer
682
  gr.Markdown("""
683
  <footer>
684
+ <p>AlgoViz Pro v1.1 | Robust & Error-Resistant | For educational purposes</p>
685
  </footer>
686
  """)
687
+
688
+ # Error handling display
689
+ def show_error(error_msg):
690
+ if error_msg:
691
+ return gr.update(value=f"⚠️ ERROR: {error_msg}", visible=True)
692
+ return gr.update(visible=False)
693
+
694
+ error_output.change(
695
+ show_error,
696
+ inputs=[error_output],
697
+ outputs=[error_output]
698
+ )
699
 
700
  if __name__ == "__main__":
701
  demo.launch()