From 79146f079b0e93204320b5f4b128ee55d3a5dc40 Mon Sep 17 00:00:00 2001 From: gestures6 Date: Sun, 5 Jul 2026 11:31:19 +0300 Subject: [PATCH] fixed heredity and saving stats --- .gitignore | 2 + config.py | 2 +- main.py | 8 +- robot/simulated_robot.py | 203 ++++++++++++++++++++++++++------------- 4 files changed, 146 insertions(+), 69 deletions(-) diff --git a/.gitignore b/.gitignore index 0a59576..302f82c 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ __pycache__/ *.pyc *.pyo *.pyd +*.yaml .env venv/ submodules/ +stats/ diff --git a/config.py b/config.py index e6dbd98..6c37042 100644 --- a/config.py +++ b/config.py @@ -53,7 +53,7 @@ class Config: COLLISION_DAMAGE = 10 # Конус перед роботом - EXTINGUISH_RADIUS = 50 + EXTINGUISH_RADIUS = 80 EXTINGUISH_ANGLE = 60 NUM_FIRES = 4 diff --git a/main.py b/main.py index 361d42d..6e1a987 100644 --- a/main.py +++ b/main.py @@ -56,10 +56,16 @@ def main(): # Геометрический контроллер скоростей arm_control = ArmController(cfg.ARM_CONTROL, mirror=cfg.MIRROR_CAMERA) + + # Создаем папку для статистики + stats_dir = './stats' + if not os.path.exists(stats_dir): + os.makedirs(stats_dir) + print(f"Создана дирректория для статистики: {stats_dir}") # Тип робота: симуляция - с игрой, дебаг - просто печать команды в консоль if cfg.ROBOT_MODE == 'simulator': - robot = DummySimRobot(cfg) + robot = DummySimRobot(cfg, stats_path=stats_dir) else: robot = DebugRobot() diff --git a/robot/simulated_robot.py b/robot/simulated_robot.py index b42c596..1669ae5 100644 --- a/robot/simulated_robot.py +++ b/robot/simulated_robot.py @@ -10,7 +10,18 @@ class Fire: self.radius = radius class DummySimRobot(RobotController): - def __init__(self, config): + def __init__(self, config, stats_path='./stats'): + super().__init__( + fire_ext_max_capacity=config.INITIAL_FIRE_CHARGE, + collision_damage=config.COLLISION_DAMAGE, + k_sof=config.SCORE_PER_FIRE, + k_cas=1, + k_asset=1, + k_hp=1, + k_charge=1, + fall_damage=10 + ) + pygame.init() self.width = config.MAP_WIDTH self.height = config.MAP_HEIGHT @@ -43,28 +54,15 @@ class DummySimRobot(RobotController): 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 @@ -72,7 +70,16 @@ class DummySimRobot(RobotController): self.linear_speed = 0.0 self.angular_speed = 0.0 + self.game_over = False + self.won = False + + self.fires = [] self._generate_fires() + + # Параметры для сохранения статистики + self.stats_path = stats_path + self._stats_saved = False + self._last_stats_path = None def _generate_fires(self): self.fires.clear() @@ -120,63 +127,94 @@ class DummySimRobot(RobotController): if math.hypot(x - closest_x, y - closest_y) < radius: return True return False + + def _is_fire_in_front(self, fire): + """Проверяет, находится ли огонь в зоне тушения""" + dx = fire.x - self.x + dy = fire.y - self.y + dist = math.hypot(dx, dy) + if dist > self.extinguish_radius: + return False + + 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 + + return abs(angle_diff) <= self.extinguish_angle / 2 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)) + """Отправка команды скорости с проверкой заряда""" + self._check_time_init() + if self._charge_points == 0: + return 0, 0 + + v = min(max(-1, v), 1) + w = min(max(-1, w), 1) + + self.linear_speed = v + self.angular_speed = w + + return v, 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): + """Команда тушения огня""" + self._check_time_init() + if self.extinguish_cooldown > 0 or self.extinguish_active: return False - if self.charge <= 0: + if self._fire_ext_capacity <= 0: return False self.extinguish_active = True self.extinguish_timer = 30 self.extinguish_cooldown = 10 - self.charge -= 1 + self._fire_ext_capacity -= 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: + if self._is_fire_in_front(fire): self.fires.remove(fire) - self.score += self.score_per_fire + self._register_exted_sof({ + 'x': fire.x, + 'y': fire.y, + 'radius': fire.radius + }) extinguished = True break - + return extinguished + + def send_fire_ext_pose_cmd(self, horizontal_pose, vertical_pose): + """Команда позиции огнетушителя""" + self._check_time_init() + raise NotImplementedError("Fire extinguish not implemented in DummySimRobot") + + def send_pick_asset_cmd(self): + """Команда подбора объекта""" + self._check_time_init() + raise NotImplementedError("Pick asset not implemented in DummySimRobot") + + def send_lights_cmd(self): + """Команда включения света""" + self._check_time_init() + raise NotImplementedError("Turn on the light not implemented in DummySimRobot") + + def send_fall_reset_cmd(self): + """Команда сброса после падения""" + self._check_time_init() + raise NotImplementedError("Fall can't be implemented DummySimRobot") def update(self, dt=1/30.0): if self.game_over or self.won: return + + # Уменьшение заряда + if self.linear_speed != 0: + self._decrease_charge(abs(self.linear_speed), dt) + # Обновление таймеров if self.invincibility_timer > 0: self.invincibility_timer -= 1 if self.extinguish_timer > 0: @@ -196,18 +234,24 @@ class DummySimRobot(RobotController): # Проверка: столкновение if self._point_collides_with_obstacle(new_x, new_y, self.robot_radius): if self.invincibility_timer == 0: - self.hp -= self.collision_damage + self._register_collision() 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._save_statistics() + if self._hit_points <= 0: self.game_over = True + self._save_statistics() + if self._charge_points <= 0: + self.game_over = True + self._save_statistics() def draw(self): + """Отрисовка симуляции""" self.screen.fill((255,255,255)) # Препятствия @@ -228,7 +272,7 @@ class DummySimRobot(RobotController): num_points = 20 points = [(self.x, self.y)] half_angle = self.extinguish_angle / 2 - cone_len = self.extinguish_cone_length + cone_len = self.extinguish_radius 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) @@ -240,7 +284,7 @@ class DummySimRobot(RobotController): # Робот if self.invincibility_timer > 0 and (self.invincibility_timer % 6 < 3): - pass + pass # Мигание else: if self.robot_image: rotated = pygame.transform.rotate(self.robot_image, -math.degrees(self.angle)) @@ -256,14 +300,15 @@ class DummySimRobot(RobotController): 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)) + hp_text = self.font.render(f"HP: {self._hit_points}", True, (0,0,0)) + charge_text = self.font.render(f"Charge: {self._charge_points:.1f}", True, (0,0,0)) + fire_charge_text = self.font.render(f"Fire charges: {self._fire_ext_capacity}", True, (0,0,0)) + score_text = self.font.render(f"Score: {self.get_score():.2f}", 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)) + self.screen.blit(fire_charge_text, (10, 70)) + self.screen.blit(score_text, (10, 100)) # Конец игры if self.game_over: @@ -274,8 +319,20 @@ class DummySimRobot(RobotController): self.screen.blit(msg, (self.width//2 - 120, self.height//2)) pygame.display.flip() + + def _save_statistics(self): + """Сохраняет статистику при окончании игры""" + if not self._stats_saved: + self._last_stats_path = self.dump_stats(save_path=self.stats_path) + self._stats_saved = True + print(f"\n{'='*50}") + print(f"GAME {'WON' if self.won else 'OVER'}") + print(f"Stats saved: {self._last_stats_path}") + print(self.get_str_status()) + print(f"{'='*50}\n") def handle_events(self): + """Обработка событий pygame""" for event in pygame.event.get(): if event.type == pygame.QUIT: return False @@ -287,6 +344,7 @@ class DummySimRobot(RobotController): return True def step(self): + """Один шаг симуляции""" if not self.handle_events(): return False self.update() @@ -295,36 +353,47 @@ class DummySimRobot(RobotController): 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_fire_charge - self.score = 0 + + # Сброс основных состояний + self._hit_points = self._start_hp + self._fire_ext_capacity = self._fire_ext_max_capacity + self._charge_points = self._start_charge + self._collisions.clear() + self._exted_sofs.clear() + self._found_casualty.clear() + self._found_assets.clear() + self._falls.clear() + self._start_time = None + + # Сброс состояния симуляции self.game_over = False self.won = False self.invincibility_timer = 0 self.extinguish_active = False self.extinguish_timer = 0 self.extinguish_cooldown = 0 + + self._stats_saved = False + self._last_stats_path = None + + # Новая генерация костров 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""" pygame.quit() -