import pygame import math import random from submodules.robot_controller_base.robot_controller import RobotController class Fire: def __init__(self, x, y, radius): self.x = x self.y = y self.radius = radius class DummySimRobot(RobotController): def __init__(self, config): pygame.init() self.width = config.MAP_WIDTH self.height = config.MAP_HEIGHT self.screen = pygame.display.set_mode((self.width, self.height)) pygame.display.set_caption("Robot Simulator – Extinguish Fires") self.clock = pygame.time.Clock() self.font = pygame.font.SysFont(None, 28) border_thickness = config.BORDER_THICKNESS if border_thickness > 0: border_obstacles = [ (0, 0, self.width, border_thickness), (0, self.height - border_thickness, self.width, border_thickness), (0, 0, border_thickness, self.height), (self.width - border_thickness, 0, border_thickness, self.height) ] self.obstacles = list(config.MAP_OBSTACLES) + border_obstacles else: self.obstacles = list(config.MAP_OBSTACLES) self.start = config.START_POS self.finish = config.FINISH_POS self.robot_radius = config.ROBOT_RADIUS self.robot_image = None if config.ROBOT_IMAGE_PATH: try: self.robot_image = pygame.image.load(config.ROBOT_IMAGE_PATH) self.robot_image = pygame.transform.scale(self.robot_image, (self.robot_radius*2, self.robot_radius*2)) except: pass self.max_hp = config.INITIAL_HP self.hp = self.max_hp self.max_fire_charge = config.INITIAL_FIRE_CHARGE self.charge = self.max_fire_charge self.collision_damage = config.COLLISION_DAMAGE self.extinguish_radius = config.EXTINGUISH_RADIUS self.extinguish_angle = math.radians(config.EXTINGUISH_ANGLE) self.score_per_fire = config.SCORE_PER_FIRE self.fires = [] self.num_fires = config.NUM_FIRES self.fire_radius = config.FIRE_RADIUS self.score = 0 self.game_over = False self.won = False self.extinguish_active = False self.extinguish_timer = 0 self.extinguish_cooldown = 0 self.extinguish_cone_length = 80 self.invincibility_timer = 0 self.x, self.y = self.start self.angle = 0.0 self.linear_speed = 0.0 self.angular_speed = 0.0 self._generate_fires() def _generate_fires(self): self.fires.clear() attempts = 0 margin = 30 # минимальное расстояние между центрами костров while len(self.fires) < self.num_fires and attempts < 2000: fx = random.randint(self.robot_radius + 10, self.width - self.robot_radius - 10) fy = random.randint(self.robot_radius + 10, self.height - self.robot_radius - 10) # Проверка: не на препятствии if self._point_collides_with_obstacle(fx, fy, self.robot_radius): attempts += 1 continue # Проверка: не слишком близко к старту if math.hypot(fx - self.start[0], fy - self.start[1]) < self.robot_radius * 3: attempts += 1 continue # Проверка: не слишком близко к финишу if math.hypot(fx - self.finish[0], fy - self.finish[1]) < self.robot_radius * 3: attempts += 1 continue # Проверка: не пересекается с уже существующими кострами overlap = False for fire in self.fires: if math.hypot(fx - fire.x, fy - fire.y) < 2 * self.fire_radius + margin: overlap = True break if overlap: attempts += 1 continue self.fires.append(Fire(fx, fy, self.fire_radius)) attempts += 1 print(f"Сгенерировано {len(self.fires)} очагов возгорания") def _point_collides_with_obstacle(self, x, y, radius): for ox, oy, ow, oh in self.obstacles: closest_x = max(ox, min(x, ox + ow)) closest_y = max(oy, min(y, oy + oh)) if math.hypot(x - closest_x, y - closest_y) < radius: return True return False def send_speed_cmd(self, v, w): self.linear_speed = max(-1.0, min(1.0, v)) self.angular_speed = max(-1.0, min(1.0, w)) ''' def send_fire_ext_burst_cmd(self): if self.charge <= 0: return False for fire in self.fires[:]: if self._is_fire_in_front(fire): self.fires.remove(fire) self.charge -= 1 self.score += self.score_per_fire return True return False ''' def send_fire_ext_burst_cmd(self): if self.extinguish_cooldown > 0 or self.extinguish_active: return False if self.charge <= 0: return False self.extinguish_active = True self.extinguish_timer = 30 self.extinguish_cooldown = 10 self.charge -= 1 cone_len = self.extinguish_cone_length cone_angle_half = self.extinguish_angle / 2 extinguished = False for fire in self.fires[:]: dx = fire.x - self.x dy = fire.y - self.y dist = math.hypot(dx, dy) if dist > cone_len: continue angle_to_fire = math.atan2(dy, dx) angle_diff = angle_to_fire - self.angle angle_diff = (angle_diff + math.pi) % (2*math.pi) - math.pi #[-π, π] if abs(angle_diff) <= cone_angle_half: self.fires.remove(fire) self.score += self.score_per_fire extinguished = True break return extinguished def update(self, dt=1/30.0): if self.game_over or self.won: return if self.invincibility_timer > 0: self.invincibility_timer -= 1 if self.extinguish_timer > 0: self.extinguish_timer -= 1 if self.extinguish_timer == 0: self.extinguish_active = False if self.extinguish_cooldown > 0: self.extinguish_cooldown -= 1 # Движение self.angle += self.angular_speed * dt * 2 dx = self.linear_speed * math.cos(self.angle) * dt * 100 dy = self.linear_speed * math.sin(self.angle) * dt * 100 new_x = self.x + dx new_y = self.y + dy # Проверка: столкновение if self._point_collides_with_obstacle(new_x, new_y, self.robot_radius): if self.invincibility_timer == 0: self.hp -= self.collision_damage self.invincibility_timer = 30 else: self.x, self.y = new_x, new_y # Конец игры? if math.hypot(self.x - self.finish[0], self.y - self.finish[1]) < self.robot_radius: self.won = True if self.hp <= 0: self.game_over = True def draw(self): self.screen.fill((255,255,255)) # Препятствия for ox, oy, ow, oh in self.obstacles: pygame.draw.rect(self.screen, (100,100,100), (ox, oy, ow, oh)) # Финиш pygame.draw.circle(self.screen, (0,200,0), (int(self.finish[0]), int(self.finish[1])), self.robot_radius, 3) # Костры for fire in self.fires: pygame.draw.circle(self.screen, (255,0,0), (int(fire.x), int(fire.y)), fire.radius) pygame.draw.circle(self.screen, (255,100,0), (int(fire.x), int(fire.y)), fire.radius-3) # Конус тушения if self.extinguish_active: cone_surface = pygame.Surface((self.width, self.height), pygame.SRCALPHA) num_points = 20 points = [(self.x, self.y)] half_angle = self.extinguish_angle / 2 cone_len = self.extinguish_cone_length for i in range(num_points + 1): angle = self.angle - half_angle + (self.extinguish_angle) * (i / num_points) x = self.x + cone_len * math.cos(angle) y = self.y + cone_len * math.sin(angle) points.append((x, y)) if len(points) > 2: pygame.draw.polygon(cone_surface, (0, 200, 255, 80), points) self.screen.blit(cone_surface, (0, 0)) # Робот if self.invincibility_timer > 0 and (self.invincibility_timer % 6 < 3): pass else: if self.robot_image: rotated = pygame.transform.rotate(self.robot_image, -math.degrees(self.angle)) rect = rotated.get_rect(center=(int(self.x), int(self.y))) self.screen.blit(rotated, rect) else: nose = (self.x + self.robot_radius * math.cos(self.angle), self.y + self.robot_radius * math.sin(self.angle)) left = (self.x + self.robot_radius * math.cos(self.angle + 2.5), self.y + self.robot_radius * math.sin(self.angle + 2.5)) right = (self.x + self.robot_radius * math.cos(self.angle - 2.5), self.y + self.robot_radius * math.sin(self.angle - 2.5)) pygame.draw.polygon(self.screen, (0,0,255), [nose, left, right]) # Статистика hp_text = self.font.render(f"HP: {self.hp}", True, (0,0,0)) charge_text = self.font.render(f"Charge: {self.charge}", True, (0,0,0)) score_text = self.font.render(f"Score: {self.score}", True, (0,0,0)) fires_text = self.font.render(f"Fires left: {len(self.fires)}", True, (0,0,0)) self.screen.blit(hp_text, (10, 10)) self.screen.blit(charge_text, (10, 40)) self.screen.blit(score_text, (10, 70)) self.screen.blit(fires_text, (10, 100)) # Конец игры if self.game_over: msg = self.font.render("GAME OVER – Press R to restart", True, (255,0,0)) self.screen.blit(msg, (self.width//2 - 150, self.height//2)) elif self.won: msg = self.font.render("YOU WIN! – Press R to restart", True, (0,100,0)) self.screen.blit(msg, (self.width//2 - 120, self.height//2)) pygame.display.flip() def handle_events(self): for event in pygame.event.get(): if event.type == pygame.QUIT: return False if event.type == pygame.KEYDOWN: if event.key == pygame.K_r: self.restart() elif event.key == pygame.K_BACKSPACE: self.reset_position() return True def step(self): if not self.handle_events(): return False self.update() self.draw() self.clock.tick(30) return True def restart(self): self.x, self.y = self.start self.angle = 0.0 self.linear_speed = 0.0 self.angular_speed = 0.0 self.hp = self.max_hp self.charge = self.max_charge self.score = 0 self.game_over = False self.won = False self.invincibility_timer = 0 self.extinguish_active = False self.extinguish_timer = 0 self.extinguish_cooldown = 0 self._generate_fires() def reset_position(self): if self.game_over or self.won: return self.x, self.y = self.start self.angle = 0.0 self.linear_speed = 0.0 self.angular_speed = 0.0 def get_score(self): return self.score def is_alive(self): return not self.game_over and not self.won def quit(self): pygame.quit()