How do I cycle through multiple lines with a javascript letter randomiser? - javascript

Ok so I have this code for a JavaScript letter randomiser that works perfectly fine, i'm just having trouble figuring out how i'd get it to produce more than just one line while still keeping my code relatively efficient.
Ideally id like it to say something like this and then cycle back to the start:
Hi, my name is Yeet
this is my website
I like making cool stuff
take a look around :)
Any help would be greatly appreciated :)))
window.onload = function() {
var theLetters = "abcdefghijklmnopqrstuvwxyz#%&^+=-";
var cntnt = "Hi, my name is Yeet";
var speed = 20; // ms per frame
var increment = 2; // frames per step
var clen = cntnt.length;
var si = 0;
var stri = 0;
var block = "";
var fixed = "";
//Call self x times, whole function wrapped in setTimeout
(function rustle(i) {
setTimeout(function() {
if (--i) {
rustle(i);
}
nextFrame(i);
si = si + 1;
}, speed);
})(clen * increment + 1);
function nextFrame(pos) {
for (var i = 0; i < clen - stri; i++) {
var num = Math.floor(theLetters.length * Math.random());
//Get random letter
var letter = theLetters.charAt(num);
block = block + letter;
}
if (si == (increment - 1)) {
stri++;
}
if (si == increment) {
// Add a letter;
// every speed*10 ms
fixed = fixed + cntnt.charAt(stri - 1);
si = 0;
}
$("#output").html(fixed + block);
block = "";
}
};

Make cntnt an array
var cntnt = ["Hi, my name is Yeet", "This is my website", "I like making cool stuff", "take a look around :)"];
and use pos % cntnt.length as the array index.
fixed = fixed + cntnt[pos % cntnt.length].charAt(stri - 1);

Related

New to JavaScript and think I need a loop for this.....?

I've got a 180 character string of numbers which needed to be broken down into 6 groups, separated into 2 character figures and then sorted into ascending order.
I've done this, but it looks dirty and I'm pretty sure with my gradual improvement of understanding of JavaScript, that a neat little loop would save me a huge amount of repetition.
<script type="text/javascript">
var ticketString = "011722475204365360702637497481233455758302154058881928446789061241507324334876840738576186051132437816395663800818206590104559628214294664710935667287132130687703253151692742547985".match(/.{1,2}/g);
var groupa = ticketString.slice (0,15);
groupa.sort();
var groupb = ticketString.slice (15,30);
groupb.sort();
var groupc = ticketString.slice (30,45);
groupc.sort();
var groupd = ticketString.slice(45,60);
groupd.sort();
var groupe = ticketString.slice(60,75);
groupe.sort();
var groupf = ticketString.slice(75,90);
groupf.sort();
function displayArray () {
document.getElementById('ticketOne').innerHTML = groupa;
document.getElementById('ticketTwo').innerHTML = groupb;
document.getElementById('ticketThree').innerHTML = groupc;
document.getElementById('ticketFour').innerHTML = groupd;
document.getElementById('ticketFive').innerHTML = groupe;
document.getElementById('ticketSix').innerHTML = groupf;
}
The outputs are placed into paragraphs for now, but I know this could be done easier than the way I have it. The function displayArray loads off the body tag.
If you could rename your elements from ticketOne, ticketTwo etc to ticket1, ticket2 etc:
<script type="text/javascript">
var ticketString = "011722475204365360702637497481233455758302154058881928446789061241507324334876840738576186051132437816395663800818206590104559628214294664710935667287132130687703253151692742547985".match(/.{1,2}/g);
for (var i=0; i<ticketString.length/15; i++) {
var group = ticketString.slice(i*15, i*15+15);
group.sort();
document.getElementById('ticket'+(i+1)).innerHTML = group;
}
</script>
So here is what I think is the best solution:
var ticketString = "011722475204365360702637497481233455758302154058881928446789061241507324334876840738576186051132437816395663800818206590104559628214294664710935667287132130687703253151692742547985".match(/.{1,2}/g);
var group = [];
for (var i = 0; i < 180 / 15 / 2; i++) {
group[i] = ticketString.slice(i * 15, (i + 1) * 15);
group[i].sort();
document.getElementById('ticket[' + i + ']').innerHTML = group[i];
}
What I'm doing here is that I created a local array called group, which is in turn easier to handle within a loop than using a name like "group1" or "ticketFirst".
After that I practically did the same you did, but since I used the for loop I was able to short it down significantly.
The reason why I used i < 180 / 15 / 2 is because you're pairing them in character packs of 2. for (var i = 0; i < 180 / 15 / 2; i++) You ofcourse have to have an HTML like this:
<body>
<p id="ticket[0]"></p>
<p id="ticket[1]"></p>
<p id="ticket[2]"></p>
<p id="ticket[3]"></p>
<p id="ticket[4]"></p>
<p id="ticket[5]"></p>
</body>
You can reduce it to:
var groupArray = [];
for(var i=0;i<100;i+=15){
groupArray.push(ticketString.slice (i,15+i));
groupArray[groupArray.length-1].sort();
}
You could do something like this, if you changed your 'ticket' id's to a generic 'ticket' class;
var ticketString = "011722475204365360702637497481233455758302154058881928446789061241507324334876840738576186051132437816395663800818206590104559628214294664710935667287132130687703253151692742547985".match(/.{1,2}/g);
var tickElem = document.querySelectorAll('.ticket');
var strPoint = 0;
for(var i in tickElem)
{
var grp = ticketString.slice(strPoint, (strPoint += 15));
grp.sort();
tickElem[i].innerText = grp.join('');
}
How about this compact version:
var start = 0;
['ticketOne', 'ticketTwo', 'ticketThree', 'ticketFour', 'ticketFive', 'ticketSix']
.forEach(function (id) {
document.getElementById(id).innerHTML = ticketString.slice(start, start += 15).sort();
});
<script>
var ticketString ="011722475204365360702637497481233455758302154058881928446789061241507324334876840738576186051132437816395663800818206590104559628214294664710935667287132130687703253151692742547985".match(/.{1,2}/g);
function loadTickets () {
var ticket = [];
for (var i = 0; i < ticketString.length / 15; i++) {
ticket[i] = ticketString.slice(i * 15, (i + 1) * 15).sort();
var tableCre = document.createElement("TABLE");
tableCre.setAttribute("id","ticketTable" + i)
document.body.appendChild(tableCre);
document.getElementById('ticketTable' + i).innerHTML = ticket[i];
}
console.log(ticketString);
console.log(ticket);
};
</script>
Got it fixed, #Quikers - given you the thumbs up; as this was the thinking I had - brought the .sort() function into the ticket[i] variable to trim it a little and based on the number of arrays, I have dynamically created 6 tables that hold the strings inside them. I have to develop the tables and set them up, but I am pretty much there now. Thanks for all your help.

