1
0

Gurkin.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. import pygame
  2. import sys
  3. import random
  4. # Инициализация Pygame
  5. pygame.init()
  6. WIDTH, HEIGHT = 1024, 768
  7. screen = pygame.display.set_mode((WIDTH, HEIGHT))
  8. pygame.display.set_caption("Диспетчер потоков – управление режимами")
  9. clock = pygame.time.Clock()
  10. # Шрифты
  11. FONT_TITLE = pygame.font.Font(None, 48)
  12. FONT = pygame.font.Font(None, 36)
  13. FONT_SMALL = pygame.font.Font(None, 28)
  14. # Цвета
  15. BG = (20, 25, 45)
  16. QUEUE_BG = (35, 45, 65)
  17. TEXT_COL = (255, 255, 255)
  18. BUTTON_COL = (70, 130, 200)
  19. BUTTON_HOVER = (100, 160, 240)
  20. ACTIVE_COL = (255, 80, 80)
  21. MODAL_BG = (0, 0, 0, 200)
  22. # ---------- Класс потока ----------
  23. class Thread:
  24. def __init__(self, tid, priority, burst):
  25. self.tid = tid
  26. self.priority = priority
  27. self.remaining = burst
  28. self.base_color = (random.randint(100, 220), random.randint(100, 220), 255)
  29. self.animation_progress = 0.0 # 0..1 для анимации выезда
  30. def get_color(self, is_active=False, pulse=0):
  31. if is_active:
  32. # Пульсирующий красный
  33. r = ACTIVE_COL[0] + int(pulse * 50)
  34. g = ACTIVE_COL[1] - int(pulse * 30)
  35. b = ACTIVE_COL[2] - int(pulse * 30)
  36. return (min(255, r), max(50, g), max(50, b))
  37. return self.base_color
  38. # ---------- Планировщик ----------
  39. class Scheduler:
  40. def __init__(self, algorithm):
  41. self.algorithm = algorithm # "rr" или "priority"
  42. self.queue = []
  43. self.current = None
  44. self.time_quantum = 3
  45. self.quantum_counter = 0
  46. self.interrupts_handled = 0
  47. self.total_steps = 0
  48. def add_thread(self, thread):
  49. thread.animation_progress = 0.0
  50. self.queue.append(thread)
  51. def step(self):
  52. self.total_steps += 1
  53. if self.current is None:
  54. if not self.queue:
  55. return False
  56. self.current = self.queue.pop(0)
  57. self.quantum_counter = 0
  58. return True
  59. self.current.remaining -= 1
  60. self.quantum_counter += 1
  61. if self.current.remaining <= 0:
  62. self.current = None
  63. self.quantum_counter = 0
  64. elif self.algorithm == "rr" and self.quantum_counter >= self.time_quantum:
  65. self.queue.append(self.current)
  66. self.current = None
  67. self.quantum_counter = 0
  68. elif self.algorithm == "priority":
  69. higher = [t for t in self.queue if t.priority < self.current.priority]
  70. if higher:
  71. self.queue.append(self.current)
  72. self.current = None
  73. self.quantum_counter = 0
  74. return True
  75. def all_finished(self):
  76. return self.current is None and len(self.queue) == 0
  77. # ---------- Вспомогательные функции отрисовки ----------
  78. def draw_button(text, x, y, w, h, color=None, hover_color=None):
  79. if color is None:
  80. color = BUTTON_COL
  81. if hover_color is None:
  82. hover_color = BUTTON_HOVER
  83. mouse = pygame.mouse.get_pos()
  84. click = pygame.mouse.get_pressed()
  85. is_hover = (x < mouse[0] < x + w and y < mouse[1] < y + h)
  86. col = hover_color if is_hover else color
  87. pygame.draw.rect(screen, col, (x, y, w, h), border_radius=12)
  88. pygame.draw.rect(screen, (0, 0, 0), (x + 2, y + 2, w, h), border_radius=12, width=1)
  89. text_surf = FONT_SMALL.render(text, True, TEXT_COL)
  90. screen.blit(text_surf, (x + w // 2 - text_surf.get_width() // 2, y + h // 2 - text_surf.get_height() // 2))
  91. return is_hover and click[0]
  92. def draw_queue_animated(threads, current, pulse):
  93. start_x = 80
  94. start_y = 180
  95. slot_w = 140
  96. slot_h = 80
  97. max_visible = 6
  98. pygame.draw.rect(screen, QUEUE_BG, (50, start_y - 20, WIDTH - 100, 130), border_radius=15)
  99. for i, thr in enumerate(threads[:max_visible]):
  100. # Анимация выезда
  101. if thr.animation_progress < 1.0:
  102. thr.animation_progress = min(1.0, thr.animation_progress + 0.1)
  103. offset = int((1 - thr.animation_progress) * slot_w)
  104. x = start_x + i * slot_w - offset
  105. y = start_y
  106. rect = pygame.Rect(x, y, slot_w - 10, slot_h)
  107. color = thr.get_color(False)
  108. pygame.draw.rect(screen, color, rect, border_radius=10)
  109. pygame.draw.rect(screen, (255, 255, 255), rect, width=2, border_radius=10)
  110. text = FONT_SMALL.render(f"T{thr.tid} P:{thr.priority} {thr.remaining}", True, TEXT_COL)
  111. screen.blit(text, (rect.x + 5, rect.y + 30))
  112. if current:
  113. # Пульсация активного потока
  114. scale = 1 + pulse * 0.05
  115. w = int(160 * scale)
  116. h = int(80 * scale)
  117. x = WIDTH // 2 - w // 2
  118. y = start_y - 70 - int(pulse * 5)
  119. color = current.get_color(True, pulse)
  120. rect = pygame.Rect(x, y, w, h)
  121. pygame.draw.rect(screen, color, rect, border_radius=15)
  122. pygame.draw.rect(screen, (255, 255, 200), rect, width=3, border_radius=15)
  123. text = FONT.render(f"Активен: T{current.tid} (ост.{current.remaining})", True, TEXT_COL)
  124. screen.blit(text, (rect.x + rect.w // 2 - text.get_width() // 2, rect.y + rect.h // 2 - text.get_height() // 2))
  125. def modal_interrupt(scheduler):
  126. """Модальное окно выбора обработки прерывания"""
  127. modal_w, modal_h = 450, 220
  128. modal_x = WIDTH // 2 - modal_w // 2
  129. modal_y = HEIGHT // 2 - modal_h // 2
  130. overlay = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
  131. overlay.fill((0, 0, 0, 180))
  132. screen.blit(overlay, (0, 0))
  133. pygame.draw.rect(screen, (50, 55, 80), (modal_x, modal_y, modal_w, modal_h), border_radius=20)
  134. pygame.draw.rect(screen, (100, 130, 200), (modal_x, modal_y, modal_w, modal_h), width=3, border_radius=20)
  135. title = FONT.render("⚠ Внешнее прерывание!", True, (255, 220, 100))
  136. screen.blit(title, (modal_x + modal_w // 2 - title.get_width() // 2, modal_y + 30))
  137. sub = FONT_SMALL.render("Переход в режим ядра. Что делать?", True, TEXT_COL)
  138. screen.blit(sub, (modal_x + modal_w // 2 - sub.get_width() // 2, modal_y + 70))
  139. btn_imm = draw_button("Обработать немедленно", modal_x + 30, modal_y + 140, 180, 45)
  140. btn_delay = draw_button("Отложить", modal_x + 240, modal_y + 140, 180, 45)
  141. pygame.display.flip()
  142. while True:
  143. for event in pygame.event.get():
  144. if event.type == pygame.QUIT:
  145. pygame.quit()
  146. sys.exit()
  147. if event.type == pygame.MOUSEBUTTONDOWN:
  148. if btn_imm:
  149. return True
  150. if btn_delay:
  151. return False
  152. # Обновляем состояние кнопок
  153. btn_imm = draw_button("Обработать немедленно", modal_x + 30, modal_y + 140, 180, 45)
  154. btn_delay = draw_button("Отложить", modal_x + 240, modal_y + 140, 180, 45)
  155. pygame.display.flip()
  156. clock.tick(30)
  157. # ---------- Основная функция ----------
  158. def main():
  159. scene = 0 # 0-выбор алгоритма, 1-добавление потоков, 2-диспетчеризация, 3-прерывание (модальное), 4-статистика
  160. algorithm = None
  161. scheduler = None
  162. next_tid = 1
  163. message = ""
  164. step_count = 0
  165. interrupt_available = False
  166. threads_added = []
  167. running = True
  168. pulse = 0.0
  169. pulse_dir = 1
  170. while running:
  171. # Пульсация для активного потока
  172. pulse += 0.05 * pulse_dir
  173. if pulse >= 1.0:
  174. pulse = 1.0
  175. pulse_dir = -1
  176. elif pulse <= 0.0:
  177. pulse = 0.0
  178. pulse_dir = 1
  179. screen.fill(BG)
  180. # Заголовок
  181. title = FONT_TITLE.render("⚙ Управление режимом потоков", True, TEXT_COL)
  182. screen.blit(title, (WIDTH // 2 - title.get_width() // 2, 20))
  183. # ------------------ СЦЕНА 0: ВЫБОР АЛГОРИТМА ------------------
  184. if scene == 0:
  185. prompt = FONT.render("Выберите алгоритм планирования:", True, TEXT_COL)
  186. screen.blit(prompt, (WIDTH // 2 - prompt.get_width() // 2, 180))
  187. if draw_button("Round Robin", WIDTH // 2 - 180, 280, 160, 60):
  188. algorithm = "rr"
  189. scheduler = Scheduler("rr")
  190. scene = 1
  191. threads_added = []
  192. next_tid = 1
  193. if draw_button("Приоритетное", WIDTH // 2 + 20, 280, 160, 60):
  194. algorithm = "priority"
  195. scheduler = Scheduler("priority")
  196. scene = 1
  197. threads_added = []
  198. next_tid = 1
  199. if draw_button("Выход", WIDTH - 120, HEIGHT - 70, 100, 50):
  200. running = False
  201. # ------------------ СЦЕНА 1: ДОБАВЛЕНИЕ ПОТОКОВ ------------------
  202. elif scene == 1:
  203. info = FONT_SMALL.render("Добавьте минимум 3 потока (случайные параметры)", True, TEXT_COL)
  204. screen.blit(info, (WIDTH // 2 - info.get_width() // 2, 120))
  205. if draw_button("+ Добавить поток", WIDTH - 200, HEIGHT - 100, 180, 50):
  206. new = Thread(next_tid, random.randint(1, 5), random.randint(4, 12))
  207. scheduler.add_thread(new)
  208. threads_added.append(new)
  209. next_tid += 1
  210. message = f"✅ Поток T{new.tid} (приор.{new.priority}, burst {new.remaining}) добавлен"
  211. # Список добавленных потоков
  212. y = 200
  213. for thr in threads_added:
  214. txt = FONT_SMALL.render(f"T{thr.tid} | Приоритет: {thr.priority} | Остаток: {thr.remaining}", True, TEXT_COL)
  215. screen.blit(txt, (60, y))
  216. pygame.draw.circle(screen, thr.base_color, (40, y + 12), 10)
  217. y += 45
  218. if len(threads_added) >= 3:
  219. if draw_button("▶ Запустить планирование", WIDTH // 2 - 120, HEIGHT - 100, 240, 50):
  220. scene = 2
  221. step_count = 0
  222. interrupt_available = False
  223. # Сообщение и кнопка назад
  224. msg_surf = FONT_SMALL.render(message, True, (200, 220, 100))
  225. screen.blit(msg_surf, (50, HEIGHT - 60))
  226. if draw_button("Назад", 50, HEIGHT - 60, 100, 45):
  227. scene = 0
  228. # ------------------ СЦЕНА 2: ДИСПЕТЧЕРИЗАЦИЯ ------------------
  229. elif scene == 2:
  230. draw_queue_animated(scheduler.queue, scheduler.current, pulse)
  231. if draw_button("⏩ Шаг (квант)", WIDTH - 200, HEIGHT - 100, 170, 55):
  232. scheduler.step()
  233. step_count += 1
  234. if scheduler.all_finished():
  235. scene = 4
  236. if step_count >= 4 and not interrupt_available:
  237. if draw_button("⚠ ПРЕРЫВАНИЕ", WIDTH - 200, HEIGHT - 180, 170, 50, (180, 60, 60), (220, 80, 80)):
  238. scene = 3
  239. info_text = FONT_SMALL.render(f"Шагов: {step_count} | Алгоритм: {'RR' if algorithm == 'rr' else 'Priority'}", True, TEXT_COL)
  240. screen.blit(info_text, (30, HEIGHT - 50))
  241. if draw_button("Выход", WIDTH - 100, 20, 80, 40):
  242. running = False
  243. # ------------------ СЦЕНА 3: ОБРАБОТКА ПРЕРЫВАНИЯ (МОДАЛЬНОЕ ОКНО) ------------------
  244. elif scene == 3:
  245. immediate = modal_interrupt(scheduler)
  246. if immediate:
  247. if scheduler.current:
  248. old = scheduler.current.priority
  249. scheduler.current.priority = max(1, scheduler.current.priority - 2)
  250. scheduler.interrupts_handled += 1
  251. message = f"✅ Прерывание обработано! Приоритет T{scheduler.current.tid} повышен с {old} до {scheduler.current.priority}"
  252. else:
  253. message = "Прерывание, но нет активного потока"
  254. else:
  255. message = "⏸ Прерывание отложено. Эффективность снижена."
  256. interrupt_available = True
  257. scene = 2
  258. # ------------------ СЦЕНА 4: ФИНАЛЬНАЯ СТАТИСТИКА ------------------
  259. elif scene == 4:
  260. if scheduler.interrupts_handled > 0:
  261. grade = "A (Отлично)"
  262. grade_color = (100, 255, 100)
  263. elif step_count < 10:
  264. grade = "B (Хорошо)"
  265. grade_color = (200, 255, 100)
  266. elif step_count < 20:
  267. grade = "C (Удовлетворительно)"
  268. grade_color = (255, 200, 100)
  269. else:
  270. grade = "D (Низкая эффективность)"
  271. grade_color = (255, 120, 120)
  272. stats = [
  273. "📊 Статистика игры",
  274. f"Алгоритм: {'Round Robin' if algorithm == 'rr' else 'Приоритетное (вытеснение)'}",
  275. f"Всего шагов (квантов): {step_count}",
  276. f"Прерываний обработано немедленно: {scheduler.interrupts_handled}",
  277. f"Потоков завершено: {next_tid - 1}",
  278. f"Оценка: {grade}"
  279. ]
  280. y = 180
  281. for line in stats:
  282. if "Оценка:" in line:
  283. txt = FONT.render(line, True, grade_color)
  284. else:
  285. txt = FONT.render(line, True, TEXT_COL)
  286. screen.blit(txt, (WIDTH // 2 - txt.get_width() // 2, y))
  287. y += 55
  288. if draw_button("🔄 Сыграть снова", WIDTH // 2 - 180, HEIGHT - 140, 160, 55):
  289. scene = 0
  290. algorithm = None
  291. scheduler = None
  292. threads_added = []
  293. next_tid = 1
  294. step_count = 0
  295. interrupt_available = False
  296. if draw_button("❌ Выход", WIDTH // 2 + 20, HEIGHT - 140, 160, 55):
  297. running = False
  298. pygame.display.flip()
  299. clock.tick(30)
  300. for event in pygame.event.get():
  301. if event.type == pygame.QUIT:
  302. running = False
  303. pygame.quit()
  304. sys.exit()
  305. if __name__ == "__main__":
  306. main()