Why isn't the starting player randomised? - javascript

I'm building a Tic-Tac-Toe game.
I am trying to randomise which player gets to go first.
As the board is loaded, a random number between 1 and 2 is generated. If the number is 1, then player1 goes first, else player2 goes first. The currentPlayer is then set.
The function, currentPlayerFlag is then called. It will set the class of whichever player is the current to "active". The active player will then be flagged by lighting up their name.
For some reason, no matter how many times I reload the page, the starting player is always set to player1. Why is this?
No errors are being caught in the console.
Here is my code:
var currentPlayer;
var player1;
var player2;
// Grab original HTML and hold it as a variable
var originalHTML = document.body.innerHTML;
// When the page loads, the startup screen should appear.
window.onload = function() {
document.body.innerHTML = '<div class="screen screen-start" id="start"><header><h1>Tic Tac Toe</h1>Start game</header></div>';
document.querySelector('a').addEventListener("click", loadBoard);
};
// playerObject
function Player(name) {
this.name = name;
this.currentPlayer = false;
};
// Add programming, so that when the player clicks the start button the start screen disappears, the board appears, and the game begins.
function loadBoard() {
document.body.innerHTML = originalHTML;
player1 = new Player(player1);
player2 = new Player(player2);
var startingPlayerNum = Math.floor(Math.random() * 1) + 1 ;
if(startingPlayerNum = 1){
player1.currentPlayer = true;
currentPlayer = player1;
} else {
player2.currentPlayer = true;
currentPlayer = player2
}
//Add clickhandlers for boxes
var a1 = document.getElementById('a1').addEventListener("click", placePiece);
var a2 = document.getElementById('a2').addEventListener("click", placePiece);
var a3 = document.getElementById('a3').addEventListener("click", placePiece);
var b1 = document.getElementById('b1').addEventListener("click", placePiece);
var b2 = document.getElementById('b2').addEventListener("click", placePiece);
var b3 = document.getElementById('b3').addEventListener("click", placePiece);
var c1 = document.getElementById('c1').addEventListener("click", placePiece);
var c2 = document.getElementById('c2').addEventListener("click", placePiece);
var c3 = document.getElementById('c3').addEventListener("click", placePiece);
currentPlayerFlag()
};
// The current player is indicated at the top of the page -- the box with the symbol O or X is highlighted for the current player.
// Do this by simply adding the class .active to the proper list item in the HTML.
function currentPlayerFlag() {
if(currentPlayer === player1){
document.getElementById('player1').classList.add("active");
document.getElementById('player2').className = "players";
}
if(currentPlayer === player2){
document.getElementById('player2').classList.add("players active");
document.getElementById('player1').className = "players";
}
};
Please tell me what I'm doing wrong

The random number algorithm for JavaScript goes as follows:
Math.floor((Math.random() * max)) + min);
So, yours would be:
Math.floor((Math.random() * 2) + 1);
Yours is always returning 1 because your range was wrong, it was generating [1, 2), thus always returning 1. You must allow it to return [1, 3) my changing what you multiply Math.random() with from 1 to 2.
Your usecase:
var startingPlayerNum = Math.floor((Math.random() * 2) + 1);
The reason Math.floor(Math.random() * 1) + 1 returns 1 is because Math.random() generates a number from [0, 1). Then, it rounds to the floor (down) and adds one.
In your if statement, you assign it to one, so it's always 1. == is for comparison, = is for assignment. Try this if:
if(startingPlayerNum == 1){
player1.currentPlayer = true;
currentPlayer = player1;
} else {
player2.currentPlayer = true;
currentPlayer = player2
}

Math.random() returns values within the range of [0, 1). Math.floor() always rounds values down. Therefore, your Math.floor(Math.random() * 1) always returns 0.
You could use Math.round instead, or provide a wider range for your Math.floor.

Math.floor(Math.random() * 1) + 1 ;
would always return 1
It happens because Math.random() returns a value within [0; 1) range (0 inclusive, 1 exclusive).
So Math.random() * 1 is always between 0 and 1.
So Math.floor(Math.random() * 1) is always 0

Related

