Isolate color of single character - javascript

I have a piece of code in p5.js that acts as a video filter:
const density = ' .:░▒▓█'
//const density = ' .tiITesgESG'
//let geist;
let video
let asciiDiv
let playing = false
let button
function preload() {
video = createVideo('assets/ripple.mp4', vidLoad)
}
function vidLoad() {
video.loop()
video.volume(0)
}
function setup() {
noCanvas()
//video = createCapture(VIDEO)
//video = createVideo('assets/01.mp4');
button = createButton('play');
button.mousePressed(toggleVid);
video.size(256, 160)
//video.size(160, 160);
asciiDiv = createDiv();
}
function toggleVid() {
if (playing) {
video.pause();
button.html('play');
} else {
video.loop();
button.html('pause');
}
playing = !playing;
}
function draw() {
background(0);
video.loadPixels();
let asciiImage = '';
//pixelarray
for (let j = 0; j < video.height; j++) {
for (let i = 0; i < video.width; i++) {
const pixelIndex = (i+j*video.width) * 4;
const r = video.pixels[pixelIndex + 0];
const g = video.pixels[pixelIndex + 1];
const b = video.pixels[pixelIndex + 2];
const avg = (r + g + b) / 3;
const len = density.length;
const charIndex = floor(map(avg, 0, 255, len, 0));
const c = density.charAt(charIndex);
if (c == ' ') asciiImage += ' '
else asciiImage += c;
}
asciiImage += '<br/>'
//console.log(row);
}
asciiDiv.html(asciiImage)
}
and CSS that looks like:
html, body {
margin: 0;
padding: 0;
border: 10px solid black;
background-color: #fff;
/*
//#fcbf45;
//aaaaaa;
*/
color: #000;
//#ffd & #00b0aa;
font-family: 'Courier Bold';
line-height: 4pt;
font-size: 5pt;
}
canvas {
display: block;
}
Which looks like
this
I apologize for all the comments.
My question is - if I use the color property in CSS, it will change the colors of all the characters. Is it possible to change the color of just one character, let's say I'm using the rightmost darkest character in const density, can I make that character alone blue for example?
Thank you.
EDIT: After the answer given below, I have added in the code like so:
const darkest = density[density.length-1]
const c = density.charAt(charIndex)
if (c == ' ') {
asciiImage += ' '
} else {
asciiImage += c;
}
if (c === darkest) {
asciiImage += `<span class='blue'>${c}</span>`
}
which now looks like this: https://imgur.com/a/dbqgCCa
I think the issue must be because it's adding c twice right?

Technically no, you can't change the color of a specific character, but you can wrap individual characters with an element (preferrably a span) that can be targeted with css.
// references the darkest character in the list
const darkest = density[density.length - 1]
// if the current character is the darkest character, wrap it in a span with a class of 'blue.'
if(c === ' ') {
asciiImage += '&nsbc;'
}
else if (c === darkest) {
asciiImage += `<span class='blue'>${c}</span>`
}
else {
asciiImage += c
}
And in your css, just color the characters blue
.blue {
color: blue;
}

Related

button won't change color