Using for loops to define global variables

I am defining some global arrays at the top of my JS file. There are for loops to populate the arrays. I have done very similar things that have worked but for some reason, this is having issues. The first for loop runs through only one time and the code below that never gets ran.
var group = 'a';
var RoofVar = 1;
var TrimVar = 1;
var BBVar = 1;
var imageArrayGroup0 = [];
var floatingButtonArrayGroup0 = [];
var labelArrayGroup0 = [];
for (a = 1; a < 3; a++) {
labelArrayGroup0[a] = document.createElement("Label");
labelArrayGroup0[a].type = "Label";
floatingButtonArrayGroup0[a] = document.getElementById('floatingButton0' + (100 + a));
floatingButtonArrayGroup0[a].style.textAlign = "center";
floatingButtonArrayGroup0[a].style.paddingTop = "5px";
labelArrayGroup0[a] = document.getElementById('floatingButton0' + (100 + x));
}
var imageArray0 = [];
var floatingButtonArray0 = [];
var labelArray0 = [];
for (b = 1; b < 5; b++) {
imageArray0[b] = document.createElement("img");
imageArray0[b].type = "image";
floatingButtonArray0[b] = document.getElementById('floatingButton0' + (b));
floatingButtonArray0[b].style.paddingLeft = "15px";
floatingButtonArray0[b].style.paddingTop = "5px";
labelArray0[b] = document.getElementById('floatingButton0' + b);
}
var imageArray1 = [];
var floatingButtonArray1 = [];
var labelArray1 = [];
for (c = 1; c < 3; c++) {
imageArray1[c] = document.createElement("img");
imageArray1[c].type = "image";
floatingButtonArray1[c] = document.getElementById('floatingButton1' + (c));
floatingButtonArray1[c].style.paddingLeft = "15px";
floatingButtonArray1[c].style.paddingTop = "5px";
labelArray1[c] = document.getElementById('floatingButton1' + c);
}
You have other issues than just "(100 + x) instead of (100 + a).
First, arrays in JavaScript are "zero-based" but your loops start at 1.
This means you're putting things in but leaving spot '0' undefined, which will probably break things later.
Next. at the beginning of the loop you set labelArrayGroup0[a] to a new blank label, but you never do anything to put it on the page. At the end the loop, you overwrite labelArrayGroup0[a] to a floatingButton element you pull from the page.
In fact, you do this in every loop - the last line resets the value of the 1st line.
It's not clear what you're trying to accomplish. Presumably you intend to append labelArrayGroup0[a] to the page instead?
Also, in the middle, you set floatingButtonArrayGroup0[a] = document.getElementById('floatingButton0' + (100 + a)); which is the same element you grab in the last line of the loop...
Paul had my answer. I forgot to switch out the "x" for an "a".

