| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348 |
- import pygame
- import sys
- import random
- # Инициализация Pygame
- pygame.init()
- WIDTH, HEIGHT = 1024, 768
- screen = pygame.display.set_mode((WIDTH, HEIGHT))
- pygame.display.set_caption("Диспетчер потоков – управление режимами")
- clock = pygame.time.Clock()
- # Шрифты
- FONT_TITLE = pygame.font.Font(None, 48)
- FONT = pygame.font.Font(None, 36)
- FONT_SMALL = pygame.font.Font(None, 28)
- # Цвета
- BG = (20, 25, 45)
- QUEUE_BG = (35, 45, 65)
- TEXT_COL = (255, 255, 255)
- BUTTON_COL = (70, 130, 200)
- BUTTON_HOVER = (100, 160, 240)
- ACTIVE_COL = (255, 80, 80)
- MODAL_BG = (0, 0, 0, 200)
- # ---------- Класс потока ----------
- class Thread:
- def __init__(self, tid, priority, burst):
- self.tid = tid
- self.priority = priority
- self.remaining = burst
- self.base_color = (random.randint(100, 220), random.randint(100, 220), 255)
- self.animation_progress = 0.0 # 0..1 для анимации выезда
- def get_color(self, is_active=False, pulse=0):
- if is_active:
- # Пульсирующий красный
- r = ACTIVE_COL[0] + int(pulse * 50)
- g = ACTIVE_COL[1] - int(pulse * 30)
- b = ACTIVE_COL[2] - int(pulse * 30)
- return (min(255, r), max(50, g), max(50, b))
- return self.base_color
- # ---------- Планировщик ----------
- class Scheduler:
- def __init__(self, algorithm):
- self.algorithm = algorithm # "rr" или "priority"
- self.queue = []
- self.current = None
- self.time_quantum = 3
- self.quantum_counter = 0
- self.interrupts_handled = 0
- self.total_steps = 0
- def add_thread(self, thread):
- thread.animation_progress = 0.0
- self.queue.append(thread)
- def step(self):
- self.total_steps += 1
- if self.current is None:
- if not self.queue:
- return False
- self.current = self.queue.pop(0)
- self.quantum_counter = 0
- return True
- self.current.remaining -= 1
- self.quantum_counter += 1
- if self.current.remaining <= 0:
- self.current = None
- self.quantum_counter = 0
- elif self.algorithm == "rr" and self.quantum_counter >= self.time_quantum:
- self.queue.append(self.current)
- self.current = None
- self.quantum_counter = 0
- elif self.algorithm == "priority":
- higher = [t for t in self.queue if t.priority < self.current.priority]
- if higher:
- self.queue.append(self.current)
- self.current = None
- self.quantum_counter = 0
- return True
- def all_finished(self):
- return self.current is None and len(self.queue) == 0
- # ---------- Вспомогательные функции отрисовки ----------
- def draw_button(text, x, y, w, h, color=None, hover_color=None):
- if color is None:
- color = BUTTON_COL
- if hover_color is None:
- hover_color = BUTTON_HOVER
- mouse = pygame.mouse.get_pos()
- click = pygame.mouse.get_pressed()
- is_hover = (x < mouse[0] < x + w and y < mouse[1] < y + h)
- col = hover_color if is_hover else color
- pygame.draw.rect(screen, col, (x, y, w, h), border_radius=12)
- pygame.draw.rect(screen, (0, 0, 0), (x + 2, y + 2, w, h), border_radius=12, width=1)
- text_surf = FONT_SMALL.render(text, True, TEXT_COL)
- screen.blit(text_surf, (x + w // 2 - text_surf.get_width() // 2, y + h // 2 - text_surf.get_height() // 2))
- return is_hover and click[0]
- def draw_queue_animated(threads, current, pulse):
- start_x = 80
- start_y = 180
- slot_w = 140
- slot_h = 80
- max_visible = 6
- pygame.draw.rect(screen, QUEUE_BG, (50, start_y - 20, WIDTH - 100, 130), border_radius=15)
- for i, thr in enumerate(threads[:max_visible]):
- # Анимация выезда
- if thr.animation_progress < 1.0:
- thr.animation_progress = min(1.0, thr.animation_progress + 0.1)
- offset = int((1 - thr.animation_progress) * slot_w)
- x = start_x + i * slot_w - offset
- y = start_y
- rect = pygame.Rect(x, y, slot_w - 10, slot_h)
- color = thr.get_color(False)
- pygame.draw.rect(screen, color, rect, border_radius=10)
- pygame.draw.rect(screen, (255, 255, 255), rect, width=2, border_radius=10)
- text = FONT_SMALL.render(f"T{thr.tid} P:{thr.priority} {thr.remaining}", True, TEXT_COL)
- screen.blit(text, (rect.x + 5, rect.y + 30))
- if current:
- # Пульсация активного потока
- scale = 1 + pulse * 0.05
- w = int(160 * scale)
- h = int(80 * scale)
- x = WIDTH // 2 - w // 2
- y = start_y - 70 - int(pulse * 5)
- color = current.get_color(True, pulse)
- rect = pygame.Rect(x, y, w, h)
- pygame.draw.rect(screen, color, rect, border_radius=15)
- pygame.draw.rect(screen, (255, 255, 200), rect, width=3, border_radius=15)
- text = FONT.render(f"Активен: T{current.tid} (ост.{current.remaining})", True, TEXT_COL)
- screen.blit(text, (rect.x + rect.w // 2 - text.get_width() // 2, rect.y + rect.h // 2 - text.get_height() // 2))
- def modal_interrupt(scheduler):
- """Модальное окно выбора обработки прерывания"""
- modal_w, modal_h = 450, 220
- modal_x = WIDTH // 2 - modal_w // 2
- modal_y = HEIGHT // 2 - modal_h // 2
- overlay = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
- overlay.fill((0, 0, 0, 180))
- screen.blit(overlay, (0, 0))
- pygame.draw.rect(screen, (50, 55, 80), (modal_x, modal_y, modal_w, modal_h), border_radius=20)
- pygame.draw.rect(screen, (100, 130, 200), (modal_x, modal_y, modal_w, modal_h), width=3, border_radius=20)
- title = FONT.render("⚠ Внешнее прерывание!", True, (255, 220, 100))
- screen.blit(title, (modal_x + modal_w // 2 - title.get_width() // 2, modal_y + 30))
- sub = FONT_SMALL.render("Переход в режим ядра. Что делать?", True, TEXT_COL)
- screen.blit(sub, (modal_x + modal_w // 2 - sub.get_width() // 2, modal_y + 70))
- btn_imm = draw_button("Обработать немедленно", modal_x + 30, modal_y + 140, 180, 45)
- btn_delay = draw_button("Отложить", modal_x + 240, modal_y + 140, 180, 45)
- pygame.display.flip()
- while True:
- for event in pygame.event.get():
- if event.type == pygame.QUIT:
- pygame.quit()
- sys.exit()
- if event.type == pygame.MOUSEBUTTONDOWN:
- if btn_imm:
- return True
- if btn_delay:
- return False
- # Обновляем состояние кнопок
- btn_imm = draw_button("Обработать немедленно", modal_x + 30, modal_y + 140, 180, 45)
- btn_delay = draw_button("Отложить", modal_x + 240, modal_y + 140, 180, 45)
- pygame.display.flip()
- clock.tick(30)
- # ---------- Основная функция ----------
- def main():
- scene = 0 # 0-выбор алгоритма, 1-добавление потоков, 2-диспетчеризация, 3-прерывание (модальное), 4-статистика
- algorithm = None
- scheduler = None
- next_tid = 1
- message = ""
- step_count = 0
- interrupt_available = False
- threads_added = []
- running = True
- pulse = 0.0
- pulse_dir = 1
- while running:
- # Пульсация для активного потока
- pulse += 0.05 * pulse_dir
- if pulse >= 1.0:
- pulse = 1.0
- pulse_dir = -1
- elif pulse <= 0.0:
- pulse = 0.0
- pulse_dir = 1
- screen.fill(BG)
- # Заголовок
- title = FONT_TITLE.render("⚙ Управление режимом потоков", True, TEXT_COL)
- screen.blit(title, (WIDTH // 2 - title.get_width() // 2, 20))
- # ------------------ СЦЕНА 0: ВЫБОР АЛГОРИТМА ------------------
- if scene == 0:
- prompt = FONT.render("Выберите алгоритм планирования:", True, TEXT_COL)
- screen.blit(prompt, (WIDTH // 2 - prompt.get_width() // 2, 180))
- if draw_button("Round Robin", WIDTH // 2 - 180, 280, 160, 60):
- algorithm = "rr"
- scheduler = Scheduler("rr")
- scene = 1
- threads_added = []
- next_tid = 1
- if draw_button("Приоритетное", WIDTH // 2 + 20, 280, 160, 60):
- algorithm = "priority"
- scheduler = Scheduler("priority")
- scene = 1
- threads_added = []
- next_tid = 1
- if draw_button("Выход", WIDTH - 120, HEIGHT - 70, 100, 50):
- running = False
- # ------------------ СЦЕНА 1: ДОБАВЛЕНИЕ ПОТОКОВ ------------------
- elif scene == 1:
- info = FONT_SMALL.render("Добавьте минимум 3 потока (случайные параметры)", True, TEXT_COL)
- screen.blit(info, (WIDTH // 2 - info.get_width() // 2, 120))
- if draw_button("+ Добавить поток", WIDTH - 200, HEIGHT - 100, 180, 50):
- new = Thread(next_tid, random.randint(1, 5), random.randint(4, 12))
- scheduler.add_thread(new)
- threads_added.append(new)
- next_tid += 1
- message = f"✅ Поток T{new.tid} (приор.{new.priority}, burst {new.remaining}) добавлен"
- # Список добавленных потоков
- y = 200
- for thr in threads_added:
- txt = FONT_SMALL.render(f"T{thr.tid} | Приоритет: {thr.priority} | Остаток: {thr.remaining}", True, TEXT_COL)
- screen.blit(txt, (60, y))
- pygame.draw.circle(screen, thr.base_color, (40, y + 12), 10)
- y += 45
- if len(threads_added) >= 3:
- if draw_button("▶ Запустить планирование", WIDTH // 2 - 120, HEIGHT - 100, 240, 50):
- scene = 2
- step_count = 0
- interrupt_available = False
- # Сообщение и кнопка назад
- msg_surf = FONT_SMALL.render(message, True, (200, 220, 100))
- screen.blit(msg_surf, (50, HEIGHT - 60))
- if draw_button("Назад", 50, HEIGHT - 60, 100, 45):
- scene = 0
- # ------------------ СЦЕНА 2: ДИСПЕТЧЕРИЗАЦИЯ ------------------
- elif scene == 2:
- draw_queue_animated(scheduler.queue, scheduler.current, pulse)
- if draw_button("⏩ Шаг (квант)", WIDTH - 200, HEIGHT - 100, 170, 55):
- scheduler.step()
- step_count += 1
- if scheduler.all_finished():
- scene = 4
- if step_count >= 4 and not interrupt_available:
- if draw_button("⚠ ПРЕРЫВАНИЕ", WIDTH - 200, HEIGHT - 180, 170, 50, (180, 60, 60), (220, 80, 80)):
- scene = 3
- info_text = FONT_SMALL.render(f"Шагов: {step_count} | Алгоритм: {'RR' if algorithm == 'rr' else 'Priority'}", True, TEXT_COL)
- screen.blit(info_text, (30, HEIGHT - 50))
- if draw_button("Выход", WIDTH - 100, 20, 80, 40):
- running = False
- # ------------------ СЦЕНА 3: ОБРАБОТКА ПРЕРЫВАНИЯ (МОДАЛЬНОЕ ОКНО) ------------------
- elif scene == 3:
- immediate = modal_interrupt(scheduler)
- if immediate:
- if scheduler.current:
- old = scheduler.current.priority
- scheduler.current.priority = max(1, scheduler.current.priority - 2)
- scheduler.interrupts_handled += 1
- message = f"✅ Прерывание обработано! Приоритет T{scheduler.current.tid} повышен с {old} до {scheduler.current.priority}"
- else:
- message = "Прерывание, но нет активного потока"
- else:
- message = "⏸ Прерывание отложено. Эффективность снижена."
- interrupt_available = True
- scene = 2
- # ------------------ СЦЕНА 4: ФИНАЛЬНАЯ СТАТИСТИКА ------------------
- elif scene == 4:
- if scheduler.interrupts_handled > 0:
- grade = "A (Отлично)"
- grade_color = (100, 255, 100)
- elif step_count < 10:
- grade = "B (Хорошо)"
- grade_color = (200, 255, 100)
- elif step_count < 20:
- grade = "C (Удовлетворительно)"
- grade_color = (255, 200, 100)
- else:
- grade = "D (Низкая эффективность)"
- grade_color = (255, 120, 120)
- stats = [
- "📊 Статистика игры",
- f"Алгоритм: {'Round Robin' if algorithm == 'rr' else 'Приоритетное (вытеснение)'}",
- f"Всего шагов (квантов): {step_count}",
- f"Прерываний обработано немедленно: {scheduler.interrupts_handled}",
- f"Потоков завершено: {next_tid - 1}",
- f"Оценка: {grade}"
- ]
- y = 180
- for line in stats:
- if "Оценка:" in line:
- txt = FONT.render(line, True, grade_color)
- else:
- txt = FONT.render(line, True, TEXT_COL)
- screen.blit(txt, (WIDTH // 2 - txt.get_width() // 2, y))
- y += 55
- if draw_button("🔄 Сыграть снова", WIDTH // 2 - 180, HEIGHT - 140, 160, 55):
- scene = 0
- algorithm = None
- scheduler = None
- threads_added = []
- next_tid = 1
- step_count = 0
- interrupt_available = False
- if draw_button("❌ Выход", WIDTH // 2 + 20, HEIGHT - 140, 160, 55):
- running = False
- pygame.display.flip()
- clock.tick(30)
- for event in pygame.event.get():
- if event.type == pygame.QUIT:
- running = False
- pygame.quit()
- sys.exit()
- if __name__ == "__main__":
- main()
|