Hite1 commited on
Commit
871093a
·
verified ·
1 Parent(s): 658fd08

Upload 4 files

Browse files
Files changed (4) hide show
  1. README.md +7 -3
  2. Roboto-Medium.ttf +0 -0
  3. config.txt +78 -0
  4. main.py +211 -0
README.md CHANGED
@@ -1,3 +1,7 @@
1
- ---
2
- license: mit
3
- ---
 
 
 
 
 
1
+ # This project involves a Mario Kart simulation that learns to navigate a modified Mario Kart track using reinforcement learning.
2
+ ### It learns the track and finds the best route based on its speed and rotation. (which can be modified)
3
+ ###### The car which finishes the fastest lap will be displayed on the screen and cooldown implemented to prevent overlapping.
4
+
5
+ <!--- citation from: https://github.com/maxontech/DriveAI -->
6
+
7
+ ![carfinishlap](https://github.com/Hite123/MarioKartReinforcementLearning/assets/96050075/4bde79a2-315a-4cdc-b281-246cc645ac5d)
Roboto-Medium.ttf ADDED
Binary file (169 kB). View file
 
config.txt ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [NEAT]
2
+ fitness_criterion = max
3
+ fitness_threshold = 10000
4
+ pop_size = 75
5
+ reset_on_extinction = False
6
+
7
+ [DefaultGenome]
8
+ # node activation options
9
+ activation_default = tanh
10
+ activation_mutate_rate = 0.1
11
+ activation_options = tanh
12
+
13
+ # node aggregation options
14
+ aggregation_default = sum
15
+ aggregation_mutate_rate = 0.1
16
+ aggregation_options = sum
17
+ # node bias options
18
+ bias_init_mean = 0.0
19
+ bias_init_stdev = 1.0
20
+ bias_max_value = 30.0
21
+ bias_min_value = -30.0
22
+ bias_mutate_power = 0.5
23
+ bias_mutate_rate = 0.7
24
+ bias_replace_rate = 0.1
25
+
26
+ # genome compatibility options
27
+ compatibility_disjoint_coefficient = 1.0
28
+ compatibility_weight_coefficient = 0.5
29
+
30
+ # connection add/remove rates
31
+ conn_add_prob = 0.7
32
+ conn_delete_prob = 0.3
33
+
34
+ # connection enable options
35
+ enabled_default = True
36
+ enabled_mutate_rate = 0.05
37
+
38
+ feed_forward = True
39
+ initial_connection = full_direct
40
+
41
+ # node add/remove rates
42
+ node_add_prob = 0.3
43
+ node_delete_prob = 0.1
44
+
45
+ # network parameters
46
+ num_hidden = 0
47
+ num_inputs = 5
48
+ num_outputs = 2
49
+
50
+ # node response options
51
+ response_init_mean = 1.0
52
+ response_init_stdev = 0.5
53
+ response_max_value = 30.0
54
+ response_min_value = -30.0
55
+ response_mutate_power = 0.5
56
+ response_mutate_rate = 0.1
57
+ response_replace_rate = 0.1
58
+
59
+ # connection weight options
60
+ weight_init_mean = 0.0
61
+ weight_init_stdev = 1.0
62
+ weight_max_value = 30
63
+ weight_min_value = -30
64
+ weight_mutate_power = 0.5
65
+ weight_mutate_rate = 0.9
66
+ weight_replace_rate = 0.2
67
+
68
+ [DefaultSpeciesSet]
69
+ compatibility_threshold = 3.0
70
+
71
+ [DefaultStagnation]
72
+ species_fitness_func = max
73
+ max_stagnation = 20
74
+ species_elitism = 2
75
+
76
+ [DefaultReproduction]
77
+ elitism = 2
78
+ survival_threshold = 0.2
main.py ADDED
@@ -0,0 +1,211 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pygame
2
+ import os
3
+ import math
4
+ import sys
5
+ import neat
6
+ import time
7
+
8
+ SCREEN_WIDTH = 1011
9
+ SCREEN_HEIGHT = 979
10
+
11
+ SCREEN = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
12
+ TRACK = pygame.image.load(os.path.join("Assets","mario_circuit1.png"))
13
+
14
+ pygame.font.init()
15
+ fastest_lap = float('inf')
16
+
17
+ class Car(pygame.sprite.Sprite):
18
+ def __init__(self):
19
+ super().__init__()
20
+ self.orig_image = pygame.image.load(os.path.join("Assets", "mario_kart_2.png"))
21
+ self.image = self.orig_image
22
+ self.rect = self.image.get_rect(center=(624, 800))
23
+ self.velocity = pygame.math.Vector2(0.8, 0)
24
+ self.angle = 0
25
+ self.rotation = 2.5
26
+ self.direction = 0
27
+ self.alive = True
28
+ self.radars = []
29
+ self.start= False
30
+ self.lap_completed=False
31
+ self.start_time=None
32
+ self.cooldown= 3
33
+
34
+
35
+ def update(self):
36
+ self.radars.clear()
37
+ self.drive()
38
+ self.rotate()
39
+
40
+ for radar_angle in (-60, -30, 0, 30, 60):
41
+ self.radar(radar_angle)
42
+ self.collision()
43
+ self.data()
44
+
45
+ def drive(self):
46
+ self.rect.center += self.velocity * 3
47
+
48
+
49
+ def collision(self):
50
+ length = 30
51
+
52
+ col_right = [int(self.rect.center[0] + math.cos(math.radians(self.angle + 18)) * length),
53
+ int(self.rect.center[1] - math.sin(math.radians(self.angle + 18)) * length)]
54
+
55
+ col_left = [int(self.rect.center[0] + math.cos(math.radians(self.angle - 18)) * length),
56
+ int(self.rect.center[1] - math.sin(math.radians(self.angle - 18)) * length)]
57
+
58
+ #Boundary checks
59
+ if SCREEN.get_at(col_right) == pygame.Color(135, 81, 48, 255) or SCREEN.get_at(col_left) == pygame.Color(135, 81, 48, 255):
60
+ self.alive = False
61
+
62
+ if SCREEN.get_at(col_right) == pygame.Color(0, 162, 232, 255) or SCREEN.get_at(col_left) == pygame.Color(0, 162, 232, 255):
63
+ current_time=time.time()
64
+ if not self.start:
65
+ self.start = True
66
+ self.start_time= current_time
67
+ elif self.start and not self.lap_completed and (current_time-self.start_time)>self.cooldown:
68
+ self.lap_completed = True
69
+
70
+
71
+ #Collision points
72
+ pygame.draw.circle(SCREEN, (0, 255, 255, 0), col_right, 4)
73
+ pygame.draw.circle(SCREEN, (0, 255, 255, 0), col_left, 4)
74
+
75
+
76
+ def rotate(self):
77
+ if self.direction == 1:
78
+ self.angle -= self.rotation
79
+ self.velocity.rotate_ip(self.rotation)
80
+ if self.direction == -1:
81
+ self.angle += self.rotation
82
+ self.velocity.rotate_ip(-self.rotation)
83
+
84
+ self.image = pygame.transform.rotozoom(self.orig_image, self.angle, 0.08)
85
+ self.rect = self.image.get_rect(center=self.rect.center)
86
+
87
+ #Radar lines
88
+ def radar(self, radar_angle):
89
+ length = 0
90
+ x = int(self.rect.center[0])
91
+ y = int(self.rect.center[1])
92
+
93
+ while not SCREEN.get_at((x, y)) == pygame.Color(135, 81, 48, 255) and length < 125:
94
+
95
+ length+=1
96
+ x = int(self.rect.center[0] + math.cos(math.radians(self.angle + radar_angle)) * length)
97
+ y = int(self.rect.center[1] - math.sin(math.radians(self.angle + radar_angle)) * length)
98
+
99
+ pygame.draw.line(SCREEN, (255, 255, 255), self.rect.center, (x, y), 1)
100
+ pygame.draw.circle(SCREEN, (239, 20, 20,0), (x, y), 3)
101
+
102
+ distance = int(math.sqrt((self.rect.center[0] - x) ** 2 + (self.rect.center[1] - y) ** 2))
103
+ self.radars.append([radar_angle, distance])
104
+
105
+ def data(self):
106
+ input = [0,0,0,0,0]
107
+ for i, radar in enumerate(self.radars):
108
+ input[i] = int(radar[1])
109
+ return input
110
+
111
+ def remove(i):
112
+ cars.pop(i)
113
+ ind.pop(i)
114
+ Neurnet.pop(i)
115
+
116
+ #Evaluation function
117
+ def eval_fitness(popul,config):
118
+ global cars,ind,Neurnet,fastest_lap
119
+ cars=[]
120
+ ind=[]
121
+ Neurnet=[]
122
+
123
+ for individual_id, individual in popul:
124
+ cars.append(pygame.sprite.GroupSingle(Car()))
125
+ ind.append(individual)
126
+ net= neat.nn.FeedForwardNetwork.create(individual,config)
127
+ Neurnet.append(net)
128
+ individual.fitness = 0
129
+
130
+ start_time= None
131
+ end_time=None
132
+
133
+ run= True
134
+ while run:
135
+ for event in pygame.event.get():
136
+ if event.type == pygame.QUIT:
137
+ pygame.quit()
138
+ sys.exit()
139
+ SCREEN.blit(TRACK, (0, 0))
140
+
141
+ if len(cars)==0:
142
+ break
143
+
144
+ for i,car in enumerate(cars):
145
+ ind[i].fitness+=1
146
+ if not car.sprite.alive:
147
+ remove(i)
148
+
149
+ for i,car in enumerate(cars):
150
+ output= Neurnet[i].activate(car.sprite.data())
151
+ if output[0]>0.7:
152
+ car.sprite.direction=1
153
+ if output[1]<0.7:
154
+ car.sprite.direction=-1
155
+ if output[0] <= 0.7 and output[1] <= 0.7:
156
+ car.sprite.direction=0
157
+
158
+ if car.sprite.start and car.sprite.start_time is not None:
159
+ current_time= time.time()-car.sprite.start_time
160
+ else:
161
+ current_time=0
162
+ if car.sprite.lap_completed:
163
+ lap_time = current_time
164
+ if lap_time < fastest_lap:
165
+ fastest_lap = lap_time
166
+ run = False
167
+ break
168
+
169
+
170
+ #Update
171
+ for car in cars:
172
+ car.draw(SCREEN)
173
+ car.update()
174
+
175
+ font = pygame.font.Font('Roboto-Medium.ttf', 20)
176
+ text = font.render(f"Time: {current_time:.2f}", True, (40, 40, 0))
177
+ SCREEN.blit(text, (10, 10))
178
+
179
+ if fastest_lap == float('inf'):
180
+ ftext = font.render("Fastest Lap: --:--", True, (40, 40, 0))
181
+ else:
182
+ ftext = font.render(f"Fastest Lap: {fastest_lap:.2f}", True, (40, 40, 0))
183
+ SCREEN.blit(ftext, (10, 40))
184
+
185
+ pygame.display.update()
186
+
187
+ return max(ind, key=lambda x: x.fitness)
188
+
189
+ #Neural Network
190
+ def run(config_file):
191
+ global population
192
+ config= neat.config.Config(
193
+ neat.DefaultGenome,
194
+ neat.DefaultReproduction,
195
+ neat.DefaultSpeciesSet,
196
+ neat.DefaultStagnation,
197
+ config_file
198
+ )
199
+
200
+
201
+ population= neat.Population(config)
202
+
203
+ population.add_reporter(neat.StdOutReporter(True))
204
+ stats_reporter = neat.StatisticsReporter()
205
+ population.add_reporter(stats_reporter)
206
+ population.run(eval_fitness,75)
207
+
208
+ if __name__=='__main__':
209
+ local_directory= os.path.dirname(__file__)
210
+ configuration_path= os.path.join(local_directory,"config.txt")
211
+ run(configuration_path)