import pygame import math import random import numpy as np import time # Game constants WIDTH, HEIGHT = 800, 600 WHITE = (255, 255, 255) RED = (255, 0, 0) BROWN = (139, 69, 19) # Terrain generation terrain_height = 50 terrain_points = [terrain_height + random.randint(5, 15) for _ in range(WIDTH // 50)] terrain = np.interp(range(WIDTH), np.linspace(0, WIDTH, len(terrain_points)), terrain_points) class Rocket: def __init__(self): # Rocket properties self.width = 40 self.height = 80 self.x = WIDTH // 2 self.y = HEIGHT // 2 # Start from middle height self.angle = 0 self.velocity_x = 0 self.velocity_y = 0 self.gravity = 10 # Gravity pulls downward, adjusted for faster game self.thrust_power = 30 # Thrust power, adjusted for faster game self.angular_velocity = 0 self.thrusting = False # Flag for thrusting self.rotation_speed = 1 # Rotation speed, adjusted for faster game def update(self, thrusting, rotate_left, rotate_right, time_step=0.1): """Update the rocket state based on user inputs.""" self.thrusting = thrusting # Update thrusting flag # Apply gravity to velocity_y (pulls downward) self.velocity_y -= self.gravity * time_step # Apply thrust only when thrusting is active if self.thrusting: # Thrust aligns with rocket's nose direction self.velocity_y += self.thrust_power * math.cos(math.radians(self.angle)) * time_step self.velocity_x += self.thrust_power * math.sin(math.radians(self.angle)) * time_step # Update position based on velocity self.x += self.velocity_x * time_step self.y += self.velocity_y * time_step # self.x = max(0, min(self.x, WIDTH)) # self.y = max(0, min(self.y, HEIGHT)) # Apply rotation, scaled by time_step if rotate_left: self.angle += self.rotation_speed * time_step if rotate_right: self.angle -= self.rotation_speed * time_step def draw(self, screen): """Draw the rocket on the screen.""" rocket_surface = pygame.Surface((self.width, self.height), pygame.SRCALPHA) pygame.draw.polygon(rocket_surface, RED, [ (self.width // 2, 0), # Nose (0, self.height - 20), # Bottom left (self.width, self.height - 20) # Bottom right ]) pygame.draw.rect(rocket_surface, (0, 0, 0), (self.width // 4, self.height - 20, self.width // 2, 20)) # Body # Draw thrust flames if thrusting if self.thrusting: # Only draw flames when thrusting pygame.draw.polygon(rocket_surface, (255, 165, 0), [ (self.width // 4, self.height - 20), (self.width // 2, self.height), (3 * self.width // 4, self.height - 20) ]) rotated_rocket = pygame.transform.rotate(rocket_surface, self.angle) rect = rotated_rocket.get_rect(center=(self.x, HEIGHT - self.y)) # Invert y for drawing screen.blit(rotated_rocket, rect.topleft) def state(self): return [self.x, self.y, self.angle, self.velocity_x, self.velocity_y] class RocketLandingGame: def __init__(self,render): self.render=render if render: pygame.init() self.screen = pygame.display.set_mode((WIDTH, HEIGHT)) pygame.display.set_caption("Rocket Landing Simulation") self.clock = pygame.time.Clock() self.running = True self.game_over = False self.win = False self.rocket = Rocket() # Create the rocket instance self.action_log = [] # Action log to track the game's state def draw_terrain(self): """Draw the terrain at the bottom of the screen.""" for i in range(len(terrain) - 1): pygame.draw.line(self.screen, BROWN, (i, HEIGHT - terrain[i]), (i + 1, HEIGHT - terrain[i + 1]), 3) def returnState(self): return self.rocket.state() def game_step(self, thrusting, rotate_left, rotate_right, time_step=0.5): """Perform one step in the game (update physics, game state).""" if not self.game_over: reward = 0 # Initialize reward self.rocket.update(thrusting, rotate_left, rotate_right, time_step) # Check for landing or crash rocket_bottom_y = self.rocket.y - self.rocket.height // 2 x_index = int(self.rocket.x) terrain_y_at_x = terrain[x_index] if 0 <= x_index < WIDTH else float('inf') if rocket_bottom_y <= terrain_y_at_x: self.rocket.y = terrain_y_at_x + self.rocket.height // 2 if abs(self.rocket.velocity_y) <= 5.0 and abs(self.rocket.velocity_x) <= 5 and abs(self.rocket.angle) <= 10: self.game_over = True self.win = True # Successful landing reward = 5 # Positive reward for successful landing else: self.game_over = True # Rocket destroyed reward = -2 # Negative reward for crash else: if thrusting: reward -= 0.002 else: reward=0 reward += (5 - abs(self.rocket.velocity_y)) * 0.01 # Reward for reducing vertical speed reward += (5 - abs(self.rocket.velocity_x)) * 0.01 # Reward for reducing horizontal speed reward+=1/(self.rocket.y-99) if abs(self.rocket.angle) <= 5: # Small angle threshold reward += 0.02 # Reward for improving angle control else: reward -= 0.02 # Small penalty for poor angle control if self.rocket.y>400: self.game_over = True reward = -2 if self.rocket.x>500 or self.rocket.x<300: self.game_over = True reward = -2 return reward, self.game_over, self.win def draw_info(self): """Draw the game info (coordinates, angle, velocity) on the screen.""" font = pygame.font.SysFont('Arial', 18) info_text = [ f"X: {self.rocket.x:.2f} Y: {self.rocket.y:.2f}", f"Angle: {self.rocket.angle:.2f}", f"Velocity X: {self.rocket.velocity_x:.2f} Velocity Y: {self.rocket.velocity_y:.2f}", f"Thrusting: {'Yes' if self.rocket.thrusting else 'No'}", ] for i, text in enumerate(info_text): label = font.render(text, True, (0, 0, 0)) self.screen.blit(label, (10, 10 + i * 20)) # Display info def draw_restart_button(self): """Draw the restart button if the game is over.""" font = pygame.font.SysFont('Arial', 30) restart_text = font.render("Restart", True, (255, 255, 255)) button_rect = pygame.Rect(WIDTH // 2 - 75, HEIGHT // 2 - 50, 150, 50) pygame.draw.rect(self.screen, (0, 128, 0), button_rect) # Green button self.screen.blit(restart_text, button_rect.move(25, 10)) # Center the text on button return button_rect def add_to_action_log(self, action): """Add actions to the action log.""" timestamp = time.strftime("%H:%M:%S", time.gmtime(time.time())) self.action_log.append(f"{action}")