how to store last 3 random number in 3 different variables javascript?

I am making a Ludo game and my condition is if there are three 6 in a row then the user's chance will be passed to the next user. I made a random number from 1 to 6. I was storing the last digit with a variable called lastDice and compared the last one with a recent random number but I can not get the idea of the last 3 random numbers to compare. But the condition is if the last 3 random numbers are 6s(6,6,6) then the game must stop. Codes are as follows:
const playing = true;
const random = Math.floor(Math.random() * 6 + 1);
const lastDice = 0;
if (playing) {
if (random === 6 && lastDice === 6) {
document.getElementById('score--1').textContent = 0; // update the ui
} else {
nextPlayer(); // its a function to call the next player
}
lastDice = dice; // storing the last random number for comparing next random number
}
You can create a simple array, and use Math.random to get the index of the number to pick:
var lastNums = [0, 0, 0]; // 3rd last, 2nd last, and last number
var index = Math.floor(Math.random()*3);
var chosenNumber = lastNums[index];
// then compare
If you want to remove the first number and replace it with a new dice roll, simply use:
lastNums.shift(); // remove the 3rd last number
lastNums.push(lastDice); // add the current dice roll
Edit: If handling arrays are a bit too complex for you, here's another method using three variables:
// create 3 number variables
// I'll give the variables a non-zero value first
var firstNum = 1;
var secondNum = 2;
var thirdNum = 3;
// shuffle the number variables after each turn
// to set lastDice as previous dice roll
// here I treat firstNum as the last dice roll,
// secondNum as the second last dice roll,
// and thirdNum as the third last dice roll
thirdNum = secondNum;
secondNum = firstNum;
firstNum = lastDice;
Example: I rolled a 4. After each turn, when the numbers are shuffled, thirdNum gets replaced with secondNum, secondNum gets replaced with firstNum, firstNum gets replaced with lastDice. So I should have: firstNum = 4, secondNum = 1, thirdNum = 2.
I guess, I solved the problem. I added the random number to an array and used a condition if the array is more than 3 first index of the array will be removed. and then I compared the indexing value of an array with each other to see if all their value is equally 6. like the following code:
const random = Math.floor(Math.random()*6+1);
// container is an empty array called before
container.push(random);
//remove if conditon full fills
if(container.length >3){
container.shift();
};
console.log(container);
if(container[0] === 6 && container[1] === 6 && container[2] === 6){
scores[activePlayer] = 0;
document.getElementById('score--'+activePlayer).textContent = 0;
switchPlayer();
};

returning NaN and Undefined in JavaScript

var currDice, totDice, dice, complete;
function moveIt(){
dice = Math.floor(Math.random()*6) + 1,
currDice = 40,
totDice = totDice+complete,
complete = dice * currDice
return totDice;
};
The function moveIt returns NaN.
It should return multiple of 40 till six randomly and keep the previous value.
If I remove it returns Undefined.
I know it is a scope problem. Please help.
You are using complete before initialising it! you habe to swap your lines of code:
function moveIt(beforeTotal) {
var dice = Math.floor(Math.random()*6) + 1;
var currDice = 40;
var complete = dice * currDice;
var totDice = (beforeTotal || 0)+complete;
return totDice;
};
var total = moveIt();
console.log(total);
total = moveIt(total);
console.log(total);
I try to point out how this method work.
You can call this function without an inital value. Then (beforeTotal || 0) is (undefined || 0) and will evaluate to 0, that's JS logic, and you get the result for one dice.
If you pass a value to this function it will be used to add complete to it. By passing 1000 and complete gets 120, you get 1120 out of it.
All other variables are only available in this function.
Initialy totDIce is undefined and when you add undefined to something, you get the value casted to NaN
function moveIt(){
//memoizing the mutable value as key of the function itself
moveIt.totDice = moveIt.totDice || 0;
// you might wanna wrap it with parseInt()/Math.floor()/Math.ceil()
var dice = Math.floor(Math.random()*6) + 1;
var currDice = 40;
var complete = dice * currDice;
var totDice = totDice+complete;
return moveIt.totDice ;
};
var totDice; will have undefined and that's why you get NaN
Assign totDice = 0 on the first line...
Also move complete = dice * currDice line above because complete has no value yet
var currDice, totDice = 0, dice, complete;
function moveIt(){
dice = Math.floor(Math.random()*6) + 1,
currDice = 40,
complete = dice * currDice,
totDice = totDice+complete
return totDice;
};
you can use the browser's integrated console.
In Chrome: Tools > Javascript Console. Or CTRL + SHIFT + J. There you can console.log from your code and watch it there, or you can use the Sources tab (at the top of the panel). Navigate to the file, click on it and you can put a breakpoint in the line you want by ckicking the line number.
In Firefox: Tools > development > Web console. Or CTRL + SHIFT + K. Yu can debug in a similar fashion like the aforementioned.
You can also use tools like Firebug. See https://getfirebug.com/wiki/index.php/Script_Debugging

