Finding solution to maze in java - java

I have a project to have a red dot inside the first box of a maze i randomly generated and the dot is supposed to follow its way through the boxes and find the end of the maze. Now if it hits a dead end, its supposed to go back to where its path started and not go back down that path, that leads to a dead end. i made it so each box represents the #1, this way when the red dot travels over the box, it increments by 1, so it can realize where its been. its always supposed to go to the lowest number possible so it can never go back to the dead ends its already been to. i am able to reach the end of the maze but i come into 2 problems.
the method i wrote that does all this work is the solve() function. I cant understand why 2 things happen...
1st thing is that when the red dot comes to a branch of dead ends, sometimes itll just go to one dead end, to a different dead end , back to the same dead end.. traveling to the same 'numbers' when im trying to have it only go towards the boxes that have 1's or just the lower numbers.
2nd thing is that once it inevitably reaches the end of the maze.. the red dot goes into the green area, where i specifically say in the while loop, it can not be in a green box.
if M[y][x] = 0, its a green box and if its = 1 its a black box. anything higher than 1 will also be inside the box.
your help is highly appreciated as ive been stuck on this problem for hours and cant seem to find out the problem.
the problem persists in the solve() method
import java.awt.*;
import java.awt.event.*;
import java.awt.Graphics;
import javax.swing.*;
public class mazedfs extends JFrame implements KeyListener
{
/* default values: */
private static int bh = 16; // height of a graphical block
private static int bw = 16; // width of a graphical block
private int mh = 41; // height and width of maze
private int mw = 51;
private int ah, aw; // height and width of graphical maze
private int yoff = 40; // init y-cord of maze
private Graphics g;
private int dtime = 40; // 40 ms delay time
byte[][] M; // the array for the maze
public static final int SOUTH = 0;
public static final int EAST = 1;
public static final int NORTH = 2;
public static final int WEST = 3;
public static boolean showvalue = true; // affects drawblock
// args determine block size, maze height, and maze width
public mazedfs(int bh0, int mh0, int mw0)
{
bh = bw = bh0; mh = mh0; mw = mw0;
ah = bh*mh;
aw = bw*mw;
M = new byte[mh][mw]; // initialize maze (all 0's - walls).
this.setBounds(0,0,aw+10,10+ah+yoff);
this.setVisible(true);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
try{Thread.sleep(500);} catch(Exception e) {} // Synch with system
this.addKeyListener(this);
g = getGraphics(); //g.setColor(Color.red);
setup();
}
public void paint(Graphics g) {} // override automatic repaint
public void setup()
{
g.setColor(Color.green);
g.fill3DRect(0,yoff,aw,ah,true); // fill raised rectangle
g.setColor(Color.black);
// showStatus("Generating maze...");
digout(mh-2,mw-2); // start digging!
// digout exit
M[mh-1][mw-2] = M[mh-2][mw-1] = 1;
drawblock(mh-2,mw-1);
solve(); // this is the function you will write for parts 1 and 2
play(); // for part 3
}
public static void main(String[] args)
{
int blocksize = bh, mheight = 41, mwidth = 41; // need to be odd
if (args.length==3)
{
mheight=Integer.parseInt(args[0]);
mwidth=Integer.parseInt(args[1]);
blocksize=Integer.parseInt(args[2]);
}
mazedfs W = new mazedfs(blocksize,mheight,mwidth);
}
public void drawblock(int y, int x)
{
g.setColor(Color.black);
g.fillRect(x*bw,yoff+(y*bh),bw,bh);
g.setColor(Color.yellow);
// following line displays value of M[y][x] in the graphical maze:
if (showvalue)
g.drawString(""+M[y][x],(x*bw)+(bw/2-4),yoff+(y*bh)+(bh/2+6));
}
void drawdot(int y, int x)
{
g.setColor(Color.red);
g.fillOval(x*bw,yoff+(y*bh),bw,bh);
try{Thread.sleep(dtime);} catch(Exception e) {}
}
/////////////////////////////////////////////////////////////////////
/* function to generate random maze */
public void digout(int y, int x)
{
M[y][x] = 1; // digout maze at coordinate y,x
drawblock(y,x); // change graphical display to reflect space dug out
int dir = (int)(Math.random()*4);
for (int i=0;i<4;i++){
int [] DX = {0,0,2,-2};
int [] DY = {-2,2,0,0};
int newx = x + DX[dir];
int newy = y + DY[dir];
if(newx>=0 && newx<mw && newy>=0 && newy<mh && M[newy][newx]==0)
{
M[y+DY[dir]/2][x+DX[dir]/2] = 1;
drawblock(y+DY[dir]/2,x+DX[dir]/2);
digout(newy,newx);
}
dir = (dir + 1)%4;}
} // digout
public void solve() // This is the method i need help with.
{
int x=1, y=1;
drawdot(y,x);
while(y!=mh-1 || x!=mw-1 && M[y][x]!=0){
int min = 0x7fffffff;
int DX = 0;
int DY = 0;
if (y-1>0 && min>M[y-1][x] && M[y-1][x]!=0){
min = M[y-1][x];
DX = 0;
DY = -1;
}//ifNORTH
if (y+1>0 && min>M[y+1][x] && M[y+1][x]!=0){
min = M[y+1][x];
DY = 1;
DX = 0;
}//ifSOUTH
if (x-1>0 && min>M[y][x-1] && M[y][x-1]!=0){
min = M[y][x-1];
DX = -1;
DY = 0;
}//ifWEST
if (x+1>0 && min>M[y][x+1] && M[y][x+1]!=0){
min = M[y][x+1];
DX = 1;
DY = 0;
}//ifEAST
M[y][x]++;
drawblock(y,x);
x = x+DX;
y = y+DY;
drawdot(y,x);
}//while
// modify this function to move the dot to the end of the maze. That
// is, when the dot reaches y==mh-2, x==mw-2
} // solve
///////////////////////////////////////////////////////////////
/// For part three (save a copy of part 2 version first!), you
// need to implement the KeyListener interface.
public void play() // for part 3
{
// code to setup game
}
// for part 3 you may also define some other instance vars outside of
// the play function.
// for KeyListener interface
public void keyReleased(KeyEvent e) {}
public void keyTyped(KeyEvent e) {}
public void keyPressed(KeyEvent e) // change this one
{
int key = e.getKeyCode(); // code for key pressed
System.out.println("YOU JUST PRESSED KEY "+key);
}
} // mazedfs
////////////
// define additional classes (stack) you may need here.

