@@ -238,7 +238,7 @@ def generate_square_track(center, size, track_width):
238
238
# Create checkpoints - perpendicular lines across track at regular intervals
239
239
checkpoints = []
240
240
total_points = len (outer_points )
241
- checkpoint_spacing = total_points // 25 # Changed from 16 to 25 for more frequent checkpoints
241
+ checkpoint_spacing = total_points // 25 # Changed from 16 to 24 for more frequent checkpoints
242
242
243
243
for i in range (0 , total_points , checkpoint_spacing ):
244
244
checkpoints .append ([inner_points [i ], outer_points [i ]])
@@ -256,8 +256,8 @@ def generate_square_track(center, size, track_width):
256
256
257
257
def set_track (track_type ):
258
258
global center , walls , checkpoints , road_polygons , start_pos , start_angle
259
- center = (SIM_WIDTH // 2 , SIM_HEIGHT // 2 )
260
259
if track_type == "easy" :
260
+ center = (SIM_WIDTH // 2 + 50 , SIM_HEIGHT // 2 ) # Shifted right to (475, 300)
261
261
outer_radius = 250
262
262
inner_radius = 150
263
263
num_segments = 40
@@ -269,14 +269,15 @@ def set_track(track_type):
269
269
track_points = outer_points + inner_points [::- 1 ]
270
270
road_polygons = [track_points ]
271
271
checkpoints .clear ()
272
- for i in range (20 ): # Increased from 10 to 20 checkpoints
272
+ for i in range (20 ):
273
273
angle = 2 * math .pi * i / 20
274
274
p_inner = (center [0 ] + inner_radius * math .cos (angle ), center [1 ] + inner_radius * math .sin (angle ))
275
275
p_outer = (center [0 ] + outer_radius * math .cos (angle ), center [1 ] + outer_radius * math .sin (angle ))
276
276
checkpoints .append ([p_inner , p_outer ])
277
277
start_pos = (center [0 ] + (inner_radius + outer_radius ) / 2 , center [1 ])
278
278
start_angle = math .pi / 2
279
279
elif track_type == "medium" :
280
+ center = (SIM_WIDTH // 2 + 50 , SIM_HEIGHT // 2 ) # Shifted right to (475, 300)
280
281
outer_a = 250
281
282
outer_b = 200
282
283
inner_a = 170
@@ -290,15 +291,15 @@ def set_track(track_type):
290
291
track_points = outer_points + inner_points [::- 1 ]
291
292
road_polygons = [track_points ]
292
293
checkpoints .clear ()
293
- for i in range (20 ): # Increased from 10 to 20 checkpoints
294
+ for i in range (20 ):
294
295
angle = 2 * math .pi * i / 20
295
296
p_inner = (center [0 ] + inner_a * math .cos (angle ), center [1 ] + inner_b * math .sin (angle ))
296
297
p_outer = (center [0 ] + outer_a * math .cos (angle ), center [1 ] + outer_b * math .sin (angle ))
297
298
checkpoints .append ([p_inner , p_outer ])
298
299
start_pos = (center [0 ] + (inner_a + outer_a ) / 2 , center [1 ])
299
300
start_angle = math .pi / 2
300
301
elif track_type == "hard" :
301
- hard_center = (center [ 0 ] + 50 , center [ 1 ] + 50 ) # New center: (475, 350)
302
+ hard_center = (SIM_WIDTH // 2 + 50 , SIM_HEIGHT // 2 + 50 ) # Remains at (475, 350)
302
303
square_size = 175
303
304
track_width = 70
304
305
walls , road_polygons , checkpoints , start_pos , start_angle = generate_square_track (
@@ -319,15 +320,19 @@ def menu():
319
320
return "medium"
320
321
elif event .key == pygame .K_3 :
321
322
return "hard"
323
+ elif event .key == pygame .K_4 : # New option
324
+ return "impossible"
322
325
screen .fill (WHITE )
323
326
title_text = FONT_MEDIUM .render ("Select Track Difficulty" , True , BLACK )
324
327
screen .blit (title_text , (SIM_WIDTH / 2 - title_text .get_width () / 2 , 100 ))
325
328
option1 = FONT_SMALL .render ("1 - Easy (Circle Track)" , True , BLACK )
326
329
option2 = FONT_SMALL .render ("2 - Medium (Oval Track)" , True , BLACK )
327
- option3 = FONT_SMALL .render ("3 - Hard (Square Track)" , True , BLACK ) # Updated description
330
+ option3 = FONT_SMALL .render ("3 - Hard (Square Track)" , True , BLACK )
331
+ option4 = FONT_SMALL .render ("4 - Impossible (Random Track Each Generation)" , True , BLACK )
328
332
screen .blit (option1 , (SIM_WIDTH / 2 - option1 .get_width () / 2 , 200 ))
329
333
screen .blit (option2 , (SIM_WIDTH / 2 - option2 .get_width () / 2 , 240 ))
330
334
screen .blit (option3 , (SIM_WIDTH / 2 - option3 .get_width () / 2 , 280 ))
335
+ screen .blit (option4 , (SIM_WIDTH / 2 - option4 .get_width () / 2 , 320 ))
331
336
pygame .display .flip ()
332
337
clock .tick (60 )
333
338
@@ -874,9 +879,21 @@ def run_simulation():
874
879
global walls , checkpoints , road_polygons , start_pos , start_angle
875
880
generation_frame_count = 0
876
881
877
- # Select track from menu
882
+ # Select difficulty from menu
878
883
track_type = menu ()
879
- set_track (track_type )
884
+
885
+ # Define track options based on difficulty
886
+ if track_type == "impossible" :
887
+ track_options = ["easy" , "medium" , "hard" ]
888
+ current_track = random .choice (track_options ) # Initial random track
889
+ previous_track = current_track # Track the previous track
890
+ else :
891
+ track_options = [track_type ]
892
+ current_track = track_type
893
+ previous_track = None # No need to track for fixed tracks
894
+
895
+ # Set up the initial track
896
+ set_track (current_track )
880
897
881
898
# Initialize first generation of cars with random NNs
882
899
cars = [Car (NeuralNetwork ()) for _ in range (NUM_CARS )]
@@ -972,6 +989,9 @@ def run_simulation():
972
989
f"Checkpoint: { best_car .current_checkpoint } /{ len (checkpoints )} " ,
973
990
f"Distance: { best_car .distance_traveled :.1f} "
974
991
])
992
+ if track_type == "impossible" :
993
+ stats .append (f"Current Track: { current_track } " )
994
+
975
995
for i , text in enumerate (stats ):
976
996
text_surface = FONT_SMALL .render (text , True , BLACK )
977
997
screen .blit (text_surface , (10 , 10 + i * 24 ))
@@ -989,21 +1009,31 @@ def run_simulation():
989
1009
'alive_count' : alive_count
990
1010
})
991
1011
1012
+ # Randomize track for impossible mode, ensuring no consecutive repeats
1013
+ if track_type == "impossible" :
1014
+ available_tracks = [t for t in track_options if t != previous_track ]
1015
+ new_track = random .choice (available_tracks )
1016
+ current_track = new_track
1017
+ previous_track = current_track
1018
+ set_track (current_track )
1019
+ else :
1020
+ set_track (current_track ) # Re-set the track, though it's the same
1021
+
992
1022
# Create next generation
993
1023
new_generation = []
994
1024
995
1025
# Elitism: keep the best performers
996
- elite_count = max (3 , NUM_CARS // 10 ) # 10% or at least 3
1026
+ elite_count = max (3 , NUM_CARS // 10 )
997
1027
elite_nns = elitism (cars , elite_count )
998
1028
for nn in elite_nns :
999
1029
new_generation .append (Car (nn ))
1000
1030
1001
1031
# Add mutants of the top cars
1002
1032
top_cars = sorted (cars , key = lambda x : x .fitness , reverse = True )[:5 ]
1003
1033
for i , top_car in enumerate (top_cars ):
1004
- num_mutants = 2 # Default number of mutants per top car
1005
- if i == 0 : # For the best car (index 0)
1006
- num_mutants += 2 # Add 2 more mutants, making it 4 total
1034
+ num_mutants = 2
1035
+ if i == 0 :
1036
+ num_mutants += 2
1007
1037
for _ in range (num_mutants ):
1008
1038
mutant_nn = top_car .nn .mutate ()
1009
1039
new_generation .append (Car (mutant_nn ))
@@ -1013,19 +1043,40 @@ def run_simulation():
1013
1043
parent1 = select_parents (cars , "tournament" )
1014
1044
parent2 = select_parents (cars , "tournament" )
1015
1045
1016
- # Crossover with increased probability
1017
- if random .random () < 0.85 : # Probability of crossover
1046
+ if random .random () < 0.85 :
1018
1047
child_nn = parent1 .nn .crossover (parent2 .nn )
1019
1048
else :
1020
1049
child_nn = parent1 .nn .clone () if random .random () < 0.5 else parent2 .nn .clone ()
1021
1050
1022
- # Mutation with dynamic rate
1023
1051
mutation_rate = MUTATION_RATE * (1.0 - min (0.5 , generation / 100 ))
1024
1052
child_nn = child_nn .mutate (mutation_rate = mutation_rate )
1025
1053
1026
1054
new_generation .append (Car (child_nn ))
1027
1055
1056
+ # Reset cars for the new generation and track
1028
1057
cars = new_generation
1058
+ for car in cars :
1059
+ car .x , car .y = start_pos
1060
+ car .angle = start_angle
1061
+ car .alive = True
1062
+ car .fitness = 0
1063
+ car .raw_fitness = 0
1064
+ car .distance_traveled = 0
1065
+ car .current_checkpoint = 0
1066
+ car .prev_checkpoint = 0
1067
+ car .lap_count = 0
1068
+ car .prev_x = car .x
1069
+ car .prev_y = car .y
1070
+ car .idle_time = 0
1071
+ car .wrong_way = False
1072
+ car .crashes = 0
1073
+ car .start_time = pygame .time .get_ticks ()
1074
+ car .checkpoint_times .clear ()
1075
+ car .position_history .clear ()
1076
+ car .avg_speed .clear ()
1077
+ for _ in range (10 ):
1078
+ car .position_history .append ((car .x , car .y ))
1079
+
1029
1080
generation += 1
1030
1081
generation_frame_count = 0
1031
1082
is_first_frame = True
0 commit comments