I'm attempting to emulate an infinite looping chaser on an LED strip with Javascript.
What I'm trying to accomplish:
Only use loops or recursive functions. No fancy JS.
Support any length of LED strip (var ledLength)
Support of multiple chasers
Support any number of LEDs (var ledDepth) with any number
of spaces separating the chasers (var blankDepth)
Loop infinitely
No magic numbers
I have this working for 1 chaser:
$(function(){
var ledLength = 20;
for(var i = 0; i < ledLength; i++) {
$('#led').append($('<div>').addClass('node'));
}
$('#led').css('width', ($('.node').length + 1) * $('.node').last().outerWidth(true));
var colors = ['green', 'red', 'blue'];
// add a "clearing" color
colors.push('black');
var ledDepth = 3;
var blankDepth = 2;
var chaserSize = ledDepth + blankDepth;
var iteration = 0;
var loop = setInterval(animate, 250);
function animate() {
if(iteration == ledLength + ledDepth) iteration = 0;
var offset = iteration < chaserSize ? iteration : chaserSize;
for(var i = 0; i <= offset; i ++) {
var colorOffset = i < ledDepth ? i : ledDepth;
$('.node').eq(iteration-i).css('background', colors[colorOffset]);
}
iteration++;
}
});
Or you can view it on the JSBin.
How would I go about tracking multiple chasers on a strip? Meaning after the initial chaserSize has been created and is moving to the right, another would be created and move long with it and loop infinitely.
Any help in to the right direction would be greatly appreciated.
I decided to take a different approach to this using an array.push method.
Basically, I continue to add points, loop through them all to increase their location, and remove them if their location is greater than the length of the strip.
This works for all LED lengths, chaser sizes (colors + blanks between each chaser).
Code:
$(function(){
// create the strip
var ledLength = 30;
for(var i = 0; i < ledLength; i++) {
$('#led').append($('<div>').addClass('node'));
}
$('#led').css('width', ($('.node').length + 1) * $('.node').last().outerWidth(true));
var points = [];
var colors = ['green', 'red', 'blue', 'pink', 'orange', 'purple'];
var ledDepth = colors.length || 6;
var blankDepth = 2;
var chaserSize = ledDepth + blankDepth;
// push blank nodes on the back of colors array
for(var i = 0; i < blankDepth; i++) {
colors.push('blank');
}
var iteration = 0;
// loop!
var loop = setInterval(animate, 100);
function animate() {
if(points.length) {
for(var i = 0; i < points.length; i++) {
// increase each pin by 1
points[i].pin++;
// remove point if pin is greater than led length
if(points[i].pin > ledLength) {
points.splice(i, 1);
}
}
}
if(iteration < chaserSize) {
points.push({pin: 0, color: colors[iteration]});
}
// draw points
for(var i = 0; i < points.length; i++) {
if(points[i].color == 'blank') { // this if for 'resetting'
$('.node').eq(points[i].pin).css('background', 'black');
} else { // this is for changing color
$('.node').eq(points[i].pin).css('background', points[i].color);
}
}
// iterate before reset
iteration++;
// reset sub-iterator (creator)
if(iteration >= chaserSize) {
iteration = 0;
}
}
});
Or JSBin
Related
I know how to construct the above triangle using a single symbol.
I do not understand how to change my code to use two symbols.
Your problem may be solved using a number of different solutions...
For the heck of it, here's how I would do it using canvas, to create an image (copied/pasted and adapted an answer I gave to another SO user yesterday...). This solution probably won't fit your needs but then again as Jishnu V S stated, you gave us nothing to help you with.
ps : Yeah, I know, WAY overkill compared to simple html lists... Oh well xD
fiddle
HTML
<canvas id="canvas" width=1000 height=1000></canvas>
JS
var rows = 8,
cols = 1,
size = 64;
var canvas = document.getElementById("canvas");
var surface = canvas.getContext("2d");
//creating tile
function box(img) {
this.xaxis = 64;
this.yaxis = 0;
//Set your image selection logic here...
this.src = (img > 0 ) ? "https://cdn0.iconfinder.com/data/icons/typicons-2/24/times-512.png" : "http://img.freepik.com/free-icon/minus-big-symbol_318-70354.jpg?size=338&ext=jpg";
console.log(img);
}
//creating map
var map =[];
function setMap() {
for (var i = 0; i < rows; i++) {
var arr = [];
map.push(arr);
for (var o = 0; o < cols; o++) {
var selectImg = (i % 2 == 0) ? 0 : 1; //select your image based on row
map[i].push(new box(selectImg));
}
cols = (cols < 8) ? cols + 1 : cols;
}
}
console.log(map)
//rendering map
function render() {
for (var i = 0; i < rows; i++) {
for (var x = 0; x < map[i].length; x++) {
var tile = map[i][x];
tile.xaxis *= x;
tile.yaxis += (i*64);
var img = new Image();
img.onload = (function(x,y) {
return function() {
surface.drawImage(this, x, y, 64, 64);
}
})(tile.xaxis, tile.yaxis);
img.src = tile.src;
}
}
}
setMap();
render();
I'm trying to animate a solution to the latin-square-problem in javascript.
To do so, I wrote the recursive backtracking algorithm below.
Solving the problem is initiated by calling search(0,0) and it works just fine, displaying a solution after calculating. But I want it to show an animation on it's progress, in terms of redrawing the whole canvas, after changing the colour of one square.
I tried to incoorporate many of the solutions to similar problems, found on stackoverflow or in tutorials about canvas gameloops. Neither of them worked for me, so I'm presenting the javascript code as close as possible to my pseudo-code algorithm (without any setTimeout's or requestAnimationFrame's)
Here's a working jsfiddle containing all the code.
function search(i, j){
if (latinSquare[i][j] != -1){
//this square is predefined, skip it
searchNext(i, j);
} else {
var colour = latinSquare[i][j];
while(colour < n-1){
colour = colour + 1;
latinSquare[i][j] = colour;
//redraw the whole canvas
drawLatinSquare();
//check if curent constellation is legal
var legal = true;
for (var k = 0; k < n; k++){
if (k != i){
if (latinSquare[k][j] == colour){
legal = false;
break;
}
}
if (k != j){
if (latinSquare[i][k] == colour){
legal = false;
break;
}
}
}
if (legal){
searchNext(i, j);
if (window.found) return;
}
}
latinSquare[i][j] = -1;
}
}
function searchNext(i, j){
if (i < n-1){
//proceed horizontally
search(i+1, j);
} else {
if (j < n-1){
//proceed in the next row
search(0, j+1);
} else {
//we're done
window.found = true;
}
}
}
In this solution, an array is created to hold each iteration of the latinSquare arrays. The timeout interval is a function of the length of the array.
An advantage of this method is that the animation doesn't start until all the calculations are completed, so it runs quite quickly (assuming a solution is found):
var lsa= [];
function drawLatinSquare() {
var l= [];
for(var i = 0 ; i < latinSquare.length ; i++) {
l.push(latinSquare[i].slice());
}
lsa.push(l);
setTimeout(function() {
var ls= lsa.shift();
ctx.clearRect ( 0 , 0 , canvas.width, canvas.height );
ctx.lineWidth= 1;
//draw the grid
for (var i = 0; i < n + 1; i++){
ctx.beginPath();
ctx.moveTo(0,i*21 + 0.5);
ctx.lineTo((n*(21)+1),i*21 + 0.5);
ctx.stroke();
}
for (var j = 0; j < n + 1; j++){
ctx.beginPath();
ctx.moveTo(j*21 + 0.5,0);
ctx.lineTo(j*21 + 0.5,(n*(21)+1));
ctx.stroke();
}
//draw the squares
for (var i = 0; i < n; i++){
for (var j = 0; j < n; j++){
colour = ls[i][j];
if (colour == -1){
colour = "#FFFFFF";
} else {
colour = colours[colour];
}
ctx.fillStyle = colour;
ctx.fillRect((i*21)+1.5,(j*21)+1.5,20,20);
}
}
},10*lsa.length);
} //drawLatinSquare
Fiddle
You can just wrap the call to the main compute function to have it display then delay the call to the actual compute function :
function search(i,j) {
drawLatinSquare();
setTimeout(function() { _search(i,j)} , 15);
}
function _search(i, j){
//... your real search function
issue being that there are too many combinations to see them all for a 'big' n : you should make a choice about what you want to show i fear.
Also if i was you i'd do a first pass to see the number of iterations, so that you can display a progress bar or like.
https://jsfiddle.net/ezstfj9f/4/
I need help fixing my existing code to accomplish what I am trying to do.
with the following sample data:
var SAMPLE_DATA = [{start: 30, end: 150}, {start: 540, end: 600}, {start: 560, end: 620}, {start: 610, end: 670}];
I need to do the following:
iterate through each sample object
determine if the current objects range (obj.start:obj.end) overlaps with any other object ranges.
record the total number of overlaps for that object into totalSlots property
determine the "index" of the object (used for it's left-to-right positioning)
mockup of what I am trying to accomplish:
As you can see in the mockup, slotIndex is used to determine the left-to-right ordering of the display. totalSlots is how many objects it shares space with (1 meaning it is the only object). 100 / totalSlots tells me how wide the square can be (i.e. totalSlots=2, means it is 100 / 2, or 50% container width).
Current Output from my code
Obj[0] slotIndex=0, totalSlots=0
Obj[1] slotIndex=1, totalSlots=1
Obj[2] slotIndex=1, totalSlots=2
Obj[3] slotIndex=0, totalSlots=1
expected/desired output from my code:
Obj[0] slotIndex=0, totalSlots=0
Obj[1] slotIndex=0, totalSlots=1
Obj[2] slotIndex=1, totalSlots=2
Obj[3] slotIndex=0, totalSlots=1
the code:
detectSlots: function(oldEventArr) {
oldEventArr.sort(this.eventSorter);
var newEventArr = [],
n = oldEventArr.length;
for (var i = 0; i < n; i++) {
var currObj = oldEventArr[i];
if ('undefined' == typeof currObj.totalSlots) {
currObj.slotIndex = 0;
currObj.totalSlots = 0;
}
for (var x = 0; x < n; x++) {
if (i == x) {
continue;
}
var nextObj = oldEventArr[x];
if (currObj.start <= nextObj.end && nextObj.start <= currObj.end) {
currObj.totalSlots++;
nextObj.slotIndex++;
}
}
newEventArr.push(currObj);
}
return newEventArr;
}
Please help me figure out what is going wrong in my code. I'm about 90% sure the problem lies in the if(currObj.start <= nextObj.end && nextObj.start <= currObj.end) statement where I am assigning/incrementing the values but I could use an extra set of eyes on this.
The slotIndex value can be calculated by using graph colouring algorithm. Note that brute force algorithm is exponential in time and will only be a viable solution for a small set of overlapping slots. Other algorithms are heuristics and you won't be guaranteed the least slot possible.
Here is an example of heuristic for your problem:
...
// init
var newEventArr = [], n = oldEventArr.length;
for (var i = 0; i < n; i+=1) {
var currObj = oldEventArr[i];
newEventArr.push({"start":currObj.start,"end":currObj.end,"slotIndex":undefined,"totalSlots":0});
}
var link = {};
// create link lists and totals
for (var i = 0; i < n; i+=1) {
var currObj = newEventArr[i];
if (!link.hasOwnProperty(""+i))
link[""+i] = {};
for (var j = i+1; j < n; j+=1) {
var nextObj = newEventArr[j];
var not_overlap = (currObj.end <= nextObj.start || nextObj.end <= currObj.start);
if (!not_overlap) {
currObj.totalSlots+=1;
nextObj.totalSlots+=1;
link[""+i][""+j] = 1;
if (!link.hasOwnProperty(""+j))
link[""+j] = {};
link[""+j][""+i] = 1;
}
}
}
var arrities = [];
for (var i = 0; i < n; i+=1) {
arrities.push( {"arrity":newEventArr[i].totalSlots, "indx":i} );
}
// sort by arrities [a better solution is using a priority queue]
for (var i = 0; i < n-1; i+=1) {
var current_arrity = -1, indx = -1;
for (var j = i; j < n; j+=1) {
if (arrities[j].arrity > current_arrity) {
indx = j;
current_arrity = arrities[j].arrity;
}
}
var temp = arrities[i];
arrities[i] = arrities[indx];
arrities[indx] = temp;
}
for (var i = 0; i < n; i+=1) {
var nodeIndex = arrities[i].indx;
// init used colors
var colors = [];
for (var j = 0; j < n; j+=1) {
colors.push(0);
}
//find used colors on links
for (var k in link[""+nodeIndex]) {
var color = newEventArr[k].slotIndex;
if (color || color === 0)
colors[color] += 1;
}
//find the first unused color
for (var j = 0; j < n; j+=1) {
if (colors[j] <= 0) {
// color the node
newEventArr[nodeIndex].slotIndex = j;
break;
}
}
}
return newEventArr;
...
like this
var not_overlap = (currObj.end <= nextObj.start || nextObj.end <= currObj.start);
if (!not_overlap) { ...
or
var overlap = (currObj.end > nextObj.start && nextObj.end < currObj.start);
if (overlap) { ...
I'm trying to create a simple memory matching game and I'm having trouble assigning one number to each table cell from my cardValues array. My giveCellValue function is supposed to generate a random number then pick that number from the array and give it to one of the table cells but I'm a little over my head on this one and having trouble accomplishing this task.
var countCells;
var cardValues = [];
var checker = true;
var createTable = function (col, row) {
$('table').empty();
for (i = 1; i <= col; i++) {
$('table').append($('<tr>'));
}
for (j = 1; j <= row; j++) {
$('tr').append($('<td>'));
}
countCells = row * col;
};
createTable(3, 6);
for (i = 1; i <= countCells / 2; i++) {
cardValues.push(i);
if (i === countCells / 2 && checker) {
checker = false;
i = 0;
}
}
var giveCellValue = function () {
var random = Math.ceil(Math.random() * cardValues.length) - 1;
for (i = 0; i <= cardValues.length; i++) {
$('td').append(cardValues[random]);
cardValues.splice(random, 1);
}
};
giveCellValue();
console.log(cardValues);
Use
var countCells;
var cardValues = [];
var checker = true;
var createTable = function (col, row) {
$('table').empty();
for (var i = 0; i < col; i++) {
$('table').append($('<tr>'));
}
for (var j = 0; j < row; j++) {
$('tr').append($('<td>'));
}
countCells = row * col;
};
createTable(3, 6);
for (i = 0; i < countCells; i++) {
cardValues.push(i % 9 + 1);
}
var giveCellValue = function () {
var len = cardValues.length, tds = $('td');
for (var i = 0; i < len; i++) {
var random = Math.floor(Math.random() * cardValues.length);
tds.eq(i).append(cardValues.splice(random, 1));
}
};
giveCellValue();
console.log(cardValues);
Demo: Fiddle
Leaving aside the problems that have already been mentioned in the comments above...
If I understand it right, you want to assign each of the numbers in cardValues to a random table cell?
Seems like the problem is with this line:
var random = Math.ceil(Math.random() * cardValues.length) - 1;
What this does is generates a random number once. If you access the variable random at a later time, you're not calling the full line of code again, you're just getting the same value that was calculated the first time. E.g. if the above code runs, spits out '7' as its random number and stores that in random, then every time you go through the for loop, the value of random will always be '7' rather than being re-generated every time - make sense?
Try putting the randomiser inside the for loop - that way it will be run multiple times, generating a new random number every time:
var giveCellValue = function () {
var random;
for (i = 0; i <= cardValues.length; i++) {
random = Math.ceil(Math.random() * cardValues.length) - 1;
$('td').append(cardValues[random]);
cardValues.splice(random, 1);
}
};
Actually, I'd change line 4 above to random = Math.floor(Math.random() * cardValues.length); too which should have the same effect.
How do I generate objects on a map, without them occupying the same space or overlapping on a HTML5 Canvas?
X coordinate is randomly generated, to an extent. I thought checking inside the array to see if it's there already, and the next 20 values after that (to account for the width), with no luck.
var nrOfPlatforms = 14,
platforms = [],
platformWidth = 20,
platformHeight = 20;
var generatePlatforms = function(){
var positiony = 0, type;
for (var i = 0; i < nrOfPlatforms; i++) {
type = ~~(Math.random()*5);
if (type == 0) type = 1;
else type = 0;
var positionx = (Math.random() * 4000) + 500 - (points/100);
var duplicatetest = 21;
for (var d = 0; d < duplicatetest; d++) {
var duplicate = $(jQuery.inArray((positionx + d), platforms));
if (duplicate > 0) {
var duplicateconfirmed = true;
}
}
if (duplicateconfirmed) {
var positionx = positionx + 20;
}
var duplicateconfirmed = false;
platforms[i] = new Platform(positionx,positiony,type);
}
}();
I originally made a cheat fix by having them generate in an area roughly 4000 big, decreasing the odds, but I want to increase the difficulty as the game progresses, by making them appear more together, to make it harder. But then they overlap.
In crude picture form, I want this
....[]....[].....[]..[]..[][]...
not this
......[]...[[]]...[[]]....[]....
I hope that makes sense.
For reference, here is the code before the array check and difficulty, just the cheap distance hack.
var nrOfPlatforms = 14,
platforms = [],
platformWidth = 20,
platformHeight = 20;
var generatePlatforms = function(){
var position = 0, type;
for (var i = 0; i < nrOfPlatforms; i++) {
type = ~~(Math.random()*5);
if (type == 0) type = 1;
else type = 0;
platforms[i] = new Platform((Math.random() * 4000) + 500,position,type);
}
}();
EDIT 1
after some debugging, duplicate is returning as [object Object] instead of the index number, not sure why though
EDIT 2
the problem is the objects are in the array platforms, and x is in the array object, so how can I search inside again ? , that's why it was failing before.
Thanks to firebug and console.log(platforms);
platforms = [Object { image=img, x=1128, y=260, more...}, Object { image=img, x=1640, y=260, more...} etc
You could implement a while loop that tries to insert an object and silently fails if it collides. Then add a counter and exit the while loop after a desired number of successful objects have been placed. If the objects are close together this loop might run longer so you might also want to give it a maximum life span. Or you could implement a 'is it even possible to place z objects on a map of x and y' to prevent it from running forever.
Here is an example of this (demo):
//Fill an array with 20x20 points at random locations without overlap
var platforms = [],
platformSize = 20,
platformWidth = 200,
platformHeight = 200;
function generatePlatforms(k) {
var placed = 0,
maxAttempts = k*10;
while(placed < k && maxAttempts > 0) {
var x = Math.floor(Math.random()*platformWidth),
y = Math.floor(Math.random()*platformHeight),
available = true;
for(var point in platforms) {
if(Math.abs(point.x-x) < platformSize && Math.abs(point.y-y) < platformSize) {
available = false;
break;
}
}
if(available) {
platforms.push({
x: x,
y: y
});
placed += 1;
}
maxAttempts -= 1;
}
}
generatePlatforms(14);
console.log(platforms);
Here's how you would implement a grid-snapped hash: http://jsfiddle.net/tqFuy/1/
var can = document.getElementById("can"),
ctx = can.getContext('2d'),
wid = can.width,
hei = can.height,
numPlatforms = 14,
platWid = 20,
platHei = 20,
platforms = [],
hash = {};
for(var i = 0; i < numPlatforms; i++){
// get x/y values snapped to platform width/height increments
var posX = Math.floor(Math.random()*(wid-platWid)/platWid)*platWid,
posY = Math.floor(Math.random()*(hei-platHei)/platHei)*platHei;
while (hash[posX + 'x' + posY]){
posX = Math.floor(Math.random()*wid/platWid)*platWid;
posY = Math.floor(Math.random()*hei/platHei)*platHei;
}
hash[posX + 'x' + posY] = 1;
platforms.push(new Platform(/* your arguments */));
}
Note that I'm concatenating the x and y values and using that as the hash key. This is to simplify the check, and is only a feasible solution because we are snapping the x/y coordinates to specific increments. The collision check would be more complicated if we weren't snapping.
For large sets (seems unlikely from your criteria), it'd probably be better to use an exclusion method: Generate an array of all possible positions, then for each "platform", pick an item from the array at random, then remove it from the array. This is similar to how you might go about shuffling a deck of cards.
Edit — One thing to note is that numPlatforms <= (wid*hei)/(platWid*platHei) must evaluate to true, otherwise the while loop will never end.
I found the answer on another question ( Searching for objects in JavaScript arrays ) using this bit of code to search the objects in the array
function search(array, value){
var j, k;
for (j = 0; j < array.length; j++) {
for (k in array[j]) {
if (array[j][k] === value) return j;
}
}
}
I also ended up rewriting a bunch of the code to speed it up elsewhere and recycle platforms better.
it works, but downside is I have fewer platforms, as it really starts to slow down. In the end this is what I wanted, but its no longer feasible to do it this way.
var platforms = new Array();
var nrOfPlatforms = 7;
platformWidth = 20,
platformHeight = 20;
var positionx = 0;
var positiony = 0;
var arrayneedle = 0;
var duplicatetest = 21;
function search(array, value){
var j, k;
for (j = 0; j < array.length; j++) {
for (k in array[j]) {
if (array[j][k] === value) return j;
}
}
}
function generatePlatforms(ind){
roughx = Math.round((Math.random() * 2000) + 500);
type = ~~(Math.random()*5);
if (type == 0) type = 1;
else type = 0;
var duplicate = false;
for (var d = 0; d < duplicatetest; d++) {
arrayneedle = roughx + d;
var result = search(platforms, arrayneedle);
if (result >= 0) {
duplicate = true;
}
}
if (duplicate = true) {
positionx = roughx + 20;
}
if (duplicate = false) {
positionx = roughx;
}
platforms[ind] = new Platform(positionx,positiony,type);
}
var generatedplatforms = function(){
for (var i = 0; i < nrOfPlatforms; i++) {
generatePlatforms(i);
};
}();
you go big data, generate all possibilities, store each in an array, shuffle the array,
trim the first X items, this is your non heuristic algorithm.