| import pygame |
| import math |
| import random |
| import csv |
|
|
| import torch |
|
|
| from collections import deque |
|
|
| from Agent import * |
| from Class_Def import * |
| from utils import * |
| from Const import * |
|
|
| device = DEVICE |
|
|
| pygame.init() |
|
|
| WIN = pygame.display.set_mode((WIDTH, HEIGHT)) |
| pygame.display.set_caption("Planet_Sim") |
| FONT = pygame.font.SysFont("Times", 16) |
|
|
| def SatSim(load_weights=False): |
| print(device) |
| Scale = 50/AU |
| Step_p_frame = 1 |
| shift_x, shift_y = 0, 0 |
| run = True |
| clock = pygame.time.Clock() |
|
|
| planets = Solar_System() |
| Target = 6 |
| rockets = [] |
| rockets.append(Launch(planets[3])) |
|
|
|
|
| |
| all_distance_acquisitions = [] |
| current_acquisition = [] |
| |
| |
| agent = Att_Agent(n_features=NUM_FEATURES, |
| seq_len=SEQ_LENGTH, |
| action_size=ACTION_SIZE, |
| replay_memory_capacity=MEMORY_CAPACITY, |
| batch_size=BATCH_SIZE, |
| gamma=GAMMA, |
| eps_start=EPS_START, eps_end=EPS_END, eps_decay=EPS_DECAY, |
| tau=TAU, lr=LR, target_update_freq=TARGET_UPDATE_FREQ, |
| |
| |
| ) |
| |
| |
| try: |
| agent.load_weights() |
| except Exception as e: |
| print(f"Error loading weights: {e}") |
| |
| |
| agent.setup_logging(model_name=MODEL_NAME) |
|
|
| |
| episode_number = 0 |
| episode_reward = 0.0 |
| episode_steps = 0 |
| episode_goal_achieved = False |
|
|
| |
| action_hist = deque(maxlen=MAX_HISTORY_LEN) |
|
|
| Flag = 0 |
| agent_controlled_rocket_index = 0 |
| rocket = rockets[agent_controlled_rocket_index] if rockets else None |
| target_planet = planets[Target] if Target < len(planets) else None |
|
|
|
|
| |
| action_hist = deque(maxlen=MAX_HISTORY_LEN) |
| rel_x_hist = deque(maxlen=MAX_HISTORY_LEN) |
| rel_y_hist = deque(maxlen=MAX_HISTORY_LEN) |
| rel_vx_hist = deque(maxlen=MAX_HISTORY_LEN) |
| rel_vy_hist = deque(maxlen=MAX_HISTORY_LEN) |
|
|
| |
| |
| initial_pad_value = 0.0 |
| initial_action_pad = 0 |
| for _ in range(SEQ_LENGTH): |
| action_hist.append(initial_action_pad) |
| rel_x_hist.append(initial_pad_value) |
| rel_y_hist.append(initial_pad_value) |
| rel_vx_hist.append(initial_pad_value) |
| rel_vy_hist.append(initial_pad_value) |
|
|
| |
| state_tuple = None |
|
|
| |
| n_step = 0 |
| while run: |
| |
| |
| done = False |
|
|
| agent_controlled_rocket_index = 0 |
| rocket = rockets[agent_controlled_rocket_index] if rockets else None |
| target_planet = planets[Target] |
| |
| clock.tick(FPS) |
| WIN.fill((0, 0, 0)) |
|
|
|
|
| |
| for event in pygame.event.get(): |
| if event.type == pygame.QUIT: |
| run = False |
| |
| if event.type == pygame.KEYDOWN: |
| if(event.key == pygame.K_1): Step_p_frame += 10 |
| if(event.key == pygame.K_2): Step_p_frame += 1 |
| if(event.key == pygame.K_3): Step_p_frame -= 1 |
| if(event.key == pygame.K_4): Step_p_frame -= 10 |
| if(event.key == pygame.K_a): Flag -=1 |
| if(event.key == pygame.K_z): Flag +=1 |
| if(event.key == pygame.K_t): Flag = Target |
| if(event.key == pygame.K_r): Target = random.randint(0,len(planets)-1) |
| if(event.key == pygame.K_SPACE): |
| rockets.append(Launch(planets[3])) |
| |
| if(event.key == pygame.K_BACKSPACE): |
| if(len(rockets)> 0): |
| rockets.pop() |
| if event.key == pygame.K_x: |
| if rocket: |
| done = True |
|
|
| |
| |
| action_hist.clear() |
| rel_x_hist.clear() |
| rel_y_hist.clear() |
| rel_vx_hist.clear() |
| rel_vy_hist.clear() |
| |
| |
| for _ in range(SEQ_LENGTH): |
| action_hist.append(initial_action_pad) |
| rel_x_hist.append(initial_pad_value) |
| rel_y_hist.append(initial_pad_value) |
| rel_vx_hist.append(initial_pad_value) |
| rel_vy_hist.append(initial_pad_value) |
| |
| state_tuple = None |
| |
| |
| |
| |
| episode_reward = 0.0 |
| episode_steps = 0 |
| episode_goal_achieved = False |
| print("Targeted planet is", Core_position[int(Target)]) |
| |
| |
| |
|
|
| if(event.key == pygame.K_q): |
| run = False |
| all_distance_acquisitions.append(current_acquisition) |
| current_acquisition = [] |
|
|
| if(Flag>(len(planets)+len(rockets)-1)): Flag = 0 |
| if(Flag<0): Flag = len(planets)+len(rockets)-1 |
| |
| |
| reward = 0 |
|
|
| if rocket and target_planet: |
| |
| |
| |
| current_rel_x, current_rel_y, current_rel_vx, current_rel_vy = calculate_current_relative_state(rocket, target_planet) |
|
|
| |
| if any(math.isnan(v) for v in [current_rel_x, current_rel_y, current_rel_vx, current_rel_vy]): |
| print("Warning: Invalid current state values (NaN). Skipping agent step.") |
| |
| |
| else: |
| |
| |
| state_tuple = get_state_sequence( |
| action_hist, rel_x_hist, rel_y_hist, rel_vx_hist, rel_vy_hist, seq_length=SEQ_LENGTH |
| ) |
|
|
| |
| action_tensor = agent.select_action(state_tuple) |
| action = action_tensor.item() |
|
|
| |
| rocket.apply_action(action) |
|
|
| |
| prev_dist = get_distance(rocket, target_planet) |
| if prev_dist == 0: prev_dist = 1e-6 |
|
|
| |
| planets, rockets = update_position(planets, rockets) |
|
|
| |
| |
| controlled_rocket_index = 0 |
| if controlled_rocket_index < len(rockets): |
| rocket = rockets[controlled_rocket_index] |
| else: |
| rocket = None |
| print("Controlled rocket destroyed during physics update.") |
| done = True |
| reward = -GOAL_REWARD * 2 |
| next_state_tuple = None |
|
|
|
|
| |
| if rocket: |
| |
| episode_steps += 1 |
| try: |
| current_distance = get_distance(rocket, target_planet) |
| current_acquisition.append(current_distance) |
|
|
| if current_distance == 0: current_distance = 1e-6 |
|
|
| |
| |
| reward_distance = float(AU/abs(current_distance - 10*target_planet.radius)) |
| if current_distance>50*target_planet.radius: |
| reward_distance *= np.sign(prev_dist - current_distance) |
|
|
|
|
| |
| target_mass = target_planet.mass |
| ideal_orbital_speed = math.sqrt(G * target_mass / max(current_distance, 1e-6)) |
| rel_vx_now = rocket.vx - target_planet.vx |
| rel_vy_now = rocket.vy - target_planet.vy |
| current_speed_relative = math.sqrt(rel_vx_now**2 + rel_vy_now**2) |
| speed_diff = abs((current_speed_relative - ideal_orbital_speed) / current_speed_relative) |
| reward_speed = REWARD_SPEED_SCALE * (1.0 - speed_diff) |
|
|
| reward_action = 0 |
| if current_distance<20*target_planet.radius: |
| reward_action = NO_THRUST_REWARD if action == 0 else 0 |
|
|
| |
| reward_time = 0 |
|
|
| |
| is_orbiting_goal = (speed_diff * ideal_orbital_speed < ORBIT_SPEED_TOLERANCE) and \ |
| (5*target_planet.radius <= current_distance <= 20*target_planet.radius) |
| |
| reactors_off = rocket.motor_off() |
| |
| if is_orbiting_goal and reactors_off: |
| episode_goal_achieved = True |
| reward_goal = GOAL_REWARD |
| else: |
| reward_goal = 0 |
|
|
| reward = reward_distance + reward_speed + reward_action + reward_time + reward_goal |
| step_reward = reward |
|
|
| |
| episode_reward += step_reward |
|
|
| |
| |
| crash_dist_m = (target_planet.radius) |
| if current_distance < crash_dist_m : |
| print("Crashed into target! Resetting rocket.") |
| print(current_distance, target_planet.radius) |
| done = True |
| reward -= GOAL_REWARD * 0.5 |
| |
|
|
|
|
| |
| if current_distance > OUT_OF_BOUNDS_DISTANCE: |
| print("Went out of bounds! Resetting rocket.") |
| done = True |
| reward -= GOAL_REWARD |
| |
|
|
|
|
| |
| |
| next_rel_x, next_rel_y, next_rel_vx, next_rel_vy = calculate_current_relative_state(rocket, target_planet) |
|
|
| |
| |
| if not math.isnan(current_rel_x): |
| action_hist.append(action) |
| rel_x_hist.append(current_rel_x) |
| rel_y_hist.append(current_rel_y) |
| rel_vx_hist.append(current_rel_vx) |
| rel_vy_hist.append(current_rel_vy) |
|
|
| |
| if not done and not any(math.isnan(v) for v in [next_rel_x, next_rel_y, next_rel_vx, next_rel_vy]): |
| next_state_tuple = get_state_sequence( |
| action_hist, rel_x_hist, rel_y_hist, rel_vx_hist, rel_vy_hist, seq_length=SEQ_LENGTH |
| ) |
| else: |
| next_state_tuple = None |
|
|
|
|
| except (NameError, AttributeError, IndexError, TypeError, ValueError) as e: |
| print(f"Warning: Error during RL step calculation: {e}") |
| import traceback |
| traceback.print_exc() |
| reward = 0 |
| next_state_tuple = None |
| |
| |
|
|
|
|
| |
| |
| if state_tuple is not None: |
| reward_tensor = torch.tensor([reward], dtype=torch.float32, device=device) |
| |
| agent.store_experience(state_tuple, action_tensor, next_state_tuple, reward_tensor, done) |
| else: |
| |
| |
| |
| print("Warning: state_tuple is None, skipping experience storage.") |
| pass |
|
|
|
|
| |
| agent.optimize_model() |
|
|
| |
| if agent.tau > 0: |
| agent.update_target_net(soft_update=True) |
| |
| |
| |
|
|
|
|
| |
| if done: |
| print(f"Episode finished. Reason: {'Crash' if current_distance < crash_dist_m else 'OOB' if current_distance > OUT_OF_BOUNDS_DISTANCE else 'User Reset'}. Final Reward: {reward}") |
| |
| action_hist.clear(); rel_x_hist.clear(); rel_y_hist.clear(); rel_vx_hist.clear(); rel_vy_hist.clear() |
| |
| for _ in range(SEQ_LENGTH): |
| action_hist.append(initial_action_pad) |
| rel_x_hist.append(initial_pad_value) |
| rel_y_hist.append(initial_pad_value) |
| rel_vx_hist.append(initial_pad_value) |
| rel_vy_hist.append(initial_pad_value) |
| |
| all_distance_acquisitions.append(list(current_acquisition)) |
| current_acquisition.clear() |
| episode_number += 1 |
| episode_reward /= episode_steps |
| print(f"--- Episode {episode_number} Finished --- Steps: {episode_steps}, Average Reward: {episode_reward:.2f}, Goal: {episode_goal_achieved} ---") |
| |
| agent.log_episode_data(episode_number, episode_steps, episode_reward, episode_goal_achieved) |
|
|
| |
| rocket = reset_rocket_state(rocket, planets) |
| |
| action_hist.clear(); rel_x_hist.clear(); rel_y_hist.clear(); rel_vx_hist.clear(); rel_vy_hist.clear() |
| for _ in range(SEQ_LENGTH): |
| action_hist.append(initial_action_pad); rel_x_hist.append(initial_pad_value); rel_y_hist.append(initial_pad_value); rel_vx_hist.append(initial_pad_value); rel_vy_hist.append(initial_pad_value) |
|
|
| |
| episode_reward = 0.0 |
| episode_steps = 0 |
| episode_goal_achieved = False |
| state_tuple = None |
| |
|
|
|
|
|
|
|
|
| |
| keys = pygame.key.get_pressed() |
| if keys[pygame.K_o]: Scale *= 1.05 |
| if keys[pygame.K_p]: Scale /= 1.05 |
| |
| if keys[pygame.K_h]: |
| help_lines = [ |
| "--- Help ---", |
| "1: Speed up time (fast)", |
| "2: Speed up time (slow)", |
| "3: Slow down time (slow)", |
| "4: Slow down time (fast)", |
| "a: Focus previous object", |
| "z: Focus next object", |
| "t: Focus target planet", |
| "r: Set random target planet", |
| "space: Launch new rocket (from Earth)", |
| "backspace: Remove last rocket", |
| "o: Zoom in", |
| "p: Zoom out", |
| "left/right/up: Activate rocket reactors (when focusing rocket)" |
| ] |
| |
| line_height = FONT.get_height() |
| start_y = HEIGHT - (len(help_lines) * line_height) - 30 |
|
|
| for i, line in enumerate(help_lines): |
| line_surface = FONT.render(line, 1, WHITE) |
| |
| WIN.blit(line_surface, (10, start_y + i * line_height)) |
|
|
|
|
| |
|
|
| speed_text = FONT.render(f"Step per Frame: {int(Step_p_frame)}", 1, WHITE) |
| WIN.blit(speed_text, (WIDTH - speed_text.get_width() - 10, 25)) |
|
|
| follow_core = Core_position[int(Flag)] if int(Flag)<len(planets) else f"Rocket {int(Flag-len(planets))}" |
|
|
| Flag_text = FONT.render(f"Followed Planet: {follow_core}", 1, WHITE) |
| WIN.blit(Flag_text, (10, 10)) |
|
|
| Target_text = FONT.render(f"Target Planet: {Core_position[int(Target)]}", 1, WHITE) |
| WIN.blit(Target_text, (10, 25)) |
|
|
| reward_text_line1 = f"Total Reward: {round(reward, 3)}" |
| reward_text_line2 = f" D: {round(reward_distance, 3)}, S: {round(reward_speed, 3)}, A: {round(reward_action, 3)}, T: {round(reward_time, 3)}, G: {round(reward_goal, 3)}" |
| reward_text1 = FONT.render(reward_text_line1, 1, WHITE) |
| reward_text2 = FONT.render(reward_text_line2, 1, WHITE) |
| WIN.blit(reward_text1, (10, 40)) |
| WIN.blit(reward_text2, (10, 55)) |
|
|
| Dist_text = FONT.render(f"Rocket-Tgt Distancce:{round(get_distance(planets[Target],rockets[0])/AU,5)}", 1, WHITE) |
| WIN.blit(Dist_text, (WIDTH-Dist_text.get_width()-10, HEIGHT-Dist_text.get_height()-25)) |
| |
| if(Flag<len(planets)): |
| Orbit_text = FONT.render(f"Is Orbiting:{is_orbiting(planets[3],planets[Flag])}", 1, WHITE) |
| else: |
| Orbit_text = FONT.render(f"Is Orbiting:{is_orbiting(rockets[Flag-len(planets)],planets[Target])}", 1, WHITE) |
| WIN.blit(Orbit_text, (WIDTH-Orbit_text.get_width()-10, HEIGHT-Dist_text.get_height()-10)) |
| |
| help_text = FONT.render("Press h to show help-commands", 1, WHITE) |
| WIN.blit(help_text, (10, HEIGHT - help_text.get_height() - 10)) |
| |
| fps = clock.get_fps() |
| fps_text = FONT.render(f"FPS: {int(fps)}", 1, WHITE) |
| WIN.blit(fps_text, (WIDTH - fps_text.get_width() - 10, HEIGHT-Dist_text.get_height()-40)) |
| |
| |
| if n_step%Step_p_frame == 0: |
| |
| if Flag<len(planets): |
| shift_x = - planets[Flag].x |
| shift_y = - planets[Flag].y |
| else: |
| shift_x = - rockets[Flag-len(planets)].x |
| shift_y = - rockets[Flag-len(planets)].y |
| for core in planets+rockets: |
| core.draw(WIN, shift_x, shift_y, Scale) |
| pygame.display.update() |
| n_step += 1 |
| pygame.quit() |
| print("Simulation ended.") |
| |
| agent.save_weights_and_distances(all_distance_acquisitions) |
| |
| agent.close_log() |
|
|
|
|
| if __name__ == '__main__': |
|
|
|
|
| SatSim(load_weights=True) |