Improved visualization. Unreachable blocks are now marked.

This commit is contained in:
t-moe
2016-06-24 22:14:29 +02:00
parent 2486b75755
commit ef0cdb4764
4 changed files with 100 additions and 27 deletions

View File

@@ -5,6 +5,7 @@ import java.awt.Graphics;
import java.awt.Point; import java.awt.Point;
import java.awt.event.MouseAdapter; import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.swing.JPanel; import javax.swing.JPanel;
@@ -36,6 +37,7 @@ public class FieldCanvas extends JPanel{
private Point src; private Point src;
private Point dst; private Point dst;
private List<Point> path; private List<Point> path;
private List<Point> blockedFields;
private boolean freeMoveMode = false; private boolean freeMoveMode = false;
/** /**
@@ -55,6 +57,12 @@ public class FieldCanvas extends JPanel{
src = null; src = null;
} else { } else {
src = p; src = p;
if(freeMoveMode) {
blockedFields = game.getReachablePoints(src);
} else {
blockedFields = game.getUnreachablePoints(src);
}
repaint();
} }
} }
@@ -64,8 +72,18 @@ public class FieldCanvas extends JPanel{
if(src!=null) { if(src!=null) {
Point lastDst = dst; Point lastDst = dst;
dst = FieldCanvas.this.getClickPoint(e.getPoint()); dst = FieldCanvas.this.getClickPoint(e.getPoint());
if(lastDst!=dst) { //hovered field changed if(lastDst!=dst && dst!=null) { //hovered field changed
if(freeMoveMode) {
if(!game.canMove(src, dst) && game.getField()[dst.x][dst.y]==0) {
path = new ArrayList<Point>();
path.add(src);
path.add(dst);
} else {
path= null;
}
} else {
path= game.getPath(src, dst); path= game.getPath(src, dst);
}
repaint(); repaint();
} }
}else { }else {
@@ -79,8 +97,10 @@ public class FieldCanvas extends JPanel{
super.mouseReleased(e); super.mouseReleased(e);
dst = FieldCanvas.this.getClickPoint(e.getPoint()); dst = FieldCanvas.this.getClickPoint(e.getPoint());
path = null; path = null;
if(freeMoveMode && !game.canMove(src, dst)) { if(freeMoveMode) {
if(!game.canMove(src, dst)) {
game.doFreeMove(src, dst); game.doFreeMove(src, dst);
}
} else { } else {
if(dst != null && src!=null && !src.equals(dst)) { if(dst != null && src!=null && !src.equals(dst)) {
System.out.println("Moving from "+src.toString()+ " to "+dst.toString()); System.out.println("Moving from "+src.toString()+ " to "+dst.toString());
@@ -89,6 +109,7 @@ public class FieldCanvas extends JPanel{
} }
freeMoveMode = false; freeMoveMode = false;
src = null; src = null;
blockedFields = null;
repaint(); repaint();
} }
}; };
@@ -104,10 +125,13 @@ public class FieldCanvas extends JPanel{
* *
* @author timo * @author timo
*/ */
public void doFreeMove() { public void toggleFreeMove() {
if(game.getAvailFreeMoves()>0) { if(freeMoveMode) {
freeMoveMode =false;
} else if(game.getAvailFreeMoves()>0) {
freeMoveMode = true; freeMoveMode = true;
} }
repaint();
} }
/** /**
@@ -146,7 +170,12 @@ public class FieldCanvas extends JPanel{
public void paintComponent(Graphics g) { public void paintComponent(Graphics g) {
super.paintComponent(g); super.paintComponent(g);
//Draw field (background and lines)
if(freeMoveMode) {
g.setColor(Color.gray);
} else {
g.setColor(Color.lightGray); g.setColor(Color.lightGray);
}
g.translate(borderLeft, borderTop); g.translate(borderLeft, borderTop);
int total = Math.min(this.getHeight()-borderTop-borderBottom,FieldCanvas.this.getWidth()-borderLeft-borderRight); int total = Math.min(this.getHeight()-borderTop-borderBottom,FieldCanvas.this.getWidth()-borderLeft-borderRight);
@@ -164,6 +193,8 @@ public class FieldCanvas extends JPanel{
if(game==null) return; if(game==null) return;
//Draw blocks
for(int x=0; x<game.getSize(); x++) { for(int x=0; x<game.getSize(); x++) {
for(int y=0; y<game.getSize(); y++) { for(int y=0; y<game.getSize(); y++) {
int colorCode = game.getField()[x][y]; int colorCode = game.getField()[x][y];
@@ -174,15 +205,24 @@ public class FieldCanvas extends JPanel{
} }
} }
//Draw blocked fields
if(blockedFields!=null) {
g.setColor(Color.darkGray);
for(int i=0; i<blockedFields.size(); i++) {
Point p = blockedFields.get(i);
g.drawLine(p.x*space+1, p.y*space+1, (p.x+1)*space, (p.y+1)*space);
g.drawLine((p.x+1)*space-1, p.y*space+2, p.x*space+1, (p.y+1)*space);
}
}
//Draw Path
if(path!=null && src!=null && dst!=null) { if(path!=null && src!=null && dst!=null) {
int colorCode = game.getField()[src.x][src.y]; int colorCode = game.getField()[src.x][src.y];
Color c = colors[colorCode-1]; Color c = colors[colorCode-1];
int sSpace = space/3; int sSpace = space/3;
int sSpace2 = space/5; int sSpace2 = space/5;
g.setColor(Color.lightGray);
g.fillRect(src.x*space+2, src.y*space+2, space -3, space -3);
g.fillRect(dst.x*space+2, dst.y*space+2, space -3, space -3);
g.setColor(c); g.setColor(c);
g.fillRect(src.x*space+2+sSpace2, src.y*space+2+sSpace2, space -3 - 2* sSpace2, space -3 - 2* sSpace2); g.fillRect(src.x*space+2+sSpace2, src.y*space+2+sSpace2, space -3 - 2* sSpace2, space -3 - 2* sSpace2);

View File

@@ -74,7 +74,7 @@ public class Game {
private int level; //the current level private int level; //the current level
private int linesLeft; //the number of lines left to the next level private int linesLeft; //the number of lines left to the next level
//General stuff
private ArrayList<State> lastStates; //Last Game States. Has one entry per move private ArrayList<State> lastStates; //Last Game States. Has one entry per move
private int size; //size of the field along one dimension private int size; //size of the field along one dimension
private int freeBlocks; //number of free block positions on the field private int freeBlocks; //number of free block positions on the field
@@ -82,7 +82,10 @@ public class Game {
private int numUndos; //number of undos left private int numUndos; //number of undos left
private Random rand; //instance to get random numbers from private Random rand; //instance to get random numbers from
private ArrayList<UpdateListener> updateListeners; //registered listeners private ArrayList<UpdateListener> updateListeners; //registered listeners
private PathFinder pathfinder;
//Stuff for pathfinding
private PathFinder pathfinder; //Path finder helper instance
private Point lastSrc; //last src point that was used with pathfinder
public Game(){ public Game(){
this(7); this(7);
@@ -210,10 +213,30 @@ public class Game {
* @return Shortest path between src and dst, or null if there is no path * @return Shortest path between src and dst, or null if there is no path
*/ */
public List<Point> getPath(final Point src, final Point dst){ public List<Point> getPath(final Point src, final Point dst){
//recalculate costs only if src changed
if(lastSrc==null || !src.equals(lastSrc)) {
pathfinder.calculateCosts(field, size, src); pathfinder.calculateCosts(field, size, src);
}
return pathfinder.getPath(dst); return pathfinder.getPath(dst);
} }
public List<Point> getReachablePoints(final Point src) {
//recalculate costs only if necessary
if(lastSrc==null || !src.equals(lastSrc)) {
pathfinder.calculateCosts(field, size, src);
}
return pathfinder.getReachablePoints();
}
public List<Point> getUnreachablePoints(final Point src) {
//recalculate costs only if necessary
if(lastSrc==null || !src.equals(lastSrc)) {
pathfinder.calculateCosts(field, size, src);
}
return pathfinder.getUnreachablePoints();
}
/** /**
* Undo the last move if there are enough available undos. * Undo the last move if there are enough available undos.
* *
@@ -223,9 +246,12 @@ public class Game {
public boolean doUndo(){ public boolean doUndo(){
if(getAvailUndo() > 0 && lastStates.size() > 0){ if(getAvailUndo() > 0 && lastStates.size() > 0){
lastStates.remove(lastStates.size() - 1).restore(); lastStates.remove(lastStates.size() - 1).restore(); //take last state from the stack and restore it
numUndos--; numUndos--;
//Reset value for pathfinding cache, so that we are sure we recalculate the pathes/costs
lastSrc = null;
emitUpdateEvent(); emitUpdateEvent();
return true; return true;
} }
@@ -234,6 +260,9 @@ public class Game {
private void saveStep() { private void saveStep() {
lastStates.add(new State()); //add a new State Object (which will be initialized to the current game state) to the backup list lastStates.add(new State()); //add a new State Object (which will be initialized to the current game state) to the backup list
//Reset value for pathfinding cache, so that we are sure we recalculate the pathes/costs
lastSrc = null;
} }
/** /**

View File

@@ -114,24 +114,28 @@ public class PathFinder {
* @param src starting point * @param src starting point
*/ */
public void calculateCosts(final int[][] field, int size, final Point src) { public void calculateCosts(final int[][] field, int size, final Point src) {
ArrayList<Vertex> vertices = new ArrayList<Vertex>(); // List of vertices ArrayList<Vertex> openVerticies = new ArrayList<Vertex>(); // List of vertices
vertices.add(new Vertex(0, src)); openVerticies.add(new Vertex(0, src));
// Get a verticies list from the field data // Get a verticies list from the field data
for(int i= 0; i < size; i++){ for(int i= 0; i < size; i++){
for(int j = 0; j < size; j++){ for(int j = 0; j < size; j++){
if(field[i][j] == 0 && (src.x!=i || src.y!=j)){ //field empty and not src if(field[i][j] == 0 && (src.x!=i || src.y!=j)){ //field empty and not src
vertices.add(new Vertex(Integer.MAX_VALUE, new Point(i, j))); openVerticies.add(new Vertex(Integer.MAX_VALUE, new Point(i, j)));
} }
} }
} }
ArrayList<Vertex> allVerticies = new ArrayList<Vertex>(vertices); // List of vertices ArrayList<Vertex> allVerticies = new ArrayList<Vertex>(openVerticies); // List of vertices
while(!vertices.isEmpty()){ // As long as there are vertices while(!openVerticies.isEmpty()){ // As long as there are vertices
final Vertex u = findNearestVertex(vertices); final Vertex u = findNearestVertex(openVerticies);
vertices.remove(u); // Remove u from the set of vertices openVerticies.remove(u); // Remove u from the set of vertices
if(u.getDist()==Integer.MAX_VALUE) {
continue;
}
final Point[] offsets = { final Point[] offsets = {
new Point(0,1), new Point(0,1),
@@ -147,7 +151,7 @@ public class PathFinder {
int y = p.y + offs.y; int y = p.y + offs.y;
if(x<0 || y<0 || x>=size || y>= size) continue; if(x<0 || y<0 || x>=size || y>= size) continue;
final Vertex v = findVertex(x,y, vertices); final Vertex v = findVertex(x,y, openVerticies);
//distanz_update(u,v) //distanz_update(u,v)
if(v!=null){ if(v!=null){
int alternative = u.getDist()+1; //alternative has cost of current path + 1 (=cost to reach neighbor) int alternative = u.getDist()+1; //alternative has cost of current path + 1 (=cost to reach neighbor)
@@ -225,7 +229,7 @@ public class PathFinder {
if(lastSrc.x == x && lastSrc.y ==y) continue; if(lastSrc.x == x && lastSrc.y ==y) continue;
if(lastField[x][y]!=0) continue; if(lastField[x][y]!=0) continue;
Vertex u = findVertex(x, y, verticies); Vertex u = findVertex(x, y, verticies);
if(u==null|| u.getPrev() == null) { if(u==null|| u.getPrev() == null || u.getDist() == Integer.MAX_VALUE) {
res.add(u.getPos()); res.add(u.getPos());
} }
} }

View File

@@ -138,7 +138,7 @@ public class Window extends JFrame implements ActionListener{
if(command.equals("undo")) { if(command.equals("undo")) {
field.doUndo(); field.doUndo();
} else if (command.equals("freemove")) { } else if (command.equals("freemove")) {
field.doFreeMove(); field.toggleFreeMove();
} else { } else {
int size = Integer.parseInt(command); int size = Integer.parseInt(command);
cardLayout.last(mainPanel); cardLayout.last(mainPanel);