2D array replacing values according conditions - javascript

I got a 10 x 10 array with the following values:
[[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, 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]];
In this 2D array, I have to randomly choose six values of 0, either horizontally or vertically (also random) and replace them with the value 6.
I've done this with the following code:
function tekenGrootstSchip(bord) {
rand = Math.floor((Math.random() * 10));
rand2 = Math.floor((Math.random() * 10));
directie = Math.floor((Math.random() * 2));
counter = 0;
if (directie == 0) {
for(y = rand2; y < 10; y++) {
if(counter < 6) {
bord[rand][y] = 6;
counter++;
}
}
for(y = rand2; y > 0; y--) {
if(counter < 6) {
bord[rand][y] = 6;
counter++;
}
}
} else {
for(x = rand; x < 10; x++) {
if(counter < 6) {
bord[x][rand2] = 6;
counter++;
}
}
for(x = rand; x > 0; x--) {
if(counter < 6) {
bord[x][rand2] = 6;
counter++;
}
}
}
}
After doing this for the value 6, I also have to do this for the value 4. The rules for value 4 are a bit different however. You can't place a 4 on a 6, neither can you place a 4 next to a 6. And the value 4 only takes four places (so 4x1, while a 6 is 6x1)
So if my randomly generated direction is horizontal, my bord[x-1][y], bord[x][y] and bord[x+1][y], with x and y initialized at the random value, with y going up to y+1, y+2, y+3, y+4 (4x1). All these values have to be checked against == 0, if true, replacing the zeroes with fours can be initialized otherwise not. If so, I have to generate a new [x][y] and check these conditions again until I can change four zeroes in my 2D array succesfully.
Although I have a general idea of implementing this, I would have a bug that, if for instance one null-value would be replaced with a four, but the one next to it can't be replaced since it's next to a != 0 value, I would be stuck with a "illegal" four.
If anyone could help me out in the right direction I would appreciate it.

feasibility
We can (trivially) show that whatever the 6 configuration chosen, we can always put the 4s.
Indeed, the maximal cover area of a 6 is 9 (a 3x3 square)
000
060
000
(we can't put a 4 in the border surrounding the 6).
So a bound for the maximal 6 configuration covered area is 6*9=54
000000xxxx
060060xxxx
000000xxxx
000000xxxx
060060xxxx
000000xxxx
000000xxxx
060060xxxx
000000xxxx
xxxxxxxxxx
and we can put as many 4 in the squares containing 'x' (which is way more than 4)
6-generation
Let's assume the board is indiced as follow:
0 1 2 3 ... 9
10 11 12 ... 19
...
90... 99
let x towards the bottom and y to the right
A square holding value id can be found at (x,y)=((id - id%10)/10, id%10) and reciprocally
id:(x,y)->x*10+y
So we will only consider numbers between 0 and 99 (since we can find back their associated position(x,y) in the grid)
I will use the copy-pasted below getRandomInt taken shamelessly from mdn
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min)) + min; //The maximum is exclusive and the minimum is inclusive
}
Finally we can draw between 0 and 99. If number already exists then discard it, otherwise take it
let indices = new Set
while(indices.size < 6){
let idx = getRandomInt(0,99)
if(!indices.has(idx)){
indices.add(idx)
}
}
return [...indices]
4-generation
We can apply the same stragy as before: draw a random number between 0 and 99 and discard it as long as it is not valid, until 4 valid positions are found
build the invalid set due to the 6 positionned
let invalids = new Set
indices.forEach(idx=>{
let [x,y] = [(idx - idx%10)/10, idx%10]
//add the adjacent squares
for(let i = -1; i<=1; ++i){
for(let j = -1; j<= 1; ++j){
if( 0<=x+i<10 && 0 <= y+j < 10){//if the cell in the board....
invalids.add( (x+i)*10 + y+j )
}
}
}
})
draw except from the invalid set
let out = []
while(out.length < 4){
let idx = getRandomInt(0,99)
if(!invalids.has(idx)){
invalids.add(idx)
out.push(idx)
}
}
return out
That approach may be not that efficient in worst case: we would have about 54% probability of drawing an invalid number!
We can thus consider an array of only the valid numbers, and draw from it
let valids = Array(100).fill(0).reduce((oks, x,i)=>{
if(invalids.has(i)) return oks
return oks.push(i),oks
},[])
//take numbers from that array
let out = []
for(let i = 0; i<4; ++i){
let idx = getRandomInt(0,valids.length)
//notice that here we take the elem from valids
//not just the idx from getRandomInt
out.push(valids[idx])
//and we takeout the element from valids
valids.splice(idx, 1);
}
return out