I have made 30 buttons in JavaScript now I want all buttons to be green like the first button and whenever you click on a button it turns and stays red.
For some reason only the first button is green and whenever I click it it won't turn red. I tried using: button.style.backgroundColor = "red"; in a function to make the button red when clicked but this doesn't work
const color = ["green"];
page();
function onbuttonclicked() {
document.body.style.backgroundColor = color[nr - 1];
}
function page() {
document.body.style.backgroundColor = "grey";
//style page
createButtons(30);
}
function set_onclick(amount) {
for (var a = 1; a < (amount + 1); a++) {
document.getElementById("button" + a).setAttribute("onclick", "onbuttonclicked(" + a + ")");
}
}
function createButtons(amount) {
for (var a = 1; a < (amount + 1); a++) {
var button = document.createElement("button");
button.style.backgroundColor = color[a - 1];
button.id = "button" + a;
button.innerHTML = "button " + a;
container.appendChild(button);
}
set_onclick(amount);
}
<div id="container"></div>
EDIT
thanks for all the answers it isnt possible to only change certain buttons when you click on them right? so when i click on button1 nothing happens but whenever i click on button 3 it turn red
You need to pass nr as a parameter in onbuttonclicked function
EDIT:
OP Comment:
okay so i need to add an parameter. but i want all 30 buttons to be green so when you open the page all buttons are green this also has to do with the parameter?
For that in your loop createButton, change color[a - 1] for color[0]
const color = ["green"];
function onbuttonclicked(nr) {
document.body.style.backgroundColor = color[nr - 1];
}
function page() {
//style page
document.body.style.backgroundColor = "grey";
createButtons(30);
}
function set_onclick(amount, nr) {
for (let a = 1; a < (amount + 1); a++) {
document.getElementById(`button${a}`).setAttribute("onclick", `onbuttonclicked(${nr})`);
}
}
function createButtons(amount) {
for (let a = 1; a < (amount + 1); a++) {
const button = document.createElement("button");
button.style.backgroundColor = color[0];
button.id = `button${a}`;
button.innerHTML = `button ${a}`;
container.appendChild(button);
}
set_onclick(amount, 1);
}
page();
<div id="container"></div>
const number_of_buttons = 30;
createButtons();
function createButtons(){
var container = document.getElementById("container");
for(var i=1;i<=number_of_buttons;i++){
var button = document.createElement("button");
button.innerText = "Button " + i;
button.style.backgroundColor = "green";
button.onclick = function(event){
var source = event.target || event.srcElementl
source.style.backgroundColor = "red";
};
container.appendChild(button);
}
}
button{
margin: 10px;
font-family: 'Consolas';
font-size: 20px;
padding: 5px;
outline: none;
cursor: pointer;
transition: 0.5s;
}
button:hover{
border: 1px solid black;
}
button:active{
transform: translateY(2px);
}
<div id="container">
</div>
This is what you have asked for!
I did the styling for better looks!
This line: button.style.backgroundColor = color[a - 1]; is wrong, since your color array only contains a single string ("green"). You should do color[0], or, you need to populate the array to have 30 elements.
document.body.style.backgroundColor = color[nr - 1]; - Where is this nr variable came from? The correct line should be button.style.backgroundColor = "red"; or - again - you can populate the color (i.e const color=["green", "red"]) and use color[1].
Check out this demo
A common way to do this is to use CSS classes:
page();
function onbuttonclicked(a) {
//document.body.style.backgroundColor = color[nr - 1];
document.getElementById("button" + a).classList.remove("button")
document.getElementById("button" + a).classList.add("clickedbutton")
}
function set_onclick(amount) {
for (var a = 1; a < (amount + 1); a++) {
document.getElementById("button" + a).setAttribute("onclick", "onbuttonclicked(" + a + ")");
}
}
function page() {
document.body.style.backgroundColor = "grey";
//style page
createButtons(30);
}
function createButtons(amount){
for (var a = 1;a <(amount + 1); a++) {
var button = document.createElement("button");
button.classList.add("button")
button.id = "button" + a;
button.innerHTML = "button " + a;
container.appendChild(button);
}
set_onclick(amount);
}
.clickedbutton {
background-color: red;
}
.button {
background-color: green;
}
<div id="container"></div>

Using nested setTimeout to create an animated selection sort

