Skip to content
38 changes: 37 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,40 @@
/.metadata
/robots/.settings
/robots/bin
eclipse.bat
eclipse.bat
out/
/robots/.classpath
/robots/.project
# Translations
*.mo

# Compiled class file
*.class

# Log file
*.log

# BlueJ files
*.ctxt

# Mobile Tools for Java (J2ME)
.mtj.tmp/

# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar

# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*

# IDEA
.idea/
*.iml

# Maven
target/
89 changes: 89 additions & 0 deletions robots/src/config/ConfigManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package config;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Properties;
import log.Logger;

public class ConfigManager {
private static final String CONFIG_FILE = System.getProperty("user.home") + File.separator + ".robot-config.properties";
private final Properties props = new Properties();

public void load() {
File file = new File(CONFIG_FILE);
if (!file.exists()) return;
try (FileInputStream fis = new FileInputStream(file)) {
props.load(fis);
} catch (IOException e) {
Logger.debug("Не удалось загрузить конфигурацию: " + e.getMessage());
}
}

public void save() {
try (FileOutputStream fos = new FileOutputStream(CONFIG_FILE)) {
props.store(fos, "Robot program configuration");
} catch (IOException e) {
Logger.debug("Не удалось сохранить конфигурацию: " + e.getMessage());
}
}

public void setMainWindowBounds(int x, int y, int width, int height, int state) {
setProperty("main.x", x);
setProperty("main.y", y);
setProperty("main.width", width);
setProperty("main.height", height);
setProperty("main.state", state);
}

public int getMainWindowX(int defaultValue) { return getIntProperty("main.x", defaultValue); }
public int getMainWindowY(int defaultValue) { return getIntProperty("main.y", defaultValue); }
public int getMainWindowWidth(int defaultValue) { return getIntProperty("main.width", defaultValue); }
public int getMainWindowHeight(int defaultValue) { return getIntProperty("main.height", defaultValue); }
public int getMainWindowState(int defaultValue) { return getIntProperty("main.state", defaultValue); }

public void setWindowBounds(String windowName, int x, int y, int width, int height, boolean icon, boolean maximized) {
setProperty(key(windowName, "x"), x);
setProperty(key(windowName, "y"), y);
setProperty(key(windowName, "width"), width);
setProperty(key(windowName, "height"), height);
setProperty(key(windowName, "icon"), icon);
setProperty(key(windowName, "maximized"), maximized);
}

public boolean hasWindow(String windowName) {
return props.containsKey(key(windowName, "x"));
}

public int getWindowX(String windowName, int defaultValue) { return getIntProperty(key(windowName, "x"), defaultValue); }
public int getWindowY(String windowName, int defaultValue) { return getIntProperty(key(windowName, "y"), defaultValue); }
public int getWindowWidth(String windowName, int defaultValue) { return getIntProperty(key(windowName, "width"), defaultValue); }
public int getWindowHeight(String windowName, int defaultValue) { return getIntProperty(key(windowName, "height"), defaultValue); }
public boolean getWindowIcon(String windowName, boolean defaultValue) { return getBooleanProperty(key(windowName, "icon"), defaultValue); }
public boolean getWindowMaximized(String windowName, boolean defaultValue) { return getBooleanProperty(key(windowName, "maximized"), defaultValue); }

public void setWindowVisible(String windowName, boolean visible) {
setProperty(key(windowName, "visible"), visible);
}

public boolean getWindowVisible(String windowName, boolean defaultValue) {
return getBooleanProperty(key(windowName, "visible"), defaultValue);
}


private String key(String windowName, String suffix) { return windowName + "." + suffix; }
private void setProperty(String key, int value) { props.setProperty(key, Integer.toString(value)); }
private void setProperty(String key, boolean value) { props.setProperty(key, Boolean.toString(value)); }

private int getIntProperty(String key, int defaultValue) {
String val = props.getProperty(key);
if (val == null) return defaultValue;
try { return Integer.parseInt(val); } catch (NumberFormatException e) { return defaultValue; }
}

private boolean getBooleanProperty(String key, boolean defaultValue) {
String val = props.getProperty(key);
return val == null ? defaultValue : Boolean.parseBoolean(val);
}
}
222 changes: 41 additions & 181 deletions robots/src/gui/GameVisualizer.java
Original file line number Diff line number Diff line change
@@ -1,210 +1,70 @@
package gui;

import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import model.RobotController;
import model.RobotModel;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import java.util.Timer;
import java.util.TimerTask;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.swing.JPanel;
public class GameVisualizer extends JPanel implements PropertyChangeListener {
private final RobotModel model;

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()
{
public GameVisualizer(RobotModel model, RobotController controller) {
this.model = model;
model.addPropertyChangeListener(this);
addMouseListener(new MouseAdapter() {
@Override
public void run()
{
onModelUpdateEvent();
}
}, 0, 10);
addMouseListener(new MouseAdapter()
{
@Override
public void mouseClicked(MouseEvent e)
{
setTargetPosition(e.getPoint());
repaint();
public void mouseClicked(MouseEvent e) {
controller.setTarget(e.getX(), e.getY());
}
});
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;
@Override
public void propertyChange(PropertyChangeEvent evt) {
repaint();
}

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);
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
drawRobot(g2d, (int)model.getRobotX(), (int)model.getRobotY(), model.getRobotDirection());
drawTarget(g2d, model.getTargetX(), model.getTargetY());
}

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);

private void drawRobot(Graphics2D g, int x, int y, double direction) {
AffineTransform t = AffineTransform.getRotateInstance(direction, x, y);
g.setTransform(t);
g.setColor(Color.MAGENTA);
fillOval(g, robotCenterX, robotCenterY, 30, 10);
fillOval(g, x, y, 30, 10);
g.setColor(Color.BLACK);
drawOval(g, robotCenterX, robotCenterY, 30, 10);
drawOval(g, x, y, 30, 10);
g.setColor(Color.WHITE);
fillOval(g, robotCenterX + 10, robotCenterY, 5, 5);
fillOval(g, x + 10, y, 5, 5);
g.setColor(Color.BLACK);
drawOval(g, robotCenterX + 10, robotCenterY, 5, 5);
drawOval(g, x + 10, y, 5, 5);
g.setTransform(new AffineTransform());
}

private void drawTarget(Graphics2D g, int x, int y)
{
AffineTransform t = AffineTransform.getRotateInstance(0, 0, 0);
g.setTransform(t);

private void drawTarget(Graphics2D g, int x, int y) {
g.setTransform(new AffineTransform());
g.setColor(Color.GREEN);
fillOval(g, x, y, 5, 5);
g.setColor(Color.BLACK);
drawOval(g, x, y, 5, 5);
}

private void fillOval(Graphics g, int cx, int cy, int w, int h) {
g.fillOval(cx - w/2, cy - h/2, w, h);
}

private void drawOval(Graphics g, int cx, int cy, int w, int h) {
g.drawOval(cx - w/2, cy - h/2, w, h);
}
}
Loading