I was working on an animation on processing. Then, I have a question about the loop. Normally, my code is more long. However, I made a simple code which can usefull also for the beginners.
My sample code:
void setup(){
println("Line between points " + curr + " and " + (curr+1));
println("initial X: " + initialX + " initial Y: " + initialY );
println("final X: " + finalX + " final Y: " + finalY );
counter = 0; // reset counter;
}
void draw() {
point(initialX, initialY);
println(initialX, initialY, p);
}
So, like you see I used "Bresenhams Algorithm" for drawing the lines. However when I draw the lines it doesn't draw the lines between points. It's just drawing a little bit. Normally my text file is so long. How to I draw lines that can follow from first x and y coordinates to last x and y coordinates without disconnection?
This is implementation of a version of Bresenham's algorithm using balancing the positive and negative error between the x and y coordinates:
/*
String[] coordinates = { // Creating an array for my text file.
"117 191",
"96 223",
"85 251",
"77 291",
"78 323",
"84 351",
"97 378",
"116 404",
"141 430"
};
*/
int[][] points;
int deltaX, deltaY;
int initialX, initialY; // Initial point of first coodinate
int finalX, finalY; // Final point of first coodinate
int counter = 0;
int curr = 0;
int sx, sy, err;
void setup() {
size(500, 500);
strokeWeight(4);
frameRate(25);
coordinates = loadStrings("coordinates.txt");
beginShape(); // It combines the all of vertexes
points = new int[coordinates.length][2];
int row = 0;
for (String line : coordinates) {
String[] pair = line.split(" ");
points[row] = new int[] { Integer.parseInt(pair[0]), Integer.parseInt(pair[1])};
println(points[row][0]); // print x
println(points[row][1]); // print y
row++;
}
fixLines();
endShape(CLOSE);
}
void fixLines() {
int ix = curr % points.length;
int jx = (curr + 1) % points.length;
initialX = points[ix][0];
initialY = points[ix][1];
finalX = points[jx][0];
finalY = points[jx][1];
deltaX = abs(finalX - initialX);
sx = initialX < finalX ? 1: -1;
deltaY = -abs(finalY - initialY);
sy = initialY < finalY ? 1: -1;
err = deltaX + deltaY;
println("Line between points " + curr + " and " + (curr+1));
println("[" + initialX + ", " + initialY + "] - [" + finalX + ", " + finalY + "]");
println("deltaX=" + deltaX);
}
void draw() {
point(initialX, initialY);
if (initialX == finalX && initialY == finalY) {
curr++;
if (curr == points.length) {
noLoop();
} else {
fixLines();
}
} else {
int e2 = 2 * err;
if (e2 >= deltaY) {
err += deltaY;
initialX += sx;
}
if (e2 <= deltaX) {
err += deltaX;
initialY += sy;
}
}
}
The output is very close to linear implementation:
I try updating method draw to update deltaY and continue drawing until deltaY != 0 but result does not look good. Most likely you need to review your implementation of the algorithm and related calculations.
void draw()
{
point(initialX, initialY);
println(initialX, initialY, p);
if (finalX > initialX )
initialX++;
else
initialX--;
if (p < 0) {
p = p + 2 * deltaY;
} else {
if (initialY > finalY)
initialY--;
else
initialY++;
p = p + 2 * deltaY - 2 * deltaX;
}
deltaY = abs(finalY - initialY); // update deltaY
counter++;
if (counter > deltaX) {
if (deltaY > 0) {
counter--;
} else {
curr++;
if (curr == points.length) {
noLoop(); // possibly you should break out of the main loop here
} else {
fixLines();
}
}
}
}
Implementation with line(initialX, initialY, finalX, finalY); looks much better.
void draw()
{
point(initialX, initialY);
println(initialX, initialY, p);
line(initialX, initialY, finalX, finalY);
curr++;
if (curr == points.length) {
noLoop();
} else {
fixLines();
}
}
my problem is that when one ball object collides with the left, right or top of the screen, or collided with either of the bricks it will invert the velocity of all the balls for example if ball 1 hits the left wall it will bounce off in the opposite X direction. However, ball 2 will also do the same even when it doesn't collide with anything (the same thing happens to ball 1 when ball 2 hits something).
I am using the Processing development environment. however, this uses Java, specifically the PApplet class
This is my method to move the balls:
void moveBalls(){
PVector ballPos;
for(balls b : ballsCollection){
ballPos = b.getPos();
if(b.isMoving()) {
//make the ball bounce off the walls
if(ballPos.y > height - blockSize){ //bottom
if(ballsInMotion == ballsCollection.size()){
ballPos.y = height - ballSize/2 - blockSize - 3;
ballStartX = ballPos.x;
ballStartY = height - blockSize - ballSize - ballSize/2;
b.invertMoving();
ballsInMotion -=1;
println("FirstBall down" + ballPos);
} else {
b.invertMoving();
ballsInMotion -=1;
PVector p = new PVector(ballStartX,ballStartY);
b.setPos(p);
println("Next ball Down new P: " + p);
}
if(ballsInMotion == 0){
setUpNextRound = true;
}
}
if (ballPos.y < ballSize + blockSize){ //top
b.invertV("y");
ballPos.y = ballSize+blockSize;
println("ball " + b.getIndex() + " hit Top");
}
if (ballPos.x > width - ballSize/2){ //right
b.invertV("x");
ballPos.x = 2*width-ballSize-ballPos.x;
println("ball " + b.getIndex() + " hit right");
}
if (ballPos.x < ballSize/2){ //left
b.invertV("x");
ballPos.x = ballSize-ballPos.x;
println("ball " + b.getIndex() + " hit left");
}
for(blocks block : blocksCollection){
int blockHealth = block.getBlockHealth();
int blockX = block.getX();
int blockY = block.getY();
boolean hit = false;
if(blockHealth > 0){
// Bottom of the block Top of the Block
if(!hit && (blockBallCollision(blockX+blockSize/2, blockY+blockSize, blockSize-blockCournerSize, 0, ballPos.x-b.getV().x, ballPos.y-b.getV().y, 2*(ballSize)) || blockBallCollision(blockX+blockSize/2, blockY, blockSize-blockCournerSize, 0, ballPos.x-b.getV().x, ballPos.y-b.getV().y, 2*(ballSize)))) {
println("Block Collision Top/Bottom: " + block.getIndex() + " by Ball: " + b.getIndex());
b.invertV("y");
block.blockHit();
hit = true;
// Left of the block Right of the Block
} else if ( !hit && (blockBallCollision(blockX, blockY+blockSize/2, 0, blockSize-blockCournerSize, ballPos.x-b.getV().x, ballPos.y-b.getV().y, 2*(ballSize)) || blockBallCollision(blockX+blockSize, blockY+blockSize/2, 0, blockSize-blockCournerSize, ballPos.x-b.getV().x, ballPos.y-b.getV().y, 2*(ballSize)))){
println("Block Collision Left/Right: " + block.getIndex() + " by Ball: " + b.getIndex());
b.invertV("x");
block.blockHit();
hit = true;
}
}
}
b.movePosV();
drawBalls();
}
}
}
The ball object is stored:
public class balls{
private int index;
private PVector b = new PVector();
private PVector bV = new PVector();
private boolean moving, needMoving;
public balls(int Index, float X, float Y, boolean m, PVector ballV){
index = Index;
b.x = X;
b.y = Y;
moving = m;
bV = ballV;
}
public void movePosV(){
b.sub(bV);
}
public void invertV(String xy){
if(xy.equals("x")){
bV.x = -bV.x;
} else {
bV.y = -bV.y;
}
}
public void setPos(PVector newB){
b = newB;
}
public void setV(PVector newV){
bV = newV;
}
public boolean getNeedMoving(){
return needMoving;
}
public PVector getV(){
return bV;
}
public PVector getPos(){
return b;
}
public int getIndex(){
return index;
}
public boolean isMoving(){
return moving;
}
public void invertMoving(){
moving = !moving;
}
public void invertNeedMoving(){
needMoving = !needMoving;
}
}
I am working or understanding how to create a simple java 2d maze that should look like this:
int [][] maze =
{ {1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,0,1,0,1,0,1,0,0,0,0,0,1},
{1,0,1,0,0,0,1,0,1,1,1,0,1},
{1,0,0,0,1,1,1,0,0,0,0,0,1},
{1,0,1,0,0,0,0,0,1,1,1,0,1},
{1,0,1,0,1,1,1,0,1,0,0,0,1},
{1,0,1,0,1,0,0,0,1,1,1,0,1},
{1,0,1,0,1,1,1,0,1,0,1,0,1},
{1,0,0,0,0,0,0,0,0,0,1,0,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1}
};
Ones this has been created the idea is to set a starting point and goal point and by using recursive depth first find the path. but must say i am having difficulties to create the maze.
Do you have any suggestion on how to do it?
Or perhaps a link to a tutorial?
The main focus for me right now is just to create the maze.
Maze implementation has a lot of variations.
All depends on which of there aspects you want to use?
Here is some start point Maze generation algorithm.
I tried to solve this problem in the past. Instead of many words how I tried this, I guess to show code snippet.
maze generator code:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Random;
public class MyMaze {
private int dimensionX, dimensionY; // dimension of maze
private int gridDimensionX, gridDimensionY; // dimension of output grid
private char[][] grid; // output grid
private Cell[][] cells; // 2d array of Cells
private Random random = new Random(); // The random object
// initialize with x and y the same
public MyMaze(int aDimension) {
// Initialize
this(aDimension, aDimension);
}
// constructor
public MyMaze(int xDimension, int yDimension) {
dimensionX = xDimension;
dimensionY = yDimension;
gridDimensionX = xDimension * 4 + 1;
gridDimensionY = yDimension * 2 + 1;
grid = new char[gridDimensionX][gridDimensionY];
init();
generateMaze();
}
private void init() {
// create cells
cells = new Cell[dimensionX][dimensionY];
for (int x = 0; x < dimensionX; x++) {
for (int y = 0; y < dimensionY; y++) {
cells[x][y] = new Cell(x, y, false); // create cell (see Cell constructor)
}
}
}
// inner class to represent a cell
private class Cell {
int x, y; // coordinates
// cells this cell is connected to
ArrayList<Cell> neighbors = new ArrayList<>();
// solver: if already used
boolean visited = false;
// solver: the Cell before this one in the path
Cell parent = null;
// solver: if used in last attempt to solve path
boolean inPath = false;
// solver: distance travelled this far
double travelled;
// solver: projected distance to end
double projectedDist;
// impassable cell
boolean wall = true;
// if true, has yet to be used in generation
boolean open = true;
// construct Cell at x, y
Cell(int x, int y) {
this(x, y, true);
}
// construct Cell at x, y and with whether it isWall
Cell(int x, int y, boolean isWall) {
this.x = x;
this.y = y;
this.wall = isWall;
}
// add a neighbor to this cell, and this cell as a neighbor to the other
void addNeighbor(Cell other) {
if (!this.neighbors.contains(other)) { // avoid duplicates
this.neighbors.add(other);
}
if (!other.neighbors.contains(this)) { // avoid duplicates
other.neighbors.add(this);
}
}
// used in updateGrid()
boolean isCellBelowNeighbor() {
return this.neighbors.contains(new Cell(this.x, this.y + 1));
}
// used in updateGrid()
boolean isCellRightNeighbor() {
return this.neighbors.contains(new Cell(this.x + 1, this.y));
}
// useful Cell representation
#Override
public String toString() {
return String.format("Cell(%s, %s)", x, y);
}
// useful Cell equivalence
#Override
public boolean equals(Object other) {
if (!(other instanceof Cell)) return false;
Cell otherCell = (Cell) other;
return (this.x == otherCell.x && this.y == otherCell.y);
}
// should be overridden with equals
#Override
public int hashCode() {
// random hash code method designed to be usually unique
return this.x + this.y * 256;
}
}
// generate from upper left (In computing the y increases down often)
private void generateMaze() {
generateMaze(0, 0);
}
// generate the maze from coordinates x, y
private void generateMaze(int x, int y) {
generateMaze(getCell(x, y)); // generate from Cell
}
private void generateMaze(Cell startAt) {
// don't generate from cell not there
if (startAt == null) return;
startAt.open = false; // indicate cell closed for generation
ArrayList<Cell> cells = new ArrayList<>();
cells.add(startAt);
while (!cells.isEmpty()) {
Cell cell;
// this is to reduce but not completely eliminate the number
// of long twisting halls with short easy to detect branches
// which results in easy mazes
if (random.nextInt(10)==0)
cell = cells.remove(random.nextInt(cells.size()));
else cell = cells.remove(cells.size() - 1);
// for collection
ArrayList<Cell> neighbors = new ArrayList<>();
// cells that could potentially be neighbors
Cell[] potentialNeighbors = new Cell[]{
getCell(cell.x + 1, cell.y),
getCell(cell.x, cell.y + 1),
getCell(cell.x - 1, cell.y),
getCell(cell.x, cell.y - 1)
};
for (Cell other : potentialNeighbors) {
// skip if outside, is a wall or is not opened
if (other==null || other.wall || !other.open) continue;
neighbors.add(other);
}
if (neighbors.isEmpty()) continue;
// get random cell
Cell selected = neighbors.get(random.nextInt(neighbors.size()));
// add as neighbor
selected.open = false; // indicate cell closed for generation
cell.addNeighbor(selected);
cells.add(cell);
cells.add(selected);
}
}
// used to get a Cell at x, y; returns null out of bounds
public Cell getCell(int x, int y) {
try {
return cells[x][y];
} catch (ArrayIndexOutOfBoundsException e) { // catch out of bounds
return null;
}
}
public void solve() {
// default solve top left to bottom right
this.solve(0, 0, dimensionX - 1, dimensionY -1);
}
// solve the maze starting from the start state (A-star algorithm)
public void solve(int startX, int startY, int endX, int endY) {
// re initialize cells for path finding
for (Cell[] cellrow : this.cells) {
for (Cell cell : cellrow) {
cell.parent = null;
cell.visited = false;
cell.inPath = false;
cell.travelled = 0;
cell.projectedDist = -1;
}
}
// cells still being considered
ArrayList<Cell> openCells = new ArrayList<>();
// cell being considered
Cell endCell = getCell(endX, endY);
if (endCell == null) return; // quit if end out of bounds
{ // anonymous block to delete start, because not used later
Cell start = getCell(startX, startY);
if (start == null) return; // quit if start out of bounds
start.projectedDist = getProjectedDistance(start, 0, endCell);
start.visited = true;
openCells.add(start);
}
boolean solving = true;
while (solving) {
if (openCells.isEmpty()) return; // quit, no path
// sort openCells according to least projected distance
Collections.sort(openCells, new Comparator<Cell>(){
#Override
public int compare(Cell cell1, Cell cell2) {
double diff = cell1.projectedDist - cell2.projectedDist;
if (diff > 0) return 1;
else if (diff < 0) return -1;
else return 0;
}
});
Cell current = openCells.remove(0); // pop cell least projectedDist
if (current == endCell) break; // at end
for (Cell neighbor : current.neighbors) {
double projDist = getProjectedDistance(neighbor,
current.travelled + 1, endCell);
if (!neighbor.visited || // not visited yet
projDist < neighbor.projectedDist) { // better path
neighbor.parent = current;
neighbor.visited = true;
neighbor.projectedDist = projDist;
neighbor.travelled = current.travelled + 1;
if (!openCells.contains(neighbor))
openCells.add(neighbor);
}
}
}
// create path from end to beginning
Cell backtracking = endCell;
backtracking.inPath = true;
while (backtracking.parent != null) {
backtracking = backtracking.parent;
backtracking.inPath = true;
}
}
// get the projected distance
// (A star algorithm consistent)
public double getProjectedDistance(Cell current, double travelled, Cell end) {
return travelled + Math.abs(current.x - end.x) +
Math.abs(current.y - current.x);
}
// draw the maze
public void updateGrid() {
char backChar = ' ', wallChar = 'X', cellChar = ' ', pathChar = '*';
// fill background
for (int x = 0; x < gridDimensionX; x ++) {
for (int y = 0; y < gridDimensionY; y ++) {
grid[x][y] = backChar;
}
}
// build walls
for (int x = 0; x < gridDimensionX; x ++) {
for (int y = 0; y < gridDimensionY; y ++) {
if (x % 4 == 0 || y % 2 == 0)
grid[x][y] = wallChar;
}
}
// make meaningful representation
for (int x = 0; x < dimensionX; x++) {
for (int y = 0; y < dimensionY; y++) {
Cell current = getCell(x, y);
int gridX = x * 4 + 2, gridY = y * 2 + 1;
if (current.inPath) {
grid[gridX][gridY] = pathChar;
if (current.isCellBelowNeighbor())
if (getCell(x, y + 1).inPath) {
grid[gridX][gridY + 1] = pathChar;
grid[gridX + 1][gridY + 1] = backChar;
grid[gridX - 1][gridY + 1] = backChar;
} else {
grid[gridX][gridY + 1] = cellChar;
grid[gridX + 1][gridY + 1] = backChar;
grid[gridX - 1][gridY + 1] = backChar;
}
if (current.isCellRightNeighbor())
if (getCell(x + 1, y).inPath) {
grid[gridX + 2][gridY] = pathChar;
grid[gridX + 1][gridY] = pathChar;
grid[gridX + 3][gridY] = pathChar;
} else {
grid[gridX + 2][gridY] = cellChar;
grid[gridX + 1][gridY] = cellChar;
grid[gridX + 3][gridY] = cellChar;
}
} else {
grid[gridX][gridY] = cellChar;
if (current.isCellBelowNeighbor()) {
grid[gridX][gridY + 1] = cellChar;
grid[gridX + 1][gridY + 1] = backChar;
grid[gridX - 1][gridY + 1] = backChar;
}
if (current.isCellRightNeighbor()) {
grid[gridX + 2][gridY] = cellChar;
grid[gridX + 1][gridY] = cellChar;
grid[gridX + 3][gridY] = cellChar;
}
}
}
}
}
// simply prints the map
public void draw() {
System.out.print(this);
}
// forms a meaningful representation
#Override
public String toString() {
updateGrid();
String output = "";
for (int y = 0; y < gridDimensionY; y++) {
for (int x = 0; x < gridDimensionX; x++) {
output += grid[x][y];
}
output += "\n";
}
return output;
}
// run it
public static void main(String[] args) {
MyMaze maze = new MyMaze(20);
maze.solve();
maze.draw();
}
}
It isn't the best solution, my task at this time was implement this algorithm by myself. It has clear comments.
Output:
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
X * X ********* X ***** X X X
X * X * XXXXX * X * X * X X X X
X ***** X ***** X * X * X X X X
XXXXXXXXX * XXXXX * X * X X X X
X X ***** X * X * X X X
X X XXXXX * X * X * XXXXXXXXX X
X X X ***** X * X
X XXXXXXXXXXXXXXXXX * XXXXXXXXXXXXX
X ***************** X ***** X X
X * XXXXXXXXXXXXX * XXXXX * X X X
X ***** X X ********* X X X
XXXXX * X XXXXXXXXXXXXXXXXXXXXX X
X ***** X ***** X ***** X
X * XXXXXXXXXXXXX * X * XXXXX * X * X
X ************* X * X * X ***** X * X
XXXXXXXXXXXXX * X * X * X * XXXXX * X
X ***** X ***** X * X
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
I hope it will be useful as an illustration of some solution.
I know this is probably completely outdated, but...
First, you should realize that the underlying structure of such a maze is an undirected graph on a 2-dimensional grid. Now to create a so called "perfect maze", you just have to create any spanning tree of a full grid graph. And to do that there are plenty of algorithms, from random graph traversals (BFS, DFS) over algorithms derived from the known minimum-spanning tree algorithms (Kruskal, Prim, Boruvka, Reverse-Delete) to algorithms creating "uniformly random" spanning trees (Wilson, Aldous-Broder) to other algorithms that don't fit into these categories like "recursive division", "Eller's" etc.
I implemented lots of these algorithms based on a grid graph structure and you can find my implementation here:
https://github.com/armin-reichert/mazes
If I understand your question correctly, what I would do is:
1. create a board of a specific size (change all the coordinates to your desired number - in your example '1').
I wouldn't use a recursive function, because you will probably end up drawing the whole board (think about what will make the recursion stop).
you can create a function that receives a starting coordination, an ending coordination,
and the array (the board).
pseudo code of the function:
set a variable for the next direction of painting (set it to the starting coordination).
paint the next coordination 0.
while the next coordination != to the ending coordination:
paint the next coordination 0.
use Random to set the coordination to one of the 4 directions.
you should add limits (if the next coordination is a painted one/the border of the maze etc... chose a different coordination).
good luck!
I am currently creating a maze using a pair of boolean array (horizontal and vertical) in order to draw lines for the maze.
The maze only every displays 5 bools from the array at one time. Then, I have an user who is always centered and as he moves through the maze the next set of bools are drawn. This is working as it should.
The issue that I am having is: when the user moves to a certain part of the maze the for loop drawing the lines becomes higher than the bool array and therefore crashes the app. Please find below some code snippets.
The onDraw:
protected void onDraw(Canvas canvas) {
canvas.drawRect(0, 0, width, height, background);
int currentX = maze.getCurrentX(),currentY = maze.getCurrentY();
int drawSizeX = 6 + currentX;
int drawSizeY = 6 + currentY;
currentX = currentX - 2;
currentY = currentY - 2;
for(int i = 0; i < drawSizeX - 1; i++) {
for(int j = 0; j < drawSizeY - 1; j++) {
float x = j * totalCellWidth;
float y = i * totalCellHeight;
if(vLines[i + currentY][j + currentX]) {
canvas.drawLine(x + cellWidth, //start X
y, //start Y
x + cellWidth, //stop X
y + cellHeight, //stop Y
line);
}
if(hLines[i + currentY][j + currentX]) {
canvas.drawLine(x, //startX
y + cellHeight, //startY
x + cellWidth, //stopX
y + cellHeight, //stopY
line);
}
}
//draw the user ball
canvas.drawCircle((2 * totalCellWidth)+(cellWidth/2), //x of center
(2 * totalCellHeight)+(cellWidth/2), //y of center
(cellWidth*0.45f), //radius
ball);
}
EDIT 1 - The Move -
public boolean move(int direction) {
boolean moved = false;
if(direction == UP) {
if(currentY != 0 && !horizontalLines[currentY-1][currentX]) {
currentY--;
moved = true;
}
}
if(direction == DOWN) {
if(currentY != sizeY-1 && !horizontalLines[currentY][currentX]) {
currentY++;
moved = true;
}
}
if(direction == RIGHT) {
if(currentX != sizeX-1 && !verticalLines[currentY][currentX]) {
currentX++;
moved = true;
}
}
if(direction == LEFT) {
if(currentX != 0 && !verticalLines[currentY][currentX-1]) {
currentX--;
moved = true;
}
}
if(moved) {
if(currentX == finalX && currentY == finalY) {
gameComplete = true;
}
}
return moved;
}
If there is anything else that I need to clarify please let me know.
Thanks in advance.
drawSizeX/Y indexes over the array when currentX/Y is high enough (length-6)
So limit the values to Math.min(current + 6, array.length)