Javascript: Random number out of 5, no repeat until all have been used

I am using the below code to assign a random class (out of five) to each individual image on my page.
$(this).addClass('color-' + (Math.floor(Math.random() * 5) + 1));
It's working great but I want to make it so that there are never two of the same class in a row.
Even better would be if there were never two of the same in a row, and it also did not use any class more than once until all 5 had been used... As in, remove each used class from the array until all of them have been used, then start again, not allowing the last of the previous 5 and the first of the next 5 to be the same color.
Hope that makes sense, and thanks in advance for any help.
You need to create an array of the possible values and each time you retrieve a random index from the array to use one of the values, you remove it from the array.
Here's a general purpose random function that will not repeat until all values have been used. You can call this and then just add this index onto the end of your class name.
var uniqueRandoms = [];
var numRandoms = 5;
function makeUniqueRandom() {
// refill the array if needed
if (!uniqueRandoms.length) {
for (var i = 0; i < numRandoms; i++) {
uniqueRandoms.push(i);
}
}
var index = Math.floor(Math.random() * uniqueRandoms.length);
var val = uniqueRandoms[index];
// now remove that value from the array
uniqueRandoms.splice(index, 1);
return val;
}
Working demo: http://jsfiddle.net/jfriend00/H9bLH/
So, your code would just be this:
$(this).addClass('color-' + (makeUniqueRandom() + 1));
Here's an object oriented form that will allow more than one of these to be used in different places in your app:
// if only one argument is passed, it will assume that is the high
// limit and the low limit will be set to zero
// so you can use either r = new randomeGenerator(9);
// or r = new randomGenerator(0, 9);
function randomGenerator(low, high) {
if (arguments.length < 2) {
high = low;
low = 0;
}
this.low = low;
this.high = high;
this.reset();
}
randomGenerator.prototype = {
reset: function() {
this.remaining = [];
for (var i = this.low; i <= this.high; i++) {
this.remaining.push(i);
}
},
get: function() {
if (!this.remaining.length) {
this.reset();
}
var index = Math.floor(Math.random() * this.remaining.length);
var val = this.remaining[index];
this.remaining.splice(index, 1);
return val;
}
}
Sample Usage:
var r = new randomGenerator(1, 9);
var rand1 = r.get();
var rand2 = r.get();
Working demo: http://jsfiddle.net/jfriend00/q36Lk4hk/
You can do something like this using an array and the splice method:
var classes = ["color-1", "color-2", "color-3", "color-4", "color-5"];
for(i = 0;i < 5; i++){
var randomPosition = Math.floor(Math.random() * classes.length);
var selected = classes.splice(randomPosition,1);
console.log(selected);
alert(selected);
}
var used = [];
var range = [0, 5];
var generateColors = (function() {
var current;
for ( var i = range[0]; i < range[5]; i++ ) {
while ( used.indexOf(current = (Math.floor(Math.random() * 5) + 1)) != -1 ) ;
used.push(current);
$(" SELECTOR ").addClass('color-' + current);
}
});
Just to explain my comment to jfriend00's excellent answer, you can have a function that returns the members of a set in random order until all have been returned, then starts again, e.g.:
function RandomList(list) {
var original = list;
this.getOriginal = function() {
return original;
}
}
RandomList.prototype.getRandom = function() {
if (!(this.remainder && this.remainder.length)) {
this.remainder = this.getOriginal().slice();
}
return this.remainder.splice(Math.random() * this.remainder.length | 0,1);
}
var list = new RandomList([1,2,3]);
list.getRandom(); // returns a random member of list without repeating until all
// members have been returned.
If the list can be hard coded, you can keep the original in a closure, e.g.
var randomItem = (function() {
var original = [1,2,3];
var remainder;
return function() {
if (!(remainder && remainder.length)) {
remainder = original.slice();
}
return remainder.splice(Math.random() * remainder.length | 0, 1);
};
}());

