progress bar movement calculation - javascript

Because I can change the winning score in the game, I'd like the progress bar to be sensitive to it. Now, it accepts percentages relative to the total height of a div.
The default score is 100pts. And the progress bar is set to move from 0%(min) ~ 100%(max) which is 100pts - the winning score.
Could you suggest me some ideas how to program the progress bar that when a user inputs the winning score of, for example, 50 pts which will be 100%(max), then when the player reaches 25pts, the progress bar will display that he's half way (50%) from winning the game. I hope my explanations are clear.
HTML input:
<input class="score-input" type="text">
function to set the winning score (100 by default)
function setWinningScore() {
let inputScore = document.querySelector('.score-input').value;
if (inputScore) {
winningScore = inputScore;
} else {
winningScore = 100;
}
}
Progress bar function which reads the player's score, the progress bar element's id and then reflects it.
function progressBar(myScore, progBarId) {
progBarId.style.height = myScore + '%';
}

You almost answered your own question.
Since you know that 25 out of 50 is 50%, then you should also know that 25/50 = 0.5
With that you can simply multiply it by 100 and that gives you your percent.
0.5 * 100 = 50
As Johan Karlsson pointed out, you would just replace your progressBar function with the following:
function progressBar(myScore, progBarId) {
progBarId.style.height = ((myScore / winningScore) * 100) + '%';
}
I would add that you should also make your setWinningScore function a bit safer by making sure that the given input is a valid positive number, otherwise you'll have some problems.
function setWinningScore() {
let inputScore = document.querySelector('.score-input').value;
if (typeof inputScore === 'number' && inputScore > 0) {
winningScore = inputScore;
} else {
winningScore = 100;
}
}

Related

Add a random change in color for every iteration

