Group rectangles in a grid - javascript

I have a randomly-sliced rectangular grid - width is 80 unit.
I already have the free spaces of each row of my grid stored in an array like this below:
[
{pX:1,sX:15},
{pX:30,sX:13},
{pX:43,sX:1},
{pX:44,sX:17}
],
[
{pX:1,sX:15},
{pX:16,sX:14},
{pX:30,sX:13},
{pX:43,sX:1},
{pX:44,sX:17}
]
where pX is the starting point and sX represent the width of each rectangle.
Some of the array entries are adjacent, i.e. pX[i]+sX[i] = pX[i+1]. How can i group these array entries together and get the resulting rectangles with the maximum adjacent width?

You need to tighten arrays, joining adjacent segments. This code (Delphi, consider it as pseudocode) shrinks arrays in needed manner:
var
pX, sX: TArray<Integer>;
i, removed: Integer;
begin
pX := [1, 30, 43, 44, 64, 66, 69, 72];
sX := [15, 13, 1, 17, 2, 2, 3, 5];
removed := 0;
for i := 1 to High(pX) do begin
if (pX[i - removed - 1] + sX[i - removed - 1] = pX[i]) then
begin ////join neighbors
sX[i - removed - 1] := sX[i - removed - 1] + sX[i];
Inc(removed); ////removed++
end
else
if (removed > 0) then
begin ////copy to new place
pX[i - removed] := pX[i];
sX[i - removed] := sX[i];
end;
end;
////shorten array, remove tail
SetLength(px, Length(pX) - removed);
SetLength(sX, Length(sX) - removed);
////output result
Memo1.Lines.Add(ArrayToString(pX));
Memo1.Lines.Add(ArrayToString(sX));
output
1 30 64 69
15 31 4 8

Related

How to compute all possible paths on a grid

