diff --git a/.gitignore b/.gitignore
index 2c6eb3893..5ff6309b7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,38 @@
-/.metadata
-/robots/.settings
-/robots/bin
-eclipse.bat
\ No newline at end of file
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### IntelliJ IDEA ###
+.idea/modules.xml
+.idea/jarRepositories.xml
+.idea/compiler.xml
+.idea/libraries/
+*.iws
+*.iml
+*.ipr
+
+### Eclipse ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
+
+### Mac OS ###
+.DS_Store
\ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100644
index 000000000..aa00ffab7
--- /dev/null
+++ b/.idea/encodings.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 000000000..9dc782bb2
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 000000000..35eb1ddfb
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
index d79735aee..d0639160a 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,10 @@
-# Robots
-The project to learn OO design concepts and MDI application development in Java
+task1
+1. Научиться собирать проект с использованием Apache Maven.
+2. Провести рефакторинг и исправить баг (см. комментарии в коде).
+3. Исправить баг убегания робота за границу окна
+
+task2
+1. Добавить окно с подтверждением на закрытие любого из окон.
+2. Добавить пункт меню ""Выйти"" (с обработкой аналогичной кнопке закрытия приложения).
+3. Добавить локализацию приложения (пункты меню, кнопки и т.д.).
+4. По умолчанию выбирается язык системы, но он может быть изменён через меню.
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 000000000..0e8c7fbcf
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,38 @@
+
+
+ 4.0.0
+
+ org.example
+ Robots
+ 1.0-SNAPSHOT
+
+
+ 20
+ 20
+ UTF-8
+
+
+
+
+ junit
+ junit
+ 4.12
+ test
+
+
+ junit
+ junit
+ 4.13.2
+ compile
+
+
+ org.junit.jupiter
+ junit-jupiter
+ RELEASE
+ compile
+
+
+
+
\ No newline at end of file
diff --git a/robots/.classpath b/robots/.classpath
deleted file mode 100644
index fceb4801b..000000000
--- a/robots/.classpath
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
diff --git a/robots/.gitignore b/robots/.gitignore
deleted file mode 100644
index 2757ffa76..000000000
--- a/robots/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-/bin/
-/.settings/
diff --git a/robots/.project b/robots/.project
deleted file mode 100644
index 78e165663..000000000
--- a/robots/.project
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
- Robots
-
-
-
-
-
- org.eclipse.jdt.core.javabuilder
-
-
-
-
-
- org.eclipse.jdt.core.javanature
-
-
diff --git a/robots/src/gui/GameVisualizer.java b/robots/src/gui/GameVisualizer.java
deleted file mode 100644
index f82cfd8f8..000000000
--- a/robots/src/gui/GameVisualizer.java
+++ /dev/null
@@ -1,210 +0,0 @@
-package gui;
-
-import java.awt.Color;
-import java.awt.EventQueue;
-import java.awt.Graphics;
-import java.awt.Graphics2D;
-import java.awt.Point;
-import java.awt.event.MouseAdapter;
-import java.awt.event.MouseEvent;
-import java.awt.geom.AffineTransform;
-import java.util.Timer;
-import java.util.TimerTask;
-
-import javax.swing.JPanel;
-
-public class GameVisualizer extends JPanel
-{
- private final Timer m_timer = initTimer();
-
- private static Timer initTimer()
- {
- Timer timer = new Timer("events generator", true);
- return timer;
- }
-
- private volatile double m_robotPositionX = 100;
- private volatile double m_robotPositionY = 100;
- private volatile double m_robotDirection = 0;
-
- private volatile int m_targetPositionX = 150;
- private volatile int m_targetPositionY = 100;
-
- private static final double maxVelocity = 0.1;
- private static final double maxAngularVelocity = 0.001;
-
- public GameVisualizer()
- {
- m_timer.schedule(new TimerTask()
- {
- @Override
- public void run()
- {
- onRedrawEvent();
- }
- }, 0, 50);
- m_timer.schedule(new TimerTask()
- {
- @Override
- public void run()
- {
- onModelUpdateEvent();
- }
- }, 0, 10);
- addMouseListener(new MouseAdapter()
- {
- @Override
- public void mouseClicked(MouseEvent e)
- {
- setTargetPosition(e.getPoint());
- repaint();
- }
- });
- setDoubleBuffered(true);
- }
-
- protected void setTargetPosition(Point p)
- {
- m_targetPositionX = p.x;
- m_targetPositionY = p.y;
- }
-
- protected void onRedrawEvent()
- {
- EventQueue.invokeLater(this::repaint);
- }
-
- private static double distance(double x1, double y1, double x2, double y2)
- {
- double diffX = x1 - x2;
- double diffY = y1 - y2;
- return Math.sqrt(diffX * diffX + diffY * diffY);
- }
-
- private static double angleTo(double fromX, double fromY, double toX, double toY)
- {
- double diffX = toX - fromX;
- double diffY = toY - fromY;
-
- return asNormalizedRadians(Math.atan2(diffY, diffX));
- }
-
- protected void onModelUpdateEvent()
- {
- double distance = distance(m_targetPositionX, m_targetPositionY,
- m_robotPositionX, m_robotPositionY);
- if (distance < 0.5)
- {
- return;
- }
- double velocity = maxVelocity;
- double angleToTarget = angleTo(m_robotPositionX, m_robotPositionY, m_targetPositionX, m_targetPositionY);
- double angularVelocity = 0;
- if (angleToTarget > m_robotDirection)
- {
- angularVelocity = maxAngularVelocity;
- }
- if (angleToTarget < m_robotDirection)
- {
- angularVelocity = -maxAngularVelocity;
- }
-
- moveRobot(velocity, angularVelocity, 10);
- }
-
- private static double applyLimits(double value, double min, double max)
- {
- if (value < min)
- return min;
- if (value > max)
- return max;
- return value;
- }
-
- private void moveRobot(double velocity, double angularVelocity, double duration)
- {
- velocity = applyLimits(velocity, 0, maxVelocity);
- angularVelocity = applyLimits(angularVelocity, -maxAngularVelocity, maxAngularVelocity);
- double newX = m_robotPositionX + velocity / angularVelocity *
- (Math.sin(m_robotDirection + angularVelocity * duration) -
- Math.sin(m_robotDirection));
- if (!Double.isFinite(newX))
- {
- newX = m_robotPositionX + velocity * duration * Math.cos(m_robotDirection);
- }
- double newY = m_robotPositionY - velocity / angularVelocity *
- (Math.cos(m_robotDirection + angularVelocity * duration) -
- Math.cos(m_robotDirection));
- if (!Double.isFinite(newY))
- {
- newY = m_robotPositionY + velocity * duration * Math.sin(m_robotDirection);
- }
- m_robotPositionX = newX;
- m_robotPositionY = newY;
- double newDirection = asNormalizedRadians(m_robotDirection + angularVelocity * duration);
- m_robotDirection = newDirection;
- }
-
- private static double asNormalizedRadians(double angle)
- {
- while (angle < 0)
- {
- angle += 2*Math.PI;
- }
- while (angle >= 2*Math.PI)
- {
- angle -= 2*Math.PI;
- }
- return angle;
- }
-
- private static int round(double value)
- {
- return (int)(value + 0.5);
- }
-
- @Override
- public void paint(Graphics g)
- {
- super.paint(g);
- Graphics2D g2d = (Graphics2D)g;
- drawRobot(g2d, round(m_robotPositionX), round(m_robotPositionY), m_robotDirection);
- drawTarget(g2d, m_targetPositionX, m_targetPositionY);
- }
-
- private static void fillOval(Graphics g, int centerX, int centerY, int diam1, int diam2)
- {
- g.fillOval(centerX - diam1 / 2, centerY - diam2 / 2, diam1, diam2);
- }
-
- private static void drawOval(Graphics g, int centerX, int centerY, int diam1, int diam2)
- {
- g.drawOval(centerX - diam1 / 2, centerY - diam2 / 2, diam1, diam2);
- }
-
- private void drawRobot(Graphics2D g, int x, int y, double direction)
- {
- int robotCenterX = round(m_robotPositionX);
- int robotCenterY = round(m_robotPositionY);
- AffineTransform t = AffineTransform.getRotateInstance(direction, robotCenterX, robotCenterY);
- g.setTransform(t);
- g.setColor(Color.MAGENTA);
- fillOval(g, robotCenterX, robotCenterY, 30, 10);
- g.setColor(Color.BLACK);
- drawOval(g, robotCenterX, robotCenterY, 30, 10);
- g.setColor(Color.WHITE);
- fillOval(g, robotCenterX + 10, robotCenterY, 5, 5);
- g.setColor(Color.BLACK);
- drawOval(g, robotCenterX + 10, robotCenterY, 5, 5);
- }
-
- private void drawTarget(Graphics2D g, int x, int y)
- {
- AffineTransform t = AffineTransform.getRotateInstance(0, 0, 0);
- g.setTransform(t);
- g.setColor(Color.GREEN);
- fillOval(g, x, y, 5, 5);
- g.setColor(Color.BLACK);
- drawOval(g, x, y, 5, 5);
- }
-}
diff --git a/robots/src/gui/GameWindow.java b/robots/src/gui/GameWindow.java
deleted file mode 100644
index ecb63c00f..000000000
--- a/robots/src/gui/GameWindow.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package gui;
-
-import java.awt.BorderLayout;
-
-import javax.swing.JInternalFrame;
-import javax.swing.JPanel;
-
-public class GameWindow extends JInternalFrame
-{
- private final GameVisualizer m_visualizer;
- public GameWindow()
- {
- super("Игровое поле", true, true, true, true);
- m_visualizer = new GameVisualizer();
- JPanel panel = new JPanel(new BorderLayout());
- panel.add(m_visualizer, BorderLayout.CENTER);
- getContentPane().add(panel);
- pack();
- }
-}
diff --git a/robots/src/gui/MainApplicationFrame.java b/robots/src/gui/MainApplicationFrame.java
deleted file mode 100644
index 62e943ee1..000000000
--- a/robots/src/gui/MainApplicationFrame.java
+++ /dev/null
@@ -1,156 +0,0 @@
-package gui;
-
-import java.awt.Dimension;
-import java.awt.Toolkit;
-import java.awt.event.KeyEvent;
-
-import javax.swing.JDesktopPane;
-import javax.swing.JFrame;
-import javax.swing.JInternalFrame;
-import javax.swing.JMenu;
-import javax.swing.JMenuBar;
-import javax.swing.JMenuItem;
-import javax.swing.SwingUtilities;
-import javax.swing.UIManager;
-import javax.swing.UnsupportedLookAndFeelException;
-
-import log.Logger;
-
-/**
- * Что требуется сделать:
- * 1. Метод создания меню перегружен функционалом и трудно читается.
- * Следует разделить его на серию более простых методов (или вообще выделить отдельный класс).
- *
- */
-public class MainApplicationFrame extends JFrame
-{
- private final JDesktopPane desktopPane = new JDesktopPane();
-
- public MainApplicationFrame() {
- //Make the big window be indented 50 pixels from each edge
- //of the screen.
- int inset = 50;
- Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
- setBounds(inset, inset,
- screenSize.width - inset*2,
- screenSize.height - inset*2);
-
- setContentPane(desktopPane);
-
-
- LogWindow logWindow = createLogWindow();
- addWindow(logWindow);
-
- GameWindow gameWindow = new GameWindow();
- gameWindow.setSize(400, 400);
- addWindow(gameWindow);
-
- setJMenuBar(generateMenuBar());
- setDefaultCloseOperation(EXIT_ON_CLOSE);
- }
-
- protected LogWindow createLogWindow()
- {
- LogWindow logWindow = new LogWindow(Logger.getDefaultLogSource());
- logWindow.setLocation(10,10);
- logWindow.setSize(300, 800);
- setMinimumSize(logWindow.getSize());
- logWindow.pack();
- Logger.debug("Протокол работает");
- return logWindow;
- }
-
- protected void addWindow(JInternalFrame frame)
- {
- desktopPane.add(frame);
- frame.setVisible(true);
- }
-
-// protected JMenuBar createMenuBar() {
-// JMenuBar menuBar = new JMenuBar();
-//
-// //Set up the lone menu.
-// JMenu menu = new JMenu("Document");
-// menu.setMnemonic(KeyEvent.VK_D);
-// menuBar.add(menu);
-//
-// //Set up the first menu item.
-// JMenuItem menuItem = new JMenuItem("New");
-// menuItem.setMnemonic(KeyEvent.VK_N);
-// menuItem.setAccelerator(KeyStroke.getKeyStroke(
-// KeyEvent.VK_N, ActionEvent.ALT_MASK));
-// menuItem.setActionCommand("new");
-//// menuItem.addActionListener(this);
-// menu.add(menuItem);
-//
-// //Set up the second menu item.
-// menuItem = new JMenuItem("Quit");
-// menuItem.setMnemonic(KeyEvent.VK_Q);
-// menuItem.setAccelerator(KeyStroke.getKeyStroke(
-// KeyEvent.VK_Q, ActionEvent.ALT_MASK));
-// menuItem.setActionCommand("quit");
-//// menuItem.addActionListener(this);
-// menu.add(menuItem);
-//
-// return menuBar;
-// }
-
- private JMenuBar generateMenuBar()
- {
- JMenuBar menuBar = new JMenuBar();
-
- JMenu lookAndFeelMenu = new JMenu("Режим отображения");
- lookAndFeelMenu.setMnemonic(KeyEvent.VK_V);
- lookAndFeelMenu.getAccessibleContext().setAccessibleDescription(
- "Управление режимом отображения приложения");
-
- {
- JMenuItem systemLookAndFeel = new JMenuItem("Системная схема", KeyEvent.VK_S);
- systemLookAndFeel.addActionListener((event) -> {
- setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
- this.invalidate();
- });
- lookAndFeelMenu.add(systemLookAndFeel);
- }
-
- {
- JMenuItem crossplatformLookAndFeel = new JMenuItem("Универсальная схема", KeyEvent.VK_S);
- crossplatformLookAndFeel.addActionListener((event) -> {
- setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
- this.invalidate();
- });
- lookAndFeelMenu.add(crossplatformLookAndFeel);
- }
-
- JMenu testMenu = new JMenu("Тесты");
- testMenu.setMnemonic(KeyEvent.VK_T);
- testMenu.getAccessibleContext().setAccessibleDescription(
- "Тестовые команды");
-
- {
- JMenuItem addLogMessageItem = new JMenuItem("Сообщение в лог", KeyEvent.VK_S);
- addLogMessageItem.addActionListener((event) -> {
- Logger.debug("Новая строка");
- });
- testMenu.add(addLogMessageItem);
- }
-
- menuBar.add(lookAndFeelMenu);
- menuBar.add(testMenu);
- return menuBar;
- }
-
- private void setLookAndFeel(String className)
- {
- try
- {
- UIManager.setLookAndFeel(className);
- SwingUtilities.updateComponentTreeUI(this);
- }
- catch (ClassNotFoundException | InstantiationException
- | IllegalAccessException | UnsupportedLookAndFeelException e)
- {
- // just ignore
- }
- }
-}
diff --git a/robots/src/log/LogWindowSource.java b/robots/src/log/LogWindowSource.java
deleted file mode 100644
index ca0ce4426..000000000
--- a/robots/src/log/LogWindowSource.java
+++ /dev/null
@@ -1,89 +0,0 @@
-package log;
-
-import java.util.ArrayList;
-import java.util.Collections;
-
-/**
- * Что починить:
- * 1. Этот класс порождает утечку ресурсов (связанные слушатели оказываются
- * удерживаемыми в памяти)
- * 2. Этот класс хранит активные сообщения лога, но в такой реализации он
- * их лишь накапливает. Надо же, чтобы количество сообщений в логе было ограничено
- * величиной m_iQueueLength (т.е. реально нужна очередь сообщений
- * ограниченного размера)
- */
-public class LogWindowSource
-{
- private int m_iQueueLength;
-
- private ArrayList m_messages;
- private final ArrayList m_listeners;
- private volatile LogChangeListener[] m_activeListeners;
-
- public LogWindowSource(int iQueueLength)
- {
- m_iQueueLength = iQueueLength;
- m_messages = new ArrayList(iQueueLength);
- m_listeners = new ArrayList();
- }
-
- public void registerListener(LogChangeListener listener)
- {
- synchronized(m_listeners)
- {
- m_listeners.add(listener);
- m_activeListeners = null;
- }
- }
-
- public void unregisterListener(LogChangeListener listener)
- {
- synchronized(m_listeners)
- {
- m_listeners.remove(listener);
- m_activeListeners = null;
- }
- }
-
- public void append(LogLevel logLevel, String strMessage)
- {
- LogEntry entry = new LogEntry(logLevel, strMessage);
- m_messages.add(entry);
- LogChangeListener [] activeListeners = m_activeListeners;
- if (activeListeners == null)
- {
- synchronized (m_listeners)
- {
- if (m_activeListeners == null)
- {
- activeListeners = m_listeners.toArray(new LogChangeListener [0]);
- m_activeListeners = activeListeners;
- }
- }
- }
- for (LogChangeListener listener : activeListeners)
- {
- listener.onLogChanged();
- }
- }
-
- public int size()
- {
- return m_messages.size();
- }
-
- public Iterable range(int startFrom, int count)
- {
- if (startFrom < 0 || startFrom >= m_messages.size())
- {
- return Collections.emptyList();
- }
- int indexTo = Math.min(startFrom + count, m_messages.size());
- return m_messages.subList(startFrom, indexTo);
- }
-
- public Iterable all()
- {
- return m_messages;
- }
-}
diff --git a/src/main/java/org/example/Test/LogWindowSourceTest.java b/src/main/java/org/example/Test/LogWindowSourceTest.java
new file mode 100644
index 000000000..e0b3da1c5
--- /dev/null
+++ b/src/main/java/org/example/Test/LogWindowSourceTest.java
@@ -0,0 +1,63 @@
+package Test;
+import log.LogWindowSource;
+import log.LogLevel;
+import log.LogEntry;
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.*;
+
+public class LogWindowSourceTest {
+
+ @Test
+ public void testLogAppending() {
+ LogWindowSource logWindow = new LogWindowSource(5); // Создаем лог с ограничением в 5 сообщений
+ logWindow.append(LogLevel.Info, "Message 1");
+ logWindow.append(LogLevel.Warning, "Message 2");
+ logWindow.append(LogLevel.Error, "Message 3");
+
+ assertEquals(3, logWindow.size()); // Проверяем, что размер лога равен 3 после добавления 3 сообщений
+ }
+
+ @Test
+ public void testLogLimit() {
+ LogWindowSource logWindow = new LogWindowSource(3); // Создаем лог с ограничением в 3 сообщения
+ logWindow.append(LogLevel.Info, "Message 1");
+ logWindow.append(LogLevel.Warning, "Message 2");
+ logWindow.append(LogLevel.Error, "Message 3");
+ logWindow.append(LogLevel.Debug, "Message 4"); // Превышаем лимит, первое сообщение должно быть удалено
+
+ assertEquals(3, logWindow.size()); // Проверяем, что размер лога равен 3 после добавления 4 сообщений
+ assertEquals("Message 2", logWindow.all().iterator().next().getMessage()); // Проверяем, что первое сообщение было удалено
+ }
+
+ @Test
+ public void testLogRange() {
+ LogWindowSource logWindow = new LogWindowSource(5); // Создаем лог с ограничением в 5 сообщений
+ logWindow.append(LogLevel.Info, "Message 1");
+ logWindow.append(LogLevel.Warning, "Message 2");
+ logWindow.append(LogLevel.Error, "Message 3");
+ logWindow.append(LogLevel.Debug, "Message 4");
+ logWindow.append(LogLevel.Info, "Message 5");
+
+ Iterable range = logWindow.range(1, 3); // Получаем диапазон сообщений с индекса 1 до 3
+
+ int count = 0;
+ for (LogEntry entry : range) {
+ count++;
+ }
+ assertEquals(3, count); // Проверяем, что в полученном диапазоне содержится 3 сообщения
+ }
+
+ @Test
+ public void testLogRangeOutOfBounds() {
+ LogWindowSource logWindow = new LogWindowSource(5); // Создаем лог с ограничением в 5 сообщений
+ logWindow.append(LogLevel.Info, "Message 1");
+
+ Iterable range = logWindow.range(2, 3); // Пытаемся получить диапазон с индекса 2, хотя в логе только 1 сообщение
+
+ int count = 0;
+ for (LogEntry entry : range) {
+ count++;
+ }
+ assertEquals(0, count); // Проверяем, что полученный диапазон пустой
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/example/gui/GameVisualizer.java b/src/main/java/org/example/gui/GameVisualizer.java
new file mode 100644
index 000000000..55fee1af1
--- /dev/null
+++ b/src/main/java/org/example/gui/GameVisualizer.java
@@ -0,0 +1,237 @@
+package gui;
+
+import java.awt.*;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.geom.AffineTransform;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Timer;
+import java.util.TimerTask;
+import javax.imageio.ImageIO;
+import javax.swing.JPanel;
+
+public class GameVisualizer extends JPanel
+{
+ private final Timer m_timer = initTimer();
+ private Image background = ImageIO.read(new File("C:\\Users\\egori\\IdeaProjects\\Robots333\\src\\main\\java\\org\\example\\gui\\grasss.png"));
+ private Image treedr = ImageIO.read(new File("C:\\Users\\egori\\IdeaProjects\\Robots333\\src\\main\\java\\org\\example\\gui\\drytree.png"));
+ private Image trunk = ImageIO.read(new File("C:\\Users\\egori\\IdeaProjects\\Robots333\\src\\main\\java\\org\\example\\gui\\trunk.png"));
+
+ private int score;
+ private static Timer initTimer()
+ {
+ Timer timer = new Timer("events generator", true);
+ return timer;
+ }
+
+ public boolean m_collisionWithBorder;
+
+ private volatile double m_robotPositionX = 100;
+ private volatile double m_robotPositionY = 100;
+ private volatile double m_robotDirection = 0;
+ private volatile int m_targetPositionX = 150;
+ private volatile int m_targetPositionY = 100;
+
+ private static final double maxVelocity = 0.1;
+ private static final double maxAngularVelocity = 0.001;
+
+ private ArrayList trees = new ArrayList<>();
+ private static final int NUM_TREES = 20;
+
+ public GameVisualizer() throws IOException {
+ for (int i = 0; i < NUM_TREES; i++) {
+ trees.add(new gui.Tree(Math.random() * 800, Math.random() * 600));
+ }
+
+ m_timer.schedule(new TimerTask() {
+ @Override
+ public void run() {
+ for (gui.Tree tree : trees) {
+ if (tree.isRobotOnTree(m_robotPositionX, m_robotPositionY)) {
+ tree.decreaseHealth();
+ if (tree.getHealth() == 0) {
+ score++;
+ }
+ }
+ }
+ repaint();
+ }
+ }, 0, 50);
+
+ m_timer.schedule(new TimerTask() {
+ @Override
+ public void run() {
+ onRedrawEvent();
+ }
+ }, 0, 50);
+ m_timer.schedule(new TimerTask() {
+ @Override
+ public void run() {
+ onModelUpdateEvent();
+ }
+ }, 0, 7);
+ addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ setTargetPosition(e.getPoint());
+ repaint();
+ }
+ });
+ setDoubleBuffered(true);
+ }
+
+ public static final double BORDER_BUFFER = 3.5;
+
+ protected void setTargetPosition(Point p) {
+ m_targetPositionX = p.x;
+ m_targetPositionY = p.y;
+ }
+
+ protected void onRedrawEvent() {
+ EventQueue.invokeLater(this::repaint);
+ }
+
+ private static double distance(double x1, double y1, double x2, double y2) {
+ double diffX = x1 - x2;
+ double diffY = y1 - y2;
+ return Math.sqrt(diffX * diffX + diffY * diffY);
+ }
+
+ private static double angleTo(double fromX, double fromY, double toX, double toY) {
+ double diffX = toX - fromX;
+ double diffY = toY - fromY;
+
+ return asNormalizedRadians(Math.atan2(diffY, diffX));
+ }
+
+ public void onModelUpdateEvent() {
+ m_targetPositionX = (int) applyLimits(m_targetPositionX, 0, this.getWidth());
+ m_targetPositionY = (int) applyLimits(m_targetPositionY, 0, this.getHeight());
+
+ double distance = distance(m_targetPositionX, m_targetPositionY,
+ m_robotPositionX, m_robotPositionY);
+ if (distance < 7) {
+ return;
+ }
+
+ double angleToTarget = angleTo(m_robotPositionX, m_robotPositionY, m_targetPositionX, m_targetPositionY);
+ double angularVelocity = 0;
+
+ if (angleToTarget > m_robotDirection) {
+ angularVelocity = maxAngularVelocity;
+ }
+ if (angleToTarget < m_robotDirection) {
+ angularVelocity = -maxAngularVelocity;
+ }
+
+ moveRobot(maxVelocity, angularVelocity, 10);
+ }
+
+ private static double applyLimits(double value, double min, double max) {
+ if (value < min)
+ return min;
+ if (value > max)
+ return max;
+ return value;
+ }
+
+ public void moveRobot(double velocity, double angularVelocity, double duration) {
+ velocity = applyLimits(velocity, 0, maxVelocity);
+ angularVelocity = applyLimits(angularVelocity, -maxAngularVelocity, maxAngularVelocity);
+ m_robotDirection = asNormalizedRadians(m_robotDirection + angularVelocity * duration);
+
+ double distanceToMove = velocity * duration;
+ double newX = m_robotPositionX + Math.cos(m_robotDirection) * distanceToMove;
+ double newY = m_robotPositionY + Math.sin(m_robotDirection) * distanceToMove;
+
+ if (newX < BORDER_BUFFER) {
+ newX = BORDER_BUFFER;
+ m_robotDirection = Math.PI - m_robotDirection;
+ } else if (newX > getWidth() - BORDER_BUFFER) {
+ newX = getWidth() - BORDER_BUFFER;
+ m_robotDirection = Math.PI - m_robotDirection;
+ }
+ if (newY < BORDER_BUFFER) {
+ newY = BORDER_BUFFER;
+ m_robotDirection = Math.PI + m_robotDirection;
+ } else if (newY > getHeight() - BORDER_BUFFER) {
+ newY = getHeight() - BORDER_BUFFER;
+ m_robotDirection = Math.PI + m_robotDirection;
+ }
+ m_robotPositionX = newX;
+ m_robotPositionY = newY;
+ if (0 <= m_robotPositionX && m_robotPositionX <= this.getWidth() && 0 <= m_robotPositionY && m_robotPositionY <= getHeight()) {
+ m_collisionWithBorder = false;
+ }
+ }
+
+ private static double asNormalizedRadians(double angle) {
+ while (angle < 0) {
+ angle += 2 * Math.PI;
+ }
+ while (angle >= 2 * Math.PI) {
+ angle -= 2 * Math.PI;
+ }
+ return angle;
+ }
+
+ public double getPositionX() {
+ return m_robotPositionX;
+ }
+
+ public double getPositionY() {
+ return m_robotPositionY;
+ }
+
+ @Override
+ public void paint(Graphics g) {
+ super.paint(g);
+ Graphics2D g2d = (Graphics2D) g;
+ g2d.drawImage(background, 0, 0, this.getWidth(), this.getHeight(), null);
+ for (gui.Tree tree : trees) {
+ tree.paint(g2d, treedr, trunk);
+ }
+ drawRobot(g2d, round(m_robotPositionX), round(m_robotPositionY), m_robotDirection);
+ drawTarget(g2d, m_targetPositionX, m_targetPositionY);
+ g2d.drawString("Score: " + score, 10, 15);
+ }
+
+ private static int round(double value) {
+ return (int) (value + 0.5);
+ }
+
+ private void drawRobot(Graphics2D g, int x, int y, double direction) {
+ int robotCenterX = round(m_robotPositionX);
+ int robotCenterY = round(m_robotPositionY);
+ AffineTransform t = AffineTransform.getRotateInstance(direction, robotCenterX, robotCenterY);
+ g.setTransform(t);
+ g.setColor(Color.MAGENTA);
+ fillOval(g, robotCenterX, robotCenterY, 30, 10);
+ g.setColor(Color.BLACK);
+ drawOval(g, robotCenterX, robotCenterY, 30, 10);
+ g.setColor(Color.WHITE);
+ fillOval(g, robotCenterX + 10, robotCenterY, 5, 5);
+ g.setColor(Color.BLACK);
+ drawOval(g, robotCenterX + 10, robotCenterY, 5, 5);
+ }
+
+ private static void fillOval(Graphics g, int centerX, int centerY, int diam1, int diam2) {
+ g.fillOval(centerX - diam1 / 2, centerY - diam2 / 2, diam1, diam2);
+ }
+
+ private static void drawOval(Graphics g, int centerX, int centerY, int diam1, int diam2) {
+ g.drawOval(centerX - diam1 / 2, centerY - diam2 / 2, diam1, diam2);
+ }
+
+ private void drawTarget(Graphics2D g, int x, int y) {
+ AffineTransform t = AffineTransform.getRotateInstance(0, 0, 0);
+ g.setTransform(t);
+ g.setColor(Color.GREEN);
+ fillOval(g, x, y, 5, 5);
+ g.setColor(Color.BLACK);
+ drawOval(g, x, y, 5, 5);
+ }
+}
diff --git a/src/main/java/org/example/gui/GameWindow.java b/src/main/java/org/example/gui/GameWindow.java
new file mode 100644
index 000000000..38b200955
--- /dev/null
+++ b/src/main/java/org/example/gui/GameWindow.java
@@ -0,0 +1,37 @@
+package gui;
+
+import java.awt.BorderLayout;
+import java.io.IOException;
+import java.util.Locale;
+
+import javax.swing.*;
+import javax.swing.event.InternalFrameEvent;
+import javax.swing.event.InternalFrameListener;
+
+import static gui.MenuBar.getLocaleString;
+
+public class GameWindow extends inter
+{
+ private final GameVisualizer m_visualizer;
+ public GameWindow()
+ {
+ super(getLocaleString("gamePlane", Locale.getDefault()), true, true, true, true);
+ setTitle(getLocaleString("gamePlane", Locale.getDefault()));
+
+ try {
+ m_visualizer = new GameVisualizer();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ JPanel panel = new JPanel(new BorderLayout());
+ panel.add(m_visualizer, BorderLayout.CENTER);
+ getContentPane().add(panel);
+ pack();
+ }
+
+ public void updateWindow() {
+ setTitle(getLocaleString("gamePlane", Locale.getDefault()));
+ }
+
+}
diff --git a/robots/src/gui/LogWindow.java b/src/main/java/org/example/gui/LogWindow.java
similarity index 60%
rename from robots/src/gui/LogWindow.java
rename to src/main/java/org/example/gui/LogWindow.java
index 723d3e2fc..0e956faf0 100644
--- a/robots/src/gui/LogWindow.java
+++ b/src/main/java/org/example/gui/LogWindow.java
@@ -1,29 +1,32 @@
package gui;
-import java.awt.BorderLayout;
-import java.awt.EventQueue;
-import java.awt.TextArea;
+import java.awt.*;
+import java.awt.event.WindowListener;
+import java.util.Locale;
-import javax.swing.JInternalFrame;
-import javax.swing.JPanel;
+import javax.swing.*;
+import javax.swing.event.InternalFrameEvent;
+import javax.swing.event.InternalFrameListener;
import log.LogChangeListener;
import log.LogEntry;
import log.LogWindowSource;
-public class LogWindow extends JInternalFrame implements LogChangeListener
+import static gui.MenuBar.getLocaleString;
+
+public class LogWindow extends inter implements LogChangeListener
{
private LogWindowSource m_logSource;
private TextArea m_logContent;
public LogWindow(LogWindowSource logSource)
{
- super("Протокол работы", true, true, true, true);
+ super(getLocaleString("workProtocol", Locale.getDefault()), true, true, true, true);
m_logSource = logSource;
m_logSource.registerListener(this);
m_logContent = new TextArea("");
m_logContent.setSize(200, 500);
-
+
JPanel panel = new JPanel(new BorderLayout());
panel.add(m_logContent, BorderLayout.CENTER);
getContentPane().add(panel);
@@ -41,10 +44,20 @@ private void updateLogContent()
m_logContent.setText(content.toString());
m_logContent.invalidate();
}
+
+ @Override
+ public void doDefaultCloseAction() {
+ m_logSource.unregisterListener(this);
+ super.doDefaultCloseAction();
+ }
@Override
public void onLogChanged()
{
EventQueue.invokeLater(this::updateLogContent);
}
+
+ public void updateWindow() {
+ setTitle(getLocaleString("workProtocol", Locale.getDefault()));
+ }
}
diff --git a/src/main/java/org/example/gui/MainApplicationFrame.java b/src/main/java/org/example/gui/MainApplicationFrame.java
new file mode 100644
index 000000000..6428e3e71
--- /dev/null
+++ b/src/main/java/org/example/gui/MainApplicationFrame.java
@@ -0,0 +1,135 @@
+package gui;
+
+import java.awt.*;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.util.Locale;
+import java.util.ResourceBundle;
+
+import javax.swing.*;
+
+
+import log.Logger;
+
+import static gui.MenuBar.getLocaleString;
+
+
+
+public class MainApplicationFrame extends JFrame
+{
+ static ResourceBundle rb;
+
+ private final JDesktopPane desktopPane = new JDesktopPane();
+
+ private String profileID;
+ public MainApplicationFrame() {
+ int inset = 50;
+ Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
+ setBounds(inset, inset,
+ screenSize.width - inset*2,
+ screenSize.height - inset*2);
+
+ setContentPane(desktopPane);
+
+
+ LogWindow logWindow = createLogWindow();
+ addWindow(logWindow);
+ setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
+ addWindowListener(new WindowAdapter() {
+ @Override
+ public void windowClosing(WindowEvent e) {
+ Object[] options = { getLocaleString("yes", Locale.getDefault()), getLocaleString("no", Locale.getDefault()) };
+ int n = JOptionPane
+ .showOptionDialog(e.getWindow(), getLocaleString("closeWindow", Locale.getDefault()),
+ getLocaleString("accept", Locale.getDefault()), JOptionPane.YES_NO_OPTION,
+ JOptionPane.QUESTION_MESSAGE, null, options,
+ options[0]);
+ if (n == 0) {
+ e.getWindow().setVisible(false);
+ setDefaultCloseOperation(EXIT_ON_CLOSE);
+ }
+ }
+ });
+
+ GameWindow gameWindow = new GameWindow();
+ gameWindow.setSize(400, 400);
+ addWindow(gameWindow);
+
+ MenuBar menuBar = new MenuBar(this);
+ setJMenuBar(generateMenuBar());
+ setDefaultCloseOperation(EXIT_ON_CLOSE);
+ selectingProfile();
+ }
+
+ private void selectingProfile(){
+ boolean flagNewProfile = false;
+ while(profileID == null) {
+ int response = MenuStart.whatProfile();
+// if (response == 0) {
+// profileID = MenuStart.selectingProfile();
+// } else
+ if (response == 1){
+ flagNewProfile = true;
+ profileID = MenuStart.createProfile();
+ }
+ else {
+ System.exit(0);
+ }
+ }
+// if (!flagNewProfile) {
+// setStates();
+// }
+ }
+
+ protected LogWindow createLogWindow()
+ {
+ LogWindow logWindow = new LogWindow(Logger.getDefaultLogSource());
+ logWindow.setLocation(10,10);
+ logWindow.setSize(300, 800);
+ setMinimumSize(logWindow.getSize());
+ logWindow.pack();
+ Logger.debug(getLocaleString("protocolWorks", Locale.getDefault()));
+ return logWindow;
+ }
+
+ protected void addWindow(JInternalFrame frame)
+ {
+ desktopPane.add(frame);
+ frame.setVisible(true);
+ }
+
+
+ public JMenuBar generateMenuBar() {
+ JMenuBar menuBar = new JMenuBar();
+
+ menuBar.add(MenuBar.createLookAndFeelMenu());
+ menuBar.add(MenuBar.createTestMenu());
+ menuBar.add(MenuBar.createLanguage());
+ menuBar.add(MenuBar.createExitButton());
+ return menuBar;
+ }
+
+ public void changeLocale(Locale newLocale) {
+ if (!(newLocale.equals(Locale.getDefault()))){
+ Locale.setDefault(newLocale);
+
+ setJMenuBar(generateMenuBar());
+ setContentPane(desktopPane);
+ updateWindowsLocale();
+ }
+ else{
+ Locale.setDefault(newLocale);
+ }
+ }
+
+ private void updateWindowsLocale() {
+ JInternalFrame[] frames = desktopPane.getAllFrames();
+ for (JInternalFrame frame : frames) {
+ if (frame instanceof inter) {
+ ((inter) frame).updateWindow();
+ }
+ }
+ }
+
+
+}
diff --git a/src/main/java/org/example/gui/MenuBar.java b/src/main/java/org/example/gui/MenuBar.java
new file mode 100644
index 000000000..daf2bb879
--- /dev/null
+++ b/src/main/java/org/example/gui/MenuBar.java
@@ -0,0 +1,129 @@
+package gui;
+
+
+import log.Logger;
+
+import javax.swing.*;
+import java.awt.event.KeyEvent;
+import java.util.Locale;
+import java.util.ResourceBundle;
+
+
+
+public class MenuBar {
+
+ static MainApplicationFrame mainFrame;
+
+ public MenuBar(MainApplicationFrame mainFrame){
+ this.mainFrame = mainFrame;
+ }
+
+
+ protected static JMenu createLookAndFeelMenu() {
+
+ JMenu lookAndFeelMenu = new JMenu(getLocaleString("displayMode", Locale.getDefault()));
+ lookAndFeelMenu.setMnemonic(KeyEvent.VK_V);
+ lookAndFeelMenu.getAccessibleContext().setAccessibleDescription(
+ getLocaleString("systemMap", Locale.getDefault()));
+
+ {
+ JMenuItem systemLookAndFeel = new JMenuItem(getLocaleString("modeControl", Locale.getDefault()), KeyEvent.VK_S);
+ systemLookAndFeel.addActionListener((event) -> {
+ setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+ mainFrame.invalidate();
+ });
+ lookAndFeelMenu.add(systemLookAndFeel);
+ }
+
+ {
+ JMenuItem crossplatformLookAndFeel = new JMenuItem(getLocaleString("universityMap", Locale.getDefault()), KeyEvent.VK_S);
+ crossplatformLookAndFeel.addActionListener((event) -> {
+ setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
+ mainFrame.invalidate();
+ });
+ lookAndFeelMenu.add(crossplatformLookAndFeel);
+ }
+
+ return lookAndFeelMenu;
+ }
+
+
+ protected static JMenu createTestMenu() {
+ JMenu testMenu = new JMenu(getLocaleString("test", Locale.getDefault()));
+ testMenu.setMnemonic(KeyEvent.VK_T);
+ testMenu.getAccessibleContext().setAccessibleDescription(
+ getLocaleString("testCommand", Locale.getDefault()));
+
+ {
+ JMenuItem addLogMessageItem = new JMenuItem(getLocaleString("messageLog", Locale.getDefault()), KeyEvent.VK_S);
+ addLogMessageItem.addActionListener((event) -> {
+ Logger.debug(getLocaleString("newString", Locale.getDefault()));
+ });
+ testMenu.add(addLogMessageItem);
+ }
+ return testMenu;
+ }
+
+ protected static JMenu createLanguage() {
+ JMenu language = new JMenu(getLocaleString("language", Locale.getDefault()));
+
+
+ {
+ JMenuItem English = new JMenuItem(getLocaleString("englishLang", Locale.getDefault()));
+ English.addActionListener((event) -> {
+ mainFrame.changeLocale(new Locale("en", "US"));
+ });
+
+ JMenuItem Russian = new JMenuItem(getLocaleString("russianLang", Locale.getDefault()));
+ Russian.addActionListener((event) -> {
+ mainFrame.changeLocale(new Locale("ru", "RU"));
+ });
+ language.add(English);
+ language.add(Russian);
+ }
+
+ return language;
+ }
+
+
+ public static String getLocaleString(String key, Locale locale) {
+ ResourceBundle rb;
+ rb = ResourceBundle.getBundle("resources", locale);
+ return rb.getString(key);
+ }
+
+ protected static JMenu createExitButton() {
+ JMenu menu = new JMenu(getLocaleString("exit", Locale.getDefault()));
+ menu.setMnemonic(KeyEvent.VK_T);
+
+ {
+ JMenuItem exit1 = new JMenuItem(getLocaleString("exit", Locale.getDefault()), KeyEvent.VK_S);
+ exit1.setFocusable(false);
+ exit1.addActionListener((event) -> {
+ Object[] options = {getLocaleString("yes", Locale.getDefault()), getLocaleString( "no", Locale.getDefault())};
+ int n = JOptionPane
+ .showOptionDialog(null , getLocaleString("closeWindow", Locale.getDefault()),
+ getLocaleString("accept", Locale.getDefault()), JOptionPane.YES_NO_OPTION,
+ JOptionPane.QUESTION_MESSAGE, null, options,
+ options[0]);
+ if (n == 0) {
+ System.exit(0);
+ }
+ });
+ menu.add(exit1);
+ }
+ return menu;
+ }
+
+
+ private static void setLookAndFeel(String className) {
+ try {
+ UIManager.setLookAndFeel(className);
+ SwingUtilities.updateComponentTreeUI(mainFrame);
+ } catch (ClassNotFoundException | InstantiationException
+ | IllegalAccessException | UnsupportedLookAndFeelException e) {
+ }
+ }
+
+}
+
diff --git a/src/main/java/org/example/gui/MenuStart.java b/src/main/java/org/example/gui/MenuStart.java
new file mode 100644
index 000000000..22b652d00
--- /dev/null
+++ b/src/main/java/org/example/gui/MenuStart.java
@@ -0,0 +1,39 @@
+package gui;
+
+import javax.swing.*;
+import java.util.Objects;
+
+public class MenuStart {
+ public static int whatProfile(){
+ Object[] options = {"save",
+ "create"};
+ return JOptionPane.showOptionDialog(null,
+ "select",
+ "confirmation",
+ JOptionPane.DEFAULT_OPTION, JOptionPane.PLAIN_MESSAGE,
+ null, options, options[0]);
+ }
+ public static String createProfile(){
+ JOptionPane newProfile = new JOptionPane("Ok", JOptionPane.PLAIN_MESSAGE,
+ JOptionPane.DEFAULT_OPTION);
+
+ newProfile.setWantsInput(true);
+ JDialog dialog = newProfile.createDialog(null);
+
+ newProfile.selectInitialValue();
+ dialog.show();
+ dialog.dispose();
+
+ Object result2 = newProfile.getInputValue();
+
+ if (result2 == JOptionPane.UNINITIALIZED_VALUE) {
+ return null;
+ }
+ else if (Objects.equals(result2.toString(), "")){
+ return createProfile();
+ }
+ else{
+ return result2.toString();
+ }
+ }
+}
diff --git a/robots/src/gui/RobotsProgram.java b/src/main/java/org/example/gui/RobotsProgram.java
similarity index 100%
rename from robots/src/gui/RobotsProgram.java
rename to src/main/java/org/example/gui/RobotsProgram.java
diff --git a/src/main/java/org/example/gui/Tree.java b/src/main/java/org/example/gui/Tree.java
new file mode 100644
index 000000000..351566f71
--- /dev/null
+++ b/src/main/java/org/example/gui/Tree.java
@@ -0,0 +1,45 @@
+package gui;
+
+import java.awt.*;
+import java.awt.image.ImageObserver;
+
+public class Tree {
+ private double x;
+ private double y;
+ private int health;
+ private static final int MAX_HEALTH = 100;
+
+ public Tree(double x, double y) {
+ this.x = x;
+ this.y = y;
+ this.health = MAX_HEALTH;
+ }
+
+ public double getX() {
+ return x;
+ }
+
+ public double getY() {
+ return y;
+ }
+
+ public int getHealth() {
+ return health;
+ }
+
+ public void decreaseHealth() {
+ if (health > 0) {
+ health--;
+ }
+ }
+
+ public boolean isRobotOnTree(double robotX, double robotY) {
+ double distance = Math.sqrt(Math.pow(robotX - x, 2) + Math.pow(robotY - y, 2));
+ return distance < 30;
+ }
+
+ public void paint(Graphics2D g, Image treeImage, Image trunkImage) {
+ Image imageToDraw = (health > 0) ? treeImage : trunkImage;
+ g.drawImage(imageToDraw, (int)x, (int)y, 32, 32, null);
+ }
+}
diff --git a/src/main/java/org/example/gui/drytree.png b/src/main/java/org/example/gui/drytree.png
new file mode 100644
index 000000000..ec4649bf4
Binary files /dev/null and b/src/main/java/org/example/gui/drytree.png differ
diff --git a/src/main/java/org/example/gui/grass.png b/src/main/java/org/example/gui/grass.png
new file mode 100644
index 000000000..aa2d995a2
Binary files /dev/null and b/src/main/java/org/example/gui/grass.png differ
diff --git a/src/main/java/org/example/gui/grasss.png b/src/main/java/org/example/gui/grasss.png
new file mode 100644
index 000000000..1155836a5
Binary files /dev/null and b/src/main/java/org/example/gui/grasss.png differ
diff --git a/src/main/java/org/example/gui/inter.java b/src/main/java/org/example/gui/inter.java
new file mode 100644
index 000000000..14d10870f
--- /dev/null
+++ b/src/main/java/org/example/gui/inter.java
@@ -0,0 +1,65 @@
+package gui;
+
+import javax.swing.*;
+import javax.swing.event.InternalFrameEvent;
+import javax.swing.event.InternalFrameListener;
+import java.util.Locale;
+
+import static gui.MenuBar.getLocaleString;
+
+public abstract class inter extends JInternalFrame{
+ public abstract void updateWindow();
+ public inter(String str, boolean b, boolean b1, boolean b2, boolean b3){
+ super(str, b, b1, b2, b3);
+ setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
+ addInternalFrameListener(new InternalFrameListener() {
+
+ @Override
+ public void internalFrameOpened(InternalFrameEvent e) {
+
+ }
+
+ @Override
+ public void internalFrameClosing(InternalFrameEvent event) {
+ Object[] options = {getLocaleString("yes", Locale.getDefault()), getLocaleString( "no", Locale.getDefault())};
+ int n = JOptionPane
+ .showOptionDialog(event.getInternalFrame(), getLocaleString("closeWindow", Locale.getDefault()),
+ getLocaleString("accept", Locale.getDefault()), JOptionPane.YES_NO_OPTION,
+ JOptionPane.QUESTION_MESSAGE, null, options,
+ options[0]);
+ if (n == 0) {
+ event.getInternalFrame().setVisible(false);
+ setDefaultCloseOperation(EXIT_ON_CLOSE);
+ }
+ }
+
+ @Override
+ public void internalFrameClosed(InternalFrameEvent e) {
+
+ }
+
+ public void internalFrameIconified(InternalFrameEvent e) {
+
+ }
+
+ @Override
+ public void internalFrameDeiconified(InternalFrameEvent e) {
+
+ }
+
+
+ @Override
+ public void internalFrameActivated(InternalFrameEvent e) {
+
+ }
+
+ @Override
+ public void internalFrameDeactivated(InternalFrameEvent e) {
+
+ }
+
+ });
+ }
+
+
+}
diff --git a/src/main/java/org/example/gui/tree.png b/src/main/java/org/example/gui/tree.png
new file mode 100644
index 000000000..ffebd350c
Binary files /dev/null and b/src/main/java/org/example/gui/tree.png differ
diff --git a/src/main/java/org/example/gui/trunk.png b/src/main/java/org/example/gui/trunk.png
new file mode 100644
index 000000000..ba9d5f904
Binary files /dev/null and b/src/main/java/org/example/gui/trunk.png differ
diff --git a/robots/src/log/LogChangeListener.java b/src/main/java/org/example/log/LogChangeListener.java
similarity index 100%
rename from robots/src/log/LogChangeListener.java
rename to src/main/java/org/example/log/LogChangeListener.java
diff --git a/robots/src/log/LogEntry.java b/src/main/java/org/example/log/LogEntry.java
similarity index 100%
rename from robots/src/log/LogEntry.java
rename to src/main/java/org/example/log/LogEntry.java
diff --git a/robots/src/log/LogLevel.java b/src/main/java/org/example/log/LogLevel.java
similarity index 100%
rename from robots/src/log/LogLevel.java
rename to src/main/java/org/example/log/LogLevel.java
diff --git a/src/main/java/org/example/log/LogWindowSource.java b/src/main/java/org/example/log/LogWindowSource.java
new file mode 100644
index 000000000..a54304894
--- /dev/null
+++ b/src/main/java/org/example/log/LogWindowSource.java
@@ -0,0 +1,93 @@
+package log;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.ArrayBlockingQueue;
+
+/**
+ * пофикшены:
+ * 1. утечка ресурсов (связанные слушатели оказываются удерживаемыми в памяти)
+ * 2. ограничения; раньше класс хранил активные сообщения лога, но в такой реализации он
+ * их лишь накапливал. Сейчас количество сообщений в логе было ограничено
+ * величиной m_iQueueLength (т.е. реально нужна очередь сообщений
+ * ограниченного размера)
+ *
+ * изменения:
+ * Используется ArrayBlockingQueue для хранения сообщений лога, что позволяет избежать утечек ресурсов и ограничить размер лога величиной m_iQueueLength.
+ * Избавлены от необходимости хранить все слушатели в виде массива, который нужно было пересоздавать каждый раз при добавлении или удалении слушателя. Вместо этого используется обычный список m_listeners, синхронизированный на запись/удаление.
+ * Используется offer() вместо add() при добавлении сообщения в очередь, чтобы избежать исключения в случае, если очередь заполнена.
+ * Метод size() теперь возвращает размер m_queue.
+ * Метод range() теперь проверяет, что count неотрицательное число и возвращает пустой список, если startFrom выходит за пределы очереди или count равен нулю.
+ * Метод range() теперь использует метод ArrayList.subList() непосредственно над m_queue, а не создает новый список, что улучшает производительность и уменьшает использование памяти.
+ * Метод all() теперь создает новый список из m_queue, вместо создания списка итератором.
+ */
+public class LogWindowSource {
+ private final int m_iQueueLength;
+ private final ArrayBlockingQueue m_queue;
+ private final List m_listeners;
+ private volatile LogChangeListener[] m_activeListeners;
+
+ public LogWindowSource(int iQueueLength) {
+ m_iQueueLength = iQueueLength;
+ m_queue = new ArrayBlockingQueue<>(iQueueLength);
+ m_listeners = new ArrayList<>();
+ }
+
+ public void registerListener(LogChangeListener listener) {
+ synchronized(m_listeners) {
+ m_listeners.add(listener);
+ m_activeListeners = null;
+ }
+ }
+
+ public void unregisterListener(LogChangeListener listener) {
+ synchronized(m_listeners) {
+ m_listeners.remove(listener);
+ m_activeListeners = null;
+ }
+ }
+
+ public void append(LogLevel logLevel, String strMessage) {
+ boolean added = false;
+ while (!added) {
+ LogEntry entry = new LogEntry(logLevel, strMessage);
+ if (m_queue.offer(entry)) {
+ added = true;
+ if (m_queue.size() > m_iQueueLength) {
+ m_queue.remove();
+ }
+ LogChangeListener[] activeListeners = m_activeListeners;
+ if (activeListeners == null) {
+ synchronized (m_listeners) {
+ if (m_activeListeners == null) {
+ activeListeners = m_listeners.toArray(new LogChangeListener[0]);
+ m_activeListeners = activeListeners;
+ }
+ }
+ }
+ for (LogChangeListener listener : activeListeners) {
+ listener.onLogChanged();
+ }
+ } else {
+ m_queue.remove();
+ }
+ }
+ }
+
+ public int size() {
+ return m_queue.size();
+ }
+
+ public Iterable range(int startFrom, int count) {
+ if (startFrom < 0 || startFrom >= m_queue.size() || count <= 0) {
+ return Collections.emptyList();
+ }
+ int indexTo = Math.min(startFrom + count, m_queue.size());
+ return new ArrayList<>(m_queue).subList(startFrom, indexTo);
+ }
+
+ public Iterable all() {
+ return new ArrayList<>(m_queue);
+ }
+}
\ No newline at end of file
diff --git a/robots/src/log/Logger.java b/src/main/java/org/example/log/Logger.java
similarity index 100%
rename from robots/src/log/Logger.java
rename to src/main/java/org/example/log/Logger.java
diff --git a/src/main/java/org/example/saveProfile/SaveProfile.java b/src/main/java/org/example/saveProfile/SaveProfile.java
new file mode 100644
index 000000000..dd337f343
--- /dev/null
+++ b/src/main/java/org/example/saveProfile/SaveProfile.java
@@ -0,0 +1,8 @@
+package saveProfile;
+
+import java.io.Serializable;
+
+public class SaveProfile implements Serializable {
+
+
+}
diff --git a/src/main/resources/resources_en_US.properties b/src/main/resources/resources_en_US.properties
new file mode 100644
index 000000000..f0d6c85f4
--- /dev/null
+++ b/src/main/resources/resources_en_US.properties
@@ -0,0 +1,20 @@
+language = Language
+yes = Yes
+no = No
+closeWindow = Close a Window?
+accept = Confirmation
+gamePlane = Playing field
+exit = Exit
+englishLang = English language
+russianLang = Russian language
+newString = New line
+messageLog = Message to log
+testCommand = Test commands
+test = Tests
+universityMap = Universal scheme
+systemMap = System diagram
+modeControl = Controlling the Application Display Mode
+displayMode = Display Mode
+protocolWorks = The protocol works
+workProtocol = Work protocol
+rrr =dfjkhfgljhjdfgh
\ No newline at end of file
diff --git a/src/main/resources/resources_ru_RU.properties b/src/main/resources/resources_ru_RU.properties
new file mode 100644
index 000000000..3bd90cbb5
--- /dev/null
+++ b/src/main/resources/resources_ru_RU.properties
@@ -0,0 +1,19 @@
+language = \u042f\u0437\u044b\u043a
+yes = \u0020\u0020\u0414\u0430
+no = \u041d\u0435\u0442
+closeWindow = \u0417\u0430\u043a\u0440\u044b\u0442\u044c\u0020\u043e\u043a\u043d\u043e\u003f
+accept = \u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435
+gamePlane = \u0418\u0433\u0440\u043e\u0432\u043e\u0435\u0020\u043f\u043e\u043b\u0435
+exit = \u0412\u044b\u0439\u0442\u0438
+englishLang = \u0410\u043d\u0433\u043b\u0438\u0439\u0441\u043a\u0438\u0439\u0020\u044f\u0437\u044b\u043a
+russianLang = \u0420\u0443\u0441\u0441\u043a\u0438\u0439\u0020\u044f\u0437\u044b\u043a
+newString = \u041d\u043e\u0432\u0430\u044f\u0020\u0441\u0442\u0440\u043e\u043a\u0430
+messageLog = \u0421\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435\u0020\u0432\u0020\u043b\u043e\u0433
+testCommand = \u0422\u0435\u0441\u0442\u043e\u0432\u044b\u0435\u0020\u043a\u043e\u043c\u0430\u043d\u0434\u044b
+test = \u0422\u0435\u0441\u0442\u044b
+universityMap = \u0423\u043d\u0438\u0432\u0435\u0440\u0441\u0430\u043b\u044c\u043d\u0430\u044f\u0020\u0441\u0445\u0435\u043c\u0430
+systemMap = \u0421\u0438\u0441\u0442\u0435\u043c\u043d\u0430\u044f\u0020\u0441\u0445\u0435\u043c\u0430
+modeControl = \u0423\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435\u0020\u0440\u0435\u0436\u0438\u043c\u043e\u043c\u0020\u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f\u0020\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f
+displayMode = \u0420\u0435\u0436\u0438\u043c\u0020\u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f
+protocolWorks = \u041f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u0020\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442
+workProtocol = \u041f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u0020\u0440\u0430\u0431\u043e\u0442\u044b
\ No newline at end of file