I'm trying to create a dungeon generator. First, I place the rooms randomly, then I grow a maze in the empty space between them. But I got a problem, everytime I run my maze algorithm, it creates sometimes strange unreachable corridors:
Take a look at the red squares there. These corridors are unreachable for the player. How to avoid them? Here's some code:
public void createMaze(byte[][] dungeon) {
for (int y = 1; y < dungeon.length; y += 2) {
for (int x = 1; x < dungeon[0].length; x += 2) {
Vector2 pos = new Vector2(x, y);
if (dungeon[y][x] != 2 && dungeon[y][x] != 1){
growMaze(pos, dungeon);
}
}
}
}
public void growMaze(Vector2 pos, byte[][] dungeon) {
// Initialize some Lists and Vars
int lastDir = 0;
ArrayList<Vector2> cells = new ArrayList<Vector2>();
// Adding the startPosition to the cell list.
cells.add(pos);
// When the position is in the Grid
if(pos.y < dungeon.length - 2 && pos.y > 0 + 2 && pos.x < dungeon[0].length - 2 && pos.x > 0 + 2){
// And no walls or floors are around it
if(isPlaceAble(dungeon , pos)){
// Then place a corridor tile
dungeon[(int) pos.y][(int) pos.x] = 4;
}
}
// Here comes the algorithm.
while(!cells.isEmpty()){
// choose the latest cell
Vector2 choosedCell = cells.get(cells.size() - 1);
// Check again if the cell is in the grid.
if(choosedCell.y < dungeon.length - 2 && choosedCell.y > 0 + 2 && choosedCell.x < dungeon[0].length - 2 && choosedCell.x > 0 + 2){
// When that's true, then check in which directions the cell is able to move
boolean canGoNorth = dungeon[(int) (choosedCell.y + 1)][(int) choosedCell.x] == 0 && dungeon[(int) (choosedCell.y + 2)][(int) choosedCell.x] == 0;
boolean canGoSouth = dungeon[(int) (choosedCell.y - 1)][(int) choosedCell.x] == 0 && dungeon[(int) (choosedCell.y - 2)][(int) choosedCell.x] == 0;
boolean canGoEast = dungeon[(int) (choosedCell.y)][(int) choosedCell.x + 1] == 0 && dungeon[(int) (choosedCell.y)][(int) choosedCell.x + 2] == 0;
boolean canGoWest = dungeon[(int) (choosedCell.y)][(int) choosedCell.x - 1] == 0 && dungeon[(int) (choosedCell.y)][(int) choosedCell.x - 2] == 0;
// When there's no available direction, then remove the cell and break the loop...
if(!canGoNorth && !canGoSouth && !canGoEast && !canGoWest ){
cells.remove(cells.size() - 1);
break;
}
else{
// But if there's a available direction, then remove the cell from the list.
Vector2 savedCell = cells.get(cells.size() - 1);
cells.remove(cells.get(cells.size() - 1));
boolean placed = false;
// And place a new one into a new direction. This will happen as long as one is placed.
while(!placed){
// pick a random direction
int randomDirection = MathUtils.random(0,3);
int rdm = randomDirection;
// Init the length of the cells.
int length = 2;
// And now begin, if the direction and the random number fits, then dig the corridor. If no direction/number fits, then redo this until it works.
if(canGoNorth && rdm == 0 ){
int ycoord = 0;
for(int y = (int) choosedCell.y; y < choosedCell.y + length; y++){
dungeon[(int) y][(int) choosedCell.x] = 4;
}
Vector2 newCell = new Vector2(choosedCell.x, choosedCell.y + length);
cells.add(newCell);
lastDir = 0;
placed = true;
}
if(canGoSouth && rdm == 1 ){
int ycoord = 0;
for(int y = (int) choosedCell.y; y > choosedCell.y - length; y--){
dungeon[(int) y][(int) choosedCell.x] = 4;
}
Vector2 newCell = new Vector2(choosedCell.x, choosedCell.y - length);
cells.add(newCell);
lastDir = 1;
placed = true;
}
if(canGoEast && rdm == 2 ){
int xcoord = 0;
for(int x = (int) choosedCell.x; x < choosedCell.x + length; x++){
dungeon[(int) choosedCell.y][x] = 4;
}
Vector2 newCell = new Vector2(choosedCell.x + length, choosedCell.y );
cells.add(newCell);
lastDir = 2;
placed = true;
}
if(canGoWest && rdm == 3 ){
int xcoord = 0;
for(int x = (int) choosedCell.x; x > choosedCell.x - length; x--){
dungeon[(int) choosedCell.y][x] = 4;
}
Vector2 newCell = new Vector2(choosedCell.x - length, choosedCell.y );
cells.add(newCell);
lastDir = 3;
placed = true;
}
}
}
}
else{
cells.remove(cells.size() - 1);
}
}
// And finally delete dead end cells :) (Those who only got 3 Wall/Floor neighbours or 4)
killDeadEnds(dungeon);
}
So how to avoid these unreachable mazes?
You can use a union-find structure to quickly find and remove all the cells that aren't connected to rooms. See: https://en.wikipedia.org/wiki/Disjoint-set_data_structure
You initially create a disjoint set for each corridor or room cell, and then union every pair of sets for adjacent rooms or cells. Finally, delete all the corridor cells that aren't in the same set as a room.
Union-find is also the basis for a nice maze generation algorithm, which is just Kruskal's algorithm for finding spanning trees in a graph ( https://en.wikipedia.org/wiki/Kruskal%27s_algorithm ) applied to a grid. See: http://weblog.jamisbuck.org/2011/1/3/maze-generation-kruskal-s-algorithm
You could use this algorithm to generate your maze in the first place, before applying your dead end removal. It would change the character of your maze, though, so maybe you don't want to.
Related
In our assignment we are only allowed to use one method. I didn't know about that and I wrote two. So I wanted to ask, if its somehow possible to integrate the function of my neighbourconditions method into the life method. I tried, but I don't know how to initialize my int neighbors. Look at the following code:
public static String[] life(String[] dish) {
String[] newGen = new String[dish.length];
//TODO: implement this function
for (int line = 0; line < dish.length; line++) { // for loop going through each line
newGen[line] = "";
for (int i = 0; i < dish[line].length(); i++) { // loops through every character in the line
String top = ""; // neighbours on the top
String middle = ""; // neighbors on the same line
String down = ""; // neighbors down
if (i == 0){
if(line == 0){
top = null;
} else {
top = dish[line-1].substring(i, i+2);
}
middle = dish[line].substring(i + 1, i +2);
if(line == dish.length -1){
down = null;
} else {
down = dish[line + 1].substring(i, i + 2);
}
} else if (i == dish[line].length() - 1){
if(line == 0){
top = null;
} else {
top = dish[line - 1].substring(i - 1, i + 1);
}
middle = dish[line].substring(i - 1, i);
if(line == dish.length - 1){
down = null;
} else {
down = dish [line + 1].substring(i - 1, i + 1);
}
} else {
if (line == 0){
top = null;
} else {
top = dish[line - 1].substring(i - 1, i + 2);
}
middle = dish[line].substring(i - 1, i) + dish[line].substring(i+1, i+2);
if (line == dish.length - 1){
down = null;
} else {
down = dish[line + 1].substring(i - 1, i + 2);
}
}
int neighbors = neighbourconditions(top, middle, down);
if (neighbors < 2 || neighbors > 3){ // neighbours < 2 or >3 neighbors -> they die
newGen[line] += "o";
} else if (neighbors == 3){
newGen[line] += "x"; // neighbours exactly 3 -> they spawn/live
} else {
newGen[line] += dish[line].charAt(i); // 2 neighbours -> stay
}
}
}
return newGen;
}
// helpmethod with three arguments and the conditions
public static int neighbourconditions(String top, String middle, String down) {
int counter = 0;
if (top != null) { // if no one's on top
for (int x = 0; x < top.length(); ++x) {
if (top.charAt(x) == 'x') {
counter++; // count if an organism's here
}
}
}
for (int x = 0; x < middle.length(); ++x) {
if (middle.charAt(x) == 'x') { // two organisms, one on each side
counter++; // count if an organism's here
}
}
if (down != null) { // if no one's down
for (int x = 0; x < down.length(); ++x) {
if (down.charAt(x) == 'x') { // each neighbour down
counter++; // count if an organism's here
}
}
}
return counter;
}
Everything you do inside the second function will have to be done in the first function. So just copy the code from function 2 into function 1:
public static String[] life(String[] dish){
String[] newGen= new String[dish.length];
//TODO: implement this functions
for(int row = 0; row < dish.length; row++){ // each row
newGen[row]= "";
for(int i = 0; i < dish[row].length(); i++){ // each char in the row
String above = ""; // neighbors above
String same = ""; // neighbors in the same row
String below = ""; // neighbors below
if(i == 0){ // all the way on the left
// no one above if on the top row
// otherwise grab the neighbors from above
above = (row == 0) ? null : dish[row - 1].substring(i, i + 2);
same = dish[row].substring(i + 1, i + 2);
// no one below if on the bottom row
// otherwise grab the neighbors from below
below = (row == dish.length - 1) ? null : dish[row + 1].substring(i, i + 2);
}else if(i == dish[row].length() - 1){//right
// no one above if on the top row
// otherwise grab the neighbors from above
above = (row == 0) ? null : dish[row - 1].substring(i - 1, i + 1);
same = dish[row].substring(i - 1, i);
// no one below if on the bottom row
// otherwise grab the neighbors from below
below = (row == dish.length - 1) ? null : dish[row + 1].substring(i - 1, i + 1);
}else{ // anywhere else
// no one above if on the top row
//otherwise grab the neighbors from above
above = (row == 0) ? null : dish[row - 1].substring(i - 1, i + 2);
same = dish[row].substring(i - 1, i) + dish[row].substring(i + 1, i + 2);
//no one below if on the bottom row
//otherwise grab the neighbors from below
below = (row == dish.length - 1) ? null : dish[row + 1].substring(i - 1, i + 2);
}
// here is the interesting part for you:
int neighbors = 0;
if(above != null){//no one above
for(char x: above.toCharArray()){ //each neighbor from above
if(x == 'x') neighbors++; //count it if someone is here
}
}
for(char x: same.toCharArray()){ //two on either side
if(x == 'x') neighbors++;//count it if someone is here
}
if(below != null){ //no one below
for(char x: below.toCharArray()){//each neighbor below
if(x == 'x') neighbors++;//count it if someone is here
}
};
//here ends the interesting part for you
if(neighbors < 2 || neighbors > 3){
newGen[row]+= "o"; // If the amount of neighbors is < 2 or >3 neighbors -> they die
}else if(neighbors == 3){
newGen[row]+= "x"; // If the amount of neighbors is exactly 3 neighbors -> they spawn/live
}else{
newGen[row]+= dish[row].charAt(i); // 2 neighbors -> stay
}
}
}
return newGen;
}
The trivial answer to this question is to copy and paste the code from the method into the body of the other method. If you're using an IDE, you can use the in-built refactoring tools to inline the method (e.g. ctrl-alt-n, in intellij).
But this is the sort of behavior that makes future generations curse your name. It makes for nasty, unreadable, unmaintainable code. Don't do it. As GhostCat pointed out in comments, you should be looking to make methods smaller, not bigger.
Take a step back, and consider whether you're approaching the problem in the right way. Look for repeating patterns in the existing code, to see if you can simplify it. Or, sometimes, consider that you've just taken the wrong approach in the first place, and so you need to find an alternative approach.
As far as I can work out, all you're trying to do is to count the number of xs in the 8 cells immediately surrounding the current position.
You don't need all of this code to do that. You could simply do:
for(int row = 0; row < dish.length; row++){ // each row
for(int col = 0; col < dish[row].length(); col++){ // each char in the row
int neighbors = 0;
for (int r = Math.max(row - 1, 0); r < Math.min(row + 2, dish.length); ++r) {
for (int c = Math.max(col - 1, 0); c < Math.min(col + 2, dish[row].length()); ++c) {
// Don't count (row, col).
if (r == row && c == col) continue;
if (dish[r].charAt(c) == 'x') ++neighbors;
}
}
//here ends the interesting part for you
if(neighbors < 2 || neighbors > 3){
// etc.
Way less code, no need for an auxiliary method. Also a lot more efficient, because it avoids unnecessarily creating strings.
I'm creating a minesweeper game and I really need a fast and Efficient way of calculating the neighbors of a mine, Actually im storing my tiles in an Arraylist so I can use them in a gridview, so the position is lineal but the rendering will be a matrix n*n. I have a way to do it but I think someone can have a more efficient way.
What I want to achieve:
0 1 1 1
0 1 * 1
0 1 1 1
0 0 0 0
So given that matrix having the indexes in a lineal List the position should be the following:
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16
So I need an efficient way of obtaining 2, 3, 4, 6, 8, 10, 11, 12 giving the index 7.
Code to generate bombs:
public void plantMines(){
Random rand = new Random();
//Used set so we dont get duplicates
Set<Integer> mineCoords = new LinkedHashSet<>(mDifficulty.mines);
//First we randomly select all coordenates
while (mineCoords.size() < mDifficulty.mines){
Integer coord = rand.nextInt(mListCap) + 1;
mineCoords.add(coord);
}
//Now we can set the mines accordingly
for (Integer coord: mineCoords){
mTiles.get(coord).setMine(true);
}
}
Actual code to find neighbors:
for (int row = 0; row < ROW_SIZE; row++) {
for (int col = 0; col < COL_SIZE; col++) {
int neighbourBombSize = 0;
// TOP ROW
if ((row-1) >= 0 && (col-1) >= 0) {
if (getTile(row-1, col-1).hasBomb()) {
neighbourBombSize++;
}
}
if ((row-1) >= 0) {
if (getTile(row-1, col).hasBomb()) {
neighbourBombSize++;
}
}
if ((row-1) >= 0 && (col+1) < COL_SIZE) {
if (getTile(row-1, col+1).hasBomb()) {
neighbourBombSize++;
}
}
// SAME ROW
if ((col-1) >= 0) {
if (getTile(row, col-1).hasBomb()) {
neighbourBombSize++;
}
}
if ((col+1) < COL_SIZE) {
if (getTile(row, col+1).hasBomb()) {
neighbourBombSize++;
}
}
// BOTTOM ROW
if ((row+1) < ROW_SIZE && (col-1) >= 0) {
if (getTile(row+1, col-1).hasBomb()) {
neighbourBombSize++;
}
}
if ((row+1) < ROW_SIZE) {
if (getTile(row+1, col).hasBomb()) {
neighbourBombSize++;
}
}
if ((row+1) < ROW_SIZE && (col+1) < COL_SIZE) {
if (getTile(row+1, col+1).hasBomb()) {
neighbourBombSize++;
}
}
getTile(row, col).setNeighbourBombSize(neighbourBombSize);
}
}
Help will be appreciated, thanks.
WARNING : I've take your code as starting point, but your index start a 1, but in java array index start at 0, so it may not work.
I would do something like that :
int neighbourBombSize = 0;
// Compute currentCell row / col
int currentCellCol = ((currentCellIndex - 1) % COL_SIZE) + 1;
int currentCellRow = ((currentCellIndex - 1) / COL_SIZE) + 1;
System.out.println("Neighbors of " + currentCellIndex + " (" + currentCellRow + ", " + currentCellCol + ")");
for (int x = -1; x <= 1; x++) {
for (int y = -1; y <= 1; y++) {
if (x == 0 && y == 0) {
continue; // Current cell index
}
int neighborCol = currentCellCol + y;
int neighborRow = currentCellRow + x;
if (neighborCol > 0 && neighborRow > 0 && neighborCol <= COL_SIZE && neighborRow <= ROW_SIZE ) {
int computedNeighborIndex = neighborCol + ((neighborRow - 1) * COL_SIZE);
if (getTile(neighborRow , neighborCol ).hasBomb()) {
neighbourBombSize++;
}
}
}
}
You can see a running example (computing neighbors index for all case) here : Running example
Are you planning to write the whole game referring to cells using this 1-based linear index? If so, you will want to isolate the conversion to and from coordinates. Otherwise you will eventually mess up somewhere.
Try something like this:
class Board {
final int rows;
final int cols;
Board(int cols, int rows) {
this.rows = rows;
this.cols = cols;
}
int col(int index) {
assert index > 0 && index <= rows * cols;
return (index - 1) % cols; // -1 because you are using 1-based indexing.
}
int row(int index) {
assert index > 0 && index <= rows * cols;
return (index - 1) / cols;
}
int index(int x, int y) {
assert x >= 0 && x < cols && y >= 0 && y < rows;
return y * cols + x + 1;
}
int[] neighbors(int point) {
int x = col(point);
int y = row(point);
int[] result = new int[8];
int cnt = 0;
// go over possible neighbors and collect valid ones
for (int ny = max(y - 1, 0); ny < min(y + 2, rows); ny++) {
for (int nx = max(x - 1, 0); nx < min(x + 2, cols); nx++) {
if (nx != x || ny != y) {
result[cnt++] = index(nx, ny);
}
}
}
return Arrays.copyOf(result, cnt);
}
}
Board brd = new Board(4, 4);
int colOf7 = brd.col(7); // 2 (0-based from left)
int rowOf7 = brd.row(7); // 1 (0-based from top)
int[] neighborsOf7 = brd.neighbors(7); // [2, 3, 4, 6, 8, 10, 11, 12]
int index = brd.index(2,1); // 7 (1-based linear index)
My code(Shown Below) is doing what it's supposed to do (generate a passageway from point a to b with random stops in between) and It works. Well, not all of the time. I have tried to research for syntax problems, and spent hours on end looking for some simple math problem, but I can't find it.
The Probolem is that it generates a valid path most of the time, but ocationaly, it is 3 spots off from the first point to the second. Does anyone see what the issue is?
public static int[][] genLayer(int enterX, int enterY) {
// Initiate Variables and arrays
ArrayList<Integer> xPos = new ArrayList<Integer>(); // Array of x
// positions
ArrayList<Integer> yPos = new ArrayList<Integer>(); // Array of y
// positions
int[][] layer = new int[20][20]; // The 2D array of the layer to be
// returned to the caller
// Generates the points for the passageway to go thru.
int point1X = rand.nextInt(20); // The first point's x
int point1Y = rand.nextInt(20); // The first point's y
int point2X = rand.nextInt(20); // The second point's x
int point2Y = rand.nextInt(20); // The second point's y
int point3X = rand.nextInt(20); // The third point's x
int point3Y = rand.nextInt(20); // The third point's y
layer[enterX][enterY] = 4; // Set the cords of enter X and Y to 4, the
// number representing the up stairs
// Enter To Point 1:
// Generate the first set of x points for the layer's passages
if (enterX > point1X) {
for (int x = enterX - 1; x > point1X; x--) {
xPos.add(x);
}
} else if (enterX < point1X) {
for (int x = enterX + 1; x < point1X; x++) {
xPos.add(x);
}
}
// Generate the first set of y points for the layer's passages
if (enterY > point1Y) {
for (int y = enterY - 1; y > point1Y; y--) {
yPos.add(y);
}
} else if (enterY < point1Y) {
for (int y = enterY + 1; y < point1Y; y++) {
yPos.add(y);
}
}
// Make Passages
if (yPos.size() > 0) {
if (rand.nextBoolean() & xPos.size() > 0) { // Chose randomly
// whether to
// make the passage up
// then
// sideways or sideways
// then
// up.
//
// Then, decide if there
// is
// any horizontal or
// vertical passages to
// generate
// x then y
for (int i = 0; i < xPos.size(); i++) {
layer[xPos.get(i)][enterY] = 1; // make the horizontal
// passage
}
for (int i = 0; i < yPos.size(); i++) {
layer[xPos.get(xPos.size() - 1)][yPos.get(i)] = 1; // make
// the
// vertical
// passage
}
} else {
// y then x
for (int i = 0; i < yPos.size(); i++) {
layer[enterX][yPos.get(i)] = 1; // make the vertical passage
}
for (int i = 0; i < xPos.size(); i++) {
layer[xPos.get(i)][yPos.get(yPos.size() - 1)] = 1; // make
// the
// horizontal
// passage
}
}
}
// Set point 1 to the last xPos and yPos to make up for unknown
// calculation errors
if (xPos.size() > 0)
point1X = xPos.get(xPos.size() - 1);
if (yPos.size() > 0)
point1Y = yPos.get(yPos.size() - 1);
// Flush the values of xPos and yPos
xPos.clear();
yPos.clear();
// Point 1 To Point 2:
// Generate the second set of x points for the layer's passages
if (point1X > point2X) {
for (int x = point1X - 1; x > point2X; x--) {
xPos.add(x);
}
} else if (point1X < point2X) {
for (int x = point1X + 1; x < point2X; x++) {
xPos.add(x);
}
}
// Generate the second set of y points for the layer's passages
if (point1Y > point2Y) {
for (int y = point1Y - 1; y > point2Y; y--) {
yPos.add(y);
}
} else if (point1Y < point2Y) {
for (int y = point1Y + 1; y < point2Y; y++) {
yPos.add(y);
}
}
// Make Passages
if (yPos.size() > 0) {
if (rand.nextBoolean() & xPos.size() > 0) { // Chose randomly
// whether to
// make the passage up
// then
// sideways or sideways
// then
// up.
//
// Then, decide if there
// is
// any horizontal or
// vertical passages to
// generate
// x then y
for (int i = 0; i < xPos.size(); i++) {
layer[xPos.get(i)][point1Y] = 1; // make the horizontal
// passage
}
for (int i = 0; i < yPos.size(); i++) {
layer[xPos.get(xPos.size() - 1)][yPos.get(i)] = 1; // make
// the
// vertical
// passage
}
} else {
// y then x
for (int i = 0; i < yPos.size(); i++) {
layer[point1X][yPos.get(i)] = 1; // make the vertical
// passage
}
for (int i = 0; i < xPos.size(); i++) {
layer[xPos.get(i)][yPos.get(yPos.size() - 1)] = 1; // make
// the
// horizontal
// passage
}
}
}
// Set point 2 to the last xPos and yPos to make up for unknown
// calculation errors
if (xPos.size() > 0)
point2X = xPos.get(xPos.size() - 1);
if (yPos.size() > 0)
point2Y = yPos.get(yPos.size() - 1);
// Flush the values of xPos and yPos
xPos.clear();
yPos.clear();
// Point 2 To Point 3:
// Generate the third set of x points for the layer's passages
if (point2X > point3X) {
for (int x = point2X - 1; x > point3X; x--) {
xPos.add(x);
}
} else if (point2X < point3X) {
for (int x = point2X + 1; x < point3X; x++) {
xPos.add(x);
}
}
// Generate the third set of y points for the layer's passages
if (point2Y > point3Y) {
for (int y = point2Y - 1; y > point3Y; y--) {
yPos.add(y);
}
} else if (point2Y < point3Y) {
for (int y = point2Y + 1; y < point3Y; y++) {
yPos.add(y);
}
}
// Make Passages
if (yPos.size() > 0) {
if (rand.nextBoolean() & xPos.size() > 0) { // Chose randomly
// whether to
// make the passage up
// then
// sideways or sideways
// then
// up.
//
// Then, decide if there
// is
// any horizontal or
// vertical passages to
// generate
// x then y
for (int i = 0; i < xPos.size(); i++) {
layer[xPos.get(i)][point2Y] = 1; // make the horizontal
// passage
}
for (int i = 0; i < yPos.size(); i++) {
layer[xPos.get(xPos.size() - 1)][yPos.get(i)] = 1; // make
// the
// vertical
// passage
}
} else {
// y then x
for (int i = 0; i < yPos.size(); i++) {
layer[point2X][yPos.get(i)] = 1; // make the vertical
// passage
}
for (int i = 0; i < xPos.size(); i++) {
layer[xPos.get(i)][yPos.get(yPos.size() - 1)] = 1; // make
// the
// horizontal
// passage
}
}
}
// Set point 3 to the last xPos and yPos to make up for unknown
// calculation errors
if (xPos.size() > 0)
point3X = xPos.get(xPos.size() - 1);
if (yPos.size() > 0)
point3Y = yPos.get(yPos.size() - 1);
// Flush the values of xPos and yPos
xPos.clear();
yPos.clear();
for (int i = 0; i < 20; i++) {
for (int j = 0; j < 20; j++) {
System.out.print(" " + layer[i][j]);
}
System.out.println();
}
return layer;
}
Note: I know this code can be much, much smaller with methods, but this is just a rough test of it's capabilities. I will be working on that later.
Thanks in advance!
One mistake I notice is in the "Make Passages" sections. Each one is wrapped in an
if(yPos.size() > 0)
conditional, but doesn't consider the case of when xPos.size() is greater than zero. Basically, if there's no change in Y, but there is a change in X, then it will just skip creating that section of passage.
Example:
p2 p3
p1
results in
1 1 1 1
0 0 0 0
4 0 0 0
Next bug:
If one of the variables is only off by one, then the size of the list of points it generates will be 0, so it will not connect the two. For example enterX equals to 10 and point1X equal to 9 will not connect them.
p3
p2
p1
results in
1 0
1 0
0 0
0 4
To fix this, I'd suggest changing all of the loops of the form
for (int x = enterX - 1; x > point1X; x--)
to
for (int x = enterX - 1; x >= point1X; x--)
In other words, including the final point in the list.
The paths you generate aren't going the entire distance between your sets of two points. Since you stop generating your path at pX - 1 and pY - 1 (in the case of positive direction traversal, and pX + 1 and pY + 1 during negative direction traversal), you get this sort of design:
0 0 0 P2
1 1 1 0
1 0 0 0
P1 0 0 0
Notice that the path doesn't actually reach P2. Try changing these sections and those like it to
// Generate the first set of x points for the layer's passages
if (enterX > point1X) {
for (int x = enterX - 1; x >= point1X; x--) { // > becomes >=
xPos.add(x);
}
} else if (enterX < point1X) {
for (int x = enterX + 1; x <= point1X; x++) { // < becomes <=
xPos.add(x);
}
}
so that the entire distance between the points is always traversed.
You are given a 2D array as a string and a word via keyboard. The word
can be in any way (all 8 neighbors to be considered) but you can’t use
same character twice while matching. Return word's first and last
character's index as (x,y). If match is not found return -1.
That's the question. I'm having trouble with searching. I tried that:
int x=0,y=0;
for(int f=0; f<WordinArray.length; f++){
for(int i=0; i<matrix.length; i++){
for(int j=0; j<matrix[0].length; j++){
if(matrix[i][j].equals(WordinArray[f])){
x=i; y=j;
System.out.print("("+x+","+y+")");
}
}
}
}
But, That code is not working as it is supposed to. How else I can write this searching code?
Referring to Sixie's code
Assuming this is a valid input/output to your program?
Size:
4x4
Matrix:
a b c d
e f g h
i j k l
m n o p
Word: afkp
(0,0)(3,3)
I edited your code, so that it should work for input on this form (it is case sensitive at the moment, but can easily be changed by setting .toLowerCase()
Scanner k = new Scanner(System.in);
System.out.println("Size: ");
String s = k.nextLine();
s.toUpperCase();
int Xindex = s.indexOf('x');
int x = Integer.parseInt(s.substring(0, Xindex));
int y = Integer.parseInt(s.substring(Xindex + 1));
System.out.println("Matrix:");
char[][] matrix = new char[x][y];
for (int i = 0; i < x; i++) {
for (int p = 0; p < y; p++) {
matrix[i][p] = k.next().charAt(0);
}
}
System.out.print("Word: ");
String word = k.next();
int xStart = -1, yStart = -1;
int xEnd = -1, yEnd = -1;
// looping through the matrix
for (int i = 0; i < x; i++) {
for (int j = 0; j < y; j++) {
// when a match is found at the first character of the word
if (matrix[i][j] == word.charAt(0)) {
int tempxStart = i;
int tempyStart = j;
// calculating all the 8 normals in the x and y direction
// (the 8 different directions from each cell)
for (int normalX = -1; normalX <= 1; normalX++) {
for (int normalY = -1; normalY <= 1; normalY++) {
// go in the given direction for the whole length of
// the word
for (int wordPosition = 0; wordPosition < word
.length(); wordPosition++) {
// calculate the new (x,y)-position in the
// matrix
int xPosition = i + normalX * wordPosition;
int yPosition = j + normalY * wordPosition;
// if the (x,y)-pos is inside the matrix and the
// (x,y)-vector normal is not (0,0) since we
// dont want to check the same cell over again
if (xPosition >= 0 && xPosition < x
&& yPosition >= 0 && yPosition < y
&& (normalX != 0 || normalY != 0)) {
// if the character in the word is not equal
// to the (x,y)-cell break out of the loop
if (matrix[xPosition][yPosition] != word
.charAt(wordPosition))
break;
// if the last character in the word is
// equivalent to the (x,y)-cell we have
// found a full word-match.
else if (matrix[xPosition][yPosition] == word
.charAt(wordPosition)
&& wordPosition == word.length() - 1) {
xStart = tempxStart;
yStart = tempyStart;
xEnd = xPosition;
yEnd = yPosition;
}
} else
break;
}
}
}
}
}
}
System.out.println("(" + xStart + "," + yStart + ")(" + xEnd + ","
+ yEnd + ")");
k.close();
I think you need to plan your algorithm a bit more carefully before you start writing code. If I were doing it, my algorithm might look something like this.
(1) Iterate through the array, looking for the first character of the word.
(2) Each time I find the first character, check out all 8 neighbours, to see if any is the second character.
(3) Each time I find the second character as a neighbour of the first, iterate along the characters in the array, moving in the correct direction, and checking each character against the word.
(4) If I have matched the entire word, then print out the place where I found the match and stop.
(5) If I have reached the edge of the grid, or found a character that doesn't match, then continue with the next iteration of loop (2).
Once you have your algorithm nailed down, think about how to convert each step to code.
If I understood your question right. This is a quick answer I made now.
int H = matrix.length;
int W = matrix[0].length;
int xStart = -1, yStart = -1;
int xEnd = -1, yEnd = -1;
String word = "WordLookingFor".toLowerCase();
for (int i = 0; i < H; i++) {
for (int j = 0; j < W; j++) {
if (matrix[i][j] == word.charAt(0)) {
int tempxStart = i;
int tempyStart = j;
for (int x = -1; x <= 1; x++) {
for (int y = -1; y <= 1; y++) {
for (int k = 0; k < word.length(); k++) {
int xx = i+x*k;
int yy = j+y*k;
if(xx >= 0 && xx < H && yy >= 0 && yy < W && (x != 0 || y != 0)) {
if(matrix[xx][yy] != word.charAt(k))
break;
else if (matrix[xx][yy] == word.charAt(k) && k == word.length()-1) {
xStart = tempxStart;
yStart = tempyStart;
xEnd = xx;
yEnd = yy;
}
} else
break;
}
}
}
}
}
}
A little trick I used for checking all the 8 neighbors is to use two for-loops to create all the directions to go in:
for (int x = -1; x <= 1; x++) {
for (int y = -1; y <= 1; y++) {
if(x !=0 || y != 0)
System.out.println(x + ", " + y);
}
}
This creates
-1, -1
-1, 0
-1, 1
0, -1
0, 1
1, -1
1, 0
1, 1
Notice: All but 0,0 (you don't want to revisit the same cell).
The rest of the code is simply traversing though the matrix of characters, and though the whole length of the word you are looking for until you find (or maybe you don't find) a full match.
This time the problem is that how could I print word's first and last
letter's indexes. I tried various ways like printing after each word
was searched. But, all of them didn't work. I am about to blow up.
int[] values = new int[2];
for(int i=0; i<matrix.length; i++){
for(int j=0; j<matrix[0].length; j++){
if(Character.toString(word.charAt(0)).equals(matrix[i][j]) == true || Character.toString(ReversedWord.charAt(0)).equals(matrix[i][j]) == true ){
System.out.print("("+ i + "," +j+")");
//First letter is found.Continue.
for(int p=1; p<word.length(); p++){
try{
for (int S = -1; S <= 1; S++) {
for (int SS = -1; SS <= 1; SS++) {
if(S !=0 || SS != 0)
if(matrix[i+S][j+SS].equals(Character.toString(word.charAt(p))) && blocksAvailable[i+S][j+SS] == true ||
matrix[i+S][j+SS].equals(Character.toString(ReversedWord.charAt(p))) && blocksAvailable[i+S][j+SS] == true) {
values[0] = i+S;
values[1] = j+SS;
blocksAvailable[i+S][j+SS] = false;
}
}
}
}catch (ArrayIndexOutOfBoundsException e) {}
The problem is to find the shortest path on a grid from a start point to a finish point. the grid is a 2 dimensional array filled with 0's and 1's. 1's are the path. I have a method that checks the neighbors of a given coordinate to see if its a path. The problem im having is with the boundaries of the grid. The right and bottom boundary can just be checked using the arrays length and the length of a column. But how would i check to make sure that i dont try to check a point thats to the left of the grid or above the grid?
This is my method
public static void neighbors(coordinate current, int[][] grid, Queue q)
{
int row = current.getRow();
int col = current.getCol();
if(grid[row-1][col] == 1)
{
if(grid[row][col] == -1)
{
grid[row-1][col] = grid[row][col] + 2;
}
else
{
grid[row-1][col] = grid[row][col] + 1;
}
coordinate x = new coordinate(row-1,col);
q.enqueue(x);
}
else if(grid[row+1][col] == 1)
{
if(grid[row][col] == -1)
{
grid[row+1][col] = grid[row][col] + 2;
}
else
{
grid[row+1][col] = grid[row][col] + 1;
}
coordinate x = new coordinate(row+1,col);
q.enqueue(x);
}
else if(grid[row][col-1] == 1)
{
if(grid[row][col] == -1)
{
grid[row][col-1] = grid[row][col] + 2;
}
else
{
grid[row][col-1] = grid[row][col] + 1;
}
coordinate x = new coordinate(row, col - 1);
q.enqueue(x);
}
else if(grid[row][col+1] == 1)
{
if(grid[row][col+1] == -1)
{
grid[row][col+1] = grid[row][col] + 1;
}
else
{
grid[row][col+1] = grid[row][col] + 1;
}
coordinate x = new coordinate(row, col + 1);
q.enqueue(x);
}
else
{
}
q.dequeue();
}
I assume that the leftmost and topmost indexes are 0 in your arrays, so just make sure that index-1 >= 0 before indexing into the appropriate array.