Calculate If Number String Loops | Hex Grid Round World - javascript

So I have a hex grid that has a 100x100 grid... The Grid is recursive so when you scroll left past the '0,0' position it draws the '99,0' row etc... My issue is finding an algorithm that will let me calculate if I am looping back around...
Example:
96 - 97 - 98 - 99 - 0 - 1 - 2 - 3 - 4
Both of these have starting locations less then the ending location
If I was at row 2 and the screen panned to row 98:
2 to 98 WORKS (98) - 100 = -2 then -2-(2) = distance of 4
96 to 98 FAILS (98) - 100 = -2 then -2-(96) = distance of 98 (correct is 2)
Both of these have ending locations less then the starting location
However this doesn't work both directions... so we do this...
from row 98 to row 2:
98 to 2 WORKS (2) + 100 = 102 then 102-(98) = distance of 4
96 to 98 FAILS (96) + 100 = 196 then 196-(98) = distance of 98 (correct is 2)
As you can see I cant just say if start < end or start > end as the number loop screws that up. I need to detect when "Crossing the line" some how...
After Jonathan suggestions I realized that the client display and behind the scenes didn't have to line up. I have changed the engine so there are 2 hex values... one is the actual position like 98,2 0,0 2,1. The other is a literal position from the view port.
Looks like this:
Actual: 96 97 98 99 00 01 02 03 04 <= display in client & reference hexGridModel
Literal: -4 -3 -2 -1 00 01 02 03 04 <= use to calculate buffer position updates from camera
The literal is just from the "display" standpoint. I will need a fix later to see if we go passed -100 or +100 but this solved issue for now and map sizes are dynamic so will need later work on that
Now I just used this for any case.
var x, y = 0
x = this.buffer.coords.current.x - this.buffer.coords.last.x;
y = this.buffer.coords.current.y - this.buffer.coords.last.y;

