From 2d02e4800c1702214cc58b8bb2389fb28bf64db6 Mon Sep 17 00:00:00 2001
From: Free-Denis <147907574+Free-Denis@users.noreply.github.com>
Date: Wed, 26 Feb 2025 12:40:08 +0500
Subject: [PATCH 01/12] Delete robots directory
---
robots/.classpath | 6 -
robots/.gitignore | 2 -
robots/.project | 17 --
robots/src/gui/GameVisualizer.java | 210 -----------------------
robots/src/gui/GameWindow.java | 20 ---
robots/src/gui/LogWindow.java | 50 ------
robots/src/gui/MainApplicationFrame.java | 156 -----------------
robots/src/gui/RobotsProgram.java | 25 ---
robots/src/log/LogChangeListener.java | 6 -
robots/src/log/LogEntry.java | 24 ---
robots/src/log/LogLevel.java | 24 ---
robots/src/log/LogWindowSource.java | 89 ----------
robots/src/log/Logger.java | 28 ---
13 files changed, 657 deletions(-)
delete mode 100644 robots/.classpath
delete mode 100644 robots/.gitignore
delete mode 100644 robots/.project
delete mode 100644 robots/src/gui/GameVisualizer.java
delete mode 100644 robots/src/gui/GameWindow.java
delete mode 100644 robots/src/gui/LogWindow.java
delete mode 100644 robots/src/gui/MainApplicationFrame.java
delete mode 100644 robots/src/gui/RobotsProgram.java
delete mode 100644 robots/src/log/LogChangeListener.java
delete mode 100644 robots/src/log/LogEntry.java
delete mode 100644 robots/src/log/LogLevel.java
delete mode 100644 robots/src/log/LogWindowSource.java
delete mode 100644 robots/src/log/Logger.java
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/LogWindow.java b/robots/src/gui/LogWindow.java
deleted file mode 100644
index 723d3e2fc..000000000
--- a/robots/src/gui/LogWindow.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package gui;
-
-import java.awt.BorderLayout;
-import java.awt.EventQueue;
-import java.awt.TextArea;
-
-import javax.swing.JInternalFrame;
-import javax.swing.JPanel;
-
-import log.LogChangeListener;
-import log.LogEntry;
-import log.LogWindowSource;
-
-public class LogWindow extends JInternalFrame implements LogChangeListener
-{
- private LogWindowSource m_logSource;
- private TextArea m_logContent;
-
- public LogWindow(LogWindowSource logSource)
- {
- super("Протокол работы", 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);
- pack();
- updateLogContent();
- }
-
- private void updateLogContent()
- {
- StringBuilder content = new StringBuilder();
- for (LogEntry entry : m_logSource.all())
- {
- content.append(entry.getMessage()).append("\n");
- }
- m_logContent.setText(content.toString());
- m_logContent.invalidate();
- }
-
- @Override
- public void onLogChanged()
- {
- EventQueue.invokeLater(this::updateLogContent);
- }
-}
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/gui/RobotsProgram.java b/robots/src/gui/RobotsProgram.java
deleted file mode 100644
index ae0930a8b..000000000
--- a/robots/src/gui/RobotsProgram.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package gui;
-
-import java.awt.Frame;
-
-import javax.swing.SwingUtilities;
-import javax.swing.UIManager;
-
-public class RobotsProgram
-{
- public static void main(String[] args) {
- try {
- UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");
-// UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel");
-// UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
-// UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
- } catch (Exception e) {
- e.printStackTrace();
- }
- SwingUtilities.invokeLater(() -> {
- MainApplicationFrame frame = new MainApplicationFrame();
- frame.pack();
- frame.setVisible(true);
- frame.setExtendedState(Frame.MAXIMIZED_BOTH);
- });
- }}
diff --git a/robots/src/log/LogChangeListener.java b/robots/src/log/LogChangeListener.java
deleted file mode 100644
index 0b0fb85dd..000000000
--- a/robots/src/log/LogChangeListener.java
+++ /dev/null
@@ -1,6 +0,0 @@
-package log;
-
-public interface LogChangeListener
-{
- public void onLogChanged();
-}
diff --git a/robots/src/log/LogEntry.java b/robots/src/log/LogEntry.java
deleted file mode 100644
index 3d9147107..000000000
--- a/robots/src/log/LogEntry.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package log;
-
-public class LogEntry
-{
- private LogLevel m_logLevel;
- private String m_strMessage;
-
- public LogEntry(LogLevel logLevel, String strMessage)
- {
- m_strMessage = strMessage;
- m_logLevel = logLevel;
- }
-
- public String getMessage()
- {
- return m_strMessage;
- }
-
- public LogLevel getLevel()
- {
- return m_logLevel;
- }
-}
-
diff --git a/robots/src/log/LogLevel.java b/robots/src/log/LogLevel.java
deleted file mode 100644
index 582d010cc..000000000
--- a/robots/src/log/LogLevel.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package log;
-
-public enum LogLevel
-{
- Trace(0),
- Debug(1),
- Info(2),
- Warning(3),
- Error(4),
- Fatal(5);
-
- private int m_iLevel;
-
- private LogLevel(int iLevel)
- {
- m_iLevel = iLevel;
- }
-
- public int level()
- {
- return m_iLevel;
- }
-}
-
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/robots/src/log/Logger.java b/robots/src/log/Logger.java
deleted file mode 100644
index b008a5d01..000000000
--- a/robots/src/log/Logger.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package log;
-
-public final class Logger
-{
- private static final LogWindowSource defaultLogSource;
- static {
- defaultLogSource = new LogWindowSource(100);
- }
-
- private Logger()
- {
- }
-
- public static void debug(String strMessage)
- {
- defaultLogSource.append(LogLevel.Debug, strMessage);
- }
-
- public static void error(String strMessage)
- {
- defaultLogSource.append(LogLevel.Error, strMessage);
- }
-
- public static LogWindowSource getDefaultLogSource()
- {
- return defaultLogSource;
- }
-}
From be1ed82b2808c9c3a375e5514e7e6323c4b10c7b Mon Sep 17 00:00:00 2001
From: Free-Denis <147907574+Free-Denis@users.noreply.github.com>
Date: Wed, 26 Feb 2025 12:40:23 +0500
Subject: [PATCH 02/12] Delete .gitignore
---
.gitignore | 4 ----
1 file changed, 4 deletions(-)
delete mode 100644 .gitignore
diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index 2c6eb3893..000000000
--- a/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-/.metadata
-/robots/.settings
-/robots/bin
-eclipse.bat
\ No newline at end of file
From f7b5f0bb1aa161afdc2433299513200238e45630 Mon Sep 17 00:00:00 2001
From: Free-Denis <147907574+Free-Denis@users.noreply.github.com>
Date: Wed, 26 Feb 2025 12:40:36 +0500
Subject: [PATCH 03/12] Delete README.md
---
README.md | 2 --
1 file changed, 2 deletions(-)
delete mode 100644 README.md
diff --git a/README.md b/README.md
deleted file mode 100644
index d79735aee..000000000
--- a/README.md
+++ /dev/null
@@ -1,2 +0,0 @@
-# Robots
-The project to learn OO design concepts and MDI application development in Java
From 0e75054827cce7b08d021542206cc2f0d03ee7bb Mon Sep 17 00:00:00 2001
From: Free-Denis <147907574+Free-Denis@users.noreply.github.com>
Date: Wed, 26 Feb 2025 12:41:46 +0500
Subject: [PATCH 04/12] Add files via upload
---
README.md | 2 +
robots/src/Main.java | 11 +++
robots/src/controller/GameController.java | 24 +++++
robots/src/model/RobotModel.java | 107 ++++++++++++++++++++++
robots/src/view/GameVisualizer.java | 71 ++++++++++++++
5 files changed, 215 insertions(+)
create mode 100644 README.md
create mode 100644 robots/src/Main.java
create mode 100644 robots/src/controller/GameController.java
create mode 100644 robots/src/model/RobotModel.java
create mode 100644 robots/src/view/GameVisualizer.java
diff --git a/README.md b/README.md
new file mode 100644
index 000000000..d79735aee
--- /dev/null
+++ b/README.md
@@ -0,0 +1,2 @@
+# Robots
+The project to learn OO design concepts and MDI application development in Java
diff --git a/robots/src/Main.java b/robots/src/Main.java
new file mode 100644
index 000000000..700ada244
--- /dev/null
+++ b/robots/src/Main.java
@@ -0,0 +1,11 @@
+import controller.GameController;
+import javax.swing.*;
+
+public class Main {
+ public static void main(String[] args) {
+ SwingUtilities.invokeLater(() -> {
+ GameController gameController = new GameController();
+ gameController.start();
+ });
+ }
+}
\ No newline at end of file
diff --git a/robots/src/controller/GameController.java b/robots/src/controller/GameController.java
new file mode 100644
index 000000000..2ec779744
--- /dev/null
+++ b/robots/src/controller/GameController.java
@@ -0,0 +1,24 @@
+package controller;
+
+import model.RobotModel;
+import view.GameVisualizer;
+
+import javax.swing.*;
+
+public class GameController {
+ private final RobotModel robotModel;
+ private final GameVisualizer gameVisualizer;
+
+ public GameController() {
+ robotModel = new RobotModel();
+ gameVisualizer = new GameVisualizer(robotModel);
+ }
+
+ public void start() {
+ JFrame frame = new JFrame("Game Window");
+ frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ frame.add(gameVisualizer);
+ frame.setSize(800, 600);
+ frame.setVisible(true);
+ }
+}
\ No newline at end of file
diff --git a/robots/src/model/RobotModel.java b/robots/src/model/RobotModel.java
new file mode 100644
index 000000000..6fa72eff4
--- /dev/null
+++ b/robots/src/model/RobotModel.java
@@ -0,0 +1,107 @@
+package model;
+
+public class RobotModel {
+ private volatile double robotPositionX = 100;
+ private volatile double robotPositionY = 100;
+ private volatile double robotDirection = 0;
+
+ private volatile int targetPositionX = 150;
+ private volatile int targetPositionY = 100;
+
+ private static final double maxVelocity = 0.1;
+ private static final double maxAngularVelocity = 0.001;
+
+ public double getRobotPositionX() {
+ return robotPositionX;
+ }
+
+ public double getRobotPositionY() {
+ return robotPositionY;
+ }
+
+ public double getRobotDirection() {
+ return robotDirection;
+ }
+
+ public int getTargetPositionX() {
+ return targetPositionX;
+ }
+
+ public int getTargetPositionY() {
+ return targetPositionY;
+ }
+
+ public void setTargetPosition(int x, int y) {
+ targetPositionX = x;
+ targetPositionY = y;
+ }
+
+ public void update(double duration) {
+ double distance = distance(targetPositionX, targetPositionY, robotPositionX, robotPositionY);
+ if (distance < 0.5) {
+ return;
+ }
+ double velocity = maxVelocity;
+ double angleToTarget = angleTo(robotPositionX, robotPositionY, targetPositionX, targetPositionY);
+ double angularVelocity = 0;
+ if (angleToTarget > robotDirection) {
+ angularVelocity = maxAngularVelocity;
+ }
+ if (angleToTarget < robotDirection) {
+ angularVelocity = -maxAngularVelocity;
+ }
+
+ moveRobot(velocity, angularVelocity, duration);
+ }
+
+ private void moveRobot(double velocity, double angularVelocity, double duration) {
+ velocity = applyLimits(velocity, 0, maxVelocity);
+ angularVelocity = applyLimits(angularVelocity, -maxAngularVelocity, maxAngularVelocity);
+ double newX = robotPositionX + velocity / angularVelocity *
+ (Math.sin(robotDirection + angularVelocity * duration) -
+ Math.sin(robotDirection));
+ if (!Double.isFinite(newX)) {
+ newX = robotPositionX + velocity * duration * Math.cos(robotDirection);
+ }
+ double newY = robotPositionY - velocity / angularVelocity *
+ (Math.cos(robotDirection + angularVelocity * duration) -
+ Math.cos(robotDirection));
+ if (!Double.isFinite(newY)) {
+ newY = robotPositionY + velocity * duration * Math.sin(robotDirection);
+ }
+ robotPositionX = newX;
+ robotPositionY = newY;
+ double newDirection = asNormalizedRadians(robotDirection + angularVelocity * duration);
+ robotDirection = newDirection;
+ }
+
+ 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));
+ }
+
+ 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 double applyLimits(double value, double min, double max) {
+ if (value < min)
+ return min;
+ if (value > max)
+ return max;
+ return value;
+ }
+}
\ No newline at end of file
diff --git a/robots/src/view/GameVisualizer.java b/robots/src/view/GameVisualizer.java
new file mode 100644
index 000000000..e4bf06de5
--- /dev/null
+++ b/robots/src/view/GameVisualizer.java
@@ -0,0 +1,71 @@
+package view;
+
+import model.RobotModel;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.geom.AffineTransform;
+
+public class GameVisualizer extends JPanel {
+ private final RobotModel robotModel;
+ private final Timer timer;
+
+ public GameVisualizer(RobotModel robotModel) {
+
+ this.robotModel = robotModel;
+ timer = new Timer(10, e -> {
+ robotModel.update(10);
+ repaint();
+ });
+ timer.start();
+
+ addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ robotModel.setTargetPosition(e.getX(), e.getY());
+ }
+ });
+ }
+
+ @Override
+ protected void paintComponent(Graphics g) {
+ super.paintComponent(g);
+ Graphics2D g2d = (Graphics2D) g;
+
+ drawRobot(g2d, (int) robotModel.getRobotPositionX(), (int) robotModel.getRobotPositionY(), robotModel.getRobotDirection());
+ drawTarget(g2d, robotModel.getTargetPositionX(), robotModel.getTargetPositionY());
+
+ }
+
+ private void drawRobot(Graphics2D g, int x, int y, double direction) {
+ AffineTransform t = AffineTransform.getRotateInstance(direction, x, y);
+ g.setTransform(t);
+ g.setColor(Color.MAGENTA);
+ fillOval(g, x, y, 30, 10);
+ g.setColor(Color.BLACK);
+ drawOval(g, x, y, 30, 10);
+ g.setColor(Color.WHITE);
+ fillOval(g, x + 10, y, 5, 5);
+ g.setColor(Color.BLACK);
+ drawOval(g, x + 10, y, 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);
+ }
+
+ 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);
+ }
+}
\ No newline at end of file
From cce3bede16908bfd0f839773fcdf7eb8a71fe222 Mon Sep 17 00:00:00 2001
From: Free-Denis <147907574+Free-Denis@users.noreply.github.com>
Date: Tue, 11 Mar 2025 10:49:33 +0500
Subject: [PATCH 05/12] Add files via upload
---
src/Main.java | 11 +++++
src/controller/GameController.java | 39 +++++++++++++++
src/model/Entity.java | 5 ++
src/model/EntityProvider.java | 7 +++
src/model/GameMath.java | 35 ++++++++++++++
src/model/Model.java | 31 ++++++++++++
src/model/Robot.java | 78 ++++++++++++++++++++++++++++++
src/view/EntityVisualizer.java | 8 +++
src/view/GameVisualizer.java | 42 ++++++++++++++++
src/view/RobotVisualizer.java | 45 +++++++++++++++++
10 files changed, 301 insertions(+)
create mode 100644 src/Main.java
create mode 100644 src/controller/GameController.java
create mode 100644 src/model/Entity.java
create mode 100644 src/model/EntityProvider.java
create mode 100644 src/model/GameMath.java
create mode 100644 src/model/Model.java
create mode 100644 src/model/Robot.java
create mode 100644 src/view/EntityVisualizer.java
create mode 100644 src/view/GameVisualizer.java
create mode 100644 src/view/RobotVisualizer.java
diff --git a/src/Main.java b/src/Main.java
new file mode 100644
index 000000000..700ada244
--- /dev/null
+++ b/src/Main.java
@@ -0,0 +1,11 @@
+import controller.GameController;
+import javax.swing.*;
+
+public class Main {
+ public static void main(String[] args) {
+ SwingUtilities.invokeLater(() -> {
+ GameController gameController = new GameController();
+ gameController.start();
+ });
+ }
+}
\ No newline at end of file
diff --git a/src/controller/GameController.java b/src/controller/GameController.java
new file mode 100644
index 000000000..d5f09371f
--- /dev/null
+++ b/src/controller/GameController.java
@@ -0,0 +1,39 @@
+package controller;
+
+import model.Model;
+import view.GameVisualizer;
+
+import javax.swing.*;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+
+public class GameController {
+ private final Model model;
+ private final GameVisualizer gameVisualizer;
+
+ public GameController() {
+ model = new Model(); // Model теперь реализует EntityProvider
+ gameVisualizer = new GameVisualizer(model); // Передаем Model как EntityProvider
+
+ Timer timer = new Timer(10, e -> {
+ model.update(10);
+ gameVisualizer.repaint();
+ });
+ timer.start();
+
+ gameVisualizer.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ model.sendMouseClickEvent(e.getX(), e.getY());
+ }
+ });
+ }
+
+ public void start() {
+ JFrame frame = new JFrame("Game Window");
+ frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ frame.add(gameVisualizer);
+ frame.setSize(800, 600);
+ frame.setVisible(true);
+ }
+}
\ No newline at end of file
diff --git a/src/model/Entity.java b/src/model/Entity.java
new file mode 100644
index 000000000..dc985d8bd
--- /dev/null
+++ b/src/model/Entity.java
@@ -0,0 +1,5 @@
+package model;
+
+public interface Entity {
+ void update(double duration);
+}
\ No newline at end of file
diff --git a/src/model/EntityProvider.java b/src/model/EntityProvider.java
new file mode 100644
index 000000000..51f56f7eb
--- /dev/null
+++ b/src/model/EntityProvider.java
@@ -0,0 +1,7 @@
+package model;
+
+import java.util.List;
+
+public interface EntityProvider {
+ List getEntities();
+}
\ No newline at end of file
diff --git a/src/model/GameMath.java b/src/model/GameMath.java
new file mode 100644
index 000000000..90925d3aa
--- /dev/null
+++ b/src/model/GameMath.java
@@ -0,0 +1,35 @@
+package model;
+
+public final class GameMath {
+ private GameMath() {} // Запрещаем создание экземпляров
+
+ public 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);
+ }
+
+ public 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 static double asNormalizedRadians(double angle) {
+ while (angle < 0) {
+ angle += 2 * Math.PI;
+ }
+ while (angle >= 2 * Math.PI) {
+ angle -= 2 * Math.PI;
+ }
+ return angle;
+ }
+
+ public static double applyLimits(double value, double min, double max) {
+ if (value < min)
+ return min;
+ if (value > max)
+ return max;
+ return value;
+ }
+}
\ No newline at end of file
diff --git a/src/model/Model.java b/src/model/Model.java
new file mode 100644
index 000000000..a48f5cd30
--- /dev/null
+++ b/src/model/Model.java
@@ -0,0 +1,31 @@
+package model;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class Model implements EntityProvider { // Реализуем интерфейс EntityProvider
+ private final List entities = new ArrayList<>();
+
+ public Model() {
+ entities.add(new Robot()); // Добавляем робота в модель
+ }
+
+ @Override
+ public List getEntities() { // Реализуем метод интерфейса
+ return entities;
+ }
+
+ public void sendMouseClickEvent(int x, int y) {
+ for (Entity entity : entities) {
+ if (entity instanceof Robot) {
+ ((Robot) entity).setTargetPosition(x, y);
+ }
+ }
+ }
+
+ public void update(double duration) {
+ for (Entity entity : entities) {
+ entity.update(duration);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/model/Robot.java b/src/model/Robot.java
new file mode 100644
index 000000000..34f7d1fba
--- /dev/null
+++ b/src/model/Robot.java
@@ -0,0 +1,78 @@
+package model;
+
+public class Robot implements Entity {
+ private volatile double positionX = 100;
+ private volatile double positionY = 100;
+ private volatile double direction = 0;
+
+ private volatile int targetPositionX = 150;
+ private volatile int targetPositionY = 100;
+
+ private static final double maxVelocity = 0.1;
+ private static final double maxAngularVelocity = 0.001;
+
+ public double getPositionX() {
+ return positionX;
+ }
+
+ public double getPositionY() {
+ return positionY;
+ }
+
+ public double getDirection() {
+ return direction;
+ }
+
+ public int getTargetPositionX() {
+ return targetPositionX;
+ }
+
+ public int getTargetPositionY() {
+ return targetPositionY;
+ }
+
+ public void setTargetPosition(int x, int y) {
+ targetPositionX = x;
+ targetPositionY = y;
+ }
+
+ @Override
+ public void update(double duration) {
+ double distance = GameMath.distance(targetPositionX, targetPositionY, positionX, positionY);
+ if (distance < 0.5) {
+ return;
+ }
+ double velocity = maxVelocity;
+ double angleToTarget = GameMath.angleTo(positionX, positionY, targetPositionX, targetPositionY);
+ double angularVelocity = 0;
+ if (angleToTarget > direction) {
+ angularVelocity = maxAngularVelocity;
+ }
+ if (angleToTarget < direction) {
+ angularVelocity = -maxAngularVelocity;
+ }
+
+ moveRobot(velocity, angularVelocity, duration);
+ }
+
+ private void moveRobot(double velocity, double angularVelocity, double duration) {
+ velocity = GameMath.applyLimits(velocity, 0, maxVelocity);
+ angularVelocity = GameMath.applyLimits(angularVelocity, -maxAngularVelocity, maxAngularVelocity);
+ double newX = positionX + velocity / angularVelocity *
+ (Math.sin(direction + angularVelocity * duration) -
+ Math.sin(direction));
+ if (!Double.isFinite(newX)) {
+ newX = positionX + velocity * duration * Math.cos(direction);
+ }
+ double newY = positionY - velocity / angularVelocity *
+ (Math.cos(direction + angularVelocity * duration) -
+ Math.cos(direction));
+ if (!Double.isFinite(newY)) {
+ newY = positionY + velocity * duration * Math.sin(direction);
+ }
+ positionX = newX;
+ positionY = newY;
+ double newDirection = GameMath.asNormalizedRadians(direction + angularVelocity * duration);
+ direction = newDirection;
+ }
+}
\ No newline at end of file
diff --git a/src/view/EntityVisualizer.java b/src/view/EntityVisualizer.java
new file mode 100644
index 000000000..58f22be5c
--- /dev/null
+++ b/src/view/EntityVisualizer.java
@@ -0,0 +1,8 @@
+package view;
+
+import model.Entity;
+import java.awt.Graphics2D;
+
+public abstract class EntityVisualizer {
+ public abstract void draw(Graphics2D g, E entity);
+}
\ No newline at end of file
diff --git a/src/view/GameVisualizer.java b/src/view/GameVisualizer.java
new file mode 100644
index 000000000..f2b122c50
--- /dev/null
+++ b/src/view/GameVisualizer.java
@@ -0,0 +1,42 @@
+package view;
+
+import model.Entity;
+import model.EntityProvider;
+
+import javax.swing.*;
+import java.awt.*;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class GameVisualizer extends JPanel {
+ private final EntityProvider entityProvider;
+ private final Map, EntityVisualizer>> visualizers = new HashMap<>();
+
+ public GameVisualizer(EntityProvider entityProvider) {
+ this.entityProvider = entityProvider;
+ // Регистрируем визуализатор для робота
+ visualizers.put(model.Robot.class, new RobotVisualizer());
+ }
+
+ @Override
+ protected void paintComponent(Graphics g) {
+ super.paintComponent(g);
+ Graphics2D g2d = (Graphics2D) g;
+
+ List entities = entityProvider.getEntities();
+ for (Entity entity : entities) {
+ EntityVisualizer> visualizer = visualizers.get(entity.getClass());
+ if (visualizer != null) {
+ // Используем cast для корректного типа
+ drawEntity(g2d, entity, visualizer);
+ }
+ }
+ }
+
+ private void drawEntity(Graphics2D g, E entity, EntityVisualizer> visualizer) {
+ @SuppressWarnings("unchecked")
+ EntityVisualizer typedVisualizer = (EntityVisualizer) visualizer;
+ typedVisualizer.draw(g, entity);
+ }
+}
\ No newline at end of file
diff --git a/src/view/RobotVisualizer.java b/src/view/RobotVisualizer.java
new file mode 100644
index 000000000..85a53ec43
--- /dev/null
+++ b/src/view/RobotVisualizer.java
@@ -0,0 +1,45 @@
+package view;
+
+import model.Robot;
+import java.awt.*;
+import java.awt.geom.AffineTransform;
+
+public class RobotVisualizer extends EntityVisualizer {
+ @Override
+ public void draw(Graphics2D g, Robot robot) {
+ // Отрисовка робота
+ AffineTransform t = AffineTransform.getRotateInstance(robot.getDirection(), robot.getPositionX(), robot.getPositionY());
+ g.setTransform(t);
+ g.setColor(Color.MAGENTA);
+ fillOval(g, (int) robot.getPositionX(), (int) robot.getPositionY(), 30, 10);
+ g.setColor(Color.BLACK);
+ drawOval(g, (int) robot.getPositionX(), (int) robot.getPositionY(), 30, 10);
+ g.setColor(Color.WHITE);
+ fillOval(g, (int) robot.getPositionX() + 10, (int) robot.getPositionY(), 5, 5);
+ g.setColor(Color.BLACK);
+ drawOval(g, (int) robot.getPositionX() + 10, (int) robot.getPositionY(), 5, 5);
+
+ // Отрисовка цели
+ drawTarget(g, robot.getTargetPositionX(), robot.getTargetPositionY());
+ }
+
+ 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);
+ }
+
+ private void fillOval(Graphics g, int centerX, int centerY, int diam1, int diam2) {
+ g.fillOval(centerX - diam1 / 2, centerY - diam2 / 2, diam1, diam2);
+ }
+
+ private void drawOval(Graphics g, int centerX, int centerY, int diam1, int diam2) {
+ g.drawOval(centerX - diam1 / 2, centerY - diam2 / 2, diam1, diam2);
+ }
+}
\ No newline at end of file
From c144898c1f7124bd51363896d29d4a7f090122c7 Mon Sep 17 00:00:00 2001
From: Free-Denis <147907574+Free-Denis@users.noreply.github.com>
Date: Tue, 11 Mar 2025 10:50:14 +0500
Subject: [PATCH 06/12] Delete src directory
---
src/Main.java | 11 -----
src/controller/GameController.java | 39 ---------------
src/model/Entity.java | 5 --
src/model/EntityProvider.java | 7 ---
src/model/GameMath.java | 35 --------------
src/model/Model.java | 31 ------------
src/model/Robot.java | 78 ------------------------------
src/view/EntityVisualizer.java | 8 ---
src/view/GameVisualizer.java | 42 ----------------
src/view/RobotVisualizer.java | 45 -----------------
10 files changed, 301 deletions(-)
delete mode 100644 src/Main.java
delete mode 100644 src/controller/GameController.java
delete mode 100644 src/model/Entity.java
delete mode 100644 src/model/EntityProvider.java
delete mode 100644 src/model/GameMath.java
delete mode 100644 src/model/Model.java
delete mode 100644 src/model/Robot.java
delete mode 100644 src/view/EntityVisualizer.java
delete mode 100644 src/view/GameVisualizer.java
delete mode 100644 src/view/RobotVisualizer.java
diff --git a/src/Main.java b/src/Main.java
deleted file mode 100644
index 700ada244..000000000
--- a/src/Main.java
+++ /dev/null
@@ -1,11 +0,0 @@
-import controller.GameController;
-import javax.swing.*;
-
-public class Main {
- public static void main(String[] args) {
- SwingUtilities.invokeLater(() -> {
- GameController gameController = new GameController();
- gameController.start();
- });
- }
-}
\ No newline at end of file
diff --git a/src/controller/GameController.java b/src/controller/GameController.java
deleted file mode 100644
index d5f09371f..000000000
--- a/src/controller/GameController.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package controller;
-
-import model.Model;
-import view.GameVisualizer;
-
-import javax.swing.*;
-import java.awt.event.MouseAdapter;
-import java.awt.event.MouseEvent;
-
-public class GameController {
- private final Model model;
- private final GameVisualizer gameVisualizer;
-
- public GameController() {
- model = new Model(); // Model теперь реализует EntityProvider
- gameVisualizer = new GameVisualizer(model); // Передаем Model как EntityProvider
-
- Timer timer = new Timer(10, e -> {
- model.update(10);
- gameVisualizer.repaint();
- });
- timer.start();
-
- gameVisualizer.addMouseListener(new MouseAdapter() {
- @Override
- public void mouseClicked(MouseEvent e) {
- model.sendMouseClickEvent(e.getX(), e.getY());
- }
- });
- }
-
- public void start() {
- JFrame frame = new JFrame("Game Window");
- frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
- frame.add(gameVisualizer);
- frame.setSize(800, 600);
- frame.setVisible(true);
- }
-}
\ No newline at end of file
diff --git a/src/model/Entity.java b/src/model/Entity.java
deleted file mode 100644
index dc985d8bd..000000000
--- a/src/model/Entity.java
+++ /dev/null
@@ -1,5 +0,0 @@
-package model;
-
-public interface Entity {
- void update(double duration);
-}
\ No newline at end of file
diff --git a/src/model/EntityProvider.java b/src/model/EntityProvider.java
deleted file mode 100644
index 51f56f7eb..000000000
--- a/src/model/EntityProvider.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package model;
-
-import java.util.List;
-
-public interface EntityProvider {
- List getEntities();
-}
\ No newline at end of file
diff --git a/src/model/GameMath.java b/src/model/GameMath.java
deleted file mode 100644
index 90925d3aa..000000000
--- a/src/model/GameMath.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package model;
-
-public final class GameMath {
- private GameMath() {} // Запрещаем создание экземпляров
-
- public 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);
- }
-
- public 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 static double asNormalizedRadians(double angle) {
- while (angle < 0) {
- angle += 2 * Math.PI;
- }
- while (angle >= 2 * Math.PI) {
- angle -= 2 * Math.PI;
- }
- return angle;
- }
-
- public static double applyLimits(double value, double min, double max) {
- if (value < min)
- return min;
- if (value > max)
- return max;
- return value;
- }
-}
\ No newline at end of file
diff --git a/src/model/Model.java b/src/model/Model.java
deleted file mode 100644
index a48f5cd30..000000000
--- a/src/model/Model.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package model;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class Model implements EntityProvider { // Реализуем интерфейс EntityProvider
- private final List entities = new ArrayList<>();
-
- public Model() {
- entities.add(new Robot()); // Добавляем робота в модель
- }
-
- @Override
- public List getEntities() { // Реализуем метод интерфейса
- return entities;
- }
-
- public void sendMouseClickEvent(int x, int y) {
- for (Entity entity : entities) {
- if (entity instanceof Robot) {
- ((Robot) entity).setTargetPosition(x, y);
- }
- }
- }
-
- public void update(double duration) {
- for (Entity entity : entities) {
- entity.update(duration);
- }
- }
-}
\ No newline at end of file
diff --git a/src/model/Robot.java b/src/model/Robot.java
deleted file mode 100644
index 34f7d1fba..000000000
--- a/src/model/Robot.java
+++ /dev/null
@@ -1,78 +0,0 @@
-package model;
-
-public class Robot implements Entity {
- private volatile double positionX = 100;
- private volatile double positionY = 100;
- private volatile double direction = 0;
-
- private volatile int targetPositionX = 150;
- private volatile int targetPositionY = 100;
-
- private static final double maxVelocity = 0.1;
- private static final double maxAngularVelocity = 0.001;
-
- public double getPositionX() {
- return positionX;
- }
-
- public double getPositionY() {
- return positionY;
- }
-
- public double getDirection() {
- return direction;
- }
-
- public int getTargetPositionX() {
- return targetPositionX;
- }
-
- public int getTargetPositionY() {
- return targetPositionY;
- }
-
- public void setTargetPosition(int x, int y) {
- targetPositionX = x;
- targetPositionY = y;
- }
-
- @Override
- public void update(double duration) {
- double distance = GameMath.distance(targetPositionX, targetPositionY, positionX, positionY);
- if (distance < 0.5) {
- return;
- }
- double velocity = maxVelocity;
- double angleToTarget = GameMath.angleTo(positionX, positionY, targetPositionX, targetPositionY);
- double angularVelocity = 0;
- if (angleToTarget > direction) {
- angularVelocity = maxAngularVelocity;
- }
- if (angleToTarget < direction) {
- angularVelocity = -maxAngularVelocity;
- }
-
- moveRobot(velocity, angularVelocity, duration);
- }
-
- private void moveRobot(double velocity, double angularVelocity, double duration) {
- velocity = GameMath.applyLimits(velocity, 0, maxVelocity);
- angularVelocity = GameMath.applyLimits(angularVelocity, -maxAngularVelocity, maxAngularVelocity);
- double newX = positionX + velocity / angularVelocity *
- (Math.sin(direction + angularVelocity * duration) -
- Math.sin(direction));
- if (!Double.isFinite(newX)) {
- newX = positionX + velocity * duration * Math.cos(direction);
- }
- double newY = positionY - velocity / angularVelocity *
- (Math.cos(direction + angularVelocity * duration) -
- Math.cos(direction));
- if (!Double.isFinite(newY)) {
- newY = positionY + velocity * duration * Math.sin(direction);
- }
- positionX = newX;
- positionY = newY;
- double newDirection = GameMath.asNormalizedRadians(direction + angularVelocity * duration);
- direction = newDirection;
- }
-}
\ No newline at end of file
diff --git a/src/view/EntityVisualizer.java b/src/view/EntityVisualizer.java
deleted file mode 100644
index 58f22be5c..000000000
--- a/src/view/EntityVisualizer.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package view;
-
-import model.Entity;
-import java.awt.Graphics2D;
-
-public abstract class EntityVisualizer {
- public abstract void draw(Graphics2D g, E entity);
-}
\ No newline at end of file
diff --git a/src/view/GameVisualizer.java b/src/view/GameVisualizer.java
deleted file mode 100644
index f2b122c50..000000000
--- a/src/view/GameVisualizer.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package view;
-
-import model.Entity;
-import model.EntityProvider;
-
-import javax.swing.*;
-import java.awt.*;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-public class GameVisualizer extends JPanel {
- private final EntityProvider entityProvider;
- private final Map, EntityVisualizer>> visualizers = new HashMap<>();
-
- public GameVisualizer(EntityProvider entityProvider) {
- this.entityProvider = entityProvider;
- // Регистрируем визуализатор для робота
- visualizers.put(model.Robot.class, new RobotVisualizer());
- }
-
- @Override
- protected void paintComponent(Graphics g) {
- super.paintComponent(g);
- Graphics2D g2d = (Graphics2D) g;
-
- List entities = entityProvider.getEntities();
- for (Entity entity : entities) {
- EntityVisualizer> visualizer = visualizers.get(entity.getClass());
- if (visualizer != null) {
- // Используем cast для корректного типа
- drawEntity(g2d, entity, visualizer);
- }
- }
- }
-
- private void drawEntity(Graphics2D g, E entity, EntityVisualizer> visualizer) {
- @SuppressWarnings("unchecked")
- EntityVisualizer typedVisualizer = (EntityVisualizer) visualizer;
- typedVisualizer.draw(g, entity);
- }
-}
\ No newline at end of file
diff --git a/src/view/RobotVisualizer.java b/src/view/RobotVisualizer.java
deleted file mode 100644
index 85a53ec43..000000000
--- a/src/view/RobotVisualizer.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package view;
-
-import model.Robot;
-import java.awt.*;
-import java.awt.geom.AffineTransform;
-
-public class RobotVisualizer extends EntityVisualizer {
- @Override
- public void draw(Graphics2D g, Robot robot) {
- // Отрисовка робота
- AffineTransform t = AffineTransform.getRotateInstance(robot.getDirection(), robot.getPositionX(), robot.getPositionY());
- g.setTransform(t);
- g.setColor(Color.MAGENTA);
- fillOval(g, (int) robot.getPositionX(), (int) robot.getPositionY(), 30, 10);
- g.setColor(Color.BLACK);
- drawOval(g, (int) robot.getPositionX(), (int) robot.getPositionY(), 30, 10);
- g.setColor(Color.WHITE);
- fillOval(g, (int) robot.getPositionX() + 10, (int) robot.getPositionY(), 5, 5);
- g.setColor(Color.BLACK);
- drawOval(g, (int) robot.getPositionX() + 10, (int) robot.getPositionY(), 5, 5);
-
- // Отрисовка цели
- drawTarget(g, robot.getTargetPositionX(), robot.getTargetPositionY());
- }
-
- 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);
- }
-
- private void fillOval(Graphics g, int centerX, int centerY, int diam1, int diam2) {
- g.fillOval(centerX - diam1 / 2, centerY - diam2 / 2, diam1, diam2);
- }
-
- private void drawOval(Graphics g, int centerX, int centerY, int diam1, int diam2) {
- g.drawOval(centerX - diam1 / 2, centerY - diam2 / 2, diam1, diam2);
- }
-}
\ No newline at end of file
From 02bbe0243d83ade429e6016c5e48c5a3f1a12a89 Mon Sep 17 00:00:00 2001
From: Free-Denis <147907574+Free-Denis@users.noreply.github.com>
Date: Tue, 11 Mar 2025 10:50:58 +0500
Subject: [PATCH 07/12] Add files via upload
---
robots/src/controller/GameController.java | 23 +++++--
robots/src/model/Entity.java | 5 ++
robots/src/model/EntityProvider.java | 7 ++
robots/src/model/GameMath.java | 35 ++++++++++
robots/src/model/Model.java | 31 +++++++++
robots/src/model/Robot.java | 78 +++++++++++++++++++++++
robots/src/view/EntityVisualizer.java | 8 +++
robots/src/view/GameVisualizer.java | 75 +++++++---------------
robots/src/view/RobotVisualizer.java | 45 +++++++++++++
9 files changed, 251 insertions(+), 56 deletions(-)
create mode 100644 robots/src/model/Entity.java
create mode 100644 robots/src/model/EntityProvider.java
create mode 100644 robots/src/model/GameMath.java
create mode 100644 robots/src/model/Model.java
create mode 100644 robots/src/model/Robot.java
create mode 100644 robots/src/view/EntityVisualizer.java
create mode 100644 robots/src/view/RobotVisualizer.java
diff --git a/robots/src/controller/GameController.java b/robots/src/controller/GameController.java
index 2ec779744..d5f09371f 100644
--- a/robots/src/controller/GameController.java
+++ b/robots/src/controller/GameController.java
@@ -1,17 +1,32 @@
package controller;
-import model.RobotModel;
+import model.Model;
import view.GameVisualizer;
import javax.swing.*;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
public class GameController {
- private final RobotModel robotModel;
+ private final Model model;
private final GameVisualizer gameVisualizer;
public GameController() {
- robotModel = new RobotModel();
- gameVisualizer = new GameVisualizer(robotModel);
+ model = new Model(); // Model теперь реализует EntityProvider
+ gameVisualizer = new GameVisualizer(model); // Передаем Model как EntityProvider
+
+ Timer timer = new Timer(10, e -> {
+ model.update(10);
+ gameVisualizer.repaint();
+ });
+ timer.start();
+
+ gameVisualizer.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ model.sendMouseClickEvent(e.getX(), e.getY());
+ }
+ });
}
public void start() {
diff --git a/robots/src/model/Entity.java b/robots/src/model/Entity.java
new file mode 100644
index 000000000..dc985d8bd
--- /dev/null
+++ b/robots/src/model/Entity.java
@@ -0,0 +1,5 @@
+package model;
+
+public interface Entity {
+ void update(double duration);
+}
\ No newline at end of file
diff --git a/robots/src/model/EntityProvider.java b/robots/src/model/EntityProvider.java
new file mode 100644
index 000000000..51f56f7eb
--- /dev/null
+++ b/robots/src/model/EntityProvider.java
@@ -0,0 +1,7 @@
+package model;
+
+import java.util.List;
+
+public interface EntityProvider {
+ List getEntities();
+}
\ No newline at end of file
diff --git a/robots/src/model/GameMath.java b/robots/src/model/GameMath.java
new file mode 100644
index 000000000..90925d3aa
--- /dev/null
+++ b/robots/src/model/GameMath.java
@@ -0,0 +1,35 @@
+package model;
+
+public final class GameMath {
+ private GameMath() {} // Запрещаем создание экземпляров
+
+ public 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);
+ }
+
+ public 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 static double asNormalizedRadians(double angle) {
+ while (angle < 0) {
+ angle += 2 * Math.PI;
+ }
+ while (angle >= 2 * Math.PI) {
+ angle -= 2 * Math.PI;
+ }
+ return angle;
+ }
+
+ public static double applyLimits(double value, double min, double max) {
+ if (value < min)
+ return min;
+ if (value > max)
+ return max;
+ return value;
+ }
+}
\ No newline at end of file
diff --git a/robots/src/model/Model.java b/robots/src/model/Model.java
new file mode 100644
index 000000000..a48f5cd30
--- /dev/null
+++ b/robots/src/model/Model.java
@@ -0,0 +1,31 @@
+package model;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class Model implements EntityProvider { // Реализуем интерфейс EntityProvider
+ private final List entities = new ArrayList<>();
+
+ public Model() {
+ entities.add(new Robot()); // Добавляем робота в модель
+ }
+
+ @Override
+ public List getEntities() { // Реализуем метод интерфейса
+ return entities;
+ }
+
+ public void sendMouseClickEvent(int x, int y) {
+ for (Entity entity : entities) {
+ if (entity instanceof Robot) {
+ ((Robot) entity).setTargetPosition(x, y);
+ }
+ }
+ }
+
+ public void update(double duration) {
+ for (Entity entity : entities) {
+ entity.update(duration);
+ }
+ }
+}
\ No newline at end of file
diff --git a/robots/src/model/Robot.java b/robots/src/model/Robot.java
new file mode 100644
index 000000000..34f7d1fba
--- /dev/null
+++ b/robots/src/model/Robot.java
@@ -0,0 +1,78 @@
+package model;
+
+public class Robot implements Entity {
+ private volatile double positionX = 100;
+ private volatile double positionY = 100;
+ private volatile double direction = 0;
+
+ private volatile int targetPositionX = 150;
+ private volatile int targetPositionY = 100;
+
+ private static final double maxVelocity = 0.1;
+ private static final double maxAngularVelocity = 0.001;
+
+ public double getPositionX() {
+ return positionX;
+ }
+
+ public double getPositionY() {
+ return positionY;
+ }
+
+ public double getDirection() {
+ return direction;
+ }
+
+ public int getTargetPositionX() {
+ return targetPositionX;
+ }
+
+ public int getTargetPositionY() {
+ return targetPositionY;
+ }
+
+ public void setTargetPosition(int x, int y) {
+ targetPositionX = x;
+ targetPositionY = y;
+ }
+
+ @Override
+ public void update(double duration) {
+ double distance = GameMath.distance(targetPositionX, targetPositionY, positionX, positionY);
+ if (distance < 0.5) {
+ return;
+ }
+ double velocity = maxVelocity;
+ double angleToTarget = GameMath.angleTo(positionX, positionY, targetPositionX, targetPositionY);
+ double angularVelocity = 0;
+ if (angleToTarget > direction) {
+ angularVelocity = maxAngularVelocity;
+ }
+ if (angleToTarget < direction) {
+ angularVelocity = -maxAngularVelocity;
+ }
+
+ moveRobot(velocity, angularVelocity, duration);
+ }
+
+ private void moveRobot(double velocity, double angularVelocity, double duration) {
+ velocity = GameMath.applyLimits(velocity, 0, maxVelocity);
+ angularVelocity = GameMath.applyLimits(angularVelocity, -maxAngularVelocity, maxAngularVelocity);
+ double newX = positionX + velocity / angularVelocity *
+ (Math.sin(direction + angularVelocity * duration) -
+ Math.sin(direction));
+ if (!Double.isFinite(newX)) {
+ newX = positionX + velocity * duration * Math.cos(direction);
+ }
+ double newY = positionY - velocity / angularVelocity *
+ (Math.cos(direction + angularVelocity * duration) -
+ Math.cos(direction));
+ if (!Double.isFinite(newY)) {
+ newY = positionY + velocity * duration * Math.sin(direction);
+ }
+ positionX = newX;
+ positionY = newY;
+ double newDirection = GameMath.asNormalizedRadians(direction + angularVelocity * duration);
+ direction = newDirection;
+ }
+}
\ No newline at end of file
diff --git a/robots/src/view/EntityVisualizer.java b/robots/src/view/EntityVisualizer.java
new file mode 100644
index 000000000..58f22be5c
--- /dev/null
+++ b/robots/src/view/EntityVisualizer.java
@@ -0,0 +1,8 @@
+package view;
+
+import model.Entity;
+import java.awt.Graphics2D;
+
+public abstract class EntityVisualizer {
+ public abstract void draw(Graphics2D g, E entity);
+}
\ No newline at end of file
diff --git a/robots/src/view/GameVisualizer.java b/robots/src/view/GameVisualizer.java
index e4bf06de5..f2b122c50 100644
--- a/robots/src/view/GameVisualizer.java
+++ b/robots/src/view/GameVisualizer.java
@@ -1,32 +1,22 @@
package view;
-import model.RobotModel;
+import model.Entity;
+import model.EntityProvider;
import javax.swing.*;
import java.awt.*;
-import java.awt.event.MouseAdapter;
-import java.awt.event.MouseEvent;
-import java.awt.geom.AffineTransform;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
public class GameVisualizer extends JPanel {
- private final RobotModel robotModel;
- private final Timer timer;
+ private final EntityProvider entityProvider;
+ private final Map, EntityVisualizer>> visualizers = new HashMap<>();
- public GameVisualizer(RobotModel robotModel) {
-
- this.robotModel = robotModel;
- timer = new Timer(10, e -> {
- robotModel.update(10);
- repaint();
- });
- timer.start();
-
- addMouseListener(new MouseAdapter() {
- @Override
- public void mouseClicked(MouseEvent e) {
- robotModel.setTargetPosition(e.getX(), e.getY());
- }
- });
+ public GameVisualizer(EntityProvider entityProvider) {
+ this.entityProvider = entityProvider;
+ // Регистрируем визуализатор для робота
+ visualizers.put(model.Robot.class, new RobotVisualizer());
}
@Override
@@ -34,38 +24,19 @@ protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
- drawRobot(g2d, (int) robotModel.getRobotPositionX(), (int) robotModel.getRobotPositionY(), robotModel.getRobotDirection());
- drawTarget(g2d, robotModel.getTargetPositionX(), robotModel.getTargetPositionY());
-
- }
-
- private void drawRobot(Graphics2D g, int x, int y, double direction) {
- AffineTransform t = AffineTransform.getRotateInstance(direction, x, y);
- g.setTransform(t);
- g.setColor(Color.MAGENTA);
- fillOval(g, x, y, 30, 10);
- g.setColor(Color.BLACK);
- drawOval(g, x, y, 30, 10);
- g.setColor(Color.WHITE);
- fillOval(g, x + 10, y, 5, 5);
- g.setColor(Color.BLACK);
- drawOval(g, x + 10, y, 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);
- }
-
- private static void fillOval(Graphics g, int centerX, int centerY, int diam1, int diam2) {
- g.fillOval(centerX - diam1 / 2, centerY - diam2 / 2, diam1, diam2);
+ List entities = entityProvider.getEntities();
+ for (Entity entity : entities) {
+ EntityVisualizer> visualizer = visualizers.get(entity.getClass());
+ if (visualizer != null) {
+ // Используем cast для корректного типа
+ drawEntity(g2d, entity, visualizer);
+ }
+ }
}
- 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 drawEntity(Graphics2D g, E entity, EntityVisualizer> visualizer) {
+ @SuppressWarnings("unchecked")
+ EntityVisualizer typedVisualizer = (EntityVisualizer) visualizer;
+ typedVisualizer.draw(g, entity);
}
}
\ No newline at end of file
diff --git a/robots/src/view/RobotVisualizer.java b/robots/src/view/RobotVisualizer.java
new file mode 100644
index 000000000..85a53ec43
--- /dev/null
+++ b/robots/src/view/RobotVisualizer.java
@@ -0,0 +1,45 @@
+package view;
+
+import model.Robot;
+import java.awt.*;
+import java.awt.geom.AffineTransform;
+
+public class RobotVisualizer extends EntityVisualizer {
+ @Override
+ public void draw(Graphics2D g, Robot robot) {
+ // Отрисовка робота
+ AffineTransform t = AffineTransform.getRotateInstance(robot.getDirection(), robot.getPositionX(), robot.getPositionY());
+ g.setTransform(t);
+ g.setColor(Color.MAGENTA);
+ fillOval(g, (int) robot.getPositionX(), (int) robot.getPositionY(), 30, 10);
+ g.setColor(Color.BLACK);
+ drawOval(g, (int) robot.getPositionX(), (int) robot.getPositionY(), 30, 10);
+ g.setColor(Color.WHITE);
+ fillOval(g, (int) robot.getPositionX() + 10, (int) robot.getPositionY(), 5, 5);
+ g.setColor(Color.BLACK);
+ drawOval(g, (int) robot.getPositionX() + 10, (int) robot.getPositionY(), 5, 5);
+
+ // Отрисовка цели
+ drawTarget(g, robot.getTargetPositionX(), robot.getTargetPositionY());
+ }
+
+ 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);
+ }
+
+ private void fillOval(Graphics g, int centerX, int centerY, int diam1, int diam2) {
+ g.fillOval(centerX - diam1 / 2, centerY - diam2 / 2, diam1, diam2);
+ }
+
+ private void drawOval(Graphics g, int centerX, int centerY, int diam1, int diam2) {
+ g.drawOval(centerX - diam1 / 2, centerY - diam2 / 2, diam1, diam2);
+ }
+}
\ No newline at end of file
From b8d335595fee313a406c95512f9256eb2b320b0e Mon Sep 17 00:00:00 2001
From: Free-Denis <147907574+Free-Denis@users.noreply.github.com>
Date: Tue, 11 Mar 2025 11:00:31 +0500
Subject: [PATCH 08/12] Delete robots/src/model/RobotModel.java
---
robots/src/model/RobotModel.java | 107 -------------------------------
1 file changed, 107 deletions(-)
delete mode 100644 robots/src/model/RobotModel.java
diff --git a/robots/src/model/RobotModel.java b/robots/src/model/RobotModel.java
deleted file mode 100644
index 6fa72eff4..000000000
--- a/robots/src/model/RobotModel.java
+++ /dev/null
@@ -1,107 +0,0 @@
-package model;
-
-public class RobotModel {
- private volatile double robotPositionX = 100;
- private volatile double robotPositionY = 100;
- private volatile double robotDirection = 0;
-
- private volatile int targetPositionX = 150;
- private volatile int targetPositionY = 100;
-
- private static final double maxVelocity = 0.1;
- private static final double maxAngularVelocity = 0.001;
-
- public double getRobotPositionX() {
- return robotPositionX;
- }
-
- public double getRobotPositionY() {
- return robotPositionY;
- }
-
- public double getRobotDirection() {
- return robotDirection;
- }
-
- public int getTargetPositionX() {
- return targetPositionX;
- }
-
- public int getTargetPositionY() {
- return targetPositionY;
- }
-
- public void setTargetPosition(int x, int y) {
- targetPositionX = x;
- targetPositionY = y;
- }
-
- public void update(double duration) {
- double distance = distance(targetPositionX, targetPositionY, robotPositionX, robotPositionY);
- if (distance < 0.5) {
- return;
- }
- double velocity = maxVelocity;
- double angleToTarget = angleTo(robotPositionX, robotPositionY, targetPositionX, targetPositionY);
- double angularVelocity = 0;
- if (angleToTarget > robotDirection) {
- angularVelocity = maxAngularVelocity;
- }
- if (angleToTarget < robotDirection) {
- angularVelocity = -maxAngularVelocity;
- }
-
- moveRobot(velocity, angularVelocity, duration);
- }
-
- private void moveRobot(double velocity, double angularVelocity, double duration) {
- velocity = applyLimits(velocity, 0, maxVelocity);
- angularVelocity = applyLimits(angularVelocity, -maxAngularVelocity, maxAngularVelocity);
- double newX = robotPositionX + velocity / angularVelocity *
- (Math.sin(robotDirection + angularVelocity * duration) -
- Math.sin(robotDirection));
- if (!Double.isFinite(newX)) {
- newX = robotPositionX + velocity * duration * Math.cos(robotDirection);
- }
- double newY = robotPositionY - velocity / angularVelocity *
- (Math.cos(robotDirection + angularVelocity * duration) -
- Math.cos(robotDirection));
- if (!Double.isFinite(newY)) {
- newY = robotPositionY + velocity * duration * Math.sin(robotDirection);
- }
- robotPositionX = newX;
- robotPositionY = newY;
- double newDirection = asNormalizedRadians(robotDirection + angularVelocity * duration);
- robotDirection = newDirection;
- }
-
- 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));
- }
-
- 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 double applyLimits(double value, double min, double max) {
- if (value < min)
- return min;
- if (value > max)
- return max;
- return value;
- }
-}
\ No newline at end of file
From f5c96784a799e8748765a9b5b83d0a1c8d3304a7 Mon Sep 17 00:00:00 2001
From: Free-Denis <147907574+Free-Denis@users.noreply.github.com>
Date: Tue, 11 Mar 2025 11:06:57 +0500
Subject: [PATCH 09/12] Update RobotVisualizer.java
---
robots/src/view/RobotVisualizer.java | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/robots/src/view/RobotVisualizer.java b/robots/src/view/RobotVisualizer.java
index 85a53ec43..68f6e6685 100644
--- a/robots/src/view/RobotVisualizer.java
+++ b/robots/src/view/RobotVisualizer.java
@@ -7,7 +7,7 @@
public class RobotVisualizer extends EntityVisualizer {
@Override
public void draw(Graphics2D g, Robot robot) {
- // Отрисовка робота
+
AffineTransform t = AffineTransform.getRotateInstance(robot.getDirection(), robot.getPositionX(), robot.getPositionY());
g.setTransform(t);
g.setColor(Color.MAGENTA);
@@ -19,16 +19,16 @@ public void draw(Graphics2D g, Robot robot) {
g.setColor(Color.BLACK);
drawOval(g, (int) robot.getPositionX() + 10, (int) robot.getPositionY(), 5, 5);
- // Отрисовка цели
+
drawTarget(g, robot.getTargetPositionX(), robot.getTargetPositionY());
}
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);
@@ -42,4 +42,4 @@ private void fillOval(Graphics g, int centerX, int centerY, int diam1, int diam2
private void drawOval(Graphics g, int centerX, int centerY, int diam1, int diam2) {
g.drawOval(centerX - diam1 / 2, centerY - diam2 / 2, diam1, diam2);
}
-}
\ No newline at end of file
+}
From e699b960cebb90befae2e2b314c6a8b83cc4eac2 Mon Sep 17 00:00:00 2001
From: Free-Denis <147907574+Free-Denis@users.noreply.github.com>
Date: Tue, 18 Mar 2025 12:26:15 +0500
Subject: [PATCH 10/12] Add files via upload
---
robots/src/controller/GameController.java | 44 ++++++++++++++++++++---
robots/src/model/Model.java | 18 +++++-----
robots/src/model/Robot.java | 21 +++++++++++
robots/src/view/RobotVisualizer.java | 24 +++++++++----
4 files changed, 89 insertions(+), 18 deletions(-)
diff --git a/robots/src/controller/GameController.java b/robots/src/controller/GameController.java
index d5f09371f..1a33a2c44 100644
--- a/robots/src/controller/GameController.java
+++ b/robots/src/controller/GameController.java
@@ -10,25 +10,61 @@
public class GameController {
private final Model model;
private final GameVisualizer gameVisualizer;
+ private Timer timer;
public GameController() {
- model = new Model(); // Model теперь реализует EntityProvider
- gameVisualizer = new GameVisualizer(model); // Передаем Model как EntityProvider
+ model = new Model();
+ gameVisualizer = new GameVisualizer(model);
- Timer timer = new Timer(10, e -> {
+ timer = new Timer(10, e -> {
model.update(10);
gameVisualizer.repaint();
+
+ // Проверка завершения игры
+ if (model.getRobot().checkWin()) {
+ endGame("Победа! Вы достигли точки победы!");
+ } else if (model.getRobot().checkLose()) {
+ endGame("Поражение! Вы достигли точки поражения!");
+ }
});
timer.start();
gameVisualizer.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
- model.sendMouseClickEvent(e.getX(), e.getY());
+ model.getRobot().setTargetPosition(e.getX(), e.getY());
}
});
}
+ private void endGame(String message) {
+ timer.stop(); // Останавливаем таймер
+
+ // Создаём диалоговое окно
+ int option = JOptionPane.showOptionDialog(
+ gameVisualizer,
+ message,
+ "Игра завершена",
+ JOptionPane.YES_NO_OPTION,
+ JOptionPane.INFORMATION_MESSAGE,
+ null,
+ new String[]{"Перезапустить", "Выход"},
+ "Перезапустить"
+ );
+
+ if (option == JOptionPane.YES_OPTION) {
+ restartGame();
+ } else {
+ System.exit(0);
+ }
+ }
+
+ private void restartGame() {
+ model.getRobot().setPosition(100, 100); // Сброс позиции робота
+ model.getRobot().setTargetPosition(150, 100); // Сброс цели
+ timer.start(); // Перезапуск таймера
+ }
+
public void start() {
JFrame frame = new JFrame("Game Window");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
diff --git a/robots/src/model/Model.java b/robots/src/model/Model.java
index a48f5cd30..82b6ac311 100644
--- a/robots/src/model/Model.java
+++ b/robots/src/model/Model.java
@@ -3,24 +3,26 @@
import java.util.ArrayList;
import java.util.List;
-public class Model implements EntityProvider { // Реализуем интерфейс EntityProvider
+public class Model implements EntityProvider {
private final List entities = new ArrayList<>();
+ private final Robot robot;
public Model() {
- entities.add(new Robot()); // Добавляем робота в модель
+ robot = new Robot();
+ entities.add(robot);
}
@Override
- public List getEntities() { // Реализуем метод интерфейса
+ public List getEntities() {
return entities;
}
+ public Robot getRobot() {
+ return robot;
+ }
+
public void sendMouseClickEvent(int x, int y) {
- for (Entity entity : entities) {
- if (entity instanceof Robot) {
- ((Robot) entity).setTargetPosition(x, y);
- }
- }
+ robot.setTargetPosition(x, y);
}
public void update(double duration) {
diff --git a/robots/src/model/Robot.java b/robots/src/model/Robot.java
index 34f7d1fba..af919b750 100644
--- a/robots/src/model/Robot.java
+++ b/robots/src/model/Robot.java
@@ -8,6 +8,12 @@ public class Robot implements Entity {
private volatile int targetPositionX = 150;
private volatile int targetPositionY = 100;
+ // Точки победы и поражения
+ private final int winPointX = 500;
+ private final int winPointY = 500;
+ private final int losePointX = 700;
+ private final int losePointY = 100;
+
private static final double maxVelocity = 0.1;
private static final double maxAngularVelocity = 0.001;
@@ -36,6 +42,16 @@ public void setTargetPosition(int x, int y) {
targetPositionY = y;
}
+ public boolean checkWin() {
+ double distanceToWin = GameMath.distance(positionX, positionY, winPointX, winPointY);
+ return distanceToWin < 10; // Робот достиг точки победы
+ }
+
+ public boolean checkLose() {
+ double distanceToLose = GameMath.distance(positionX, positionY, losePointX, losePointY);
+ return distanceToLose < 10; // Робот достиг точки поражения
+ }
+
@Override
public void update(double duration) {
double distance = GameMath.distance(targetPositionX, targetPositionY, positionX, positionY);
@@ -75,4 +91,9 @@ private void moveRobot(double velocity, double angularVelocity, double duration)
double newDirection = GameMath.asNormalizedRadians(direction + angularVelocity * duration);
direction = newDirection;
}
+
+ public void setPosition(double x, double y) {
+ this.positionX = x;
+ this.positionY = y;
+ }
}
\ No newline at end of file
diff --git a/robots/src/view/RobotVisualizer.java b/robots/src/view/RobotVisualizer.java
index 68f6e6685..22f8d69c4 100644
--- a/robots/src/view/RobotVisualizer.java
+++ b/robots/src/view/RobotVisualizer.java
@@ -7,7 +7,7 @@
public class RobotVisualizer extends EntityVisualizer {
@Override
public void draw(Graphics2D g, Robot robot) {
-
+ // Отрисовка робота
AffineTransform t = AffineTransform.getRotateInstance(robot.getDirection(), robot.getPositionX(), robot.getPositionY());
g.setTransform(t);
g.setColor(Color.MAGENTA);
@@ -19,22 +19,34 @@ public void draw(Graphics2D g, Robot robot) {
g.setColor(Color.BLACK);
drawOval(g, (int) robot.getPositionX() + 10, (int) robot.getPositionY(), 5, 5);
-
+ // Отрисовка цели
drawTarget(g, robot.getTargetPositionX(), robot.getTargetPositionY());
+
+ // Отрисовка точки победы
+ drawPoint(g, 500, 500, Color.BLUE, "Win");
+
+ // Отрисовка точки поражения
+ drawPoint(g, 700, 100, Color.RED, "Lose");
}
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);
}
+ private void drawPoint(Graphics2D g, int x, int y, Color color, String label) {
+ AffineTransform t = AffineTransform.getRotateInstance(0, 0, 0);
+ g.setTransform(t);
+ g.setColor(color);
+ fillOval(g, x, y, 10, 10);
+ g.setColor(Color.BLACK);
+ g.drawString(label, x + 15, y + 5);
+ }
+
private void fillOval(Graphics g, int centerX, int centerY, int diam1, int diam2) {
g.fillOval(centerX - diam1 / 2, centerY - diam2 / 2, diam1, diam2);
}
@@ -42,4 +54,4 @@ private void fillOval(Graphics g, int centerX, int centerY, int diam1, int diam2
private void drawOval(Graphics g, int centerX, int centerY, int diam1, int diam2) {
g.drawOval(centerX - diam1 / 2, centerY - diam2 / 2, diam1, diam2);
}
-}
+}
\ No newline at end of file
From d11e12e41f5b241bca334cc525d371ed130daecb Mon Sep 17 00:00:00 2001
From: Free-Denis <147907574+Free-Denis@users.noreply.github.com>
Date: Tue, 18 Mar 2025 14:18:08 +0500
Subject: [PATCH 11/12] Add files via upload
---
robots/src/controller/GameController.java | 16 +++++-------
robots/src/model/LosePoint.java | 32 +++++++++++++++++++++++
robots/src/model/Model.java | 15 +++++++++++
robots/src/model/Robot.java | 16 ------------
robots/src/model/WinPoint.java | 32 +++++++++++++++++++++++
robots/src/view/GameVisualizer.java | 4 +--
robots/src/view/LosePointVisualizer.java | 14 ++++++++++
robots/src/view/RobotVisualizer.java | 17 ------------
robots/src/view/WinPointVisualizer.java | 14 ++++++++++
9 files changed, 116 insertions(+), 44 deletions(-)
create mode 100644 robots/src/model/LosePoint.java
create mode 100644 robots/src/model/WinPoint.java
create mode 100644 robots/src/view/LosePointVisualizer.java
create mode 100644 robots/src/view/WinPointVisualizer.java
diff --git a/robots/src/controller/GameController.java b/robots/src/controller/GameController.java
index 1a33a2c44..ebf2f61e2 100644
--- a/robots/src/controller/GameController.java
+++ b/robots/src/controller/GameController.java
@@ -20,10 +20,9 @@ public GameController() {
model.update(10);
gameVisualizer.repaint();
- // Проверка завершения игры
- if (model.getRobot().checkWin()) {
+ if (model.getWinPoint().checkWin(model.getRobot())) {
endGame("Победа! Вы достигли точки победы!");
- } else if (model.getRobot().checkLose()) {
+ } else if (model.getLosePoint().checkLose(model.getRobot())) {
endGame("Поражение! Вы достигли точки поражения!");
}
});
@@ -32,15 +31,14 @@ public GameController() {
gameVisualizer.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
- model.getRobot().setTargetPosition(e.getX(), e.getY());
+ model.sendMouseClickEvent(e.getX(), e.getY());
}
});
}
private void endGame(String message) {
- timer.stop(); // Останавливаем таймер
+ timer.stop();
- // Создаём диалоговое окно
int option = JOptionPane.showOptionDialog(
gameVisualizer,
message,
@@ -60,9 +58,9 @@ private void endGame(String message) {
}
private void restartGame() {
- model.getRobot().setPosition(100, 100); // Сброс позиции робота
- model.getRobot().setTargetPosition(150, 100); // Сброс цели
- timer.start(); // Перезапуск таймера
+ model.getRobot().setPosition(100, 100);
+ model.getRobot().setTargetPosition(150, 100);
+ timer.start();
}
public void start() {
diff --git a/robots/src/model/LosePoint.java b/robots/src/model/LosePoint.java
new file mode 100644
index 000000000..019c2eb9e
--- /dev/null
+++ b/robots/src/model/LosePoint.java
@@ -0,0 +1,32 @@
+package model;
+
+public class LosePoint implements Entity {
+ private final int x;
+ private final int y;
+ private final int size = 100;
+
+ public LosePoint(int x, int y) {
+ this.x = x;
+ this.y = y;
+ }
+
+ public int getX() {
+ return x;
+ }
+
+ public int getY() {
+ return y;
+ }
+
+ public int getSize() {
+ return size;
+ }
+
+ @Override
+ public void update(double duration) {}
+
+ public boolean checkLose(Robot robot) {
+ double distance = GameMath.distance(x, y, robot.getPositionX(), robot.getPositionY());
+ return distance < size / 2;
+ }
+}
\ No newline at end of file
diff --git a/robots/src/model/Model.java b/robots/src/model/Model.java
index 82b6ac311..f809d26b0 100644
--- a/robots/src/model/Model.java
+++ b/robots/src/model/Model.java
@@ -6,10 +6,17 @@
public class Model implements EntityProvider {
private final List entities = new ArrayList<>();
private final Robot robot;
+ private final WinPoint winPoint;
+ private final LosePoint losePoint;
public Model() {
robot = new Robot();
+ winPoint = new WinPoint(500, 500);
+ losePoint = new LosePoint(700, 700);
+
entities.add(robot);
+ entities.add(winPoint);
+ entities.add(losePoint);
}
@Override
@@ -21,6 +28,14 @@ public Robot getRobot() {
return robot;
}
+ public WinPoint getWinPoint() {
+ return winPoint;
+ }
+
+ public LosePoint getLosePoint() {
+ return losePoint;
+ }
+
public void sendMouseClickEvent(int x, int y) {
robot.setTargetPosition(x, y);
}
diff --git a/robots/src/model/Robot.java b/robots/src/model/Robot.java
index af919b750..02a813326 100644
--- a/robots/src/model/Robot.java
+++ b/robots/src/model/Robot.java
@@ -8,12 +8,6 @@ public class Robot implements Entity {
private volatile int targetPositionX = 150;
private volatile int targetPositionY = 100;
- // Точки победы и поражения
- private final int winPointX = 500;
- private final int winPointY = 500;
- private final int losePointX = 700;
- private final int losePointY = 100;
-
private static final double maxVelocity = 0.1;
private static final double maxAngularVelocity = 0.001;
@@ -42,16 +36,6 @@ public void setTargetPosition(int x, int y) {
targetPositionY = y;
}
- public boolean checkWin() {
- double distanceToWin = GameMath.distance(positionX, positionY, winPointX, winPointY);
- return distanceToWin < 10; // Робот достиг точки победы
- }
-
- public boolean checkLose() {
- double distanceToLose = GameMath.distance(positionX, positionY, losePointX, losePointY);
- return distanceToLose < 10; // Робот достиг точки поражения
- }
-
@Override
public void update(double duration) {
double distance = GameMath.distance(targetPositionX, targetPositionY, positionX, positionY);
diff --git a/robots/src/model/WinPoint.java b/robots/src/model/WinPoint.java
new file mode 100644
index 000000000..f1e588e37
--- /dev/null
+++ b/robots/src/model/WinPoint.java
@@ -0,0 +1,32 @@
+package model;
+
+public class WinPoint implements Entity {
+ private final int x;
+ private final int y;
+ private final int size = 100;
+
+ public WinPoint(int x, int y) {
+ this.x = x;
+ this.y = y;
+ }
+
+ public int getX() {
+ return x;
+ }
+
+ public int getY() {
+ return y;
+ }
+
+ public int getSize() {
+ return size;
+ }
+
+ @Override
+ public void update(double duration) {}
+
+ public boolean checkWin(Robot robot) {
+ double distance = GameMath.distance(x, y, robot.getPositionX(), robot.getPositionY());
+ return distance < size / 2;
+ }
+}
\ No newline at end of file
diff --git a/robots/src/view/GameVisualizer.java b/robots/src/view/GameVisualizer.java
index f2b122c50..e810732bb 100644
--- a/robots/src/view/GameVisualizer.java
+++ b/robots/src/view/GameVisualizer.java
@@ -15,8 +15,9 @@ public class GameVisualizer extends JPanel {
public GameVisualizer(EntityProvider entityProvider) {
this.entityProvider = entityProvider;
- // Регистрируем визуализатор для робота
visualizers.put(model.Robot.class, new RobotVisualizer());
+ visualizers.put(model.WinPoint.class, new WinPointVisualizer());
+ visualizers.put(model.LosePoint.class, new LosePointVisualizer());
}
@Override
@@ -28,7 +29,6 @@ protected void paintComponent(Graphics g) {
for (Entity entity : entities) {
EntityVisualizer> visualizer = visualizers.get(entity.getClass());
if (visualizer != null) {
- // Используем cast для корректного типа
drawEntity(g2d, entity, visualizer);
}
}
diff --git a/robots/src/view/LosePointVisualizer.java b/robots/src/view/LosePointVisualizer.java
new file mode 100644
index 000000000..b3ee1a564
--- /dev/null
+++ b/robots/src/view/LosePointVisualizer.java
@@ -0,0 +1,14 @@
+package view;
+
+import model.LosePoint;
+import java.awt.*;
+
+public class LosePointVisualizer extends EntityVisualizer {
+ @Override
+ public void draw(Graphics2D g, LosePoint losePoint) {
+ g.setColor(Color.RED);
+ g.fillOval(losePoint.getX() - losePoint.getSize() / 2, losePoint.getY() - losePoint.getSize() / 2, losePoint.getSize(), losePoint.getSize());
+ g.setColor(Color.BLACK);
+ g.drawString("Lose", losePoint.getX() + losePoint.getSize() / 2 + 5, losePoint.getY());
+ }
+}
\ No newline at end of file
diff --git a/robots/src/view/RobotVisualizer.java b/robots/src/view/RobotVisualizer.java
index 22f8d69c4..65779f74c 100644
--- a/robots/src/view/RobotVisualizer.java
+++ b/robots/src/view/RobotVisualizer.java
@@ -7,7 +7,6 @@
public class RobotVisualizer extends EntityVisualizer {
@Override
public void draw(Graphics2D g, Robot robot) {
- // Отрисовка робота
AffineTransform t = AffineTransform.getRotateInstance(robot.getDirection(), robot.getPositionX(), robot.getPositionY());
g.setTransform(t);
g.setColor(Color.MAGENTA);
@@ -19,14 +18,7 @@ public void draw(Graphics2D g, Robot robot) {
g.setColor(Color.BLACK);
drawOval(g, (int) robot.getPositionX() + 10, (int) robot.getPositionY(), 5, 5);
- // Отрисовка цели
drawTarget(g, robot.getTargetPositionX(), robot.getTargetPositionY());
-
- // Отрисовка точки победы
- drawPoint(g, 500, 500, Color.BLUE, "Win");
-
- // Отрисовка точки поражения
- drawPoint(g, 700, 100, Color.RED, "Lose");
}
private void drawTarget(Graphics2D g, int x, int y) {
@@ -38,15 +30,6 @@ private void drawTarget(Graphics2D g, int x, int y) {
drawOval(g, x, y, 5, 5);
}
- private void drawPoint(Graphics2D g, int x, int y, Color color, String label) {
- AffineTransform t = AffineTransform.getRotateInstance(0, 0, 0);
- g.setTransform(t);
- g.setColor(color);
- fillOval(g, x, y, 10, 10);
- g.setColor(Color.BLACK);
- g.drawString(label, x + 15, y + 5);
- }
-
private void fillOval(Graphics g, int centerX, int centerY, int diam1, int diam2) {
g.fillOval(centerX - diam1 / 2, centerY - diam2 / 2, diam1, diam2);
}
diff --git a/robots/src/view/WinPointVisualizer.java b/robots/src/view/WinPointVisualizer.java
new file mode 100644
index 000000000..ced9ffed3
--- /dev/null
+++ b/robots/src/view/WinPointVisualizer.java
@@ -0,0 +1,14 @@
+package view;
+
+import model.WinPoint;
+import java.awt.*;
+
+public class WinPointVisualizer extends EntityVisualizer {
+ @Override
+ public void draw(Graphics2D g, WinPoint winPoint) {
+ g.setColor(Color.BLUE);
+ g.fillOval(winPoint.getX() - winPoint.getSize() / 2, winPoint.getY() - winPoint.getSize() / 2, winPoint.getSize(), winPoint.getSize());
+ g.setColor(Color.BLACK);
+ g.drawString("Win", winPoint.getX() + winPoint.getSize() / 2 + 5, winPoint.getY());
+ }
+}
\ No newline at end of file
From 5f03a618eb18e2d819c6dbc4dd9fdc3f2489d047 Mon Sep 17 00:00:00 2001
From: Free-Denis <147907574+Free-Denis@users.noreply.github.com>
Date: Tue, 20 May 2025 11:50:46 +0500
Subject: [PATCH 12/12] Add files via upload
---
src/Main.java | 11 ++
src/controller/GameController.java | 111 ++++++++++++++
src/model/GameMath.java | 35 +++++
src/model/GameState.java | 7 +
src/model/Model.java | 151 +++++++++++++++++++
src/model/ModelController.java | 24 +++
src/model/NavigationGrid.java | 147 +++++++++++++++++++
src/model/TimerModel.java | 31 ++++
src/model/entities/Entity.java | 7 +
src/model/entities/EntityProvider.java | 13 ++
src/model/entities/LosePoint.java | 43 ++++++
src/model/entities/Obstacle.java | 29 ++++
src/model/entities/Robot.java | 193 +++++++++++++++++++++++++
src/model/entities/WinPoint.java | 43 ++++++
src/view/EntityVisualizer.java | 8 +
src/view/GameVisualizer.java | 49 +++++++
src/view/LosePointVisualizer.java | 14 ++
src/view/ObstacleVisualizer.java | 14 ++
src/view/RobotVisualizer.java | 47 ++++++
src/view/TimerView.java | 34 +++++
src/view/WinPointVisualizer.java | 14 ++
21 files changed, 1025 insertions(+)
create mode 100644 src/Main.java
create mode 100644 src/controller/GameController.java
create mode 100644 src/model/GameMath.java
create mode 100644 src/model/GameState.java
create mode 100644 src/model/Model.java
create mode 100644 src/model/ModelController.java
create mode 100644 src/model/NavigationGrid.java
create mode 100644 src/model/TimerModel.java
create mode 100644 src/model/entities/Entity.java
create mode 100644 src/model/entities/EntityProvider.java
create mode 100644 src/model/entities/LosePoint.java
create mode 100644 src/model/entities/Obstacle.java
create mode 100644 src/model/entities/Robot.java
create mode 100644 src/model/entities/WinPoint.java
create mode 100644 src/view/EntityVisualizer.java
create mode 100644 src/view/GameVisualizer.java
create mode 100644 src/view/LosePointVisualizer.java
create mode 100644 src/view/ObstacleVisualizer.java
create mode 100644 src/view/RobotVisualizer.java
create mode 100644 src/view/TimerView.java
create mode 100644 src/view/WinPointVisualizer.java
diff --git a/src/Main.java b/src/Main.java
new file mode 100644
index 000000000..700ada244
--- /dev/null
+++ b/src/Main.java
@@ -0,0 +1,11 @@
+import controller.GameController;
+import javax.swing.*;
+
+public class Main {
+ public static void main(String[] args) {
+ SwingUtilities.invokeLater(() -> {
+ GameController gameController = new GameController();
+ gameController.start();
+ });
+ }
+}
\ No newline at end of file
diff --git a/src/controller/GameController.java b/src/controller/GameController.java
new file mode 100644
index 000000000..a0fb4876b
--- /dev/null
+++ b/src/controller/GameController.java
@@ -0,0 +1,111 @@
+package controller;
+
+import model.GameState;
+import model.Model;
+import view.GameVisualizer;
+import view.TimerView;
+
+import javax.swing.*;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+
+public class GameController {
+ private final GameVisualizer gameVisualizer;
+ private Timer timer;
+ private final Model model;
+ private TimerView timerView;
+ private Timer uiTimer;
+
+ public GameController() {
+ model = new Model();
+ gameVisualizer = new GameVisualizer(model);
+ timerView = new TimerView();
+
+ model.setUpdateDuration(10);
+
+ timer = new Timer(10, e -> {
+ model.update(model.getUpdateDuration());
+ gameVisualizer.repaint();
+
+ GameState state = model.checkGameState();
+
+ if (state != GameState.PLAYING) {
+ timer.stop();
+ uiTimer.stop();
+ String message = (state == GameState.VICTORY)
+ ? "Победа! Вы достигли точки победы!"
+ : "Поражение! Время вышло!";
+ endGame(message);
+ }
+ });
+
+ uiTimer = new Timer(100, e -> {
+ timerView.updateTime(model.getTimerModel().getRemainingTimeMillis());
+ });
+
+ gameVisualizer.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ if (model.checkGameState() == GameState.PLAYING) {
+ model.setRobotTargetPosition(e.getX(), e.getY());
+ }
+ }
+ });
+ }
+
+ private void endGame(String message) {
+ timer.stop();
+ uiTimer.stop();
+
+ int option = JOptionPane.showOptionDialog(
+ gameVisualizer,
+ message,
+ "Игра завершена",
+ JOptionPane.YES_NO_OPTION,
+ JOptionPane.INFORMATION_MESSAGE,
+ null,
+ new String[]{"Перезапустить", "Выход"},
+ "Перезапустить"
+ );
+
+ if (option == JOptionPane.YES_OPTION) {
+ restartGame();
+ } else {
+ timerView.dispose();
+ System.exit(0);
+ }
+ }
+
+ private void restartGame() {
+
+ timer.stop();
+ uiTimer.stop();
+ timerView.dispose();
+
+
+ timerView = new TimerView();
+ timerView.setVisible(true);
+
+
+ model.resetGame();
+ gameVisualizer.repaint();
+
+
+ timer.start();
+ uiTimer.start();
+ model.getTimerModel().start();
+ }
+
+ public void start() {
+ JFrame frame = new JFrame("Game Window");
+ frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ frame.add(gameVisualizer);
+ frame.setSize(800, 600);
+ frame.setVisible(true);
+
+ timerView.setVisible(true);
+ timer.start();
+ uiTimer.start();
+ model.getTimerModel().start();
+ }
+}
\ No newline at end of file
diff --git a/src/model/GameMath.java b/src/model/GameMath.java
new file mode 100644
index 000000000..90925d3aa
--- /dev/null
+++ b/src/model/GameMath.java
@@ -0,0 +1,35 @@
+package model;
+
+public final class GameMath {
+ private GameMath() {} // Запрещаем создание экземпляров
+
+ public 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);
+ }
+
+ public 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 static double asNormalizedRadians(double angle) {
+ while (angle < 0) {
+ angle += 2 * Math.PI;
+ }
+ while (angle >= 2 * Math.PI) {
+ angle -= 2 * Math.PI;
+ }
+ return angle;
+ }
+
+ public static double applyLimits(double value, double min, double max) {
+ if (value < min)
+ return min;
+ if (value > max)
+ return max;
+ return value;
+ }
+}
\ No newline at end of file
diff --git a/src/model/GameState.java b/src/model/GameState.java
new file mode 100644
index 000000000..a0df6e631
--- /dev/null
+++ b/src/model/GameState.java
@@ -0,0 +1,7 @@
+package model;
+
+public enum GameState {
+ PLAYING,
+ VICTORY,
+ DEFEAT
+}
diff --git a/src/model/Model.java b/src/model/Model.java
new file mode 100644
index 000000000..18ddb867f
--- /dev/null
+++ b/src/model/Model.java
@@ -0,0 +1,151 @@
+package model;
+
+import model.entities.*;
+import model.entities.Robot;
+
+import java.awt.*;
+import java.util.*;
+import java.util.List;
+import java.util.function.Predicate;
+
+public class Model implements EntityProvider {
+ private final List entities = new ArrayList<>();
+ private GameState currentGameState = GameState.PLAYING;
+ private final ModelController internalController;
+ private double updateDuration = 10;
+ private final TimerModel timerModel;
+ private final NavigationGrid navigationGrid;
+ private final int worldWidth = 1900;
+ private final int worldHeight = 1000;
+ private final int cellWidth = 20;
+ private final int cellHeight = 20;
+
+ public Model() {
+ this.internalController = new ModelController(this);
+ this.navigationGrid = new NavigationGrid(worldWidth, worldHeight, cellWidth, cellHeight);
+ initializeLevel();
+ this.timerModel = new TimerModel();
+ resetGame();
+ }
+ private void generateRandomObstacles(int count) {
+ Random random = new Random();
+
+ for (int i = 0; i < count; i++) {
+
+ int x = random.nextInt(worldWidth / cellWidth) * cellWidth;
+ int y = random.nextInt(worldHeight / cellHeight) * cellHeight;
+ int width = cellWidth;
+ int height = cellHeight;
+
+
+ Obstacle obstacle = new Obstacle(x, y, width, height);
+ entities.add(obstacle);
+
+
+ Point gridCell = navigationGrid.worldToGrid(x, y);
+ navigationGrid.addObstacleCell(gridCell.y, gridCell.x);
+ }
+ }
+ private void initializeLevel() {
+ entities.clear();
+ navigationGrid.resetGrid();
+ generateRandomObstacles(1200);
+ addObstacleRect(200, 200, 40, 120);
+ addObstacleRect(400, 300, 120, 40);
+
+ Robot robot = new Robot(navigationGrid);
+ robot.setPosition(100, 100);
+ entities.add(robot);
+
+ entities.add(new WinPoint(500, 500));
+ entities.add(new LosePoint(700, 700));
+
+ }
+
+ private void addObstacleRect(int worldX, int worldY, int width, int height) {
+ Point topLeft = navigationGrid.worldToGrid(worldX, worldY);
+ Point bottomRight = navigationGrid.worldToGrid(worldX + width -1, worldY + height -1);
+
+ for (int r = topLeft.y; r <= bottomRight.y; r++) {
+ for (int c = topLeft.x; c <= bottomRight.x; c++) {
+ navigationGrid.addObstacleCell(r, c);
+ }
+ }
+ }
+
+ public void resetGame() {
+ currentGameState = GameState.PLAYING;
+ timerModel.reset();
+ initializeLevel();
+ }
+
+ public void update(double duration) {
+ if (currentGameState == GameState.PLAYING) {
+ if (timerModel.isTimeExpired()) {
+ setGameState(GameState.DEFEAT);
+ return;
+ }
+ List currentEntities = new ArrayList<>(entities);
+ for (Entity entity : currentEntities) {
+ entity.update(this.internalController, duration);
+ }
+ }
+ }
+ public TimerModel getTimerModel() {
+ return timerModel;
+ }
+ public void setRobotTargetPosition(int worldX, int worldY) {
+ findFirst(e -> e instanceof Robot)
+ .map(e -> (Robot) e)
+ .ifPresent(robot -> {
+ double startX = robot.getPositionX();
+ double startY = robot.getPositionY();
+
+
+ Point targetCell = navigationGrid.worldToGrid(worldX, worldY);
+ if (!navigationGrid.isTraversable(targetCell.y, targetCell.x)) {
+ return;
+ }
+
+ List gridPath = navigationGrid.findPath(startX, startY, worldX, worldY);
+ robot.setPath(gridPath, worldX, worldY);
+ });
+ }
+
+ @Override
+ public List getEntities() {
+ return Collections.unmodifiableList(entities);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Optional findFirst(Predicate predicate) {
+ return (Optional) entities.stream()
+ .filter(predicate)
+ .findFirst();
+ }
+
+ @Override
+ public GameState checkGameState() {
+ return currentGameState;
+ }
+
+ public void setGameState(GameState newState) {
+ if (this.currentGameState == GameState.PLAYING) {
+ this.currentGameState = newState;
+ System.out.println("Game State Changed To: " + newState);
+ }
+ }
+
+ public void setUpdateDuration(double duration) {
+ this.updateDuration = duration;
+ }
+
+ public double getUpdateDuration() {
+ return updateDuration;
+ }
+
+ public NavigationGrid getNavigationGrid() {
+ return navigationGrid;
+ }
+}
diff --git a/src/model/ModelController.java b/src/model/ModelController.java
new file mode 100644
index 000000000..2389805d8
--- /dev/null
+++ b/src/model/ModelController.java
@@ -0,0 +1,24 @@
+package model;
+
+import model.entities.Entity;
+
+import java.util.Optional;
+import java.util.function.Predicate;
+
+public class ModelController {
+ private final Model model;
+
+ public ModelController(Model model) {
+ this.model = model;
+ }
+
+
+ public Optional findFirst(Predicate predicate) {
+ return model.findFirst(predicate);
+ }
+
+ public void setGameState(GameState state) {
+ model.setGameState(state);
+ }
+
+}
\ No newline at end of file
diff --git a/src/model/NavigationGrid.java b/src/model/NavigationGrid.java
new file mode 100644
index 000000000..854601364
--- /dev/null
+++ b/src/model/NavigationGrid.java
@@ -0,0 +1,147 @@
+package model;
+
+import java.awt.Point;
+import java.util.*;
+
+public class NavigationGrid {
+
+ private final int worldWidth;
+ private final int worldHeight;
+ private final int cellWidth;
+ private final int cellHeight;
+ private final int gridCols; // ширина сетки в ячейках
+ private final int gridRows; // высота
+ private final boolean[][] traversable;
+
+ private static final Point[] NEIGHBOR_OFFSETS = {
+ new Point(0, -1), // Вверх (row - 1)
+ new Point(0, 1), // Вниз (row + 1)
+ new Point(-1, 0), // Влево (col - 1)
+ new Point(1, 0) // Вправо (col + 1)
+ };
+
+ public NavigationGrid(int worldWidth, int worldHeight, int cellWidth, int cellHeight) {
+ this.worldWidth = worldWidth;
+ this.worldHeight = worldHeight;
+ this.cellWidth = cellWidth;
+ this.cellHeight = cellHeight;
+
+ this.gridCols = (int) Math.ceil((double) worldWidth / cellWidth);
+ this.gridRows = (int) Math.ceil((double) worldHeight / cellHeight);
+
+ this.traversable = new boolean[gridRows][gridCols];
+ for (int r = 0; r < gridRows; r++) {
+ Arrays.fill(traversable[r], true);
+ }
+ }
+
+ public Point worldToGrid(double worldX, double worldY) {
+ int col = (int) Math.floor(worldX / cellWidth);
+ int row = (int) Math.floor(worldY / cellHeight);
+ col = Math.max(0, Math.min(gridCols - 1, col));
+ row = Math.max(0, Math.min(gridRows - 1, row));
+ return new Point(col, row);
+ }
+
+ public Point gridCellCenterToWorld(Point gridCell) {
+ return gridCellCenterToWorld(gridCell.y, gridCell.x);
+ }
+
+ public Point gridCellCenterToWorld(int row, int col) {
+ int worldX = (int) (col * cellWidth + cellWidth / 2.0);
+ int worldY = (int) (row * cellHeight + cellHeight / 2.0);
+ return new Point(worldX, worldY);
+ }
+
+
+ public void addObstacleCell(int row, int col) {
+ if (isValid(row, col)) {
+ traversable[row][col] = false;
+ }
+ }
+
+
+
+ public boolean isTraversable(int row, int col) {
+ return isValid(row, col) && traversable[row][col];
+ }
+
+
+ boolean isValid(int row, int col) {
+ return row >= 0 && row < gridRows && col >= 0 && col < gridCols;
+ }
+
+ public List findPath(double startWorldX, double startWorldY, double endWorldX, double endWorldY) {
+ Point startCell = worldToGrid(startWorldX, startWorldY);
+ Point endCell = worldToGrid(endWorldX, endWorldY);
+
+ if (!isTraversable(endCell.y, endCell.x)) {
+ return Collections.emptyList();
+ }
+// if (!isTraversable(startCell.y, startCell.x)) {
+// //System.err.println("стартовая ячейка непроходима: " + startCell);
+// return Collections.emptyList();
+// }
+ if (!isTraversable(endCell.y, endCell.x)) {
+ //System.err.println("конечная ячейка непроходима: " + endCell);
+ return Collections.emptyList();
+ }
+
+ if (startCell.equals(endCell)) {
+ return Collections.emptyList();
+ }
+
+ Deque queue = new ArrayDeque<>();
+ Map cameFrom = new HashMap<>();
+ Set visited = new HashSet<>();
+
+ queue.offer(startCell);
+ visited.add(startCell);
+ cameFrom.put(startCell, null);
+
+ Point current;
+ boolean pathFound = false;
+
+
+ while (!queue.isEmpty()) {
+ current = queue.poll();
+
+ if (current.equals(endCell)) {
+ pathFound = true;
+ break;
+ }
+
+ for (Point offset : NEIGHBOR_OFFSETS) {
+ int nextRow = current.y + offset.y; // offset.y - это смещение по строке
+ int nextCol = current.x + offset.x; // offset.x - это смещение по столбцу
+ Point neighbor = new Point(nextCol, nextRow);
+
+ if (isTraversable(nextRow, nextCol) && !visited.contains(neighbor)) {
+ visited.add(neighbor);
+ cameFrom.put(neighbor, current);
+ queue.offer(neighbor);
+ }
+ }
+ }
+
+ if (pathFound) {
+ LinkedList path = new LinkedList<>();
+ Point step = endCell;
+ while (step != null && !step.equals(startCell)) {
+ path.addFirst(step);
+ step = cameFrom.get(step);
+ }
+ return path;
+ } else {
+ return Collections.emptyList();
+ }
+ }
+
+ public void resetGrid() {
+ for (int r = 0; r < gridRows; r++) {
+ for (int c = 0; c < gridCols; c++) {
+ traversable[r][c] = true; // все ячейки проходимы
+ }
+ }
+ }
+}
diff --git a/src/model/TimerModel.java b/src/model/TimerModel.java
new file mode 100644
index 000000000..a4904fde4
--- /dev/null
+++ b/src/model/TimerModel.java
@@ -0,0 +1,31 @@
+// src/model/TimerModel.java
+package model;
+
+public class TimerModel {
+ private long startTime;
+ private final long timeLimitMillis = 120000;
+ private boolean isRunning;
+
+ public void start() {
+ this.startTime = System.currentTimeMillis();
+ this.isRunning = true;
+ }
+
+ public void stop() {
+ this.isRunning = false;
+ }
+
+ public void reset() {
+ start();
+ }
+
+ public long getRemainingTimeMillis() {
+ if (!isRunning) return timeLimitMillis;
+ long elapsed = System.currentTimeMillis() - startTime;
+ return Math.max(0, timeLimitMillis - elapsed);
+ }
+
+ public boolean isTimeExpired() {
+ return getRemainingTimeMillis() <= 0;
+ }
+}
\ No newline at end of file
diff --git a/src/model/entities/Entity.java b/src/model/entities/Entity.java
new file mode 100644
index 000000000..6be411178
--- /dev/null
+++ b/src/model/entities/Entity.java
@@ -0,0 +1,7 @@
+package model.entities;
+
+import model.ModelController;
+
+public interface Entity {
+ void update(ModelController controller, double duration);
+}
\ No newline at end of file
diff --git a/src/model/entities/EntityProvider.java b/src/model/entities/EntityProvider.java
new file mode 100644
index 000000000..2e3dbeb57
--- /dev/null
+++ b/src/model/entities/EntityProvider.java
@@ -0,0 +1,13 @@
+package model.entities;
+
+import model.GameState;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.function.Predicate;
+
+public interface EntityProvider {
+ List getEntities();
+ Optional findFirst(Predicate predicate);
+ GameState checkGameState();
+}
\ No newline at end of file
diff --git a/src/model/entities/LosePoint.java b/src/model/entities/LosePoint.java
new file mode 100644
index 000000000..0b4b6875e
--- /dev/null
+++ b/src/model/entities/LosePoint.java
@@ -0,0 +1,43 @@
+package model.entities;
+
+import model.ModelController;
+import model.GameMath;
+import model.GameState;
+
+import java.util.Optional;
+
+public class LosePoint implements Entity {
+ private final int x;
+ private final int y;
+ private final int size = 100;
+
+ public LosePoint(int x, int y) {
+ this.x = x;
+ this.y = y;
+ }
+
+ public int getX() {
+ return x;
+ }
+
+ public int getY() {
+ return y;
+ }
+
+ public int getSize() {
+ return size;
+ }
+
+ @Override
+ public void update(ModelController controller, double duration) {
+ Optional robotOpt = controller.findFirst(e -> e instanceof Robot).map(e -> (Robot) e);
+
+ if (robotOpt.isPresent()) {
+ Robot robot = robotOpt.get();
+ double distance = GameMath.distance(x, y, robot.getPositionX(), robot.getPositionY());
+ if (distance < size / 2.0) {
+ controller.setGameState(GameState.DEFEAT);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/model/entities/Obstacle.java b/src/model/entities/Obstacle.java
new file mode 100644
index 000000000..b00e7e001
--- /dev/null
+++ b/src/model/entities/Obstacle.java
@@ -0,0 +1,29 @@
+// src/model/entities/Obstacle.java
+package model.entities;
+
+import model.ModelController;
+import java.awt.*;
+
+public class Obstacle implements Entity {
+ private final int x;
+ private final int y;
+ private final int width;
+ private final int height;
+
+ public Obstacle(int x, int y, int width, int height) {
+ this.x = x;
+ this.y = y;
+ this.width = width;
+ this.height = height;
+ }
+
+ public int getX() { return x; }
+ public int getY() { return y; }
+ public int getWidth() { return width; }
+ public int getHeight() { return height; }
+
+ @Override
+ public void update(ModelController controller, double duration) {
+
+ }
+}
\ No newline at end of file
diff --git a/src/model/entities/Robot.java b/src/model/entities/Robot.java
new file mode 100644
index 000000000..d9230dac2
--- /dev/null
+++ b/src/model/entities/Robot.java
@@ -0,0 +1,193 @@
+package model.entities;
+
+import model.ModelController;
+import model.GameMath;
+import model.NavigationGrid;
+
+import java.awt.*;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Collections;
+
+public class Robot implements Entity {
+ private volatile double positionX = 100;
+ private volatile double positionY = 100;
+ private volatile double direction = 0;
+
+ private final NavigationGrid navigationGrid;
+ private List currentGridPath = Collections.emptyList();
+ private int currentPathIndex = -1;
+
+ private volatile double intermediateTargetX;
+ private volatile double intermediateTargetY;
+ private volatile int finalTargetX;
+ private volatile int finalTargetY;
+ private volatile boolean hasTarget = false;
+
+ private static final double maxVelocity = 0.45;
+ private static final double maxAngularVelocity = 0.02;
+
+ private static final double TARGET_REACH_THRESHOLD = 3.0; // дистанция для достижения точки
+ private static final double FINAL_TARGET_REACH_THRESHOLD = 1.5; // более точный порог для конечной точки
+ private static final double ANGLE_ALIGN_THRESHOLD = 0.03; // порог для выравнивания
+ private static final double DECELERATION_DISTANCE = maxVelocity * 400; // дистанция для начала замедления
+ private static final double MIN_VELOCITY_FACTOR = 0.2; // мин скорость как доля от макс
+ private static final double TURN_PRIORITY_ANGLE = Math.PI / 3.0; // снижаем скорость для поворота
+ private static final double SLOW_DOWN_TURN_ANGLE = Math.PI / 4.0; // снижаем скорость
+
+ public Robot(NavigationGrid grid) {
+ this.navigationGrid = grid;
+ }
+
+ public double getPositionX() { return positionX; }
+ public double getPositionY() { return positionY; }
+ public double getDirection() { return direction; }
+ public Point getFinalTarget() { return hasTarget ? new Point(finalTargetX, finalTargetY) : null; }
+
+
+ @Override
+ public void update(ModelController controller, double duration) {
+ if (!hasTarget || duration <= 0) {
+ return;
+ }
+
+
+ double currentTargetReachThreshold = TARGET_REACH_THRESHOLD;
+ boolean isTargetingFinal = (intermediateTargetX == finalTargetX && intermediateTargetY == finalTargetY);
+ if (isTargetingFinal) {
+ currentTargetReachThreshold = FINAL_TARGET_REACH_THRESHOLD;
+ }
+
+ double distance = GameMath.distance(intermediateTargetX, intermediateTargetY, positionX, positionY);
+ double angleToTarget = GameMath.angleTo(positionX, positionY, intermediateTargetX, intermediateTargetY);
+ double angleDifference = angleToTarget - direction;
+
+ while (angleDifference <= -Math.PI) angleDifference += 2 * Math.PI;
+ while (angleDifference > Math.PI) angleDifference -= 2 * Math.PI;
+
+
+ if (distance < currentTargetReachThreshold) {
+
+ boolean switchedTarget = false;
+
+ if (currentPathIndex < currentGridPath.size() - 1) {
+ currentPathIndex++;
+ Point nextGridCell = currentGridPath.get(currentPathIndex);
+ Point nextWorldTarget = navigationGrid.gridCellCenterToWorld(nextGridCell);
+ setIntermediateTarget(nextWorldTarget.x, nextWorldTarget.y);
+ switchedTarget = true;
+ } else {
+ if (!isTargetingFinal) {
+ setIntermediateTarget(finalTargetX, finalTargetY);
+ switchedTarget = true;
+ } else {
+ if (distance < FINAL_TARGET_REACH_THRESHOLD) {
+ hasTarget = false;
+ currentGridPath = Collections.emptyList();
+ currentPathIndex = -1;
+ return;
+ }
+ }
+ }
+
+ if (switchedTarget) {
+ distance = GameMath.distance(intermediateTargetX, intermediateTargetY, positionX, positionY);
+ angleToTarget = GameMath.angleTo(positionX, positionY, intermediateTargetX, intermediateTargetY);
+ angleDifference = angleToTarget - direction;
+ while (angleDifference <= -Math.PI) angleDifference += 2 * Math.PI;
+ while (angleDifference > Math.PI) angleDifference -= 2 * Math.PI;
+ if (intermediateTargetX != finalTargetX || intermediateTargetY != finalTargetY) {
+ isTargetingFinal = false;
+ }
+ }
+ }
+
+ if (!hasTarget) return;
+
+ double velocity = maxVelocity;
+ if (distance < DECELERATION_DISTANCE) {
+ velocity = maxVelocity * (distance / DECELERATION_DISTANCE);
+ velocity = Math.max(velocity, maxVelocity * MIN_VELOCITY_FACTOR);
+ }
+
+ double angularVelocity = 0;
+ if (Math.abs(angleDifference) > ANGLE_ALIGN_THRESHOLD) {
+ angularVelocity = Math.copySign(maxAngularVelocity, angleDifference);
+
+ double maxAngleChangePerTick = maxAngularVelocity * duration;
+ if (Math.abs(angleDifference) < maxAngleChangePerTick) {
+ angularVelocity = angleDifference / duration;
+ angularVelocity = GameMath.applyLimits(angularVelocity, -maxAngularVelocity, maxAngularVelocity);
+ }
+ } else {
+ angularVelocity = 0;
+ }
+
+ double turnFactor = 1.0;
+ if (Math.abs(angleDifference) > TURN_PRIORITY_ANGLE) {
+ turnFactor = 0.6;
+ } else if (Math.abs(angleDifference) > SLOW_DOWN_TURN_ANGLE) {
+ turnFactor = 0.8;
+ }
+
+ velocity *= turnFactor;
+
+ if (turnFactor < 1.0 && Math.abs(angleDifference) > ANGLE_ALIGN_THRESHOLD) {
+ velocity = Math.max(velocity, maxVelocity * MIN_VELOCITY_FACTOR * 0.5);
+ }
+
+ moveRobot(velocity, angularVelocity, duration);
+ }
+
+ private void moveRobot(double velocity, double angularVelocity, double duration) {
+ double newDirection = GameMath.asNormalizedRadians(direction + angularVelocity * duration);
+ double moveX, moveY;
+ double avgDirection = GameMath.asNormalizedRadians(direction + angularVelocity * duration / 2.0);
+ moveX = velocity * duration * Math.cos(avgDirection);
+ moveY = velocity * duration * Math.sin(avgDirection);
+
+ if (Double.isFinite(moveX) && Double.isFinite(moveY)) {
+ positionX += moveX;
+ positionY += moveY;
+ }
+
+ direction = newDirection;
+ }
+
+
+ public void setPath(List gridPath, int finalWorldX, int finalWorldY) {
+ this.currentGridPath = gridPath == null ? Collections.emptyList() : new ArrayList<>(gridPath);
+ this.finalTargetX = finalWorldX;
+ this.finalTargetY = finalWorldY;
+ this.hasTarget = false;
+
+ if (!this.currentGridPath.isEmpty()) {
+ this.currentPathIndex = 0;
+ Point firstCell = this.currentGridPath.get(0);
+ Point firstWorldTarget = navigationGrid.gridCellCenterToWorld(firstCell);
+ setIntermediateTarget(firstWorldTarget.x, firstWorldTarget.y);
+ this.hasTarget = true;
+ } else {
+
+ double distToFinal = GameMath.distance(finalWorldX, finalWorldY, positionX, positionY);
+ if (distToFinal <= FINAL_TARGET_REACH_THRESHOLD) {
+
+ this.hasTarget = false;
+ } else {
+
+ this.hasTarget = false;
+ this.currentGridPath = Collections.emptyList();
+ }
+ }
+ }
+
+ private void setIntermediateTarget(double x, double y) {
+ this.intermediateTargetX = x;
+ this.intermediateTargetY = y;
+ }
+
+ public void setPosition(double x, double y) {
+ this.positionX = x;
+ this.positionY = y;
+ }
+}
\ No newline at end of file
diff --git a/src/model/entities/WinPoint.java b/src/model/entities/WinPoint.java
new file mode 100644
index 000000000..0114640c4
--- /dev/null
+++ b/src/model/entities/WinPoint.java
@@ -0,0 +1,43 @@
+package model.entities;
+
+import model.ModelController;
+import model.GameMath;
+import model.GameState;
+
+import java.util.Optional;
+
+public class WinPoint implements Entity {
+ private final int x;
+ private final int y;
+ private final int size = 100;
+
+ public WinPoint(int x, int y) {
+ this.x = x;
+ this.y = y;
+ }
+
+ public int getX() {
+ return x;
+ }
+
+ public int getY() {
+ return y;
+ }
+
+ public int getSize() {
+ return size;
+ }
+
+ @Override
+ public void update(ModelController controller, double duration) {
+ Optional robotOpt = controller.findFirst(e -> e instanceof Robot).map(e -> (Robot) e);
+
+ if (robotOpt.isPresent()) {
+ Robot robot = robotOpt.get();
+ double distance = GameMath.distance(x, y, robot.getPositionX(), robot.getPositionY());
+ if (distance < size / 2.0) {
+ controller.setGameState(GameState.VICTORY);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/view/EntityVisualizer.java b/src/view/EntityVisualizer.java
new file mode 100644
index 000000000..107c15f49
--- /dev/null
+++ b/src/view/EntityVisualizer.java
@@ -0,0 +1,8 @@
+package view;
+
+import model.entities.Entity;
+import java.awt.Graphics2D;
+
+public abstract class EntityVisualizer {
+ public abstract void draw(Graphics2D g, E entity);
+}
\ No newline at end of file
diff --git a/src/view/GameVisualizer.java b/src/view/GameVisualizer.java
new file mode 100644
index 000000000..f244df4c0
--- /dev/null
+++ b/src/view/GameVisualizer.java
@@ -0,0 +1,49 @@
+package view;
+
+import model.Model;
+import model.entities.Entity;
+import model.entities.EntityProvider;
+import model.entities.LosePoint;
+import model.entities.Robot;
+import model.entities.WinPoint;
+import model.entities.Obstacle;
+import javax.swing.*;
+import java.awt.*;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class GameVisualizer extends JPanel {
+ private final EntityProvider entityProvider;
+ private final Map, EntityVisualizer>> visualizers = new HashMap<>();
+ private final Color backgroundColor = new Color(0, 102, 51); // Темно-синий цвет фона
+
+ public GameVisualizer(Model entityProvider) {
+ this.entityProvider = entityProvider;
+ visualizers.put(Obstacle.class, new ObstacleVisualizer());
+ visualizers.put(Robot.class, new RobotVisualizer());
+ visualizers.put(WinPoint.class, new WinPointVisualizer());
+ visualizers.put(LosePoint.class, new LosePointVisualizer());
+ }
+
+ @Override
+ protected void paintComponent(Graphics g) {
+ super.paintComponent(g);
+ Graphics2D g2d = (Graphics2D) g;
+ g2d.setColor(backgroundColor);
+ g2d.fillRect(0, 0, getWidth(), getHeight());
+ List entities = entityProvider.getEntities();
+ for (Entity entity : entities) {
+ EntityVisualizer> visualizer = visualizers.get(entity.getClass());
+ if (visualizer != null) {
+ drawEntity(g2d, entity, visualizer);
+ }
+ }
+ }
+
+ private void drawEntity(Graphics2D g, E entity, EntityVisualizer> visualizer) {
+ @SuppressWarnings("unchecked")
+ EntityVisualizer typedVisualizer = (EntityVisualizer) visualizer;
+ typedVisualizer.draw(g, entity);
+ }
+}
\ No newline at end of file
diff --git a/src/view/LosePointVisualizer.java b/src/view/LosePointVisualizer.java
new file mode 100644
index 000000000..a4ff807f3
--- /dev/null
+++ b/src/view/LosePointVisualizer.java
@@ -0,0 +1,14 @@
+package view;
+
+import model.entities.LosePoint;
+import java.awt.*;
+
+public class LosePointVisualizer extends EntityVisualizer {
+ @Override
+ public void draw(Graphics2D g, LosePoint losePoint) {
+ g.setColor(Color.RED);
+ g.fillOval(losePoint.getX() - losePoint.getSize() / 2, losePoint.getY() - losePoint.getSize() / 2, losePoint.getSize(), losePoint.getSize());
+ g.setColor(Color.BLACK);
+ g.drawString("Lose", losePoint.getX() + losePoint.getSize() / 2 + 5, losePoint.getY());
+ }
+}
\ No newline at end of file
diff --git a/src/view/ObstacleVisualizer.java b/src/view/ObstacleVisualizer.java
new file mode 100644
index 000000000..3222ad2b8
--- /dev/null
+++ b/src/view/ObstacleVisualizer.java
@@ -0,0 +1,14 @@
+
+package view;
+
+import model.entities.Obstacle;
+import java.awt.*;
+
+public class ObstacleVisualizer extends EntityVisualizer {
+ @Override
+ public void draw(Graphics2D g, Obstacle obstacle) {
+ g.setColor(Color.BLACK);
+ g.fillRect(obstacle.getX(), obstacle.getY(),
+ obstacle.getWidth(), obstacle.getHeight());
+ }
+}
\ No newline at end of file
diff --git a/src/view/RobotVisualizer.java b/src/view/RobotVisualizer.java
new file mode 100644
index 000000000..663b0e8d6
--- /dev/null
+++ b/src/view/RobotVisualizer.java
@@ -0,0 +1,47 @@
+package view;
+
+import model.entities.Robot;
+import java.awt.*;
+import java.awt.geom.AffineTransform;
+
+public class RobotVisualizer extends EntityVisualizer {
+ @Override
+ public void draw(Graphics2D g, Robot robot) {
+ AffineTransform originalTransform = g.getTransform();
+
+ AffineTransform robotTransform = AffineTransform.getRotateInstance(robot.getDirection(), robot.getPositionX(), robot.getPositionY());
+ g.setTransform(robotTransform);
+ g.setColor(Color.MAGENTA);
+ fillOval(g, (int) robot.getPositionX(), (int) robot.getPositionY(), 30, 10);
+ g.setColor(Color.BLACK);
+ drawOval(g, (int) robot.getPositionX(), (int) robot.getPositionY(), 30, 10);
+ g.setColor(Color.WHITE);
+ fillOval(g, (int) robot.getPositionX() + 10, (int) robot.getPositionY(), 5, 5);
+ g.setColor(Color.BLACK);
+ drawOval(g, (int) robot.getPositionX() + 10, (int) robot.getPositionY(), 5, 5);
+
+ g.setTransform(originalTransform);
+
+ Point finalTarget = robot.getFinalTarget();
+ if (finalTarget != null) {
+ drawTarget(g, finalTarget.x, finalTarget.y);
+ }
+ }
+
+ 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);
+ }
+
+ private void fillOval(Graphics g, int centerX, int centerY, int diam1, int diam2) {
+ g.fillOval(centerX - diam1 / 2, centerY - diam2 / 2, diam1, diam2);
+ }
+
+ private void drawOval(Graphics g, int centerX, int centerY, int diam1, int diam2) {
+ g.drawOval(centerX - diam1 / 2, centerY - diam2 / 2, diam1, diam2);
+ }
+}
\ No newline at end of file
diff --git a/src/view/TimerView.java b/src/view/TimerView.java
new file mode 100644
index 000000000..0349d98d2
--- /dev/null
+++ b/src/view/TimerView.java
@@ -0,0 +1,34 @@
+// src/view/TimerView.java
+package view;
+
+import javax.swing.*;
+import java.awt.*;
+
+public class TimerView extends JFrame {
+ private final JLabel timeLabel;
+
+ public TimerView() {
+ setTitle("Game Timer");
+ setSize(200, 100);
+ setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
+ setResizable(false);
+
+ timeLabel = new JLabel("02:00", SwingConstants.CENTER);
+ timeLabel.setFont(new Font("Arial", Font.BOLD, 32));
+ add(timeLabel);
+
+ setLocation(100, 100); // Позиция окна
+ }
+
+ public void updateTime(long remainingMillis) {
+ int minutes = (int) (remainingMillis / 60000);
+ int seconds = (int) ((remainingMillis % 60000) / 1000);
+ timeLabel.setText(String.format("%02d:%02d", minutes, seconds));
+
+ if (remainingMillis < 10000) {
+ timeLabel.setForeground(Color.RED);
+ } else {
+ timeLabel.setForeground(Color.BLACK);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/view/WinPointVisualizer.java b/src/view/WinPointVisualizer.java
new file mode 100644
index 000000000..e9eae4a82
--- /dev/null
+++ b/src/view/WinPointVisualizer.java
@@ -0,0 +1,14 @@
+package view;
+
+import model.entities.WinPoint;
+import java.awt.*;
+
+public class WinPointVisualizer extends EntityVisualizer {
+ @Override
+ public void draw(Graphics2D g, WinPoint winPoint) {
+ g.setColor(Color.BLUE);
+ g.fillOval(winPoint.getX() - winPoint.getSize() / 2, winPoint.getY() - winPoint.getSize() / 2, winPoint.getSize(), winPoint.getSize());
+ g.setColor(Color.BLACK);
+ g.drawString("Win", winPoint.getX() + winPoint.getSize() / 2 + 5, winPoint.getY());
+ }
+}
\ No newline at end of file