Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 29 additions & 11 deletions src/main/java/thommynator/App.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package thommynator;

import thommynator.game.Drawer;
import thommynator.game.GameCanvas;
import thommynator.game.Population;
import thommynator.game.Racetrack;

Expand All @@ -24,20 +24,38 @@ public App() {

public static void main(String[] args) {
System.setProperty(org.slf4j.impl.SimpleLogger.DEFAULT_LOG_LEVEL_KEY, "DEBUG");

EventQueue.invokeLater(() -> {
App app = new App();
app.setVisible(true);
});
App app = new App();
}


private void initUI() {
add(new Drawer(new Population(200)));
setSize(MAP_WIDTH + 10, MAP_HEIGHT + 30); // some buffer to make everything visible
setBackground(Racetrack.BACKGROUND_COLOR);
setResizable(false);
setTitle("ML-Race");

// add a menu at the top
JMenuBar menuBar = new JMenuBar();
JMenu raceMenu = new JMenu("Race");
raceMenu.add(new JMenuItem("Start"));
raceMenu.add(new JMenuItem("Stop"));
menuBar.add(raceMenu);
JMenu neuralNetMenu = new JMenu("Neural Net");
neuralNetMenu.add(new JMenuItem("Save best..."));
neuralNetMenu.add(new JMenuItem("Load best..."));
menuBar.add(neuralNetMenu);
setJMenuBar(menuBar);

// add the content panel
JPanel contentPanel = new JPanel();
contentPanel.setLayout(null);
contentPanel.add(new GameCanvas(new Population(200)));
contentPanel.add(new Racetrack());

setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setContentPane(contentPanel);
setPreferredSize(new Dimension(App.MAP_WIDTH, App.MAP_HEIGHT));
setBounds(new Rectangle(App.MAP_WIDTH, App.MAP_HEIGHT));
setVisible(true);

// close window
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,65 @@
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;

public class Drawer extends JPanel implements Runnable {
public class GameCanvas extends JPanel implements Runnable {

private static final int DELAY = 20; // time in ms between each frame
private static final long MAX_EPOCH_TIME = 15000; // hard reset after this amount of milliseconds
private Population population;
private transient Population population;
private boolean showCarIds;

public Drawer(Population population) {
public GameCanvas(Population population) {
setSize(App.MAP_WIDTH, App.MAP_HEIGHT);
setOpaque(false);
setVisible(true);
this.population = population;
this.showCarIds = false;
}

@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
population.getCars().forEach(car -> drawCar(g, car, showCarIds));
}

private void drawCar(Graphics g, Car car, boolean showId) {
Graphics2D graphics = (Graphics2D) g;
RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
rh.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
graphics.setRenderingHints(rh);

int carWidth = car.getWidth();
int carLength = car.getLength();

Rectangle rect = new Rectangle(0, 0, carLength, carWidth);
Ellipse2D scanner = new Ellipse2D.Double((int) (0.75 * car.getLength()), 3, 4, 4);
graphics.setStroke(new BasicStroke(2));
graphics.setColor(car.getColor());

AffineTransform transformToOrigin = AffineTransform.getTranslateInstance(-carLength / 2.0, -carWidth / 2.0);
AffineTransform transformToHeading = AffineTransform.getRotateInstance(car.getHeading());
AffineTransform transformToDestination = AffineTransform.getTranslateInstance(
car.getPosition().getX(), car.getPosition().getY());

// build final transform matrix, order matters
AffineTransform at = new AffineTransform();
at.concatenate(transformToDestination);
at.concatenate(transformToHeading);
at.concatenate(transformToOrigin);

graphics.draw(at.createTransformedShape(rect));
graphics.draw(at.createTransformedShape(scanner));

graphics.setColor(Color.BLUE);
graphics.draw(new Rectangle2D.Double((int) car.getPosition().getX(), (int) car.getPosition().getY(), 1, 1));

if (showId) {
graphics.setColor(Color.BLACK);
graphics.setFont(new Font("Arial", Font.PLAIN, 12));
graphics.drawString(String.valueOf(car.getId()), (int) car.getPosition().getX(), (int) car.getPosition().getY());
}
}

@Override
public void run() {
long epochStartTime = System.currentTimeMillis();
Expand Down Expand Up @@ -62,59 +109,10 @@ public void run() {
}
}

@Override
public void paintComponent(Graphics graphics) {
super.paintComponent(graphics);
drawRacetrack(graphics);
population.getCars().forEach(car -> drawCar(graphics, car, showCarIds));
}

@Override
public void addNotify() {
super.addNotify();
Thread animator = new Thread(this);
animator.start();
}

private void drawCar(Graphics g, Car car, boolean showId) {
Graphics2D graphics = (Graphics2D) g;
RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
rh.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
graphics.setRenderingHints(rh);

int carWidth = car.getWidth();
int carLength = car.getLength();

Rectangle rect = new Rectangle(0, 0, carLength, carWidth);
Ellipse2D scanner = new Ellipse2D.Double((int) (0.75 * car.getLength()), 3, 4, 4);
graphics.setStroke(new BasicStroke(2));
graphics.setColor(car.getColor());

AffineTransform transformToOrigin = AffineTransform.getTranslateInstance(-carLength / 2.0, -carWidth / 2.0);
AffineTransform transformToHeading = AffineTransform.getRotateInstance(car.getHeading());
AffineTransform transformToDestination = AffineTransform.getTranslateInstance(
car.getPosition().getX(), car.getPosition().getY());

// build final transform matrix, order matters
AffineTransform at = new AffineTransform();
at.concatenate(transformToDestination);
at.concatenate(transformToHeading);
at.concatenate(transformToOrigin);

graphics.draw(at.createTransformedShape(rect));
graphics.draw(at.createTransformedShape(scanner));

graphics.setColor(Color.BLUE);
graphics.draw(new Rectangle2D.Double((int) car.getPosition().getX(), (int) car.getPosition().getY(), 1, 1));

if (showId) {
graphics.setColor(Color.BLACK);
graphics.setFont(new Font("Arial", Font.PLAIN, 12));
graphics.drawString(String.valueOf(car.getId()), (int) car.getPosition().getX(), (int) car.getPosition().getY());
}
}

private void drawRacetrack(Graphics g) {
g.drawImage(App.RACETRACK.image, 0, 0, null);
}
}
}
37 changes: 32 additions & 5 deletions src/main/java/thommynator/game/Racetrack.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,32 @@
import lombok.Data;
import thommynator.App;

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.util.Arrays;
import java.util.List;

@Data
public class Racetrack {
public class Racetrack extends JPanel implements MouseMotionListener {

public static final Color FOREGROUND_COLOR = new Color(100, 100, 100);
public static final Color BACKGROUND_COLOR = new Color(200, 200, 200);

int roadWidth;
public static final int ROAD_WIDTH = 100;
List<Point2D> anchorPoints;
BufferedImage image;

public Racetrack() {
roadWidth = 100;
addMouseMotionListener(this);
setBackground(BACKGROUND_COLOR);
setSize(App.MAP_WIDTH, App.MAP_HEIGHT);
setOpaque(true);

anchorPoints = Arrays.asList(
new Point2D.Double(0.0, 0.0),
new Point2D.Double(0.7 * App.MAP_WIDTH, 0.2 * App.MAP_HEIGHT),
Expand All @@ -30,17 +37,37 @@ public Racetrack() {
image = createTrack();
}

public BufferedImage createTrack() {
private BufferedImage createTrack() {
BufferedImage img = new BufferedImage(App.MAP_WIDTH, App.MAP_HEIGHT, BufferedImage.TYPE_3BYTE_BGR);
Graphics2D graphics = img.createGraphics();
graphics.setColor(BACKGROUND_COLOR);
graphics.fillRect(0, 0, img.getWidth(), img.getHeight());
graphics.setColor(FOREGROUND_COLOR);
graphics.setStroke(new BasicStroke(roadWidth));
graphics.setStroke(new BasicStroke(ROAD_WIDTH));
for (int i = 0; i < anchorPoints.size() - 1; i++) {
graphics.draw(new Line2D.Double(anchorPoints.get(i), anchorPoints.get(i + 1)));
}
return img;
}

@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, null);
}

@Override
public void mouseDragged(MouseEvent e) {
Graphics graphics = super.getGraphics();
graphics.setColor(FOREGROUND_COLOR);
int w = ROAD_WIDTH;
int x = e.getX() - (int) (w / 2.0);
int y = e.getY() - (int) (w / 2.0);
graphics.fillOval(x, y, w, w);
}

@Override
public void mouseMoved(MouseEvent e) {
// do nothing
}
}
Binary file modified src/main/resources/demo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.