I have a grid in my page that I want to populate via javascript with 200 elements. The actual code that populate the .grid
element is the following:
$(function () {
var $gd = $(".grid");
var blocks="";
for(i=0; i < 200; i++){
blocks += '<div class="block"></div>';
}
$gd.append(blocks);
});
What I'm trying to do now is assign to each element created a random picked color from a list. Lets say red, blue, yellow, green (unexpected eh?). I'd like the values to be the most random possible, and also to avoid the same color to be picked again twice consequentially (just to be clear, it's ok something like red-blue-red-green-blue
and so on, NOT red-red-green-yellow
).
Maybe this can help in the randomize process, Fisher–Yates Shuffle, but I don't how to implement the non-twice adjacent rule stated above (if it is possible to apply at all).
What would be the best way to achieve this result? I'm also wondering if I could apply a gradient to each .block
instead of a flat hex color; I guess the better route for this would be to assign a random class to each element mapped in CSS for the gradient and so on..
If the above script can be optimized performance-wise, I apreciate any suggestions!
Additional info:
- I'm using jQuery
- The grid is posed with 20 elements per row, for 10 rows
- The colors should be 4, but can be raised to 5-7 adding some neutral grey tones if it can help
Here is a pen to experiment with /
Bonus feature request: as stated above I'd like to avoide duplicate adjacent colors. Is it possible to avoid this also "above and below"? I guess its very hard if not impossible to totally avoid this, but well if anyone can find a solution it would be awesome! Something like this, where the "nope" marked element is prevented, while the "yep" diagonal marked are allowed:
I have a grid in my page that I want to populate via javascript with 200 elements. The actual code that populate the .grid
element is the following:
$(function () {
var $gd = $(".grid");
var blocks="";
for(i=0; i < 200; i++){
blocks += '<div class="block"></div>';
}
$gd.append(blocks);
});
What I'm trying to do now is assign to each element created a random picked color from a list. Lets say red, blue, yellow, green (unexpected eh?). I'd like the values to be the most random possible, and also to avoid the same color to be picked again twice consequentially (just to be clear, it's ok something like red-blue-red-green-blue
and so on, NOT red-red-green-yellow
).
Maybe this can help in the randomize process, Fisher–Yates Shuffle, but I don't how to implement the non-twice adjacent rule stated above (if it is possible to apply at all).
What would be the best way to achieve this result? I'm also wondering if I could apply a gradient to each .block
instead of a flat hex color; I guess the better route for this would be to assign a random class to each element mapped in CSS for the gradient and so on..
If the above script can be optimized performance-wise, I apreciate any suggestions!
Additional info:
- I'm using jQuery
- The grid is posed with 20 elements per row, for 10 rows
- The colors should be 4, but can be raised to 5-7 adding some neutral grey tones if it can help
Here is a pen to experiment with http://codepen.io/Gruber/pen/lDxBw/
Bonus feature request: as stated above I'd like to avoide duplicate adjacent colors. Is it possible to avoid this also "above and below"? I guess its very hard if not impossible to totally avoid this, but well if anyone can find a solution it would be awesome! Something like this, where the "nope" marked element is prevented, while the "yep" diagonal marked are allowed:
Share Improve this question edited Mar 18, 2014 at 14:34 Gruber asked Mar 17, 2014 at 23:58 GruberGruber 2,3085 gold badges32 silver badges51 bronze badges 1- In the end, all answer brought interesting solutions and ideas for other stuff I'm working on, but indeed @phil-h solution is the most ingenious for solving this. The pen I posted is updated with his code plus a couple enhancement for responsiveness and gradients. – Gruber Commented Mar 20, 2014 at 1:09
3 Answers
Reset to default 3$(function () {
var colors = ["red","blue","green","yellow"];
var $gd = $(".grid");
var previousColor;
var blocks="";
for(i=0; i < 200; i++){
var color = "";
while(color === previousColor) {
color= colors [Math.floor(Math.random()*colors .length)];
}
blocks += '<div class="block" style="color:' + color + '"></div>';
previousColor = color;
}
$gd.append(blocks);
});
First, I'd use classes for the colors:
CSS:
.red { background-color: red; }
.blue { background-color: blue; }
.green { background-color: green; }
.yellow { background-color: yellow; }
And then here's the javascript:
$(document).ready(function() {
var colors = ["red","blue","green","yellow"];
var $gd = $(".grid");
var previousColor;
var previousRow;
var rowSize = 10;
while(rowSize--) previousRow.push("none");
var blocks = "";
for(i=0; i < 200; i++){
var color = colors [Math.floor(Math.random()*colors .length)];
while((color == previousColor) || (color == previousRow[i%rowSize])) {
color = colors [Math.floor(Math.random()*colors .length)];
}
blocks += '<div class="block ' + color + '"></div>';
previousColor = color;
previousRow[i%rowSize] = color;
}
$gd.append(blocks);
});
I started off with something similar to MikeB's code but added a row element so we know what is above your current block.
The first thing I'd like to introduce is a filtered indexing function.
Given an array:
var options = ['red', 'green', 'blue', 'purple', 'yellow']; // never less than 3!
And a filter:
function filterFunc(val) {
var taken = { 'red': 1, 'blue': 1 };
return taken[val] ? 0 : 1;
}
We can take the nth item from the values permitted (==1) by the filter (not a quick way to do it, but until there is a performance constraint...):
// filteredIndex returns nth (0-index) element satisfying filterFunc
// returns undefined if insufficient options
function filteredIndex(options, filterFunc, n) {
var i=-1, j=0;
for(;j<options.length && i<n; ++j) {
i += filterFunc(options[j]);
if(i==n)
break;
}
return options[j];
}
So now we can pick up a value with index 2 in the filtered list. If we don't have enough options to do so, we should get undefined
.
If you are populating the colours from, say, the top left corner, you can use as few as 3 colours, as you are only constrained by the cells above and to the left.
To pick randomly, we need to set up the filter. We can do that from a list of known values thus:
function genFilterFunc(takenValues) {
var takenLookup = {};
for(var i=0; i < takenValues.length; ++i) {
takenLookup[takenValues[i]] = 1;
}
var filterFunc = function(val) {
return takenLookup[val] ? 0 : 1;
};
return filterFunc;
}
We can choose a random colour, then, for a cell in a grid[rows][cols]
:
function randomColourNotUpOrLeft(grid, row, col, options, ignoreColour) {
var takenlist = [];
if(row > 0 && grid[row-1][col] != ignoreColour) {
takenlist.push(grid[row-1][col]);
}
if(col > 0 && grid[row][col-1] != ignoreColour) {
takenlist.push(grid[row][col-1]);
}
var filt = genFilterFunc(takenlist);
var randomIndex = Math.floor(Math.random()*(options.length-takenlist.length));
var randomColour = filteredIndex(options, filt, randomIndex);
return randomColour;
}
Note here that the random index used depends on how many colours have been filtered out; if there are 4 left we can have 0-3, but if only 2 are left it must be 0-1, etc. When the adjacent cells are the same colour and/or we are near the boundary, there is less constraint about which colour is chosen. Finally fill in a grid:
function fillGridSpeckled(grid, options, nullColour) {
for(var row=0; row<grid.length; ++row) {
for(var col=0; col<grid[row].length; ++col) {
grid[row][col] = randomColourNotUpOrLeft(grid,row,col,options,nullColour);
}
}
}
I've put it all in this jsbin, along with a few bits to demo the code working.