Spaces:
Sleeping
Sleeping
| import random | |
| import gradio as gr | |
| # ---------- Helper functions ---------- | |
| def generate_random_students(n_students: int): | |
| """ | |
| Generate a random class with names 'Student 1', 'Student 2', ... | |
| and random grades between 0 and 100. | |
| """ | |
| students = [] | |
| for i in range(n_students): | |
| name = f"Student {i + 1}" | |
| grade = random.randint(0, 100) | |
| students.append([name, grade]) | |
| return students | |
| def format_students(students): | |
| """ | |
| Turn a list like [["Student 1", 78], ["Student 2", 92]] | |
| into: [Student 1 (78), Student 2 (92)] | |
| """ | |
| parts = [f"{name} ({grade:g})" for name, grade in students] | |
| return "[" + ", ".join(parts) + "]" | |
| def is_sorted(students, ascending): | |
| """ | |
| Check if the list is sorted by grade in the chosen order. | |
| """ | |
| grades = [g for _, g in students] | |
| if ascending: | |
| return all(grades[i] <= grades[i + 1] for i in range(len(grades) - 1)) | |
| else: | |
| return all(grades[i] >= grades[i + 1] for i in range(len(grades) - 1)) | |
| def bubble_rule_should_swap(left_grade, right_grade, ascending): | |
| """ | |
| Bubble Sort rule: when do we swap? | |
| """ | |
| if ascending: | |
| return left_grade > right_grade | |
| else: | |
| return left_grade < right_grade | |
| # ---------- Simulation functions ---------- | |
| def start_simulation(n_students, order_choice): | |
| """ | |
| Create a random class and set up the first comparison. | |
| """ | |
| try: | |
| n_students = int(n_students) | |
| except ValueError: | |
| n_students = 5 # default | |
| if n_students < 2: | |
| n_students = 2 | |
| students = generate_random_students(n_students) | |
| n = len(students) | |
| ascending = (order_choice == "Lowest to highest") | |
| # Initial pass and position | |
| i = 0 # pass index | |
| j = 0 # position index | |
| list_text = format_students(students) | |
| left_name, left_grade = students[j] | |
| right_name, right_grade = students[j + 1] | |
| should_swap = bubble_rule_should_swap(left_grade, right_grade, ascending) | |
| comparison_text = ( | |
| f"Pass {i + 1} – Compare {left_name} ({left_grade:g}) " | |
| f"and {right_name} ({right_grade:g})\n" | |
| f"Bubble Sort rule for this comparison: " | |
| f"{'Swap' if should_swap else 'Do NOT swap'}" | |
| ) | |
| status = ( | |
| "New random class generated.\n" | |
| "Use the buttons below to decide each step." | |
| ) | |
| # At start: no swaps yet, no accuracy info yet | |
| swap_count = 0 | |
| correct_decisions = 0 | |
| total_decisions = 0 | |
| finished = False | |
| return ( | |
| list_text, | |
| comparison_text, | |
| status, | |
| students, | |
| i, | |
| j, | |
| swap_count, | |
| finished, | |
| order_choice, | |
| correct_decisions, | |
| total_decisions, | |
| ) | |
| def step_simulation( | |
| action, | |
| students, | |
| pass_index, | |
| position_index, | |
| swap_count, | |
| finished, | |
| order_choice, | |
| correct_decisions, | |
| total_decisions, | |
| ): | |
| """ | |
| One step after the teacher presses Swap / Don't swap. | |
| """ | |
| if students is None: | |
| return ( | |
| "Click 'Start Simulation' to generate a class.", | |
| "", | |
| "No active simulation.", | |
| students, | |
| pass_index, | |
| position_index, | |
| swap_count, | |
| finished, | |
| order_choice, | |
| correct_decisions, | |
| total_decisions, | |
| ) | |
| if finished: | |
| list_text = format_students(students) | |
| status = "Simulation already finished. Start again with a new class." | |
| return ( | |
| list_text, | |
| "", | |
| status, | |
| students, | |
| pass_index, | |
| position_index, | |
| swap_count, | |
| finished, | |
| order_choice, | |
| correct_decisions, | |
| total_decisions, | |
| ) | |
| n = len(students) | |
| ascending = (order_choice == "Lowest to highest") | |
| i = pass_index | |
| j = position_index | |
| # Current pair before action | |
| left_name, left_grade = students[j] | |
| right_name, right_grade = students[j + 1] | |
| rule_says_swap = bubble_rule_should_swap(left_grade, right_grade, ascending) | |
| teacher_chose_swap = (action == "swap") | |
| # Update accuracy counters | |
| total_decisions += 1 | |
| if teacher_chose_swap == rule_says_swap: | |
| correct_decisions += 1 | |
| # Apply teacher choice | |
| if teacher_chose_swap: | |
| students[j], students[j + 1] = students[j + 1], students[j] | |
| swap_count += 1 | |
| teacher_action_text = "Teacher chose to SWAP these two students." | |
| else: | |
| teacher_action_text = "Teacher chose NOT to swap these two students." | |
| # Move to next position | |
| j += 1 | |
| status = teacher_action_text | |
| # End of this pass? | |
| if j >= n - 1 - i: | |
| i += 1 | |
| j = 0 | |
| status += f"\nEnd of pass {i}. Moving to the next pass." | |
| # All passes done? | |
| if i >= n - 1: | |
| finished = True | |
| list_text = format_students(students) | |
| correct_order = is_sorted(students, ascending) | |
| # Accuracy | |
| if total_decisions > 0: | |
| accuracy = (correct_decisions / total_decisions) * 100 | |
| else: | |
| accuracy = 0.0 | |
| comparison_text = "" | |
| status += "\n\nSimulation finished. Final list locked." | |
| if correct_order: | |
| status += "\nThe final list IS correctly sorted according to the chosen order." | |
| else: | |
| status += "\nThe final list is NOT correctly sorted according to the chosen order." | |
| status += f"\nTotal swaps you performed: {swap_count}" | |
| status += f"\nYour decision accuracy: {accuracy:.1f}% " | |
| status += f"({correct_decisions} out of {total_decisions} decisions matched the Bubble Sort rule.)" | |
| return ( | |
| list_text, | |
| comparison_text, | |
| status, | |
| students, | |
| i, | |
| j, | |
| swap_count, | |
| finished, | |
| order_choice, | |
| correct_decisions, | |
| total_decisions, | |
| ) | |
| # Not finished: compute next comparison | |
| left_name, left_grade = students[j] | |
| right_name, right_grade = students[j + 1] | |
| should_swap = bubble_rule_should_swap(left_grade, right_grade, ascending) | |
| list_text = format_students(students) | |
| comparison_text = ( | |
| f"Pass {i + 1} – Compare {left_name} ({left_grade:g}) " | |
| f"and {right_name} ({right_grade:g})\n" | |
| ) | |
| return ( | |
| list_text, | |
| comparison_text, | |
| status, | |
| students, | |
| i, | |
| j, | |
| swap_count, | |
| finished, | |
| order_choice, | |
| correct_decisions, | |
| total_decisions, | |
| ) | |
| def finish_now( | |
| students, | |
| pass_index, | |
| position_index, | |
| swap_count, | |
| finished, | |
| order_choice, | |
| correct_decisions, | |
| total_decisions, | |
| ): | |
| """ | |
| Teacher clicks 'Finish now' before all passes complete. | |
| """ | |
| if students is None: | |
| return ( | |
| "Click 'Start Simulation' to generate a class.", | |
| "", | |
| "No active simulation.", | |
| students, | |
| pass_index, | |
| position_index, | |
| swap_count, | |
| finished, | |
| order_choice, | |
| correct_decisions, | |
| total_decisions, | |
| ) | |
| ascending = (order_choice == "Lowest to highest") | |
| list_text = format_students(students) | |
| correct_order = is_sorted(students, ascending) | |
| status = "Teacher finished the list early.\n" | |
| if correct_order: | |
| status += "The final list IS correctly sorted according to the chosen order." | |
| else: | |
| status += "The final list is NOT correctly sorted according to the chosen order." | |
| if total_decisions > 0: | |
| accuracy = (correct_decisions / total_decisions) * 100 | |
| else: | |
| accuracy = 0.0 | |
| status += f"\nTotal swaps you performed: {swap_count}" | |
| status += f"\nYour decision accuracy: {accuracy:.1f}% " | |
| status += f"({correct_decisions} out of {total_decisions} decisions matched the Bubble Sort rule.)" | |
| finished = True | |
| return ( | |
| list_text, | |
| "", | |
| status, | |
| students, | |
| pass_index, | |
| position_index, | |
| swap_count, | |
| finished, | |
| order_choice, | |
| correct_decisions, | |
| total_decisions, | |
| ) | |
| # ---------- Gradio UI ---------- | |
| with gr.Blocks(title="Marking Day – Interactive Bubble Sort") as demo: | |
| gr.Markdown( | |
| """ | |
| # Marking Day – Interactive Bubble Sort Grade Ranker | |
| You are the teacher on marking day. | |
| 1. Choose how many students are in the class. | |
| 2. Choose the sort order. | |
| 3. Click **Start Simulation** to generate a random class. | |
| 4. For each comparison, choose whether to **Swap** or **Don't swap**. | |
| 5. Watch the list update after each choice. | |
| 6. Continue until Bubble Sort finishes, or click **Finish now**. | |
| At the end, you will see: | |
| - Whether the final list is correctly sorted. | |
| - How many swaps you performed. | |
| - How accurate your decisions were compared to the Bubble Sort rule. | |
| """ | |
| ) | |
| n_students_input = gr.Slider( | |
| minimum=2, | |
| maximum=20, | |
| value=5, | |
| step=1, | |
| label="Number of students in the class", | |
| ) | |
| order_choice = gr.Radio( | |
| choices=["Highest to lowest", "Lowest to highest"], | |
| value="Highest to lowest", | |
| label="Sort order", | |
| ) | |
| start_button = gr.Button("Start Simulation", variant="primary") | |
| list_display = gr.Markdown(label="Current list") | |
| comparison_display = gr.Markdown(label="Current comparison") | |
| status_display = gr.Markdown(label="Status") | |
| # State variables | |
| students_state = gr.State() | |
| pass_state = gr.State(0) | |
| position_state = gr.State(0) | |
| swap_count_state = gr.State(0) | |
| finished_state = gr.State(False) | |
| order_state = gr.State("Highest to lowest") | |
| correct_decisions_state = gr.State(0) | |
| total_decisions_state = gr.State(0) | |
| # Start simulation | |
| start_button.click( | |
| fn=start_simulation, | |
| inputs=[n_students_input, order_choice], | |
| outputs=[ | |
| list_display, | |
| comparison_display, | |
| status_display, | |
| students_state, | |
| pass_state, | |
| position_state, | |
| swap_count_state, | |
| finished_state, | |
| order_state, | |
| correct_decisions_state, | |
| total_decisions_state, | |
| ], | |
| ) | |
| with gr.Row(): | |
| swap_button = gr.Button("Swap students") | |
| dont_swap_button = gr.Button("Don't swap") | |
| finish_button = gr.Button("Finish now") | |
| # Step with Swap | |
| swap_button.click( | |
| fn=lambda students, p, pos, swaps, fin, ord_choice, correct_dec, total_dec: step_simulation( | |
| "swap", | |
| students, | |
| p, | |
| pos, | |
| swaps, | |
| fin, | |
| ord_choice, | |
| correct_dec, | |
| total_dec, | |
| ), | |
| inputs=[ | |
| students_state, | |
| pass_state, | |
| position_state, | |
| swap_count_state, | |
| finished_state, | |
| order_state, | |
| correct_decisions_state, | |
| total_decisions_state, | |
| ], | |
| outputs=[ | |
| list_display, | |
| comparison_display, | |
| status_display, | |
| students_state, | |
| pass_state, | |
| position_state, | |
| swap_count_state, | |
| finished_state, | |
| order_state, | |
| correct_decisions_state, | |
| total_decisions_state, | |
| ], | |
| ) | |
| # Step with Don't swap | |
| dont_swap_button.click( | |
| fn=lambda students, p, pos, swaps, fin, ord_choice, correct_dec, total_dec: step_simulation( | |
| "no_swap", | |
| students, | |
| p, | |
| pos, | |
| swaps, | |
| fin, | |
| ord_choice, | |
| correct_dec, | |
| total_dec, | |
| ), | |
| inputs=[ | |
| students_state, | |
| pass_state, | |
| position_state, | |
| swap_count_state, | |
| finished_state, | |
| order_state, | |
| correct_decisions_state, | |
| total_decisions_state, | |
| ], | |
| outputs=[ | |
| list_display, | |
| comparison_display, | |
| status_display, | |
| students_state, | |
| pass_state, | |
| position_state, | |
| swap_count_state, | |
| finished_state, | |
| order_state, | |
| correct_decisions_state, | |
| total_decisions_state, | |
| ], | |
| ) | |
| # Finish early | |
| finish_button.click( | |
| fn=finish_now, | |
| inputs=[ | |
| students_state, | |
| pass_state, | |
| position_state, | |
| swap_count_state, | |
| finished_state, | |
| order_state, | |
| correct_decisions_state, | |
| total_decisions_state, | |
| ], | |
| outputs=[ | |
| list_display, | |
| comparison_display, | |
| status_display, | |
| students_state, | |
| pass_state, | |
| position_state, | |
| swap_count_state, | |
| finished_state, | |
| order_state, | |
| correct_decisions_state, | |
| total_decisions_state, | |
| ], | |
| ) | |
| if __name__ == "__main__": | |
| demo.launch() | |