I apologize for the somewhat vague title, I'm unsure what you would call this puzzle.
I'm making a path finding method to find the route with the least moves, not the distance traveled.
The rules of the game are simple, you must traverse from the orange square to the green square, but you can only move in a straight line, and cannot stop moving in that direction until you hit a boundary (either the wall of the arena or an obstacle), as if they were sliding across ice.
Example map, and unless I'm mistaken, the desired path (8 moves)
Arena.java: https://gist.github.com/CalebWhiting/3a6680d40610829b1b6d
ArenaTest.java: https://gist.github.com/CalebWhiting/9a4767508831ea5dc0da
I'm assuming this would be best handled with a Dijkstras or A* path finding algorithm, however I'm not only not very experienced with these algorithms, but also don't know how I would go about defining the path rules.
Thank you for any help in advance.
Here's my solution (Java) in case someone is still interested. As #tobias_k suggested in his comment above, indeed BFS is the way to go:
import java.util.LinkedList;
public class PokemonIceCave {
public static void main(String[] args) {
int[][] iceCave1 = {
{0, 0, 0, 1, 0},
{0, 0, 0, 0, 1},
{0, 1, 1, 0, 0},
{0, 1, 0, 0, 1},
{0, 0, 0, 1, 0}
};
System.out.println(solve(iceCave1, 0, 0, 2, 4));
System.out.println();
int[][] iceCave2 = {
{0, 0, 0, 1, 0},
{0, 0, 0, 0, 1},
{0, 1, 1, 0, 0},
{0, 1, 0, 0, 1},
{0, 0, 0, 1, 0},
{0, 0, 0, 0, 0}
};
System.out.println(solve(iceCave2, 0, 0, 2, 5));
}
public static int solve(int[][] iceCave, int startX, int startY, int endX, int endY) {
Point startPoint = new Point(startX, startY);
LinkedList<Point> queue = new LinkedList<>();
Point[][] iceCaveColors = new Point[iceCave.length][iceCave[0].length];
queue.addLast(new Point(0, 0));
iceCaveColors[startY][startX] = startPoint;
while (queue.size() != 0) {
Point currPos = queue.pollFirst();
System.out.println(currPos);
// traverse adjacent nodes while sliding on the ice
for (Direction dir : Direction.values()) {
Point nextPos = move(iceCave, iceCaveColors, currPos, dir);
System.out.println("\t" + nextPos);
if (nextPos != null) {
queue.addLast(nextPos);
iceCaveColors[nextPos.getY()][nextPos.getX()] = new Point(currPos.getX(), currPos.getY());
if (nextPos.getY() == endY && nextPos.getX() == endX) {
// we found the end point
Point tmp = currPos; // if we start from nextPos we will count one too many edges
int count = 0;
while (tmp != startPoint) {
count++;
tmp = iceCaveColors[tmp.getY()][tmp.getX()];
}
return count;
}
}
}
System.out.println();
}
return -1;
}
public static Point move(int[][] iceCave, Point[][] iceCaveColors, Point currPos, Direction dir) {
int x = currPos.getX();
int y = currPos.getY();
int diffX = (dir == Direction.LEFT ? -1 : (dir == Direction.RIGHT ? 1 : 0));
int diffY = (dir == Direction.UP ? -1 : (dir == Direction.DOWN ? 1 : 0));
int i = 1;
while (x + i * diffX >= 0
&& x + i * diffX < iceCave[0].length
&& y + i * diffY >= 0
&& y + i * diffY < iceCave.length
&& iceCave[y + i * diffY][x + i * diffX] != 1) {
i++;
}
i--; // reverse the last step
if (iceCaveColors[y + i * diffY][x + i * diffX] != null) {
// we've already seen this point
return null;
}
return new Point(x + i * diffX, y + i * diffY);
}
public static class Point {
int x;
int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
#Override
public String toString() {
return "Point{" +
"x=" + x +
", y=" + y +
'}';
}
}
public enum Direction {
LEFT,
RIGHT,
UP,
DOWN
}
}
I think the best solution would probably be the BFS, where you represent the state of the board with a "State" object with the following parameters: number of moves made so far, and coordinates. It should also have a method to find the next states attainable (which should be fairly easy to code, just go N, S, E, W and return an array of the first blocking spots).
Create initial state (0 moves with initial coordinates)
Put in a priority queue (sorting by number moves)
while(priority queue has more states):
Remove node
if it is a goal state:
return the state
Find all neighbors of current state
Add them to priority queue (remembering to increment number of moves by 1)
This uses an implicit graph representation. Optimality is guaranteed because of the priority queue; when the goal state is found, it will have been reached with the fewest moves. If the whole priority queue is exhausted and no state is returned, then no solution exists. This solution takes O(V^2logV) time because of the priority queue, but I think this is the simplest to code. A straight up O(V) BFS solution is possible but you'll have to keep track of what states you have or have not visited yet and the fewest number of moves to reach them, which would take O(V) memory.
Related
Quick question about Java 2D arrays; For my tile-based, top-down, 2D game (using swing) I use
a 2D array to create a map, like this
public int[][] createMap(){
return new int[][]{
{0, 0, 1, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}};
}
I then use this in my gameComponents class where I draw the individual tiles unto the map, like this
protected void paintComponent(Graphics g){
super.paintComponent(g);
for (int row = 0; row < game.getMap().getWidth(); row++) {
for (int col = 0; col < game.getMap().getHeight(); col++) {
g.drawImage(tile.getTileImage().get(values()[game.getMap().getMapArray()[col][row]]), row * SIZE,
col * SIZE, this);
}
} }
(where size is the size of a tile)
This works, and it correctly draws each tile to the map as expected, however
this also causes a problem for collision detection. As you may have noted, while I do define the size between the tiles in draw method, it is not defined in the array at all. Which, as you'd imagine, raises issues when checking for collision as the drawn tile is not where the tile is in the 2D array (due to size offset).
This is the code I use for checking collision (of course, not working due to ArrayIndexOutofbounds).
public boolean collisionDetected(int xDirection, int yDirection, Game game, Player player){
for (int row = 0; row < game.getMap().getHeight() * 16; row ++){
for (int col = 0; col < game.getMap().getWidth() * 16; col++) {
System.out.println(col + xDirection + player.getPositionX());
if(game.getMap().getTile(col + xDirection + player.getPositionX() ,
row + yDirection + player.getPositionY()) == Tiles.GRASS ){
System.out.println("COLLISION DETECTED");
return true;
}
}
}
return false;
}
This method uses a method within the map class that returns the tile on that
specific coordinate, like this
public Tiles getTile(int col,int row){
return Tiles.values()[mapArray[col][row]];
}
And, of course, as the 2D array doesn't know of the size offset, it just throws
an arrayindexoutofbound.
My question is, is it possible to define a 2D map array with the size of a tile in-mind? I appreciate any help & input I can get, after-all I am here to learn!
Extra clarification: All the tiles are in an enum class (i.e AIR, GRASS, STONE...). Also worth noting that the player position is not bound by an array, I merely move it the amount of pixels I want it to move.
Thanks in advance!
This method uses a method within the map class that returns the tile on that specific coordinate, like this
public Tiles getTile(int col,int row){
return Tiles.values()[mapArray[col][row]];
}
So if you have a "coordinate", why do you call the parameters col/row?
If you have a 10x10 grid and each tile is 20 pixels then the grid size is 200x200 so you could have x/y values in the range 0-199
So if you have a coordinate of 25x35 you would simply calculate the row/col values as:
int row = 35 / 20;
int column = 25 / 20;
So your method would be something like :
public Tiles getTile(int x, int y)
{
int row = y / 20;
int column = x / 20;
return Tiles.values()[mapArray[row][column]];
}
I want to write a recursive algorithm that highlights all possible nodes on base of dice value .
How can i do this ? Shouldn't move from null nodes .
In the Image you can see , my current node is blue and for example when the dice value is 4 I want to highlight red places. I wrote a code like this but doesn't work Thanks in advance
f(node n, dice d){
if(d == 0)
n.setDest();
if(Up node != null)
f(Up node , d-1);
if(Down node !=null)
f(Down node, d-1);
if(Right node != null)
f(Right node,d-1);
if(Left node != null)
f(Left node,d-1);
}
Here's a non-recursive solution
public class Move {
private List<Node> steps;
private int stepsRemaining;
private Node lastStep;
public Move(List<Node> steps, int stepsRemaining) {
this.steps = steps;
this.stepsRemaining = stepsRemaining;
this.lastStep = steps.get(steps.size() - 1);
}
// getters and setters
}
public List<Node> getOptions(Node node, int steps) {
LinkedList<Move> stack = new LinkedList<Move>();
stack.addFirst(new Move(Arrays.asList(node), steps);
List<Node> options = new ArrayList<Node>();
while (!stack.isEmpty()) {
Move currentMove = stack.removeFirst();
Node lastStep = currentMove.lastStep;
Node[] childNodes = new Node[] { lastStep.up, lastStep.down, lastStep.left, lastStep.right };
for (Node childNode : childNodes) {
// make sure we don't go back on ourselves
if (childNode != null && !currentMove.steps.contains(childNode)) {
if (currentMove.stepsRemaining == 1) {
options.add(childNode);
continue;
}
List<Node> childSteps = new ArrayList<Node>(currentNode.steps);
childSteps.add(childNode);
stack.addFirst(new Move(childSteps, currentMove.stepsRemaining - 1));
}
}
}
return options;
}
Add a visited boolean to your Node class. Then in each if test if the node has been visited yet.
if(Up node != null && node.visited != true)
f(Up node, d - 1)
don't forget to reset each time you launch your dice.
Maybe this die drawing code will help.
private void drawSpots(Graphics g, int w, int h, int count) {
g.setColor(Color.BLACK);
switch (count) {
case 1:
drawSpot(g, w / 2, h / 2);
break;
case 3:
drawSpot(g, w / 2, h / 2);
// Fall thru to next case
case 2:
drawSpot(g, w / 4, h / 4);
drawSpot(g, 3 * w / 4, 3 * h / 4);
break;
case 5:
drawSpot(g, w / 2, h / 2);
// Fall thru to next case
case 4:
drawSpot(g, w / 4, h / 4);
drawSpot(g, 3 * w / 4, 3 * h / 4);
drawSpot(g, 3 * w / 4, h / 4);
drawSpot(g, w / 4, 3 * h / 4);
break;
case 6:
drawSpot(g, w / 4, h / 4);
drawSpot(g, 3 * w / 4, 3 * h / 4);
drawSpot(g, 3 * w / 4, h / 4);
drawSpot(g, w / 4, 3 * h / 4);
drawSpot(g, w / 4, h / 2);
drawSpot(g, 3 * w / 4, h / 2);
break;
}
}
As you can see, drawing 1 and 6 are separate nodes. Drawing 3 is a special case of drawing 2. Drawing 5 is a special case of drawing 4.
It might be possible to arrange the die numbers differently. Drawing 1, 3, and 5 are similar. Drawing 2, 4, and 6 are similar as well.
I'm not going to write any code here, just explain the algorithm.
First of all, you don't need a recursive algorithm to solve this problem. You could, of course, implement it in a recursive way, but it would bring no advantages.
That being said, in order to solve the problem you just need to keep track of the intersection node (in case of just one intersection). After that you just need to calculate the distance between the current node - the blue one - and the intersection node, let's call it d. Whenever you roll the dice and have a number - let's call it c - you can calculate the position of the red nodes in O(1) time. In fact you have a distance of d + c for the branch where the current node is and c - d for the other branches. The only exception is when d > c in which case you have only 2 red nodes on the current node's branch and they are in positions d - c and d + c from the intersection node.
Pseudo Code
The following pseudo code considers only a situation as the one shown in the image (1 intersection, 2 directions from non intersection nodes, 2+ directions for intersection nodes). Keep in mind that the code is not optimized, it's just shown to illustrate the idea.
// currentNode - the node where you start from
// c - the number given by the die
GetNeighbouringNodes(Node currentNode, int c)
{
Node intersectionNode = null;
Direction intersectionNodeDirection = Direction.None;
if(currentNode.numberOfDirections > 2)
{
intersectionNode = currentNode;
}
else
{
intersectionNodeDirection = Direction.Up;
// the implementation of the getIntersectionNode is not included intentionally
intersectionNode = getIntersectionNode(currentNode, intersectionNodeDirection);
if(intersectionNode == null)
{
intersectionNodeDirection = Direction.Down;
intersectionNode = getIntersectionNode(currentNode, intersectionNodeDirection);
}
// check in other directions also
}
// at this point we have the intersection node
int d = getDistance(currentNode, intersectionNode);
List<Node> resultingNodes = new List<Node>();
if(d > c)
{
resultingNodes.add(getDistantNode(from: intersectionNode, distance: d - c, direction: inverseDirectionOf(intersectionNodeDirection)));
resultingNodes.add(getDistantNode(from: intersectionNode, distance: d + c, direction: inverseDirectionOf(intersectionNodeDirection)));
}
else
{
resultingNodes.add(getDistantNode(from: intersectionNode, distance: d + c, direction: inverseDirectionOf(intersectionNodeDirection)));
foreach(otherDirection)
{
resultingNodes.add(getDistantNode(from: intersectionNode, distance: c - d, direction: otherDirection)));
}
}
return resultingNodes;
}
Let me know if anything is unclear.
I've been trying to implement an A* algorithm in a tower defense style game and after hours of trying I thought I'd ask for some insight in my logic flaws.
I've been trying to create it via http://web.mit.edu/eranki/www/tutorials/search/ & https://stackoverflow.com/a/5602061/1550619
But and infinite problem occurs when trying to generate the successors/neighbors to an AStarNode(I think) - First time implementing A* and still learning.
private ArrayList<AStarNode> aStaring(Object o, AStarNode start, AStarNode goal) {
ArrayList<AStarNode> closed = new ArrayList();
ArrayList<AStarNode> open = new ArrayList();
start.g = 0;
start.h = estimateDistance(start, goal);
start.f = 0;
open.add(start);
while(!open.isEmpty()){
AStarNode q = null;
for(AStarNode asn : open){
if(q == null || asn.f < q.f){
q = asn;
}
}
open.remove(q);
closed.add(q);
for(AStarNode succesor : q.generateSuccesors()){
if(closed.contains(succesor)){
System.out.println("Closed contained succesor");
//TODO Check if walkable
}else{
if(!open.contains(succesor)){
succesor.g = q.g+1;
succesor.h = estimateDistance(succesor, goal);
succesor.f = succesor.g + succesor.h;
succesor.parent = q;
open.add(succesor);
}else{
float nextG = q.g + succesor.cost;
if(nextG < succesor.g){
open.remove(succesor);
closed.add(succesor);
}
}
if(succesor.x == goal.x && succesor.y == goal.y){ //path found
System.out.println("hurray");
return reconstructPath(succesor);
}
}
}
}
return null;
}
public class AStarNode {
private MapDimension md = new MapDimension();
public AStarNode parent;
public int x,y;
public int f,g,h;
public int cost = 1;
public AStarNode(int x, int y){
this.x = x;
this.y = y;
}
//Looking up 4 neighbors and adding to node;
public ArrayList<AStarNode> generateSuccesors(){
ArrayList<AStarNode> neighbors = new ArrayList<>();
if(x+1 < md.getWidth()){
AStarNode temp = new AStarNode(x+1,y);
temp.parent = this;
neighbors.add(temp);
}
if(x-1 > 0){
AStarNode temp = new AStarNode(x-1,y);
temp.parent = this;
neighbors.add(temp);
}
if(y+1 < md.getHeight()){
AStarNode temp = new AStarNode(x,y+1);
temp.parent = this;
neighbors.add(temp);
}
if(y-1 > 0){
AStarNode temp = new AStarNode(x,y-1);
temp.parent = this;
neighbors.add(temp);
}
return neighbors;
}
}
Map:
public static final int[][] MAP = {
{1, 1, 1, 1, 2, 2},
{1, 1, 1, 0, 0, 2},
{2, 0, 1, 0, 0, 0},
{2, 0, 1, 0, 1, 1},
{2, 2, 1, 1, 1, 1},
{2, 2, 0, 0, 0, 1},
{2, 1, 1, 1, 1, 1},
{0, 1, 0, 0, 2, 2},
{2, 1, 0, 2, 2, 2},
{0, 1, 0, 0, 2, 2},
};
Any pointers towards the right direction would be fantastic :]
Everytime you run generateSuccessors, you create 4 (or less) NEW instances of AStarNode objects. This is not a problem per se, but AStarNode does not define hashCode and equals. So two nodes with the same coordinates are not considered equals, so closed.contains(successor) will NEVER return true.
Implement hashCode and equals on AStarNode (or get your IDE to do it for you). Something as simple as:
public int hashCode(){
return x*y;
}
public boolean equals(Object o){
if (o instanceof AStarNode){
return x==((AStarNode)o).x && y==((AStarNode)o).y;
}
return false;
}
So I am making a simple game using LibGDX which involes a 150*150 hexagonal map, and various types of cell, rocky, clear etc.
Problem is when i load the map, my computer almost completely freezes up and any movement thats supposed to be fluid (character moving, button highlights) take 5+ seconds longer than they should.
Here's the relevant code:
public void render(float deltY){
Gdx.gl.glClearColor(255, 255, 255, 100);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
stage.act();
polygonSpriteBatch.begin();
for (int j = 0; j < 150; j++) {
for (int i = 0; i < 150; i++) {
offset = i%2 == 0 ? multipleX/2 : 0;
if (mc.getMap().getRow(i).getTile(j).getTileType().equals(TileType.Rocky)) {
drawCell(Color.BLACK, j, i);}
if (mc.getMap().getRow(i).getTile(j).getTileType().equals(TileType.Clear)) {
drawCell(Color.LIGHT_GRAY, j, i);}
}
}
polygonSpriteBatch.end();
stage.draw();
}
private void drawCell(Color color, int x, int y) {
polySprite = new PolygonSprite(makePoints(color));
polySprite.setX(mc.getMap().getRow(y).getTile(x).getTilePosition().get_x() * multipleX + offset);
polySprite.setY(mc.getMap().getRow(y).getTile(x).getTilePosition().get_y() * multipleY);
polySprite.draw(polygonSpriteBatch);
}
public PolygonRegion makePoints(Color color){
side = 5;
h = CalculateH(side);
r = CalculateR(side);
multipleX = (float)Math.sqrt(3)*side;
multipleY = side+(side/2);
float[] points = { // vertices
x, y,
x+r, y+h,
x+r, y+side+h,
x,y+side+h+h,
x-r, y+side+h,
x-r, y+h};
return new PolygonRegion(new TextureRegion(getTexture(color)),points
, new short[] { //4 triangles using vertices to make hexagon
0, 1, 5,
1, 4, 2,
5, 1, 4,
2, 3, 4});
}
public Texture getTexture(Color color){
Pixmap pix = new Pixmap(1, 1, Pixmap.Format.RGBA8888);
pix.setColor(color);
pix.fill();
textureSolid = new Texture(pix);
return textureSolid;
}
I'm new to coding and LibGDX so there's probably something stupid i'm doing. Is there any way to render the map once and only redraw the polygons if they change?
Thanks
Looking at your code, you are computing a square root for each cell, for each rendering pass.
So your code currently involves more than 22500 square root operations for each frame you render and is creating as many objects, that's quite a lot !
You should compute the points for your hexagons only once.
How long does it last to solve the knights tour problem with backtracking on an 8x8 board? Because my algorithm already computes somehow too long and it seems, like it wont finish. But when I try a 6x6, or 5x5 board, it finishes successfully.
the code:
class KnightsTour{
private boolean[][] board;
private int count, places;
private static final Point[] moves = new Point[]{
new Point(-2, -1),
new Point(-2, 1),
new Point(2, -1),
new Point(2, 1),
new Point(-1, -2),
new Point(-1, 2),
new Point(1, -2),
new Point(1, 2)
};
public KnightsTour(int n) {
board = new boolean[n][n];
places = n*n;
count = 0;
}
public boolean ride(int x, int y) {
board[x][y] = true;
count++;
if (count == places) {
return true;
}
for (Point p : moves) {
int nextX = x + p.x;
int nextY = y + p.y;
if (nextX < 0 || nextX >= board.length || nextY < 0 || nextY >= board.length || board[nextX][nextY]) {
continue;
}
if (ride(nextX, nextY)) {
return true;
}
}
board[x][y] = false;
count--;
return false;
}
}
I came across the same problem. Everything runs smoothly till n=7 and suddenly it takes forever to calculate for n=8. I hope this helps someone :)
The problem lies with the order in which you are checking for the moves. You are using :
xMove[8] = { -2, -2, 2, 2, -1, -1, 1, 1}
yMove[8] = { -1, 1, -1, 1, -2, 2, -2, 2}
If you plot these vectors in the 2D plane, they are haphazardly placed. In other words, they are not ordered in either a clockwise or an anti-clockwise manner. Consider this instead :
xMove[8] = { 2, 1, -1, -2, -2, -1, 1, 2 }
yMove[8] = { 1, 2, 2, 1, -1, -2, -2, -1 }
If you plot these vectors, they are neatly arranged in an anticlockwise circle.
Somehow this causes the recursion to run much quickly for large values of n. Mind you, it still takes forever to calculate for n=9 onwards.