I've recently seen a challenge picture on brillant.org's Instagram account:
The instructions:
The robot takes 4 random steps (can't go diagonal).
In which area is it most likely to land?
Obviously there are 44 = 256 possible paths for the robot.
I tried to write a program (Javascript) to solve that problem but my approaches didn't work.
Actually I don't have useful code to show here because I got stuck pretty early on.
So my question:
How would you write a program that:
Checks all 256 possible paths and
Tells me how many (%) landed in which area
This is a very cool question!
And thanks for letting me discover brillant.org's Instagram account.
So, I would proceed as following:
Write a function to calculate all possible permutation with repetition (n^k)
Generate a map where to execute all possible moves calculated in the step 1
Check the area where the robot would land on with the final step and store it
Calculate the percentage based on the counting in step 3
The first step is a problem by itself, and it's not part of this scope. You can use or adapt the code here: https://rosettacode.org/wiki/Permutations_with_repetitions
Then, to generate the map, I simply used an array:
const map = [
0, 0, 0, 0, 1, 0, 0, 0, 0,
0, 0, 0, 1, 1, 1, 0, 0, 0,
0, 0, 1, 1, 2, 1, 1, 0, 0,
0, 1, 1, 2, 2, 2, 1, 1, 0,
1, 1, 3, 3, 2, 3, 3, 1, 1,
0, 1, 1, 3, 3, 3, 1, 1, 0,
0, 0, 1, 1, 3, 1, 1, 0, 0,
0, 0, 0, 1, 1, 1, 0, 0, 0,
0, 0, 0, 0, 1, 0, 0, 0, 0,
];
This is a representation of the image you gave, each area is marked with a different number, that we will reuse later.
At this point I defined an array of the 4 possible moves:
const moves = [
-1, // left
1, // right,
-9, // top
9, // bottom
];
The values indicates the offset needed to move in the direction wrote in in the comment: left and right I guess are self explanatory. For the top and bottom, since we're using an array as "matrix", we basically need to translate the y value to a index value in the array. The formula is simple: index = x + y * width there fore it means if you want to specify a y to move up by one cell you have -1 * 9, and to move down is 1 * 9.
For the same reason the robot's starting position (at the center of the map) is calculate as follow: 4 + 4 * 9.
Now I calculate all the possible moves combination with the permutation function:
const allmoves = permutationsWithRepetition(4, moves);
And create an array to store the results:
let results = [0, 0, 0, 0];
After that, I just iterate all the possible moves array, and calculate the position at the end of the moves:
for (let j = 0; j < allmoves.length; j++) {
// set the robot's initial position at the center
// before executing any moves' list
let pos = 4 + 4 * 9;
// calculate the new position using the current moves
for (let i = 0; i < moves.length; i++) {
let move = allmoves[j][i];
pos += move;
}
// now `map[pos]` points to a number between 1 and 3
// that identify the area where the robot is.
// we use that number as index of the results
// to increment its value.
// Notice that it's impossible land to any 0 area
// if we start from the center and we can only perform
// 4 moves.
// We use that number as `index` for our `results`
results[map[pos]]++;
}
Now in results you will have how many times the robot ended up in which area:
console.log(results); // [0, 60, 100, 96]
As mentioned is impossible given the starting position and the number of moves for the robot to land in any of the 0 area, so the first index would have 0 as value.
You can see that it landed in the area 1 (the orange one) 60 times, in the area 2 100 times (the smallest area, the green / aqua one), and in the area 3, 96 times (the blue / purple one).
At this point you can calculate the percentage (times / total * 100) and display it with a proper formatting:
// we skip the first element of results since
// it's not an area (and we'll be always 0)
for (let k = 1; k < results.length; k++) {
console.log(k, `${(results[k] / allmoves.length * 100).toFixed(2)}%`)
}
And you'll get:
1 "23.44%"
2 "39.06%"
3 "37.50%"
You can also do an empiric check, and actually generate ten thousands of moves randomly and make the program apply those instead of allmoves, and you'll see that you end always with similar number (obviously, but that also the fun part of math, verify that is actually what you will expect!).
Here the a working code that also implement the permutation code mentioned at the beginning, from rosettacode.org, and the code explained in this post: https://codepen.io/zer0/pen/RwWPZmE
(You need to open the console to see the results)
I would create different objects representing the different possibilities like below:
function Path(x, y, movesLeft) {
this.x = x;
this.y = y;
this.movesLeft = movesLeft;
this.paths = [];
if (movesLeft > 0) {
this.paths.push(new Path(x - 1, y, movesLeft - 1));
this.paths.push(new Path(x + 1, y, movesLeft - 1));
this.paths.push(new Path(x, y - 1, movesLeft - 1));
this.paths.push(new Path(x, y + 1, movesLeft - 1));
}
this.getArray = function() {
if (this.movesLeft > 0) {
var output = [];
for (var i = 0; i < this.paths.length; i++) {
output = output.concat(this.paths[i].getArray());
}
return output;
}
return [this];
}
}
Now, you can create an object and test the results:
var endPosArray = new Path(0, 0, 4).getArray();
All you need to do is loop through the array and calculate the chances.

Calculating distances between cubes (when wraparound exists)

I have a large cube composed of smaller cubes. The large cube consists of 10 cubes wide, by 10 cubes in length, by 10 cubes in height. For a total of 1000 cubes.
One cube will be randomly chosen to be blue
Three cubes will be randomly chosen to be green
I want to be able to determine which is the closest green cube to the blue cube.
One other thing that is important is that each side of the cube is connected to the opposite side (i.e. row 10 is considered next to row 1). This is the wraparound effect.
So, for example, if the blue cube is at coordinates 9:8:8 and the green cubes are each at 1:2:2, 5:5:3, and 6:3:4. Then the green cube at 1:2:2 should be considered the closest cube. If my calculations are correct, it should have a distance of 10 whereas the other two would each have a distance of 12.
Without the cube wraparound (side 1 connected with side 10) I have been able to come up with the following in JavaScript:
let lowest = 1000;
let lowest_index = -1;
for (i = 0; i < green_cube.length; i++){
let x_offset = Math.abs(blue_cube.x - green_cube[i].x);
let y_offset = Math.abs(blue_cube.y - green_cube[i].y);
let z_offset = Math.abs(blue_cube.z - green_cube[i].z);
let distance = x_offset + y_offset + z_offset;
if (distance < lowest){
lowest = distance;
lowest_index = i;
}
}
What is the proper way to code this when taking wraparound into effect?
Update
To clarify, the distance needs to be distance by number of cubes traveled to get from point A to point B. Distance must be traveled only along the X, Y, and Z axis, therefore, diagonal distance will not work. I believe this is referred to as taxicab distance in 3D space.
I believe it's often termed wraparound.
To take wraparound into account your distance measure, e.g. for the x dimension, should be:
let x_offset = Math.min((10 + blue.x - green[i].x) % 10, (10 + green[i].x - blue.x) % 10)
x_offset will always be positive.
Here is a stupid trick to keep your thinking straight.
Let v be the vector (5, 5, 5) - blue_cube. Add v to every cube's position, adding/subtracting 10 if it goes off an edge. Now the blue cube is at (5, 5, 5) and the shortest path to the other cubes no longer goes off the edge.
In your example, v = (5, 5, 5) - (9, 8, 8) = (-4, -3, -3). The first green cube moves to (1, 2, 2) + (-4, -3, -3) = (-3, -1, -1) = (7, 9, 9) and its distance is 10. The second green cube moves to (5, 5, 3) + (-4, -3, -3) = (1, 2, 0) and its distance is 12. The third green cube moves to (6, 3, 4) + (-4, -3, -3) = (2, 0, 1) and its distance is again 12. So the first is indeed closest.
In this code I am using distance calculation formula for 2 points in 3d (reference).
const calculateDistance3d = ({x: x1, y: y1, z: z1}, {x: x2, y: y2, z: z2}) => {
return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2) + Math.pow(z2 - z1, 2));
}
const calculateLoopedDistance = (cubeA, cubeB) => {
return calculateDistance3d(cubeA, {
x: cubeA.x + 10 - Math.abs(cubeB.x - cubeA.x),
y: cubeA.y + 10 - Math.abs(cubeB.y - cubeA.y),
z: cubeA.z + 10 - Math.abs(cubeB.z - cubeA.z)
});
};
const getClosest = (green_cube, blue_cube) => {
let minDistance = 1000;
let closestIndex = 0;
blue_cube.forEach((cube, index) => {
const distance = calculateDistance3d(green_cube, cube);
const loopedDistance = calculateLoopedDistance(green_cube, cube);
if (distance < minDistance || loopedDistance < minDistance) {
minDistance = Math.min(distance, loopedDistance);
closestIndex = index;
}
});
return closestIndex;
}
console.log(getClosest({x: 9, y: 8, z: 8}, [
{x: 1, y: 2, z: 2},
{x: 5, y: 5, z: 3},
{x: 6, y: 3, z: 4}
]));
console.log(getClosest({x: 9, y: 8, z: 8}, [
{x: 5, y: 5, z: 3},
{x: 1, y: 2, z: 2},
{x: 6, y: 3, z: 4}
]));
At the end of this script there are 2 logs with cube's data. You can test different data there.
I updated / fixed calculateLoopedDistance() function, which was incorrect.
Virtually replicate the green cubes as if they appeared at x, x-10 and x+10 and keep the minimum delta. This is done on the three axis independently.
I've come across another solution that also works:
let cube_width = 10;
let mid_point = cube_width / 2;
let x_offset = Math.abs(point1 - point2);
if (x_offset > mid_point){
x_offset = cube_width - x_offset;
}
I'm having a hard time figuring out whether this one or SirRaffleBuffle's solution is more efficient for time.

