@@ -881,6 +881,158 @@ def energy(state):
881881
882882 self .save_frame ()
883883
884+ def scene_vrp (self , duration_sec = 3 ):
885+ """Scene 10: Vehicle Routing Problem"""
886+ print ("🚚 Rendering: Vehicle Routing Problem..." )
887+
888+ frames = int (FPS * duration_sec )
889+
890+ # Setup VRP: Depot + 20 customers, 3 vehicles
891+ n_customers = 20
892+ n_vehicles = 3
893+ depot = (WIDTH // 2 , HEIGHT // 2 )
894+
895+ # Customer locations
896+ customers = [
897+ (random .randint (200 , WIDTH - 200 ), random .randint (150 , HEIGHT - 150 ))
898+ for _ in range (n_customers )
899+ ]
900+ demands = [random .randint (5 , 20 ) for _ in range (n_customers )]
901+ vehicle_capacity = 50
902+
903+ # Assignment: which vehicle serves which customer
904+ state = [random .randint (0 , n_vehicles - 1 ) for _ in range (n_customers )]
905+
906+ def route_distance (assignment ):
907+ """Calculate total distance for all routes"""
908+ total_dist = 0
909+
910+ for v in range (n_vehicles ):
911+ # Get customers for this vehicle
912+ vehicle_customers = [
913+ customers [i ] for i , a in enumerate (assignment ) if a == v
914+ ]
915+ vehicle_demand = sum (
916+ demands [i ] for i , a in enumerate (assignment ) if a == v
917+ )
918+
919+ # Check capacity
920+ if vehicle_demand > vehicle_capacity :
921+ total_dist += 10000 # Heavy penalty
922+
923+ # TSP-like route for this vehicle (simplified)
924+ if vehicle_customers :
925+ # Nearest neighbor approximation
926+ route = [depot ]
927+ unvisited = vehicle_customers .copy ()
928+
929+ while unvisited :
930+ last = route [- 1 ]
931+ nearest = min (unvisited , key = lambda c : math .dist (last , c ))
932+ total_dist += math .dist (last , nearest )
933+ route .append (nearest )
934+ unvisited .remove (nearest )
935+
936+ # Return to depot
937+ total_dist += math .dist (route [- 1 ], depot )
938+
939+ return total_dist
940+
941+ current = route_distance (state )
942+ best = current
943+ step = 0
944+
945+ vehicle_colors = [(255 , 100 , 100 ), (100 , 255 , 100 ), (100 , 100 , 255 )]
946+
947+ for frame in range (frames ):
948+ self .screen .fill (BG_COLOR )
949+
950+ # Optimize
951+ for _ in range (15 ):
952+ idx = random .randint (0 , n_customers - 1 )
953+ new = state .copy ()
954+ new [idx ] = random .randint (0 , n_vehicles - 1 )
955+ new_dist = route_distance (new )
956+ if new_dist < current or random .random () < 0.2 :
957+ state = new
958+ current = new_dist
959+ if current < best :
960+ best = current
961+ step += 1
962+
963+ # Draw depot
964+ pygame .draw .circle (self .screen , (255 , 200 , 100 ), depot , 25 )
965+ pygame .draw .circle (self .screen , (255 , 255 , 255 ), depot , 18 )
966+ depot_text = self .font_small .render ("DEPOT" , True , (0 , 0 , 0 ))
967+ depot_rect = depot_text .get_rect (center = depot )
968+ self .screen .blit (depot_text , depot_rect )
969+
970+ # Draw routes for each vehicle
971+ for v in range (n_vehicles ):
972+ vehicle_customers = [
973+ (i , customers [i ]) for i , a in enumerate (state ) if a == v
974+ ]
975+ vehicle_demand = sum (demands [i ] for i , a in enumerate (state ) if a == v )
976+
977+ if vehicle_customers :
978+ # Draw route
979+ points = [depot ]
980+ for idx , pos in vehicle_customers :
981+ points .append (pos )
982+ points .append (depot )
983+
984+ # Draw lines with vehicle color
985+ for i in range (len (points ) - 1 ):
986+ pygame .draw .line (
987+ self .screen , vehicle_colors [v ], points [i ], points [i + 1 ], 4
988+ )
989+
990+ # Draw customers
991+ for idx , pos in vehicle_customers :
992+ color = vehicle_colors [v ]
993+ pygame .draw .circle (self .screen , color , pos , 12 )
994+ pygame .draw .circle (self .screen , (255 , 255 , 255 ), pos , 8 )
995+ # Demand text
996+ d_text = self .font_small .render (
997+ str (demands [idx ]), True , (0 , 0 , 0 )
998+ )
999+ d_rect = d_text .get_rect (center = pos )
1000+ self .screen .blit (d_text , d_rect )
1001+
1002+ # Capacity bars
1003+ bar_y = HEIGHT - 80
1004+ for v in range (n_vehicles ):
1005+ load = sum (demands [i ] for i , a in enumerate (state ) if a == v )
1006+ bar_x = 300 + v * 400
1007+ bar_w = 200
1008+ bar_h = 25
1009+
1010+ # Background
1011+ pygame .draw .rect (
1012+ self .screen , (50 , 50 , 70 ), (bar_x , bar_y , bar_w , bar_h )
1013+ )
1014+ # Fill
1015+ fill_w = (load / vehicle_capacity ) * bar_w
1016+ color = vehicle_colors [v ] if load <= vehicle_capacity else (255 , 50 , 50 )
1017+ pygame .draw .rect (self .screen , color , (bar_x , bar_y , fill_w , bar_h ))
1018+
1019+ # Label
1020+ label = self .font_small .render (
1021+ f"Vehicle { v + 1 } : { load } /{ vehicle_capacity } " ,
1022+ True ,
1023+ vehicle_colors [v ],
1024+ )
1025+ self .screen .blit (label , (bar_x , bar_y - 25 ))
1026+
1027+ self .draw_stats_panel (
1028+ "Vehicle Routing" , step , int (current / 100 ), int (best / 100 ), 100
1029+ )
1030+
1031+ title = self .font .render ("Problem 10: Vehicle Routing" , True , ACCENT2 )
1032+ self .screen .blit (title , (50 , 50 ))
1033+
1034+ self .save_frame ()
1035+
8841036 def scene_final (self , duration_sec = 3 ):
8851037 """Scene 10: Final showcase with all stats"""
8861038 print ("🏆 Rendering: Final showcase..." )
@@ -897,13 +1049,14 @@ def scene_final(self, duration_sec=3):
8971049 ("Max Clique" , "35 nodes" , "✓ Clique found" ),
8981050 ("Bin Packing" , "40 items, 8 bins" , "✓ Packed optimally" ),
8991051 ("Max Independent Set" , "40 nodes" , "✓ Set isolated" ),
1052+ ("Vehicle Routing" , "20 customers, 3 vehicles" , "✓ Routes optimized" ),
9001053 ]
9011054
9021055 for frame in range (frames ):
9031056 self .screen .fill (BG_COLOR )
9041057
9051058 # Title
906- title = self .font_large .render ("BAHA: 9 Problems Conquered" , True , ACCENT )
1059+ title = self .font_large .render ("BAHA: 10 Problems Conquered" , True , ACCENT )
9071060 title_rect = title .get_rect (center = (WIDTH // 2 , 100 ))
9081061 self .screen .blit (title , title_rect )
9091062
@@ -978,6 +1131,7 @@ def generate(self):
9781131 self .scene_clique (duration_sec = 2 )
9791132 self .scene_binpacking (duration_sec = 2 )
9801133 self .scene_mis (duration_sec = 2 )
1134+ self .scene_vrp (duration_sec = 3 )
9811135
9821136 # Final showcase
9831137 self .scene_final (duration_sec = 4 )
0 commit comments