From 13e9021ff408d4ee605e98f5aac4e7a99b5bc701 Mon Sep 17 00:00:00 2001 From: VolodyaDe <114046476+VolodyaDe@users.noreply.github.com> Date: Tue, 19 Sep 2023 16:37:17 +0500 Subject: [PATCH 1/4] Reupload game model --- robots/src/Main.java | 37 ++++ robots/src/MainApplicationFrame.java | 114 ++++++++++ robots/src/gui/GameVisualizer.java | 210 ------------------ robots/src/gui/GameWindow.java | 20 -- robots/src/gui/MainApplicationFrame.java | 156 -------------- robots/src/gui/RobotsProgram.java | 25 --- robots/src/log/LogChangeListener.java | 4 +- robots/src/log/LogEntry.java | 15 +- robots/src/log/LogLevel.java | 11 +- robots/src/{gui => log}/LogWindow.java | 23 +- robots/src/log/LogWindowSource.java | 42 ++-- robots/src/log/Logger.java | 17 +- robots/src/model/Bacteria.java | 263 +++++++++++++++++++++++ robots/src/model/Entity.java | 15 ++ robots/src/model/GameModel.java | 85 ++++++++ robots/src/model/Target.java | 56 +++++ robots/src/model/additionals/Mood.java | 32 +++ robots/src/view/GameView.java | 57 +++++ robots/src/view/GameWindow.java | 24 +++ robots/src/view/draw/BacteriaDrawer.java | 52 +++++ robots/src/view/draw/Drawer.java | 22 ++ robots/src/view/draw/TargetDrawer.java | 26 +++ robots/src/viewModel/GameViewModel.java | 87 ++++++++ 23 files changed, 916 insertions(+), 477 deletions(-) create mode 100644 robots/src/Main.java create mode 100644 robots/src/MainApplicationFrame.java delete mode 100644 robots/src/gui/GameVisualizer.java delete mode 100644 robots/src/gui/GameWindow.java delete mode 100644 robots/src/gui/MainApplicationFrame.java delete mode 100644 robots/src/gui/RobotsProgram.java rename robots/src/{gui => log}/LogWindow.java (70%) create mode 100644 robots/src/model/Bacteria.java create mode 100644 robots/src/model/Entity.java create mode 100644 robots/src/model/GameModel.java create mode 100644 robots/src/model/Target.java create mode 100644 robots/src/model/additionals/Mood.java create mode 100644 robots/src/view/GameView.java create mode 100644 robots/src/view/GameWindow.java create mode 100644 robots/src/view/draw/BacteriaDrawer.java create mode 100644 robots/src/view/draw/Drawer.java create mode 100644 robots/src/view/draw/TargetDrawer.java create mode 100644 robots/src/viewModel/GameViewModel.java diff --git a/robots/src/Main.java b/robots/src/Main.java new file mode 100644 index 000000000..b58626c8e --- /dev/null +++ b/robots/src/Main.java @@ -0,0 +1,37 @@ +package application; + +import com.formdev.flatlaf.FlatLightLaf; +import application.view.GameView; +import application.view.GameWindow; +import application.model.GameModel; +import application.viewModel.GameViewModel; + +import javax.swing.*; +import java.awt.*; + +public class Main +{ + public static void main(String[] args) + { + FlatLightLaf.setup(); + try + { + UIManager.setLookAndFeel("com.formdev.flatlaf.FlatLightLaf"); + } + catch (Exception e) + { + e.printStackTrace(); + } + GameModel gameModel = new GameModel(); + GameView gameView = new GameView(gameModel); + GameWindow gameWindow = new GameWindow(gameView); + GameViewModel viewModel = new GameViewModel(gameModel, gameWindow); + + SwingUtilities.invokeLater(() -> { + MainApplicationFrame frame = new MainApplicationFrame(viewModel); + frame.pack(); + frame.setVisible(true); + frame.setExtendedState(Frame.MAXIMIZED_BOTH); + }); + } +} diff --git a/robots/src/MainApplicationFrame.java b/robots/src/MainApplicationFrame.java new file mode 100644 index 000000000..459d83006 --- /dev/null +++ b/robots/src/MainApplicationFrame.java @@ -0,0 +1,114 @@ +package application; + +import application.view.GameWindow; +import application.log.LogWindow; +import application.log.Logger; +import application.viewModel.GameViewModel; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; + +public class MainApplicationFrame extends JFrame +{ + private final JDesktopPane desktopPane = new JDesktopPane(); + + public MainApplicationFrame(GameViewModel gameViewModel) + { + + 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 = gameViewModel.getGameWindow(); + 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); + } + + private JMenu createMenu(String nameOfMenu, String description, int mnemonic) + { + JMenu lookAndFeelMenu = new JMenu(nameOfMenu); + lookAndFeelMenu.setMnemonic(KeyEvent.VK_V); + lookAndFeelMenu.getAccessibleContext().setAccessibleDescription( + description); + return lookAndFeelMenu; + } + + private JMenuItem createMenuItem(String name, ActionListener l) + { + JMenuItem systemLookAndFeel = new JMenuItem(name, KeyEvent.VK_S); + systemLookAndFeel.addActionListener(l); + + return systemLookAndFeel; + } + + private JMenuBar generateMenuBar() + { + JMenuBar menuBar = new JMenuBar(); + + JMenu lookAndFeelMenu = createMenu("Режим отображения", + "Управление режимом отображения приложения", KeyEvent.VK_V); + JMenuItem systemLookAndFeel = createMenuItem("Системная схема", (event) -> { + setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + this.invalidate(); + }); + lookAndFeelMenu.add(systemLookAndFeel); + + JMenuItem crossplatformLookAndFeel = createMenuItem("Универсальная схема", (event) -> { + setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName()); + this.invalidate(); + }); + lookAndFeelMenu.add(crossplatformLookAndFeel); + + JMenu testMenu = createMenu("Тесты", "Тестовые команды", KeyEvent.VK_S); + + JMenuItem addLogMessageItem = createMenuItem("Сообщение в лог", + (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/GameVisualizer.java b/robots/src/gui/GameVisualizer.java deleted file mode 100644 index f82cfd8f8..000000000 --- a/robots/src/gui/GameVisualizer.java +++ /dev/null @@ -1,210 +0,0 @@ -package gui; - -import java.awt.Color; -import java.awt.EventQueue; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.Point; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.awt.geom.AffineTransform; -import java.util.Timer; -import java.util.TimerTask; - -import javax.swing.JPanel; - -public class GameVisualizer extends JPanel -{ - private final Timer m_timer = initTimer(); - - private static Timer initTimer() - { - Timer timer = new Timer("events generator", true); - return timer; - } - - private volatile double m_robotPositionX = 100; - private volatile double m_robotPositionY = 100; - private volatile double m_robotDirection = 0; - - private volatile int m_targetPositionX = 150; - private volatile int m_targetPositionY = 100; - - private static final double maxVelocity = 0.1; - private static final double maxAngularVelocity = 0.001; - - public GameVisualizer() - { - m_timer.schedule(new TimerTask() - { - @Override - public void run() - { - onRedrawEvent(); - } - }, 0, 50); - m_timer.schedule(new TimerTask() - { - @Override - public void run() - { - onModelUpdateEvent(); - } - }, 0, 10); - addMouseListener(new MouseAdapter() - { - @Override - public void mouseClicked(MouseEvent e) - { - setTargetPosition(e.getPoint()); - repaint(); - } - }); - setDoubleBuffered(true); - } - - protected void setTargetPosition(Point p) - { - m_targetPositionX = p.x; - m_targetPositionY = p.y; - } - - protected void onRedrawEvent() - { - EventQueue.invokeLater(this::repaint); - } - - private static double distance(double x1, double y1, double x2, double y2) - { - double diffX = x1 - x2; - double diffY = y1 - y2; - return Math.sqrt(diffX * diffX + diffY * diffY); - } - - private static double angleTo(double fromX, double fromY, double toX, double toY) - { - double diffX = toX - fromX; - double diffY = toY - fromY; - - return asNormalizedRadians(Math.atan2(diffY, diffX)); - } - - protected void onModelUpdateEvent() - { - double distance = distance(m_targetPositionX, m_targetPositionY, - m_robotPositionX, m_robotPositionY); - if (distance < 0.5) - { - return; - } - double velocity = maxVelocity; - double angleToTarget = angleTo(m_robotPositionX, m_robotPositionY, m_targetPositionX, m_targetPositionY); - double angularVelocity = 0; - if (angleToTarget > m_robotDirection) - { - angularVelocity = maxAngularVelocity; - } - if (angleToTarget < m_robotDirection) - { - angularVelocity = -maxAngularVelocity; - } - - moveRobot(velocity, angularVelocity, 10); - } - - private static double applyLimits(double value, double min, double max) - { - if (value < min) - return min; - if (value > max) - return max; - return value; - } - - private void moveRobot(double velocity, double angularVelocity, double duration) - { - velocity = applyLimits(velocity, 0, maxVelocity); - angularVelocity = applyLimits(angularVelocity, -maxAngularVelocity, maxAngularVelocity); - double newX = m_robotPositionX + velocity / angularVelocity * - (Math.sin(m_robotDirection + angularVelocity * duration) - - Math.sin(m_robotDirection)); - if (!Double.isFinite(newX)) - { - newX = m_robotPositionX + velocity * duration * Math.cos(m_robotDirection); - } - double newY = m_robotPositionY - velocity / angularVelocity * - (Math.cos(m_robotDirection + angularVelocity * duration) - - Math.cos(m_robotDirection)); - if (!Double.isFinite(newY)) - { - newY = m_robotPositionY + velocity * duration * Math.sin(m_robotDirection); - } - m_robotPositionX = newX; - m_robotPositionY = newY; - double newDirection = asNormalizedRadians(m_robotDirection + angularVelocity * duration); - m_robotDirection = newDirection; - } - - private static double asNormalizedRadians(double angle) - { - while (angle < 0) - { - angle += 2*Math.PI; - } - while (angle >= 2*Math.PI) - { - angle -= 2*Math.PI; - } - return angle; - } - - private static int round(double value) - { - return (int)(value + 0.5); - } - - @Override - public void paint(Graphics g) - { - super.paint(g); - Graphics2D g2d = (Graphics2D)g; - drawRobot(g2d, round(m_robotPositionX), round(m_robotPositionY), m_robotDirection); - drawTarget(g2d, m_targetPositionX, m_targetPositionY); - } - - private static void fillOval(Graphics g, int centerX, int centerY, int diam1, int diam2) - { - g.fillOval(centerX - diam1 / 2, centerY - diam2 / 2, diam1, diam2); - } - - private static void drawOval(Graphics g, int centerX, int centerY, int diam1, int diam2) - { - g.drawOval(centerX - diam1 / 2, centerY - diam2 / 2, diam1, diam2); - } - - private void drawRobot(Graphics2D g, int x, int y, double direction) - { - int robotCenterX = round(m_robotPositionX); - int robotCenterY = round(m_robotPositionY); - AffineTransform t = AffineTransform.getRotateInstance(direction, robotCenterX, robotCenterY); - g.setTransform(t); - g.setColor(Color.MAGENTA); - fillOval(g, robotCenterX, robotCenterY, 30, 10); - g.setColor(Color.BLACK); - drawOval(g, robotCenterX, robotCenterY, 30, 10); - g.setColor(Color.WHITE); - fillOval(g, robotCenterX + 10, robotCenterY, 5, 5); - g.setColor(Color.BLACK); - drawOval(g, robotCenterX + 10, robotCenterY, 5, 5); - } - - private void drawTarget(Graphics2D g, int x, int y) - { - AffineTransform t = AffineTransform.getRotateInstance(0, 0, 0); - g.setTransform(t); - g.setColor(Color.GREEN); - fillOval(g, x, y, 5, 5); - g.setColor(Color.BLACK); - drawOval(g, x, y, 5, 5); - } -} diff --git a/robots/src/gui/GameWindow.java b/robots/src/gui/GameWindow.java deleted file mode 100644 index ecb63c00f..000000000 --- a/robots/src/gui/GameWindow.java +++ /dev/null @@ -1,20 +0,0 @@ -package gui; - -import java.awt.BorderLayout; - -import javax.swing.JInternalFrame; -import javax.swing.JPanel; - -public class GameWindow extends JInternalFrame -{ - private final GameVisualizer m_visualizer; - public GameWindow() - { - super("Игровое поле", true, true, true, true); - m_visualizer = new GameVisualizer(); - JPanel panel = new JPanel(new BorderLayout()); - panel.add(m_visualizer, BorderLayout.CENTER); - getContentPane().add(panel); - pack(); - } -} diff --git a/robots/src/gui/MainApplicationFrame.java b/robots/src/gui/MainApplicationFrame.java deleted file mode 100644 index 62e943ee1..000000000 --- a/robots/src/gui/MainApplicationFrame.java +++ /dev/null @@ -1,156 +0,0 @@ -package gui; - -import java.awt.Dimension; -import java.awt.Toolkit; -import java.awt.event.KeyEvent; - -import javax.swing.JDesktopPane; -import javax.swing.JFrame; -import javax.swing.JInternalFrame; -import javax.swing.JMenu; -import javax.swing.JMenuBar; -import javax.swing.JMenuItem; -import javax.swing.SwingUtilities; -import javax.swing.UIManager; -import javax.swing.UnsupportedLookAndFeelException; - -import log.Logger; - -/** - * Что требуется сделать: - * 1. Метод создания меню перегружен функционалом и трудно читается. - * Следует разделить его на серию более простых методов (или вообще выделить отдельный класс). - * - */ -public class MainApplicationFrame extends JFrame -{ - private final JDesktopPane desktopPane = new JDesktopPane(); - - public MainApplicationFrame() { - //Make the big window be indented 50 pixels from each edge - //of the screen. - int inset = 50; - Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); - setBounds(inset, inset, - screenSize.width - inset*2, - screenSize.height - inset*2); - - setContentPane(desktopPane); - - - LogWindow logWindow = createLogWindow(); - addWindow(logWindow); - - GameWindow gameWindow = new GameWindow(); - gameWindow.setSize(400, 400); - addWindow(gameWindow); - - setJMenuBar(generateMenuBar()); - setDefaultCloseOperation(EXIT_ON_CLOSE); - } - - protected LogWindow createLogWindow() - { - LogWindow logWindow = new LogWindow(Logger.getDefaultLogSource()); - logWindow.setLocation(10,10); - logWindow.setSize(300, 800); - setMinimumSize(logWindow.getSize()); - logWindow.pack(); - Logger.debug("Протокол работает"); - return logWindow; - } - - protected void addWindow(JInternalFrame frame) - { - desktopPane.add(frame); - frame.setVisible(true); - } - -// protected JMenuBar createMenuBar() { -// JMenuBar menuBar = new JMenuBar(); -// -// //Set up the lone menu. -// JMenu menu = new JMenu("Document"); -// menu.setMnemonic(KeyEvent.VK_D); -// menuBar.add(menu); -// -// //Set up the first menu item. -// JMenuItem menuItem = new JMenuItem("New"); -// menuItem.setMnemonic(KeyEvent.VK_N); -// menuItem.setAccelerator(KeyStroke.getKeyStroke( -// KeyEvent.VK_N, ActionEvent.ALT_MASK)); -// menuItem.setActionCommand("new"); -//// menuItem.addActionListener(this); -// menu.add(menuItem); -// -// //Set up the second menu item. -// menuItem = new JMenuItem("Quit"); -// menuItem.setMnemonic(KeyEvent.VK_Q); -// menuItem.setAccelerator(KeyStroke.getKeyStroke( -// KeyEvent.VK_Q, ActionEvent.ALT_MASK)); -// menuItem.setActionCommand("quit"); -//// menuItem.addActionListener(this); -// menu.add(menuItem); -// -// return menuBar; -// } - - private JMenuBar generateMenuBar() - { - JMenuBar menuBar = new JMenuBar(); - - JMenu lookAndFeelMenu = new JMenu("Режим отображения"); - lookAndFeelMenu.setMnemonic(KeyEvent.VK_V); - lookAndFeelMenu.getAccessibleContext().setAccessibleDescription( - "Управление режимом отображения приложения"); - - { - JMenuItem systemLookAndFeel = new JMenuItem("Системная схема", KeyEvent.VK_S); - systemLookAndFeel.addActionListener((event) -> { - setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); - this.invalidate(); - }); - lookAndFeelMenu.add(systemLookAndFeel); - } - - { - JMenuItem crossplatformLookAndFeel = new JMenuItem("Универсальная схема", KeyEvent.VK_S); - crossplatformLookAndFeel.addActionListener((event) -> { - setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName()); - this.invalidate(); - }); - lookAndFeelMenu.add(crossplatformLookAndFeel); - } - - JMenu testMenu = new JMenu("Тесты"); - testMenu.setMnemonic(KeyEvent.VK_T); - testMenu.getAccessibleContext().setAccessibleDescription( - "Тестовые команды"); - - { - JMenuItem addLogMessageItem = new JMenuItem("Сообщение в лог", KeyEvent.VK_S); - addLogMessageItem.addActionListener((event) -> { - Logger.debug("Новая строка"); - }); - testMenu.add(addLogMessageItem); - } - - menuBar.add(lookAndFeelMenu); - menuBar.add(testMenu); - return menuBar; - } - - private void setLookAndFeel(String className) - { - try - { - UIManager.setLookAndFeel(className); - SwingUtilities.updateComponentTreeUI(this); - } - catch (ClassNotFoundException | InstantiationException - | IllegalAccessException | UnsupportedLookAndFeelException e) - { - // just ignore - } - } -} diff --git a/robots/src/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 index 0b0fb85dd..d08b21815 100644 --- a/robots/src/log/LogChangeListener.java +++ b/robots/src/log/LogChangeListener.java @@ -1,6 +1,6 @@ -package log; +package application.log; public interface LogChangeListener { - public void onLogChanged(); + void onLogChanged(); } diff --git a/robots/src/log/LogEntry.java b/robots/src/log/LogEntry.java index 3d9147107..0c652f53d 100644 --- a/robots/src/log/LogEntry.java +++ b/robots/src/log/LogEntry.java @@ -1,24 +1,23 @@ -package log; +package application.log; public class LogEntry { - private LogLevel m_logLevel; - private String m_strMessage; - + private final LogLevel m_logLevel; + private final String m_strMessage; + public LogEntry(LogLevel logLevel, String strMessage) { - m_strMessage = strMessage; m_logLevel = logLevel; + m_strMessage = strMessage; } - + 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 index 582d010cc..b1e85462c 100644 --- a/robots/src/log/LogLevel.java +++ b/robots/src/log/LogLevel.java @@ -1,4 +1,4 @@ -package log; +package application.log; public enum LogLevel { @@ -8,17 +8,16 @@ public enum LogLevel Warning(3), Error(4), Fatal(5); - - private int m_iLevel; - + + private final int m_iLevel; + private LogLevel(int iLevel) { m_iLevel = iLevel; } - + public int level() { return m_iLevel; } } - diff --git a/robots/src/gui/LogWindow.java b/robots/src/log/LogWindow.java similarity index 70% rename from robots/src/gui/LogWindow.java rename to robots/src/log/LogWindow.java index 723d3e2fc..95dff53ff 100644 --- a/robots/src/gui/LogWindow.java +++ b/robots/src/log/LogWindow.java @@ -1,29 +1,22 @@ -package gui; - +package application.log; 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; + private final LogWindowSource m_logSource; + private final TextArea m_logContent; - public LogWindow(LogWindowSource logSource) + public LogWindow(LogWindowSource logSource) { - super("Протокол работы", true, true, true, true); + super("work protocol", 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); @@ -35,13 +28,11 @@ private void updateLogContent() { StringBuilder content = new StringBuilder(); for (LogEntry entry : m_logSource.all()) - { - content.append(entry.getMessage()).append("\n"); - } + content.append(entry.getMessage()); m_logContent.setText(content.toString()); m_logContent.invalidate(); } - + @Override public void onLogChanged() { diff --git a/robots/src/log/LogWindowSource.java b/robots/src/log/LogWindowSource.java index ca0ce4426..d05c67285 100644 --- a/robots/src/log/LogWindowSource.java +++ b/robots/src/log/LogWindowSource.java @@ -1,62 +1,56 @@ -package log; +package application.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 int m_iQueueLength; + + private final ArrayList m_messages; private final ArrayList m_listeners; private volatile LogChangeListener[] m_activeListeners; - - public LogWindowSource(int iQueueLength) + + public LogWindowSource(int iQueueLength) { m_iQueueLength = iQueueLength; m_messages = new ArrayList(iQueueLength); m_listeners = new ArrayList(); } - + public void registerListener(LogChangeListener listener) { - synchronized(m_listeners) + synchronized (m_listeners) { m_listeners.add(listener); m_activeListeners = null; } } - + public void unregisterListener(LogChangeListener listener) { - synchronized(m_listeners) + synchronized (m_listeners) { m_listeners.remove(listener); m_activeListeners = null; } } - + public void append(LogLevel logLevel, String strMessage) { LogEntry entry = new LogEntry(logLevel, strMessage); + if (size() >= m_iQueueLength) + m_messages.remove(0); + m_messages.add(entry); - LogChangeListener [] activeListeners = m_activeListeners; + LogChangeListener[] activeListeners = m_activeListeners; if (activeListeners == null) { synchronized (m_listeners) { if (m_activeListeners == null) { - activeListeners = m_listeners.toArray(new LogChangeListener [0]); + activeListeners = m_listeners.toArray(new LogChangeListener[0]); m_activeListeners = activeListeners; } } @@ -66,7 +60,7 @@ public void append(LogLevel logLevel, String strMessage) listener.onLogChanged(); } } - + public int size() { return m_messages.size(); @@ -75,9 +69,7 @@ public int 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); } diff --git a/robots/src/log/Logger.java b/robots/src/log/Logger.java index b008a5d01..e44b85dc7 100644 --- a/robots/src/log/Logger.java +++ b/robots/src/log/Logger.java @@ -1,21 +1,20 @@ -package log; +package application.log; -public final class Logger -{ +public final class Logger { private static final LogWindowSource defaultLogSource; - static { - defaultLogSource = new LogWindowSource(100); - } - - private Logger() + + static { + defaultLogSource = new LogWindowSource(3); } + private Logger() {} + public static void debug(String strMessage) { defaultLogSource.append(LogLevel.Debug, strMessage); } - + public static void error(String strMessage) { defaultLogSource.append(LogLevel.Error, strMessage); diff --git a/robots/src/model/Bacteria.java b/robots/src/model/Bacteria.java new file mode 100644 index 000000000..d027a4959 --- /dev/null +++ b/robots/src/model/Bacteria.java @@ -0,0 +1,263 @@ +package application.model; + + +import application.model.additionals.Mood; + +import java.awt.*; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeSupport; + +public class Bacteria implements Entity +{ + private double positionX; + private double positionY; + private volatile double bacteriaDirection; + private Target target; + + private Mood mood; + private int satiety; + public static double maxVelocity = 0.05; + public static final double maxAngularVelocity = 0.005; + private boolean isAlive; + private Dimension dimension; + private static final int INITIAL_SATIETY = 50; + private static final int MAX_SATIETY = 100; + + public Bacteria(double x, double y) + { + this.positionX = x; + this.positionY = y; + this.target = new Target(); + this.bacteriaDirection = Math.random() * 10; + this.dimension = new Dimension(400, 400); + this.mood = Mood.randomMood(); + this.satiety = (int) (INITIAL_SATIETY + Math.random() * (MAX_SATIETY - INITIAL_SATIETY)); + this.isAlive = true; + } + + private void setRandomMood() + { + this.mood = Mood.randomMood(); + } + + public void setMood(Mood mood) + { + this.mood = mood; + } + + public void setDimension(Dimension dimension) + { + this.dimension = dimension; + if (!target.isPositionCorrect(dimension)) + { + target = new Target((int) (Math.random() * dimension.width), (int) (Math.random() * dimension.height)); + } + } + + public Dimension getDimension() + { + return this.dimension; + } + + public double getPositionX() + { + return positionX; + } + + public void setPositionX(double positionX) + { + this.positionX = positionX; + } + + public double getPositionY() + { + return positionY; + } + + public void setPositionY(double positionY) + { + this.positionY = positionY; + } + + public double getBacteriaDirection() + { + return bacteriaDirection; + } + + public void setBacteriaDirection(double bacteriaDirection) + { + this.bacteriaDirection = bacteriaDirection; + } + + 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 double normalizedPositionX(double x) + { + if (x < 0) + return 0; + if (x > dimension.width) + return dimension.width; + return x; + } + + private double normalizedPositionY(double y) + { + if (y < 0) + return 0; + if (y > dimension.height) + return dimension.height; + return y; + } + + private static double applyLimits(double value, double min, double max) + { + if (value < min) + return min; + return Math.min(value, max); + } + + public Target getTarget() + { + return target; + } + + public void changeSatiety(int satiety) + { + this.satiety += satiety; + } + + public void setTarget(Point point) + { + if (point.x > dimension.width || point.y > dimension.height) + { + this.target.setTargetPosition(new Point(point.x / dimension.width, point.y / dimension.height)); + } + this.target.setTargetPosition(point); + } + + private void moveBacteria(double velocity, double angularVelocity, double duration) + { + velocity = applyLimits(velocity, 0, Bacteria.maxVelocity); + angularVelocity = applyLimits(angularVelocity, -Bacteria.maxAngularVelocity, Bacteria.maxAngularVelocity); + double newX = getPositionX() + velocity / angularVelocity * + (Math.sin(getBacteriaDirection() + angularVelocity * duration) - + Math.sin(getBacteriaDirection())); + if (!Double.isFinite(newX)) + newX = getPositionX() + velocity * duration * Math.cos(getBacteriaDirection()); + + double newY = getPositionY() - velocity / angularVelocity * + (Math.cos(getBacteriaDirection() + angularVelocity * duration) - + Math.cos(getBacteriaDirection())); + if (!Double.isFinite(newY)) + newY = getPositionY() + velocity * duration * Math.sin(getBacteriaDirection()); + + setPositionX(normalizedPositionX(newX)); + setPositionY(normalizedPositionY(newY)); + double newDirection = asNormalizedRadians(getBacteriaDirection() + angularVelocity * duration); + setBacteriaDirection(newDirection); + } + + + @Override + public void update() + { + if (!this.isAlive) + { + this.setMood(Mood.DEAD); + + return; + } + if (this.satiety < 50) + { + if (this.satiety < 0) + isAlive = false; + this.setMood(Mood.HUNGRY); + + } + double distance = distance(target.getX(), target.getY(), + getPositionX(), getPositionY()); + + if (distance < 0.5) + { + this.onTargetAchieved(); + return; + } + double angleToTarget = angleTo(getPositionX(), getPositionY(), + target.getX(), target.getY()); + double angularVelocity = 0; + if (angleToTarget > getBacteriaDirection()) + angularVelocity = Bacteria.maxAngularVelocity; + if (angleToTarget < getBacteriaDirection()) + angularVelocity = -Bacteria.maxAngularVelocity; + + moveBacteria(Bacteria.maxVelocity, angularVelocity, 10); + } + + @Override + public void onStart(PropertyChangeSupport publisher) + { + publisher.addPropertyChangeListener(this); + } + + @Override + public void onFinish(PropertyChangeSupport publisher) + { + publisher.removePropertyChangeListener(this); + } + + @Override + public boolean isAlive() + { + return isAlive; + } + + private void onTargetAchieved() + { + this.setTarget(new Point((int) (Math.random() * dimension.width), (int) (Math.random() * dimension.height))); + this.satiety += 20; + if (this.satiety > 50) + { + this.setRandomMood(); + } + } + + @Override + public void propertyChange(PropertyChangeEvent evt) + { + if (evt.getPropertyName().equals("new point")) + setTarget((Point) evt.getNewValue()); + if (evt.getPropertyName().equals("change satiety")) + changeSatiety((int) evt.getNewValue()); + if (evt.getPropertyName().equals("set dimension")) + setDimension((Dimension) evt.getNewValue()); + } + + public Mood getMood() + { + return mood; + } +} \ No newline at end of file diff --git a/robots/src/model/Entity.java b/robots/src/model/Entity.java new file mode 100644 index 000000000..2736c829a --- /dev/null +++ b/robots/src/model/Entity.java @@ -0,0 +1,15 @@ +package application.model; + +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; + +public interface Entity extends PropertyChangeListener +{ + void update(); + + void onStart(PropertyChangeSupport publisher); + + void onFinish(PropertyChangeSupport publisher); + + boolean isAlive(); +} diff --git a/robots/src/model/GameModel.java b/robots/src/model/GameModel.java new file mode 100644 index 000000000..62aa23415 --- /dev/null +++ b/robots/src/model/GameModel.java @@ -0,0 +1,85 @@ +package application.model; + +import java.awt.*; +import java.beans.PropertyChangeSupport; +import java.util.ArrayList; +import java.util.List; +import java.util.Timer; +import java.util.TimerTask; + +public class GameModel +{ + private List entities; + private final PropertyChangeSupport support; + + private static final int MIN_BACTERIA_COUNT = 4; + + public GameModel() + { + this.support = new PropertyChangeSupport(this); + this.entities = initStateOfBacterias(20); + + Timer timer = initTimer(); + timer.schedule(new TimerTask() + { + @Override + public void run() + { + System.out.println("timer"); + support.firePropertyChange("change satiety", null, -10); + } + }, 0, 2400); + } + + private static java.util.Timer initTimer() + { + return new Timer("satiety generator", true); + } + + public void setDimension(Dimension dimension) + { + support.firePropertyChange("set dimension", null, dimension); + } + + public Dimension getDimension() + { + return ((Bacteria) entities.get(0)).getDimension(); + } + + public void updateModel() + { + for (Entity entity : entities) + { + entity.update(); + if (!entity.isAlive()) + entity.onFinish(support); + } + entities.removeIf(entity -> !entity.isAlive()); + + if (entities.size() <= MIN_BACTERIA_COUNT) + this.entities = initStateOfBacterias(20); + } + + public List getEntities() + { + return entities; + } + + public void setTarget(Point point) + { + support.firePropertyChange("new point", null, point); + } + + public List initStateOfBacterias(int amount) + { + List entityList = new ArrayList<>(); + for (int i = 0; i < amount; i++) + { + Bacteria bacteria = new Bacteria(Math.random() * 400, Math.random() * 400); + bacteria.setTarget(new Point((int) (Math.random() * 400), (int) (Math.random() * 400))); + bacteria.onStart(support); + entityList.add(bacteria); + } + return entityList; + } +} diff --git a/robots/src/model/Target.java b/robots/src/model/Target.java new file mode 100644 index 000000000..9f8984f1c --- /dev/null +++ b/robots/src/model/Target.java @@ -0,0 +1,56 @@ +package application.model; + +import java.awt.*; + +public class Target { + private volatile int x; + private volatile int y; + + public Target() + { + this.x = 150; + this.y = 100; + } + + public Target(int x, int y) + { + this.x = x; + this.y = y; + } + + public void setX(int x) + { + this.x = x; + } + + public void setY(int y) + { + this.y = y; + } + + public int getX() + { + return x; + } + + public int getY() + { + return y; + } + + public void setTargetPosition(Point p) + { + setX(p.x); + setY(p.y); + } + + protected Point getTargetPosition() + { + return new Point(getX(), getY()); + } + + public boolean isPositionCorrect(Dimension dimension) + { + return this.x <= dimension.width && this.y <= dimension.height; + } +} diff --git a/robots/src/model/additionals/Mood.java b/robots/src/model/additionals/Mood.java new file mode 100644 index 000000000..5fdfbfc75 --- /dev/null +++ b/robots/src/model/additionals/Mood.java @@ -0,0 +1,32 @@ +package application.model.additionals; + +import java.awt.*; +import java.util.List; +import java.util.Random; + +public enum Mood +{ + CALM(Color.GREEN), + EVIL(Color.RED), + ENAMORED(Color.MAGENTA), + HUNGRY(Color.YELLOW), + DEAD(Color.BLACK); + private final Color color; + private static final Random RANDOM = new Random(); + private static final List values = List.of(values()); + + public static Mood randomMood() + { + return values.get(RANDOM.nextInt(values().length - 2)); + } + + Mood(Color color) + { + this.color = color; + } + + + public Color getColor() { + return color; + } +} diff --git a/robots/src/view/GameView.java b/robots/src/view/GameView.java new file mode 100644 index 000000000..23afad436 --- /dev/null +++ b/robots/src/view/GameView.java @@ -0,0 +1,57 @@ +package application.view; + +import application.model.Entity; +import application.model.GameModel; +import application.model.Target; +import application.view.draw.Drawer; +import application.view.draw.BacteriaDrawer; +import application.view.draw.TargetDrawer; + +import javax.swing.*; +import java.awt.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +public class GameView extends JPanel +{ + private final GameModel gameModel; + private final Map, Drawer> map; + + public GameView(GameModel gameModel) + { + map = new HashMap<>(); + map.put(new BacteriaDrawer().getDrawingType(), new BacteriaDrawer()); + map.put(new TargetDrawer().getDrawingType(), new TargetDrawer()); + this.gameModel = gameModel; + setDoubleBuffered(true); + this.setPreferredSize(new Dimension(400, 400)); + this.setSize(new Dimension(400, 400)); + this.setBorder(BorderFactory.createLineBorder(Color.GREEN)); + } + + public void updateView() + { + onRedrawEvent(); + } + + protected void onRedrawEvent() + { + EventQueue.invokeLater(this::repaint); + } + + @Override + public void paint(Graphics g) + { + super.paint(g); + Graphics2D g2d = (Graphics2D) g; + RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g2d.setRenderingHints(rh); + ArrayList entities = (ArrayList) gameModel.getEntities(); + for (Entity entity : entities) + { + map.get(entity.getClass()).draw(g2d, entity); + map.get(Target.class).draw(g2d, entity); + } + } +} diff --git a/robots/src/view/GameWindow.java b/robots/src/view/GameWindow.java new file mode 100644 index 000000000..f69304220 --- /dev/null +++ b/robots/src/view/GameWindow.java @@ -0,0 +1,24 @@ +package application.view; + + +import javax.swing.*; +import java.awt.*; + +public class GameWindow extends JInternalFrame +{ + private final GameView gameView; + + public GameWindow(GameView gameView) + { + super("Игровое поле", true, true, true, true); + this.gameView = gameView; + JPanel panel = new JPanel(new BorderLayout()); + panel.add(this.gameView, BorderLayout.CENTER); + getContentPane().add(panel); + pack(); + } + + public GameView getGameView() { + return this.gameView; + } +} diff --git a/robots/src/view/draw/BacteriaDrawer.java b/robots/src/view/draw/BacteriaDrawer.java new file mode 100644 index 000000000..e78fbac8e --- /dev/null +++ b/robots/src/view/draw/BacteriaDrawer.java @@ -0,0 +1,52 @@ +package application.view.draw; + +import application.model.Bacteria; +import application.model.Entity; +import application.model.additionals.Mood; + +import java.awt.*; +import java.awt.geom.AffineTransform; + +public class BacteriaDrawer extends Drawer +{ + @Override + public void draw(Graphics2D g, Entity entity) + { + Bacteria bacteria = (Bacteria) entity; + AffineTransform oldTransform = g.getTransform(); + int bacteriaCenterX = (int) (Math.round(bacteria.getPositionX())); + int bacteriaCenterY = (int) (Math.round(bacteria.getPositionY())); + AffineTransform l = new AffineTransform(oldTransform); + AffineTransform t = AffineTransform.getRotateInstance(bacteria.getBacteriaDirection(), bacteriaCenterX, bacteriaCenterY); + l.concatenate(t); + + g.setTransform(l); + + g.setColor(bacteria.getMood().getColor()); + if (bacteria.getMood().equals(Mood.HUNGRY)) + { + fillOval(g, bacteriaCenterX, bacteriaCenterY, 25, 10); + g.setColor(Color.BLACK); + drawOval(g, bacteriaCenterX, bacteriaCenterY, 25, 10); + g.setColor(Color.WHITE); + } + else + { + fillOval(g, bacteriaCenterX, bacteriaCenterY, 25, 15); + g.setColor(Color.BLACK); + drawOval(g, bacteriaCenterX, bacteriaCenterY, 25, 15); + g.setColor(Color.WHITE); + } + + fillOval(g, bacteriaCenterX + 7, bacteriaCenterY, 5, 5); + g.setColor(Color.BLACK); + drawOval(g, bacteriaCenterX + 7, bacteriaCenterY, 5, 5); + g.setTransform(oldTransform); + } + + @Override + public Class getDrawingType() + { + return Bacteria.class; + } +} diff --git a/robots/src/view/draw/Drawer.java b/robots/src/view/draw/Drawer.java new file mode 100644 index 000000000..79f411404 --- /dev/null +++ b/robots/src/view/draw/Drawer.java @@ -0,0 +1,22 @@ +package application.view.draw; + +import application.model.Entity; + +import java.awt.*; + +public abstract class Drawer +{ + protected static void fillOval(Graphics g, int centerX, int centerY, int diam1, int diam2) + { + g.fillOval(centerX - diam1 / 2, centerY - diam2 / 2, diam1, diam2); + } + + protected static void drawOval(Graphics g, int centerX, int centerY, int diam1, int diam2) + { + g.drawOval(centerX - diam1 / 2, centerY - diam2 / 2, diam1, diam2); + } + + public abstract void draw(Graphics2D g, Entity entity); + + public abstract Class getDrawingType(); +} diff --git a/robots/src/view/draw/TargetDrawer.java b/robots/src/view/draw/TargetDrawer.java new file mode 100644 index 000000000..16d1bbf86 --- /dev/null +++ b/robots/src/view/draw/TargetDrawer.java @@ -0,0 +1,26 @@ +package application.view.draw; + +import application.model.Bacteria; +import application.model.Entity; +import application.model.Target; + +import java.awt.*; + +public class TargetDrawer extends Drawer +{ + @Override + public void draw(Graphics2D g, Entity entity) + { + Target target = ((Bacteria) entity).getTarget(); + g.setColor(Color.GREEN); + fillOval(g, target.getX(), target.getY(), 5, 5); + g.setColor(Color.BLACK); + drawOval(g, target.getX(), target.getY(), 5, 5); + } + + @Override + public Class getDrawingType() + { + return Target.class; + } +} diff --git a/robots/src/viewModel/GameViewModel.java b/robots/src/viewModel/GameViewModel.java new file mode 100644 index 000000000..4e94ef72f --- /dev/null +++ b/robots/src/viewModel/GameViewModel.java @@ -0,0 +1,87 @@ +package application.viewModel; + +import application.view.GameView; +import application.view.GameWindow; +import application.model.GameModel; + +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.Timer; +import java.util.TimerTask; + + +public class GameViewModel +{ + private final GameModel gameModel; + private final GameWindow gameWindow; + private final java.util.Timer timer = initTimer(); + + private static java.util.Timer initTimer() + { + return new Timer("events generator", true); + } + + public GameViewModel(GameModel gameModel, GameWindow gameWindow) + { + this.gameModel = gameModel; + this.gameWindow = gameWindow; + initListeners(); + } + + private void initListeners() + { + timer.schedule(new TimerTask() + { + @Override + public void run() + { + gameModel.setDimension(gameWindow.getGameView().getSize()); + getGameView().updateView(); + } + }, 0, 50); + + timer.schedule(new TimerTask() + { + @Override + public void run() + { + gameModel.updateModel(); + } + }, 0, 10); + + gameWindow.getGameView().addMouseListener(new MouseAdapter() + { + @Override + public void mouseClicked(MouseEvent e) + { + System.out.println(e.getPoint()); + gameModel.setTarget(e.getPoint()); + getGameView().repaint(); + } + }); + + gameWindow.addComponentListener(new ComponentAdapter() + { + @Override + public void componentResized(final ComponentEvent e) + { + super.componentResized(e); + System.out.println("resize"); + gameModel.setDimension((gameWindow.getSize())); + System.out.println(gameModel.getDimension()); + } + }); + } + + public GameView getGameView() + { + return gameWindow.getGameView(); + } + + public GameWindow getGameWindow() + { + return gameWindow; + } +} From 639a0d309fed458168d790d70c4163bd0e423d85 Mon Sep 17 00:00:00 2001 From: VolodyaDe <114046476+VolodyaDe@users.noreply.github.com> Date: Sun, 8 Oct 2023 18:11:29 +0500 Subject: [PATCH 2/4] Update model of game --- robots/src/Main.java | 6 +- robots/src/MainApplicationFrame.java | 12 +- robots/src/model/Bot.java | 266 ++++++++++++++++++ robots/src/model/Entity.java | 2 - robots/src/model/Food.java | 56 ++++ robots/src/model/GameModel.java | 79 +++--- robots/src/model/additionals/Condition.java | 30 ++ robots/src/view/GameView.java | 31 +- robots/src/view/GameWindow.java | 1 + robots/src/view/draw/BotDrawer.java | 50 ++++ robots/src/view/draw/Drawer.java | 6 +- robots/src/view/draw/FoodDrawer.java | 19 ++ robots/src/view/draw/TargetDrawer.java | 25 -- .../{GameViewModel.java => ViewModel.java} | 10 +- 14 files changed, 491 insertions(+), 102 deletions(-) create mode 100644 robots/src/model/Bot.java create mode 100644 robots/src/model/Food.java create mode 100644 robots/src/model/additionals/Condition.java create mode 100644 robots/src/view/draw/BotDrawer.java create mode 100644 robots/src/view/draw/FoodDrawer.java rename robots/src/viewModel/{GameViewModel.java => ViewModel.java} (89%) diff --git a/robots/src/Main.java b/robots/src/Main.java index b58626c8e..ee65706a6 100644 --- a/robots/src/Main.java +++ b/robots/src/Main.java @@ -1,10 +1,9 @@ package application; -import com.formdev.flatlaf.FlatLightLaf; import application.view.GameView; import application.view.GameWindow; import application.model.GameModel; -import application.viewModel.GameViewModel; +import application.viewModel.ViewModel; import javax.swing.*; import java.awt.*; @@ -13,7 +12,6 @@ public class Main { public static void main(String[] args) { - FlatLightLaf.setup(); try { UIManager.setLookAndFeel("com.formdev.flatlaf.FlatLightLaf"); @@ -25,7 +23,7 @@ public static void main(String[] args) GameModel gameModel = new GameModel(); GameView gameView = new GameView(gameModel); GameWindow gameWindow = new GameWindow(gameView); - GameViewModel viewModel = new GameViewModel(gameModel, gameWindow); + ViewModel viewModel = new ViewModel(gameModel, gameWindow); SwingUtilities.invokeLater(() -> { MainApplicationFrame frame = new MainApplicationFrame(viewModel); diff --git a/robots/src/MainApplicationFrame.java b/robots/src/MainApplicationFrame.java index 459d83006..f8c8ae293 100644 --- a/robots/src/MainApplicationFrame.java +++ b/robots/src/MainApplicationFrame.java @@ -3,7 +3,7 @@ import application.view.GameWindow; import application.log.LogWindow; import application.log.Logger; -import application.viewModel.GameViewModel; +import application.viewModel.ViewModel; import javax.swing.*; import java.awt.*; @@ -14,7 +14,7 @@ public class MainApplicationFrame extends JFrame { private final JDesktopPane desktopPane = new JDesktopPane(); - public MainApplicationFrame(GameViewModel gameViewModel) + public MainApplicationFrame(ViewModel gameViewModel) { int inset = 50; @@ -52,7 +52,7 @@ protected void addWindow(JInternalFrame frame) frame.setVisible(true); } - private JMenu createMenu(String nameOfMenu, String description, int mnemonic) + private JMenu createMenu(String nameOfMenu, String description) { JMenu lookAndFeelMenu = new JMenu(nameOfMenu); lookAndFeelMenu.setMnemonic(KeyEvent.VK_V); @@ -74,7 +74,7 @@ private JMenuBar generateMenuBar() JMenuBar menuBar = new JMenuBar(); JMenu lookAndFeelMenu = createMenu("Режим отображения", - "Управление режимом отображения приложения", KeyEvent.VK_V); + "Управление режимом отображения приложения"); JMenuItem systemLookAndFeel = createMenuItem("Системная схема", (event) -> { setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); this.invalidate(); @@ -87,7 +87,7 @@ private JMenuBar generateMenuBar() }); lookAndFeelMenu.add(crossplatformLookAndFeel); - JMenu testMenu = createMenu("Тесты", "Тестовые команды", KeyEvent.VK_S); + JMenu testMenu = createMenu("Тесты", "Тестовые команды"); JMenuItem addLogMessageItem = createMenuItem("Сообщение в лог", (event) -> Logger.debug("Новая строка")); @@ -108,7 +108,7 @@ private void setLookAndFeel(String className) catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException e) { - // just ignore + // skip } } } diff --git a/robots/src/model/Bot.java b/robots/src/model/Bot.java new file mode 100644 index 000000000..c04b84638 --- /dev/null +++ b/robots/src/model/Bot.java @@ -0,0 +1,266 @@ +package application.model; + + +import application.model.additionals.Condition; + +import java.awt.*; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeSupport; + +public class Bot implements Entity +{ + private double positionX; + private double positionY; + private volatile double botDirection; + private Food foodGoal; + private int lifeTime; + public static double maxVelocity = 0.05; + public static final double maxAngularVelocity = 0.005; + private Dimension dimension; + private Condition condition; + private boolean isAlive; + private static final int START_LIFETIME = 40; + private static final int FULL_LIFETIME = 90; + + public Bot(double startX, double startY) + { + this.positionX = startX; + this.positionY = startY; + this.dimension = new Dimension(400, 400); + this.foodGoal = new Food((int) (Math.random() * dimension.width), (int) (Math.random() * dimension.height)); + this.botDirection = Math.random() * 10; + this.lifeTime = (int) (START_LIFETIME + Math.random() * (FULL_LIFETIME - START_LIFETIME)); + this.condition = Condition.randomCondition(); + this.isAlive = true; + } + + public Bot() + { + this.positionX = 300; + this.positionY = 300; + this.foodGoal = new Food(); + this.botDirection = 0; + } + + public void setDimension(Dimension dimension) + { + this.dimension = dimension; + if (!foodGoal.isPositionCorrect(dimension)) + { + foodGoal = new Food((int) (Math.random() * dimension.width), (int) (Math.random() * dimension.height)); + } + } + + private void setRandomCondition() + { + this.condition = Condition.randomCondition(); + } + + public Dimension getDimension() + { + return this.dimension; + } + + public double getPositionX() + { + return positionX; + } + + public void setPositionX(double positionX) + { + this.positionX = positionX; + } + + public double getPositionY() + { + return positionY; + } + + public void setPositionY(double positionY) + { + this.positionY = positionY; + } + + public double getBotDirection() + { + return botDirection; + } + + public void setBotDirection(double botDirection) + { + this.botDirection = botDirection; + } + + 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 double normalizedPositionX(double x) + { + if (x < 0) + return 0; + if (x > dimension.width) + return dimension.width; + return x; + } + + private double normalizedPositionY(double y) + { + if (y < 0) + return 0; + if (y > dimension.height) + return dimension.height; + return y; + } + + private static double applyLimits(double value, double min, double max) + { + if (value < min) + return min; + return Math.min(value, max); + } + + public Food getFoodGoal() + { + return foodGoal; + } + + public void changeLifeTime(int lifeTmeDif) + { + this.lifeTime += lifeTmeDif; + } + + public void setFoodGoal(Point point) + { + if (point.x > dimension.width || point.y > dimension.height) + { + this.foodGoal.setFoodPosition(new Point(point.x / dimension.width, point.y / dimension.height)); + } + this.foodGoal.setFoodPosition(point); + } + + private void moveBot(double velocity, double angularVelocity, double duration) + { + velocity = applyLimits(velocity, 0, Bot.maxVelocity); + angularVelocity = applyLimits(angularVelocity, -Bot.maxAngularVelocity, Bot.maxAngularVelocity); + double newX = getPositionX() + velocity / angularVelocity * + (Math.sin(getBotDirection() + angularVelocity * duration) - + Math.sin(getBotDirection())); + if (!Double.isFinite(newX)) + newX = getPositionX() + velocity * duration * Math.cos(getBotDirection()); + + double newY = getPositionY() - velocity / angularVelocity * + (Math.cos(getBotDirection() + angularVelocity * duration) - + Math.cos(getBotDirection())); + if (!Double.isFinite(newY)) + newY = getPositionY() + velocity * duration * Math.sin(getBotDirection()); + + setPositionX(normalizedPositionX(newX)); + setPositionY(normalizedPositionY(newY)); + double newDirection = asNormalizedRadians(getBotDirection() + angularVelocity * duration); + setBotDirection(newDirection); + } + + public Condition getCondition() + { + return this.condition; + } + + @Override + public void update() + { + if (!this.isAlive) + { + this.setCondition(Condition.DEAD); + + return; + } + if (this.lifeTime < 50) + { + if (this.lifeTime < 0) + { + isAlive = false; + } + this.setCondition(Condition.HUNGRY); + } + + double distance = distance(foodGoal.getX(), foodGoal.getY(), + getPositionX(), getPositionY()); + + if (distance < 0.5) + { + this.foodGoalAchieved(); + return; + } + double angleToFoodGoal = angleTo(getPositionX(), getPositionY(), + foodGoal.getX(), foodGoal.getY()); + double angularVelocity = 0; + if (angleToFoodGoal > getBotDirection()) + angularVelocity = Bot.maxAngularVelocity; + if (angleToFoodGoal < getBotDirection()) + angularVelocity = -Bot.maxAngularVelocity; + + moveBot(Bot.maxVelocity, angularVelocity, 10); + } + + private void setCondition(Condition condition) + { + this.condition = condition; + } + + @Override + public void onStart(PropertyChangeSupport publisher) + { + publisher.addPropertyChangeListener(this); + } + + @Override + public void onFinish(PropertyChangeSupport publisher) + { + publisher.removePropertyChangeListener(this); + } + + private void foodGoalAchieved() + { + this.setFoodGoal(new Point((int) (Math.random() * dimension.width), (int) (Math.random() * dimension.height))); + this.setFoodGoal(new Point((int) (Math.random() * dimension.width), (int) (Math.random() * dimension.height))); + this.lifeTime += 20; + if (this.lifeTime > 50) + { + this.setRandomCondition(); + } + } + + @Override + public void propertyChange(PropertyChangeEvent evt) + { + if (evt.getPropertyName().equals("new point")) + setFoodGoal((Point) evt.getNewValue()); + if (evt.getPropertyName().equals("change life time")) + changeLifeTime((int) evt.getNewValue()); + if (evt.getPropertyName().equals("set dimension")) + setDimension((Dimension) evt.getNewValue()); + } +} \ No newline at end of file diff --git a/robots/src/model/Entity.java b/robots/src/model/Entity.java index 2736c829a..7d9f595ee 100644 --- a/robots/src/model/Entity.java +++ b/robots/src/model/Entity.java @@ -10,6 +10,4 @@ public interface Entity extends PropertyChangeListener void onStart(PropertyChangeSupport publisher); void onFinish(PropertyChangeSupport publisher); - - boolean isAlive(); } diff --git a/robots/src/model/Food.java b/robots/src/model/Food.java new file mode 100644 index 000000000..311b4474f --- /dev/null +++ b/robots/src/model/Food.java @@ -0,0 +1,56 @@ +package application.model; + +import java.awt.*; + +public class Food { + private volatile int x; + private volatile int y; + + public Food(int x, int y) + { + this.x = x; + this.y = y; + } + + public Food() + { + this.x = 100; + this.y = 100; + } + + public void setX(int x) + { + this.x = x; + } + + public void setY(int y) + { + this.y = y; + } + + public int getX() + { + return x; + } + + public int getY() + { + return y; + } + + public void setFoodPosition(Point p) + { + setX(p.x); + setY(p.y); + } + + protected Point getFoodPosition() + { + return new Point(getX(), getY()); + } + + public boolean isPositionCorrect(Dimension dimension) + { + return this.x <= dimension.width && this.y <= dimension.height; + } +} diff --git a/robots/src/model/GameModel.java b/robots/src/model/GameModel.java index 62aa23415..89d8d88c6 100644 --- a/robots/src/model/GameModel.java +++ b/robots/src/model/GameModel.java @@ -1,23 +1,28 @@ package application.model; import java.awt.*; +import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; -import java.util.ArrayList; +import java.util.*; import java.util.List; -import java.util.Timer; -import java.util.TimerTask; public class GameModel { - private List entities; + private final List bots; private final PropertyChangeSupport support; - - private static final int MIN_BACTERIA_COUNT = 4; + private static final int BOT_COUNT = 5; public GameModel() { + this.bots = new ArrayList<>(); this.support = new PropertyChangeSupport(this); - this.entities = initStateOfBacterias(20); + + for (int i = 0; i < BOT_COUNT; i++) + { + Bot bot = new Bot(Math.random() * 400, Math.random() * 400); + bots.add(bot); + addPropertyChangeListener(bot); + } Timer timer = initTimer(); timer.schedule(new TimerTask() @@ -26,60 +31,62 @@ public GameModel() public void run() { System.out.println("timer"); - support.firePropertyChange("change satiety", null, -10); + support.firePropertyChange("change life time", null, -10); + if (Math.random() * 4 > 2.3) + suddenBotClone(); } - }, 0, 2400); + }, 0, 3000); } private static java.util.Timer initTimer() { - return new Timer("satiety generator", true); + return new Timer("events generator", true); + } + + public void addPropertyChangeListener(PropertyChangeListener pcl) { + support.addPropertyChangeListener(pcl); + } + + public void removePropertyChangeListener(PropertyChangeListener pcl) { + support.removePropertyChangeListener(pcl); } public void setDimension(Dimension dimension) { - support.firePropertyChange("set dimension", null, dimension); + for (Bot robot : bots) + robot.setDimension(dimension); } public Dimension getDimension() { - return ((Bacteria) entities.get(0)).getDimension(); + return bots.get(0).getDimension(); } public void updateModel() { - for (Entity entity : entities) - { - entity.update(); - if (!entity.isAlive()) - entity.onFinish(support); - } - entities.removeIf(entity -> !entity.isAlive()); - - if (entities.size() <= MIN_BACTERIA_COUNT) - this.entities = initStateOfBacterias(20); + for (Bot bot : bots) + bot.update(); } - public List getEntities() + private void suddenBotClone() { - return entities; + Random rand = new Random(); + Bot bot = bots.get(rand.nextInt(bots.size())); + double x = bot.getPositionX(); + double y = bot.getPositionY(); + removePropertyChangeListener(bot); + bots.remove(bot); + for (int i = 0; i < 2; i++) + bots.add(new Bot(x, y)); } - public void setTarget(Point point) + public List getRobots() { - support.firePropertyChange("new point", null, point); + return bots; } - public List initStateOfBacterias(int amount) + public void setFoodGoal(Point point) { - List entityList = new ArrayList<>(); - for (int i = 0; i < amount; i++) - { - Bacteria bacteria = new Bacteria(Math.random() * 400, Math.random() * 400); - bacteria.setTarget(new Point((int) (Math.random() * 400), (int) (Math.random() * 400))); - bacteria.onStart(support); - entityList.add(bacteria); - } - return entityList; + support.firePropertyChange("new point", null, point); } } diff --git a/robots/src/model/additionals/Condition.java b/robots/src/model/additionals/Condition.java new file mode 100644 index 000000000..4947c1512 --- /dev/null +++ b/robots/src/model/additionals/Condition.java @@ -0,0 +1,30 @@ +package application.model.additionals; + +import java.awt.*; +import java.util.List; +import java.util.Random; + +public enum Condition +{ + CALM(Color.GREEN), + HUNGRY(Color.RED), + DEAD(Color.BLACK); + private final Color color; + private static final Random RANDOM = new Random(); + private static final List values = List.of(values()); + + public static Condition randomCondition() + { + return values.get(RANDOM.nextInt(values().length - 2)); + } + + Condition(Color color) + { + this.color = color; + } + + + public Color getColor() { + return color; + } +} diff --git a/robots/src/view/GameView.java b/robots/src/view/GameView.java index 23afad436..78b9faef6 100644 --- a/robots/src/view/GameView.java +++ b/robots/src/view/GameView.java @@ -1,33 +1,26 @@ package application.view; -import application.model.Entity; +import application.model.Bot; import application.model.GameModel; -import application.model.Target; -import application.view.draw.Drawer; -import application.view.draw.BacteriaDrawer; -import application.view.draw.TargetDrawer; +import application.view.draw.BotDrawer; +import application.view.draw.FoodDrawer; import javax.swing.*; import java.awt.*; import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; public class GameView extends JPanel { private final GameModel gameModel; - private final Map, Drawer> map; + BotDrawer botDrawer; + FoodDrawer foodDrawer; public GameView(GameModel gameModel) { - map = new HashMap<>(); - map.put(new BacteriaDrawer().getDrawingType(), new BacteriaDrawer()); - map.put(new TargetDrawer().getDrawingType(), new TargetDrawer()); + botDrawer = new BotDrawer(); + foodDrawer = new FoodDrawer(); this.gameModel = gameModel; setDoubleBuffered(true); - this.setPreferredSize(new Dimension(400, 400)); - this.setSize(new Dimension(400, 400)); - this.setBorder(BorderFactory.createLineBorder(Color.GREEN)); } public void updateView() @@ -45,13 +38,11 @@ public void paint(Graphics g) { super.paint(g); Graphics2D g2d = (Graphics2D) g; - RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - g2d.setRenderingHints(rh); - ArrayList entities = (ArrayList) gameModel.getEntities(); - for (Entity entity : entities) + ArrayList bots = (ArrayList) gameModel.getRobots(); + for (Bot bot : bots) { - map.get(entity.getClass()).draw(g2d, entity); - map.get(Target.class).draw(g2d, entity); + botDrawer.draw(g2d, bot); + foodDrawer.draw(g2d, bot.getFoodGoal()); } } } diff --git a/robots/src/view/GameWindow.java b/robots/src/view/GameWindow.java index f69304220..5b5682b8e 100644 --- a/robots/src/view/GameWindow.java +++ b/robots/src/view/GameWindow.java @@ -16,6 +16,7 @@ public GameWindow(GameView gameView) panel.add(this.gameView, BorderLayout.CENTER); getContentPane().add(panel); pack(); + setSize(400, 400); } public GameView getGameView() { diff --git a/robots/src/view/draw/BotDrawer.java b/robots/src/view/draw/BotDrawer.java new file mode 100644 index 000000000..cc73769c1 --- /dev/null +++ b/robots/src/view/draw/BotDrawer.java @@ -0,0 +1,50 @@ +package application.view.draw; + +import application.model.Bot; +import application.model.additionals.Condition; + +import java.awt.*; +import java.awt.geom.AffineTransform; + +public class BotDrawer extends Drawer { + public void draw(Graphics2D g, Bot bot) + { + AffineTransform oldTransform = g.getTransform(); + int robotCenterX = (int) Math.round(bot.getPositionX()); + int robotCenterY = (int) Math.round(bot.getPositionY()); + AffineTransform l = new AffineTransform(oldTransform); + AffineTransform t = AffineTransform.getRotateInstance(bot.getBotDirection(), robotCenterX, robotCenterY); + l.concatenate(t); + g.setTransform(l); + g.setColor(bot.getCondition().getColor()); + + if (bot.getCondition().equals(Condition.HUNGRY)) + { + fillOval(g, robotCenterX, robotCenterY, 20, 10); + g.setColor(Color.BLACK); + drawOval(g, robotCenterX, robotCenterY, 20, 10); + g.setColor(Color.YELLOW); + } + + if (bot.getCondition().equals(Condition.CALM)) + { + fillOval(g, robotCenterX, robotCenterY, 20, 10); + g.setColor(Color.BLACK); + drawOval(g, robotCenterX, robotCenterY, 20, 10); + g.setColor(Color.WHITE); + } + + if (bot.getCondition().equals(Condition.DEAD)) + { + fillOval(g, robotCenterX, robotCenterY, 20, 10); + g.setColor(Color.BLACK); + drawOval(g, robotCenterX, robotCenterY, 20, 10); + g.setColor(Color.MAGENTA); + } + + fillOval(g, robotCenterX + 10, robotCenterY, 5, 5); + g.setColor(Color.BLACK); + drawOval(g, robotCenterX + 10, robotCenterY, 5, 5); + g.setTransform(oldTransform); + } +} diff --git a/robots/src/view/draw/Drawer.java b/robots/src/view/draw/Drawer.java index 79f411404..788ebf35a 100644 --- a/robots/src/view/draw/Drawer.java +++ b/robots/src/view/draw/Drawer.java @@ -1,6 +1,8 @@ package application.view.draw; +import application.model.Bot; import application.model.Entity; +import application.model.Food; import java.awt.*; @@ -15,8 +17,4 @@ protected static void drawOval(Graphics g, int centerX, int centerY, int diam1, { g.drawOval(centerX - diam1 / 2, centerY - diam2 / 2, diam1, diam2); } - - public abstract void draw(Graphics2D g, Entity entity); - - public abstract Class getDrawingType(); } diff --git a/robots/src/view/draw/FoodDrawer.java b/robots/src/view/draw/FoodDrawer.java new file mode 100644 index 000000000..f04bd8335 --- /dev/null +++ b/robots/src/view/draw/FoodDrawer.java @@ -0,0 +1,19 @@ +package application.view.draw; + +import application.model.Food; + +import java.awt.*; +import java.awt.geom.AffineTransform; + +public class FoodDrawer extends Drawer +{ + public void draw(Graphics2D g, Food food) + { + AffineTransform t = AffineTransform.getRotateInstance(0, 0, 0); + g.setTransform(t); + g.setColor(Color.GREEN); + fillOval(g, food.getX(), food.getY(), 5, 5); + g.setColor(Color.BLACK); + drawOval(g, food.getX(), food.getY(), 5, 5); + } +} diff --git a/robots/src/view/draw/TargetDrawer.java b/robots/src/view/draw/TargetDrawer.java index 16d1bbf86..2b0658601 100644 --- a/robots/src/view/draw/TargetDrawer.java +++ b/robots/src/view/draw/TargetDrawer.java @@ -1,26 +1 @@ -package application.view.draw; - import application.model.Bacteria; -import application.model.Entity; -import application.model.Target; - -import java.awt.*; - -public class TargetDrawer extends Drawer -{ - @Override - public void draw(Graphics2D g, Entity entity) - { - Target target = ((Bacteria) entity).getTarget(); - g.setColor(Color.GREEN); - fillOval(g, target.getX(), target.getY(), 5, 5); - g.setColor(Color.BLACK); - drawOval(g, target.getX(), target.getY(), 5, 5); - } - - @Override - public Class getDrawingType() - { - return Target.class; - } -} diff --git a/robots/src/viewModel/GameViewModel.java b/robots/src/viewModel/ViewModel.java similarity index 89% rename from robots/src/viewModel/GameViewModel.java rename to robots/src/viewModel/ViewModel.java index 4e94ef72f..2905333fc 100644 --- a/robots/src/viewModel/GameViewModel.java +++ b/robots/src/viewModel/ViewModel.java @@ -12,7 +12,7 @@ import java.util.TimerTask; -public class GameViewModel +public class ViewModel { private final GameModel gameModel; private final GameWindow gameWindow; @@ -23,7 +23,7 @@ private static java.util.Timer initTimer() return new Timer("events generator", true); } - public GameViewModel(GameModel gameModel, GameWindow gameWindow) + public ViewModel(GameModel gameModel, GameWindow gameWindow) { this.gameModel = gameModel; this.gameWindow = gameWindow; @@ -37,10 +37,10 @@ private void initListeners() @Override public void run() { - gameModel.setDimension(gameWindow.getGameView().getSize()); + gameModel.setDimension(gameWindow.getSize()); getGameView().updateView(); } - }, 0, 50); + }, 0, 10); timer.schedule(new TimerTask() { @@ -57,7 +57,7 @@ public void run() public void mouseClicked(MouseEvent e) { System.out.println(e.getPoint()); - gameModel.setTarget(e.getPoint()); + gameModel.setFoodGoal(e.getPoint()); getGameView().repaint(); } }); From 4367c2d3a99ff8250e83a9a4ec78341a465a3ef1 Mon Sep 17 00:00:00 2001 From: VolodyaDe <114046476+VolodyaDe@users.noreply.github.com> Date: Sun, 8 Oct 2023 18:21:02 +0500 Subject: [PATCH 3/4] Change game model #2 --- robots/src/{ => application}/Main.java | 0 .../MainApplicationFrame.java | 0 .../log/LogChangeListener.java | 0 .../src/{ => application}/log/LogEntry.java | 0 .../src/{ => application}/log/LogLevel.java | 0 .../src/{ => application}/log/LogWindow.java | 0 .../log/LogWindowSource.java | 0 robots/src/{ => application}/log/Logger.java | 0 robots/src/{ => application}/model/Bot.java | 0 .../src/{ => application}/model/Entity.java | 0 robots/src/{ => application}/model/Food.java | 0 .../{ => application}/model/GameModel.java | 0 .../model/additionals/Condition.java | 0 .../src/{ => application}/view/GameView.java | 0 .../{ => application}/view/GameWindow.java | 0 .../view/draw/BotDrawer.java | 0 .../{ => application}/view/draw/Drawer.java | 0 .../view/draw/FoodDrawer.java | 0 .../viewModel/ViewModel.java | 0 robots/src/model/Bacteria.java | 263 ------------------ robots/src/model/Target.java | 56 ---- robots/src/model/additionals/Mood.java | 32 --- robots/src/view/draw/BacteriaDrawer.java | 52 ---- robots/src/view/draw/TargetDrawer.java | 1 - 24 files changed, 404 deletions(-) rename robots/src/{ => application}/Main.java (100%) rename robots/src/{ => application}/MainApplicationFrame.java (100%) rename robots/src/{ => application}/log/LogChangeListener.java (100%) rename robots/src/{ => application}/log/LogEntry.java (100%) rename robots/src/{ => application}/log/LogLevel.java (100%) rename robots/src/{ => application}/log/LogWindow.java (100%) rename robots/src/{ => application}/log/LogWindowSource.java (100%) rename robots/src/{ => application}/log/Logger.java (100%) rename robots/src/{ => application}/model/Bot.java (100%) rename robots/src/{ => application}/model/Entity.java (100%) rename robots/src/{ => application}/model/Food.java (100%) rename robots/src/{ => application}/model/GameModel.java (100%) rename robots/src/{ => application}/model/additionals/Condition.java (100%) rename robots/src/{ => application}/view/GameView.java (100%) rename robots/src/{ => application}/view/GameWindow.java (100%) rename robots/src/{ => application}/view/draw/BotDrawer.java (100%) rename robots/src/{ => application}/view/draw/Drawer.java (100%) rename robots/src/{ => application}/view/draw/FoodDrawer.java (100%) rename robots/src/{ => application}/viewModel/ViewModel.java (100%) delete mode 100644 robots/src/model/Bacteria.java delete mode 100644 robots/src/model/Target.java delete mode 100644 robots/src/model/additionals/Mood.java delete mode 100644 robots/src/view/draw/BacteriaDrawer.java delete mode 100644 robots/src/view/draw/TargetDrawer.java diff --git a/robots/src/Main.java b/robots/src/application/Main.java similarity index 100% rename from robots/src/Main.java rename to robots/src/application/Main.java diff --git a/robots/src/MainApplicationFrame.java b/robots/src/application/MainApplicationFrame.java similarity index 100% rename from robots/src/MainApplicationFrame.java rename to robots/src/application/MainApplicationFrame.java diff --git a/robots/src/log/LogChangeListener.java b/robots/src/application/log/LogChangeListener.java similarity index 100% rename from robots/src/log/LogChangeListener.java rename to robots/src/application/log/LogChangeListener.java diff --git a/robots/src/log/LogEntry.java b/robots/src/application/log/LogEntry.java similarity index 100% rename from robots/src/log/LogEntry.java rename to robots/src/application/log/LogEntry.java diff --git a/robots/src/log/LogLevel.java b/robots/src/application/log/LogLevel.java similarity index 100% rename from robots/src/log/LogLevel.java rename to robots/src/application/log/LogLevel.java diff --git a/robots/src/log/LogWindow.java b/robots/src/application/log/LogWindow.java similarity index 100% rename from robots/src/log/LogWindow.java rename to robots/src/application/log/LogWindow.java diff --git a/robots/src/log/LogWindowSource.java b/robots/src/application/log/LogWindowSource.java similarity index 100% rename from robots/src/log/LogWindowSource.java rename to robots/src/application/log/LogWindowSource.java diff --git a/robots/src/log/Logger.java b/robots/src/application/log/Logger.java similarity index 100% rename from robots/src/log/Logger.java rename to robots/src/application/log/Logger.java diff --git a/robots/src/model/Bot.java b/robots/src/application/model/Bot.java similarity index 100% rename from robots/src/model/Bot.java rename to robots/src/application/model/Bot.java diff --git a/robots/src/model/Entity.java b/robots/src/application/model/Entity.java similarity index 100% rename from robots/src/model/Entity.java rename to robots/src/application/model/Entity.java diff --git a/robots/src/model/Food.java b/robots/src/application/model/Food.java similarity index 100% rename from robots/src/model/Food.java rename to robots/src/application/model/Food.java diff --git a/robots/src/model/GameModel.java b/robots/src/application/model/GameModel.java similarity index 100% rename from robots/src/model/GameModel.java rename to robots/src/application/model/GameModel.java diff --git a/robots/src/model/additionals/Condition.java b/robots/src/application/model/additionals/Condition.java similarity index 100% rename from robots/src/model/additionals/Condition.java rename to robots/src/application/model/additionals/Condition.java diff --git a/robots/src/view/GameView.java b/robots/src/application/view/GameView.java similarity index 100% rename from robots/src/view/GameView.java rename to robots/src/application/view/GameView.java diff --git a/robots/src/view/GameWindow.java b/robots/src/application/view/GameWindow.java similarity index 100% rename from robots/src/view/GameWindow.java rename to robots/src/application/view/GameWindow.java diff --git a/robots/src/view/draw/BotDrawer.java b/robots/src/application/view/draw/BotDrawer.java similarity index 100% rename from robots/src/view/draw/BotDrawer.java rename to robots/src/application/view/draw/BotDrawer.java diff --git a/robots/src/view/draw/Drawer.java b/robots/src/application/view/draw/Drawer.java similarity index 100% rename from robots/src/view/draw/Drawer.java rename to robots/src/application/view/draw/Drawer.java diff --git a/robots/src/view/draw/FoodDrawer.java b/robots/src/application/view/draw/FoodDrawer.java similarity index 100% rename from robots/src/view/draw/FoodDrawer.java rename to robots/src/application/view/draw/FoodDrawer.java diff --git a/robots/src/viewModel/ViewModel.java b/robots/src/application/viewModel/ViewModel.java similarity index 100% rename from robots/src/viewModel/ViewModel.java rename to robots/src/application/viewModel/ViewModel.java diff --git a/robots/src/model/Bacteria.java b/robots/src/model/Bacteria.java deleted file mode 100644 index d027a4959..000000000 --- a/robots/src/model/Bacteria.java +++ /dev/null @@ -1,263 +0,0 @@ -package application.model; - - -import application.model.additionals.Mood; - -import java.awt.*; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeSupport; - -public class Bacteria implements Entity -{ - private double positionX; - private double positionY; - private volatile double bacteriaDirection; - private Target target; - - private Mood mood; - private int satiety; - public static double maxVelocity = 0.05; - public static final double maxAngularVelocity = 0.005; - private boolean isAlive; - private Dimension dimension; - private static final int INITIAL_SATIETY = 50; - private static final int MAX_SATIETY = 100; - - public Bacteria(double x, double y) - { - this.positionX = x; - this.positionY = y; - this.target = new Target(); - this.bacteriaDirection = Math.random() * 10; - this.dimension = new Dimension(400, 400); - this.mood = Mood.randomMood(); - this.satiety = (int) (INITIAL_SATIETY + Math.random() * (MAX_SATIETY - INITIAL_SATIETY)); - this.isAlive = true; - } - - private void setRandomMood() - { - this.mood = Mood.randomMood(); - } - - public void setMood(Mood mood) - { - this.mood = mood; - } - - public void setDimension(Dimension dimension) - { - this.dimension = dimension; - if (!target.isPositionCorrect(dimension)) - { - target = new Target((int) (Math.random() * dimension.width), (int) (Math.random() * dimension.height)); - } - } - - public Dimension getDimension() - { - return this.dimension; - } - - public double getPositionX() - { - return positionX; - } - - public void setPositionX(double positionX) - { - this.positionX = positionX; - } - - public double getPositionY() - { - return positionY; - } - - public void setPositionY(double positionY) - { - this.positionY = positionY; - } - - public double getBacteriaDirection() - { - return bacteriaDirection; - } - - public void setBacteriaDirection(double bacteriaDirection) - { - this.bacteriaDirection = bacteriaDirection; - } - - 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 double normalizedPositionX(double x) - { - if (x < 0) - return 0; - if (x > dimension.width) - return dimension.width; - return x; - } - - private double normalizedPositionY(double y) - { - if (y < 0) - return 0; - if (y > dimension.height) - return dimension.height; - return y; - } - - private static double applyLimits(double value, double min, double max) - { - if (value < min) - return min; - return Math.min(value, max); - } - - public Target getTarget() - { - return target; - } - - public void changeSatiety(int satiety) - { - this.satiety += satiety; - } - - public void setTarget(Point point) - { - if (point.x > dimension.width || point.y > dimension.height) - { - this.target.setTargetPosition(new Point(point.x / dimension.width, point.y / dimension.height)); - } - this.target.setTargetPosition(point); - } - - private void moveBacteria(double velocity, double angularVelocity, double duration) - { - velocity = applyLimits(velocity, 0, Bacteria.maxVelocity); - angularVelocity = applyLimits(angularVelocity, -Bacteria.maxAngularVelocity, Bacteria.maxAngularVelocity); - double newX = getPositionX() + velocity / angularVelocity * - (Math.sin(getBacteriaDirection() + angularVelocity * duration) - - Math.sin(getBacteriaDirection())); - if (!Double.isFinite(newX)) - newX = getPositionX() + velocity * duration * Math.cos(getBacteriaDirection()); - - double newY = getPositionY() - velocity / angularVelocity * - (Math.cos(getBacteriaDirection() + angularVelocity * duration) - - Math.cos(getBacteriaDirection())); - if (!Double.isFinite(newY)) - newY = getPositionY() + velocity * duration * Math.sin(getBacteriaDirection()); - - setPositionX(normalizedPositionX(newX)); - setPositionY(normalizedPositionY(newY)); - double newDirection = asNormalizedRadians(getBacteriaDirection() + angularVelocity * duration); - setBacteriaDirection(newDirection); - } - - - @Override - public void update() - { - if (!this.isAlive) - { - this.setMood(Mood.DEAD); - - return; - } - if (this.satiety < 50) - { - if (this.satiety < 0) - isAlive = false; - this.setMood(Mood.HUNGRY); - - } - double distance = distance(target.getX(), target.getY(), - getPositionX(), getPositionY()); - - if (distance < 0.5) - { - this.onTargetAchieved(); - return; - } - double angleToTarget = angleTo(getPositionX(), getPositionY(), - target.getX(), target.getY()); - double angularVelocity = 0; - if (angleToTarget > getBacteriaDirection()) - angularVelocity = Bacteria.maxAngularVelocity; - if (angleToTarget < getBacteriaDirection()) - angularVelocity = -Bacteria.maxAngularVelocity; - - moveBacteria(Bacteria.maxVelocity, angularVelocity, 10); - } - - @Override - public void onStart(PropertyChangeSupport publisher) - { - publisher.addPropertyChangeListener(this); - } - - @Override - public void onFinish(PropertyChangeSupport publisher) - { - publisher.removePropertyChangeListener(this); - } - - @Override - public boolean isAlive() - { - return isAlive; - } - - private void onTargetAchieved() - { - this.setTarget(new Point((int) (Math.random() * dimension.width), (int) (Math.random() * dimension.height))); - this.satiety += 20; - if (this.satiety > 50) - { - this.setRandomMood(); - } - } - - @Override - public void propertyChange(PropertyChangeEvent evt) - { - if (evt.getPropertyName().equals("new point")) - setTarget((Point) evt.getNewValue()); - if (evt.getPropertyName().equals("change satiety")) - changeSatiety((int) evt.getNewValue()); - if (evt.getPropertyName().equals("set dimension")) - setDimension((Dimension) evt.getNewValue()); - } - - public Mood getMood() - { - return mood; - } -} \ No newline at end of file diff --git a/robots/src/model/Target.java b/robots/src/model/Target.java deleted file mode 100644 index 9f8984f1c..000000000 --- a/robots/src/model/Target.java +++ /dev/null @@ -1,56 +0,0 @@ -package application.model; - -import java.awt.*; - -public class Target { - private volatile int x; - private volatile int y; - - public Target() - { - this.x = 150; - this.y = 100; - } - - public Target(int x, int y) - { - this.x = x; - this.y = y; - } - - public void setX(int x) - { - this.x = x; - } - - public void setY(int y) - { - this.y = y; - } - - public int getX() - { - return x; - } - - public int getY() - { - return y; - } - - public void setTargetPosition(Point p) - { - setX(p.x); - setY(p.y); - } - - protected Point getTargetPosition() - { - return new Point(getX(), getY()); - } - - public boolean isPositionCorrect(Dimension dimension) - { - return this.x <= dimension.width && this.y <= dimension.height; - } -} diff --git a/robots/src/model/additionals/Mood.java b/robots/src/model/additionals/Mood.java deleted file mode 100644 index 5fdfbfc75..000000000 --- a/robots/src/model/additionals/Mood.java +++ /dev/null @@ -1,32 +0,0 @@ -package application.model.additionals; - -import java.awt.*; -import java.util.List; -import java.util.Random; - -public enum Mood -{ - CALM(Color.GREEN), - EVIL(Color.RED), - ENAMORED(Color.MAGENTA), - HUNGRY(Color.YELLOW), - DEAD(Color.BLACK); - private final Color color; - private static final Random RANDOM = new Random(); - private static final List values = List.of(values()); - - public static Mood randomMood() - { - return values.get(RANDOM.nextInt(values().length - 2)); - } - - Mood(Color color) - { - this.color = color; - } - - - public Color getColor() { - return color; - } -} diff --git a/robots/src/view/draw/BacteriaDrawer.java b/robots/src/view/draw/BacteriaDrawer.java deleted file mode 100644 index e78fbac8e..000000000 --- a/robots/src/view/draw/BacteriaDrawer.java +++ /dev/null @@ -1,52 +0,0 @@ -package application.view.draw; - -import application.model.Bacteria; -import application.model.Entity; -import application.model.additionals.Mood; - -import java.awt.*; -import java.awt.geom.AffineTransform; - -public class BacteriaDrawer extends Drawer -{ - @Override - public void draw(Graphics2D g, Entity entity) - { - Bacteria bacteria = (Bacteria) entity; - AffineTransform oldTransform = g.getTransform(); - int bacteriaCenterX = (int) (Math.round(bacteria.getPositionX())); - int bacteriaCenterY = (int) (Math.round(bacteria.getPositionY())); - AffineTransform l = new AffineTransform(oldTransform); - AffineTransform t = AffineTransform.getRotateInstance(bacteria.getBacteriaDirection(), bacteriaCenterX, bacteriaCenterY); - l.concatenate(t); - - g.setTransform(l); - - g.setColor(bacteria.getMood().getColor()); - if (bacteria.getMood().equals(Mood.HUNGRY)) - { - fillOval(g, bacteriaCenterX, bacteriaCenterY, 25, 10); - g.setColor(Color.BLACK); - drawOval(g, bacteriaCenterX, bacteriaCenterY, 25, 10); - g.setColor(Color.WHITE); - } - else - { - fillOval(g, bacteriaCenterX, bacteriaCenterY, 25, 15); - g.setColor(Color.BLACK); - drawOval(g, bacteriaCenterX, bacteriaCenterY, 25, 15); - g.setColor(Color.WHITE); - } - - fillOval(g, bacteriaCenterX + 7, bacteriaCenterY, 5, 5); - g.setColor(Color.BLACK); - drawOval(g, bacteriaCenterX + 7, bacteriaCenterY, 5, 5); - g.setTransform(oldTransform); - } - - @Override - public Class getDrawingType() - { - return Bacteria.class; - } -} diff --git a/robots/src/view/draw/TargetDrawer.java b/robots/src/view/draw/TargetDrawer.java deleted file mode 100644 index 2b0658601..000000000 --- a/robots/src/view/draw/TargetDrawer.java +++ /dev/null @@ -1 +0,0 @@ -import application.model.Bacteria; From 170e84766db0ea9987f2413244fdf3fccdbac53a Mon Sep 17 00:00:00 2001 From: VolodyaDe <114046476+VolodyaDe@users.noreply.github.com> Date: Tue, 24 Oct 2023 02:47:28 +0500 Subject: [PATCH 4/4] Combining all changes --- robots/src/application/model/Bot.java | 30 +++-- robots/src/application/model/BotHandler.java | 133 +++++++++++++++++++ robots/src/application/model/Food.java | 27 +++- robots/src/application/model/GameModel.java | 55 ++------ robots/src/application/view/GameView.java | 11 +- 5 files changed, 198 insertions(+), 58 deletions(-) create mode 100644 robots/src/application/model/BotHandler.java diff --git a/robots/src/application/model/Bot.java b/robots/src/application/model/Bot.java index c04b84638..93d3ad98f 100644 --- a/robots/src/application/model/Bot.java +++ b/robots/src/application/model/Bot.java @@ -22,32 +22,25 @@ public class Bot implements Entity private static final int START_LIFETIME = 40; private static final int FULL_LIFETIME = 90; - public Bot(double startX, double startY) + public Bot(double startX, double startY, int lifeTime) { this.positionX = startX; this.positionY = startY; this.dimension = new Dimension(400, 400); - this.foodGoal = new Food((int) (Math.random() * dimension.width), (int) (Math.random() * dimension.height)); + this.foodGoal = new Food(); this.botDirection = Math.random() * 10; - this.lifeTime = (int) (START_LIFETIME + Math.random() * (FULL_LIFETIME - START_LIFETIME)); + this.lifeTime = lifeTime; this.condition = Condition.randomCondition(); this.isAlive = true; } - public Bot() - { - this.positionX = 300; - this.positionY = 300; - this.foodGoal = new Food(); - this.botDirection = 0; - } - public void setDimension(Dimension dimension) { this.dimension = dimension; if (!foodGoal.isPositionCorrect(dimension)) { - foodGoal = new Food((int) (Math.random() * dimension.width), (int) (Math.random() * dimension.height)); + foodGoal.setX((int) (Math.random() * dimension.width)); + foodGoal.setY((int) (Math.random() * dimension.height)); } } @@ -147,6 +140,11 @@ public Food getFoodGoal() return foodGoal; } + public int getLifeTime() + { + return lifeTime; + } + public void changeLifeTime(int lifeTmeDif) { this.lifeTime += lifeTmeDif; @@ -161,6 +159,10 @@ public void setFoodGoal(Point point) this.foodGoal.setFoodPosition(point); } + public void setFoodGoal(Food food){ + this.foodGoal = food; + } + private void moveBot(double velocity, double angularVelocity, double duration) { velocity = applyLimits(velocity, 0, Bot.maxVelocity); @@ -244,8 +246,8 @@ public void onFinish(PropertyChangeSupport publisher) private void foodGoalAchieved() { - this.setFoodGoal(new Point((int) (Math.random() * dimension.width), (int) (Math.random() * dimension.height))); - this.setFoodGoal(new Point((int) (Math.random() * dimension.width), (int) (Math.random() * dimension.height))); + //this.setFoodGoal(new Point((int) (Math.random() * dimension.width), (int) (Math.random() * dimension.height))); + this.foodGoal=new Food(); this.lifeTime += 20; if (this.lifeTime > 50) { diff --git a/robots/src/application/model/BotHandler.java b/robots/src/application/model/BotHandler.java new file mode 100644 index 000000000..0261f2f72 --- /dev/null +++ b/robots/src/application/model/BotHandler.java @@ -0,0 +1,133 @@ +package application.model; + +import java.awt.*; +import java.beans.PropertyChangeSupport; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import static java.awt.geom.Point2D.distance; + +public class BotHandler +{ + private final Dimension dimension; + private final java.util.List bots; + private final List foods; + private final PropertyChangeSupport support; + private static final int BOT_COUNT = 5; + private static final int FOOD_COUNT = 5; + private static final int START_LIFETIME = 40; + private static final int FULL_LIFETIME = 90; + + public BotHandler() + { + this.bots = new ArrayList<>(); + this.foods = new ArrayList<>(); + this.dimension = new Dimension(400, 400); + this.support = new PropertyChangeSupport(this); + + for (int i = 0; i < FOOD_COUNT; i++) + { + Food food = new Food((int) (Math.random() * 400), (int) (Math.random() * 400)); + foods.add(food); + } + + for (int i = 0; i < BOT_COUNT; i++) + { + Bot bot = new Bot(Math.random() * 400, Math.random() * 400, (int) (START_LIFETIME + Math.random() * (FULL_LIFETIME - START_LIFETIME))); + bot.setFoodGoal(findFood(bot)); + bots.add(bot); + bot.onStart(support); + } + } + + private Food findFood(Bot bot) + { + double minDistance = Double.MAX_VALUE; + Food nearestFood = null; + + for (Food food : foods) + { + double distance = distance(bot.getPositionX(), bot.getPositionY(), food.getX(), food.getY()); + + if (distance < minDistance) + { + minDistance = distance; + nearestFood = food; + } + } + + if (nearestFood != null) + { + foods.remove(nearestFood); + } + + return nearestFood; + } + + public void suddenBotClone() + { + if (Math.random() * 6 > 2) + { + Random rand = new Random(); + Bot bot = bots.get(rand.nextInt(bots.size())); + double x = bot.getPositionX(); + double y = bot.getPositionY(); + bot.onFinish(support); + bots.remove(bot); + for (int i = 0; i < 2; i++) + { + int xx = (int) (Math.random() * dimension.width); + int yy = (int) (Math.random() * dimension.height); + Food food = new Food(xx, yy); + foods.add(food); + Bot b = new Bot(x, y, bot.getLifeTime() / 2); + b.setFoodGoal(findFood(b)); + bots.add(b); + } + } + } + + public void changeLifeTime() + { + support.firePropertyChange("change life time", null, -10); + } + + public List getBotList() + { + return bots; + } + + public List getFoodList() + { + return foods; + } + + public void setFoodGoal(Point point) + { + support.firePropertyChange("new point", null, point); + } + + public void update() + { + for (Food food : foods){ + if (!food.isPositionCorrect(dimension)) + { + food.setX((int) (Math.random() * dimension.width)); + food.setY((int) (Math.random() * dimension.height)); + } + } + for (Bot bot : bots){ + Food f = bot.getFoodGoal(); + if (!f.spawn) + { + int xx = (int) (Math.random() * dimension.width); + int yy = (int) (Math.random() * dimension.height); + Food food = new Food(xx, yy); + foods.add(food); + bot.setFoodGoal(findFood(bot)); + } + bot.update(); + } + } +} diff --git a/robots/src/application/model/Food.java b/robots/src/application/model/Food.java index 311b4474f..b23f5664b 100644 --- a/robots/src/application/model/Food.java +++ b/robots/src/application/model/Food.java @@ -1,21 +1,26 @@ package application.model; import java.awt.*; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeSupport; -public class Food { +public class Food implements Entity{ private volatile int x; private volatile int y; + boolean spawn; public Food(int x, int y) { this.x = x; this.y = y; + this.spawn=true; } public Food() { this.x = 100; this.y = 100; + this.spawn=false; } public void setX(int x) @@ -53,4 +58,24 @@ public boolean isPositionCorrect(Dimension dimension) { return this.x <= dimension.width && this.y <= dimension.height; } + + @Override + public void update() { + + } + + @Override + public void onStart(PropertyChangeSupport publisher) { + + } + + @Override + public void onFinish(PropertyChangeSupport publisher) { + + } + + @Override + public void propertyChange(PropertyChangeEvent evt) { + + } } diff --git a/robots/src/application/model/GameModel.java b/robots/src/application/model/GameModel.java index 89d8d88c6..aba7e5d27 100644 --- a/robots/src/application/model/GameModel.java +++ b/robots/src/application/model/GameModel.java @@ -1,28 +1,16 @@ package application.model; import java.awt.*; -import java.beans.PropertyChangeListener; -import java.beans.PropertyChangeSupport; import java.util.*; import java.util.List; public class GameModel { - private final List bots; - private final PropertyChangeSupport support; - private static final int BOT_COUNT = 5; + private final BotHandler botHandler; public GameModel() { - this.bots = new ArrayList<>(); - this.support = new PropertyChangeSupport(this); - - for (int i = 0; i < BOT_COUNT; i++) - { - Bot bot = new Bot(Math.random() * 400, Math.random() * 400); - bots.add(bot); - addPropertyChangeListener(bot); - } + this.botHandler = new BotHandler(); Timer timer = initTimer(); timer.schedule(new TimerTask() @@ -31,9 +19,8 @@ public GameModel() public void run() { System.out.println("timer"); - support.firePropertyChange("change life time", null, -10); - if (Math.random() * 4 > 2.3) - suddenBotClone(); + botHandler.changeLifeTime(); + botHandler.suddenBotClone(); } }, 0, 3000); } @@ -43,50 +30,34 @@ private static java.util.Timer initTimer() return new Timer("events generator", true); } - public void addPropertyChangeListener(PropertyChangeListener pcl) { - support.addPropertyChangeListener(pcl); - } - - public void removePropertyChangeListener(PropertyChangeListener pcl) { - support.removePropertyChangeListener(pcl); - } - public void setDimension(Dimension dimension) { - for (Bot robot : bots) - robot.setDimension(dimension); + for (Bot bot : botHandler.getBotList()) + bot.setDimension(dimension); } public Dimension getDimension() { - return bots.get(0).getDimension(); + return botHandler.getBotList().get(0).getDimension(); } public void updateModel() { - for (Bot bot : bots) - bot.update(); + botHandler.update(); } - private void suddenBotClone() + public List getRobots() { - Random rand = new Random(); - Bot bot = bots.get(rand.nextInt(bots.size())); - double x = bot.getPositionX(); - double y = bot.getPositionY(); - removePropertyChangeListener(bot); - bots.remove(bot); - for (int i = 0; i < 2; i++) - bots.add(new Bot(x, y)); + return botHandler.getBotList(); } - public List getRobots() + public List getFoods() { - return bots; + return botHandler.getFoodList(); } public void setFoodGoal(Point point) { - support.firePropertyChange("new point", null, point); + botHandler.setFoodGoal(point); } } diff --git a/robots/src/application/view/GameView.java b/robots/src/application/view/GameView.java index 78b9faef6..b12c4d97d 100644 --- a/robots/src/application/view/GameView.java +++ b/robots/src/application/view/GameView.java @@ -1,6 +1,7 @@ package application.view; import application.model.Bot; +import application.model.Food; import application.model.GameModel; import application.view.draw.BotDrawer; import application.view.draw.FoodDrawer; @@ -39,10 +40,18 @@ public void paint(Graphics g) super.paint(g); Graphics2D g2d = (Graphics2D) g; ArrayList bots = (ArrayList) gameModel.getRobots(); + ArrayList foods = (ArrayList) gameModel.getFoods(); for (Bot bot : bots) { botDrawer.draw(g2d, bot); - foodDrawer.draw(g2d, bot.getFoodGoal()); + if (bot.getFoodGoal() != null) + { + foodDrawer.draw(g2d, bot.getFoodGoal()); + } + } + for (Food food : foods) + { + foodDrawer.draw(g2d,food); } } }