Related

Return coordinates (x,y) from an array of numbers in Javascript

I am trying to make a collision map in a 2d top down view game in Javascript. I am using Tiled to build the maps. Tiled generates a json / javascript file with an array of each element in my map. I can set a boolean for each tile, so those with the true setting will come out in the array as 1 and those with the false value will return 0.
Now I want output those data as x,y coordenates. I mean if i have the array [0,0,0,0] I want that those output like (x,y) (0,0), (1,0), (2,0), (3,0) based in the width and height of the tilemap array. Something like this:
"data":[0,0,0,0,
1,1,1,1,
0,0,0,0,
0,0,0,0]
"height":4,
"width":4,
"x":0,
"y":0
My problem is I don't know how to say it to my loop that there will be four columns and four rows and the index should change from (0,0), (0,1), (0,2), (0,3) to (1,0), (1,1),(1,2),(1,3) after end the first row continue until the last column.
Any idea?
You could use two for-loops for the current row and col respectively:
const tiledOutputJson = {
"data": [
0, 0, 0, 0,
1, 1, 1, 1,
0, 0, 0, 0,
0, 0, 0, 0
],
"height": 4,
"width": 4,
"x": 0,
"y": 0
};
const { data, height, width, x, y } = tiledOutputJson;
for (let row = 0; row < height; row++) {
for (let col = 0; col < width; col++) {
console.log(`(${row}, ${col}) = ${data[row * height + col]}`);
}
console.log();
}
In general I would recommend to make it an array for x which contains the arrays for y
[
[0,0,0,0],
[1,1,1,1],
[0,0,0,0],
]
You can do the mapping like so
const data = {
"data": [0, 0, 0, 0,
1, 1, 1, 1,
0, 0, 0, 0,
0, 0, 0, 0
],
"height": 4,
"width": 4,
"x": 0,
"y": 0
}
for (let x = 0; x < data.height * data.width; x = x + data.width) {
for (let y = x; y < data.width + x; y++) {
console.log({
x: x / data.height,
y: y % data.width,
v: data.data[y]
})
}
}

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.

Issues with displaying a map in p5.js

I am currently attempting to display a map in processing using a 2d array.
Currently I have this down:
var start_map = [
[1, 1, 1, 1, 1],
[1, 0, 0, 0, 0],
[1, 0, 0, 0, 0],
[1, 0, 0, 0, 0],
[1, 1, 1, 1, 1]
];
function drawMap(map) {
for (var x = 0; x < 5; x++) {
for (var y = 0; y < 5; y++) {
if (map[x][y] == 0) {
fill(51, 153, 51);
rect((10 + 50*x), (10 + 50*y), 50, 50);
}
else if (map[x][y] == 1) {
fill(0, 102, 0);
rect((10 + 50*x), (10 + 50*y), 50, 50);
}
}
}
}
But, while I do get a map displayed, it appears to be rotated 90 degrees clockwise. What is causing this and how can I fix this?
Think about how the indexes of a 2D array work. Let's look at a simpler example:
var map = [
[1, 2],
[3, 4]
];
Where is map[0][1]? Where is map[1][0]?
A 2D array is an array of arrays. The first index selects the subarray at that index, and the second index selects the element in that subarray.
So in the simple example above, map[0] selects the subarray at index 0, which is [1, 2]. Then map[0][1] selects the element in that subarray at index 1, which is 2.
This might seem a bit surprising if you were treating the indexes as an x, y pair. In that case, you'd expect 0, 1 to give you 3, right? But it's not an x, y pair. It's an index into the outer array, then an index into the subarray.
In other words, it's actually a y, x pair. So to fix your problem, you can actually just swap the order of your indexes:
`map[y][x]`
Now you use the y value to select which subarray you want (which row you want), and the x value to select which element in that subarray you want (which column you want).

javascript 2d array tile map srcolling

