diff --git a/README.md b/README.md index 60f55e53..00e79ef2 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,21 @@ # Project Name -Replace this readme with your own information about your project. - -Start by briefly describing the assignment in a sentence or two. Keep it short and to the point. +A digital version of the classic board game "Guess Who" using JavaScript, HTML, and CSS. Applying concepts like DOM manipulation, event handling, function definitions, conditional statements, delayed execution, variable assignment, and object and array manipulation. ## The problem -Describe how you approached to problem, and what tools and techniques you used to solve it. How did you plan? What technologies did you use? If you had more time, what would be next? +Describe how you approached to problem: This project was fun! I enjoyed practicing my JAvaScript skills and it helped me improve my knowledge of syntax and conditional statements. I was able to push myself with some stretch goals, adding more functionality, including a timer, more question options, and game counter. + +How did you plan?: I created a flow diagram to help tract the user experience and used many console.logs to keep tract of the answer. + +What technologies did you use?: HTML, CSS, JavaScript + +If you had more time, what would be next?: Make the design cleaner! ## View it live -Have you deployed your project somewhere? Be sure to include the link to the deployed project so that the viewer can click around and see what it's all about. +https://wk3-guesswho-c638bb.netlify.app/ + +## Screenshots + +![Screenshot of front page](/code/images/screenshot1.png?raw=true "Screenshot of front page") diff --git a/code/images/screenshot1.png b/code/images/screenshot1.png new file mode 100644 index 00000000..74cf7d5f Binary files /dev/null and b/code/images/screenshot1.png differ diff --git a/code/index.html b/code/index.html index 0479b061..1f1001b2 100644 --- a/code/index.html +++ b/code/index.html @@ -1,64 +1,84 @@ - - - - Project Name - - - - - -
-
- Guess Who -

- -
-
- - - +
+
+
+ +
+ Guess Who +

+

+ +

