Final exam version
This commit is contained in:
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
venv/
|
||||||
|
__pycache__/
|
||||||
|
build/
|
||||||
|
dist/
|
||||||
|
*.spec
|
||||||
|
*.pyc
|
||||||
39
README.md
Normal file
39
README.md
Normal file
@@ -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`.
|
||||||
BIN
SolarSystem
Executable file
BIN
SolarSystem
Executable file
Binary file not shown.
57
UML_classes.puml
Normal file
57
UML_classes.puml
Normal file
@@ -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
|
||||||
14
main.py
Normal file
14
main.py
Normal file
@@ -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()
|
||||||
18
solar_input.py
Normal file
18
solar_input.py
Normal file
@@ -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")
|
||||||
117
solar_model.py
Normal file
117
solar_model.py
Normal file
@@ -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
|
||||||
89
solar_objects.py
Normal file
89
solar_objects.py
Normal file
@@ -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)
|
||||||
31
solar_state.txt
Normal file
31
solar_state.txt
Normal file
@@ -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
|
||||||
|
|
||||||
81
solar_vis.py
Normal file
81
solar_vis.py
Normal file
@@ -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()
|
||||||
Reference in New Issue
Block a user