I am working on a basic sorting visualizer with using only HTML, CSS, and JS, and I've run into a problem with the animation aspect. To initialize the array, I generate random numbers within some specified range and push them on to the array. Then based on the webpage dimensions, I create divs for each element and give each one height and width dimensions accordingly, and append each to my "bar-container" div currently in the dom.
function renderVisualizer() {
var barContainer = document.getElementById("bar-container");
//Empties bar-container div
while (barContainer.hasChildNodes()) {
barContainer.removeChild(barContainer.lastChild);
}
var heightMult = barContainer.offsetHeight / max_element;
var temp = barContainer.offsetWidth / array.length;
var barWidth = temp * 0.9;
var margin = temp * 0.05;
//Creating array element bars
for (var i = 0; i < array.length; i++) {
var arrayBar = document.createElement("div");
arrayBar.className = "array-bar"
if (barWidth > 30)
arrayBar.textContent = array[i];
//Style
arrayBar.style.textAlign = "center";
arrayBar.style.height = array[i] * heightMult + "px";
arrayBar.style.width = barWidth;
arrayBar.style.margin = margin;
barContainer.appendChild(arrayBar);
}
}
I wrote the following animated selection sort and it works well, but the only "animated" portion is in the outer for-loop, and I am not highlighting bars as I traverse through them.
function selectionSortAnimated() {
var barContainer = document.getElementById("bar-container");
var barArr = barContainer.childNodes;
for (let i = 0; i < barArr.length - 1; i++) {
let min_idx = i;
let minNum = parseInt(barArr[i].textContent);
for (let j = i + 1; j < barArr.length; j++) {
let jNum = parseInt(barArr[j].textContent, 10);
if (jNum < minNum) {
min_idx = j;
minNum = jNum;
}
}
//setTimeout(() => {
barContainer.insertBefore(barArr[i], barArr[min_idx])
barContainer.insertBefore(barArr[min_idx], barArr[i]);
//}, i * 500);
}
}
I am trying to use nested setTimeout calls to highlight each bar as I traverse through it, then swap the bars, but I'm running into an issue. I'm using idxContainer object to store my minimum index, but after each run of innerLoopHelper, it ends up being equal to i and thus there is no swap. I have been stuck here for a few hours and am utterly confused.
function selectionSortTest() {
var barContainer = document.getElementById("bar-container");
var barArr = barContainer.childNodes;
outerLoopHelper(0, barArr, barContainer);
console.log(array);
}
function outerLoopHelper(i, barArr, barContainer) {
if (i < array.length - 1) {
setTimeout(() => {
var idxContainer = {
idx: i
};
innerLoopHelper(i + 1, idxContainer, barArr);
console.log(idxContainer);
let minIdx = idxContainer.idx;
let temp = array[minIdx];
array[minIdx] = array[i];
array[i] = temp;
barContainer.insertBefore(barArr[i], barArr[minIdx])
barContainer.insertBefore(barArr[minIdx], barArr[i]);
//console.log("Swapping indices: " + i + " and " + minIdx);
outerLoopHelper(++i, barArr, barContainer);
}, 100);
}
}
function innerLoopHelper(j, idxContainer, barArr) {
if (j < array.length) {
setTimeout(() => {
if (j - 1 >= 0)
barArr[j - 1].style.backgroundColor = "gray";
barArr[j].style.backgroundColor = "red";
if (array[j] < array[idxContainer.idx])
idxContainer.idx = j;
innerLoopHelper(++j, idxContainer, barArr);
}, 100);
}
}
I know this is a long post, but I just wanted to be as specific as possible. Thank you so much for reading, and any guidance will be appreciated!
Convert your sorting function to a generator function*, this way, you can yield it the time you update your rendering:
const sorter = selectionSortAnimated();
const array = Array.from( { length: 100 }, ()=> Math.round(Math.random()*50));
const max_element = 50;
renderVisualizer();
anim();
// The animation loop
// simply calls itself until our generator function is done
function anim() {
if( !sorter.next().done ) {
// schedules callback to before the next screen refresh
// usually 60FPS, it may vary from one monitor to an other
requestAnimationFrame( anim );
// you could also very well use setTimeout( anim, t );
}
}
// Converted to a generator function
function* selectionSortAnimated() {
const barContainer = document.getElementById("bar-container");
const barArr = barContainer.children;
for (let i = 0; i < barArr.length - 1; i++) {
let min_idx = i;
let minNum = parseInt(barArr[i].textContent);
for (let j = i + 1; j < barArr.length; j++) {
let jNum = parseInt(barArr[j].textContent, 10);
if (jNum < minNum) {
barArr[min_idx].classList.remove( 'selected' );
min_idx = j;
minNum = jNum;
barArr[min_idx].classList.add( 'selected' );
}
// highlight
barArr[j].classList.add( 'checking' );
yield; // tell the outer world we are paused
// once we start again
barArr[j].classList.remove( 'checking' );
}
barArr[min_idx].classList.remove( 'selected' );
barContainer.insertBefore(barArr[i], barArr[min_idx])
barContainer.insertBefore(barArr[min_idx], barArr[i]);
// pause here too?
yield;
}
}
// same as OP
function renderVisualizer() {
const barContainer = document.getElementById("bar-container");
//Empties bar-container div
while (barContainer.hasChildNodes()) {
barContainer.removeChild(barContainer.lastChild);
}
var heightMult = barContainer.offsetHeight / max_element;
var temp = barContainer.offsetWidth / array.length;
var barWidth = temp * 0.9;
var margin = temp * 0.05;
//Creating array element bars
for (var i = 0; i < array.length; i++) {
var arrayBar = document.createElement("div");
arrayBar.className = "array-bar"
if (barWidth > 30)
arrayBar.textContent = array[i];
//Style
arrayBar.style.textAlign = "center";
arrayBar.style.height = array[i] * heightMult + "px";
arrayBar.style.width = barWidth;
arrayBar.style.margin = margin;
barContainer.appendChild(arrayBar);
}
}
#bar-container {
height: 250px;
white-space: nowrap;
width: 3500px;
}
.array-bar {
border: 1px solid;
width: 30px;
display: inline-block;
background-color: #00000022;
}
.checking {
background-color: green;
}
.selected, .checking.selected {
background-color: red;
}
<div id="bar-container"></div>
So I thought about this, and it's a little tricky, what I would do is just store the indexes of each swap as you do the sort, and then do all of the animation seperately, something like this:
// how many elements we want to sort
const SIZE = 24;
// helper function to get a random number
function getRandomInt() {
return Math.floor(Math.random() * Math.floor(100));
}
// this will hold all of the swaps of the sort.
let steps = [];
// the data we are going to sort
let data = new Array(SIZE).fill(null).map(getRandomInt);
// and a copy that we'll use for animating, this will simplify
// things since we can just run the sort to get the steps and
// not have to worry about timing yet.
let copy = [...data];
let selectionSort = (arr) => {
let len = arr.length;
for (let i = 0; i < len; i++) {
let min = i;
for (let j = i + 1; j < len; j++) {
if (arr[min] > arr[j]) {
min = j;
}
}
if (min !== i) {
let tmp = arr[i];
// save the indexes to swap
steps.push({i1: i, i2: min});
arr[i] = arr[min];
arr[min] = tmp;
}
}
return arr;
}
// sort the data
selectionSort(data);
const container = document.getElementById('container');
let render = (data) => {
// initial render...
data.forEach((el, index) => {
const div = document.createElement('div');
div.classList.add('item');
div.id=`i${index}`;
div.style.left = `${2 + (index * 4)}%`;
div.style.top = `${(98 - (el * .8))}%`
div.style.height = `${el * .8}%`
container.appendChild(div);
});
}
render(copy);
let el1, el2;
const interval = setInterval(() => {
// get the next step
const {i1, i2} = steps.shift();
if (el1) el1.classList.remove('active');
if (el2) el2.classList.remove('active');
el1 = document.getElementById(`i${i1}`);
el2 = document.getElementById(`i${i2}`);
el1.classList.add('active');
el2.classList.add('active');
[el1.id, el2.id] = [el2.id, el1.id];
[el1.style.left, el2.style.left] = [el2.style.left, el1.style.left]
if (!steps.length) {
clearInterval(interval);
document.querySelectorAll('.item').forEach((el) => el.classList.add('active'));
}
}, 1000);
#container {
border: solid 1px black;
box-sizing: border-box;
padding: 20px;
height: 200px;
width: 100%;
background: #EEE;
position: relative;
}
#container .item {
position: absolute;
display: inline-block;
padding: 0;
margin: 0;
width: 3%;
height: 80%;
background: #cafdac;
border: solid 1px black;
transition: 1s;
}
#container .item.active {
background: green;
}
<div id="container"></div>

