how to generate random points in a circle (fractal trees) - javascript

I am making a tree with space-colonization algorithm in javascript. (with p5.js)
I followed the tutorial of https://www.youtube.com/watch?v=kKT0v3qhIQY&ab_channel=TheCodingTrain
or in C#
http://www.jgallant.com/procedurally-generating-trees-with-space-colonization-algorithm-in-xna/
I want to make a customized area (in this case a circle) where the leaves are generated randomly.
in Leaf.js I try to include some calculations of how the leaves get the random Coordinates within the circle.
So there is sketch.js , leaf.js, branch.js, tree.js
//leaf.js
function Leaf() {
function getChord(){
var r = 150;
var angle = random(0, 2 * PI);
var xChord = 2*r + sqrt(r) * cos(angle);
var yChord = r + sqrt(r) * sin(angle);
return (createVector(xChord, yChord));
}
this.pos = getChord();
this.reached = false;
// // var t = 2 * PI * random(0,1);
// // var r = sqrt(random(0,1));
// // var x = r * cos(t);
// // var y = r * sin(t);
// // this.pos = createVector(x, y);
// this.reached = false;
this.show = function() {
fill(58, 126, 34);
noStroke();
ellipse(this.pos.x, this.pos.y, 4, 4);
};
}
//branch.js
function Branch(parent, pos, dir) {
this.pos = pos;
this.parent = parent;
this.dir = dir;
this.origDir = this.dir.copy();
this.count = 0;
this.len = 3;
this.reset = function() {
this.dir = this.origDir.copy();
this.count = 0;
};
this.next = function() {
var nextDir = p5.Vector.mult(this.dir, this.len);
var nextPos = p5.Vector.add(this.pos, nextDir);
var nextBranch = new Branch(this, nextPos, this.dir.copy());
return nextBranch;
};
this.show = function() {
if (parent != null) {
stroke(151, 53, 48);
strokeWeight(1);
line(this.pos.x, this.pos.y, this.parent.pos.x, this.parent.pos.y);
}
};
}
And then in tree.js I push every leaf into leaves.
//tree.js
function Tree() {
this.leaves = [];
this.branches = [];
for (var i = 0; i < 1500; i++) {
this.leaves.push(new Leaf());
}
var pos = createVector(width / 2, height);
var dir = createVector(0, -1);
var root = new Branch(null, pos, dir);
this.branches.push(root);
var current = root;
var found = false;
while (!found) {
for (var i = 0; i < this.leaves.length; i++) {
var d = p5.Vector.dist(current.pos, this.leaves[i].pos);
if (d < max_dist) {
found = true;
}
}
if (!found) {
var branch = current.next();
current = branch;
this.branches.push(current);
}
}
this.grow = function() {
for (var i = 0; i < this.leaves.length; i++) {
var leaf = this.leaves[i];
var closestBranch = null;
var record = max_dist;
for (var j = 0; j < this.branches.length; j++) {
var branch = this.branches[j];
var d = p5.Vector.dist(leaf.pos, branch.pos);
if (d < min_dist) {
leaf.reached = true;
closestBranch = null;
break;
} else if (d < record) {
closestBranch = branch;
record = d;
}
}
if (closestBranch != null) {
var newDir = p5.Vector.sub(leaf.pos, closestBranch.pos);
newDir.normalize();
closestBranch.dir.add(newDir);
closestBranch.count++;
}
}
for (var i = this.leaves.length - 1; i >= 0; i--) {
if (this.leaves[i].reached) {
this.leaves.splice(i, 1);
}
}
for (var i = this.branches.length - 1; i >= 0; i--) {
var branch = this.branches[i];
if (branch.count > 0) {
branch.dir.div(branch.count + 1);
this.branches.push(branch.next());
branch.reset();
}
}
};
this.show = function() {
for (var i = 0; i < this.leaves.length; i++) {
this.leaves[i].show();
}
for (var i = 0; i < this.branches.length; i++) {
this.branches[i].show();
}
};
}
//sketch.js
var tree;
var max_dist = 30;
var min_dist = 10;
function setup() {
createCanvas(600, 600);
tree = new Tree();
}
function draw() {
background(111, 149, 231);
tree.show();
tree.grow();
}
Some how in the function getChord I think there is an infinite loop or having trouble getting the random value? Because it is not loading at all... It works when I change this.pos = getChord(); to this.pos = createVector(random(width),random(height-100);
Does anyone know how to solve this?
Or how to write codes to make an area of the circle where the leaves can be generated?
Thank you!

Related

Detecting if something in a position already exists

I added something to my code that's supposed to detect when an object (pixel) already exists, and then replace the color instead of creating a new one. However, for some reason, it just doesn't do anything.
Code:
var gridSize = 16;
var pixels = [];
var draw = function() {
background(255, 255, 255);
for (var i = 0; i < gridSize; i++) {
stroke(0, 0, 0);
line(400/gridSize*i, 0, 400/gridSize*i, 400);
line(0, 400/gridSize*i, 400, 400/gridSize*i);
}
for (var i = 0; i < pixels.length; i++) {
noStroke();
fill(pixels[i][2][0], pixels[i][2][1], pixels[i][2][2]);
rect(pixels[i][0]*(400/gridSize), pixels[i][1]*(400/gridSize), 400/gridSize, 400/gridSize);
}
document.getElementById("output").innerHTML = pixels;
fill(255, 0, 0);
text(alreadyExists, 200, 200);
};
var mousePressed = function() {
alreadyExists = false;
for (var i = 0; i < pixels.length; i++) {
if (pixels[i] === [ceil(mouseX/(400/gridSize))-1, ceil(mouseY/(400/gridSize))-1, [document.getElementById("color1").value, document.getElementById("color2").value, document.getElementById("color3").value]]) {
alreadyExists = true;
pixels[i][2] = [document.getElementById("color1").value, document.getElementById("color2").value, document.getElementById("color3").value];
}
}
if (!alreadyExists) {
pixels.push([ceil(mouseX/(400/gridSize))-1, ceil(mouseY/(400/gridSize))-1, [document.getElementById("color1").value, document.getElementById("color2").value, document.getElementById("color3").value]]);
}
};
var mouseDragged = function() {
alreadyExists = false;
for (var i = 0; i < pixels.length; i++) {
if (pixels[i] === [ceil(mouseX/(400/gridSize))-1, ceil(mouseY/(400/gridSize))-1, [document.getElementById("color1").value, document.getElementById("color2").value, document.getElementById("color3").value]]) {
alreadyExists = true;
pixels[i][2] = [document.getElementById("color1").value, document.getElementById("color2").value, document.getElementById("color3").value];
}
}
if (!alreadyExists) {
pixels.push([ceil(mouseX/(400/gridSize))-1, ceil(mouseY/(400/gridSize))-1, [document.getElementById("color1").value, document.getElementById("color2").value, document.getElementById("color3").value]]);
}
};
When comparing arrays, you need to compare individually every item, in your case. I modified a the original example to minimize repetition of code blocks and to improve a bit speed and memory usage. You can also a working example here
The idea is to compare the pixels positions, encoded at index 0 for x and 1 for y. And then to compare the color channels, which are also encoded in an array so we need to then again compare individually every component.
The function samePixels in the following example is exactly doing that:
var samePixels = function (p1, p2) {
var samePosition = p1[0] === p2[0] && p1[1] === p2[1];
if (!samePosition) {
return false;
}
var colors1 = p1[2];
var colors2 = p2[2];
return (colors1[0] === colors2[0]) &&
(colors1[1] === colors2[1]) &&
(colors1[2] === colors2[2]);
}
Full source code for the js part:
var gridSize = 16;
var pixels = [];
var alreadyExists;
var color1 = document.getElementById('color1');
var color2 = document.getElementById('color2');
var color3 = document.getElementById('color3');
var draw = function() {
background(255, 255, 255);
for (var i = 0; i < gridSize; i++) {
stroke(0, 0, 0);
line(400/gridSize*i, 0, 400/gridSize*i, 400);
line(0, 400/gridSize*i, 400, 400/gridSize*i);
}
for (var i = 0; i < pixels.length; i++) {
stroke(0);
fill(pixels[i][2][0], pixels[i][2][1], pixels[i][2][2]);
rect(pixels[i][0]*(400/gridSize), pixels[i][1]*(400/gridSize), 400/gridSize, 400/gridSize);
}
document.getElementById("output").innerHTML = pixels;
fill(255, 0, 0);
text(alreadyExists, 200, 200);
};
var mousePressed = mouseDragged = function() {
alreadyExists = false;
closestPixel = [ceil(mouseX/(400/gridSize))-1, ceil(mouseY/(400/gridSize))-1, [color1.value, color2.value, color3.value]];
for (var i = 0; i < pixels.length; i++) {
if (samePixels(pixels[i], closestPixel)) {
alreadyExists = true;
pixels[i][2] = [color1.value, color2.value, color3.value];
break;
}
}
console.log('Does the pixel already exist?', alreadyExists);
if (!alreadyExists) {
pixels.push(closestPixel);
}
};
var samePixels = function (p1, p2) {
var samePosition = p1[0] === p2[0] && p1[1] === p2[1];
if (!samePosition) {
return false;
}
var colors1 = p1[2];
var colors2 = p2[2];
return (colors1[0] === colors2[0]) &&
(colors1[1] === colors2[1]) &&
(colors1[2] === colors2[2]);
}

vue show js 's error :TypeError: "this is undefined"

I want to make a dynamic water pipe with water flowing. The front picture is an error message.
The following picture is a method in the method in the vue page. I don't know if it will report an error. If you don't call this method, nothing will be reported.
The following code is the js file that introduces the vue page
//构造函数
function Createline(config) {
this.c = 0;
this.lines = [];
var self = this;
//初始化线
(function (self){
if(config.fx == "w"){
var number = config.canvas_w / (config.width + config.width/2 + config.jiange*2);
number = Math.ceil(number) + 1;
for(var n = 0 ; n < number ; n++){
var mxx = config.canvas_w - n*(config.width+config.width/2+config.jiange*2);
var lines_data = {
mx:mxx,
lx:mxx - config.width,
my:config.my,
ly:config.ly,
vx:config.vx,
vy:config.vy,
}
self.lines.push(lines_data);
var lines_data2 = {
mx:mxx-config.width-config.jiange,
lx:mxx-config.width-config.jiange-config.width/2,
my:config.my,
ly:config.ly,
vx:config.vx,
vy:config.vy,
}
self.lines.push(lines_data2);
}
}
if(config.fx == "h"){
var number = config.canvas_w / (config.width + config.width/2 + config.jiange*2);
number = Math.ceil(number) + 1;
for(var n = 0 ; n < number ; n++){
var myy = config.canvas_h - n*(config.width+config.width/2+config.jiange*2);
var lines_data3 = {
mx:config.mx,
lx:config.lx,
my:myy,
ly:myy - config.width,
vx:config.vx,
vy:config.vy,
}
self.lines.push(lines_data3);
var lines_data4 = {
mx:config.mx,
lx:config.lx,
my:myy-config.width-config.jiange,
ly:myy-config.width-config.jiange-config.width/2,
vx:config.vx,
vy:config.vy,
}
self.lines.push(lines_data4);
}
}
})(self);
}
//开始
Createline.prototype.begin = function (element,config) {
var canvasObj = document.getElementById(element);
var self = this;
var canvas = {
line_w: config.line_w || 3, //线条厚度
vx: config.vx || 0,//x轴运动速度
vy:config.vy || 0,//Y轴速度
color:config.color || "blue",//线条颜色
canvas_w:config.canvas_w || 0,//画布宽度
canvas_h:config.canvas_h || 0,//画布高度
mx: config.mx || 0,//线的开始位置X坐标
my: config.my || 0,//线的开始位置Y坐标
lx: config.lx || 0,//线的结束位置X坐标
ly: config.ly || 0,//线的结束位置Y坐标
fx:config.fx || "w",//运动方向 w 水平 ,h: 垂直
width:config.width || 20,//线的长度
jiange:config.jiange || 10, //线的间隔
}
if(canvasObj.getContext("2d")){
canvas.ctx = canvasObj.getContext("2d"),
canvasObj.width = canvas.canvas_w;
canvasObj.height = canvas.canvas_h;
}else{
console.log("当前环境不支持canvas");
return null;
}
setInterval(function(){
self.createline(self,canvas);
if(canvas.fx == "w"){
self.updateline_w(self,canvas);
}
if(canvas.fx == "h"){
self.updateline_h(self,canvas);
}
self.addline(self,canvas);
},config.time);
return canvas.ctx;
}
//画线
Createline.prototype.createline = function(self,canvas){
var content = canvas.ctx;
content.clearRect(0,0,canvas.canvas_w,canvas.canvas_h);
for(var i = 0 ; i < self.lines.length ; i++){
content.beginPath();
content.moveTo(self.lines[i].mx,self.lines[i].my);
//线经过的转折点或结束点
content.lineTo(self.lines[i].lx,self.lines[i].ly);
//线条宽度
content.lineWidth = canvas.line_w;
content.strokeStyle = canvas.color;
content.stroke();
//线条颜色
content.closePath();
//画线
}
}
//改变数据 水平运动
Createline.prototype.updateline_w = function(self,canvas){
for (var i = 0 ; i < self.lines.length ; i++) {
self.lines[i].mx = self.lines[i].mx + self.lines[i].vx;
self.lines[i].lx = self.lines[i].lx + self.lines[i].vx;
}
//超出画布的线条从数组内删除,减少系统消耗
var cnt = [];
for (var j = 0 ;j < self.lines.length ; j++) {
if(self.lines[j].lx > canvas.canvas_w){
cnt.push(j);
}
}
for(var k = 0; k < cnt.length ; k++){
self.lines.splice(k,1);
}
}
//改变数据 垂直运动
Createline.prototype.updateline_h = function(self,canvas){
for (var i = 0 ; i < self.lines.length ; i++) {
self.lines[i].my = self.lines[i].my + lines[i].vy;
self.lines[i].ly = self.lines[i].ly + lines[i].vy;
}
//超出画布的线条从数组内删除,减少系统消耗
var cnt = [];
for (var j = 0 ;j < self.lines.length ; j++) {
if( self.lines[j].ly > canvas.canvas_h){
cnt.push(j);
}
}
for(var k = 0; k < cnt.length ; k++){
self.lines.splice(k,1);
}
}
//增加线
Createline.prototype.addline = function(self,canvas){
var length = self.lines.length;
//横线
if(canvas.fx == "w"){
//判断是否需要生成线
if(self.lines[length-1].lx > 0){
//起点位置 等于 左右一根线的坐标减去间隔
var ww = self.lines[length - 1].lx - canvas.jiange;
//判断长短
if(self.c == 0){
var jg = canvas.width;
self.c = 10;
}else{
var jg = canvas.width/2;
self.c = 0;
}
var lxx = ww - jg;
var data = {
mx:ww,
lx:lxx,
my:canvas.my,
ly:canvas.ly,
vx:canvas.vx,
vy:canvas.vy,
}
self.lines.push(data);
}
}
//垂直
if(canvas.fx == "h"){
if(length>0){
//判断是否需要生成线
if(self.lines[length-1].ly > 0){
//起点位置 等于 左右一根线的坐标减去间隔
var my = self.lines[length - 1].ly - canvas.jiange;
//判断长短
if(c == 0){
var jg = canvas.width;
c = 10;
}else{
var jg = canvas.width/2;
c = 0;
}
var ly = my - jg;
var data = {
mx:canvas.mx,
lx:canvas.lx,
my:my,
ly:ly,
vx:canvas.vx,
vy:canvas.vy,
}
self.lines.push(data);
}
}else{
var data = {
mx:canvas.mx,
my:canvas.my,
lx:canvas.lx,
ly:canvas.ly,
vx:canvas.vx,
vy:canvas.vy,
}
self.lines.push(data);
}
}
}
export{
Createline
}
You need to use new when creating a instance for a constructor that uses this:
var cre = new Createline(data);
When a function is executed using new it does the following:
function User(name) {
// this = {}; (implicitly)
// add properties to this
this.name = name;
this.isAdmin = false;
// return this; (implicitly)
}

How to fix similar predictions in tenserflow.js

I'm doing a coding challenge from the coding train, and I'm trying to improve on his code. The idea is that the cars are driving around a race track. When I went back to check something, I noticed that I misspelled "activation: sigmoid", as in activation function. When I fixed it, the cars seemed to be driving in circles.
I'm a very new coder (as I am 12 years old), so many things in my code are broken, hard to understand, or just not finished. I'm also pretty new to stack overflow, so I might be breaking a lot of rules.
The link to download my project is here: https://1drv.ms/u/s!ApmY_SAko19ChzCKe5uNT7I9EZAX?e=YUg2ff
The misspelled words are at lines 29 and 34 in the nn.js file.
car.js
function pldistance(p1, p2, x, y) {
const num = abs((p2.y - p1.y) * x - (p2.x - p1.x) * y + p2.x * p1.y - p2.y * p1.x);
const den = p5.Vector.dist(p1, p2);
return num / den;
}
class Car {
constructor(brain, color = [random(255), random(255), random(255)]) {
this.colorGene = color;
this.dead = false;
this.finished = false;
this.fitness = 0;
this.rays = [];
this.wallRays = [];
this.degreeOfSight = degreeOfSight;
this.degreeOfRays = degreeOfSight / (numOfRays - 1);
if (this.degreeOfSight == 360) {
this.degreeOfRays = degreeOfSight / numOfRays;
}
this.pos = createVector(start.x, start.y);
this.vel = createVector();
this.acc = createVector();
this.sight = sight;
this.maxspeed = maxspeed;
this.maxforce = maxTurningSpeed;
this.currentGoal = 0;
this.timeTillDeadC = timeTillDead;
this.timeTillDead = this.timeTillDeadC;
this.goal;
this.rate = mutationRate;
if (degreeOfSight != 360) {
for (let a = -(this.degreeOfSight / 2); a <= this.degreeOfSight / 2; a += this.degreeOfRays) {
this.rays.push(new Ray(this.pos, radians(a)));
}
} else {
for (let a = -(this.degreeOfSight / 2); a < this.degreeOfSight / 2; a += this.degreeOfRays) {
this.rays.push(new Ray(this.pos, radians(a)));
}
}
for (let a = 0; a < 360; a += 45) {
this.wallRays.push(new Ray(this.pos, radians(a)));
}
if (brain) {
this.brain = brain.copy();
} else {
this.brain = new NeuralNetwork(this.rays.length + 2, 16, 2);
}
}
applyForce(force) {
this.acc.add(force);
}
update(x, y) {
this.timeTillDead--;
if (this.timeTillDead <= 0) {
this.dead = true;
}
if (!this.dead || this.finished) {
this.pos.add(this.vel);
this.vel.add(this.acc);
this.vel.limit(this.maxspeed);
this.acc.set(0, 0);
}
for (let i = 0; i < this.rays.length; i++) {
this.rays[i].rotate(this.vel.heading());
}
for (let i = 0; i < this.wallRays.length; i++) {
this.wallRays[i].rotate(this.vel.heading());
}
}
show(walls) {
push();
translate(this.pos.x, this.pos.y);
if (visualization) {
fill(this.colorGene[0], this.colorGene[1], this.colorGene[1]);
} else {
fill(0);
}
stroke(255);
const heading = this.vel.heading();
rotate(heading);
rectMode(CENTER);
rect(0, 0, 10, 5);
pop();
if (!this.dead) {
checkpoints[this.currentGoal].show();
}
for (let i = 0; i < this.rays.length; i++) {
let closest = null;
let record = this.sight;
for (let wall of walls) {
const pt = this.rays[i].cast(wall);
if (pt) {
const d = p5.Vector.dist(this.pos, pt);
if (d < record && d < this.sight) {
record = d;
closest = pt;
}
}
}
if (closest) {
if (showLines) {
ellipse(closest.x, closest.y, 4)
stroke(255, 100)
line(this.pos.x, this.pos.y, closest.x, closest.y);
}
}
}
}
check(checkpoints, walls) {
if (!this.dead) {
this.goal = checkpoints[this.currentGoal];
const d = pldistance(this.goal.a, this.goal.b, this.pos.x, this.pos.y);
if (d < 5) {
this.fitness++;
this.currentGoal++;
this.timeTillDead = this.timeTillDeadC;
if (this.currentGoal == checkpoints.length) {
this.finished = true;
this.fitness = this.fitness * 1.5;
if (endBarrier) {
this.dead = true;
} else {
this.currentGoal = 0;
}
}
}
}
for (let i = 0; i < this.wallRays.length; i++) {
let closest = null;
let record = this.sight;
for (let wall of walls) {
const pt = this.wallRays[i].cast(wall);
if (pt) {
const d = p5.Vector.dist(this.pos, pt);
if (d < record) {
record = d;
closest = pt;
}
}
}
if (record < 4) {
this.dead = true;
}
}
}
look(walls) {
const inputs = [];
for (let i = 0; i < this.wallRays.length; i++) {
let closest = null;
let record = this.sight;
for (let wall of walls) {
const pt = this.rays[i].cast(wall);
if (pt) {
const d = p5.Vector.dist(this.pos, pt);
if (d < record && d < this.sight) {
record = d;
closest = pt;
}
}
}
inputs[i] = map(record, 0, 50, 1, 0);
}
inputs.push(end.x);
inputs.push(end.y);
const output = this.brain.predict(inputs);
let angle = map(output[0], 0, 1, -PI, PI);
let speed = map(output[1], 0, 1, -this.maxspeed, this.maxspeed);
angle += this.vel.heading();
const steering = p5.Vector.fromAngle(angle);
steering.setMag(speed);
steering.limit(this.maxforce);
this.applyForce(steering);
}
mutateDemBabies() {
if (this.finished) {
this.rate = finishingMutationRate;
}
this.brain.mutate(this.rate);
let changeColor = this.brain.mutated();
if (changeColor) {
for (let color of this.colorGene) {
let r = map(random(20), 0, 20, -25, 25);
color += r;
}
}
this.rate = mutationRate;
}
dispose() {
this.brain.dispose();
}
}
nn.js
//<script src="https://cdn.jsdelivr.net/npm/#tensorflow/tfjs#1.1.0/dist/tf.min.js"></script>
class NeuralNetwork {
//this how many inputs, hidden, and output nodes there are. modelC is the brain that we want to copy to give to the new bird
constructor(inputNumber, hiddenNumber, outputNumber, modelC) {
if (modelC instanceof tf.Sequential) {
//this is the making a copy of the neural network
this.input_nodes = inputNumber;
this.hidden_nodes = hiddenNumber;
this.output_nodes = outputNumber;
this.model = modelC;
} else {
//this is the creating a random brain
this.input_nodes = inputNumber;
this.hidden_nodes = hiddenNumber;
this.output_nodes = outputNumber;
this.model = this.createBrain();
}
this.changeColor = false;
}
createBrain() {
//the model is the neural network
const model = tf.sequential();
//configuring the hidden layer
const hiddenLayer = tf.layers.dense({
units: this.hidden_nodes,
inputShape: [this.input_nodes],
activaation: "sigmoid"
});
//configuring the output layer
const outputLayer = tf.layers.dense({
units: this.output_nodes,
activaation: "sigmoid"
});
//adding the hidden layer to the model
model.add(hiddenLayer);
//adding the output layer to the model
model.add(outputLayer);
//returning the model
return model;
}
predict(inputs) {
//clearing the tensors after using them
//then returning the output
return tf.tidy(() => {
//creating a tensor with the inputs
const xs = tf.tensor2d([inputs]);
//running the inputs through the neural network
const ys = this.model.predict(xs);
//getting the raw numbers from the tensor object
const outputs = ys.dataSync();
//returning the outputs
return outputs;
});
}
copy() {
//clearing the tensors after using them
//then returning the output
return tf.tidy(() => {
//creating a new neural network
const modelCopy = this.createBrain();
//getting the weights from the old neural network
const weights = this.model.getWeights();
//setting the new weights
modelCopy.setWeights(weights);
//making a new network but this time with all the weights then returning it
return new NeuralNetwork(
this.input_nodes,
this.hidden_nodes,
this.output_nodes,
modelCopy
);
});
}
mutate(rate, colorGene) {
//clearing the tensors after using them
tf.tidy(() => {
this.changeColor = false;
//getting the weights so that we can change them later
const weights = this.model.getWeights();
//the variable that will be holding the mutated weights
const mutatedWeights = [];
for (let i = 0; i < weights.length; i++) {
//getting the shape of the current weights
let shape = weights[i].shape;
//making a copy of the raw numbers from the object tensor
//dataSync gets the numbers, but doesn't make a copy, so slice will make the copy
let values = weights[i].dataSync().slice();
for (let j = 0; j < values.length; j++) {
//if the random number is less than mutation rate the it runs the code
if (random(1) < rate) {
this.changeColor = true;
//mutating the value
//randomGaussianis returns a float from a series of numbers with a mean of 0
values[j] = values[j] + randomGaussian();
}
}
//holding the new value of each weight
mutatedWeights[i] = tf.tensor(values, shape);
}
//setting the mutated weights as the new weights
this.model.setWeights(mutatedWeights);
});
}
mutated() {
if (this.changeColor) {
this.changeColor = false;
return true;
} else {
this.changeColor = false;
return false;
}
}
dispose() {
//disposing the brain so that memory doesn't leak
this.model.dispose();
}
}

Hard-dropping pieces in tetris-like game

I'm building a tetris-like game, where in stead of removing just one line when you've got a full line I remove all the connected pieces. This has got me stumped on the hard-drop after clearing the pieces.
See this example for a quick and dirty version of what I'm trying to do.
function Board (width, height) {
this.width = width;
this.height = height;
this.board = [];
this.pieces = [];
for (var y = 0; y < this.height; y++) {
for (var x = 0; x < this.width; x++) {
if (!this.board[y]) {
this.board[y] = [];
}
this.board[y][x] = null;
}
}
this.canPlace = function(piece, at) {
for (var y = 0; y < piece.getHeight(); y++) {
for (var x = 0; x < piece.getWidth(); x++) {
if ((y+at.y >= this.height) || this.board[y+at.y][x+at.x]) {
return false;
}
}
}
return true;
}
this.hasFullLine = function(line) {
for (var x = 0; x < this.width; x++) {
if (!this.board[line][x]) {
return false;
}
}
return true;
}
this.place = function(piece) {
var position = piece.getPosition();
var shape = piece.getShape();
for (var y = 0; y < piece.getHeight(); y++) {
for (var x = 0; x < piece.getWidth(); x++) {
if (shape[y][x]) {
this.board[y+position.y][x+position.x] = piece;
}
}
}
if (this.pieces.indexOf(piece) === -1) {
this.pieces.push(piece);
}
piece.render();
}
this.hardDropPieces = function() {
var pieces = this.pieces.slice();
pieces = pieces.sort(function(a,b) {
var aBottom = a.getPosition().y+a.getHeight();
var bBottom = b.getPosition().y+b.getHeight();
return bBottom-aBottom;
});
for (var i = 0; i < pieces.length; i++) {
this.hardDrop(pieces[i]);
}
}
this.hardDrop = function(piece) {
var position = piece.getPosition();
this.clearArea(piece);
while(this.canPlace(piece, {x: piece.getPosition().x, y: piece.getPosition().y+1})) {
piece.setPosition(piece.getPosition().x, piece.getPosition().y+1);
}
this.place(piece);
}
this.clearArea = function(piece) {
var position = piece.getPosition();
var shape = piece.getShape();
for (var y = 0; y < piece.getHeight(); y++) {
for (var x = 0; x < piece.getWidth(); x++) {
if (shape[y][x]) {
this.board[y+position.y][x+position.x] = null;
}
}
}
}
this.remove = function(piece) {
this.clearArea(piece);
this.pieces.splice(this.pieces.indexOf(piece),1);
}
this.clearPiecesOnLine = function(line) {
var piecesToClear = [];
for (var x = 0; x < this.width; x++) {
var piece = this.board[line][x];
if (piecesToClear.indexOf(piece) === -1) {
piecesToClear.push(piece);
}
}
for (var i = 0; i < piecesToClear.length; i++) {
this.remove(piecesToClear[i]);
}
return piecesToClear;
}
this.toString = function() {
var str = "";
for (var y = 0; y < this.height; y++) {
for (var x = 0; x < this.width; x++) {
str += this.board[y][x] ? "1" : "0";
}
str += "\n";
}
return str;
}
}
function Piece (shape, fill, stroke, paper, cellWidth) {
this.shape = shape;
this.fill = fill;
this.stroke = stroke;
this.cellWidth = cellWidth;
this.svgGroup = paper.g().append();
this.position = {x:0, y:0};
this.width = this.shape[0].length;
this.height = this.shape.length;
this.removed = false;
for (var y = 0; y < this.height; y++) {
for (var x = 0; x < this.width; x++) {
if (this.shape[y][x]) {
var rect = paper.rect(x*cellWidth, y*cellWidth, cellWidth, cellWidth);
rect.attr({
fill: this.fill,
stroke: this.stroke
});
rect.appendTo(this.svgGroup);
}
}
}
this.setPosition = function(x, y) {
this.position.x = x;
this.position.y = y;
}
this.getPosition = function() {
return this.position;
}
this.render = function() {
var matrix = new Snap.Matrix();
matrix.translate(this.position.x*cellWidth, this.position.y*cellWidth);
this.svgGroup.attr({
transform: matrix
});
}
this.getWidth = function() {
return this.width;
}
this.getHeight = function() {
return this.height;
}
this.getShape = function() {
return this.shape;
}
this.delete = function() {
this.svgGroup.remove();
}
this.isRemoved = function() {
return this.removed;
}
}
var shapes = [
[
[0,1,0],
[1,1,1]
],
[
[1,1,1,1]
],
[
[1,1,1],
[0,1,0],
[1,1,1]
],
[
[1,1],
[1,1]
],
[
[1,1,1],
[0,1,1],
[0,1,1],
[1,1,1]
],
[
[1,1,1,1],
[1,1,1,1],
[1,1,1,1],
[1,1,1,1]
],
[
[1,0,1],
[1,1,1]
]
];
var width = 10;
var height = 20;
var cellWidth = 20;
var paper = Snap("#svg");
var board = new Board(width, height);
var tick = 500;
paper.attr({
width: cellWidth*width,
height: cellWidth*height
});
for (var x = 0; x < width; x++) {
for (var y = 0; y < height; y++) {
var rect = paper.rect(x*cellWidth, y*cellWidth, cellWidth, cellWidth);
rect.attr({
fill: "#ccc",
stroke: "#ddd"
});
}
}
var piece = new Piece(shapes[0], "red", "white", paper, cellWidth);
piece.setPosition(0, 18);
board.place(piece);
piece = new Piece(shapes[1], "orange", "white", paper, cellWidth);
piece.setPosition(3, 19);
board.place(piece);
piece = new Piece(shapes[2], "yellow", "white", paper, cellWidth);
piece.setPosition(2, 8);
board.place(piece);
piece = new Piece(shapes[3], "green", "white", paper, cellWidth);
piece.setPosition(0, 17);
board.place(piece);
piece = new Piece(shapes[4], "blue", "white", paper, cellWidth);
piece.setPosition(2, 15);
board.place(piece);
piece = new Piece(shapes[5], "indigo", "white", paper, cellWidth);
piece.setPosition(1, 11);
board.place(piece);
piece = new Piece(shapes[6], "violet", "white", paper, cellWidth);
piece.setPosition(7, 17);
piece.render();
function update() {
if (piece.isRemoved()) {
return;
}
var position = piece.getPosition();
if (board.canPlace(piece, {x:position.x,y:position.y+1})) {
piece.setPosition(position.x,position.y+1);
board.place(piece);
for (var y = 0; y < piece.getHeight(); y++) {
if (board.hasFullLine(piece.getPosition().y+y)) {
var removed = board.clearPiecesOnLine(piece.getPosition().y+y);
setTimeout(function() {
for (var i = 0; i < removed.length; i++) {
removed[i].delete();
}
board.hardDropPieces();
},tick);
}
}
}
}
setTimeout(update, tick);
That's pretty much the gist of the Board-logic. Placed pieces kept by reference in an array, after clearing I sort the pieces that are not removed by their lowest point and then drop each one of them as far as they can go.
This works when no pieces are interconnected, but I just can't figure out how to do it when they are, as in this example.
Obviously, the blue piece is the lowest point, but it cannot move downards since the green piece is inside of it. I thought about merging them and dropping them, but that leads to other problems. Like what would happen in this case?
I'm pretty sure I'm just being thick, and there's a relatively easy way of fixing this...? Any help would be much appreciated!
All the pieces are automatically generated, and there's way too many, and more could be added any time, to not make a general solution.
I found two parts that had some missing logic. The first part was where you were performing the drops. You'll need to do it one step at a time for each block, and then keep doing it until you can drop no more. Like this
this.hardDropPieces = function() {
var pieces = this.pieces.slice();
pieces = pieces.sort(function(a,b) {
var aBottom = a.getPosition().y+a.getHeight();
var bBottom = b.getPosition().y+b.getHeight();
return bBottom-aBottom;
});
var canStillDrop = true;
while (canStillDrop) { // Keep going until we can't drop no more
canStillDrop = false;
for (var i = 0; i < pieces.length; i++) {
canStillDrop = this.hardDrop(pieces[i]) ? true : canStillDrop;
}
}
}
this.hardDrop = function(piece) {
var didDrop = false;
var position = piece.getPosition();
this.clearArea(piece);
if(this.canPlace(piece, {x: position.x, y: position.y+1})) {
piece.setPosition(position.x, position.y+1);
didDrop = true; // Oh, I see we have dropped
}
this.place(piece);
return didDrop; // Did we drop a spot? Then we should keep going
}
The second part is that you could use a little recursiveness to check if any of the tiles keeping you from dropping is actually connected to the floor. This one you already recognize:
this.canPlace = function(piece, at) {
// Will it fall below the floor? Then it's a no-go
if (piece.getHeight()+at.y > this.height) {
return false;
}
// Loop through shape
for (var y = 0; y < piece.getHeight(); y++) {
for (var x = 0; x < piece.getWidth(); x++) {
// Ignore non-shape positions
if (!piece.shape[y][x]) continue;
// Get piece at current shape position
var pieceAtPos = this.board[y+at.y][x+at.x];
// Is the piece (or any that it's resting on) connected to the floor?
if (pieceAtPos && pieceAtPos!==piece && this.isPieceGrounded(pieceAtPos, [piece]) ){
return false;
}
}
}
return true;
}
But say hello also to isPieceGrounded.
this.isPieceGrounded = function(piece, testedPieces) {
// Check all positions BELOW the piece
var at = { x: piece.getPosition().x, y: piece.getPosition().y+1 };
// Is it connected to the floor?
if (piece.getHeight()+at.y+1 >= this.height) {
return true;
}
// *Sigh* Loop through THIS whole piece
for (var y = 0; y < piece.getHeight(); y++) {
for (var x = 0; x < piece.getWidth(); x++) {
if (!piece.shape[y][x]) continue;
var pieceAtPos = this.board[y+at.y][x+at.x];
if (pieceAtPos && pieceAtPos!==piece && testedPieces.indexOf(pieceAtPos) < 0) {
// Keep a list of all tested pieces so we don't end up in an infinite loop by testing them back and forth
testedPieces.push(pieceAtPos);
// Let's test that one and all its connected ones as well
if (this.isPieceGrounded(pieceAtPos, testedPieces)) {
return true;
};
}
}
}
return false;
}
http://jsfiddle.net/971yvc8r/2/
I'm sure there are lots of different solutions, but I think something like this might be the most efficient.

'Arc is not a constructor' error

I am getting an error saying Arc is not a constructor. I have checked my code and can't understand why.
/*
Exception: Arc is not a constructor
RouteFinder/this.getPairs#Scratchpad/8:72:6
#Scratchpad/8:5:3
*/
The other answers for this error suggest that this because Arc has previously been defined, but I cant find anywhere that it is. This is a big chunk of code, so scroll down for the error - it is commented where it is
var lats = [ 51.445371, 51.45526, 51.426765, 51.441304 ]
var lons = [ -0.077581, -0.113248, -0.13091, -0.060596 ]
var finder = new RouteFinder(lats, lons);
finder.getPairs();
var nodeList = ''
for(var count = 1; count < lats.length; count++) {
nodeList += count;
}
finder.permutation("", nodeList);
finder.removeDuplicateRoutes();
var shortestRoute = finder.getShortestRoute();
var finalRouteOrder = [];
shortestRoute.nodeList.forEach(function(node) {
finalRouteOrder.push(+node);
});
alert(finalRouteOrder);
var Route = function() {
this.totalWeight = 0;
this.nodeList = [];
};
var Node = function() {
this.lat = 0;
this.lon = 0;
this.number = 0;
};
var Arc = function() {
this.startNode = new Node();
this.endNode = new Node();
this.weight = 0;
};
function reverseString(initialString) {
var reversed = '';
for(var count = initialString.length -1; count > -1; count--) {
reversed += initialString.charAt(count);
}
return reversed;
}
function calcDistance(lat1, lng1, lat2, lng2) {
var earthRadius = 6371;
var dLat = (lat2 - lat1)/180*Math.PI;
var dLng = (lng2 - lng1)/180*Math.PI;
var a = Math.sin(dLat/2) * Math.sin(dLat/2) + Math.cos(lat1/180*Math.PI) * Math.cos(lat2/180*Math.PI) * Math.sin(dLng/2) * Math.sin(dLng/2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var dist = earthRadius * c;
return dist;
}
function RouteFinder(lats, lons) {
this.latArray = lats;
this.lonArray = lons;
this.orders = [];
this.arcArray = [];
this.routes = [];
this.shortestRoute;
this.numOfPoints = lats.length - 1;
this.shortestRouteLength = -1;
this.getPairs = function() {
var timesLooped = 0;
for(var count1 = 0; count1 < this.numOfPoints; count1++) {
for(var count2 = 0; count2 < (this.numOfPoints - count1); count2++) {
//I get an error here for new Arc()
this.arcArray.push(new Arc());
this.arcArray[timesLooped].startNode = {
number: count1,
};
this.arcArray[timesLooped].endNode = {
number: this.numOfPoints - count2,
};
this.arcArray[timesLooped].weight = calcDistance(this.latArray[count1], this.lonArray[count1], this.latArray[this.numOfPoints - count2], this.lonArray[this.numOfPoints - count2]);
timesLooped++;
}
}
};
this.permutation = function(prefix, str) {
var n = str.length;
if(n === 0) this.orders.push('0' + prefix + '0');
else {
for(var i = 0; i <n; i++) {
this.permutation(prefix + str.charAt(i), str.substring(0, i) + str.substring(i + 1, n));
}
}
};
this.removeDuplicateRoutes = function() {
var numberOfPermutations = this.orders.length -1;
var temp;
var size;
var toRemove = [];
for(var count1 = 0; count1 < numberOfPermutations; count1++) {
for(var count2 = 0; count2 < (numberOfPermutations - count1); count2++) {
if(this.orders[count1] == reverseString(this.orders[numberOfPermutations - count2])) {
toRemove.push(count1);
}
}
}
size = toRemove.length;
for(var count3 = 0; count3 < size; count3++) {
temp = toRemove[size - 1- count3];
var index = this.orders.indexOf(temp);
if(index > -1) {
temp.splice(index, 1);
}
}
};
this.getShortestRoute = function() {
var routesMade = 0;
for(var routeNumber = 0; routeNumber < (this.orders.length -1); routeNumber++) {
this.routes.push(new Route());
this.routes[routesMade].totalWeight = 0;
for(var count1 = 0; count1 < this.orders[routeNumber].length; count1++) {
this.routes[routesMade].nodeList.push(+this.orders[routeNumber].charAt(count1));
}
for(var count2 = 1; count2 < this.orders[routeNumber].length; count2++) {
for(var count3 = 0; count3 < this.arcArray.length; count3++) {
if(this.routes[routesMade].nodeList[count2 - 1] === this.arcArray[count3].startNode.number) {
if(this.routes[routesMade].nodeList[count2] === this.arcArray[count3].endNode.number) {
this.routes[routesMade].totalWeight += this.arcArray[count3].weight;
}
} else if (this.routes[routesMade].nodeList[count2 - 1] === this.arcArray[count3].endNode.number) {
if(this.routes[routesMade].nodeList[count2] === this.arcArray[count3].startNode.number) {
this.routes[routesMade].totalWeight += this.arcArray[count3].weight;
}
}
}
}
if(!this.shortestRoute) {
this.shortestRoute = this.routes[routesMade];
} else if(this.routes[routesMade].totalWeight < this.shortestRoute.totalWeight) {
this.shortestRoute = this.routes[routesMade];
}
routesMade++;
}
return this.shortestRoute;
};
}
I'm tearing my hair out trying to work out the problem. Help is greatly appreciated, thanks!
This
var finder = new RouteFinder(lats, lons);
gets executed before Arc variable gets its value (function). Thus at the moment of RouteFinder call that Arc variable is undefined and cannot be used as a constructor.
Update: it is about finder.getPairs(); actually where you try to call that new Arc();
You need to have Route, Node, and Arc like this:
function Route() {
this.totalWeight = 0;
this.nodeList = [];
}
function Node() {
this.lat = 0;
this.lon = 0;
this.number = 0;
}
function Arc() {
this.startNode = new Node();
this.endNode = new Node();
this.weight = 0;
}
The issue is that Arc is not yet defined. Either move your function expression assignment (var Arc = function ...) to the top of your script or convert them to function definition statements like this
function Arc() {
this.startNode = new Node();
this.endNode = new Node();
this.weight = 0;
}
Statements are hoisted and their order does not matter, but assignment to var happens in the order they appear in the file (the declaration is hoisted, but the assignment isn't).
Same goes for your Route and Node functions.
Read more about var hoisting on MDN.

Categories

Resources