I'd say it's not really possible without more information than you've given us. Take the example of moving from 2 to 98. You will not be able to tell whether the user moved from 2 -> 1 -> 0 -> 99 -> 98 or 2 -> 3 -> 4 -> ... -> 97 -> 98.
So the key to being able to determine this is knowing the direction in which the player or object is moving.
If you know that, you can say (assuming 0 is at the left boundary of the grid and 99 is at the right boundary of the grid):
if ((direction == LEFT && new_pos > old_pos) || (direction == RIGHT && new_pos < old_pos) {
// The "line" has been crossed.
} else {
// The "line" was not crossed.
}
If you also need to know the distance travelled, you can break it down like this:
GRID_SIZE = 100
if (direction == LEFT && new_pos > old_pos) {
distance = (GRID_SIZE - new_pos) + old_pos;
} else if (direction == RIGHT && new_pos < old_pos) {
distance = (GRID_SIZE - old_pos) + new_pos;
} else {
distance = abs(new_pos - old_pos)
}

Note: I posted the following before I saw the update to the question. It's what I guessed you might have meant in your original question. Maybe it'll still be helpful somehow.
I don't really understand the requirement as worded, so I'm guessing that it is "Find the shortest distance between two positions, allowing wrapping across the 99/0 boundary if it is shorter".
This function returns an object with three properties, the distance, the direction, and wrapAround (true/false). And I threw in a toString() for ease of testing it out.
function shortestDistance(startPosition, endPosition) {
var difference = startPosition > endPosition
? startPosition - endPosition
: endPosition - startPosition,
wrapped = difference > 50;
return {
distance : wrapped ? 100 - difference : difference,
direction : (startPosition < endPosition
? (wrapped ? "left" : "right")
: (wrapped ? "right" : "left")),
wrappedAround : wrapped,
toString : function() {
return (
this.distance === 0
? "Didn't move"
: "Travelled " + this.distance
+ " to the " + this.direction + ", and "
+ (this.wrappedAround ? "did" : "didn't")
+ " wrap around.");
}
};
}
var result = shortestDistance(2,98);
alert(result.distance); // 4
alert(result.direction); // "left"
alert(result.wrappedAround); // true
alert(shortestDistance(2,98));
// Travelled 4 to the left, and did wrap around.
alert(shortestDistance(98,2));
// Travelled 4 to the right, and did wrap around.
alert(shortestDistance(96,98));
// Travelled 2 to the right, and didn't wrap around.
alert(shortestDistance(98,96));
// Travelled 2 to the left, and didn't wrap around.
alert(shortestDistance(43, 43));
// Didn't move
alert(shortestDistance(1, 50));
// Travelled 49 to the right, and didn't wrap around.
alert(shortestDistance(1, 51));
// Travelled 50 to the right, and didn't wrap around.
alert(shortestDistance(1, 52));
// Travelled 49 to the left, and did wrap around.
alert(shortestDistance(50, 1));
// Travelled 49 to the left, and didn't wrap around.
alert(shortestDistance(51, 1));
// Travelled 50 to the left, and didn't wrap around.
alert(shortestDistance(52, 1));
// Travelled 49 to the right, and did wrap around.

Related

Algorithm to convert a 2d voxel map to line sometimes gets stuck

I have a 2D voxel map for a game, which is a 2D array where 1 means ground and 0 means sky.
Example: all 1's in the array (ground) are green boxes
The algorithm starts at the leftmost ground voxel that touches the sky (red box in picture).
It will explore 8 neighbours of the current position to check if one of them is a ground voxel and also touches a sky voxel. This means it should be added to the groundline.
Example of the algorithm working (it's able to go in 'caves' too)
On this map it figured it out and returned a line across the ground.
In some situations it suddenly stops though, like on this map:
After about 10 loops it stopped creating the line.
Here's the code, with some explanatory comments in there:
voxelToLine() {
let voxels = this.voxels.length,//this.voxels is the 2d array
lineGround = [],
checkedVoxels = [],
nowChecking,
toCheck = [],
otherPaths = [],
done = false;
for (let y = 1; y < voxels - 1; y++)//sets first coordinate for line
if (this.voxels[0][y] && (!this.voxels[0][y - 1] || !this.voxels[1][y] || !this.voxels[0][y + 1])) {
lineGround[0] = [0, y / voxels];
nowChecking = [1, y];//search starts from this point
}
let looped = 0;
while (!done) {//continues search untill right side is located, or it got stuk (max 10*voxelmap width loops)
toCheck = nowChecking.neighbours(8, (n) => n[0] > 0 && n[0] < voxels - 1);//gets 8 neighbour points around current point, neighbours between 1 and (voxelwidth -1) get returned
let foundNew = false;
for (let i = 0; i < toCheck.length; i++) {//check every neighbour
let x = toCheck[i][0],
y = toCheck[i][1],
index = y * voxels + x;
if (!checkedVoxels.includes(index)) {
if (this.voxels[x][y] && (!this.voxels[x][y - 1] || !this.voxels[x + 1][y] || !this.voxels[x - 1][y] || !this.voxels[x][y + 1])) {
//if the neighbour is a floor voxel, and touches a skyvoxel this neighbour is added to the line
checkedVoxels.push(index);
if (foundNew) {//if a valid neighbour is already found, this means there are 2 possible paths from the current point
otherPaths.push([x, y]);
} else {
lineGround.push([x / voxels, y / voxels]);
nowChecking = [x, y];
//valid point gets added to the line and currently explored point get updated
foundNew = true;
}
if (x >= voxels) done = true;
}
} else if (i == toCheck.length - 1 && !foundNew) {
if (otherPaths.length > 0) {
nowChecking = otherPaths.pop();
//if none of the neighbours are correct an alternative path gets explored
foundNew = true;
}
}
}
if (!foundNew || looped++ > voxels * 10) {
//if it never found a valid neighbour, or it's looped too often break from the whileloop
console.log('loops: ', looped);
break;
}
}
if (lineGround[0][0] !== 0) lineGround.splice(0, 0, [0, lineGround[0][1]]);
if (lineGround[lineGround.length - 1][0] !== 1) lineGround.push([1, lineGround[lineGround.length - 1][1]]);
//x=0 and x=1 have to exist, so if they don't exist yet, add them
return lineGround;
}
You can also test it here: game. If you click you remove (set to 0) a few voxels within a radius of where you clicked. Also the line gets recalculated.
I'm stuck on this, because I have no idea why the line stops in some situations.
All code is here. The relevant file is js/Level.js
There are more problems than the one you raised. I played a bit on your site and there are many patterns where things go wrong.
I tried to follow the logic of your code, but got lost in details. So I rewrote most of the code. The main idea is that you should keep record of which direction (slope) you are travelling along the ground in order to know in which order you should look among the neighbours for one that is part of the ground.
Let's say the neighbours are numbered as follows, from 0 to 7:
+---+---+---+
| 7 | 0 | 1 |
+---+---+---+
| 6 | * | 2 |
+---+---+---+
| 5 | 4 | 3 |
+---+---+---+
The cell marked with * is the last cell you found to be on ground level. Now let's say the previous one found was at 6, then the search among the neighbours should start at 7, then 0, 1, 2, ... 5. The first one that is found to be solid, should be the next cell added to ground level.
Another example: if the previous one found was at 4 (we're going upward), then the neighbours should be searched starting at 5, then 6, 7, 0, 1, 2 and 3.
The first neighbour that is found to be solid (ground) is the one you want to add to your ground line. This way you will follow every curve, into "caves", upward or downward, left or right.
Of course, things can still go weird if you start on an island. But I did not attempt to solve that particular case.
I've implemented the above idea in the following version of your method:
voxelToLine() {
let voxels = this.voxels.length, x, y, i;
// neighbors' relative coordinates listed in clockwise order
const neighbor = [ [0,-1], [1,-1], [1,0], [1,1], [0,1], [-1,1], [-1,0], [-1,-1] ];
for (y = 0; y < voxels; y++) //sets first coordinate for line.
if (this.voxels[0][y]) break; // found ground, don't look further down
let lineGround = [[0, y / voxels]];
let [curX, curY] = [0, y]; //search starts here
let direction = 0; // upward
let looped = 0;
do {// Continues search until right side is located,
// or it got stuk (max 10*voxelmap width loops)
for (i = 0; i < 8; i++) {//check every neighbour, starting at `direction`
[x, y] = [curX + neighbor[direction][0], curY + neighbor[direction][1]];
// if we found ground, then pick that cell as the next one on the line
if (x>=0 && x<voxels && y>=0 && y<voxels && this.voxels[x][y]) break;
direction = (direction + 1) % 8; // turn clockwise to get next neighbour
}
//if it never found a valid neighbour
if (i === 8) break;
lineGround.push([x / voxels, y / voxels]);
// prepare for next round
[curX, curY] = [x, y];
direction = (direction + 5) % 8;
} while (looped++ <= voxels*10 && curX < voxels - 1);
//x=0 and x=1 have to exist, so if they don't exist yet, add them
if (lineGround[0][0] !== 0) lineGround.splice(0, 0, [0, lineGround[0][1]]);
if (lineGround[lineGround.length - 1][0] !== 1)
lineGround.push([1, lineGround[lineGround.length - 1][1]]);
return lineGround;
}
Looks like it's skipping over the voxel right below the last legitimate ground voxel because it's already been "checked" (added to the checkedVoxels array).
Interestingly, this would prevent your ground path to ever turn 90 degrees (you'll notice your example picture doesn't have such a voxel pattern).

Javascript: Calculating the Remainder not working?

I'm working on a game where the player / enemy has a balance bar with 3 overlays. What i'm trying to do is manipulate the 1st overlay so it goes fully across the bar by percentages.
Example: first overlay is 50% of enemy's balance. Then moves into another 'phase' or 'state. Then that overlay is gone and the second overlay is triggered and starts to decrease as well.
The width of the bar is 200 pixels, so what i'm trying to do is say "hey, if 50% of the enemies balance is gone, THEN trigger/animate the 2nd overlay.
The problem i'm running into is the remainder line. When I hit the enemy for say.. 10 balance damage of 200. It will give me the proper percentage left AND the proper remainder left. But once I hit 50%, the remainder = 0! This is where that line or function no longer works properly and it breaks the design pattern of what I want to do. Here is a example in the console log.
balanceCounter: function (character, boss){
var percentage = Math.floor((boss.balance/200) * 100)
var remain = 100 % percentage; // <--- This is not working properly
console.log(percentage);
console.log(remain);
if (character.virtue == "hero"){
if (percentage > 49){
$("#bossbalanceoverlay2").animate({
width: percentage * 2 - (remain * 2)
}, 200)
}
else
$("#bossbalanceoverlay1").animate({
width: percentage * 2 - (remain * 2)
}, 200);
}
**click attack button**
97 // <--- Percent
3 // <--- Remainder
**click attack button**
95 // <--- Percent
5 // <--- Remainder
**click attack button**
92 // <--- Percent
8 // <--- Remainder
(When i hit the 50% mark)
**click attack button**
50 // <--- Percent
0 // <--- Remainder **Why 0?**
**click attack button**
47 // <--- Percent
6 // <--- Remainder **Why 6 instead of 3?**
**click attack button**
45 // <--- Percent
10 // <--- Remainder **Why 10 instead of 5?**
You probably want to do this, instead of %:
var remain = 100 - percentage;
You want remain + percentage to always add up to 100, so this is a subtraction you need, not a modulo operation.
It is normal that with % (modulo) you get zero when the percentage is 50, because 100 is a multiple of 50 and so there is no remainder.
The problem your facing is that % returns the remainder.
100 % 45 => divisible by 45 twice, remainder = 10.
100 % 50 => divisible by 50 twice, remainder = 0.
What you want is remain = 100 - percentage
For starters I would add missing semicolons.
Secondly it looks like your else bracket is missing curly braces { }, which could cause both blocks to be hit depending on how your code is formatted.
Thirdly I would read up on the % operator, you may be overthinking it ;)

Snap.svg determine drag distance along a path

As referenced here and updated for use with Snap.svg here, I'd like to better understand how the provided gradSearch function actually works (it's a bit over my head), and whether there are any good alternatives to this approach?
gradSearch = function (l0, pt) {
l0 = l0 + totLen;
var l1 = l0,
dist0 = dist(path.getPointAtLength(l0 % totLen), pt),
dist1,
searchDir;
if (dist(path.getPointAtLength((l0 - searchDl) % totLen), pt) >
dist(path.getPointAtLength((l0 + searchDl) % totLen), pt)) {
searchDir = searchDl;
} else {
searchDir = -searchDl;
}
l1 += searchDir;
dist1 = dist(path.getPointAtLength(l1 % totLen), pt);
while (dist1 < dist0) {
dist0 = dist1;
l1 += searchDir;
dist1 = dist(path.getPointAtLength(l1 % totLen), pt);
}
l1 -= searchDir;
return (l1 % totLen);
}
Took me a while to understand the code, but this is how I understand it to be working (trying to keep the explanation simplified):
First the position of the draggable object on the path is recorded as l0 (Note that this is L0, easy to confuse with the number 10 when first looking at the code).
The distance from the new point (where mouse has dragged to) to the position on the path is recorded as dist0.
The if statement then determines which direction to drag in. It does this by adding searchDl value to the path position (i.e. length along the path), and subtracting the same value and seeing which is closer to the mouse position.
Once it knows which direction to move in, it uses the while loop to keep adding/subtracting the position by the searchDl size until the distance between the point on the path and the position of the mouse is as low as it can get.
It then returns the new position on the path.
By changing searchDir to a larger value you can move in larger increments and it can calculate faster at the expense of precision (If I've understood the code correctly).

Collision detection through array

Hi everyone I started to write a little game with ball and bricks and have some problem with collision detection. Here is my code http://jsbin.com/ibufux/9 . I know that detection works though array, but I can't figure how I can apply it to my code.
Here is what i have tried:
bricksCollision: function() {
for (var i = 0; i < $bricks.length; i++) {
if ($ball.t == $bricks[i].offset().top) {
$bricks[i].splice(i, 1);
}
}
Every bricks in game are generated by for loop and then goes to $bricks array. Every brick after generating receive top and left position and have position absolute. I have tried to check if $ball.t (it's properties of my ball object which detects ball top position) reach the bricks and than remove bricks.
Thanks for any help. I'm only start to learn JS that's why my code is
knotty.
First of all, let'stalk about some code errors
$ball.t should be probably $ball.top
you do not need to have $ as a prefix, for your code, it's simply a variable and you are calling $ball instead of ball witch results in assumption errors!
for those assumption errors here is what you are doing wrong:
$ball is a dom element, not a jQuery element
the same as $bricks
$ball is an array
with those concluded from some console.log() let's try to fix the code:
the $ball should be called, as there's only one, by it's array element, as $ball[0] and because you have variables pointing to DOM elements and not jQuery elements, you need to wrap it in Jquery as:
if ( $($ball[0]).top === $($bricks[i]).offset().top ) { ...
a good idea not to get confused is only use $ in jQuery elements, prefixing it in a variable, does not make them a jQuery Element.
And everytime you see that you have an error such as "element x has no method y" always assume that you're calling a method from a DOM element, and not a jQuery element.
Now, that #balexandre has nicely explained some points about your code, lets examine how we can compute the collision.
Imagine 2 Ranges overlapping each other (Range a partly overlaps Range b)
[100 .|.. 300]
[200 ..|. 400]
The part overlapping is from | to | -> 200 to 300, so the size of the overlap is 100
If you look at the numbers, you notice, that the overlap could be seen like,
Take the smaller number of the right side -> 300
Take the greate number of the left side -> 200
Subtract them from each other -> 300 - 200 = 100.
Lets take a look at 2 other situations. (Range b completely in Range a)
[50 ... 150]
[75...125]
So the values we have are: Math.min (150,125) //125 for the end value and Math.max (50,75) // 75 for the start value, resulting in a value of 125 - 75 = 50 for the overlap
Let's take a look the last example (Range a not in Range b)
[50 ... 150]
[200 ... 300]
Using the above formula, yields the result Math.min (150 , 300 ) - Math.max (50,200) // -50 which absolutes' value is the gap between the 2 Ranges, 50
Now we can add a last condition, as you want to compute the collision, only values > 0 are of interest for us. Given this we can put it into one condition.
Math.min ((Brick["Right"],Ball["Right"]) - Math.max (Brick["Left"], Ball["Left"]) > 0)
Which will yield true if the elements' overlap and false if they don't.
Applying this to your code, we could compute the collision the following way
bricksCollision: function () {
for (var i = 0; i < $bricks.length; i++) {
var $brick = $($bricks[i]);
var offset = $brick.offset();
var brickBounds = [offset.left - field.l]; //brick left pos
brickBounds[1] = brickBounds[0] + 40 //bricks right pos -> left pos + .bricks.width;
var ballBounds = [ball.l]; //balls left pos
ballBounds[1] = ballBounds[0] + 20 //balls right pos -> left pos + #ball.width;
if (ball.t <= (offset.top + 20) && (Math.min(brickBounds[1], ballBounds[1]) - Math.max(brickBounds[0], ballBounds[0])) > 0) {
$bricks[i].style.opacity = 0; //Make the brick opaque so it is not visible anymore
$bricks.splice(i, 1) //remove the brick from the array -> splice on the array, not the element
return true;
}
}
}
With this we could return true to the move function, when the Ball collides with a Brick.
But hey, we want it to Bounce off in the right direction, so we will face another problem.
So rather then returning a Boolean value whether the Brick collides or not, we could return a new direction in which the Ball will should move.
To be able to easily change only the x or the y part of the direction, we should use something like a vector.
To do so, we could use 2 Bits of an Integer, where the bit b0 stays for the x direction and the bit b1 for the y direction. Such that.
Dec Bin Direction
0 -> 00 -> Down Left
^ -> Left
^ -> Down
1 -> 01 -> Down Right
^ -> Right
^ -> Down
2 -> 10 -> Up Left
^ -> Left
^ -> Up
3 -> 11 -> Up Right
^ -> Right
^ -> Up
But to be able to change only a part of the direction, we need to pass the old direction to the collision function, and use bitwise & and | respectively to turn them off or on
Also we have to compute from which side the ball collides.
Fortunatly we have overlap calculation from before, which already uses all values we need, to compute the direction of collision.
If it comes frome the
right
Brick ["Right"] - Ball["Left"] has to be the same value as the overlap.
left
Ball ["Right"] - Brick["Left"] has to be the same value as the overlap.
If none of them are true, it has to either come from the
bottom
if Ball["Top"] is more than ( Brick["Top"] plus half of the Brick["height"] )
or else from the top.
To reduce the range where the condition, for the collision from the side, evaluates to true we can add another condition that the overlap has to be less than e.g ... && overlap < 2
So if it collides with the edge it doesn't always bounce of to the side.
So enough the talking, in code this could look like something like this.
bricksCollision: function (direction) {
var newDirection = direction
var ballBounds = [ball.l]; //balls left pos
ballBounds[1] = ballBounds[0] + 20 //balls right pos -> left pos + #ball.width;
for (var i = 0; i < $bricks.length; i++) {
var $brick = $($bricks[i]);
var offset = $brick.offset();
var brickBounds = [offset.left - field.l]; //brick left pos
brickBounds[1] = brickBounds[0] + 40 //bricks right pos -> left pos + .bricks.width;
var overlap = Math.min(brickBounds[1], ballBounds[1]) - Math.max(brickBounds[0], ballBounds[0]);
if (ball.t <= ((offset.top - field.t) + 20) && overlap > 0) {
$bricks[i].style.opacity = 0; //Make the brick opaque so it is not visible anymore
$bricks.splice(i, 1) //remove the brick from the array -> splice on the array, not the element
if (ballBounds[1] - brickBounds[0] == overlap && overlap < 2) { //ball comes from the left side
newDirection &= ~(1); //Turn the right bit off -> set x direction to left
} else if (brickBounds[1] - ballBounds[0] == overlap && overlap < 2) { //ball comes from the right side
newDirection |= 1; // Turn the right bit on -> set x direction to right;
} else {
if (ball.t > (offset.top + (20 / 2))) //Ball comes from downwards
newDirection &= ~(2) // Turn the left bit off -> set y direction to down;
else //Ball comes from upwards
newDirection |= 2; // Turn the left bit on -> set y direction to up;
}
//console.log("Coming from: %s Going to: %s", field.directionsLkp[direction], field.directionsLkp[newDirection], direction)
return newDirection;
}
}
return direction;
}
To get that to work, we should also change the moveXX functions, to use the new direction, returned.
But if we are going to get the new direction from the collision function anyway, we could move the complete collision detection to the function, to simplify our move functions.
But before that, we should have a look at the move functions and, add a lookup object to field which holds the numbers for the direction, to maintain readability.
var field = {
directions: {
uR : 3, // 11
dR : 1, // 01
dL : 0, // 00
uL : 2 // 10
},
directionsLkp: [
"dL","dR","uL","uR"
],
...
}
Now the move functions could then look like this,
ballCondact: function () {
var moves = [moveDl,moveDr,moveUl,moveUr]
var timeout = 5;
function moveUr() {
var timer = setInterval(function () {
$ball.css({
top: (ball.t--) + "px",
left: (ball.l++) + "px"
})
var newDirection = game.bricksCollision(field.directions.uR) //get the new direction from the collision function
if (newDirection !== field.directions.uR) {
clearInterval(timer);
moves[newDirection](); //move in the new direction
}
}, timeout);
}
...
}
Like this, the move function simply changes the direction if the collision function returns a direction which differs from the current one.
Now we can start moving the wall collisions to the collision function, to do this we could add another check at the beginning.
bricksCollision: function (direction) {
...
if (ball.t <= field.t)
newDirection &= ~(2); //Ball is at top, move down
else if (ball.l <= 0) //Ball is at the left, move right
newDirection |= 1;
else if (ball.t >= field.b - ball.height) //Ball is at the bottom, move up
newDirection |= 2;
else if (ball.l > field.width - ball.width) //Ball is at the right, move left
newDirection &= ~(1);
if (direction !== newDirection)
return newDirection
...
}
Note, i left out the collision check for the platform, as the idea should be clear =)
Here is a Fiddle

JS: given a point in a 2d array and a distance, which coordinates are travelable?

Given a 2D array of any size like so:
var board = [
[0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0]
];
...and a given [y][x] point in that array, such as:
board[3][4]
...and a given number of spaces it can travel (up/down/left/right, not diagonally), like:
var distance = 3;
...how would a function loop through the 2D array and create a list of only those coordinates that may be traveled?
(Here's a visual example of the given coordinate (*) in the array, and the surrounding travelable coordinates.)
0 0 0 0 0 0 0 0
0 0 0 3 0 0 0 0
0 0 3 2 3 0 0 0
0 3 2 1 2 3 0 0
3 2 1 * 1 2 3 0
0 3 2 1 2 3 0 0
0 0 3 2 3 0 0 0
0 0 0 3 0 0 0 0
Reference: JS: how to algorithmically highlight a diamond-shaped selection of x/y coordinates?
(I asked this question before, but I can't understand how to input a coordinate and receive a list of coordinates)
Iterate over all coordinates (or a subset x-d,y-d ... x+d,y+d if the area is big).
For each field of those, calculate the distance - in your case as dx - dy - and whenever you find a point with the distance > 0, do anything you want with it. Otherwise, ignore it. That's it!
Compared to a flood-fill approach, you get simple code and no overhead of additinal lookup tables.
This is the simplest solution I could think of, it involves working from top to bottom and left to right, iterating over only the co-ordinates that are permissible moves so it should be pretty fast:
function getPossibleMoves(x, y) {
var r, c, cMax,
distance = 3,
rows = board.length,
cols = board[0].length,
rMax = Math.min(y + distance + 1, rows),
ret = [],
yOff;
// Start at the first row with a permissible move
for (r = Math.max(y - distance, 0); r < rMax; r++) {
yOff = Math.abs(r - y);
// Work out where we should stop looping for this row
cMax = Math.min(x + distance - yOff + 1, cols);
// Start at the first column with a permissible move
for (c = Math.max(x - distance + yOff, 0); c < cMax; c++) {
// If it's not the current position, add it to the result
if (x != c || y != r)
ret.push([c, r]);
}
}
return ret;
}
To give you a better idea, I threw together a demo that allows you to adjust all the different variables, e.g. board size, distance, etc.
Working demo: http://jsfiddle.net/AndyE/fWDHy/2/
Use recursion along with a list/hash of visited links. Take a step, reduce your ability to travel by one and pass along the list of what you've seen. Add your current location to the list of visited spots. Go in each direction one step (using recursion), passing along a 'steps left' value that is one less than you were given.
Here's an answer that almost works; the only problem is that it never re-visits a cell even if the long way was used to get there. You could overcome this either via a breadth-first search or by detecting if the visited cell was reached via more steps than you are about to take to get there.

Categories