Flatten curve of array of numbers

I'm trying to find the technique of Normalising (not sure if that is the right word) a range of numbers.
Say I have an array:
[0, 1, 2, 3, 4, 70, 80, 900]
I want to flatten or average out the range curve, so it's more like:
[10, 11, 12, 13, 14, 50, 100, 300]. // not a real calculation
So increasing the smaller numbers in relation to reducing the larger numbers.
What is this technique called? Normalised scale? I wish to implement this in some Javascript.
UPDATE: Here is hopefully a better description of what I'm trying to do:
I have an original array of numbers:
[0, 10, 15, 50, 70, 100]
When processed through a function averageOutAllNumbers(array, slider) will produce and array that when slider is set to 100% looks like:
[0, 20, 40, 60, 80, 100] // the curve has been flattened
when slider is set to 0%, it will return the original array. If slider is set to 50%, the returned array will look something like:
[0, 12, 19, 52, 88, 100] // the curve is less steep [do not take these number literally, I'm guess what the output would be based on the slider].
the array.max() will alway be 100.
Thanks for the comments so far, they did point me closer to the solution.
No thanks to the trolls who down-voted; if you can't fix it no one can—right!
When I updated my question I realised that "increasing the smaller numbers in relation to reducing the larger numbers" would eventually lead to an evenly distributed set of numbers, e.g. [20, 20, 20, 20, 20]. However I did actually want something like I stated in the question: [0, 20, 40, 60, 80, 100] // the curve has been flattened. I did some more searching for things like:
Evenly space an array of oddly spaced numbers
Making a list of evenly spaced numbers in a certain range
Return evenly spaced numbers over a specified interval
Find what percent X is between two numbers
Amongst the list of search result I saw the answer to my original question: "What is this technique called?" Linear Interpolation
Based on that I was able to create the following:
var orig = [3, 11, 54, 72, 100];
function lerp(n, x0, x1) {
// returns a position: x that is n percent between y0 and y1
// As numbers in array are x only, y values are fixed to 0(start) - 1(end)
const y0 = 0;
const y1 = 1;
const x = ((y1 - n)*x0 + (n - y0)*x1) / (y1 - y0);
return x;
}
var flattenedEven = orig.map((value, index, arr) => {
return lerp(1, value, (Math.max(...arr)/arr.length) * (index + 1));
});
//=> [20, 40, 60, 80, 100]
var flattenedCurve = orig.map((value, index, arr) => {
return lerp(.7, value, (Math.max(...arr)/arr.length) * (index + 1));
});
//=> [14.9, 31.3, 58.2, 77.6, 100]