The issue causing the second problem you are facing (dot moving to green box) lies in the while loop conditiony!=mh-1 || x!=mw-1 && M[y][x]!=0 . The condition evaluates to y!=mh-1 ||(x!=mw-1 && M[y][x]!=0) since && has higher precedence over the || and || just needs one of its operand to be true. In your case, y!=mh-1 is still ture at the end of maze. Hence the loop continues and the dot moves into green area. To fix the issue modify the condition as (y!=mh-1 || x!=mw-1) && M[y][x]!=0. Hope this helps.

Related

Java beginning if statement in for loop being skipped while others work

Back again with another problem that has stumped me completely and cannot for the life of me fix.
So I had previously posted a question about my Java game I am making for a class that with the help of #MadProgrammer was fixed...mostly. Now there is a new problem that needs a post all to it's own
Previous Post:
Rows and columns with multidimensional array java
Problem:
In the code below I have it set up to loop through the variables x and y to make rows and columns on a jPanel. Each time it loops through it should randomly mark the "x,y" location with one of the "terrains" so that later it can "paint" that location with the appropriate colored 20x20 square.
The code runs great except for one thing, it looks like it skips the very first "if statement" that marks the "terran[0]" which is "floor". When the code is ran it "paints" the other three "terrains" and not a single "floor" "terrain".
I have looked for a solution on these posts but no success:
Java if statement is skipped
If statement being skipped during execution
Java - for loops being skipped
Java if-statement being skipped
Here is a working piece of code that results in the problem at hand:
import java.awt.*;
import javax.swing.*;
import java.util.*;
public class gamePanel extends JPanel
{
public gamePanel()
{
setBounds(115,93,480,480);
}
private Random generator = new Random();
int floor = 0; //initializes the variable floor to zero for later use
int dirt = 1;
int stone = 2;
int water = 3;
int width = 24;
int height = 24;
int x, y; // my x & y variables for coordinates
int[][] coords = new int[width][height]; //my array that I want to store the coordinates for later use in painting
int[] terrain = {floor, dirt, stone, water}; //my terrain that will determine the color of the paint
public void mapGen() //what should mark/generate the JPanel
{
for(x = 0; x < width; x++)
{
for(y = 0; y < height; y++)
{
int z = generator.nextInt(20);// part of the randomization
if(z <= 10)
{
coords[x][y] = terrain[0]; //should mark the coordinates as floor
}
if(z == 11)
{
coords[x][y] = terrain[3];//should mark the coordinates as water
}
if(z >= 12 && z <= 16)
{
coords[x][y] = terrain[2];//should mark the coordinates as stone
}
if(z >= 17 && z <= 19)
{
coords[x][y] = terrain[1];//should mark the coordinates as dirt
}
coords[0][0] = terrain[0]; // sets coordinate 0,0 to floor //need to have these always be floor
coords[23][23] = terrain[0]; // sets coordinate 24,24 to floor //^^^^^^^^^^
}
}
}
#Override
public void paintComponent(Graphics g)//what will paint each 20x20 square on the grid what it is assigned
{
super.paintComponent(g);
for(int x = 0; x < width; x++)
{
for(int y = 0; y < height; y++)
{
mapGen();
if(coords[x][y] == terrain[floor])//should paint the floor color at marked coordinates
{
g.setColor(Color.white);
g.fillRect((x*20), (y*20), 20, 20);
}
if(coords[x][y] == terrain[dirt]);//should paint the dirt color at marked coordinates
{
g.setColor(new Color(135,102,31));
g.fillRect((x*20), (y*20), 20, 20);
}
if(coords[x][y] == terrain[stone])//should paint the stone color at marked coordinates
{
g.setColor(new Color(196,196,196));
g.fillRect((x*20),(y*20),20,20);
}
if(coords[x][y] == terrain[water])//should paint the water color at marked coordinates
{
g.setColor(new Color(85,199,237));
g.fillRect((x*20),(y*20),20,20);
}
}
}
}//end paintComponent
public static void main(String[] args)
{
gamePanel panel = new gamePanel();
JFrame frame = new JFrame();
frame.setSize(500,550);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(panel);
frame.setVisible(true);
}//end main
}// end gamePanel
Please keep in mind that I am a novice programmer and I am still learning. So anything that is not considered "basic" code please explain in detail.

Error passing array as parameter - Java