javascript recursive function returns initial value

Please, help needed!
I'm trying to make a small js compounding calculator
function calculateCompound(amount, earning, remains, roi, compound) {
if (remains > 0) {
var ini_earning = earning;
var accrual = amount * roi;
earning = accrual * (1 - compound);
var new_amount = amount + accrual - earning;
var new_earning = ini_earning + earning;
remains--;
calculateCompound(new_amount, new_earning, remains, roi, compound);
}
return earning + amount;
}
calculateCompound(100, 0, 5, .1, .5);
but after 5 calls it returns the initial values (105 in this case)
Many thanks in advance!
The you never return the value from your recursion call, this is why it returns the initial value.
The idea is to have a base case which returns earning + amount
And recur by returning the calculateCompound call value
function calculateCompound(amount, earning, remains, roi, compound) {
if (remains <= 0) {
return earning + amount;
}
var ini_earning = earning;
var accrual = amount * roi;
earning = accrual * (1 - compound);
var new_amount = amount + accrual - earning;
var new_earning = ini_earning + earning;
return calculateCompound(new_amount, new_earning, remains - 1, roi, compound);
}

How to use the various rampToValueAtTime methods

When I use the following code to fade in a file it doesn't work as I expect. I expect a gradual fade in from 0 to 1 over the course of 5 seconds, instead I get an abrupt change five seconds into playing the file where the gain instantly goes from 0 to 1. What am I not understanding ?
soundObj.play = function() {
playSound.buffer = soundObj.soundToPlay;
playSound.connect(gainNode);
gainNode.gain.value = 0;
gainNode.gain.exponentialRampToValueAtTime(1, audioContext.currentTime + 5);
gainNode.connect(audioContext.destination);
playSound.start(audioContext.currentTime);
}
Update/Edit
I changed the above code to the following and it seems to work, now I am researching why. I've added a few comments. Mainly inquiring as to if adding a setValueAtTime method is necessary and if a non zero value is necessary for the gain.value properties default value.
soundObj.play = function() {
playSound.buffer = soundObj.soundToPlay;
playSound.connect(gainNode);
gainNode.gain.value = 0.001; // If set to 0 it doesn't fade in
gainNode.gain.setValueAtTime(gainNode.gain.value, audioContext.currentTime); // Is this needed to have the other RampToValue methods work ?
gainNode.gain.exponentialRampToValueAtTime(1, audioContext.currentTime + 7);
gainNode.connect(audioContext.destination);
playSound.start(audioContext.currentTime);
}
A non-zero positive value is necessary for exponentialRampToValueAtTime. This isn't a Web Audio thing as much as it's just a math thing.
There's really no way to exponentially grow a value of 0.
Here's a rough version of the algorithm Chrome uses (rewritten in JS):
// start value
var value1 = 0.1;
// target value
var value2 = 1;
// start time (in seconds)
var time1 = 0;
// end time (in seconds)
var time2 = 2;
// duration
var deltaTime = time2 - time1;
// AudioContext sample rate
var sampleRate = 44100;
// total number of samples
var numSampleFrames = deltaTime * sampleRate;
// time incrementer
var sampleFrameTimeIncr = 1 / sampleRate;
// current time (in seconds)
var currentTime = 0;
// per-sample multiplier
var multiplier = Math.pow( value2 / value1, 1 / numSampleFrames );
// output gain values
var values = new Array( numSampleFrames );
// set up first value
var value = value1 * Math.pow( value2 / value1, ( ( currentTime - time1 ) * sampleRate ) / numSampleFrames );
for ( var i = 0; i < numSampleFrames; ++i ) {
values[ i ] = value;
value *= multiplier;
currentTime += sampleFrameTimeIncr;
}
If you change value1 to zero, you'll see that the output array is basically full of NaN. But Chrome also adds a bit of extra code to save you from that by special-casing instances where your value is <= 0 so that you don't actually end up with gain values of NaN.
If none of that makes sense, let me put it this way. In order to exponentially grow a value, you basically need a loop that looks like this:
for ( var i = 0; i < length; ++i ) {
values[ i ] = value;
value *= multiplier;
}
But if your initial value is 0, well, 0 multiplied by any other number is always 0.
Oh, and if you're interested (and can read C++), here's a link to the code that Chrome uses: https://chromium.googlesource.com/chromium/blink/+/master/Source/modules/webaudio/AudioParamTimeline.cpp
Relevant stuff is on line 316.
Edit
Apologies for a Chrome-centric explanation. But the underlying math concept of not being able to exponentially grow a value of zero will hold with any implementation.

