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