diff --git a/5030102_30201/Zhdanov_Dmitriy/__pycache__/stage0.cpython-313.pyc b/5030102_30201/Zhdanov_Dmitriy/__pycache__/stage0.cpython-313.pyc new file mode 100644 index 0000000..680b3c1 Binary files /dev/null and b/5030102_30201/Zhdanov_Dmitriy/__pycache__/stage0.cpython-313.pyc differ diff --git a/5030102_30201/Zhdanov_Dmitriy/mapping_stage0.yaml b/5030102_30201/Zhdanov_Dmitriy/mapping_stage0.yaml new file mode 100644 index 0000000..577916e --- /dev/null +++ b/5030102_30201/Zhdanov_Dmitriy/mapping_stage0.yaml @@ -0,0 +1,69 @@ +классы: + - РоботЛаборант: RobotLabAssistant + свойства: + - лабиринт: labyrinth + методы: + - ПодойтиКПробирке: move_to_test_tube + - ОтойтиОтСтола: move_back_from_table + - СместитьсяВлево: move_left + - СместитьсяВправо: move_right + - Подняться: move_up + - Спуститься: move_down + - Пробирка: handle_test_tube + - Пусто: handle_empty + + - ЛабиринтРоботЛаборант: LabyrinthRobot + свойства: + - ширина: width + - длина: height + - ячейки: cells + методы: + - ПолучитьСоседнююЯчейку: get_neighbor_cell + - ПолучитьИтератор: get_iterator + - ИнициализироватьЛабиринт: initialize_labyrinth + + - ЯчейкаРоботЛаборант: RobotCell + свойства: + - ячейка_робота: is_robot + - тип_ячейки: cell_type + методы: [] + + - СторонаНаправление: DirectionSide + свойства: + - сторона: side + - направление: direction + методы: [] + + - МетодНаправление: MethodDirection + свойства: + - метод: method + - направление: direction + методы: [] +перечисления: + - ТипЯчеекЛаборатории: CellType + опции: + - Пусто: EMPTY + - Пробирка: TEST_TUBE + - Проанализировано: ANALYZED + - Реагент: REAGENT + - Барьер: BARRIER + - Мусор: TRASH + - Финиш: FINISH + + - БазовыеТипыНаправления: BaseDirection + опции: + - С: N + - Ю: S + - З: W + - В: E + - СЗ: NW + - ЮВ: SE + + - ТипНаправленийЛабНаправлений: LabDirection + опции: + - ВзятьВперёд: FORWARD + - ВернутьсяНазад: BACKWARD + - СместитьсяВлево: LEFT + - СместитьсяВправо: RIGHT + - КУглуВверх: UP_CORNER + - КУглуВниз: DOWN_CORNER \ No newline at end of file diff --git a/5030102_30201/Zhdanov_Dmitriy/robot_ui_stage1.mp4 b/5030102_30201/Zhdanov_Dmitriy/robot_ui_stage1.mp4 new file mode 100644 index 0000000..4bccf0e Binary files /dev/null and b/5030102_30201/Zhdanov_Dmitriy/robot_ui_stage1.mp4 differ diff --git a/5030102_30201/Zhdanov_Dmitriy/robot_ui_stage1.py b/5030102_30201/Zhdanov_Dmitriy/robot_ui_stage1.py new file mode 100644 index 0000000..b530b06 --- /dev/null +++ b/5030102_30201/Zhdanov_Dmitriy/robot_ui_stage1.py @@ -0,0 +1,256 @@ +import tkinter as tk +from tkinter import messagebox +import time + +from stage0 import ( + LabyrinthRobot, + RobotLabAssistant, + CellType, +) + +CELL_COLORS = { + CellType.EMPTY: "#ffffff", + CellType.TEST_TUBE: "#ffd966", + CellType.ANALYZED: "#93c47d", + CellType.REAGENT: "#9fc5e8", + CellType.BARRIER: "#333333", + CellType.TRASH: "#6d6d6d", + CellType.FINISH: "#76d7c4", +} + +CELL_ORDER = [ + CellType.EMPTY, + CellType.TEST_TUBE, + CellType.ANALYZED, + CellType.REAGENT, + CellType.BARRIER, + CellType.TRASH, + CellType.FINISH, +] + + +class RobotLabUI: + def __init__(self, root): + self.root = root + self.root.title("Робот Лаборант") + + self.width = 7 + self.height = 7 + + self.lab = LabyrinthRobot(self.width, self.height) + self.robot = RobotLabAssistant(self.lab) + + layout = { + (2, 2): CellType.TEST_TUBE, + (2, 3): CellType.TEST_TUBE, + (2, 4): CellType.TEST_TUBE, + + (3, 2): CellType.REAGENT, + (3, 3): CellType.REAGENT, + (3, 4): CellType.REAGENT, + + (4, 2): CellType.BARRIER, + (4, 3): CellType.BARRIER, + (4, 4): CellType.BARRIER, + (3, 5): CellType.BARRIER, + (4, 5): CellType.BARRIER, + + (2, 5): CellType.TRASH, + (1, 5): CellType.TRASH, + + (6, 6): CellType.FINISH, + } + + + for (x, y), t in layout.items(): + self.lab.cells[y][x].cell_type = t + + start_cell = self.lab.cells[0][0] + start_cell.is_robot = True + self.robot.current_cell = start_cell + + self._snapshot = self._take_snapshot() + + self._build_ui() + self.update_grid() + + def _build_ui(self): + self.frame_grid = tk.Frame(self.root) + self.frame_grid.grid(row=0, column=0, padx=10, pady=10) + + self.buttons = [[None for _ in range(self.width)] for _ in range(self.height)] + + for y in range(self.height): + ui_row = self.height - 1 - y + for x in range(self.width): + b = tk.Button( + self.frame_grid, + width=10, + height=4, + command=lambda xx=x, yy=y: self.on_cell_click(xx, yy), + ) + b.grid(row=ui_row, column=x, padx=2, pady=2) + self.buttons[y][x] = b + + self.frame_right = tk.Frame(self.root) + self.frame_right.grid(row=0, column=1, sticky="n", padx=10, pady=10) + + tk.Label( + self.frame_right, + text="Программа (1 команда = 1 строка):" + ).pack(anchor="w") + + self.txt_program = tk.Text(self.frame_right, width=36, height=18) + self.txt_program.pack() + + self.txt_program.insert( + "1.0", + "move_to_test_tube\n" + "move_to_test_tube\n" + "\n" + "move_right\n" + "move_right\n" + "handle_test_tube\n" + "\n" + "move_to_test_tube\n" + "handle_test_tube\n" + "\n" + "move_to_test_tube\n" + "handle_test_tube\n" + "\n" + "move_left\n" + "move_left\n" + "\n" + "move_to_test_tube\n" + "move_to_test_tube\n" + "move_right\n" + "move_right\n" + "move_right\n" + "move_right\n" + "move_right\n" + "move_right\n" + ) + + self.btn_execute = tk.Button( + self.frame_right, + text="Выполнить", + command=self.on_execute + ) + self.btn_execute.pack(fill="x", pady=(8, 4)) + + self.btn_reset = tk.Button( + self.frame_right, + text="Сброс", + command=self.on_reset + ) + self.btn_reset.pack(fill="x") + + tk.Label( + self.frame_right, + text=( + "Команды движения:\n" + "move_to_test_tube — вверх (север)\n" + "move_back_from_table — вниз (юг)\n" + "move_left — влево (запад)\n" + "move_right — вправо (восток)\n" + "move_up — вверх-влево (северо-запад)\n" + "move_down — вниз-вправо (юго-восток)\n\n" + "Команды действия:\n" + "handle_test_tube — Пробирка → Проанализировано\n" + "handle_empty — Пусто → Пробирка" + ), + justify="left" + ).pack(anchor="w", pady=(8, 0)) + + def _take_snapshot(self): + types = {(c.x, c.y): c.cell_type for c in self.lab.all_cells()} + cur = self.robot.current_cell + robot_pos = (cur.x, cur.y) + return types, robot_pos + + def on_reset(self): + types, robot_pos = self._snapshot + for (x, y), t in types.items(): + cell = self.lab.cells[y][x] + cell.cell_type = t + cell.is_robot = False + + rx, ry = robot_pos + self.lab.cells[ry][rx].is_robot = True + self.robot.current_cell = self.lab.cells[ry][rx] + + self.update_grid() + + def on_cell_click(self, x, y): + cell = self.lab.cells[y][x] + idx = CELL_ORDER.index(cell.cell_type) + cell.cell_type = CELL_ORDER[(idx + 1) % len(CELL_ORDER)] + self.update_grid() + + def update_grid(self): + for y in range(self.height): + for x in range(self.width): + cell = self.lab.cells[y][x] + b = self.buttons[y][x] + + text = cell.cell_type.name + if cell.is_robot: + text = "🤖\n" + text + + b.config(text=text, bg=CELL_COLORS[cell.cell_type]) + + def on_execute(self): + program_text = self.txt_program.get("1.0", "end").strip() + commands = [ + c.strip() for c in program_text.splitlines() + if c.strip() and not c.startswith("#") + ] + + self.btn_execute.config(state="disabled") + self.btn_reset.config(state="disabled") + + try: + for cmd in commands: + if not self.execute_command(cmd): + messagebox.showerror("Ошибка", f"Команда не выполнена: {cmd}") + return + + self.update_grid() + self.root.update() + time.sleep(0.15) + + messagebox.showinfo("Готово", "Программа выполнена") + + finally: + self.btn_execute.config(state="normal") + self.btn_reset.config(state="normal") + + def execute_command(self, cmd: str): + r = self.robot + + if cmd == "move_to_test_tube": + return r.move_to_test_tube() + elif cmd == "move_back_from_table": + return r.move_back_from_table() + elif cmd == "move_left": + return r.move_left() + elif cmd == "move_right": + return r.move_right() + elif cmd == "move_up": + return r.move_up() + elif cmd == "move_down": + return r.move_down() + elif cmd == "handle_test_tube": + r.handle_test_tube() + return True + elif cmd == "handle_empty": + r.handle_empty() + return True + else: + return False + + +if __name__ == "__main__": + root = tk.Tk() + app = RobotLabUI(root) + root.mainloop() diff --git a/5030102_30201/Zhdanov_Dmitriy/stage0.py b/5030102_30201/Zhdanov_Dmitriy/stage0.py new file mode 100644 index 0000000..16b80b8 --- /dev/null +++ b/5030102_30201/Zhdanov_Dmitriy/stage0.py @@ -0,0 +1,234 @@ +from __future__ import annotations +from dataclasses import dataclass +from enum import Enum +from typing import List, Optional, Iterator, Tuple +from collections import deque + +class CellType(Enum): + EMPTY = "empty" + TEST_TUBE = "test_tube" + ANALYZED = "analyzed" + REAGENT = "reagent" + BARRIER = "barrier" + TRASH = "trash" + FINISH = "finish" + +class LabDirection(Enum): + FORWARD = "forward" # North + BACKWARD = "backward" # South + LEFT = "left" # West + RIGHT = "right" # East + UP_CORNER = "up_corner" # NW + DOWN_CORNER = "down_corner" # SE + +DIRECTION_DELTAS = { + LabDirection.FORWARD.value: (0, 1), + LabDirection.BACKWARD.value: (0, -1), + LabDirection.LEFT.value: (-1, 0), + LabDirection.RIGHT.value: (1, 0), + LabDirection.UP_CORNER.value: (-1, 1), + LabDirection.DOWN_CORNER.value: (1, -1), +} + +@dataclass +class RobotCell: + is_robot: bool + cell_type: CellType + x: int = 0 + y: int = 0 + + def __repr__(self) -> str: + return f"" + +class LabyrinthRobot: + def __init__(self, width: int, height: int, + cells: Optional[List[List[RobotCell]]] = None): + self.width = width + self.height = height + if cells is None: + self.cells: List[List[RobotCell]] = [ + [RobotCell(False, CellType.EMPTY, x, y) for x in range(width)] + for y in range(height) + ] + else: + self.cells = cells + for y, row in enumerate(self.cells): + for x, cell in enumerate(row): + cell.x = x + cell.y = y + + def get_neighbor_cell(self, current_cell: RobotCell, direction_value: str) -> Optional[RobotCell]: + dx, dy = DIRECTION_DELTAS[direction_value] + nx, ny = current_cell.x + dx, current_cell.y + dy + if 0 <= nx < self.width and 0 <= ny < self.height: + return self.cells[ny][nx] + return None + + def get_iterator(self) -> Iterator[RobotCell]: + return SnakeIteratorLab(self) + + def initialize_labyrinth(self, default_cell_type: CellType) -> None: + for row in self.cells: + for cell in row: + cell.cell_type = default_cell_type + cell.is_robot = False + + def all_cells(self) -> List[RobotCell]: + return [cell for row in self.cells for cell in row] + +class SnakeIteratorLab: + def __init__(self, maze: LabyrinthRobot): + self.maze = maze + self.x = 0 + self.y = 0 + self.moving_right = True + self._finished = False + + def __iter__(self) -> "SnakeIteratorLab": + return self + + def __next__(self) -> RobotCell: + if self._finished: + raise StopIteration + + cell = self.maze.cells[self.y][self.x] + + if self.moving_right: + if self.x < self.maze.width - 1: + self.x += 1 + else: + if self.y == self.maze.height - 1: + self._finished = True + else: + self.y += 1 + self.moving_right = False + else: + if self.x > 0: + self.x -= 1 + else: + if self.y == self.maze.height - 1: + self._finished = True + else: + self.y += 1 + self.moving_right = True + + return cell + +class RobotLabAssistant: + def __init__(self, labyrinth: LabyrinthRobot): + self.labyrinth = labyrinth + self.current_cell: RobotCell = self._find_robot_cell() + + def _find_robot_cell(self) -> RobotCell: + for row in self.labyrinth.cells: + for cell in row: + if cell.is_robot: + return cell + cell = self.labyrinth.cells[0][0] + cell.is_robot = True + return cell + + def _can_enter(self, cell: RobotCell) -> bool: + return cell.cell_type not in {CellType.BARRIER, CellType.TRASH} + + def _step(self, direction: LabDirection) -> Optional[RobotCell]: + neighbor = self.labyrinth.get_neighbor_cell(self.current_cell, direction.value) + if neighbor is None or not self._can_enter(neighbor): + return None + self.current_cell.is_robot = False + neighbor.is_robot = True + self.current_cell = neighbor + return neighbor + + def move_to_test_tube(self) -> Optional[RobotCell]: + return self._step(LabDirection.FORWARD) + + def move_back_from_table(self) -> Optional[RobotCell]: + return self._step(LabDirection.BACKWARD) + + def move_left(self) -> Optional[RobotCell]: + return self._step(LabDirection.LEFT) + + def move_right(self) -> Optional[RobotCell]: + return self._step(LabDirection.RIGHT) + + def move_up(self) -> Optional[RobotCell]: + return self._step(LabDirection.UP_CORNER) + + def move_down(self) -> Optional[RobotCell]: + return self._step(LabDirection.DOWN_CORNER) + + def handle_test_tube(self) -> None: + if self.current_cell.cell_type == CellType.TEST_TUBE: + self.current_cell.cell_type = CellType.ANALYZED + + def handle_empty(self) -> None: + if self.current_cell.cell_type == CellType.EMPTY: + self.current_cell.cell_type = CellType.TEST_TUBE + + def _find_path_to(self, target: RobotCell) -> Optional[List[LabDirection]]: + start = (self.current_cell.x, self.current_cell.y) + goal = (target.x, target.y) + if start == goal: + return [] + + directions = list(LabDirection) + visited = set() + q = deque() + q.append((start, [])) + visited.add(start) + + while q: + (cx, cy), path = q.popleft() + for d in directions: + dx, dy = DIRECTION_DELTAS[d.value] + nx, ny = cx + dx, cy + dy + if not (0 <= nx < self.labyrinth.width and 0 <= ny < self.labyrinth.height): + continue + cell = self.labyrinth.cells[ny][nx] + if (nx, ny) != goal and not self._can_enter(cell): + continue + pos = (nx, ny) + if pos in visited: + continue + new_path = path + [d] + if pos == goal: + return new_path + visited.add(pos) + q.append((pos, new_path)) + return None + + def _follow_path(self, path: List[LabDirection]) -> bool: + for d in path: + moved = self._step(d) + if moved is None: + return False + return True + + def _process_current_cell(self) -> None: + if self.current_cell.cell_type == CellType.TEST_TUBE: + self.handle_test_tube() + elif self.current_cell.cell_type == CellType.EMPTY: + self.handle_empty() + + def run_full_experiment(self) -> None: + for cell in self.labyrinth.get_iterator(): + if cell.cell_type not in (CellType.TEST_TUBE, CellType.EMPTY): + continue + path = self._find_path_to(cell) + if path is None: + continue + if not self._follow_path(path): + continue + self._process_current_cell() + + finish_cell: Optional[RobotCell] = None + for c in self.labyrinth.all_cells(): + if c.cell_type == CellType.FINISH: + finish_cell = c + break + if finish_cell is None: + return + path_to_finish = self._find_path_to(finish_cell) + if path_to_finish: + self._follow_path(path_to_finish)