commit b03800ab6322640cfaab3d1db554cd897c513bc1 Author: 24_OskinEA <24_OskinEA@iux.local> Date: Mon Jun 22 18:49:13 2026 +0300 Final exam version diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cfff3be --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +venv/ +__pycache__/ +build/ +dist/ +*.spec +*.pyc diff --git a/README.md b/README.md new file mode 100644 index 0000000..7ba2f4f --- /dev/null +++ b/README.md @@ -0,0 +1,39 @@ +# Solar System — билет №3 + +Программа моделирует систему из двух звезд. + +У первой звезды 11 планет, на каждой орбите по одной планете. +У второй звезды 12 планет, на каждой орбите по две планеты. +Четные орбиты вращаются против часовой стрелки, нечетные — по часовой стрелке. +Орбиты двух звезд пересекаются, как указано в билете. + +## Управление + +- SPACE — пауза +- O — показать или скрыть орбиты +- S — сохранить состояние в файл +- ESC — выход + +## Запуск на Linux + +```bash +./run_linux.sh +``` + +## Сборка приложения на Linux + +```bash +./build_linux_app.sh +``` + +Готовый файл появится в папке `dist`. + +## Сборка exe на Windows + +Запустить файл: + +```text +build_windows_exe.bat +``` + +После сборки exe появится в папке `dist`. diff --git a/SolarSystem b/SolarSystem new file mode 100755 index 0000000..487f374 Binary files /dev/null and b/SolarSystem differ diff --git a/UML_classes.puml b/UML_classes.puml new file mode 100644 index 0000000..6ea6ceb --- /dev/null +++ b/UML_classes.puml @@ -0,0 +1,57 @@ +@startuml +class SpaceObject { + x + y + radius + color + update() + draw() +} + +class Star { + name + planets + add_planet() + set_position() + update() + draw() +} + +class Planet { + star + orbit_number + orbit_radius + angle + speed + direction + calculate_position() + update() + draw_orbit() + draw() +} + +class SolarSystem { + stars + show_orbits + paused + create_system() + resize() + update() + draw() + toggle_pause() + toggle_orbits() +} + +class SolarApp { + system + handle_events() + draw_panel() + run() +} + +SpaceObject <|-- Star +SpaceObject <|-- Planet +Star "1" o-- "many" Planet +SolarSystem "1" o-- "2" Star +SolarApp --> SolarSystem +@enduml diff --git a/main.py b/main.py new file mode 100644 index 0000000..7ca0d6c --- /dev/null +++ b/main.py @@ -0,0 +1,14 @@ +from solar_model import SolarSystem, TICKET_CONFIG +from solar_vis import SolarApp +from solar_input import save_system_to_file + + +def main(): + window = TICKET_CONFIG["window"] + system = SolarSystem(window["width"], window["height"]) + app = SolarApp(system, save_system_to_file) + app.run() + + +if __name__ == "__main__": + main() diff --git a/main.pyw b/main.pyw new file mode 100644 index 0000000..ce7e40b --- /dev/null +++ b/main.pyw @@ -0,0 +1,2 @@ +from main import main +main() diff --git a/solar_input.py b/solar_input.py new file mode 100644 index 0000000..22ffa59 --- /dev/null +++ b/solar_input.py @@ -0,0 +1,18 @@ +from datetime import datetime + + +def save_system_to_file(system, filename="solar_state.txt"): + with open(filename, "w", encoding="utf-8") as file: + file.write(f"Saved: {datetime.now()}\n") + file.write(f"Paused: {system.paused}\n") + file.write(f"Show orbits: {system.show_orbits}\n\n") + + for star in system.stars: + file.write(f"{star.name}: x={round(star.x, 2)}, y={round(star.y, 2)}, radius={star.radius}\n") + for planet in star.planets: + file.write( + f" Planet orbit={planet.orbit_number}, " + f"x={round(planet.x, 2)}, y={round(planet.y, 2)}, " + f"radius={planet.radius}, speed={round(planet.speed, 4)}\n" + ) + file.write("\n") diff --git a/solar_model.py b/solar_model.py new file mode 100644 index 0000000..c95eacf --- /dev/null +++ b/solar_model.py @@ -0,0 +1,117 @@ +import math +import random +from solar_objects import Star, Planet + + +TICKET_CONFIG = { + "window": { + "width": 1200, + "height": 720, + "min_width": 850, + "min_height": 560, + }, + "stars": [ + { + "name": "Star 1", + "planet_count": 11, + "planets_per_orbit": 1, + "first_orbit": 48, + "orbit_step": 24, + "color": (255, 190, 40), + }, + { + "name": "Star 2", + "planet_count": 12, + "planets_per_orbit": 2, + "first_orbit": 58, + "orbit_step": 38, + "color": (255, 150, 55), + }, + ], + "rules": { + "even_orbit_direction": -1, + "odd_orbit_direction": 1, + "orbits_must_intersect": True, + }, +} + + +class SolarSystem: + def __init__(self, width=1200, height=720): + self.width = width + self.height = height + self.stars = [] + self.show_orbits = True + self.paused = False + self.create_system() + self.resize(width, height) + + def create_system(self): + self.stars = [] + + for star_data in TICKET_CONFIG["stars"]: + star = Star(0, 0, radius=34, color=star_data["color"], name=star_data["name"]) + planet_count = star_data["planet_count"] + planets_per_orbit = star_data["planets_per_orbit"] + orbit_count = math.ceil(planet_count / planets_per_orbit) + created = 0 + + for orbit_number in range(1, orbit_count + 1): + orbit_radius = star_data["first_orbit"] + (orbit_number - 1) * star_data["orbit_step"] + direction = self.get_direction(orbit_number) + speed = 0.009 + orbit_number * 0.0007 + base_angle = random.uniform(0, math.pi * 2) + + for place in range(planets_per_orbit): + if created >= planet_count: + break + + angle = base_angle + place * (2 * math.pi / planets_per_orbit) + planet = Planet(star, orbit_number, orbit_radius, angle, speed, direction) + star.add_planet(planet) + created += 1 + + self.stars.append(star) + + def get_direction(self, orbit_number): + if orbit_number % 2 == 0: + return TICKET_CONFIG["rules"]["even_orbit_direction"] + return TICKET_CONFIG["rules"]["odd_orbit_direction"] + + def resize(self, width, height): + self.width = width + self.height = height + center_x = width // 2 + center_y = height // 2 + distance = min(370, max(300, width // 3)) + + self.stars[0].set_position(center_x - distance // 2, center_y) + self.stars[1].set_position(center_x + distance // 2, center_y) + + for star in self.stars: + for planet in star.planets: + planet.x, planet.y = planet.calculate_position() + + def update(self): + if self.paused: + return + + for star in self.stars: + star.update() + + def draw(self, screen, pygame): + for star in self.stars: + star.draw(screen, pygame, self.show_orbits) + + def toggle_pause(self): + self.paused = not self.paused + + def toggle_orbits(self): + self.show_orbits = not self.show_orbits + + def get_all_objects(self): + objects = [] + for star in self.stars: + objects.append(star) + objects.extend(star.planets) + return objects diff --git a/solar_objects.py b/solar_objects.py new file mode 100644 index 0000000..349d925 --- /dev/null +++ b/solar_objects.py @@ -0,0 +1,89 @@ +import math +import random + + +class SpaceObject: + def __init__(self, x, y, radius, color): + self.x = x + self.y = y + self.radius = radius + self.color = color + + def update(self): + pass + + def draw(self, screen, pygame): + pass + + +class Star(SpaceObject): + def __init__(self, x, y, radius=34, color=(255, 185, 40), name="Star"): + super().__init__(x, y, radius, color) + self.name = name + self.planets = [] + + def add_planet(self, planet): + self.planets.append(planet) + + def set_position(self, x, y): + self.x = x + self.y = y + + def update(self): + for planet in self.planets: + planet.update() + + def draw(self, screen, pygame, show_orbits=True): + if show_orbits: + for planet in self.planets: + planet.draw_orbit(screen, pygame) + + pygame.draw.circle(screen, self.color, (int(self.x), int(self.y)), self.radius) + pygame.draw.circle(screen, (255, 230, 120), (int(self.x), int(self.y)), self.radius // 2) + + for planet in self.planets: + planet.draw(screen, pygame) + + +class Planet(SpaceObject): + def __init__(self, star, orbit_number, orbit_radius, angle, speed, direction, radius=None, color=None): + self.star = star + self.orbit_number = orbit_number + self.orbit_radius = orbit_radius + self.angle = angle + self.speed = speed + self.direction = direction + + if radius is None: + radius = random.randint(8, 13) + + if color is None: + color = ( + random.randint(40, 220), + random.randint(70, 230), + random.randint(90, 255), + ) + + x, y = self.calculate_position() + super().__init__(x, y, radius, color) + + def calculate_position(self): + x = self.star.x + math.cos(self.angle) * self.orbit_radius + y = self.star.y + math.sin(self.angle) * self.orbit_radius + return x, y + + def update(self): + self.angle += self.speed * self.direction + self.x, self.y = self.calculate_position() + + def draw_orbit(self, screen, pygame): + pygame.draw.circle( + screen, + (160, 160, 160), + (int(self.star.x), int(self.star.y)), + int(self.orbit_radius), + 1, + ) + + def draw(self, screen, pygame): + pygame.draw.circle(screen, self.color, (int(self.x), int(self.y)), self.radius) diff --git a/solar_state.txt b/solar_state.txt new file mode 100644 index 0000000..9b59ef4 --- /dev/null +++ b/solar_state.txt @@ -0,0 +1,31 @@ +Saved: 2026-06-22 18:02:46.891167 +Paused: False +Show orbits: True + +Star 1: x=415, y=360, radius=34 + Planet orbit=1, x=385.28, y=397.69, radius=9, speed=0.0097 + Planet orbit=2, x=391.79, y=428.16, radius=9, speed=0.0104 + Planet orbit=3, x=509.95, y=345.83, radius=8, speed=0.0111 + Planet orbit=4, x=302.54, y=318.14, radius=8, speed=0.0118 + Planet orbit=5, x=364.99, y=224.96, radius=13, speed=0.0125 + Planet orbit=6, x=582.78, y=368.53, radius=9, speed=0.0132 + Planet orbit=7, x=373.14, y=172.62, radius=12, speed=0.0139 + Planet orbit=8, x=554.07, y=194.72, radius=11, speed=0.0146 + Planet orbit=9, x=652.36, y=324.47, radius=12, speed=0.0153 + Planet orbit=10, x=245.7, y=562.57, radius=10, speed=0.016 + Planet orbit=11, x=358.22, y=77.65, radius=11, speed=0.0167 + +Star 2: x=785, y=360, radius=34 + Planet orbit=1, x=727.07, y=362.78, radius=12, speed=0.0097 + Planet orbit=1, x=842.93, y=357.22, radius=9, speed=0.0097 + Planet orbit=2, x=738.35, y=443.9, radius=8, speed=0.0104 + Planet orbit=2, x=831.65, y=276.1, radius=8, speed=0.0104 + Planet orbit=3, x=673.36, y=434.11, radius=10, speed=0.0111 + Planet orbit=3, x=896.64, y=285.89, radius=8, speed=0.0111 + Planet orbit=4, x=614.18, y=339.86, radius=13, speed=0.0118 + Planet orbit=4, x=955.82, y=380.14, radius=11, speed=0.0118 + Planet orbit=5, x=781.37, y=150.03, radius=12, speed=0.0125 + Planet orbit=5, x=788.63, y=569.97, radius=10, speed=0.0125 + Planet orbit=6, x=601.69, y=192.96, radius=13, speed=0.0132 + Planet orbit=6, x=968.31, y=527.04, radius=12, speed=0.0132 + diff --git a/solar_vis.py b/solar_vis.py new file mode 100644 index 0000000..eac6032 --- /dev/null +++ b/solar_vis.py @@ -0,0 +1,81 @@ +import pygame +from solar_model import TICKET_CONFIG + + +class SolarApp: + def __init__(self, system, save_callback): + pygame.init() + + window = TICKET_CONFIG["window"] + self.width = window["width"] + self.height = window["height"] + self.min_width = window["min_width"] + self.min_height = window["min_height"] + + self.screen = pygame.display.set_mode((self.width, self.height), pygame.RESIZABLE) + pygame.display.set_caption("Solar System") + + self.clock = pygame.time.Clock() + self.font = pygame.font.Font(None, 27) + self.system = system + self.save_callback = save_callback + self.running = True + self.message = "" + + def draw_text(self, text, x, y, color=(30, 30, 30)): + image = self.font.render(text, True, color) + self.screen.blit(image, (x, y)) + + def draw_panel(self): + panel = pygame.Surface((330, 205)) + panel.set_alpha(230) + panel.fill((245, 245, 245)) + self.screen.blit(panel, (15, 15)) + + self.draw_text("Билет 3", 30, 30) + self.draw_text("1 звезда: 11 планет", 30, 62) + self.draw_text("2 звезда: 12 планет", 30, 94) + self.draw_text("SPACE — пауза", 30, 126) + self.draw_text("O — орбиты", 30, 158) + self.draw_text("S — сохранить", 30, 190) + + if self.system.paused: + self.draw_text("Пауза", 230, 30, (180, 0, 0)) + + if self.message: + self.draw_text(self.message, 30, 230, (0, 90, 0)) + + def handle_events(self): + for event in pygame.event.get(): + if event.type == pygame.QUIT: + self.running = False + + if event.type == pygame.VIDEORESIZE: + self.width = max(self.min_width, event.w) + self.height = max(self.min_height, event.h) + self.screen = pygame.display.set_mode((self.width, self.height), pygame.RESIZABLE) + self.system.resize(self.width, self.height) + + if event.type == pygame.KEYDOWN: + if event.key == pygame.K_ESCAPE: + self.running = False + elif event.key == pygame.K_SPACE: + self.system.toggle_pause() + elif event.key == pygame.K_o: + self.system.toggle_orbits() + elif event.key == pygame.K_s: + self.save_callback(self.system) + self.message = "Состояние сохранено" + + def run(self): + while self.running: + self.clock.tick(60) + self.handle_events() + self.system.update() + + self.screen.fill((255, 255, 255)) + self.system.draw(self.screen, pygame) + self.draw_panel() + pygame.display.update() + + pygame.quit()