I have searched this for weeks but just can't find the right tutorial.
Lets say we have a canvas that is 800x800.
<canvas id='draw' width=800 height=800></canvas>
And we have a tile map(0 will be square barriers and 1 will be air).
var tileMap = [ [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
]
How do I make the map scroll so that only 3x3 of the squares be seen whenever the player moves?
For example:
canvas screen--> [0,0,0]
[0,1,1] <-- just this part to be seen
[0,0,0]
when player moves:
canvas screen--> [0,0,0]
[1,1,1] <-- now this part will be seen
[0,0,0]
So how do I make the tile map move to give the illusion that the player is moving?
tileMap should not be modify instead you create some object that represents center of current view e.g. player and use it in your display function. Whenever you want to scroll you just move center of view.
var tileMap = [ [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
]
var hero = {
position: {
x: 0,
y: 1
}
};
const air = 1;
const barrier = 0;
display(hero,tileMap);
// move player instead of scrolling data
// tileMap is untouched
hero.position.x += 3;
display(hero,tileMap);
// use player position to display only portion of map
function display(player,map) {
var result = [
"",
"",
""
];
for(var y = 0, i = player.position.y - 1; y < 3; i++,y++) {
if (i >= 0 && i < map.length) {
for(var x = 0, j = player.position.x - 1; x < 3; j++,x++) {
if ( j >= 0 && j < map[i].length) {
result[y] += map[i][j] + ",";
}
else {
// outside map only ait
result[y] += air+ ",";
}
}
}
else {
// outside map only ait
result[y] += air +","+ air +","+air+",";
}
}
console.log(result);
}
You did not explained how is your array correlated with your canvas and animations - therefore a clear guess is that you're concerned about your Arrays only.
You need a viewport Array viewMap dictated by the camera cam position and size values.
In the example below it's anchor is left/top (you might want to change the logic later to use center/center instead, up to you).
on keyboard event, change the camera x y position and prevent going out of map boundaries
Populate your viewMap array and print it:
var tileMap = [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
[0, 1, 4, 1, 1, 3, 3, 1, 1, 1, 1, 1, 1, 8, 8, 1, 0],
[0, 6, 1, 1, 1, 3, 3, 1, 1, 1, 1, 1, 1, 9, 9, 1, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
];
var cam = {
x: 0,
y: 0,
width: 3,
height: 3
};
// Create empty 2D viewMap of viewport
var viewMap = [];
for (var i = 0; i < cam.height; i++) viewMap[i] = new Array(cam.width);
function tileViewport() {
for (var y = 0; y < cam.height; y++)
for (var x = 0; x < cam.width; x++)
viewMap[y][x] = tileMap[y + cam.y][x + cam.x];
// PRINT
console.clear(); console.log(viewMap.map(a => a.join(" ")).join("\n"))
}
document.body.addEventListener("keydown", function(e) {
var key = e.which;
if( /^(37|38|39|40)$/.test(key) ) e.preventDefault(); // prevent browser default stuff
if (key === 38) --cam.y;
if (key === 40) ++cam.y;
if (key === 37) --cam.x;
if (key === 39) ++cam.x;
// Fix movement to tileMap area boundary
cam.y = Math.max(0, Math.min(cam.y, tileMap.length - cam.height));
cam.x = Math.max(0, Math.min(cam.x, tileMap[0].length - cam.width));
tileViewport();
});
// INITIALIZE
tileViewport();
Click here and user your keyboard arrows!
Now that the above works correctly you can:
Add logic for obstacles behavior by using the new viewMap Array
prefetch new tiles for your canvas,
animate the canvas depending on the movement

CSS3D matrix generation

I am trying to achieve following effect using css3 and javascript when we move mouse to center div (MouseOver effect)
I have created small library which accepts 3 arguments element,sourcePoints,destination points and returns css3D matrix and update element. here is my javascript code.
var BLEND = BLEND || {};
BLEND.Util = BLEND.Util || {};
function print(msg) {
console.log(msg);
}
BLEND.Util.VendorPrefix = "";
BLEND.Util.DetectVendorPrefix = function() {
var styles = window.getComputedStyle(document.documentElement, ''),
pre = (Array.prototype.slice.call(styles).join('').match(/-(moz|webkit|ms)-/) || (styles.OLink === '' && ['', 'o']))[1];
BLEND.Util.VendorPrefix = pre[0].toUpperCase() + pre.substr(1) + "Transform";
}
BLEND.Util.DetectVendorPrefix();
BLEND.TransformElement = function(elm, src, dest) {
var L = [[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]];
var R = [0, 0, 0, 0, 0, 0, 0, 0];
for (var i = 0; i < 4; i++) {
L[i] = [];
L[i][0] = L[i + 4][3] = src[i].x;
L[i][2] = L[i + 4][4] = src[i].y;
L[i][2] = L[i + 4][5] = 1;
L[i][3] = L[i][4] = L[i][5] = L[i + 4][0] = L[i + 4][3] = L[i + 4][2] = 0;
L[i][6] = -src[i].x * dest[i].x;
L[i][7] = -src[i].y * dest[i].x;
L[i + 4][6] = -src[i].x * dest[i].y;
L[i + 4][7] = -src[i].y * dest[i].y;
R[i] = dest[i].x;
R[i + 4] = dest[i].y;
}
var RM = [];
for (i = 0; i < R.length; i++) {
RM[i] = [R[i]];
}
var Left = Matrix.create(L);
var Right = Matrix.create(R);
var res = Matrix.calculate(Left, Right);
print (res);
if (BLEND.Util.VendorPrefix == 'WebkitTransform') {
var matrix3D = new CSSMatrix();
matrix3D.m11 = res.get(0,0);
matrix3D.m12 = res.get(3,0);
matrix3D.m13 = 0;
matrix3D.m14 = res.get(6,0);
matrix3D.m21 = res.get(1,0);
matrix3D.m22 = res.get(4,0);
matrix3D.m23 = 0;
matrix3D.m24 = res.get(7,0);
matrix3D.m31 = 0;
matrix3D.m32 = 0;
matrix3D.m33 = 1;
matrix3D.m34 = 0;
matrix3D.m41 = res.get(2,0);
matrix3D.m42 = res.get(5,0);
matrix3D.m43 = 0;
matrix3D.m44 = 1;
elm.style.webkitTransform = matrix3D;
} else {
if (BLEND.Util.VendorPrefix === "")
BLEND.Util.DetectVendorPrefix();
elm.style[BLEND.Util.VendorPrefix] = "matrix3d(" + res.get(0,0) + "," + res.get(3,0) + ", 0," + res.get(6,0) + "," + res.get(1,0) + "," + res.get(4,0) + ", 0," + res.get(7,0) + ",0, 0, 1, 0," + res.get(2,0) + "," + res.get(5,0) + ", 0, 1)";
}
}
UPDATE: Here is JSFiddle
I am calling TransformElement method for each of 9 div with proper source and destination coordinates. But its not working as expected. Please suggest the possible solution.
can we do it using three.js in anyway (just asking may be its silly idea)?
UPDATE: Can we do it with CSS3D renderer and Three.js.
Idea is to create plane and slice it in 3x3 grid and on mouse over of each face of plane we can scale that div upside and respectivly we have to scale others div according to current div? Is it possible?
I didn't try to use your library but here is my solution to your problem: http://codepen.io/bali_balo/pen/87b980a2acf722b1c9feb35f3fcb1c65/ (I used Haml and SCSS, you can see compiled code by clicking the little eye on the top right corner of each panel)
I used this article to write this code.
9 matrices are computed first (corresponding to the hovered tile and every surrounding ones), using numericjs and fomulas available on the article linked before. Then on mouseover these matrices are applied to the corresponding tiles. Here is the code to get the transform matrix knowing the locations of 4 points before and after transformation:
//from and to are arrays of 4 objects containing a property x and a property y
//describing the 4 points of the quadrilateral to transform
function getTransform(from, to)
{
var A = [], i;
for(i = 0; i < 4; i++)
{
A.push([from[i].x, from[i].y, 1, 0, 0, 0, -from[i].x * to[i].x, -from[i].y * to[i].x]);
A.push([0, 0, 0, from[i].x, from[i].y, 1, -from[i].x * to[i].y, -from[i].y * to[i].y]);
}
var b = [];
for(i = 0; i < 4; i++)
{
b.push(to[i].x);
b.push(to[i].y);
}
//Uses numericjs to solve matrices equations
//returns h knowing A and b where A.h = b
var h = numeric.solve(A, b);
//Column major representation
return [[h[0], h[3], 0, h[6]],
[h[1], h[4], 0, h[7]],
[ 0 , 0 , 1, 0 ],
[h[2], h[5], 0, 1 ]];
}
Note that, as mentionned in my code, if you want to animate transitions, you can't just use CSS (as it would just transition each number of the matrix, but this would rarely give an appropriate result). You could try doing it using javascript but it might be a bit slow (I didn't test it), because you could not cache matrices and would have to compute them at every frame.

Categories

Resources