import%20marimo%0A%0A__generated_with%20%3D%20%220.23.8%22%0Aapp%20%3D%20marimo.App(width%3D%22full%22)%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20import%20marimo%20as%20mo%0A%20%20%20%20import%20numpy%20as%20np%0A%20%20%20%20import%20matplotlib.pyplot%20as%20plt%0A%0A%20%20%20%20return%20mo%2C%20np%2C%20plt%0A%0A%0A%40app.cell%0Adef%20_(np)%3A%0A%20%20%20%20np.random.seed(42)%0A%20%20%20%20n_cities%20%3D%2012%0A%20%20%20%20cities%20%3D%20np.random.rand(n_cities%2C%202)%20*%20100%0A%20%20%20%20return%20cities%2C%20n_cities%0A%0A%0A%40app.cell%0Adef%20_(cities%2C%20np)%3A%0A%20%20%20%20def%20_dist(city1%2C%20city2)%3A%0A%20%20%20%20%20%20%20%20return%20np.sqrt((city1%5B0%5D%20-%20city2%5B0%5D)**2%20%2B%20(city1%5B1%5D%20-%20city2%5B1%5D)**2)%0A%0A%20%20%20%20def%20_solve_tsp(cities)%3A%0A%20%20%20%20%20%20%20%20n%20%3D%20len(cities)%0A%20%20%20%20%20%20%20%20unvisited%20%3D%20set(range(1%2C%20n))%0A%20%20%20%20%20%20%20%20path%20%3D%20%5B0%5D%0A%20%20%20%20%20%20%20%20current%20%3D%200%0A%20%20%20%20%20%20%20%20while%20unvisited%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20nearest%20%3D%20min(unvisited%2C%20key%3Dlambda%20i%3A%20_dist(cities%5Bcurrent%5D%2C%20cities%5Bi%5D))%0A%20%20%20%20%20%20%20%20%20%20%20%20path.append(nearest)%0A%20%20%20%20%20%20%20%20%20%20%20%20unvisited.remove(nearest)%0A%20%20%20%20%20%20%20%20%20%20%20%20current%20%3D%20nearest%0A%20%20%20%20%20%20%20%20path.append(0)%0A%20%20%20%20%20%20%20%20return%20path%0A%0A%20%20%20%20tsp_path%20%3D%20_solve_tsp(cities)%0A%20%20%20%20return%20(tsp_path%2C)%0A%0A%0A%40app.cell%0Adef%20_(mo%2C%20n_cities)%3A%0A%20%20%20%20slider%20%3D%20mo.ui.slider(%0A%20%20%20%20%20%20%20%20start%3D0%2C%0A%20%20%20%20%20%20%20%20stop%3Dn_cities%20%2B%201%2C%0A%20%20%20%20%20%20%20%20value%3D0%2C%0A%20%20%20%20%20%20%20%20step%3D1%2C%0A%20%20%20%20%20%20%20%20label%3D%22City%20Index%22%2C%0A%20%20%20%20%20%20%20%20full_width%3DTrue%0A%20%20%20%20)%0A%20%20%20%20return%20(slider%2C)%0A%0A%0A%40app.cell%0Adef%20_(cities%2C%20np%2C%20plt%2C%20slider%2C%20tsp_path)%3A%0A%20%20%20%20path_coords%20%3D%20np.array(%5Bcities%5Bi%5D%20for%20i%20in%20tsp_path%5D)%0A%20%20%20%20total_cities%20%3D%20len(path_coords)%0A%20%20%20%20_current_pos%20%3D%20slider.value%0A%0A%20%20%20%20fig%2C%20ax%20%3D%20plt.subplots(figsize%3D(10%2C%2010))%0A%20%20%20%20ax.set_xlim(-10%2C%20110)%0A%20%20%20%20ax.set_ylim(-10%2C%20110)%0A%20%20%20%20ax.set_aspect('equal')%0A%20%20%20%20ax.set_facecolor('%230a0a0f')%0A%20%20%20%20fig.patch.set_facecolor('%230a0a0f')%0A%0A%20%20%20%20%23%20Plot%20all%20cities%0A%20%20%20%20ax.scatter(cities%5B%3A%2C%200%5D%2C%20cities%5B%3A%2C%201%5D%2C%20c%3D'%2300d4ff'%2C%20s%3D300%2C%20alpha%3D0.6%2C%20zorder%3D5)%0A%0A%20%20%20%20%23%20Plot%20city%20labels%0A%20%20%20%20for%20idx%2C%20(x%2C%20y)%20in%20enumerate(cities)%3A%0A%20%20%20%20%20%20%20%20ax.text(x%2C%20y%20%2B%203%2C%20str(idx)%2C%20ha%3D'center'%2C%20va%3D'bottom'%2C%20color%3D'white'%2C%20fontsize%3D10%2C%20fontweight%3D'bold')%0A%0A%20%20%20%20%23%20Plot%20the%20complete%20path%20in%20dim%20color%0A%20%20%20%20ax.plot(path_coords%5B%3A%2C%200%5D%2C%20path_coords%5B%3A%2C%201%5D%2C%20'c--'%2C%20alpha%3D0.2%2C%20linewidth%3D2)%0A%0A%20%20%20%20%23%20Plot%20the%20active%20path%0A%20%20%20%20if%20_current_pos%20%3E%200%3A%0A%20%20%20%20%20%20%20%20active_path%20%3D%20path_coords%5B%3A_current_pos%20%2B%201%5D%0A%20%20%20%20%20%20%20%20ax.plot(active_path%5B%3A%2C%200%5D%2C%20active_path%5B%3A%2C%201%5D%2C%20'c-'%2C%20linewidth%3D3%2C%20alpha%3D0.8)%0A%0A%20%20%20%20%23%20Highlight%20current%20city%0A%20%20%20%20if%20_current_pos%20%3C%20total_cities%3A%0A%20%20%20%20%20%20%20%20current_city%20%3D%20path_coords%5B_current_pos%5D%0A%20%20%20%20%20%20%20%20ax.scatter(current_city%5B0%5D%2C%20current_city%5B1%5D%2C%20c%3D'%23ff6b35'%2C%20s%3D500%2C%20zorder%3D10%2C%20edgecolors%3D'white'%2C%20linewidths%3D2)%0A%0A%20%20%20%20%23%20Add%20title%20and%20info%0A%20%20%20%20ax.set_title('Traveling%20Salesman%20Problem%3A%20The%20Slider%20That%20Closes%20the%20Loop'%2C%20color%3D'white'%2C%20fontsize%3D14%2C%20pad%3D20)%0A%20%20%20%20info_text%20%3D%20f%22Step%20%7B_current_pos%7D%2F%7Btotal_cities%20-%201%7D%20%7C%20Path%3A%20%7B'%20%E2%86%92%20'.join(map(str%2C%20tsp_path%5B%3A_current_pos%20%2B%201%5D))%7D%22%0A%20%20%20%20ax.text(50%2C%20-5%2C%20info_text%2C%20ha%3D'center'%2C%20va%3D'top'%2C%20color%3D'%2300d4ff'%2C%20fontsize%3D10%2C%20fontweight%3D'bold')%0A%0A%20%20%20%20ax.tick_params(colors%3D'white')%0A%20%20%20%20for%20spine%20in%20ax.spines.values()%3A%0A%20%20%20%20%20%20%20%20spine.set_color('white')%0A%0A%20%20%20%20plt.tight_layout()%0A%20%20%20%20plt%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mo%2C%20slider)%3A%0A%20%20%20%20play_button%20%3D%20mo.ui.button(label%3D%22%E2%96%B6%20Auto%20Play%22%2C%20kind%3D%22primary%22)%0A%20%20%20%20reset_button%20%3D%20mo.ui.button(label%3D%22%E2%86%BA%20Reset%22)%0A%20%20%20%20mo.hstack(%5Bplay_button%2C%20reset_button%2C%20slider%5D)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(cities%2C%20mo%2C%20np%2C%20slider%2C%20tsp_path)%3A%0A%20%20%20%20_total_distance%20%3D%200%0A%20%20%20%20for%20_idx%20in%20range(len(tsp_path)%20-%201)%3A%0A%20%20%20%20%20%20%20%20dx%20%3D%20cities%5Btsp_path%5B_idx%20%2B%201%5D%5D%5B0%5D%20-%20cities%5Btsp_path%5B_idx%5D%5D%5B0%5D%0A%20%20%20%20%20%20%20%20dy%20%3D%20cities%5Btsp_path%5B_idx%20%2B%201%5D%5D%5B1%5D%20-%20cities%5Btsp_path%5B_idx%5D%5D%5B1%5D%0A%20%20%20%20%20%20%20%20_total_distance%20%2B%3D%20np.sqrt(dx**2%20%2B%20dy**2)%0A%0A%20%20%20%20_current_pos%20%3D%20slider.value%0A%20%20%20%20_current_distance%20%3D%200%0A%20%20%20%20for%20_idx%20in%20range(_current_pos)%3A%0A%20%20%20%20%20%20%20%20if%20_idx%20%2B%201%20%3C%20len(tsp_path)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20dx%20%3D%20cities%5Btsp_path%5B_idx%20%2B%201%5D%5D%5B0%5D%20-%20cities%5Btsp_path%5B_idx%5D%5D%5B0%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20dy%20%3D%20cities%5Btsp_path%5B_idx%20%2B%201%5D%5D%5B1%5D%20-%20cities%5Btsp_path%5B_idx%5D%5D%5B1%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20_current_distance%20%2B%3D%20np.sqrt(dx**2%20%2B%20dy**2)%0A%0A%20%20%20%20stats_text%20%3D%20f%22%22%22%0A%20%20%20%20%23%23%23%20Path%20Statistics%0A%20%20%20%20-%20**Total%20distance%3A**%20%7B_total_distance%3A.1f%7D%20units%0A%20%20%20%20-%20**Current%20distance%3A**%20%7B_current_distance%3A.1f%7D%20units%0A%20%20%20%20-%20**Progress%3A**%20%7B_current_pos%7D%2F%7Blen(tsp_path)%20-%201%7D%20cities%20visited%0A%20%20%20%20-%20**Path%3A**%20%7B'%20%E2%86%92%20'.join(map(str%2C%20tsp_path))%7D%0A%20%20%20%20%22%22%22%0A%20%20%20%20mo.md(stats_text)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%22%22%22%0A%20%20%20%20%23%23%20The%20Slider%20That%20Closes%20the%20Loop%0A%0A%20%20%20%20This%20is%20a%20**reactive%20Marimo%20demo**%20showing%20the%20Traveling%20Salesman%20Problem%3A%0A%0A%20%20%20%20-%20The%20**slider**%20controls%20which%20city%20in%20the%20path%20is%20currently%20%22active%22%0A%20%20%20%20-%20The%20**path**%20automatically%20closes%20the%20loop%20(returns%20to%20start)%0A%20%20%20%20-%20Each%20city%20is%20connected%20via%20the%20**nearest-neighbor%20algorithm**%0A%20%20%20%20-%20The%20visualization%20updates%20in%20real-time%20as%20you%20move%20the%20slider%0A%0A%20%20%20%20%23%23%23%20Key%20Features%0A%20%20%20%201.%20**Reactive**%3A%20Slider%20value%20drives%20the%20path%20visualization%0A%20%20%20%202.%20**Closed%20Loop**%3A%20The%20path%20always%20returns%20to%20the%20starting%20city%0A%20%20%20%203.%20**Interactive**%3A%20Drag%20the%20slider%20to%20see%20the%20path%20build%0A%0A%20%20%20%20%23%23%23%20Try%20it%0A%20%20%20%20-%20Drag%20the%20slider%20to%20watch%20the%20path%20grow%0A%20%20%20%20-%20Click%20**Auto%20Play**%20to%20animate%20the%20tour%0A%20%20%20%20-%20Click%20**Reset**%20to%20start%20over%0A%0A%20%20%20%20%23python%20%23engineering%20%23travelingsalesman%20%23widget%20%23slider%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20app.run()%0A
6eadead8d18fd0f86bd87d3069e2c59c