Check if a string contains a number grater/less than and change it's style in the DOM

I'm trying to write a function that changes the color of a student grades strings according to a number inside of this string.
How to ask JavaScript to look for a number inside a string and also implement an if statement?
// courses grades function
function passStudentInfo(studentID) {
$.get("https://college.com/api/getCourses/" + studentID, function (data) {
if (!data || !data.length) {
console.log = 'not getting courses';
return;
}
data.forEach(function(element) {
document.getElementById('grade').innerHTML += element.courseName + ": " + element.examMark + "<br />" + "<hr>";
//this is the function I'm trying to write
function colors() {
let allGrades = document.getElementById('grade');
for (i = 0; i < allGrades.length; i++ ) {
if ( ?? < 60) {
allGrades[i].innerHTML.style.color = 'red';
} else if ( ?? >= 60 && x < 80 ) {
allGrades[i].innerHTML.style.color = 'yellow';
}
else {
allGrades[i].innerHTML.style.color = 'green';
}
}
}
colors()
//this is my html page
<h1 class="green">Your Grades</h1>
<h2>A summary of Your achievements</h2>
<div class="container achieve">
<p id="grade"></p>
</div>
.achieve {
border: 2px solid #3d3e5b;
margin-bottom: 2rem;
padding-top: 1rem;
}
.achieve p {
font-weight: 600;
font-size: 1.5;
}
I expect the output of red color string for a sentence containing a grade less than 60, yellow for 60-80 and green for above 80.
Example of one way to get it done, checking the examMark within the forEach and applying an appropriate class to a surrounding element in each row.
var data = [
{courseName: 'Math', examMark: 76.4},
{courseName: 'Science', examMark: 66.3},
{courseName: 'Spanish', examMark: 98.6}
];
data.forEach(function(element) {
// figure out what color the grade gives
var gradeColor = 'red';
if (element.examMark >= 80) {
gradeColor = 'green';
} else if (element.examMark >= 60) {
gradeColor = 'yellow';
}
document.getElementById('grade').innerHTML += "<div class='" + gradeColor + "'>" + element.courseName + ": " + element.examMark + "</div>";
});
.red {
color: red;
}
.yellow {
color: yellow;
}
.green {
color: green;
}
<div id='grade'></div>
for your If statements, try using >= or <=. it worked for me when my if statements weren't working correctly. Replace all of your < with <= or >=.

javascript settimeout takes too much time than expected to excute the code

I have tried to make the code works fine in stackoverflow but when I added the javascript code, it has stopped.any way
I have this code:
showCharacters("hey, how are you");
function showCharacters(text) {
var charactersArray = new String(text).split('');
var i;
for (i = 0; i < charactersArray.length; i++) {
setCharacter(i, charactersArray);
}
deleteAll(i - 1);
}
function setCharacter(i, charactersArray) {
setTimeout(function() {
document.getElementById("characters").innerHTML = document.getElementById("characters").innerHTML + charactersArray[i];
}, 1000 * i);
}
function deleteAll(i) {
setTimeout(function() {
var text = document.getElementById("characters").innerHTML;
var charactersArray = new String(text).split('');
var charactersArrayLength = charactersArray.length - 1;
for (var j = charactersArrayLength; j >= 0; j--) {
charactersArray.splice(j, 1);
deleteCharacter(charactersArray.join(""), i + charactersArrayLength - j + 1);
}
}, (i + 1) * 1000);
}
function deleteCharacter(text, i) {
setTimeout(function() {
document.getElementById("characters").innerHTML = text;
}, 1000 * i);
}
#divContainer {
display: flex;
align-items: center;
float: right;
}
#characters {
font-weight: 700;
color: rgb(0, 0, 0);
font-size: 40px;
padding-top: 2px;
white-space: nowrap;
}
<div id="divContainer">
<h1 id="characters">
</h1>
</div>
I made the code to write one character every second and after complete the text it will delete one character every second,the problem is that after complete the text it will wait for a few seconds before starting deleting the characters.I did not do that.
I have followed the time value of settimeout method and everything is fine.
I want it to start deleting characters directly after complete the text.
any help please.
Dont line up app the timeouts in a row do them one after the other.
Here is how you should do it. I speed the time up a bit, but that matters not.
var text = "Add a new timeout as needed."
var div = document.createElement("div");
document.body.appendChild(div);
var textPos = 0;
function textAdd(){
div.textContent += text[textPos++];
if(textPos >= text.length){
setTimeout(clearText,1000);
}else{
setTimeout(textAdd,200);
}
}
function clearText(){
textPos --;
div.textContent = text.substr(0,textPos);
if(textPos === 0){
setTimeout(textAdd,1000);
}else{
setTimeout(clearText,200);
}
}
textAdd();
I stayed close to your code, the fix was actually quite simple, I marked it with THE FIX below.
// Replace spaces with non-breaking spaces so it does not stutter, and use global var
var text = "hey, how are you".replace(/ /g, String.fromCharCode(160));
showCharacters();
function showCharacters() {
var chars = new String(text).split('');
var i;
for (i = 0; i < chars.length; i++) {
setCharacter(i, chars);
}
deleteAll(i - 1);
}
function setCharacter(i, chars) {
setTimeout(function() {
document.getElementById("characters").innerHTML = document.getElementById("characters").innerHTML + chars[i];
}, 200 * i);
}
function deleteAll(i) {
setTimeout(function() {
var chars = new String(text).split('');
var len = chars.length - 1;
for (var j = len; j >= 0; j--) {
chars.splice(j, 1);
deleteCharacter(chars.join(""), i - j + 1); // THE FIX: don't add length here
}
}, (i + 1) * 200);
}
function deleteCharacter(text, i) {
setTimeout(function() {
document.getElementById("characters").innerHTML = text;
}, 200 * i);
}
#divContainer {
display: flex;
align-items: center;
float: right;
}
#characters {
font-weight: 700;
color: rgb(0, 0, 0);
font-size: 40px;
padding-top: 2px;
white-space: nowrap;
}
<div id="divContainer">
<h1 id="characters">
</h1>
</div>

.forEach method applying update to all items in array instead of individual item

I'm making a small exercise for some students of mine where I am automating a kind of 10 pin bowling game I have put it into a JsBin here https://jsbin.com/qilizo/edit?html,js,output. I don't know whether I am tired, stupid or it's just because I am working on a national holiday but something has me puzzled. When i start the game I prompt the user to set up a number of desired players. This automatically produces an object array of Players like so:
[{score: Array[10], spareCount: 0, strikeCount: 0, username: "Player 1"}, ...]
Now later I allow the user to play frames where each Player in our array has two throws... I collect the score and add it to the certain player's score array. However when I try to perform this action using a .forEach method the score I generate is applied to all items in my Players array (play the game and see). I have put my code in a jsBin and the problem is on line 109 : a.score[currentFrame - 1] = playFrame();
I have tried to amend my code but I can't work out why the current (or last) frame score is being applied to all Player objects! If you can understand my syntax error and explain why I would be most appreciative. Play the game (just click the button after setting the player numbers) and you will see what I mean...
Snippet:
var players,
currentFrame = 0,
currentThrow = 0;
// helper functions
// Accurate isNumber function... Thank you Crockford (see JavaScript: The Good Parts)
function isNumber(value) {
return typeof(value === 'number') && isFinite(value);
}
function frameStyle(k) {
var returnCssClass,
k = k + 1;
if (k < currentFrame) {
returnCssClass = 'played-frame';
} else if (k === currentFrame) {
returnCssClass = 'current-frame';
} else {
returnCssClass = null;
}
return returnCssClass;
}
function setUpPlayers(num) {
var tempArray = [],
tempName = 'Player ',
emptyScores = Array(10).fill([-1, -1]); // set default to -1 as a rubbish player may hit no pins!
for (var i = 0; i < num; i++) {
tempArray.push({
username: tempName + (i + 1),
score: emptyScores,
strikeCount: 0,
spareCount: 0
}); // the way I have named the tempName is technically an antipattern!
}
return tempArray;
}
function getTotals(scores) {
var totalScore = scores.reduce(function(a, b) {
return a + b.reduce(function(c, d) {
return (c + (c + ((d > 0) ? d : 0)));
}, 0);
}, 0);
return totalScore;
}
function displayScore(score) {
// toDo reformat!
var formatScore = score.map(function(a, b) {
if (a === -1) {
a = '-';
} else if (a === 10) {
a = 'X';
}
return a;
});
return formatScore;
}
function createGrid() {
// If only I was using ES6 I could have multi line support!
var playerLen = players.length,
scoresLen = players[0].score.length;
boards = '<div class="score-board">' +
'<!-- one row for each player -->';
// need to loop this through the players...
for (var i = 0; i < playerLen; i++) {
boards += '<div class="row">' +
'<!-- first cell is the name -->' +
'<div class="name">' + players[i].username + '</div>';
// need to loop this with the users scores
for (var k = 0; k < scoresLen; k++) {
boards += '<div class="game ' + frameStyle(k) + ' ">' + displayScore(players[i].score[k]) + '</div>';
}
// don't forget the total
boards += '<div class="player-total">' + getTotals(players[i].score) + '</div>';
boards += '</div>';
}
boards += '</div>';
boards += '<div>Current Frame: ' + currentFrame + '</div>';
boards += '<button type="button" onclick="startGame()">Start Game</button>';
// fill the holder....
document.getElementById('boardHolder').innerHTML = boards;
}
function startGame() {
if (currentFrame >= 10) {
announceWinner();
} else {
currentFrame++;
// do the throws for Each Player!
players.forEach(function(a, b) {
a.score[currentFrame - 1] = playFrame();
});
// update the grid
createGrid();
// recurrrrrrsion....
//startGame();
}
}
function throwBall(pinsStanding) {
// i know it isn't a ball
return Math.floor(Math.random() * (pinsStanding + 1));
}
function playFrame() {
// here we just create the array and determine if we have a strike or a spare!
var pinsStanding = 10,
frameScore = [],
frameThrows = 2,
pinsDown;
for(var i = 0; i < frameThrows; i++) {
pinsDown = throwBall(pinsStanding);
pinsStanding = pinsStanding - pinsDown;
// if it is the pinsStanding = 0 and it is the first throw - a strike!
if(pinsStanding === 0 && i === 1) {
pinsStanding = 10;
frameThrows = 3;
}
// what if it is a spare?
frameScore.push(pinsDown);
}
return frameScore;
}
function announceWinner() {
}
// kick it all off!!!
window.onload = function() {
// lets get some users....
players = prompt('Please enter the NUMBER of players?', 2);
// check we have a number...
if (isNumber(players)) {
players = setUpPlayers(players);
createGrid();
}
};
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}
/* classes */
.score-board {
border: 1px solid #000;
}
.row {
display: block;
border-bottom: 1px solid #000;
}
.row:last-child {
border-bottom: none;
}
.row > div {
display: inline-block;
padding: 5px;
}
.game {
border-right: 1px solid #000;
}
.name {
background-color: #f5f5f5;
border-right: 1px solid #000;
}
.player-total {
text-align: right;
background-color: #d5eabb;
}
.played-frame {
background-color: #aee1e8;
}
.current-frame {
background-color: #ffc0cb;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<h1>Let's go bowling!</h1>
<div id="boardHolder">
</div>
</body>
</html>
Here is the bin!
https://jsbin.com/qilizo/edit?html,js,output
You need to call Array(10).fill([-1, -1]) inside for loop, because otherwise all objects will share the same score array:
function setUpPlayers(num) {
var tempArray = [],
tempName = 'Player ';
for (var i = 0; i < num; i++) {
tempArray.push({
username: tempName + (i + 1),
score: Array(10).fill([-1, -1]),// set default to -1 as a rubbish player may hit no pins!
strikeCount: 0,
spareCount: 0
}); // the way I have named the tempName is technically an antipattern!
}
return tempArray;
}
https://jsbin.com/yeyupiteyu/1/edit?html,js,output
In JavaScript objects are passed by reference, and since array is an object, if you declare emptyScores outside the loop and then assign it to every element of the array, all elements will share the same score array.
You have make new emptyScores array for each element, so you have to declare it inside the loop:
var tempArray = [],
tempName = 'Player ';
for (var i = 0; i < num; i++) {
var emptyScores = Array(10).fill([-1, -1]);
tempArray.push({
username: tempName + (i + 1),
score: emptyScores,
strikeCount: 0,
spareCount: 0
});
}

Categories

Resources