refactor
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
package fr.gasser.autosnake;
|
||||
|
||||
import fr.gasser.autosnake.core.AStarSearch;
|
||||
import fr.gasser.autosnake.model.Direction;
|
||||
import fr.gasser.autosnake.model.Snake;
|
||||
import fr.gasser.autosnake.path.AStarSearch;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
package fr.gasser.autosnake;
|
||||
|
||||
import fr.gasser.autosnake.core.AStarSearch;
|
||||
import fr.gasser.autosnake.model.Snake;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Queue;
|
||||
|
||||
public class GamePanel extends JPanel {
|
||||
|
||||
@@ -61,9 +59,6 @@ public class GamePanel extends JPanel {
|
||||
final Rectangle head = model.getHead();
|
||||
if (head != null) g2d.fill(head);
|
||||
|
||||
|
||||
|
||||
|
||||
// Avoid choppy movement when not moving mouse cursor
|
||||
// https://stackoverflow.com/a/26388175
|
||||
Toolkit.getDefaultToolkit().sync();
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
package fr.gasser.autosnake.model;
|
||||
|
||||
import fr.gasser.autosnake.core.AStarSearch;
|
||||
import fr.gasser.autosnake.core.Cell;
|
||||
import fr.gasser.autosnake.core.Grid;
|
||||
import fr.gasser.autosnake.path.AStarSearch;
|
||||
import fr.gasser.autosnake.path.Cell;
|
||||
import fr.gasser.autosnake.path.Grid;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@@ -23,7 +22,7 @@ public class Snake {
|
||||
this.cellSize = cellSize;
|
||||
this.head = new V2d(5 * cellSize, 5 * cellSize);
|
||||
this.length = cellSize;
|
||||
this.trail = new ArrayList<>(); // TODO trim trail
|
||||
this.trail = new ArrayList<>();
|
||||
this.bounds = new V2d(screen.width, screen.height);
|
||||
this.apple = this.randomApple();
|
||||
}
|
||||
@@ -44,8 +43,7 @@ public class Snake {
|
||||
this.speed += 10;
|
||||
}
|
||||
|
||||
trimTrail(); // trim excess trail
|
||||
|
||||
trimTrail(); // trim excess trail.
|
||||
|
||||
// check for collisions with snake body
|
||||
var collisionPoint = checkBodyCollisions();
|
||||
@@ -57,53 +55,26 @@ public class Snake {
|
||||
return false;
|
||||
}
|
||||
|
||||
private Point checkBodyCollisions() {
|
||||
Point headCell = cellOf(head);
|
||||
boolean leftHeadCell = false;
|
||||
for (int i = trail.size() - 1; i >= 0; --i) {
|
||||
var segment = trail.get(i);
|
||||
Point bodyCell = cellOf(segment);
|
||||
// Ignore the contiguous trail portion created while the head
|
||||
// was moving through its current cell.
|
||||
if (!leftHeadCell) {
|
||||
if (bodyCell.equals(headCell)) {
|
||||
private V2d checkBodyCollisions() {
|
||||
boolean leftHead = false;
|
||||
for (V2d segment : sampleTrail()) {
|
||||
double dist = segment.wrap(bounds).wrappedDist(head, bounds);
|
||||
|
||||
// Do not collide the head with itself
|
||||
if (!leftHead) {
|
||||
if (dist < cellSize) {
|
||||
continue;
|
||||
}
|
||||
leftHeadCell = true;
|
||||
leftHead = true;
|
||||
}
|
||||
if (headCell.equals(bodyCell)) {
|
||||
return headCell;
|
||||
|
||||
if (dist < cellSize) {
|
||||
return segment;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private HashSet<Point> getBodyCells() {
|
||||
Point headCell = cellOf(head);
|
||||
HashSet<Point> cells = new HashSet<>();
|
||||
boolean leftHeadCell = false;
|
||||
for (int i = trail.size() - 1; i >= 0; --i) {
|
||||
Point bodyCell = cellOf(trail.get(i));
|
||||
|
||||
// Ignore all historical samples belonging to the head's
|
||||
// current cell. These are the neck/current-cell samples.
|
||||
if (!leftHeadCell) {
|
||||
if (bodyCell.equals(headCell)) {
|
||||
continue;
|
||||
}
|
||||
leftHeadCell = true;
|
||||
}
|
||||
|
||||
cells.add(bodyCell);
|
||||
}
|
||||
return cells;
|
||||
}
|
||||
|
||||
private Point cellOf(V2d pos) {
|
||||
var wrapped = pos.wrap(bounds);
|
||||
return new Point((int) (wrapped.x / cellSize), (int) (wrapped.y / cellSize));
|
||||
}
|
||||
|
||||
private void trimTrail() {
|
||||
int trimPoint;
|
||||
V2d current = head;
|
||||
@@ -132,19 +103,48 @@ public class Snake {
|
||||
var wrappedHead = this.head.wrap(this.bounds);
|
||||
var start = new Point((int) (wrappedHead.x / cellSize), (int) (wrappedHead.y / cellSize));
|
||||
var goal = new Point((int) (this.apple.x / cellSize), (int) (this.apple.y / cellSize));
|
||||
var gridSize = new Dimension((int) (bounds.x / cellSize), (int) (bounds.y / cellSize));
|
||||
var grid = new Grid() {
|
||||
@Override
|
||||
public Dimension getSize() {
|
||||
return new Dimension((int) (bounds.x / cellSize), (int) (bounds.y / cellSize));
|
||||
return gridSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cell get(Point loc) {
|
||||
V2d pos = new V2d(loc.x * cellSize, loc.y * cellSize);
|
||||
if (pos.wrappedDist(wrappedHead, bounds) < cellSize) return () -> true;
|
||||
/*if (pos.wrappedDist(wrappedHead, bounds) < cellSize) return () -> true;
|
||||
var bodyCells = getBodyCells(); // TODO inline trail sampling in here to remove cellOf
|
||||
if (bodyCells.contains(loc)) return () -> true; // body is an obstacle
|
||||
return () -> false;
|
||||
return () -> false;*/
|
||||
return () -> checkCollisions(pos);
|
||||
}
|
||||
|
||||
private boolean checkCollisions(V2d pos) {
|
||||
var trail = sampleTrail();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private HashSet<Point> getBodyCells() {
|
||||
Point headCell = cellOf(head);
|
||||
HashSet<Point> cells = new HashSet<>();
|
||||
boolean leftHeadCell = false;
|
||||
for (int i = trail.size() - 1; i >= 0; --i) {
|
||||
Point bodyCell = cellOf(trail.get(i));
|
||||
|
||||
// Ignore all historical samples belonging to the head's
|
||||
// current cell. These are the neck/current-cell samples.
|
||||
if (!leftHeadCell) {
|
||||
if (bodyCell.equals(headCell)) {
|
||||
continue;
|
||||
}
|
||||
leftHeadCell = true;
|
||||
}
|
||||
|
||||
cells.add(bodyCell);
|
||||
}
|
||||
return cells;
|
||||
}
|
||||
};
|
||||
return new AStarSearch(grid, start, goal, maxIterations);
|
||||
@@ -155,11 +155,15 @@ public class Snake {
|
||||
}
|
||||
|
||||
public Stream<Rectangle> getTrail() {
|
||||
var visibleTrail = new ArrayList<Rectangle>();
|
||||
return this.trail.stream().map(v -> getRect(v.x, v.y));
|
||||
}
|
||||
|
||||
private ArrayList<V2d> sampleTrail() {
|
||||
// Sample points from the trail until the accumulated distance is >= this.length.
|
||||
var visibleTrail = new ArrayList<V2d>();
|
||||
var remaining = this.length;
|
||||
var current = this.head;
|
||||
|
||||
// Sample points from the trail every cellSize until the accumulated distance is >= this.length.
|
||||
for (var i = this.trail.size() - 1; i >= 0 && remaining > 0; --i) {
|
||||
var previous = this.trail.get(i);
|
||||
var dx = previous.x - current.x;
|
||||
@@ -168,20 +172,19 @@ public class Snake {
|
||||
if (d > remaining) {
|
||||
// The tail ends somewhere between current and previous.
|
||||
var ratio = remaining / d;
|
||||
|
||||
var tailX = current.x + dx * ratio;
|
||||
var tailY = current.y + dy * ratio;
|
||||
|
||||
visibleTrail.add(getRect(tailX, tailY));
|
||||
visibleTrail.add(new V2d(tailX, tailY));
|
||||
break;
|
||||
}
|
||||
visibleTrail.add(getRect(previous.x, previous.y));
|
||||
|
||||
visibleTrail.add(previous);
|
||||
|
||||
remaining -= d;
|
||||
current = previous;
|
||||
}
|
||||
|
||||
|
||||
return visibleTrail.stream();
|
||||
return visibleTrail;
|
||||
}
|
||||
|
||||
public Rectangle getHead() {
|
||||
@@ -196,7 +199,12 @@ public class Snake {
|
||||
return new V2d(column * cellSize, row * cellSize);
|
||||
}
|
||||
|
||||
private Rectangle getRect(double x, double y) {
|
||||
private Point cellOf(V2d pos) {
|
||||
var wrapped = pos.wrap(bounds);
|
||||
return new Point((int) (wrapped.x / cellSize), (int) (wrapped.y / cellSize));
|
||||
}
|
||||
|
||||
private Rectangle getRect(double x, double y) {
|
||||
var wrapped = new V2d(x, y).wrap(bounds);
|
||||
return new Rectangle((int) wrapped.x, (int) wrapped.y, cellSize, cellSize);
|
||||
}
|
||||
@@ -205,36 +213,4 @@ private Rectangle getRect(double x, double y) {
|
||||
// mod that works correctly for negative numbers
|
||||
return ((a % b) + b) % b;
|
||||
}
|
||||
|
||||
private static class V2d {
|
||||
private final double x;
|
||||
private final double y;
|
||||
|
||||
public V2d(double x, double y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("x=%s y=%s", x, y);
|
||||
}
|
||||
|
||||
private double wrappedDist(V2d other, V2d bounds) {
|
||||
var dx = Math.abs(this.x - other.x);
|
||||
var dy = Math.abs(this.y - other.y);
|
||||
|
||||
dx = Math.min(dx, bounds.x - dx);
|
||||
dy = Math.min(dy, bounds.y - dy);
|
||||
|
||||
return Math.hypot(dx, dy);
|
||||
}
|
||||
|
||||
private V2d wrap(V2d bounds) {
|
||||
// convert world space coordinates to wrapped coordinates
|
||||
var newX = trueMod(x, bounds.x);
|
||||
var newY = trueMod(y, bounds.y);
|
||||
return new V2d(newX, newY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package fr.gasser.autosnake.model;
|
||||
|
||||
class V2d {
|
||||
final double x;
|
||||
final double y;
|
||||
|
||||
V2d(double x, double y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("x=%s y=%s", x, y);
|
||||
}
|
||||
|
||||
double wrappedDist(V2d other, V2d bounds) {
|
||||
var dx = Math.abs(this.x - other.x);
|
||||
var dy = Math.abs(this.y - other.y);
|
||||
|
||||
dx = Math.min(dx, bounds.x - dx);
|
||||
dy = Math.min(dy, bounds.y - dy);
|
||||
|
||||
return Math.hypot(dx, dy);
|
||||
}
|
||||
|
||||
V2d wrap(V2d bounds) {
|
||||
// convert world space coordinates to wrapped coordinates
|
||||
var newX = trueMod(x, bounds.x);
|
||||
var newY = trueMod(y, bounds.y);
|
||||
return new V2d(newX, newY);
|
||||
}
|
||||
|
||||
private static double trueMod(double a, double b) {
|
||||
// mod that works correctly for negative numbers
|
||||
return ((a % b) + b) % b;
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
package fr.gasser.autosnake.core;
|
||||
package fr.gasser.autosnake.path;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.*;
|
||||
|
||||
public class AStarSearch {
|
||||
private final Grid grid;
|
||||
@@ -1,4 +1,4 @@
|
||||
package fr.gasser.autosnake.core;
|
||||
package fr.gasser.autosnake.path;
|
||||
|
||||
public interface Cell {
|
||||
boolean isObstacle();
|
||||
+1
-2
@@ -1,6 +1,5 @@
|
||||
package fr.gasser.autosnake.core;
|
||||
package fr.gasser.autosnake.path;
|
||||
import java.awt.*;
|
||||
import java.util.Random;
|
||||
|
||||
public enum Direction {
|
||||
UP(0, -1),
|
||||
@@ -1,4 +1,4 @@
|
||||
package fr.gasser.autosnake.core;
|
||||
package fr.gasser.autosnake.path;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
Reference in New Issue
Block a user