Javascript: generate random numbers in range, avoiding previous two

I'm creating a slider with 6 slides, and I want to randomly move between them, making sure that neither of the previous two slides are shown as the next slide. The functionality doesn't really matter, since what I'm really doing is generating random numbers and keeping track of the previous two. The first slide is always numbered 1, so for the first two iterations that'll be one of the previous numbers that can't be used.
Here's what I have so far, and it works fine for generating the random numbers in the range, but 'caching' the last two values doesn't work reliably:
var rand = Math.floor(Math.random() * 6) + 1;
var prev1 = 1;
var prev2;
function randomSlide() {
// 5 second interval between slides
// Don't show either of previous two slides next
random = setInterval(function() {
prev2 = prev1;
prev1 = rand;
do {
rand = Math.floor(Math.random() * 6) + 1;
} while (rand == prev1 || rand == prev2);
prev1 = rand;
$('#slider').anythingSlider(rand);
//console.log(prev1,prev2);
}, 5000);
}
function firstSlide() {
firstTime = setTimeout(function() {
randomSlide();
}, 5000);
}
firstSlide();
randomSlide();
It's quite simple I think but my brain's getting frazzled trying to parse the values of the two 'cache' variables at the first, and then each subsequent, iteration.
I'm executing a single iteration at the beginning because if randomSlide() executes on load then the first (welcome) slide doesn't get a chance to display.
When you do the prev1 = rand the second time after you've changed the value of rand, you're assigning the new slide's number to it. The next time you enter the loop you do prev2 = prev1, and since prev1 == rand it means that now all three variables prev1, prev2 and rand are the same. Just remove the second prev1 = rand.
Another issue is that you set the interval twice: first you call firstSlide() which executes randomSlide() after a 5 second delay (which sets one interval), then right after you call randomSlide() again which sets another interval.
Here's another (simpler?) approach to getting the result:
<script>
// Return a random number from 1 to 6, exclude
// the last two numbers.
var getRandom = (function() {
var a = [1,2,3,4,5,6];
return function() {
var i = (Math.random() * 4 ) | 0;
a[5] = a.splice(i,1);
return a[5];
}
}());
function writeRandom() {
document.getElementById('d0').innerHTML += getRandom() + '<br>';
}
setInterval(writeRandom, 100)
</script>
<div id="d0"></div>
Not exactly random for the first 2 iterations, but you can fix that by randomising the array when it's initialised. But likely it doesn't matter for a slide show.
It's less code, but the splice part makes it slower in the browsers I tested. My version of the OP is:
var getRandom2 = (function() {
var r0 = r1 = r2 = 1;
return function() {
r0 = r1;
r1 = r2;
do {
r2 = Math.floor(Math.random() * 6) + 1;
} while (r2 == r0 || r2 == r1);
return r1;
}
}());

Categories