Question
How can i compare two adjacent cells thanks to them coordinates?
Documentation which helped me
I already saw these questions, they helped me but they are different from my case:
question on stackOverflow
question on stackOverflow
question on stackOverflow
Mds documentation to build a dynamic table
Code
I've a dynamically generated table
function tableGenerate(Mytable){
for(var i = 0; i < myTable.length; i++) {
var innerArrayLength = myTable[i].length;
for(var j = 0; j<innerArrayLength; j++){
if(myTable[i][j] === 0){
myTable[i][j]="x";
}else{
myTable[i][j]="y";
};
};
$("#aTable").append("<tr><td>"+ myTable[i].join('</td><td>') + "</td></tr>")
}
}
About the interested cells (two global variables) in actualPosition row and cell have random values
var mainTd = {
name: 'interestedValue',
actualPosition:{
row: 5,
cell: 4
}
};
var otherMainTd = {
actualPosition:{
row: 2,
cell: 3
}
};
The final part of the code works in this way:
I save the position of selectedTd in two differents variables
I create the 2d array directions with the coordinates of near cells relatives to the selectedTd
enter in the first if, compare the two cells. If one of the coordinates are the same, you enter in this last if.
function compare(selectedTd) {
let tdRow = selectedTd.actualPosition.row;
let tdCell = selectedTd.actualPosition.cell;
let directions = [
[tdRow - 1, tdCell],
[tdRow + 1, tdCell],
[tdRow, tdCell + 1],
[tdRow, tdCell - 1]
]; //these are the TD near the mainTd, the one i need to compare to the others
let tdToCompare = [];
if (selectedTd.name === 'interestedValue') {
tdToCompare = [otherMainTd.actualPosition.row, otherMainTd.actualPosition.cell];
for (let i = 0; i < directions.length; i++) {
if (directions[i] == tdToCompare) {
console.log('you are here');
}
}
} else {
tdToCompare = [mainTd.actualPosition.row, mainTd.actualPosition.cell];
for (let i = 0; i < directions.length; i++) {
if (directions[i] === tdToCompare) {
console.log('you are here');
}
}
}
};
Now the main problem is: I read the coordinates, I store them in the 2 arrays, I can read them but I cannot able to enter in the if statement.
This is what I want to achieve: compare the coordinates of the blackTd with the coordinates of the red-borders td.
Codepen
the interested functions in the codepen are with different names, but the structure is the same that you saw in this post. I changed the original names because I think it could be more clear with general names instead of the names that i choose.
the interested functions are:
function fight(playerInFight) ---> function compare(selectedTd)
function mapGenerate(map) ---> function tableGenerate(MyTable)
mainTd and otherMainTd ---> character and characterTwo
CodepenHere
Update:
Reading your code again I think I figured out the problem. You're comparing array instances instead of their actual values. See this simple example to illustrate the issue:
var a = [1];
var b = [1];
console.log(a===b);
What you'd need to do in your code is this:
if (selectedTd.name === 'interestedValue') {
tdToCompare = [otherMainTd.actualPosition.row, otherMainTd.actualPosition.cell];
for (let i = 0; i < directions.length; i++) {
if (
directions[i][0] === tdToCompare[0] &&
directions[i][1] === tdToCompare[1]
) {
console.log('you are here');
}
}
} else {
tdToCompare = [mainTd.actualPosition.row, mainTd.actualPosition.cell];
for (let i = 0; i < directions.length; i++) {
if (
directions[i][0] === tdToCompare[0] &&
directions[i][1] === tdToCompare[1]
) {
console.log('you are here');
}
}
}
Now it checks if the values, and thus the cells, are matching.
Recommendations:
If I were you, I would write the method a bit different. Below is how I would do it.
function compare(selectedTd) {
const
// Use destructuring assignemnt to get the row and cell. Since these are
// values that won't be changed in the method declare them as "const". Also
// drop the "td" prefix, it doesn't add anything useful to the name.
// See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
{ row, cell } = selectedTd.actualPosition,
// Directions can also be a const, it will not be reassigned in the method.
directions = [
[row - 1, cell],
[row + 1, cell],
[row, cell + 1],
[row, cell - 1]
],
// A few things happens in this line:
// - It is a destructuring assignment where the names are changed. In this case
// row and cell are already assigned so it is necessary to give them another name.
// - Don't put the row and cell in an array. You will have to access the actual values
// anyway as you can't compare the array instances.
// - Instead of doing this in the if…else you had, decide here which cell you want to
// look for. It means the rest of the method can be written without wrapping any
// logic in an if…else making it less complex.
{ row: referenceRow, cell: referenceCell } = (selectedTd.name === 'interestedValue')
? otherMainTd.actualPosition
: mainTd.actualPosition,
// Use find instead of a for loop. The find will stop as soon as it finds a match. The
// for loop you had kept evaluating direction items even if the first one was already
// a match.
// The "([row,cell])" is the signature of the callback method for the find. This too is
// a destructuring assignment only this time with one of the arrays of the directions
// array. The first array item will be named "row" and the second item "cell". These
// variable names don't clash with those declared at the top of this method as this
// is a new scope.
// The current directions entry is a match when the row and cell values match.
matchingNeighbor = directions.find(([row, cell]) => row === referenceRow && cell === referenceCell);
// "find" returns undefined when no match was found. So when match is NOT unddefined
// it means directions contained the cell you were looking for.
if (matchingNeighbor !== undefined) {
console.log('you are here');
}
};
const
mainTd = {
name: 'interestedValue',
actualPosition: {
cell: 1,
row: 1
}
},
otherMainTd = {
actualPosition: {
cell: 0,
row: 1
}
};
compare(mainTd);
Orginal answer:
There is quite a bit going on in your question, I hope I understood it properly.
What I've done is create a Grid, you pass this the dimensions and it will create the array for each cell in the grid. Then it returns an object with some methods you can use to interact with the grid. It has the following methods:
cellAtCoordinate: Pass it an X and Y coordinate and it returns the cell.
isSameLocation: Pass it two cells and it checks if the cells are in the same location.
neighborsForCoordinate: Pass it an X and Y coordinate and it returns an array with the cells above, below, to the right, and to the left (if they exist).
With all of this out of the way the compare method becomes a little more manageable. Getting the neighbours is now just a single call, the same for the check if two cells match.
Like I said, I hope this is what you were trying to achieve. If I got the problem wrong and something needs some further explaining, please let me know.
/**
* Creates grid with the provided dimensions. The cell at the top left corner
* is at coordinate (0,0). The method returns an object with the following
* three methods:
* - cellAtCoordinate
* - isSameLocation
* - neighborsForCoordinate
*/
function Grid(width, height) {
if (width === 0 || height === 0) {
throw 'Invalid grid size';
}
const
// Create an array, each item will represent a cell. The cells in the
// array are laid out per row.
cells = Array.from(Array(width * height), (value, index) => ({
x: index % width,
y: Math.floor(index / height)
}));
function cellAtCoordinate(x, y) {
// Make sure we don't consider invalid coordinate
if (x >= width || y >= height || x < 0 || y < 0) {
return null;
}
// To get the cell at the coordinate we need to calculate the Y offset
// by multiplying the Y coordinate with the width, these are the cells
// to "skip" in order to get to the right row.
return cells[(y * width) + x];
}
function isSameLocation(cellA, cellB) {
return (
cellA.x === cellB.x &&
cellA.y === cellB.y
);
}
function neighborsForCoordinate(x, y) {
// Make sure we don't consider invalid coordinate
if (x >= width || y >= height || x < 0 || y < 0) {
return null;
}
const
result = [];
// Check if there is a cell above.
if (y > 0) result.push(cellAtCoordinate(x, y - 1));
// Check if there is a cel to the right
if (x < width) result.push(cellAtCoordinate(x + 1, y));
// Check if there is a cell below.
if (y < height) result.push(cellAtCoordinate(x, y + 1));
// Check if there is a cell to the left.
if (x > 0) result.push(cellAtCoordinate(x - 1, y));
return result;
}
return {
cellAtCoordinate,
isSameLocation,
neighborsForCoordinate
}
}
function compareCells(grid, selectedCell) {
const
// Get the neighbors for the selected cell.
neighbors = grid.neighborsForCoordinate(selectedCell.x, selectedCell.y);
compareAgainst = (selectedCell.name === 'interestedValue')
? otherMainTd
: mainTd;
// In the neighbors, find the cell with the same location as the cell
// we want to find.
const
match = neighbors.find(neighbor => grid.isSameLocation(neighbor, compareAgainst));
// When match is NOT undefined it means the compareAgainst cell is
// a neighbor of the selected cell.
if (match !== undefined) {
console.log(`You are there at (${match.x},${match.y})`);
} else {
console.log('You are not there yet');
}
}
// Create a grid which is 3 by 3.
const
myGrid = Grid(3, 3),
// Place the main TD here:
// - | X | -
// - | - | -
// - | - | -
mainTd = {
name: 'interestedValue',
x: 1,
y: 0
},
// Place the other TD here:
// - | - | -
// Y | - | -
// - | - | -
otherMainTd = {
x: 0,
y: 1
};
// Check if the mainTd is in a cell next to the otherMainTd. It is not
// as the neighboring cells are:
// N | X | N
// Y | N | -
// - | - | -
compareCells(myGrid, mainTd);
// Move the mainTd to the center of the grid
// - | - | -
// Y | X | -
// - | - | -
mainTd.y = 1;
// Compare again, now the main TD is next the the other.
// - | N | -
// YN | X | N
// - | N | -
compareCells(myGrid, mainTd);
Related
I am trying to make a sudoku generator on a 4 by 4 grid in JavaScript.
So I declare a matrix of objects such as {number: 0, cbd: 1}, which represent the number that is going to be on the cell, and a state variable that controls if that cell can later be dug (cbd):
var matrix = new Array(4).fill({number: 0, cbd: 1}).map(() => new Array(4).fill({number: 0, cbd: 1}));
I then used this function to fill the grid, respecting all the 3 sudoku restraints: no repetitions on row, columns and square:
fillmatrix4(matrix,tam){
var numberList = [...Array(4+1).keys()].slice(1); //[1,2,3,4]
for(var k = 0; k < 4**2; k++){
var row = Math.floor(k/4);
var col = k%4;
if (matrix[row][col].number == 0){
numberList.sort(() => Math.random() - 0.5); //shuffles the array
for(var q = 0; q < numberList.length; q++){
var ids = matrix[row].map(a => a.number); //creates an array with the number property from all cells in that row
if (!(ids.includes(numberList[q]))){ //check if number is in row
if ((matrix[0][col].number != numberList[q]) && (matrix[1][col].number != numberList[q]) && (matrix[2][col].number != numberList[q]) && (matrix[3][col].number != numberList[q])){ //check if number is in column
var square = [];
if (row<2){It is used to find out what subgrid we are in
if (col<2){
square = [matrix[0][0].number, matrix[0][1].number, matrix[1][0].number, matrix[1][1].number];
} else {
square = [matrix[0][2].number, matrix[0][3].number, matrix[1][2].number, matrix[1][3].number];
}
} else {
if (col<2){
square = [matrix[2][0].number, matrix[2][1].number, matrix[3][0].number, matrix[3][1].number];
} else {
square = [matrix[2][2].number, matrix[2][3].number, matrix[3][2].number, matrix[3][3].number];
}
}
if (!(square.includes(numberList[q]))){ //check if number is that subgrid
matrix[row][col].number = numberList[q]; //if number not in row, column and square, adds it to matrix
if (this.checkGrid(matrix,tam)){ //returns true all matrix is filled
return matrix;
}
}
}
}
}
}
}
I know for a fact that this function works. I tested it on a matrix of integers, but I need that each individually cell has that variable cbd.
But now, with the matrix of objects above declared, it's not "shuffling". The result is always a grid filled like this:
a | a | a | a
b | b | b | b
c | c | c | c
d | d | d | d
where a,b,c,d are numbers from 1 to 4.
What might be the reason for this?
When you do:
Array(4).fill({number: 0, cbd: 1})
this fills every entry with a reference to the same object in memory. Graphically, it looks like:
.-----------.
| number: 0 |
| cbd: 1 |
`-----------`
^ ^ ^ ^
| | | |
variable `matrix`: [0, 1, 2, 3]
Changes made on any of these array indices will mutate the same underlying object.
Here's a minimal, complete, reproducible example:
const arr = Array(4).fill({foo: "bar"});
arr[0].foo = "baz";
console.log(JSON.stringify(arr, null, 2));
console.log(arr);
You can see all 4 entries reflect the change made to the first object. Note that the stack snippet console.log of the array shows "ref" to an object id for 3 of the 4 buckets.
To fix the problem, use .map to generate distinct objects:
const arr = Array(4).fill().map(() => ({foo: "bar"}));
arr[0].foo = "baz";
console.log(JSON.stringify(arr, null, 2));
console.log(arr);
Or, in your code:
const matrix = Array(4).fill().map(() => ({number: 0, cbd: 1}))
.map(() => Array(4).fill().map(() => ({number: 0, cbd: 1})));
console.log(matrix);
(the new is unnecessary).
As an aside, try to avoid deeply nested blocks. Consider writing helper functions to handle some of this logic and make the code easier to follow.
Also, if you're looking for good shuffling in JS, I recommend looking into the Fisher-Yates shuffle.
var image = new SimpleImage("lena.png");
var col = [];
var uniqcol = [];
for (var px of image.values()){
col.push([px.getRed,px.getGreen,px.getBlue]);
if(uniqcol.includes([px.getRed +- 1, px.getGreen +- 1, px.getBlue +- 1]) ){
print('not unique');
}else{
uniqcol.push([px.getRed,px.getGreen,px.getBlue]);
}
}
I would like to count the number of unique pixels within an image. A unique pixel being one which RGB values are not within 1 to anothers pixels. I have the above code but it does not work. I think the issue that I am having is with checking that the RGB values are either +1 or -1 from the selected pixel px value. If a unique pixel is found, id like to add to the the uniqcol array. Is there any other way to count the unique pixels, or a way to check that the RGB values are within 1 from the selected px value?
Thanks.
This tests each component to see if it's within 1 by subtracting the two, taking the absolute value, and checking if it's less than 2.
This is probably super inefficient. For each pixel you're iterating a potentially massive array until you get a match, or worst case, you don't find a match.
var image = new SimpleImage("lena.png");
var col = [];
var uniqcol = [];
for (var px of image.values()){
var found = uniqcol.find(function (el) {
return
Math.abs(el[0] - px.getRed) < 2 &&
Math.abs(el[1] - px.getGreen) < 2 &&
Math.abs(el[2] - px.getBlue) < 2;
});
if (!found) {
uniqcol.push([px.getRed,px.getGreen,px.getBlue]);
} else {
print('not unique');
}
}
Here's another approach that uses memoization. It should be a lot faster at the expense of storing a separate lookup structure.
Edit - I deleted this approach because it can fail. It's probably possible to do but quite tricky.
You need to check for all the different pixel values, putting +- will not match a range of values. .includes() looks for exact matches.
for (var px of image.values()) {
col.push([px.getRed,px.getGreen,px.getBlue]);
var found = false;
for (dRed of [-1, 0, +1]) {
for (dGreen of [-1, 0, +1]) {
for (dBlue of [-1, 0, +1]) {
if (uniqcol.includes([px.getRed + dRed, px.getGreen + dGreen, px.getBlue + dBlue]) {
found = true;
print("not unique");
break;
}
}
if (found) {
break;
}
if (found) {
break;
}
}
if (!found) {
uniqcol.push([px.getRed,px.getGreen,px.getBlue]);
}
}
This is probably not a very efficient way to do it, since it will search the entire image 9 times for each pixel. It would probably be better to loop through all the pixels, testing if all the colors are within a range of the current pixel:
if (px.getRed >= curPixel.getRed - 1 && px.getRed <= curPixel.getRed + 1 &&
px.getGreen >= curPixel.getGreen - 1 && px.getGreen <= curPixel.getGreen + 1 &&
px.getBlue >= curPixel.getBlue - 1 && px.getBlue <= curPixel.getBlue + 1)
A really efficient algorithm would involve sorting all the pixels (nested arrays of red, blue, and green values would be a good structure), then searching this. But that's more a topic for CodeReview.stackexchange.com.
I have a 2D array, something like the following:
[1.11, 23]
[2.22, 52]
[3.33, 61]
...
Where the array is ordered by the first value in each row.
I am trying to find a value within the array that is close to the search value - within a certain sensitivity. The way this is set up, and the value of the sensitivity, ensure only one possible match within the array.
The search value is the current x-pos of the mouse. The search is called on mousemove, and so is being called often.
Originally I had the following (using a start-to-end for loop):
for(var i = 0; i < arr.length; i++){
if(Math.abs(arr[i][0] - x) <= sensitivity){
hit = true;
break;
}
}
And it works like a charm. So far, I've only been using small data sets, so there is no apparent lag using this method. But, eventually I will be using much larger data sets, and so want to switch this to a Binary Search:
var a = 0;
var b = arr.length - 1;
var c = 0;
while(a < b){
c = Math.floor((a + b) / 2);
if(Math.abs(arr[c][0] - x) <= sensitivity){
hit = true;
break;
}else if(arr[c][0] < x){
a = c;
}else{
b = c;
}
}
This works well, for all of 2 seconds, and then it hangs to the point where I need to restart my browser. I've used binary searches plenty in the past, and cannot for the life of me figure out why this one isn't working properly.
EDIT 1
var sensitivity = (width / arr.length) / 2.001
The points in the array are equidistant, and so this sensitivity ensures that there is no ambiguous 1/2-way point in between two arr values. You are either in one or the other.
Values are created dynamically at page load, but look exactly like what I've mentioned above. The x-values have more significant figures, and the y values are all over the place, but there is no significant difference between the small sample I provided and the generated one.
EDIT 2
Printed a list that was dynamically created:
[111.19999999999999, 358.8733333333333]
[131.4181818181818, 408.01333333333326]
[151.63636363636363, 249.25333333333327]
[171.85454545454544, 261.01333333333326]
[192.07272727272726, 298.39333333333326]
[212.29090909090908, 254.2933333333333]
[232.5090909090909, 308.47333333333324]
[252.72727272727272, 331.1533333333333]
[272.94545454545454, 386.1733333333333]
[293.16363636363633, 384.9133333333333]
[313.3818181818182, 224.05333333333328]
[333.6, 284.53333333333325]
[353.81818181818187, 278.2333333333333]
[374.0363636363637, 391.63333333333327]
[394.25454545454556, 322.33333333333326]
[414.4727272727274, 300.9133333333333]
[434.69090909090926, 452.95333333333326]
[454.9090909090911, 327.7933333333333]
[475.12727272727295, 394.9933333333332]
[495.3454545454548, 451.27333333333326]
[515.5636363636366, 350.89333333333326]
[535.7818181818185, 308.47333333333324]
[556.0000000000003, 395.83333333333326]
[576.2181818181822, 341.23333333333323]
[596.436363636364, 371.47333333333324]
[616.6545454545459, 436.9933333333333]
[636.8727272727277, 280.7533333333333]
[657.0909090909096, 395.4133333333333]
[677.3090909090914, 433.21333333333325]
[697.5272727272733, 355.09333333333325]
[717.7454545454551, 333.2533333333333]
[737.963636363637, 255.55333333333328]
[758.1818181818188, 204.7333333333333]
[778.4000000000007, 199.69333333333327]
[798.6181818181825, 202.63333333333327]
[818.8363636363644, 253.87333333333328]
[839.0545454545462, 410.5333333333333]
[859.272727272728, 345.85333333333324]
[879.4909090909099, 305.11333333333323]
[899.7090909090917, 337.8733333333333]
[919.9272727272736, 351.3133333333333]
[940.1454545454554, 324.01333333333326]
[960.3636363636373, 331.57333333333327]
[980.5818181818191, 447.4933333333333]
[1000.800000000001, 432.3733333333333]
As you can see, it is ordered by the first value in each row, ascending.
SOLUTION
Changing the condition to
while(a < b)
and
var b = positions.length;
and
else if(arr[c][0] < x){
a = c + 1;
}
did the trick.
Your binary search seems to be a bit off: try this.
var arr = [[1,0],[3,0],[5,0]];
var lo = 0;
var hi = arr.length;
var x = 5;
var sensitivity = 0.1;
while (lo < hi) {
var c = Math.floor((lo + hi) / 2);
if (Math.abs(arr[c][0] - x) <= sensitivity) {
hit = true;
console.log("FOUND " + c);
break;
} else if (x > arr[c][0]) {
lo = c + 1;
} else {
hi = c;
}
}
This is meant as a general reference to anyone implementing binary search.
Let:
lo be the smallest index that may possibly contain your value,
hi be one more than the largest index that may contain your value
If these conventions are followed, then binary search is simply:
while (lo < hi) {
var mid = (lo + hi) / 2;
if (query == ary[mid]) {
// do stuff
else if (query < ary[mid]) {
// query is smaller than mid
// so query can be anywhere between lo and (mid - 1)
// the upper bound should be adjusted
hi = mid;
else {
// query can be anywhere between (mid + 1) and hi.
// adjust the lower bound
lo = mid + 1;
}
I don't know your exact situation, but here's a way the code could crash:
1) Start with an array with two X values. This array will have a length of 2, so a = 0, b = 1, c = 0.
2) a < b, so the while loop executes.
3) c = floor((a + b) / 2) = floor(0.5) = 0.
4) Assume the mouse is not within sensitivity of the first X value, so the first if branch does not hit.
5) Assume our X values are to the right of our mouse, so the second if branch enters. This sets a = c, or 0, which it already is.
6) Thus, we get an endless loop.
I got this as an assignment, but I'm stuck on implementing the area portion. The assignment is already turned in (finished about 80%). I still want to know how to implement this, though. I am stuck on line 84-86. Here is the prompt.
Prompt: Calculate the area of irregularly shaped polygons using JS
INPUT: a nested array of 3-6 coordinates represented as [x,y] (clockwise order)
OUTPUT: area calculated to 2 significant figures
Pseudocode:
loop the input and check for error cases:
a. array length < 3 or array length > 6
b. array is not empty
c. numbers within -10 and 10 range
loop to each inner array, and multiply x and y in the formula below:
sum_x_to_y = (X0 *Y1) + (X1* Y2)...X(n-1)* Yn
sum_y_to_x = (Y0 * X1) + (Y1-X2)...Y(n-1)* Xn
ex:
(0, -10) (7,-10) (0,-8) (0,-10)
| x | y |
| 0 |-10 |
| 7 |-10 |
| 0 |-8 |
| 0 |-10 |
sum_x_to_y = 0*-10 + 7*-8 + 0*-10 = -56
sum_y_to_x = -10*7 + -10*0 + -8*0 = -70
area = (sum_y_to_x - sum_x_to_y) / (2.00)
ex: area = -70 -(-56) = 57/2 = 7
return area.toPrecision(2) to have one sig fig
function PaddockArea(array_coords) {
var sum_x_to_y = 0;
var sum_y_to_x = 0;
var arr_size = array_coords.length;
if (arr_size === 0) {
//check for empty array
console.log("Invalid input. Coordinates cannot be empty.");
}
if (arr_size < 3 || arr_size > 7) {
//check input outside of 3-6 range
console.log("Input out of range.");
}
for (var i = 0; i < arr_size; i++) {
for (var j = 0; j < array_coords[i].length; j++) {
//test for inner coordinates -10 to 10 range
if (array_coords[i][j] < -10 || array_coords[i][j] > 10) {
console.log("Coordinates outside of -10 to 10 range.");
}
// I NEED TO IMPLEMENT TO calc for AREA here
sum_x_to_y += array_coords[i][j] * array_coords[j][i];
sum_y_to_x += array_coords[j][i] * array_coords[i][j];
var area = (sum_y_to_x - sum_x_to_y) / 2;
console.log(area.toPrecision(2) + "acres");
}
}
}
If you're just using Simpson's rule to calculate area, the following function will do the job. Just make sure the polygon is closed. If not, just repeat the first coordinate pair at the end.
This function uses a single array of values, assuming they are in pairs (even indexes are x, odd are y). It can be converted to using an array of arrays containing coordinate pairs.
The function doesn't do any out of bounds or other tests on the input values.
function areaFromCoords(coordArray) {
var x = coordArray,
a = 0;
// Must have even number of elements
if (x.length % 2) return;
// Process pairs, increment by 2 and stop at length - 2
for (var i=0, iLen=x.length-2; i<iLen; i+=2) {
a += x[i]*x[i+3] - x[i+2]*x[i+1];
}
return Math.abs(a/2);
}
console.log('Area: ' + areaFromCoords([1,1,3,1,3,3,1,3,1,1])); // 4
console.log('Area: ' + areaFromCoords([0,-10, 7,-10, 0,-8, 0,-10,])); // 7
Because you haven't posted actual code, I haven't input any of your examples. The sequence:
[[1,0],[1,1],[0,0],[0,1]]
is not a polygon, it's a Z shaped line, and even if converted to a unit polygon can't resolve to "7 acres" unless the units are not standard (e.g. 1 = 184 feet approximately).
I've been trying to implement a recursive backtracking maze generation algorithm in javascript. These were done after reading a great series of posts on the topic here
While the recursive version of the algorithm was a no brainer, the iterative equivalent has got me stumped.
I thought I understood the concept, but my implementation clearly produces incorrect results. I've been trying to pin down a bug that might be causing it, but I am beginning to believe that my problems are being caused by a failure in logic, but of course I am not seeing where.
My understanding of the iterative algorithm is as follows:
A stack is created holding representations of cell states.
Each representation holds the coordinates of that particular cell, and a list of directions to access adjacent cells.
While the stack isn't empty iterate through the directions on the top of the stack, testing adjacent cells.
If a valid cell is found place it at the top of the stack and continue with that cell.
Here is my recursive implementation ( note: keydown to step forward ): http://jsbin.com/urilan/14
And here is my iterative implementation ( once again, keydown to step forward ): http://jsbin.com/eyosij/2
Thanks for the help.
edit: I apologize if my question wasn't clear. I will try to further explain my problem.
When running the iterative solution various unexpected behaviors occur. First and foremost, the algorithm doesn't exhaust all available options before backtracking. Rather, it appears to be selecting cells at a random when there is one valid cell left. Overall however, the movement doesn't appear to be random.
var dirs = [ 'N', 'W', 'E', 'S' ];
var XD = { 'N': 0, 'S':0, 'E':1, 'W':-1 };
var YD = { 'N':-1, 'S':1, 'E':0, 'W': 0 };
function genMaze(){
var dirtemp = dirs.slice().slice(); //copies 'dirs' so its not overwritten or altered
var path = []; // stores path traveled.
var stack = [[0,0, shuffle(dirtemp)]]; //Stack of instances. Each subarray in 'stacks' represents a cell
//and its current state. That is, its coordinates, and which adjacent cells have been
//checked. Each time it checks an adjacent cell a direction value is popped from
//from the list
while ( stack.length > 0 ) {
var current = stack[stack.length-1]; // With each iteration focus is to be placed on the newest cell.
var x = current[0], y = current[1], d = current[2];
var sLen = stack.length; // For testing whether there is a newer cell in the stack than the current.
path.push([x,y]); // Store current coordinates in the path
while ( d.length > 0 ) {
if( stack.length != sLen ){ break;}// If there is a newer cell in stack, break and then continue with that cell
else {
var cd = d.pop();
var nx = x + XD[ cd ];
var ny = y + YD[ cd ];
if ( nx >= 0 && ny >= 0 && nx < w && ny < h && !cells[nx][ny] ){
dtemp = dirs.slice().slice();
cells[nx][ny] = 1;
stack.push( [ nx, ny, shuffle(dtemp) ] ); //add new cell to the stack with new list of directions.
// from here the code should break from the loop and start again with this latest addition being considered.
}
}
}
if (current[2].length === 0){stack.pop(); } //if all available directions have been tested, remove from stack
}
return path;
}
I hope that helps clear up the question for you. If it is still missing any substance please let me know.
Thanks again.
I'm not very good in javascript, but I try to implement your recursive code to iterative. You need to store For index on stack also. So code look like:
function genMaze(cx,cy) {
var dirtemp = dirs; //copies 'dirs' so its not overwritten
var path = []; // stores path traveled.
var stack = [[cx, cy, shuffle(dirtemp), 0]]; // we also need to store `for` indexer
while (stack.length > 0) {
var current = stack[stack.length - 1]; // With each iteration focus is to be placed on the newest cell.
var x = current[0], y = current[1], d = current[2], i = current[3];
if (i > d.length) {
stack.pop();
continue;
}
stack[stack.length - 1][3] = i + 1; // for next iteration
path.push([x, y]); // Store current coordinates in the path
cells[x][y] = 1;
var cd = d[i];
var nx = x + XD[cd];
var ny = y + YD[cd];
if (nx >= 0 && ny >= 0 && nx < w && ny < h && !cells[nx][ny]) {
dtemp = dirs;
stack.push([nx, ny, shuffle(dtemp), 0]);
}
}
return path;
}
Does this little code could also help ?
/**
Examples
var sum = tco(function(x, y) {
return y > 0 ? sum(x + 1, y - 1) :
y < 0 ? sum(x - 1, y + 1) :
x
})
sum(20, 100000) // => 100020
**/
function tco(f) {
var value, active = false, accumulated = []
return function accumulator() {
accumulated.push(arguments)
if (!active) {
active = true
while (accumulated.length) value = f.apply(this, accumulated.shift())
active = false
return value
}
}
}
Credits, explanations ans more infos are on github https://gist.github.com/1697037
Is has the benefit to not modifying your code, so it could be applied in other situations too. Hope that helps :)