Im creating an object that randomly moves in a natural way using noise like this (works as intended):
The objects encounter a collision and their trajectory is manipulated, the movement path now changes to straight line (words as intended)
thisRabbit.x = _world.width * (noise(thisRabbit.t));
thisRabbit.y = _world.height * (noise(thisRabbit.t+5));
thisRabbit.t += 0.001;
The problem is after this movement , i want the object to start moving in a random direction again as it was initially. If i use the same function, the object jumps to the last location before the trajectory was modified.
let vx = this.acquiredFood[0] - this.x;
let vy = this.acquiredFood[1] - this.y;
let f = (this.genes.speed + 10) / Math.sqrt(vx*vx+vy*vy);
vx = vx * f;
vy = vy * f;
let newX = this.x + vx;
let newY = this.y + vy;
So how do i get the object to move as before, given a starting position
edit: snippet here: https://editor.p5js.org/vince.chinner/sketches/HPFKR8eIw
Your problem is that you used a factor from 0 to 1 generated with noise and an incremented seed to generate the position by multiplying directly the world dimentions. When reaching food, you cannot increment the seed as to be in the exact position where the movement to get your food led you (I found no inverse function for noise to get the seed from the return value).
What you need to do instead is use the noise to increment or decrement the coordinates, so that no matter where the seed is, you don't loose your current position.
Here are the different corrections I applied to the code, as there were also syntax errors, I can't really paste the whole stuff here for copyright reasons (you didn't share the whole code here and the sketch belongs to you)
MAIN CORRECTION:
used a var found because returning from the forEach callback doesn't make you leave the findFood function, but the callback one. And the forEach loop doesn't stop. Using this var prevents the further forEach tests to be made and allows you to return from findFood so that no further move is made after seeing food.
noise is now applied to a value of 4 and I subtract 2, so that x and y now change with a range of -2 to 2 each. Of course, with this method, you need to check against world dimentions or else the rabbit could leave the world. The seed increment has been changed too or else it would vary too slowly (adapt values as you wish)
findFood(){
var thisRabbit = this, found = false;
_world.food.forEach(f => {
if(!found){
let d = int(dist(f[0], f[1], thisRabbit.x, thisRabbit.y));
if(d < (thisRabbit.genes.vision / 2)+3){
thisRabbit.state = "foundFood";
this.acquiredFood = f;
found = true;
}
}
});
if(found){ return; }
thisRabbit.x += (noise(thisRabbit.t) * 4) - 2;
if(thisRabbit.x < 0){ thisRabbit.x = 0; }
if(thisRabbit.x > _world.width){ thisRabbit.x = _world.width; }
thisRabbit.y += (noise(thisRabbit.t + 5) * 4) - 2;
if(thisRabbit.y < 0){ thisRabbit.y = 0; }
if(thisRabbit.y > _world.height){ thisRabbit.y = _world.height; }
thisRabbit.t += 0.01;
}
SYNTAX ERRORS:
lines 23 / 24: assignment should be with a value (null or false)
this.genes = null;
this.acquiredFood = null;
lines 129 to 133: end you instructions with a ; instead of a ,
this.width = w;
this.height = h;
this.foodDensity = foodDensity;
this.food = [];
this.rabits = [];
line 156 to 160: there should be no space between rabbit and .t. Additionnally, because the coordinates are not directly linked to t, I would prefer to use random for starting position:
let x = this.width * random();
let y = this.height * random();
let _rabbit = new rabbit(x, y);
_rabbit.genes = genes;
_rabbit.t = t;
Related
I'm currently working on a maze generating algorithm called recursive division. The algorithm is quite simple to understand: Step 1: if the height of your chamber is smaller than the width, divide your grid/chamber with a vertical line. If the height is greater than the width, then divide your chamber with a horizontal line. Step 2: Repeat step 1 with the sub-chambers that were created by the lines. You want to repeat these steps until you get a maze (until the width or height equals 1 unit).
The problem that I have with this algorithm is that JavaScript prints out a RangeError, meaning that I called the function that creates the maze too many times (I'm trying to implement this algorithm with a recursive function). Is there any way to avoid/prevent this from happening? Or am I missing something important in my code that makes the algorithm not work properly?
I have tried to implement a trampoline function, but since I'm a beginner I just don't understand it well enough to implement my self. I have also restarted my entire project ruffly 3 times with some hope that I will come up with a different approach to this problem, but I get the same error every time.
My code here:
//leftCord = the left most x coordinate of my chamber/grid, upCord = the upmost y coordinate of my
grid etc.
//(0, 0) IS POSITIONED IN THE LEFT TOP NODE OF MY GRID
function createMaze(leftCord, rightCord, upCord, downCord) {
var height = Math.abs(downCord - upCord);
var width = Math.abs(rightCord - leftCord);
if (height < 2 || width < 2) {
//The maze is completed!
return;
} else {
if (height < width) {
//cut the chamber/grid vertically
//Getting a random number that's EVEN and drawing the function x = 'random number' on the grid
var x = randomNum(leftCord / 2, rightCord / 2) * 2;
var lineX = [];
for (i = upCord; i < downCord; i++) {
lineX.push(grid[i][x]);
}
//Making a random door/passage and making sure it's ODD
var randomDoor = randomNum(0, lineX.length / 2) * 2 + 1;
lineX.splice(randomDoor, 1);
//Drawing the line
for (i = 0; i < lineX.length; i++) {
lineX[i].className = "wall";
}
//Making the same thing again, but with the left and right sub-chambers that were created by the line
createMaze(leftCord, x, upCord, downCord);
createMaze(x, rightCord, upCord, downCord);
} else {
//cut the chamber/grid horizontally
//Getting a random number that's EVEN and drawing the function y = 'random number' on the grid
var y = randomNum(0, downCord / 2) * 2;
var lineY = [];
for (i = leftCord; i < rightCord; i++) {
lineY.push(grid[y][i]);
}
//Making a random door/passage and making sure it's ODD
var randomDoor = randomNum(0, lineY.length / 2) * 2 + 1;
lineY.splice(randomDoor, 1);
//Drawing the line
for(i = 0; i < lineY.length; i++){
lineY[i].className = "wall";
}
//Making the same thing again, but with the upper and lower-chambers that were created by the line
createMaze(leftCord, rightCord, upCord, y);
createMaze(leftCord, rightCord, y, downCord);
}
}
}
This is happening because you never initialize i with var- it is sent into the global scope and is overwritten each function call.
Trying to write a simple web app to solve the following common calculus problem in JavaScript.
Suppose you wanted to make an open-topped box out of a flat piece of cardboard that is L long by W wide by cutting the same size
square (h × h) out of each corner and then folding the flaps to form the box,
as illustrated below:
You want to find out how big to make the cut-out squares in order to maximize the volume of the box.
Ideally I want to avoid using any calculus library to solve this.
My initial naive solution:
// V = l * w * h
function getBoxVolume(l, w, h) {
return (l - 2*h)*(w - 2*h)*h;
}
function findMaxVol(l, w) {
const STEP_SIZE = 0.0001;
let ideal_h = 0;
let max_vol = 0;
for (h = 0; h <= Math.min(l, w) / 2; h = h + STEP_SIZE) {
const newVol = getBoxVolume(l, w, h);
if (max_vol <= newVol) {
ideal_h = h;
max_vol = newVol;
} else {
break;
}
}
return {
ideal_h,
max_vol
}
}
const WIDTH_1 = 20;
const WIDTH_2 = 30;
console.log(findMaxVol(WIDTH_1, WIDTH_2))
// {
// ideal_h: 3.9237000000038558,
// max_vol: 1056.3058953402121
// }
The problem with this naive solution is that it only gives an estimate because you have to provide STEP_SIZE and it heavily limits the size of the problem this can solve.
You have an objective function: getBoxVolume(). Your goal is to maximize the value of this function.
Currently, you're maximizing it using something equivalent to sampling: you're checking every STEP_SIZE, to see whether you get a better result. You've identified the main problem: there's no guarantee the edge of the STEP_SIZE interval falls anywhere near the max value.
Observe something about your objective function: it's convex. I.e., it starts by going up (when h = 0, volume is zero, then it grows as h does), it reaches a maximum, then it goes down, eventually reaching zero (when h = min(l,w)/2).
This means that there's guaranteed to be one maximum value, and you just need to find it. This makes this problem a great case for binary search, because given the nature of the function, you can sample two points on the function and know which direction the maximum lies relative to those two points. You can use this, with three points at a time (left, right, middle), to figure out whether the max is between left and middle, or middle and right. Once these values get close enough together (they're within some fixed amount e of each other), you can return the value of the function there. You can even prove that the value you return is within some value e' of the maximum possible value.
Here's pseudocode:
max(double lowerEnd, upperEnd) {
double midPoint = (upperEnd + lowerEnd) / 2
double midValue = getBoxVolume(l, w, midpoint)
double slope = (getBoxVolume(l, w, midpoint + epsilon) - midValue) / epsilon
if (Math.abs(slope) < epsilon2) { // or, if you choose, if (upperEnd - lowerEnd < epsilon3)
return midpoint
}
if (slope < 0) { // we're on the downslope
return max(lowerEnd, midPoint)
}
else { // we're on the up-slope
return max(midpoint, upperEnd)
}
}
After realising that the derivative of the volume function is a second degree polynomial you can apply a quadratic formula to solve for x.
Using calculus, the vertex point, being a maximum or minimum of the function, can be obtained by finding the roots of the derivative
// V = l * w * h
function getBoxVolume(l, w, h) {
return (l - 2*h)*(w - 2*h)*h;
}
// ax^2 + bx + c = 0
function solveQuad(a, b, c) {
var x1 = (-1 * b + Math.sqrt(Math.pow(b, 2) - (4 * a * c))) / (2 * a);
var x2 = (-1 * b - Math.sqrt(Math.pow(b, 2) - (4 * a * c))) / (2 * a);
return { x1, x2 };
}
function findMaxVol(l, w) {
// V'(h) = 12h^2-4(l+w)h+l*w - second degree polynomial
// solve to get the critical numbers
const result = solveQuad(12, -4*(l + w), l*w)
const vol1 = getBoxVolume(l, w, result.x1);
const vol2 = getBoxVolume(l, w, result.x2);
let ideal_h = 0;
let max_vol = 0;
// check for max
if (vol1 > vol2) {
ideal_h = result.x1;
max_vol = vol1;
} else {
ideal_h = result.x2;
max_vol = vol2;
}
return {
ideal_h,
max_vol
}
}
const WIDTH_1 = 20;
const WIDTH_2 = 30;
console.log(findMaxVol(WIDTH_1, WIDTH_2))
// {
// ideal_h: 3.9237478148923493,
// max_vol: 1056.30589546119
// }
I want to write a program that draws a surface (X * Y) evenly.I already have an approach for this at the moment, but it doesn't quite work yet and is also very slow. Since this approach is far too slow, I do not want to pursue it much further.
At the beginning there is always the first point and the last one - so with an area of 10 x 10 the pixel at position 0 and the pixel at position 99.
Then the next best pixel must be found, i.e. the one with the largest distance. This is relatively easy with only two points - (99 - 0 / 2) so 49 or 48.
Now you have to look for the next best one again. So (49 - 0) / 2 or if 48 was taken before (99 - 48) / 2 so 24/25 or 74/75.
This process must be repeated until the correct sequence is found.
0,99,49,74,24,36,61,86,12,42,67,92,6,18,30,55,80,45,70,95,3,9,15,21,27,33,39,52,58,64,77,83,89,47,72,97,1,4,7,10,13,16,19,22,25,28,31,34,37,40,43,50,53,56,59,62,65,68,75,78,81,84,87,90,93,2,5,8,11,14,17,20,23,26,29,32,35,38,41,44,46,48,51,54,57,60,63,66,69,71,73,76,79,82,85,88,91,94,96,98
I also added a small example here, which shows how it should work. The function getElementOrder should be replaced by a mathematical expression to get the fastest possible solution.
// define variables
const width = 20; // this will be > 2100
const height = 20; // this will be > 1600
const size = 20;
let elements = {};
// create all cells
for (let x = 0; x < width; x++) {
for (let y = 0; y < height; y++) {
let id = x + y * height;
let div = document.createElement("div");
div.style.border = "solid 1px black";
div.style.width = size + "px";
div.style.height = size + "px";
div.style.position = "absolute";
div.style.left = x * size + "px";
div.style.top = y * size + "px";
div.style.backgroundColor = "#F0F0F0";
let textDiv = document.createElement("div");
textDiv.innerHTML = id;
textDiv.style.position = "absolute";
textDiv.style.fontSize = "6pt";
textDiv.style.top = "1px";
textDiv.style.right = "1px";
div.appendChild(textDiv);
document.body.appendChild(div);
elements[id] = div;
}
}
function getElementOrder(width, height) {
/* BAD SLOW CODE START - This sould be better: */
const length = width * height;
const order = [0, length -1];
const result = [0, length -1];
while (order.length !== length) {
let index = 0;
let diff = 0;
for (let i = 0, m = order.length - 1; i < m; i++) {
let localDiff = order[i+1] - order[i];
if (localDiff > diff) {
index = i;
diff = localDiff;
}
}
let offset = Math.floor(diff/2);
let value = order[index] + offset;
order.splice(index + 1, 0, value);
result.push(value);
}
return result;
/* BAD SLOW CODE END */
}
// get the draw order
let order = getElementOrder(width, height);
// change color of each pixel in draw order
let interval = setInterval(() => {
if (order.length === 0) {
clearInterval(interval);
return;
}
const value = order.shift();
elements[value].style.backgroundColor = "#00abab";
}, 10);
Are there any mathematical approaches to solve this problem?
You are welcome to post better solutions, approaches or links to mathematical formulas for this problem here.
I think I get what you're trying to accomplish, and what the underlying routine is. The way I see it, you're probably overcomplicating the question of "finding the biggest distance", since from what I can see, what you're basically doing is halving increasingly fine intervals.
So, here's my version:
function getElementOrder(width, height) {
const length = width * height;
const order = [ 0 ];
for (let denominator = 2; order.length < length; denominator *= 2) {
for (let enumerator = 1; enumerator < denominator; enumerator += 2) {
order.push(Math.round(length * enumerator / denominator));
}
}
return order;
}
I'm using very long and clunky variable names to make the principle behind it clearer: if you project the entire interval of [0, width*height] to the interval of [0, 1] then what you're doing is adding 1/2, then 1/4 and 3/4, then 1/8 and 3/8 and 5/8 and 7/8, and so on; each time you multiply the denominator by 2, and take all the odd-numbered multiples.
(Addendum: you can probably squeeze even better performance out of it by using a fixed-length TypedArray for the results, and adding elements by index instead of using .push(). I just didn't want to obscure the gist of the solution with the additional loop variable and such.)
Question: What in the world is this piece of code doing?
Also: Is the way 'w' is being used some sort of existing algorithm? I'm trying to figure out the intent of the function, or at least describe what sorts of numbers it produces.
Context: I'm looking at Martin O'Leary's "Fantasy Map Generation" code - full source here, which in short summary generates fantasy maps on the canvas. There is some insightful explanations of how the higher level process works in a blog post, but this is too low level to get any coverage there. There is a particular function called 'rnorm' that gets used in a couple of places, and I'm lost at how it works. I've included it below, followed by a couple of instances where it comes up for some context. Any help on what this thing is doing would be great!
var rnorm = (function() {
var z2 = null;
function rnorm() {
if (z2 != null) {
var tmp = z2;
z2 = null;
return tmp;
}
var x1 = 0;
var x2 = 0;
var w = 2.0;
while (w >= 1) {
x1 = runif(-1, 1);
x2 = runif(-1, 1);
w = x1 * x1 + x2 * x2;
}
w = Math.sqrt(-2 * Math.log(w) / w);
z2 = x2 * w;
return x1 * w;
}
return rnorm;
})();
runif(), which is called in the code above, is a short function that generates a random number between two given values
function runif(lo, hi) {
return lo + Math.random() * (hi - lo);
}
This code is used to produce random vectors (actually the only place it's used during the generation process) -
function randomVector(scale) {
return [scale * rnorm(), scale * rnorm()];
}
But I think it's doing more than that because the following, when provided a direction of 'randomVector(4),' produces a gradual slope over the entire mesh heightmap: EDIT: no, it actually is having no effect on the gradual slope. That comes from some sneakyness using the fact that one side of the map is 0,0, and the other side of the map is width,height, which creates numbers that gradually increase.
function slope(mesh, direction) {
return mesh.map(function (x) {
return x[0] * direction[0] + x[1] * direction[1];
});
}
Let me know if there's anything else I should be providing. This is my first question here, so I may be a little soft on conventions.
I think it's horrible code. It appears to create a pair of values, z1 and z2, but instead of putting them in a tuple and returning that it returns z1 and on every second call the corresponding z2 value. I have no idea why they'd do such a thing, my only guess would be to avoid allocation of objects and make usage syntactically more convenient.
It should be simplified to
function rnorm() {
var x1 = 0;
var x2 = 0;
var w = 2.0;
while (w >= 1) {
x1 = runif(-1, 1);
x2 = runif(-1, 1);
w = x1 * x1 + x2 * x2;
}
w = Math.sqrt(-2 * Math.log(w) / w);
return [x1 * w, x2 * w];
}
function randomVector(scale) {
var [z1, z2] = rnorm();
return [scale * z1, scale * z2];
}
A modern compiler should be able to avoid array allocation for the returned literal and subsequent destructuring. If it's not, you can do it manually by inlining rnorm in randomVector, especially if this is the only place where it's called anyway.
I'm new in JavaScript and I'm creating a kind of shooting game. I'm to make 1 object to move toward another object. So the "bullet" get the location of the "prey" and it will move toward it. I have no idea how to implement that, I can't find anything similar on the internet. So, I tried at first something simpler:
In the following code the "bullet" move to the left. How can I specify to move it toward an object?
I have 2 object. It's the enemyBullet object(not the bullet object) which should go toward another object.
PS: English is not my native language. Sorry for any confusion
thanks.
this.draw = function () {
this.context.clearRect(this.x + 2, this.y + 1.5, this.width - 4.5, this.height);
//x+ the speed make it go to the left
this.x += this.speed;
if (self === "bullet" && this.x >= this.canvasWidth) {
return true;
}
else if (self === "enemyBullet" && this.x >= 1000) {
console.log(this.x);
return true;
}
else {
if (self === "bullet") {
this.context.drawImage(imageRepository.bullet, this.x, this.y);
}
else if (self === "enemyBullet") {
this.context.drawImage(imageRepository.enemyBullet, this.x, this.y);
}
return false;
}
};
Normalised vector
You need to find the normalised vector from one object to the next. A vector is just an arrow that has a direction and a length. In this case we normalise the length, that is make it equal to 1 unit long. We do this so we can easily set a speed when using the vector.
Function to return a vector from one point to the next
// Code is in ES6
// fx, fy is from coordinate
// tx, ty is to coordinate
function getNormVec(fx, fy, tx, ty){
var x = tx - fx; // get differance
var y = ty - fy;
var dist = Math.sqrt(x * x + y * y); // get the distance.
x /= dist; // normalised difference
y /= dist;
return {x,y};
}
Once you have the vector you can move an object by adding the vector times the speed. Example of creating and moving a bullet from myObj to myTarget
var myObj = {}
var myTarget = {};
var myBullet = {}
myObj.x = 100;
myObj.y = 100;
myTarget.x = 1000
myTarget.y = 1000
var vecToTag = getNormVect(myObj.x, myObj.y, myTarget.x, myTarget.y);
myBullet.nx = vecToTag.x; // set bullets direction vector
myBullet.ny = vecToTag.y;
myBullet.x = myObj.x; // set the bullet start position.
myBullet.y = myObj.y;
myBullet.speed = 5; // set speed 5 pixels per frame
To move the bullet
myBullet.x += myBullet.nx * myBullet.speed;
myBullet.y += myBullet.ny * myBullet.speed;