A program I'm modifying is supposed to use a drawing panel to randomly move a square, starting from the center, either left or right and use an array to tally the position it moves to while the square stays on screen (the panel is 400 x 400 and the square is 10 x 10, so there are only 40 possible positions it can move to) After the square goes off screen, I have to print a histogram that shows how many times the square moved to that index (i.e if the square moved from the x coordinate of 200 to 190, index 19 would get a tally) Here is my code:
import java.awt.*;
import java.util.*;
public class RandomWalkCountSteps {
// DrawingPanel will have dimensions HEIGHT by WIDTH
public static final int HEIGHT = 100;
public static final int WIDTH = 400;
public static final int CENTER_X = WIDTH / 2;
public static final int CENTER_Y = HEIGHT / 2;
public static final int CURSOR_DIM = 10;
public static final int SLEEP_TIME = 25; // milliseconds
public static void main( String[] args ) {
DrawingPanel panel = new DrawingPanel( WIDTH, HEIGHT );
Random rand = new Random();
walkRandomly( panel, rand );
}
public static void walkRandomly( DrawingPanel panel, Random rand ) {
Graphics g = panel.getGraphics();
int[] positionCounts = new int[ WIDTH / CURSOR_DIM ];
// start in center of panel
int x = CENTER_X;
int y = CENTER_Y;
// Draw the cursor in BLACK
g.fillRect(x, y, CURSOR_DIM, CURSOR_DIM);
// randomly step left, right, up, or down
while ( onScreen( x, y ) ) {
panel.sleep( SLEEP_TIME );
// Show a shadow version of the cursor
g.setColor(Color.GRAY);
g.fillRect(x, y, CURSOR_DIM, CURSOR_DIM);
if ( rand.nextBoolean() ) { // go left
x -= CURSOR_DIM;
}
else { // go right
x += CURSOR_DIM;
}
positionCounts[ x / CURSOR_DIM ]++;
histogram(positionCounts, x, y);
// draw the cursor at its new location
g.setColor(Color.BLACK);
g.fillRect(x, y, CURSOR_DIM, CURSOR_DIM);
}
}
public static boolean onScreen( int x, int y ) {
return 0 <= x && x < WIDTH
&& 0 <= y && y < HEIGHT;
}
public static void histogram(int[] positionCounts, int x, int y) {
if (onScreen(x, y) == false) {
for (int i = 0; i < WIDTH / CURSOR_DIM; i++) {
System.out.print(i + ": ");
for (int j = 1; j <= positionCounts[i]; j++) {
System.out.print("*");
}
System.out.println();
}
}
}
}
My problem was that I couldn't find a good place to initialize the array so that it wouldn't re-initialize every time I passed the x coordinate to the histogram method. Now that I thought I had it in the right place, I get this error message on both calls to histogram in the method walkRandomly "error: method histogram in class RandomWalkCountSteps cannot be applied to given types;" I'm fairly new to java and programming in general, so there's probably something I'm missing regarding arrays as parameters. Thanks in advance.
histogram takes two parameters, positionCounts of type int[] and x of type int. In walkRandomly, you call histogram twice: once with an argument positionCounts of type int[] and once with an argument x of type int. That’s why the compiler complains that the method ”cannot be applied to given types”: the method histogram(int[], int) can’t be applied to (called with) the given types, i.e., histogram(int[]) and histogram(int).
I’m not sure what you’re trying to do with this code, but I’d guess that you want remove the first call and change the second call (inside of the while loop) to histogram(positionCounts, x).
(You’ve edited your code, so my answer doesn’t make much sense.)

Counter loop in Java