iterate through JSON object with pause before next iteration

I am trying to build a fake chat box. I can retrieve the data from the database using jQuery to return a JSON array ok. I then i want each line of text contained in the JSON object to display on the page one line at a time BUT the code must pause for the length of time it would normally take to type the line if text before it displays it. then the code must wait for it to be displayed before it iterated on to the next value in the JSON object.
I hope all that makes sense...
$.getJSON('includes/get-mentor-dialogue.php?e=' + new Date().getTime(), function(data){
var mainDialogue = data.item;
var l = mainDialogue.length;
$.each(mainDialogue, function(index, d){
var delay = Math.round(countWords(d.content) / WPS) * 1000;
setTimeout(function(){$('#chatBox #chatBody').append('<p>'+ d.content +'</p>');},delay);
});
});
This is what i have and it kinda works... countWords() is a function that returns the number of words in the sentence and WPS is a variable that contains the average "Words Per Second" value in it.
The issue is that all the lines of text are displayed out of sequence. i can't get it to wait for the previous line to be displayed before it moves on to the next one...
Really need help with this one guys...
You can use a function to iterate with timeout, without using the $.each, see below
$.getJSON('includes/get-mentor-dialogue.php?e=' + new Date().getTime(), function(data){
var mainDialogue = data.item;
var l = mainDialogue.length;
var actual = -1;
function showNextMessage(){
actual++;
if( actual >= l ) return;
var content = mainDialogue[actual].content;
var delay = Math.round(countWords(content) / WPS) * 1000;
setTimeout(function(){
$('#chatBox #chatBody').append('<p>'+ content +'</p>');
showNextMessage();
},delay);
}
showNextMessage();
});
I think this will work for you.
$.getJSON('includes/get-mentor-dialogue.php?e=' + new Date().getTime(), function(data){
var mainDialogue = data.item;
var l = mainDialogue.length;
var i = 0;
function appendText(content) {
var delay = Math.round(countWords(content) / WPS) * 1000;
if ( i < l - 1 ) {
setTimeout(function(){
$('#chatBox #chatBody').append('<p>'+ content +'</p>');
appendText(mainDialogue[i++].content);
}, delay);
}
};
appendText(mainDialogue[i++].content);
});
I usually create an Iterator class, somewhere along the lines of this:
Iterator = function (iterable) {
var keys = [];
for (var i in iterable)
keys.push(i);
var pointer = 0;
this.next = function () {
return keys[pointer++];
}
this.hasNext = function () {
return i < keys.length;
}
}
That way you don't have to directly iterate across the object, you can just easily keep track of your place in the object as you would in Java.
$.getJSON('includes/get-mentor-dialogue.php?e=' + new Date().getTime(), function(data){
var mainDialogue = data.item;
var l = mainDialogue.length;
var iter = new Iterator(mainDialogue);
(function appendChat () {
var d = iter.next();
var delay = Math.round(countWords(d.content) / WPS) * 1000;
$('#chatBox #chatBody').append('<p>'+ d.content + '</p>');
if (iter.hasNext())
setTimeout(appendChat, delay);
})();
});
I don't have a good way to test this, but you should look into doing something like this:
$.getJSON('includes/get-mentor-dialogue.php?e=' + new Date().getTime(), function(data){
var mainDialogue = data.item;
var l = mainDialogue.length;
var delay = 0;
$.each(mainDialogue, function(index, d){
var myDelay = Math.round(countWords(d.content) / WPS) * 1000;
myDelay+=delay; // how long it takes me to render + the guy before me
delay=myDelay + (Math.random()*2000); // this will give a random pause between each step
setTimeout(function(){$('#chatBox #chatBody').append('<p>'+ d.content +'</p>');},myDelay);
});
});
I added in that little random bit, to help give a more realistic variable timing to the chat sequence.

JavaScript syntax issue