+ +
+ + + + \ No newline at end of file diff --git a/code/script.js b/code/script.js index 5207730c..03dafc6d 100644 --- a/code/script.js +++ b/code/script.js @@ -1,9 +1,25 @@ -// All the DOM selectors stored as short variables +//--------DOM selectors stored as short variables-------------// + const board = document.getElementById('board') const questions = document.getElementById('questions') const restartButton = document.getElementById('restart') +const findoutButton = document.getElementById('filter') +const playAgainButton = document.getElementById('playAgain') + +const winOrLoseText = document.getElementById("winOrLoseText") +const winOrLoseTexth2 = document.getElementById("winOrLoseTexth2") +const winOrLoseSection = document.getElementById("winOrLose") +const questionsAskedCount = document.getElementById("questionsAsked") +const worLQuestionsCount = document.getElementById("worLQuestionsAsked") + +//for Timer +let minutesLabel = document.getElementById("minutes"); +let secondsLabel = document.getElementById("seconds"); + +//NOTE: don't need a GUESS button DOM selector because we add this button dynamically in this J.S + +//-------- Main Array (with all characters as objects) -------// -// Array with all the characters, as objects const CHARACTERS = [ { name: 'Jabala', @@ -11,7 +27,8 @@ const CHARACTERS = [ hair: 'hidden', eyes: 'hidden', accessories: ['glasses', 'hat'], - other: [] + other: [], + nationality: 'australian' //added another property to ask about nationality }, { name: 'Jack', @@ -19,7 +36,8 @@ const CHARACTERS = [ hair: 'hidden', eyes: 'blue', accessories: ['hat'], - other: [] + other: [], + nationality: 'australian' }, { name: 'Jacques', @@ -27,7 +45,8 @@ const CHARACTERS = [ hair: 'grey', eyes: 'blue', accessories: ['hat'], - other: ['smoker'] + other: ['smoker'], + nationality: 'british' }, { name: 'Jai', @@ -35,7 +54,8 @@ const CHARACTERS = [ hair: 'black', eyes: 'brown', accessories: [], - other: [] + other: [], + nationality: 'icelandic' }, { name: 'Jake', @@ -43,7 +63,8 @@ const CHARACTERS = [ hair: 'yellow', eyes: 'green', accessories: ['glasses'], - other: [] + other: [], + nationality: 'british' }, { name: 'James', @@ -51,7 +72,8 @@ const CHARACTERS = [ hair: 'brown', eyes: 'green', accessories: ['glasses'], - other: [] + other: [], + nationality: 'icelandic' }, { name: 'Jana', @@ -59,7 +81,8 @@ const CHARACTERS = [ hair: 'black', eyes: 'hidden', accessories: ['glasses'], - other: [] + other: [], + nationality: 'swiss' }, { name: 'Jane', @@ -67,7 +90,8 @@ const CHARACTERS = [ hair: 'yellow', eyes: 'hidden', accessories: ['glasses'], - other: [] + other: [], + nationality: 'australian' }, { name: 'Jaqueline', @@ -75,16 +99,17 @@ const CHARACTERS = [ hair: 'orange', eyes: 'green', accessories: ['glasses'], - other: [] + other: [], + nationality: 'icelandic' }, - { name: 'Jazebelle', img: 'images/jazebelle.svg', hair: 'purple', eyes: 'hidden', accessories: ['glasses'], - other: ['smoker'] + other: ['smoker'], + nationality: 'swiss' }, { name: 'Jean', @@ -92,7 +117,8 @@ const CHARACTERS = [ hair: 'brown', eyes: 'blue', accessories: ['glasses', 'hat'], - other: ['smoker'] + other: ['smoker'], + nationality: 'australian' }, { name: 'Jeane', @@ -100,7 +126,8 @@ const CHARACTERS = [ hair: 'brown', eyes: 'green', accessories: ['glasses'], - other: [] + other: [], + nationality: 'swiss' }, { name: 'Jed', @@ -108,7 +135,8 @@ const CHARACTERS = [ hair: 'orange', eyes: 'green', accessories: ['glasses', 'hat'], - other: ['smoker'] + other: ['smoker'], + nationality: 'icelandic' }, { name: 'Jenni', @@ -116,7 +144,8 @@ const CHARACTERS = [ hair: 'white', eyes: 'hidden', accessories: ['hat'], - other: [] + other: [], + nationality: 'british' }, { name: 'Jeri', @@ -124,7 +153,8 @@ const CHARACTERS = [ hair: 'orange', eyes: 'green', accessories: ['glasses'], - other: [] + other: [], + nationality: 'australian' }, { name: 'Jerry', @@ -132,7 +162,8 @@ const CHARACTERS = [ hair: 'hidden', eyes: 'blue', accessories: ['hat'], - other: [] + other: [], + nationality: 'icelandic' }, { name: 'Jess', @@ -140,7 +171,8 @@ const CHARACTERS = [ hair: 'black', eyes: 'blue', accessories: ['glasses'], - other: [] + other: [], + nationality: 'swiss' }, { name: 'Jocelyn', @@ -148,7 +180,8 @@ const CHARACTERS = [ hair: 'black', eyes: 'brown', accessories: ['glasses'], - other: [] + other: [], + nationality: 'british' }, { name: 'Jon', @@ -156,7 +189,8 @@ const CHARACTERS = [ hair: 'brown', eyes: 'green', accessories: ['glasses'], - other: [] + other: [], + nationality: 'icelandic' }, { name: 'Jordan', @@ -164,7 +198,8 @@ const CHARACTERS = [ hair: 'yellow', eyes: 'hidden', accessories: ['glasses', 'hat'], - other: [] + other: [], + nationality: 'british' }, { name: 'Josephine', @@ -172,7 +207,8 @@ const CHARACTERS = [ hair: 'grey', eyes: 'brown', accessories: [], - other: [] + other: [], + nationality: 'swiss' }, { name: 'Josh', @@ -180,7 +216,8 @@ const CHARACTERS = [ hair: 'yellow', eyes: 'green', accessories: [], - other: [] + other: [], + nationality: 'icelandic' }, { name: 'Jude', @@ -188,7 +225,8 @@ const CHARACTERS = [ hair: 'black', eyes: 'green', accessories: [], - other: [] + other: [], + nationality: 'australian' }, { name: 'Julie', @@ -196,16 +234,40 @@ const CHARACTERS = [ hair: 'black', eyes: 'brown', accessories: ['glasses', 'hat'], - other: [] + other: [], + nationality: 'icelandic' }, ] +//---------------- Global Variables -------------------------// + // Global variables -let secret -let currentQuestion -let charactersInPlay +let secret // Will be the secret person object +let currentQuestion // Will be the current question object +let charactersInPlay = []; // Array of people still in the game +let totalSeconds = 0; // for Timer +let questionsAskedCounter = 0; //questions asked counter + -// Draw the game board +//----------- Functions after this comment -----------------// + +// Function - start - to start (and restart) the game +const start = () => { + charactersInPlay = CHARACTERS + //if this is not the first call of start() the next 2 lines clear the win and lose screens + winOrLoseSection.style.display = "none"; + board.style.display = "flex"; + generateBoard(); //Step 1 - I added this on Tues to see the board + setSecret(); //Step 1 - 'Computer player' selects the secret character the user tries to guess + valString = 0; + totalSeconds = 0; + questionsAskedCounter = 0; + setInterval(setTime, 1000); //Start/re-start timer + questionsAskedCount.innerHTML = `Questions asked: ${questionsAskedCounter}` //Set questions asked to display: 0 at start +} + + +// Function - generateBoard - to draw the game board const generateBoard = () => { board.innerHTML = '' charactersInPlay.forEach((person) => { @@ -222,104 +284,178 @@ const generateBoard = () => { }) } -// Randomly select a person from the characters array and set as the value of the variable called secret + +// Function - setSecret - Randomly select a person from the characters array and set as the value of the variable called secret const setSecret = () => { secret = charactersInPlay[Math.floor(Math.random() * charactersInPlay.length)] + console.log(`Secret person is set as: ${secret.name}`); //Used for debugging } -// This function to start (and restart) the game -const start = () => { - // Here we're setting charactersInPlay array to be all the characters to start with - charactersInPlay = CHARACTERS - // What else should happen when we start the game? -} -// setting the currentQuestion object when you select something in the dropdown +// Function - selectQuestion - setting currentQuestion object when you select something in the dropdown const selectQuestion = () => { - const category = questions.options[questions.selectedIndex].parentNode.label - // This variable stores what option group (category) the question belongs to. - // We also need a variable that stores the actual value of the question we've selected. - // const value = + const category = questions.options[questions.selectedIndex].parentNode.label //stores selected category after find out button + const value = questions.options[questions.selectedIndex].value //stores selected value after find out button + //declare an object currentQuestion = { category: category, - // value: value + value: value } + return (currentQuestion) //do I need this?? } -// This function should be invoked when you click on 'Find Out' button. + +// Function - checkQuestion - should be invoked when you click on 'Find Out' button const checkQuestion = () => { - const { category, value } = currentQuestion - // Compare the currentQuestion details with the secret person details in a different manner based on category (hair/eyes or accessories/others). - // See if we should keep or remove people based on that - // Then invoke filterCharacters - if (category === 'hair' || category === 'eyes') { + selectQuestion(); //jump into selectQuestion to get details of what the user has guessed and return here to compare the values - } else if (category === 'accessories' || category === 'other') { + const { category, value, } = currentQuestion + if (category === 'hair' || category === 'eyes' || category === 'nationality') { + if (secret.hair === value || secret.eyes === value || secret.nationality === value) { + filterCharacters(true) + } else { + filterCharacters(false) + } } + + if (category === 'accessories' || category === 'other') { + if (secret.accessories.includes(value) || secret.other.includes(value)) { //the .include array property took me a ages to work out + filterCharacters(true) + } else { + filterCharacters(false) + } + } + questionsAskedCounter = questionsAskedCounter + 1; //add 1 to question counter + questionsAskedCount.innerHTML = `Questions asked: ${questionsAskedCounter}`; //display question counter } -// It'll filter the characters array and redraw the game board. + +// Function - filterCharacters - filter characters array and redraw the game board const filterCharacters = (keep) => { const { category, value } = currentQuestion - // Show the correct alert message for different categories - if (category === 'accessories') { + + // Filter by category and display correct alert, then invoke generateBoard function + if (category === 'hair') { + if (keep) { + alert(`YES, the person has ${value} hair! We will keep all people who have ${value} hair.`) + charactersInPlay = charactersInPlay.filter((person) => person.hair === value); + generateBoard(charactersInPlay); + } else { + alert(`NO, the person doesn't have ${value} hair. We will remove all people who have ${value} hair`) + charactersInPlay = charactersInPlay.filter((person) => person.hair !== value); + generateBoard(charactersInPlay); + } + } else if (category === 'eyes') { if (keep) { - alert( - `Yes, the person wears ${value}! Keep all people that wears ${value}` - ) + alert(`YES, the person has ${value} eyes! We will keep all people who have ${value} coloured eyes.`) + charactersInPlay = charactersInPlay.filter((person) => person.eyes === value); + generateBoard(charactersInPlay); } else { - alert( - `No, the person doesn't wear ${value}! Remove all people that wears ${value}` - ) + alert(`NO, the person doesn't have ${value} eyes. We will remove all people who have ${value} eyes`) + charactersInPlay = charactersInPlay.filter((person) => person.eyes !== value); + generateBoard(charactersInPlay); + } + } else if (category === 'nationality') { + if (keep) { + alert(`YES, the person is ${value} ! We will keep all people who are ${value}.`) + charactersInPlay = charactersInPlay.filter((person) => person.nationality === value); + generateBoard(charactersInPlay); + } else { + alert(`NO, the person is not ${value}. We will remove all people who are ${value}`) + charactersInPlay = charactersInPlay.filter((person) => person.nationality !== value); + generateBoard(charactersInPlay); + } + } else if (category === 'accessories') { + if (keep) { + alert(`YES, the person has ${value}! We will keep all people who have ${value}.`) + charactersInPlay = charactersInPlay.filter((person) => person.accessories.includes(value)); + generateBoard(charactersInPlay); + } else { + alert(`NO, the person doesn't have ${value}. We will remove all people who have ${value}`) + charactersInPlay = charactersInPlay.filter((person) => !person.accessories.includes(value)); + generateBoard(charactersInPlay); } } else if (category === 'other') { - // Similar to the one above - } else { if (keep) { - // alert popup that says something like: "Yes, the person has yellow hair! Keep all people with yellow hair" + alert(`YES, the person is a ${value}! We will keep all people who haven't kicked the habit.`) + charactersInPlay = charactersInPlay.filter((person) => person.other.includes(value)); + generateBoard(charactersInPlay); } else { - // alert popup that says something like: "No, the person doesnt have yellow hair! Remove all people with yellow hair" + alert(`NO, the person is not a ${value}. We will remove all people who are ${value}s`) + charactersInPlay = charactersInPlay.filter((person) => !person.other.includes(value)); + generateBoard(charactersInPlay); } + } else { + generateBoard(charactersInPlay); } - - // Determine what is the category - // filter by category to keep or remove based on the keep variable. - /* - for hair and eyes : - charactersInPlay = charactersInPlay.filter((person) => person[attribute] === value) - or - charactersInPlay = charactersInPlay.filter((person) => person[attribute] !== value) - - for accessories and other - charactersInPlay = charactersInPlay.filter((person) => person[category].includes(value)) - or - charactersInPlay = charactersInPlay.filter((person) => !person[category].includes(value)) - */ - - // Invoke a function to redraw the board with the remaining people. } -// when clicking guess, the player first have to confirm that they want to make a guess. + +// Function - guess - when clicking guess, the player first has to confirm that they want to make a guess. const guess = (personToConfirm) => { - // store the interaction from the player in a variable. - // remember the confirm() ? - // If the player wants to guess, invoke the checkMyGuess function. + + let confirmGuess = confirm(`Are you sure you want to guess on ${personToConfirm}?`); + + if (confirmGuess) { + checkMyGuess(personToConfirm); + } else if (!confirmGuess) { + return; + } } -// If you confirm, this function is invoked + +// Function - checkMyGuess - if you confirm, this function is invoked const checkMyGuess = (personToCheck) => { - // 1. Check if the personToCheck is the same as the secret person's name - // 2. Set a Message to show in the win or lose section accordingly - // 3. Show the win or lose section - // 4. Hide the game board + + if (personToCheck == secret.name) { // Check if the personToCheck is the same as the secret person's name + winOrLoseText.innerHTML = "You won! 🎉" // Set a Message to show in the win or lose section accordingly + winOrLoseTexth2.innerHTML = "Well done, Thank you for playing!" + } + else { + winOrLoseText.innerHTML = "You lost 👎" + winOrLoseTexth2.innerHTML = `Good try, bad luck this time, you should try again` + } + winOrLoseSection.style.display = "block"; // Show the win or lose section + board.style.display = "none"; // Hide the game board + worLQuestionsCount.innerHTML = `Questions asked: ${questionsAskedCounter}`; // Display question counter +} + + +// Functions - setTime - added for TIMER - +// I found this timer function online and it only works so far when you refresh the page, if you reset with a button it then counts in a weird 2 step quick pace, also does not record time or display how long the game went for. But I wanted to keep the code here so, if I get time in the future, I may come back and try to get it to work correctly +function setTime() { + ++totalSeconds; + secondsLabel.innerHTML = pad(totalSeconds % 60); + minutesLabel.innerHTML = pad(parseInt(totalSeconds / 60)); } +function pad(val) { + let valString = val + ""; + if (valString.length < 2) { + return "0" + valString; + } + else { + return valString; + } +} + + +//-------------------- Program Starts Here --------------------// + // Invokes the start function when website is loaded start() -// All the event listeners -restartButton.addEventListener('click', start) + +//-------------------- All Event Listeners --------------------// + +//NOTE: 3 events - restart button, find out button, play again button (NONE for Guess button as it is added dynamically) + +restartButton.addEventListener('click', start) //NOTE: technigo wrote this +playAgainButton.addEventListener('click', start) +findoutButton.addEventListener('click', checkQuestion) + +//filterDropdown.addEventListener('change', filterGuess) //this is suggested in instructions but I have not used it as I have all my event listeners linked to buttons. diff --git a/code/style.css b/code/style.css index 1602adfe..a4fe8a31 100644 --- a/code/style.css +++ b/code/style.css @@ -1,7 +1,7 @@ /* Global css variables used for colors */ :root { - --primary: #a259ff; - --secondary: #b0a6ff; + --primary: #00804d; + --secondary: #ccffeb; } body { @@ -16,7 +16,12 @@ h1 { font-weight: 500; line-height: 48px; margin: 10px 0; - color: white; + color: #00804d; +} + +p { + font-size: 20px; + color: #00804d; } .question-section { @@ -25,7 +30,7 @@ h1 { background-color: var(--secondary); display: flex; flex-direction: column; - padding: 5vw; + padding: 4vw; align-items: flex-start; box-sizing: border-box; } @@ -35,9 +40,18 @@ select { border: none; font-size: 22px; font-family: 'Montserrat'; - color: var(--secondary); + color: var(--primary); width: 100%; - margin: 24px 0; + margin: 15px 0; + padding: 5px; +} + +input { + font-size: 20px; + font-family: 'Montserrat'; + color: var(--primary); + margin: 2px 0; + padding: 2px; } .guess-who-icon { @@ -64,8 +78,8 @@ select { width: 135px; height: 165px; overflow: hidden; - border: 3px solid var(--secondary); - border-radius: 4px; + border: 2px solid var(--primary); + border-radius: 3px; margin: 3px; display: flex; flex-direction: column; @@ -85,7 +99,7 @@ select { flex-direction: column; align-items: center; justify-content: center; - color: white; + color: #00804d; text-align: center; height: inherit; } @@ -148,7 +162,6 @@ button { /****** WIN OR LOSE SECTION ******/ .win-or-lose-wrapper { display: none; - align-items: center; justify-content: center; background: var(--secondary); position: absolute; @@ -157,24 +170,8 @@ button { left: 0; right: 0; height: 100vh; -} - -.win-or-lose { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - max-width: 700px; text-align: center; -} - -.win-or-lose img { - align-self: flex-start; - margin-left: 15%; -} - -.win-or-lose h1 { - margin-bottom: 32px; + padding: 100px 0 100px 0; } @media (max-width: 768px) { @@ -186,9 +183,11 @@ button { width: 100%; min-height: 0; } + button { padding: 8px 16px; } + h1 { font-size: 24px; line-height: 30px; @@ -231,3 +230,30 @@ button { border: none; } } + +/****** TIMER - ADDED BY BEC ******/ +#minutes, +#colon, +#seconds { + font-size: 25px; +} + +.game-timer { + display: inline-block; + color: #00804d; + border: 2px solid #00804d; + background: #ccffeb; + border-radius: 4px; + padding: 0 10px 0 10px; + margin: 10px 0 0 0; + vertical-align: middle; + min-width: 120px; + text-align: center; +} + +/*media query for mobile and tablet - remove the timer*/ +@media screen and (max-width: 933px) { + .game-timer { + display: none; + } +} \ No newline at end of file