I am writing a quiz sort of game and I want the background to change every time there is a new question. Could someone help me with either how to code this or ideally the code? (I haven't copied the array in as it links to a CSV.
Here is my current code:
for (var i = 1; i <= 100; i++) {
var number = Math.floor((Math.random() * 18) + 1);
var stringGuess = prompt(questions[number])
if (stringGuess == answers[number]) {
alert("YOU GOT IT RIGHT!! THE ANSWER IS " + answers[number])
} else if (stringGuess > answers[number]) {
alert("Too high. The answer is " + answers[number]);
} else {
alert("Too low. The answer is " + answers[number]);
}
}
Thank you!!!
For a random colour you could set a random RGB value using the function below. To set css background-color on an element you can use element.style.backgroundColour = colour;
function randomRGB(){
return `rgb(${Math.floor(Math.random()*256)},${Math.floor(Math.random()*256)},${Math.floor(Math.random()*256)})`;
}
function setColour(element, colour){
element.style.backgroundColour = colour;
}
Therefore, use this whenever you need to set a random background colour:
setColour(document.getElementById("my-background-element"), randomRGB());
remembering to change "my-background-element" to the id of the background element you have.

In javascript, how do I add a random amount to a user's balance while controlling how much gets given total?

I'm trying to make it to where when a user does a certain thing, they get between 2 and 100 units. But for every 1,000 requests I want it to add up to 3,500 units given collectively.
Here's the code I have for adding different amounts randomly to a user:
if (Math.floor(Math.random() * 1000) + 1 === 900) {
//db call adding 100
}
else if (Math.floor(Math.random() * 100) + 1 === 90) {
//db call adding 40
}
else if (Math.floor(Math.random() * 30) + 1 === 20) {
//db call adding 10
}
else if (Math.floor(Math.random() * 5) + 1 === 4) {
//db call adding 5
}
else {
//db call adding 2
}
If my math is correct, this should average around 4,332 units per 1,000 calls. But obviously it would vary and I don't want that. I'd also like it to add random amounts instead, as the units added in my example are arbitrary.
EDIT: Guys, Gildor is right that I simply want to have 3,500 units, and give them away within 1,000 requests. It isn't even entirely necessary that it always reaches that maximum of 3,500 either (I could have specified that). The important thing is that I'm not giving users too much, while creating a chance for them to win a bigger amount.
Here's what I have set up now, and it's working well, and will work even better with some tweaking:
Outside of call:
var remaining = 150;
var count = 0;
Inside of call:
count += 1;
if (count === 100) {
remaining = 150;
count = 0;
}
if (Math.floor(Math.random() * 30) + 1 === 20) {
var addAmount = Math.floor(Math.random() * 85) + 15;
if (addAmount <= remaining) {
remaining -= addAmount;
//db call adding addAmount + 2
}
else {
//db call adding 2
}
}
else if (Math.floor(Math.random() * 5) + 1 === 4) {
var addAmount1 = Math.floor(Math.random() * 10) + 1;
if (addAmount1 <= remaining) {
remaining -= addAmount1;
//db call adding addAmount1 + 2
}
else {
//db call adding 2
}
}
else {
//db call adding 2
}
I guess I should have clarified, I want a "random" number with a high likelihood of being small. That's kind of part of the gimmick, where you have low probability of getting a larger amount.
As I've commented, 1,000 random numbers between 2 and 100 that add up to 3,500 is an average number of 3.5 which is not consistent with random choices between 2 and 100. You'd have to have nearly all 2 and 3 values in order to achieve that and, in fact couldn't have more than a couple large numbers. Nothing even close to random. So, for this to even be remotely random and feasible, you'd have to pick a total much large than 3,500. A random total of 1,000 numbers between 2 and 100 would be more like 51,000.
Furthermore, you can't dynamically generate each number in a truly random fashion and guarantee a particular total. The main way to guarantee that outcome is to pre-allocate random numbers that add up to the total that are known to achieve that and then random select each number from the pre-allocated scheme, then remove that from the choice for future selections.
You could also try to keep a running total and bias your randomness if you get skewed away form your total, but doing it that way, the last set of numbers may have to be not even close to random in order to hit your total consistently.
A scheme that could work if you reset the total to support what it should be for actual randomness (e.g. to 51,000) would be to preallocated an array of 500 random numbers between 2 and 100 and then add another 500 numbers that are the complements of those. This guarantees the 51 avg number. You can then select each number randomly from the pre-allocated array and then remove it form the array so it won't be selected again. I can add code to do this in a second.
function RandResults(low, high, qty) {
var results = new Array(qty);
var limit = qty/2;
var avg = (low + high) / 2;
for (var i = 0; i < limit; i++) {
results[i] = Math.floor((Math.random() * (high - low)) + low);
//
results[qty - i - 1] = (2 * avg) - results[i];
}
this.results = results;
}
RandResults.prototype.getRand = function() {
if (!this.results.length) {
throw new Error("getRand() called, but results are empty");
}
var randIndex = Math.floor(Math.random() * this.results.length);
var value = this.results[randIndex];
this.results.splice(randIndex, 1);
return value;
}
RandResults.prototype.getRemaining = function() {
return this.results.length;
}
var randObj = new RandResults(2, 100, 1000);
// get next single random value
if (randObj.getRemaining()) {
var randomValue = randObj.getRand();
}
Working demo for a truly random selection of numbers that add up to 51,000 (which is what 1,000 random values between 2 and 100 should add up to): http://jsfiddle.net/jfriend00/wga26n7p/
If what you want is the following: 1,000 numbers that add up to 3,500 and are selected from between the range 2 to 100 (inclusive) where most numbers will be 2 or 3, but occasionally something could be up to 100, then that's a different problem. I wouldn't really use the word random to describe it because it's a highly biased selection.
Here's a way to do that. It generates 1,000 random numbers between 2 and 100, keeping track of the total. Then, afterwards it corrects the random numbers to hit the right total by randomly selected values and decrementing them until the total is down to 3,500. You can see it work here: http://jsfiddle.net/jfriend00/m4ouonj4/
The main part of the code is this:
function RandResults(low, high, qty, total) {
var results = new Array(qty);
var runningTotal = 0, correction, index, trial;
for (var i = 0; i < qty; i++) {
runningTotal += results[i] = Math.floor((Math.random() * (high - low)) + low);
}
// now, correct to hit the total
if (runningTotal > total) {
correction = -1;
} else if (runningTotal < total) {
correction = 1;
}
// loop until we've hit the total
// randomly select a value to apply the correction to
while (runningTotal !== total) {
index = Math.floor(Math.random() * qty);
trial = results[index] + correction;
if (trial >= low && trial <= high) {
results[index] = trial;
runningTotal += correction;
}
}
this.results = results;
}
This meets an objective of a biased total of 3,500 and all numbers between 2 and 100, though the probability of a 2 in this scheme is very high and the probably of a 100 in this scheme is almost non-existent.
And, here's a weighted random generator that adds up to a precise total. This uses a cubic weighting scheme to favor the lower numbers (the probably of a number goes down with the cube of the number) and then after the random numbers are generated, a correction algorithm applies random corrections to the numbers to make the total come out exactly as specified. The code for a working demo is here: http://jsfiddle.net/jfriend00/g6mds8rr/
function RandResults(low, high, numPicks, total) {
var avg = total / numPicks;
var i, j;
// calculate probabilities for each value
// by trial and error, we found that a cubic weighting
// gives an approximately correct sub-total that can then
// be corrected to the exact total
var numBuckets = high - low + 1;
var item;
var probabilities = [];
for (i = 0; i < numBuckets; i++) {
item = low + i;
probabilities[i] = avg / (item * item * item);
}
// now using those probabilities, create a steps array
var sum = 0;
var steps = probabilities.map(function(item) {
sum += item;
return sum;
});
// now generate a random number and find what
// index it belongs to in the steps array
// and use that as our pick
var runningTotal = 0, rand;
var picks = [], pick, stepsLen = steps.length;
for (i = 0; i < numPicks; i++) {
rand = Math.random() * sum;
for (j = 0; j < stepsLen; j++) {
if (steps[j] >= rand) {
pick = j + low;
picks.push(pick);
runningTotal += pick;
break;
}
}
}
var correction;
// now run our correction algorithm to hit the total exactly
if (runningTotal > total) {
correction = -1;
} else if (runningTotal < total) {
correction = 1;
}
// loop until we've hit the total
// randomly select a value to apply the correction to
while (runningTotal !== total) {
index = Math.floor(Math.random() * numPicks);
trial = picks[index] + correction;
if (trial >= low && trial <= high) {
picks[index] = trial;
runningTotal += correction;
}
}
this.results = picks;
}
RandResults.prototype.getRand = function() {
if (!this.results.length) {
throw new Error("getRand() called, but results are empty");
}
return this.results.pop();
}
RandResults.prototype.getAllRand = function() {
if (!this.results.length) {
throw new Error("getAllRand() called, but results are empty");
}
var r = this.results;
this.results = [];
return r;
}
RandResults.prototype.getRemaining = function() {
return this.results.length;
}
As some comments pointed out... the numbers in the question does not quite make sense, but conceptually there are two approaches: calculate dynamically just in time or ahead of time.
To calculate just in time:
You can maintain a remaining variable which tracks how many of 3500 left. Each time when you randomly give some units, subtract the number from remaining until it goes to 0.
In addition, to make sure each time at least 2 units are given, you can start with remaining = 1500 and give random + 2 units each time.
To prevent cases that after 1000 gives there are still balances left, you may need to add some logic to give units more aggressively towards the last few times. However it will result in not-so-random results.
To calculate ahead of time:
Generate a random list with 1000 values in [2, 100] and sums up to 3500. Then shuffle the list. Each time you want to give some units, pick the next item in the array. After 1000 gives, generate another list in the same way. This way you get much better randomized results.
Be aware that both approaches requires some kind of shared state that needs to be handled carefully in a multi-threaded environment.
Hope the ideas help.

DC.js - trigger elastic resize when criteria are met

I would like to trigger elastic resizing on outliers in certain graphs when the largest values are removed. Here's what I have so far:
dmsn = ndx.dimension(...)
groupTotal = dmsn.group().reduceSum(...)
row
.width(100)
.height(100)
.dimension(dmsn)
.group(groupTotal)
document.addEventListener('mouseup',function(){
setTimeout(function(e){
newTop = groupTotal.top(1)[0].value;
ratio = newTop / origTop;
if(ratio < .1){
row.elasticX(true);
dc.redrawAll('groupTotal');
} else {
row.elasticX(false);
dc.redrawAll('groupTotal');
}
},1);
}, true);
The function checks to see whether the new highest value is sufficiently small that a readjustment is useful.
The timeout ensures that the ratio is correctly calculated after the filters are applied. However, this also means that the opportunity to apply the new elasticity has passed. Unfortunately the redrawAll doesn't appear to work here.
Any advice on fixing this implementation or achieving this some other way?
Here's how to achieve this, for anyone curious. This results in conditional scaling of axes when the largest current bar is too small (or large) to be valuable.
var currentMax = 0,
ratio,
chartMax = groupData.top(1)[0].value; // initialize with largest value
row
.on('postRedraw', function(chart){
currentMax = groupData.top(1)[0].value; // after redraw, capture largest val
ratio = currentMax/chartMax;
if(ratio < .1 || ratio > 1){ // check if bars are too small or too large
row.elasticX(true);
chartMax = currentMax; // always be sure to reset the chartMax
dc.redrawAll();
} else {
row.elasticX(false);
chartMax = currentMax;
}
});

how to make number repeatedly add in javascript

okay i guys i just got into java script and i made a simple game it works and does everything i wanted but now i want to make it difficult so that every time the enemy health is 0 it refill but comes back stronger as in it has more health this is what i came up with this is the part that makes it refill it adds 100 but only stays 200 when it refills i want it to increase by 100 every time it becomes 0
if (enemyHealth<=0) {
enemyHealth=0;
alert("you win");
gold=gold+500;
document.FGame.Output.value=gold;
enemyHealth=100+100;
}
It's because you always set the enemyHealth to 100 + 100, which will always equal 200. What you could do is to have a totalEnemyHealth variable which increase by 100 every time the enemy dies. When you revive that enemy, you would set it's health to the new totalEnemyHealth value.
//make sure not to re-initialize this variable every time
var totalEnemyHealth = 100;
//then in your function
if (enemyHealth<=0){
enemyHealth = totalEnemyHealth += 100;
alert("you win")
gold=gold+500;
document.FGame.Output.value=gold;
}
However if you have multiple ennemies, that approach will not give the desired results. To solve your problem you will need something more object-oriented, such as all ennemies represented by an Enemy instance, where the instance would have the capability of tracking how many times it died and use this as a health multiplier.
Simple example:
var Enemy = {
baseHealth: 100,
health: 100,
deathCount: 0,
takeDamage: function (amount) {
if ((this.health -= amount) <= 0) {
this.die();
this.respawn();
}
},
die: function () {
this.deathCount++;
},
respawn: function () {
this.health = (this.deathCount + 1) * this.baseHealth;
}
};
var someEnemy = Object.create(Enemy);
console.log(someEnemy.health); //100
someEnemy.takeDamage(150); //violent attack
console.log(someEnemy.health); //200
Here is an example:
http://jsfiddle.net/9zhqg/1/
var gold = 0;
for(i = 0; i < 20; i++){
var num = Math.floor(Math.random()*10);
var enemyHealth = num *i;
if (enemyHealth<=0){
//enemyHealth=0
alert(i + "you win")
gold+= 500;
//document.FGame.Output.value=gold;
enemyHealth += 100
alert(enemyHealth);
}
}
You can see when it runs, the number of times it hit 0 out of a for loop of 20 times. Then it bounces the health back to 100. I think this is what you need. - not 100% sure if I understand your use case.
You could just add a counter. Something simple like:
var counter=1; //at start of game
if (enemyHealth<=0)
{
enemyHealth=0
alert("you win")
gold=gold+500;
document.FGame.Output.value=gold;
enemyHealth=100+(100*counter);
counter++;
}

Javascript animate math

I'm struggling to get my head around such simple Math, well at least it seems it should be simple.
I'm basically trying to mirror what jQuery's .animate does, but to no luck.
Here's a simplified version of what I have so far:
var args = {
speed: 1000, // 1 second.
left: 65 // distance.
}, rot, step;
// Terrible math.
rot = step = (((args.left / args.speed) * 10) - 0.10);
var t = setInterval(function() {
if(elem.style.left >= args.left) {
clearInterval(t);
return;
}
rot += step;
elem.style.left = rot;
}, 10);
Please excuse any illogical code (or math), I've been messing around for a good few hours and totally lost my sanity.
Edit:
Here's the way I would do it.
var start_time = Date.now();
// Get the starting time in milliseconds
var t = setInterval(function() {
var delta_time = Date.now() - start_time;
// Get time that has elapsed since starting
if (delta_time >= 1000) {
// if it's been a second
clearInterval(t);
// Stop the timer
elem.style.left = args.left + 'px';
// Set the element to exactly the value it should be (avoids having it set to a float value)
return;
}
elem.style.left = delta_time * args.left / args.speed + 'px';
// Move the element according to how much time has elapsed
}, 10);​
This method has a few advantages. For example, you can adjust the interval to make it more or less smooth, and it won't mess up the animation.
The reason why your solution was taking longer than one second is because of how you used setInterval. setInterval doesn't account for the time your code takes to run, so the total time is always increased by a bit. You can fix this by using delta timing (like in my example).
Try using useing sin and cos to calculate rotation Some what like this
newx = distance * Math.cos(direction) + x
newy = distance * Math.sin(direction) + y
Not sure , this will solve your problem I guess you want to to do a smooth rotation
Try making it as a function it will work , I am not seeing any problem in your math ,
like this
function move(elem) {
var left = 0
function frame() {
left++ // update parameters
elem.style.left = left // show frame
if (left == 100) // check finish condition
clearInterval(id)
}
var id = setInterval(frame, 10) // draw every 10ms
}
Well for one it should be
var args = { ... }
assuming you have the elem set up correctly, you're going to need a inline styling of the attribute you want to animate. Also, you're going to need to parse the style since it has the 'px' attached to it, but you can always add that after you do the math within the interval function.
I set up something here so you can mess around with the settings and whatnot.
edit:
http://jsfiddle.net/mb4JA/2/
edit2:
this should be one second
http://jsfiddle.net/mb4JA/4/
final answer ;) http://jsfiddle.net/mb4JA/10/
You should be able to put any speed in there, and have it animate for that amount of seconds.

Categories

Resources