Calculate normal vector of a polygon - Newells Method

I'm trying to calculate the surface normal of a 2D polygon. I am using Newell's method from the OpenGL wiki to calculate the surface normal. https://www.opengl.org/wiki/Calculating_a_Surface_Normal From my understanding the normal should be in the y direction but it always returns [0, 0, 0]. The y value gets changed to -1 on the second iteration and back to zero on the fourth iteration.
p = [[0, 0, 0]
[1, 0, 0]
[0, 0, 1]
[1, 0, 1]]
function calcNormal(p) {
var normal = [0, 0, 0];
for(var i = 0; i < p.length; i++) {
var j = (i + 1) % (p.length);
normal[0] += (p[i][1] - p[j][1]) * (p[i][2] + p[j][2]);
normal[1] += (p[i][2] - p[j][2]) * (p[i][0] + p[j][0]);
normal[2] += (p[i][0] - p[j][0]) * (p[i][1] + p[j][1]);
}
return normal;
}
You're using a degenerate polygon for testing. If you draw it in the xz-plane, with the vertices numbered from 0 to 3, it looks like this:
2 ---- 3
\ /
\ /
\/
/\
/ \
/ \
0 ---- 1
This polygon does not have a well defined normal, since it changes orientation in the middle, and folds over itself.
If you swap the last two vertices:
p = [[0, 0, 0]
[1, 0, 0]
[1, 0, 1]
[0, 0, 1]]
It will look like this, and you should get much more meaningful results:
3 ---- 2
| |
| |
| |
0 ---- 1
OpenGL's version is failing for some cases especially when Polygon is 2D and you providing more than 3 vertices for calculation (4 in your case). If you provide only 3 vertices it will calculate correctly (also consider to use vector product to get normal).
Here is the link to Game Development Stack Exchange to the similar question with different approaches to this problem.

Solve max height of up to 20 objects

I have 1 to 20 objects inside a canvas.
The objects have left, width, top and height attributes.
I divided my canvas into 12 sections. Each objects width can be 1/12th of the canvas width (month) or 3/12th (quarter).
Now I need to get the sum of the heights of all objects inside each of the 12 grid sections.
For example.
In section 1 is 1 month-object with the height 12 --> result 12
In section 2 are 2 month-objects. heights 10 and 14 --> result 24
In section 4 are 1 month-object and one quarter object: heights 12 and 14 -->result 26
In section 5 are 1 month-object and the same quarter object: heights 4 and 14 --> result 18
Hope this was understandable. Solution is needed in javascript.
What I have is an array with all the beginning sections and all the ending sections.
For example:
startingSections = [1, 2, 2, 4, 4, 5]
endingSections = [1, 2, 2, 4, 6, 5]
My desired output is the following:
var sectionsMax = [];
sectiosnMax = getMonthlyTops();
function getMonthlyTops(){
var monthlyTops = [];
/* Code here */
return monthlyTops;
}
Results could be:
sectionsMax = [12, 24, 0, 26, 18, 14, 0, 0, 0, 0, 0, 0]
Example picture:
Can you try this and post what you get please. Since you can loop through the objects, i dont think you actually need startingSections and endingSections arrays.
var monthlyTops = new Array(0,0,0,0,0,0,0,0,0,0,0,0);
canvas.forEachObject(function (targ) {
x = targ.left / (canvas.width / 12);
for (i = x; i < (x + targ.width / (canvas.width / 12) ); i++)
monthlyTops[i] += targ.height;
});
return monthlyTops;

Categories

Resources