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}")