I'm doing "fifteen puzzle" game. I'm only a beginner, so I chose this project to implement. My problem is shuffle algorithm :
function shuffle() {
$('td').empty();
var p = 0;
var f = 0;
do {
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
var rand = arr[Math.floor(Math.random() * arr.length)];
if ($('#' + rand).is(':empty')) {
p = p + 1;
document.getElementById(rand).textContent = p
var f = $('td').not(":empty").length;
} else {}
} while (f < 15)
That works cool, but I've heard that almost 50% of all random shuffle like mine is unsolvable. So I found math formula at wikipedia.org for this game, explaining how you can avoid that.
Here's modified algorithm that doesn't work either. The way I know it is alert stuff: it launches only 2 times instead of 31.
array = [];
function algorithm (){
// alert('works')
for (var c=16; c<17; c++){
document.getElementById(c).textContent = '100';
}
for (var i=1; i<16; i++){
var curId = document.getElementById(i).id;
var curIdNum = Math.floor(curId);
alert('works')
var curIn = document.getElementById(i).textContent;
var curInNum = Math.floor(curIn);
array.push(i);
array[i] = new Array();
for (var j=1; j<15; j++){
var nextId = curIdNum + j; //curIdNum NOT cerIdNum
var nextIn = document.getElementById(nextId).textContent;
//alert('works')
if (nextId < 16){
var nextInNum = Math.floor(nextIn);
if (curInNum > nextInNum){
array[i].push(j)
}
}
}
var sum = 0;
for (var a=0; a<15; a++){
var add = array[a].length;
sum = sum + add;
}
var end = sum + 4;
if (end % 2 == 0){
document.getElementById('16').textContent = "";
}
else {
shuffle();
}
}
}
The question is the same:
What's wrong? Two-dimensional array doesn't work.If you've got any questions - ask.
Just to make it clear: 2 for loops with i and j should make a 2-dimensional array like this [ this is " var i" -->[1,3,4,5,7], this is "var i" too-->[5,7,9,14,15]]. Inside each i there's j. The for loop with var a should count the number of js inside each i. if the number of js is even, the code is finished and shuffle's accomplished, otherwise shuffle should be made once again.
var nextId = cerIdNum + j;
in that fiddle, I don't see this cerIdNum declared & defined neither as local nor as global variable, I suppose that is curIdNum
Please use the below definition of algorithm and let us know if this works. Basically, the alert messages would come only twice, since there were usages of undefined variables. For the purpose of illustration, I have placed comments at where the problem points occured. Due to these problems, your script would stop executing abruptly thereby resulting in the behavior you described.
Oh and by the way - I did not have time to go through the Wiki link provided - hence you will have to verify your logic is correct. However, I have definitely resolved the errors causing the behavior you observed.
As an aside - consider using jQuery, your code will be a lot cleaner...
function algorithm (){
// alert('works')
for (var c=16; c<17; c++){
document.getElementById(c).textContent = '100';
}
for (var i=1; i<16; i++){
var curId = document.getElementById(i).id;
var curIdNum = Math.floor(curId);
alert('works')
var curIn = document.getElementById(i).textContent;
var curInNum = Math.floor(curIn);
array.push(i);
for (var j=1; j<15; j++){
var nextId = curIdNum + j; //curIdNum NOT cerIdNum
var nextIn = document.getElementById(nextId).textContent;
//alert('works')
if (nextId < 16){
var nextInNum = Math.floor(nextIn);
if (curInNum > nextInNum){
array.push(j) //array[i].push does not make sense
}
}
}
var sum = 0;
for (var a=0; a<15; a++){
var add = array.length; //array[1].length does not make sense
sum = sum + add;
}
var end = sum + 4;
if (end % 2 == 0){
document.getElementById('16').textContent = "";
}
else {
shuffle();
}
}
}
I found the solution by totally rewriting the code. Thank everyone for help!
Here's what do work:
function shuffle (){
press = 1;
$('td').empty().removeClass();
p=0;
var f;
do {
var arr=[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
var rand=arr[Math.floor(Math.random()*arr.length)];
if ($('#'+ rand).is(':empty')){
p = p + 1;
document.getElementById(rand).textContent = p
var f = $('td').not(":empty").length;
}
else{}
}while(f < 15){
winChance();
}
}
function winChance (){
array = [];
for (i=1;i<16;i++){
array[i]= new Array();
var currentId = $('#' + i).attr('id');
var currentIn = $('#' + i).html()
var currentIdNum = parseInt(currentId, 10);
var currentInNum = parseInt(currentIn, 10);
for (j=1;j<16;j++){
var nextId = currentIdNum + j;
if (nextId < 16){
var nextIn = $('#' + nextId).html();
var nextInNum = parseInt(nextIn, 10);
if (currentInNum > nextInNum){
array[i].push(j);
}
}
}
}
checkSum();
}
function checkSum(){
var sum = 0;
for (var a=1; a<16; a++){
var add = array[a].length;
sum = sum + add;
}
var end = sum + 4;
if (end % 2 == 0){}
else {
shuffle();
}
}

Categories

Resources