I have created a game where the player collects pods on the game board. I need to make an evasive maneuver so that after five turns when the player is one space away from the pod, the pod will transport 10 spaces away from the player. I believe my counter is not working and that is why the evasive maneuver is not working.
public class Pod {
int podPositionX;
int podPositionY;
String podDirection;
int layoutWidth;
int layoutHeight;
boolean podVisable = true;
int playerX;
int playerY;
int count = 0;
public Pod(int x, int y, String direction, int width, int height){
podPositionX = x;
podPositionY = y;
podDirection = direction;
layoutWidth = width;
layoutHeight = height;
count=count+1;
}
//Inspector Methods?
// Will get the the pods positon and will return it
public int getX()
{
return podPositionX;
}
//This method returns the current Y coordinate of the pod.
public int getY()
{
return podPositionY;
}
//This method returns true if the pod is visible, false otherwise. Once the
//pod has been caught, it should always return false.
public boolean isVisible(){
if (playerX == podPositionX && playerY == podPositionY && podVisable == true){
podVisable = false;}
return podVisable;
}
// to move pod diagonally across screen//
public void move(){
//Calling invasive methods!!
while (count>5 && count < 100){
podPositionX=transportX();
podPositionY=transportY();
}
/****************** To make pod move **************/
if (podDirection.equals("NW")|| podDirection.equals("NE")){
podPositionY = podPositionY + 1;}
if (podDirection.equals("SW")|| podDirection.equals("SE")){
podPositionY = podPositionY-1;}
if (podDirection.equals("NE")|| podDirection.equals("SE")){
podPositionX = podPositionX+1;}
if(podDirection.equals("NW")|| podDirection.equals("SW")){
podPositionX = podPositionX-1;}
/****************To make Pod bounce off wall******************/
//make pod bounce off left wall
if (podPositionX <= 1){
if (podDirection.equals("SW")){
podDirection = "SE";}
if (podDirection.equals("NW")){
podDirection = "NE";}
}
//make pod bounce off top
if (podPositionY >= layoutHeight-1){
if (podDirection.equals("NW")){
podDirection = "SW";}
if (podDirection.equals("NE")){
podDirection = "SE";}
}
//make pod bounce off right wall
if (podPositionX >= layoutWidth-1){
if (podDirection.equals("NE")){
podDirection = "NW";}
if (podDirection.equals("SE" )){
podDirection = "SW";}
}
//make pod bounce off bottom wall
if (podPositionY <= 1){
if (podDirection.equals("SW")){
podDirection = "NW";}
if (podDirection.equals("SE")){
podDirection = "NE";}
}
}
// to get player x and y positions
public void playerAt(int x, int y){
playerX = x;
playerY = y;
}
//envasive maneuver so that after 5 turns the pod can be transported away from the player if it is 1 spot away from the player.
//then the count is set to 100 so it will exit the loop and not be able to transport the pod again.
public int transportX(){
if (podPositionX == playerX -1 || podPositionX == playerX +1){
podPositionX= playerX +10;
count = 100;}
return podPositionX;
}
public int transportY(){
if (podPositionY == playerY -1 || podPositionY == playerY +1){
podPositionY= playerY +10;
count=100;}
return podPositionY;
}
}
the code my teacher provided us. Can no be touched so i can not put a counter in this file.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Project1 extends JFrame implements ActionListener {
// Member variables for visual objects.
private JLabel[][] board; // 2D array of labels. Displays either # for player,
// * for pod, or empty space
private JButton northButton, // player presses to move up
southButton, // player presses to move down
eastButton, // player presses to move right
westButton; // player presses to move left
// Current width and height of board (will make static later).
private int width = 15;
private int height = 9;
// Current location of player
private int playerX = 7;
private int playerY = 4;
// Pod object stored in array for efficiency
private Pod[] pods;
int podCount = 4;
public Project1() {
// Construct a panel to put the board on and another for the buttons
JPanel boardPanel = new JPanel(new GridLayout(height, width));
JPanel buttonPanel = new JPanel(new GridLayout(1, 4));
// Use a loop to construct the array of labels, adding each to the
// board panel as it is constructed. Note that we create this in
// "row major" fashion by making the y-coordinate the major
// coordinate. We also make sure that increasing y means going "up"
// by building the rows in revers order.
board = new JLabel[height][width];
for (int y = height-1; y >= 0; y--) {
for (int x = 0; x < width; x++) {
// Construct a label to represent the tile at (x, y)
board[y][x] = new JLabel(" ", JLabel.CENTER);
// Add it to the 2D array of labels representing the visible board
boardPanel.add(board[y][x]);
}
}
// Construct the buttons, register to listen for their events,
// and add them to the button panel
northButton = new JButton("N");
southButton = new JButton("S");
eastButton = new JButton("E");
westButton = new JButton("W");
// Listen for events on each button
northButton.addActionListener(this);
southButton.addActionListener(this);
eastButton.addActionListener(this);
westButton.addActionListener(this);
// Add each to the panel of buttons
buttonPanel.add(northButton);
buttonPanel.add(southButton);
buttonPanel.add(eastButton);
buttonPanel.add(westButton);
// Add everything to a main panel attached to the content pane
JPanel mainPanel = new JPanel(new BorderLayout());
getContentPane().add(mainPanel);
mainPanel.add(boardPanel, BorderLayout.CENTER);
mainPanel.add(buttonPanel, BorderLayout.SOUTH);
// Size the app and make it visible
setSize(300, 200);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Auxiliary method to create game setup
createGame();
}
// Auxiliary method used to create board. Sets player, treasure, and walls.
public void createGame() {
// Construct array of Pod objects
pods = new Pod[podCount];
// Construct each Pod in the array, passing it its initial location,
// direction of movement, and the width and heigh of board. This will
// later be modified to be done at random.
pods[0] = new Pod(1, 5, "NE", width, height);
pods[1] = new Pod(2, 1, "SW", width, height);
pods[2] = new Pod(12, 2, "NW", width, height);
pods[3] = new Pod(13, 6, "SE", width, height);
// Call method to draw board
drawBoard();
}
// Auxiliary method to display player and pods in labels.
public void drawBoard() {
// "Erase" previous board by writing " " in each label
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
board[y][x].setText(" ");
}
}
// Get location of each pod and write * into that label. We only
// do this for pods not yet caught.
for (int p = 0; p < podCount; p++) {
if (pods[p].isVisible()) {
board[pods[p].getY()][pods[p].getX()].setText("*");
}
}
// Write the player onto the board.
board[playerY][playerX].setText("#");
}
public void actionPerformed(ActionEvent e) {
// Determine which button was pressed, and move player in that
// direction (making sure they don't leave the board).
if (e.getSource() == southButton && playerY > 0) {
playerY--;
}
if (e.getSource() == northButton && playerY < height-1) {
playerY++;
}
if (e.getSource() == eastButton && playerX < width-1) {
playerX++;
}
if (e.getSource() == westButton && playerX > 0) {
playerX--;
}
// Move the pods and notify the pods about player location.
for (int p = 0; p < podCount; p++) {
pods[p].move();
pods[p].playerAt(playerX, playerY);
}
// Redraw the board
drawBoard();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
Project1 a = new Project1();
}
}
I am looking at this quickly now. You said the problem is with the count variable. I see that you are incrementing it in the constructor i.e
public Pod(int, int, String, int, int){
count = count + 1;
}
why not put it in the move method. That way every time the move method is used the count variable will be incremented by one.
public void move(){
this.count++;
}
Also it is very hard to see exactly what you think your code is doing. I tried to run it and nothing happens. Sometimes it is good to print something to the console.

Maze, optimal path finding using stacks

