I'm currently programming a Web game for my APCS end-of-year project. I'm trying to find the closest number in an array that is full of coords for towns. Since it is coords, I need to do this for an X and Y array. For example, x=[10, 20, 30], y=[20, 10, 23], the first town would be at [10, 20]. But since exactly finding this exact location would be hard, there is a area of 10 coords around it that you can be in, in order to discover the town.
Here's my current code:
function setTowns() {
for(i = 0; i < 9000; i++){
townLocations.x.push(random(-1000, 1000));
townLocations.y.push(random(-1000, 1000));
}
}
function checkTown() {
var townX = townLocations.x;
var townY = townLocations.y;
for(i = 0; i < townX.length; i++){
if((Math.abs(townX[i]) - Math.abs(bb.location.x)) < 10){
console.log(townX[i]);
for(i = 0; i < townY.length; i++){
if((Math.abs(townY[i]) - Math.abs(bb.location.y)) < 10){
console.log(townY[i]);
return true;
}
else {
return false;
}
}
}
}
}
checkTown() is called every time the player moves.
Imaging a map looking like this:
1 0 0 0 0 0
0 0 1 0 0 1
0 0 πΆπ½ββοΈ0 0 0
0 1 0 0 0 1
0 0 0 0 0 0
Ones are a town, and zeros contains nothing. πΆπ½ββοΈ marks the player.
You can use Pythagorean theorem to spot any kind of "collision". Let say the player is close to two towns (the ones) and each town has it's own coordinate (X, Y), as well as the player. Draw a triangle in your mind, where the Y coordinates for both places creates one side of the triangle, and the X coordinates are another side. The distance is the hypotenuse.
Let say that the distance for collision (read: detection) is 1. If the player is on coordinate (3, 3), would a town with coordinate (2, 4) be close enough? One side of the triangle would go from 3 to 2 and the other from 3 to 4. What's the hypotenuse, aka the distance?
OK, that's math for children, because you can probably see it straight away, but lets see the math behind it.
let playerXCoordinate = 3;
let playerYCoordinate = 3;
let townXCoordinate = 2;
let townYCoordinate = 4;
let xDifference = playerXCoordinate - townXCoordinate; // 3 - 2
let yDifference = playerYCoordinate - townYCoordinate; // 3 - 4
// Pythagorean theorem
let hypotenuseAsDistance = Math.sqr( Math.pow(xDifference, 2) + Math.pow(yDifference, 2))
// β(1^2 + 1^2) = β1 = 1
let isWithinDistance = hypotenuseAsDistance <= 1 // true
Lets take another example. This time with a town located at (5, 4).
let xDifference = 3 - 5;
let yDifference = 3 - 4
let hypotenuseAsDistance = Math.sqr( Math.pow(xDifference, 2) + Math.pow(yDifference, 2))
// β((3-5)^2 + (3-4)^2) = β(2^2+1^2) = β(4+1) = β5 = 2.23606797749979
let isWithinDistance = hypotenuseAsDistance <= 1 // false
A shorter version is, however, available as part of the Math object.
let xDifference = 3 - 5;
let yDifference = 3 - 4;
let hypotenuseAsDistance = Math.hypot(xDifference, yDifference);
So as for your example, it would be something like this.
let playerXPos = 3; // just an example
let playerXPos = 4; // just an example
let townArrLength = townLocations.x;
let detectionRange = 10;
let townXPos = 0;
let townYPos = 0;
let distance = 0;
for (let i = 0; i < townArrLength; i++) {
townXPos = townLocations.x[i];
townYPos = townLocations.y[i];
distance = Math.hypot(playerXPos - townXPos, playerYPos - townYPos);
if (distance <= detectionRange) {
// do something
}
}
For more reading:
https://developer.mozilla.org/en-US/docs/Games/Techniques/2D_collision_detection
Related
I have a variable called turnRadius that comes from user input and can be between -1 and 1, 0 being default.
I then have to convert this number into its equivalent in a range of 275 and 0 and store in variable spriteHorizontalPosition.
The problem is, for reasons I cant expect the user to set turnRadius all the way to -1 or 1, so I want that when turnRadius reaches 0.65 or -0.65, to increase exponentially to its max/min so that user doesnt have to reach the full number with input.
I think I get the idea but I don't know how to write the function, can I have some help?
Below is what I had, but I'm aware is not exponential and when it reaches 0.65 the spriteHorizontalPosition is suddenly yanked to its max and looks awkward.
let turnRadius = user.input;
if (turnRadius <= -0.65) {
turnRadius = -1;
} else if (turnRadius >= 0.65 ) {
turnRadius = 1;
}
spriteHorizontalPosition = ((turnRadius * -1) + 1) * 137.5;
if ( spriteHorizontalPosition >= 275 ) {
spriteHorizontalPosition = 275;
}
else if ( spriteHorizontalPosition <= 0 ) {
spriteHorizontalPosition = 0;
}
playerSprite.transform.x = spriteHorizontalPosition;
How about a nice cubic curve to map between the realistically possible user input (-0.65..0.65) and the desired (/ virtual) user range (-1..1)?
The smooth curve cubicMap can be given as:
const A = 0.65;
const C = 1/A - A*A;
const cubicMap = (x) => {
if (x > 0.65) return 1;
if (x < -0.65) return -1;
return C*x + x*x*x;
}
As seen in this graph: Desmos Link, it maps user input between -0.65 and 0.65 smoothly to between -1 and 1.
Your code could look like this:
const turnRadius = cubicMap(user.input);
const spriteHorizontalPosition = 275 * (1 + turnRadius) / 2;
playerSprite.transform.x = spriteHorizontalPosition;
There's no need for the extra if statements, the value is already clamped by cubicMap.
The question is as follows:
The string "PAYPALISHIRING" is written in a zigzag pattern on a given number of rows like this: (you may want to display this pattern in a fixed font for better legibility)
P A H N
A P L S I I G
Y I R
And then read line by line: "PAHNAPLSIIGYIR"
Write the code that will take a string and make this conversion given a number of rows:
string convert(string s, int numRows);
Example 1:
Input: s = "PAYPALISHIRING", numRows = 3
Output: "PAHNAPLSIIGYIR"
Example 2:
Input: s = "PAYPALISHIRING", numRows = 4
Output: "PINALSIGYAHRPI"
Explanation:
P I N
A L S I G
Y A H R
P I
I have written the following code, but I am stuck in terms of how to flag the row as one time to be downward moving, where I increment the start row, but when it's zigzagging back to the top, it should be decremented. I am unable to figure out the logic to make this work without affecting the downward movement. Any help would be appreciated.
const convert = (s, numRows) => {
let startRow = 0
let endRow = numRows - 1
let startColumn = 0
let endColumn = Math.floor((s.length / 2) - 1)
s = s.split('')
let results = []
// to setup the columns
for (let i = 0; i < numRows; i++) {
results.push([])
}
while (startRow <= endRow && startColumn <= endColumn && s.length) {
for (let i = startRow; i <= endRow; i++) {
results[i][startColumn] = s.shift()
}
for (let i = endRow - 1; i >= startRow; i--) {
results[i][startColumn + 1] = s.shift()
startColumn++
}
//this line seems to be the issue
startRow++
}
return results
}
console.log(convert('PAYPALISHIRING', 4))
I rewrote your while loop as follows where I simply walk a "zigzag" pattern! Hopefully, it is simple enough to understand.
let c=0, row=0,col=0, down=0;
while(c<s.length) {
results[row][col]=s[c];
if(down==0) { // moving down
row++;
if(row==numRows) {
down = 1;
col++;
row-=2;
}
} else { // moving up
row--;
col++;
if(row==0) {
down=0;
}
}
c++;
}
Ps. Above code does not handle numRows < 3 so you have to manage them before this loop.
My precalculus is a little rusty, but the logic behind this problem seems like a sine wave. I made a math error somewhere in creating the sin equation that prevents this from working (r never equals c with the current paramaters), but hopefully this will help if this is the direction you choose to go in.
/*If x-axis is position in string, and y-axis is row number...
n=number of rows
Equation for a sin curve: y = A sin(B(x + C)) + D
D=vertical shift (y value of mid point)
D=median of 1 and n
n: median:
1 1
2 1.5
3 2
4 2.5
5 3
6 3.5
7 4
median=(n+1)/2
D=(n+1)/2
A=amplitude (from the mid-point, how high does the curve go)
median + amplitude = number of rows
amplitude = number of rows - median
A=n-D
C=phase shift
Phase shift for a sin curve starting at its lowest point: 3Ο/2
(so at time 1, row number is 1, and curve goes up from there)
C=3Ο/2
Period is 2Ο/B
n p
3 4
4 6
5 8
6 10
period=2(n-1)
2(n-1)=2Ο/B
B(2(n-1)=2Ο
B=2Ο/2(n-1)
B=Ο/(n-1)
Variables:
s = string
n = number of rows
c = current row number being evaluated
p = position in string
r = row number
*/
var output='';
function convert(s,n) {
D=(n+1)/2
A=n-D
C=(3*Math.PI)/2
B=Math.PI/(n-1)
for (c=1;c<=n;c++) { //loop from 1st row to number of rows
for (p=1;p<=s.length;p++) { //loop from 1st to last character in string
r=A*Math.sin(B*(p+C))+D //calculate the row this character belongs in
if (r==c) { output+= s.charAt(r) } //if the character belongs in this row, add it to the output variable. (minus one because character number 1 is at position 0)
}}
//do something with output here
}
The following will output the slope, intercept and correlation coefficient R^2 for a given set of x and y values.
let linearRegression = (y,x) => {
let lr = {}
let n = y.length
let sum_x = 0
let sum_y = 0
let sum_xy = 0
let sum_xx = 0
let sum_yy = 0
for (let i = 0; i < y.length; i++) {
sum_x += x[i]
sum_y += y[i]
sum_xy += (x[i]*y[i])
sum_xx += (x[i]*x[i])
sum_yy += (y[i]*y[i])
}
lr['slope'] = (n * sum_xy - sum_x * sum_y) / (n*sum_xx - sum_x * sum_x)
lr['intercept'] = (sum_y - lr.slope * sum_x)/n
lr['r2'] = Math.pow((n*sum_xy - sum_x*sum_y)/Math.sqrt((n*sum_xx-sum_x*sum_x)*(n*sum_yy-sum_y*sum_y)),2)
return lr
}
How can I adapt this to accept two independent variables x1, x2 rather than one?
This page goes into the modified formulas:
http://faculty.cas.usf.edu/mbrannick/regression/Reg2IV.html
But i've been struggling to adapt this to the above function.
Step-by-step
First, look at the input line:
let linearRegression = (y,x) => {. You have 2 variables, so we could call them x1 and x2: let linearRegression = (y,x1,x2) => {
Now the formulae for the regression depend on dot products between the pairs of variables - x1.x1, x1.x2, x1.y etc.
So instead of calculating sum_xx, sum_xy, sum_yy, we need to calculate the sums for all of those pairs (with 3 variables instead of 2, there are 6 sums to calculate).
Finally, the two-variable equation is y=a + b1.x1 + b2.x2, so there are 2 slopes to calculate rather than 1, and the page you linked to gives all the formulae you would need there.
given a matrix like this one:
1 2 3
4 5 6
7 8 9
which can be represented as a 2 dimensional array:
arr = [[1,2,3], [4,5,6], [7,8,9]];
rotate the array so that it is read diagonally at a 45 degree angle and prints out this:
1
4 2
7 5 3
8 6
9
I spent a while coming up with a solution that I don't even fully intuitively understand, but it works, at least for 3x3 and 4x4 matrices. I was hoping to see more logical and clean implementations.
Here's my solution:
arr = [[1,2,3,0],[4,5,6,0],[7,8,9,0], [0,0,0,0]];
// arr[i][j];
transform(arr);
function transform(ar) {
// the number of lines in our diagonal matrix will always be rows + columns - 1
var lines = ar.length + ar[0].length - 1;
// the length of the longest line...
var maxLen = ~~(ar.length + ar[0].length)/2;
var start = 1;
var lengths = [];
// this for loop creates an array of the lengths of each line, [1,2,3,2,1] in our case
for (i=0;i<lines; i++) {
lengths.push(start);
if (i+1 < maxLen) {
start++;
} else {
start--;
}
}
// after we make each line, we're going to append it to str
var str = "";
// for every line
for(j=0; j<lengths.length; j++) {
// make a new line
var line = "";
// i tried to do it all in one for loop but wasn't able to (idk if it's possible) so here we use a particular for loop while lengths of the lines are increasing
if (j < maxLen) {
// lengths[j] is equal to the elements in this line, so the for loop will run that many times and create that many elements
for(c=0; c<lengths[j]; c++) {
// if ar[r][c], the pattern here is that r increases along rows (as we add new lines), and decreases along columns. c stays the same as we add rows, and increases across columns
line += ar[lengths[j]-1-c][c] + " ";
// when we've added all the elements we need for this line, add it to str with a line break
if (c == lengths[j]-1) {
line += "\n"; str += line;
}
}
} else {
// when we're headed down or decreasing the length of each line
for (r=0; r<lengths[j]; r++) {
// the pattern here tripped me up, and I had to introduce another changing variable j-maxLen (or the distance from the center). r stays the same as rows increase and decreases across columns. c increases along rows and decreases across columns
line += ar[lengths[j]-r+j-maxLen][j-maxLen+r +1] + " ";
// that's all our elements, add the line to str;
if (r == lengths[j] -1) {
line += "\n"; str += line;
}
}
}
}
console.log(str);
}
The main idea is to partition the original matrix indexed by (i,j) according to i+j.
This is expressed in the code snippet rotated[i+j].push(arr[i][j]) below:
arr = [[1,2,3], [4,5,6], [7,8,9]];
var summax = arr.length + arr[0].length - 1; // max index of diagonal matrix
var rotated = []; // initialize to an empty matrix of the right size
for( var i=0 ; i<summax ; ++i ) rotated.push([]);
// Fill it up by partitioning the original matrix.
for( var j=0 ; j<arr[0].length ; ++j )
for( var i=0 ; i<arr.length ; ++i ) rotated[i+j].push(arr[i][j]);
// Print it out.
for( var i=0 ; i<summax ; ++i ) console.log(rotated[i].join(' '))
Output:
1
4 2
7 5 3
8 6
9
In Ruby
Produces same output:
puts arr.transpose.flatten.group_by.with_index { |_,k|
k.divmod(arr.size).inject(:+) }.values.map { |a| a.join ' ' }
function transform(ar) {
var result = [],
i, x, y, row;
for (i = 0; i < ar.length; i++) {
row = [];
for (x = 0, y = i; y >= 0; x++, y--) {
row.push(ar[y][x]);
}
result.push(row);
}
for (i = 1; i < ar[0].length; i++) {
row = [];
for (x = i, y = ar[0].length - 1; x < ar[0].length; x++, y--) {
row.push(ar[y][x]);
}
result.push(row);
}
return result;
}
This returns the rotated array, to print it out as you go just replace each result.push(row); line with console.log(row.join(" "));.
Here's my approach:
var origMatrix = [[1,2,3,4,5], [4,5,6,7,8], [9,10,11,12,13], [14,15,16,17,18], [19,20,21,22,23]];
var maxSize = origMatrix.length;//Presuming all internal are equal!
var rotatedMatrix = [];
var internalArray;
var keyX,keyY,keyArray;
for(var y=0;y<((maxSize * 2)-1);y++){
internalArray = [];
for(var x=0;x<maxSize;x++){
keyX = x;
keyY = y - x;
if(keyY > -1){
keyArray = origMatrix[keyY];
if(typeof(keyArray) != 'undefined' && typeof(keyArray[keyX]) != 'undefined'){
internalArray.push(keyArray[keyX]);
}
}
}
rotatedMatrix.push(internalArray);
}
//log results
for(var i=0;i<rotatedMatrix.length;i++){
console.log(rotatedMatrix[i]);
}
Here's a JSFiddle of it in action (open the Console to see the results)
The Idea: Walk the Diagonals and Clip
You could use the diagonal enumeration from Cantor, see Cantor pairing function,
which is used to show that the set N x N has the same cardinality as the set N (i.e. natural numbers can be mapped one to one to pairs of natural numbers) and combine it with a condition that one skips those values which lie outside the rectangular matrix.
The Cantor pairing function pi takes two natural numbers i and j, i.e. the pair (i, j) and maps it to a natural number k
pi : |N x |N -> |N : pi(i, j) = k
Use the reverse mapping to get this
pi^-1 : |N -> |N x |N : pi^-1(k) = (i, j)
i.e. one enumerates the cells of the "infinite Matrix" N x N diagonally.
So counting up k and applying the inverse function will give the proper pair of indices (i, j) for printing the rotated matrix.
Example:
0->(0, 0) 2->(0, 1) | 5->(0, 2) 9->(0, 3) . .
1->(1, 0) 4->(1, 1) | 8->(1, 2)
3->(2, 0) 7->(2, 2) |
---------------------+ <- clipping for 3 x 2 matrix
6->(3, 0)
.
.
Calculation of the Inverse Cantor Pair Function
For input k these formulas give the pair (i, j):
w = floor((sqrt(8*k + 1) - 1) / 2)
t = (w*w + w) / 2
j = k - t
i = w - j
See the link given above for a derivation.
Resulting Algorithm
Given a m x n matrix A: i from [0, .., m - 1] enumerates the rows, and j from [0, .., n - 1] enumerates the columns
Start at k = 0
calculate the corresponding index pair (i, j)
print the matrix value A[i, j] if the indices i and j lie within your matrix dimensions m and n
print a new line, once your i hit the top of the matrix, i.e. if i == 0
increment k
continue with step 2 until you arrived at the index pair (i, j) = (n - 1, n - 1)
Sample Implementation in JavaScript
Note: I tried this out in the MongoDB shell, using its print() function.
Helper functions
function sprint(k) {
var s = '' + k;
while (s.length < 3) {
s = ' ' + s;
}
return s;
}
function print_matrix(a) {
var m = a.row_size;
var n = a.column_size;
for (var i = 0; i < m; i++) {
var s = '';
for (var j = 0; j < n; j++) {
s += sprint(a.value[i][j]);
}
print(s);
}
}
The Inverse of the Cantor pairing function
// inverse of the Cantor pair function
function pi_inv(k) {
var w = Math.floor((Math.sqrt(8*k + 1) - 1) / 2);
var t = (w*w + w) /2;
var j = k - t;
var i = w -j;
return [i, j];
}
The algorithm
// "rotate" matrix a
function rot(a) {
var m = a.row_size;
var n = a.column_size;
var i_max = m - 1;
var j_max = n - 1;
var k = 0;
var s = '';
do {
var ij = pi_inv(k);
var i = ij[0];
var j = ij[1];
if ((i <= i_max) && (j <= j_max)) {
s += sprint(a.value[i][j]);
}
if (i == 0) {
print(s);
s = '';
}
k += 1
} while ((i != i_max) || (j != j_max));
print(s);
}
Example usage
// example
var a = {
row_size: 4,
column_size: 4,
value: [ [1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16] ]
};
print('in:');
print_matrix(a);
print('out:');
rot(a);
Output for 4x4 Matrix
in:
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16
out:
1
5 2
9 6 3
13 10 7 4
14 11 8
15 12
16
This method works for any m x n Matrix, e.g. 4 x 5:
in:
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19 20
out:
1
6 2
11 7 3
16 12 8 4
17 13 9 5
18 14 10
19 15
20
or 4 x 3:
in:
1 2 3
4 5 6
7 8 9
10 11 12
out:
1
4 2
7 5 3
10 8 6
11 9
12
I am working on looping through a few grid items and was hoping someone could help me find a way to figure out how to get to the items that I need and possibly I will better understand how to access the items in the grid. So here is the grid.
[0] [1] [2] [3] [4]
[5] [6] [7] [8] [9]
[10] [11] [12] [13] [14]
[15] [16] [17] [18] [19]
[20] [21] [22] [23] [24]
This is basically a 5x5 grid, however it could be any size but I just used this for the example. There are two ways I would like to loop through this. The first one being in this order:
0,1,2,3,4,9,14,19,24,23,22,21,20,15,10,5,6,7,8,13,18,17,16,11,12
Basically all that is doing is going around the outside starting from the top left. The next way I would want to loop through that is through the same exact values except in reverse order (basically inside out instead of outside in) and actually thinking about this now I could just loop through the first method backwards. If anyone can help me with this it would be great. I really want to learn more on how to loop through items in crazy arrangements like this.
This function
function n(i, w, h)
{
if (i < w)
return i;
if (i < w + h-1)
return w-1 + (i-w+1)*w;
if (i < w + h-1 + w-1)
return w-1 + (h-1)*w - (i - (w + h-1 - 1));
if (i < w + h-1 + w-1 + h-2)
return (h - (i - (w + w-1 + h-1 - 2)))*w;
var r = n(i - (w-1)*2 - (h-1)*2, w-2, h-2);
var x = r % (w-2);
var y = Math.floor(r / (w-2));
return (y+1)*w + (x+1);
}
accepts as input
i: Index of the item you're looking for
w: Width of the grid
h: Height of the grid
and returns the corresponding element of the grid assuming that clock-wise spiral traversal.
The implementation simply checks if we're on the top side (i<w), on the downward right side (i<w+h-1) and so on and for these cases it computes the cell element explicitly.
If we complete one single trip around the spiral then it calls recursively itself to find the element in the inner (w-2)*(h-2) grid and then extracts and adjusts the two coordinates considering the original grid size.
This is much faster for big grids than just iterating and emulating the spiral walk, for example computing n(123121, 1234, 3012) is immediate while the complete grid has 3712808 elements and a walk of 123121 steps would require quite a long time.
You just need a way to represent the traversal pattern.
Given an NxM matrix (e.g. 5x5), the pattern is
GENERAL 5x5
------- -------
N right 5
M-1 down 4
N-1 left 4
M-2 up 3
N-2 right 3
M-3 down 2
N-3 left 2
M-4 up 1
N-4 right 1
This says move 5 to the right, 4 down, 4 left, 3 up, 3 right, 2 down, 2 left, 1 up, 1 right. The step size shifts after each two iterations.
So, you can track the current "step-size" and the current direction while decrementing N, M until you reach the end.
IMPORTANT: make sure to write down the pattern for a non-square matrix to see if the same pattern applies.
Here's the "walking" method. Less efficient, but it works.
var arr = new Array();
for(var n=0; n<25; n++) arr.push(n);
var coords = new Array();
var x = 0;
var y = 0;
for(var i=0; i<arr.length; i++) {
if( x > 4 ) {
x = 0;
y++;
}
coords[i] = {'x': x, 'y': y};
x++;
}
// okay, coords contain the coordinates of each item in arr
// need to move along the perimeter until a collision, then turn.
// start at 0,0 and move east.
var dir = 0; // 0=east, 1=south, 2=west, 3=north.
var curPos = {'x': 0, 'y': 0};
var resultList = new Array();
for(var x=0; x<arr.length; x++) {
// record the current position in results
var resultIndex = indexOfCoords(curPos, coords);
if(resultIndex > -1) {
resultList[x] = arr[resultIndex];
}
else {
resultList[x] = null;
}
// move the cursor to a valid position
var tempCurPos = movePos(curPos, dir);
var outOfBounds = isOutOfBounds(tempCurPos, coords);
var itemAtTempPos = arr[indexOfCoords(tempCurPos, coords)];
var posInList = resultList.indexOf( itemAtTempPos );
if(outOfBounds || posInList > -1) {
dir++;
if(dir > 3) dir=0;
curPos = movePos(curPos, dir);
}
else {
curPos = tempCurPos;
}
}
/* int indexOfCoords
*
* Searches coordList for a match to myCoords. If none is found, returns -1;
*/
function indexOfCoords(myCoords, coordsList) {
for(var i=0; i<coordsList.length; i++) {
if(myCoords.x == coordsList[i].x && myCoords.y == coordsList[i].y) return i;
}
return -1;
}
/* obj movePos
*
* Alters currentPosition by incrementing it 1 in the direction provided.
* Valid directions are 0=east, 1=south, 2=west, 3=north
* Returns the resulting coords as an object with x, y.
*/
function movePos(currentPosition, direction) {
var newPosition = {'x':currentPosition.x, 'y':currentPosition.y};
if(direction == 0) {
newPosition.x++;
}
else if(direction == 1) {
newPosition.y++;
}
else if(direction == 2) {
newPosition.x--;
}
else if(direction == 3) {
newPosition.y--;
}
return newPosition;
}
/* bool isOutOfBounds
*
* Compares the x and y coords of a given position to the min/max coords in coordList.
* Returns true if the provided position is outside the boundaries of coordsList.
*
* NOTE: This is just one, lazy way of doing this. There are others.
*/
function isOutOfBounds(position, coordsList) {
// get min/max
var minx=0, miny=0, maxx=0, maxy=0;
for(var i=0; i<coordsList.length; i++) {
if(coordsList[i].x > maxx) maxx = coordsList[i].x;
else if(coordsList[i].x < minx) minx = coordsList[i].x;
if(coordsList[i].y > maxy) maxy = coordsList[i].y;
else if(coordsList[i].y < miny) miny = coordsList[i].y;
}
if(position.x < minx || position.x > maxx || position.y < miny || position.y > maxy) return true;
else return false;
}
This will move through the grid in a direction until it hits the bounds or an item already in the results array, then turn clockwise. It's pretty rudimentary, but I think it would do the job. You could reverse it pretty simply.
Here's a working example: http://www.imakewidgets.com/test/walkmatrix.html