File size: 7,633 Bytes
f386f57 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 | 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}")
|