i have a program that generates a random maze. In the maze a red dot is displayed and the red dot flashes on by each block in the maze. all the blocks in the maze are == 1 and if the red dot goes through that block, it increments ++. the red dot goes in the direction towards the lowest number, that way it wont stay in an infinite loop by a dead end. once it reaches the end, ive solved the maze.
This is where im stumped, im trying to print the red dot to find the optimal route all the way back to the beginning where it started. I used a stack class that i made to record all the y and x components of where the red dot traveled. im able to traceback every where the red dot has gone but that isnt the optimal solution.
My question is how could i print the red dot tracing back in only the optimal path. My idea of solving this would be to check and see if the coordinates of a stack where visited before, if so..find the last case where it was visited and print the red dot up until that point. that way itll never deal with the dead ends it traveled.
the method solve() is what contains the traceback and solving technique for the red dot to travel through the maze and back.
Im not the greatest programmer and im still learning how to use stacks, ive been stumped for hours and dont know how to approach this. Please be kind and explain how you would do it using the stack i made. Thank you
import java.awt.*;
import java.awt.event.*;
import java.awt.Graphics;
import javax.swing.*;
public class mazedfs extends JFrame implements KeyListener
{
/* default values: */
private static int bh = 16; // height of a graphical block
private static int bw = 16; // width of a graphical block
private int mh = 41; // height and width of maze
private int mw = 51;
private int ah, aw; // height and width of graphical maze
private int yoff = 40; // init y-cord of maze
private Graphics g;
private int dtime = 40; // 40 ms delay time
byte[][] M; // the array for the maze
public static final int SOUTH = 0;
public static final int EAST = 1;
public static final int NORTH = 2;
public static final int WEST = 3;
public static boolean showvalue = true; // affects drawblock
// args determine block size, maze height, and maze width
public mazedfs(int bh0, int mh0, int mw0)
{
bh = bw = bh0; mh = mh0; mw = mw0;
ah = bh*mh;
aw = bw*mw;
M = new byte[mh][mw]; // initialize maze (all 0's - walls).
this.setBounds(0,0,aw+10,10+ah+yoff);
this.setVisible(true);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
try{Thread.sleep(500);} catch(Exception e) {} // Synch with system
this.addKeyListener(this);
g = getGraphics(); //g.setColor(Color.red);
setup();
}
public void paint(Graphics g) {} // override automatic repaint
public void setup()
{
g.setColor(Color.green);
g.fill3DRect(0,yoff,aw,ah,true); // fill raised rectangle
g.setColor(Color.black);
// showStatus("Generating maze...");
digout(mh-2,mw-2); // start digging!
// digout exit
M[mh-1][mw-2] = M[mh-2][mw-1] = 1;
drawblock(mh-2,mw-1);
solve(); // this is the function you will write for parts 1 and 2
play(); // for part 3
}
public static void main(String[] args)
{
int blocksize = bh, mheight = 41, mwidth = 41; // need to be odd
if (args.length==3)
{
mheight=Integer.parseInt(args[0]);
mwidth=Integer.parseInt(args[1]);
blocksize=Integer.parseInt(args[2]);
}
mazedfs W = new mazedfs(blocksize,mheight,mwidth);
}
public void drawblock(int y, int x)
{
g.setColor(Color.black);
g.fillRect(x*bw,yoff+(y*bh),bw,bh);
g.setColor(Color.yellow);
// following line displays value of M[y][x] in the graphical maze:
if (showvalue)
g.drawString(""+M[y][x],(x*bw)+(bw/2-4),yoff+(y*bh)+(bh/2+6));
}
void drawdot(int y, int x)
{
g.setColor(Color.red);
g.fillOval(x*bw,yoff+(y*bh),bw,bh);
try{Thread.sleep(dtime);} catch(Exception e) {}
}
/////////////////////////////////////////////////////////////////////
/* function to generate random maze */
public void digout(int y, int x)
{
M[y][x] = 1; // digout maze at coordinate y,x
drawblock(y,x); // change graphical display to reflect space dug out
int dir = (int)(Math.random()*4);
for (int i=0;i<4;i++){
int [] DX = {0,0,2,-2};
int [] DY = {-2,2,0,0};
int newx = x + DX[dir];
int newy = y + DY[dir];
if(newx>=0 && newx<mw && newy>=0 && newy<mh && M[newy][newx]==0)
{
M[y+DY[dir]/2][x+DX[dir]/2] = 1;
drawblock(y+DY[dir]/2,x+DX[dir]/2);
digout(newy,newx);
}
dir = (dir + 1)%4;}
} // digout
/* Write a routine to solve the maze.
Start at coordinates x=1, y=1, and stop at coordinates
x=mw-1, y=mh-2. This coordinate was especially dug out
after the program called your digout function (in the "actionPerformed"
method).
*/
public void solve()
{
int x=1, y=1;
Stack yourstack = null;
drawdot(y,x);
while(y!=mh-2 || x!=mw-2 && M[y][x]!=0){
int min = 0x7fffffff;
int DX = 0;
int DY = 0;
if (y-1>0 && min>M[y-1][x] && M[y-1][x]!=0){
min = M[y-1][x];
DX = 0;
DY = -1;
}//ifNORTH
if (y+1>0 && min>M[y+1][x] && M[y+1][x]!=0){
min = M[y+1][x];
DY = 1;
DX = 0;
}//ifSOUTH
if (x-1>0 && min>M[y][x-1] && M[y][x-1]!=0){
min = M[y][x-1];
DX = -1;
DY = 0;
}//ifWEST
if (x+1>0 && min>M[y][x+1] && M[y][x+1]!=0){
min = M[y][x+1];
DX = 1;
DY = 0;
}//ifEAST
M[y][x]++;
drawblock(y,x);
x = x+DX;
y = y+DY;
drawdot(y,x);
yourstack = new Stack(y,x,yourstack); // creates new stack for each coordinate travelled
}//while
while(yourstack != null){
yourstack = yourstack.tail;
drawdot(yourstack.y,yourstack.x); // this will traceback every box ive been through
}//while
} // solve
class Stack{
int x;
int y;
Stack tail;
public Stack(int a, int b, Stack t){
y = a;
x=b;
tail=t;
}
}//stackclass
///////////////////////////////////////////////////////////////
/// For part three (save a copy of part 2 version first!), you
// need to implement the KeyListener interface.
public void play() // for part 3
{
// code to setup game
}
// for part 3 you may also define some other instance vars outside of
// the play function.
// for KeyListener interface
public void keyReleased(KeyEvent e) {}
public void keyTyped(KeyEvent e) {}
public void keyPressed(KeyEvent e) // change this one
{
int key = e.getKeyCode(); // code for key pressed
System.out.println("YOU JUST PRESSED KEY "+key);
}
} // mazedfs
////////////
// define additional classes (stack) you may need here.
when you trace your path back you currently just go back to your stack - but thats not the shortest path...
...whenever you can go back check the values of M around you:
byte valueOfFieldNorthOfXY = M[x][y-1]; //x/y is your current position
byte valueOfFieldWesthOfXY = M[x-1][y];
byte ...;
byte ...; //and so on...
while the first while-loop in your solve-methode simplay solves the maze by flooding it the second while-method is for going back...
and when i say flooding i mean: each time a field has been passed by the 'walker' the value of M[x][y] has been increased by 1 (when the 'walker' has walked 3x over field 5/6 then the value from M[5][6] = 3)
so when you go back from the end (#40/50) to the start (#1/1), you do this algorith:
1) i stand on x/y
2) i check the values north/east/south/west of me
2a) if i come from north, then i ignore the north field
2 ) ... and so on...
2d) if i come from west , then i ignore the west field
3) i pick that direction, where the value is the least
4) put the current field int your packPathStack and walk to
the 'best' direction
5) repeat (go back to Nr.1) until you are #1/1
example
? 4 ? //i'm standing at X (x/y)
2 x f //? are values from walls or not yet considerd
? ? ? //f is where i come from
--> i pick direction WEST(2) because thats less than NORTH(4)
implement this algorithm and you a NEW stack i call it yourPathBackStack
Stack yourPathBackStack = new Stack();
while(x != 1 && y != 1 ){ //x/y = 1/1 = start - do it until you are there (Nr. 5)
// 1) i stand on x/y
int x = yourPathBackStack.x;
int y = yourPathBackStack.y;
// 2) i check the values north/east/south/west of me
byte valueOfFieldNorthOfXY = ... ; //as seen above
// 2a) if i come from north, then i ignore the north field
if (yourstack.tail.x == x && yourstack.tail.y == y-1){
//check - i DO come from north
//make then valueOfFieldNorthOfXY very high
valueOfFieldNorthOfXY = 100; //it's the same as ignoring
}
// 2 ) ... and so on...
// 2d) if i come from west , then i ignore the west field
if (yourstack.tail.x == x-1 && ...// as seen above
// 3) i pick that direction, where the value is the least
int direction = NORTH; //lets simply start with north;
byte maxValue = 100;
if ( valueOfFieldNorthOfXY < maxValue ){ //First north
maxValue = valueOfFieldNorthOfXY;
direction = NORTH;
}
if ( valueOfFieldWestOfXY < maxValue ){ //Then east
maxValue = valueOfFieldWestOfXY ;
direction = WEST;
}
//then the also other directions
if ( value ... //as seen above
// 4) put the current field int your yourPathBackStack and walk to
// the 'best' direction
int newx = x;
int newy = y;
if (direction == NORTH){ //direction has been caclulated above
newy = newy - 1;
}
if (direc ... //with all other direction)
yourPathBackStack = new Stack(newx, newy, yourPathBackStack );
drawdot(yourPathBackStack.y,yourPathBackStack.x);
}

Hit detection in tilemaps

I'm working on a Mario game and am in need of assistance and suggestions on how to go about creating hit detection for a tilemap.
Currently, the player has the ability to walk/jump through the blocks.
I added in a fixed detection to the ground for now which I am hoping to replace with regular hit detection.
I understand that there are four sides to each block and the player. Only some blocks need hit detection and some things you might need to know is that the player stays at 300px(middle of screen) 98% of the time.
The only thing that moves is the map
The map is rendered from a .txt file and is rendered like so:
for(int y=0;y<map.length;y++) {
for(int x=0;x<map[y].length;x++) {
int index = map[y][x];
int yOffset = 0;
if(index>(tileSheet.getWidth() / Engine.TILE_WIDTH) -1) {
yOffset++;
index = index - (tileSheet.getWidth() / Engine.TILE_WIDTH);
}
g.drawImage(tileSheet,
((x * Engine.TILE_WIDTH)*scale)+position,
((y * Engine.TILE_HEIGHT)*scale),
(((x * Engine.TILE_WIDTH) + Engine.TILE_WIDTH )*scale)+position,
(((y * Engine.TILE_HEIGHT) + Engine.TILE_HEIGHT)*scale),
index * Engine.TILE_WIDTH,
yOffset * Engine.TILE_HEIGHT,
(index * Engine.TILE_WIDTH) + Engine.TILE_WIDTH,
(yOffset * Engine.TILE_HEIGHT) + Engine.TILE_HEIGHT,
null
);
}
}
//This code is actually longer(included file later on)
Colour hit detection is too slow and inconsistent for multi coloured tiles
Since the map is moving I suppose I need to move the hit detection boxes with it. As for selecting the boxes that it should detect might be difficult. Maybe it would be a better idea to make the code NOT hit detect certain tiles.
My attempts have ended in obfuscation of code. Can anyone suggest the easiest way to implement the hit detection? (keep in mind I have jumping).
The important codes are listed below:
Board.java(The panel where everything is drawn)
package EvilMario; //Include this class in the EvilMario game package
import java.awt.*; //Imported to allow use of Image
import java.awt.event.*; //Imported to allow use of ActionListener
import javax.swing.*; //Import swing
public class Board extends JPanel implements ActionListener { //Class Board
private TileLayer l; //Instance of TileLayer class
private Menu m; //Instance of menu class
private Player p; //Instance of player class
Timer time; //A timer
public static enum STATE {MENU,GAME}; //The game states
public static STATE State = STATE.MENU; //Set the first state to menu
//END
//GLOBAL
//DECLARATIONS
public Board() {
l = TileLayer.FromFile("D:/ICS3U1/EvilMario/map.txt"); //Tile map data from .txt file
this.addMouseListener(new MouseInput()); //Listen for mouse input
this.addKeyListener(new AL()); //Listen for key input
p = new Player(); //Start running Player class
m = new Menu(); //Start running Menu class
setFocusable(true); //Allows movement
time = new Timer(20,this); //Timer set to update "this" class every 20 milliseconds(Approximately 50fps)
time.start(); //Actually start the timer
}
public void actionPerformed(ActionEvent e) {
p.move(); //Call the move method from the player class
repaint(); //Repaint
}
public void paintComponent(Graphics g) { //Graphics method
super.paintComponent(g); //Super hero?
Graphics2D g2d = (Graphics2D) g; //Cast 2D graphics
if(State==STATE.GAME) {
if(p.distanceTraveled<300)l.DrawLayer(g,0);else l.DrawLayer(g, -(p.distanceTraveled-300)); //Draw the tile map
g2d.drawImage(p.getImage(), p.getX(), p.getY(), 48, 48, null); //Draw the player
if(p.distanceTraveled==3488) System.out.println("You have won the game!"); //Draw the end game screen
} else {
m.render(g); //Render the menu
}
}
private class AL extends KeyAdapter { //Action Listener extends key adapter
public void keyPressed(KeyEvent e) { //On key press
p.keyPressed(e); //Send whatever key was pressed TO the keyPressed method in the player class
}
public void keyReleased(KeyEvent e) { //On key release
p.keyReleased(e); //Send whatever key was released TO the keyReleased method in the player class
}
}
}
Player.java(player logic)
package EvilMario; //Include this class in the EvilMario game package
import java.awt.Image;
import java.awt.event.KeyEvent;
import javax.swing.ImageIcon;
public class Player {
int x, dx, y, distanceTraveled; //x coordinate,change in x coordinate,y coordinate,1st rep bg,2nd rep bg,dist traveled
Image player; //The player variable
ImageIcon walk_L_anim = new ImageIcon("D:/ICS3U1/EvilMario/images/animatedMario/walk_L_anim.gif");
ImageIcon walk_L_idle = new ImageIcon("D:/ICS3U1/EvilMario/images/animatedMario/walk_L_idle.png");
ImageIcon walk_R_anim = new ImageIcon("D:/ICS3U1/EvilMario/images/animatedMario/walk_R_anim.gif");
ImageIcon walk_R_idle = new ImageIcon("D:/ICS3U1/EvilMario/images/animatedMario/walk_R_idle.png");
ImageIcon jump_L_anim = new ImageIcon("D:/ICS3U1/EvilMario/images/animatedMario/jump_L_anim.gif");
ImageIcon jump_L_idle = new ImageIcon("D:/ICS3U1/EvilMario/images/animatedMario/jump_L_idle.png");
ImageIcon jump_R_anim = new ImageIcon("D:/ICS3U1/EvilMario/images/animatedMario/jump_R_anim.gif");
ImageIcon jump_R_idle = new ImageIcon("D:/ICS3U1/EvilMario/images/animatedMario/jump_R_idle.png");
boolean holdingLeft = false;
boolean holdingRight = false;
static boolean jumping = false;
static boolean falling = false;
static int jumpingTime = 350;
public Player() {
player = walk_R_idle.getImage(); //Give the player the image
x = 75; //The original x position of the player
y = 277; //The original y position of the player
distanceTraveled = 75; //Original distance traveled
}
public void move() {
if(x>=0 && x<=300) { //If the player is within the moving area
x = x+dx; //The x position is updated to become itself+the amount you moved
}
if(x<0) //If the player has reached he very left side of the screen(0px)
x=0; //Move him up a pixel so he can move again
if(x>300) //If the player has reached the center of the screen(300px)
x=300; //Move him down a pixel so he can move again
distanceTraveled=distanceTraveled+dx; //Calculate distanceTraveled
if(distanceTraveled<0) //Make sure distanceTraveled isn't a negative
distanceTraveled=0; //Make sure distanceTraveled isn't a negative
if(distanceTraveled>=300) //Keep player at center position once past 300 mario meters
x=300; //Keep player at center position once past 300 mario meters
if(holdingLeft && !holdingRight) {
if(distanceTraveled<300)dx=-5; else dx=-4;
if(jumping && !falling) {
player = jump_L_anim.getImage();
y-=8;
} else {
player = walk_L_anim.getImage();
if(y<277)
y+=8;
}
} else if(holdingRight && !holdingLeft) {
if(distanceTraveled<300)dx=5; else dx=4;
if(jumping && !falling) {
player = jump_R_anim.getImage();
y-=8;
} else {
player = walk_R_anim.getImage();
if(y<277)
y+=8;
}
} else if(!holdingRight && !holdingLeft) {
dx = 0;
if(jumping && !falling) {
player = jump_R_anim.getImage();
y-=8;
} else {
if(y<277)
y+=8;
}
}
if(y==277) {
falling = false;
}
System.out.println("LEFT: "+holdingLeft+" JUMP: "+jumping+" RIGHT: "+holdingRight+" FALLING: "+falling+" Y: "+y);
}
public int getX() { return x; } //This method will return the x. Is used by other classes
public int getY() { return y; } //This method will return the y. Is used by other classes
public Image getImage() { return player; } //This method will return the player. Is used by other classes
public void keyPressed(KeyEvent e) { //Called from the board class, the argument is whatever key was pressed
int key = e.getKeyCode(); //The key originally sent from the board class
if(key == KeyEvent.VK_LEFT && !holdingLeft)
holdingLeft = true;
if(key == KeyEvent.VK_RIGHT && !holdingRight)
holdingRight = true;
if(key == KeyEvent.VK_UP && !jumping && !falling)
new Thread(new JumpThread(this)).start();
}
public void keyReleased(KeyEvent e) { //Called from the board class, the argument is whatever key was released
int key = e.getKeyCode(); //The key originally sent from the board class
if(key == KeyEvent.VK_LEFT) { //If the left or right key was released
dx = 0; //Stop moving
holdingLeft = false;
player = walk_L_idle.getImage();
}
if(key == KeyEvent.VK_RIGHT) {
dx = 0;
holdingRight = false;
player = walk_R_idle.getImage();
}
}
}
TileLayer.java (Rendering of the tile layer)(Probably most important part relating to the question)
package EvilMario; //Include this class in the EvilMario game package
import java.awt.Graphics; //
public class TileLayer {
private int[][] map; //2D array
private BufferedImage tileSheet; //The tile sheet
public TileLayer(int[][] existingMap) { //
map = new int[existingMap.length][existingMap[0].length]; //map initialized
for(int y=0;y<map.length;y++) { //Loop through all boxes
for(int x=0;x<map[y].length;y++) { //Loop through all boxes
map[y][x] = existingMap[y][x]; //Update the map
}
}
tileSheet = LoadTileSheet("D:/ICS3U1/EvilMario/images/tilemap.gif"); //Load the tilesheet
}
public TileLayer(int width, int height) {
map = new int[height][width];
}
public static TileLayer FromFile(String fileName) {
TileLayer layer = null;
ArrayList<ArrayList<Integer>> tempLayout = new ArrayList<>();
try (BufferedReader br = new BufferedReader(new FileReader(fileName))) {
String currentLine;
while((currentLine = br.readLine()) !=null) {
if(currentLine.isEmpty())
continue;
ArrayList<Integer> row = new ArrayList<>();
String[] values = currentLine.trim().split(" ");
for(String string: values) {
if(!string.isEmpty()) {
int id = Integer.parseInt(string);
row.add(id);
}
}
tempLayout.add(row);
}
} catch(IOException e) {
System.out.println("ERROR");
}
int width = tempLayout.get(0).size();
int height = tempLayout.size();
layer = new TileLayer(width,height);
for(int y=0;y<height;y++) {
for(int x=0;x<width;x++) {
layer.map[y][x] = tempLayout.get(y).get(x);
}
}
layer.tileSheet = layer.LoadTileSheet("D:/ICS3U1/EvilMario/images/tilemap.gif");
return layer;
}
public BufferedImage LoadTileSheet(String fileName) {
BufferedImage img = null;
try {
img = ImageIO.read(new File(fileName));
} catch(Exception e) {
System.out.println("Could not load image");
}
return img;
}
int scale = 2;
public void DrawLayer(Graphics g, int position) {
for(int y=0;y<map.length;y++) {
for(int x=0;x<map[y].length;x++) {
int index = map[y][x];
int yOffset = 0;
if(index>(tileSheet.getWidth() / Engine.TILE_WIDTH) -1) {
yOffset++;
index = index - (tileSheet.getWidth() / Engine.TILE_WIDTH);
}
g.drawImage(tileSheet,
((x * Engine.TILE_WIDTH)*scale)+position,
((y * Engine.TILE_HEIGHT)*scale),
(((x * Engine.TILE_WIDTH) + Engine.TILE_WIDTH )*scale)+position,
(((y * Engine.TILE_HEIGHT) + Engine.TILE_HEIGHT)*scale),
index * Engine.TILE_WIDTH,
yOffset * Engine.TILE_HEIGHT,
(index * Engine.TILE_WIDTH) + Engine.TILE_WIDTH,
(yOffset * Engine.TILE_HEIGHT) + Engine.TILE_HEIGHT,
null
);
}
}
}
}
Engine.java (Not as important)(Simple variables for tile sizes)
package EvilMario;
public class Engine {
public static final int TILE_WIDTH = 16;
public static final int TILE_HEIGHT = 16;
}
If you need other pieces of code, just ask for them. I am not asking you to give me a specific answer to the question but simply a method that would work with my following code.
A specific answer would be nice though :)
I also believe the answer to this question will be useful to others because this method was explained in a popular java 2d game tutorial video(They never showed hit detection).
Methods I tried:
Creating a new java file called HitDetectionLayer with the exact code in TileLayer.java that stored positions in arrays. It failed :(
Ok, I'm not entirely sure what you are doing, if you throw up some images it would be more clear.
At any rate, 'hit detection' aka collision detection is a very complex topic, but it depends on what you want to do. If you want everything to be boxes or circles, then it is quite easy. If however you want things to rotate or you want collision for complex shapes it becomes extreme difficult.
Most games use circles or spheres for collision. You put the majority of your graphics (it may not fit perfectly either leaving part of your images in or out of the circle but that's life). Now lets say you have your mario sprite and one of those turtles. Well, you have circles around them both and once the circles touch you trigger your event.
The math for this is very easy because circles are by definition a perimeter around a constant length. Look at this:
You probably already know this, and it may seem obvious, but if you think about it this is what a circle really is: a consistent length in every fathomable direction. The directions are measured in degrees and from there you move on to trigonometry but you don't need that. What you need is coordinance aka vectors. So look at this:
All you need to determine circle collision is the distance between the circles. No matter what angle the circles collide from it does not matter because the distances from the circle's centre are consistent all the way around. Even if the circles are different sizes, it doesn't matter, just account for the radii difference.
Too compute all of this, you would write a method like this:
public boolean testDistanceBetween( float radius1, float radius2,
float x1, float x2, float y1, float y2 ){
double distanceBetween = Math.sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
if(distanceBetween < (radius1+radius2) ){
return true;
}
return false;
}
The moral of the story is that circles are just good that way. If you want to do rectangle collision you take the bottom-left and top right point and you test if other rectangles are in between those points. This should be pretty straight forward, each point is a vector, each rectangle has 4 points. If any of the 4 points of one rectangle are between points on the other rectangle, there is collision.
You can use this system to handle ground and walls also. For example, if ground is at Y=300, then if your sprite's y coordinance are == 300, you suspend your gravity.
The main thing I wanted to explain is that if you intend to have rotating rectangles or polygons and you want to detect collision on them... good luck. It can be done yes, but you should understand you are implementing complex physics, especially when/if you implement gravity.
So my answer is cautionary: there is NO easy way to detect collision of rotating rectangles or polygons. Circles and static rectangles are the limits. If you really want to do rotating rectangles/polygons get a physics engine. Box2d is pretty good and has a Java version Jbox2d.

Categories