Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Guess who game #335

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
8 changes: 3 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
# 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 'guess who' game that filters the cards based on user selection. The game selects a new "secret person" every time new board is loaded and you win by correctly guessing who it is.

## 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?
The problem for me was to understand categories, values and parameters in the objects. I had to rely on coursemates code to debug and get it working (almost completely). At the time of submission the there is no working Timer-reset function when game is restarted (only on page refresh).

## View it live
https://mellifluous-smakager-f98424.netlify.app/

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.
33 changes: 27 additions & 6 deletions code/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Project Name</title>
<title>Guess who project</title>
<link rel="stylesheet" href="style.css" />
<link
href="https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap"
Expand All @@ -12,7 +12,7 @@
</head>
<body>
<aside class="question-section">
<button type="button" id="restart" class="outlined-button">RESTART</button>
<button type="button" id="restart" class="outlined-button" onclick="start()">RESTART</button>

<img
class="guess-who-icon"
Expand All @@ -23,27 +23,46 @@
<h1>Does the person have</h1>

<select title="questions" id="questions">
//default value
<option value="default">--Please choose an option--</option>
<optgroup label="hair">
<option value="brown">brown hair</option>
<option value="yellow">yellow hair</option>
<!-- Add the rest of hair colors as options -->
<option value="red">red hair</option>
<option value="black">black hair</option>
<option value="white">white hair</option>

</optgroup>
<optgroup label="eyes">
<option value="hidden">hidden eyes</option>
<!-- Add the rest of eye colors as options -->
<option value="blue">blue eyes</option>
<option value="green">green eyes</option>
<option value="brown">brown eyes</option>
<option value="black">black eyes</option>

</optgroup>
<optgroup label="accessories">
<option value="glasses">glasses</option>
<!-- Add the other accessory option here. (hat) -->
<option value="earrings">earrings</option>
<option value="hat">a hat</option>

</optgroup>
<optgroup label="other">
<option value="smoker">a smoking habit</option>
</optgroup>
</select>

<button id="filter" class="filled-button">FIND OUT</button>
<button id="filter" class="filled-button" >FIND OUT</button>

<!-- html for the clock counter-->
<div class="timer">
<p>Time: <span id="timer">0</span></p>
</div>

</aside>



<section class="board-wrapper">
<main class="game-board" id="board"></main>
</section>
Expand All @@ -55,6 +74,8 @@ <h1>Does the person have</h1>
src="images/guess-who-bubble.png"
alt="Guess Who"
/>


<h1 id="winOrLoseText"></h1>
<button type="button" id="playAgain" class="filled-button">PLAY AGAIN</button>
</div>
Expand Down
191 changes: 154 additions & 37 deletions code/script.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,56 @@
// All the DOM selectors stored as short variables
const board = document.getElementById('board')
const questions = document.getElementById('questions')
const restartButton = document.getElementById('restart')

const board = document.getElementById('board') //board is the id of the div in html file where the cards are displayed

const questions = document.getElementById('questions') //questions is the id of the select element in html file

const restartButton = document.getElementById('restart') //restart is the id of the button element in html file

const resultScreen = document.getElementById('winOrLoseSection') //winOrLoseSection is the id of the section element in html file

const resultText= document.getElementById('winOrLoseText') //winOrLoseText is the id of the p element in html file

const playAgainButton = document.getElementById('playAgain') //playAgain is the id of the button element in html file

const findOutButton = document.getElementById('filter') //filter is the id of the button element in html file

//declaring a clock counter for the website, it starts at 2 minutes and counts down to 0
// In minute format, sarting at 02:00
let time = 120

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the timer function, as I did not do that myself! Made the page more alive and interactive. Stressed me out through ;)





// Function to format the time as "MM:SS"
function formatTime(seconds) {
const minutes = Math.floor(seconds / 60);
const remainingSeconds = seconds % 60;
return `${String(minutes).padStart(2, '0')}:${String(remainingSeconds).padStart(2, '0')}`;
}
/* This code will display the initial time as "02:00" and then count down in the "MM:SS" format until it reaches "00:00". When it reaches "00:00", you can add your logic to handle the end of the timer.
*/

// When timer starts, display the starting time

document.getElementById("timer").innerHTML = formatTime(time)
let timer = setInterval(function() {
time--; // decrease time by 1
if (time <= 0) {
clearInterval(timer)
alert("You lost! The correct person was " + secret.name + ".");
winOrLose.style.display = 'flex';
board.style.display = 'none';
winOrLoseText.innerHTML = `You lost! The correct person was ${secret.name}.`
}
document.getElementById("timer").innerHTML = formatTime(time);
// update the element where the timer will be displayed
}, 1000)//1000ms = 1s




// Array with all the characters, as objects
const CHARACTERS = [
const CHARACTERS = [
{
name: 'Jabala',
img: 'images/jabala.svg',
Expand Down Expand Up @@ -199,11 +245,13 @@ const CHARACTERS = [
other: []
},
]

// Global variables
let secret
let currentQuestion
let charactersInPlay
let secret // The current secret person
let keep // The current keep value for filtering
let currentQuestion // The current question in play
let charactersInPlay// charactersInPlay is an array of objects


// Draw the game board
const generateBoard = () => {
Expand All @@ -221,105 +269,174 @@ const generateBoard = () => {
`
})
}

// 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);
}

// This function to start (and restart) the game
/// 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
board.style.display = 'flex'
winOrLose.style.display = 'none'
// What else should happen when we start the game?
//Game board should be rendered on the screen
generateBoard();
setSecret();
//startTimer(); // can't get this to work together with findout dropdown filter

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It could be helpful to console log when the start function is being called, as it is easier to spot errors in restart / play again

}

// setting the 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 value = questions.options[questions.selectedIndex].value

currentQuestion = {
category: category,
// value: value
value: value
}
console.log(currentQuestion);
}

// This function should be invoked when you click on 'Find Out' button.
const checkQuestion = () => {
const { category, value } = currentQuestion
const { category, value } = currentQuestion // MISSING PIECE THAT FINALLY MADE THE FILTER WORK..currentQuestion is an object with two properties: category and value. Declaring these two variables and assigning them to the properties of currentQuestion object makes it possible to use them in the if statement below.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found this complicated, you explain it well here


// 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') {
if (secret[category].includes(value)) {
filterCharacters(true)
//(value === secret[category]) {
// filterCharacters(true)
console.log("keep")
} else
filterCharacters(false)
console.log("remove")

} else if (category === 'accessories' || category === 'other') {

if (secret[category].includes(value)) {
filterCharacters(true)
console.log("keep")
} else
filterCharacters(false)
console.log("remove")
}
}

// It'll filter the characters array and redraw the game board.
// Determine what is the category
// filter by category to keep or remove based on the keep variable.
const filterCharacters = (keep) => {
const { category, value } = currentQuestion
// Show the correct alert message for different categories
if (category === 'accessories') {
if (keep) {
alert(
`Yes, the person wears ${value}! Keep all people that wears ${value}`
`Yes, the person wears ${value}! Keep all people that wear ${value}.`
)
//filter out people from the charactersInPlay array using the filter method
charactersInPlay = charactersInPlay.filter((person) => person[category].includes(value))
} else {
alert(
`No, the person doesn't wear ${value}! Remove all people that wears ${value}`
`No, the person doesn't wear ${value}! Remove all people that wear ${value}.`
)
charactersInPlay = charactersInPlay.filter((person) => !person[category].includes(value))
}
} else if (category === 'other') {
// Similar to the one above
if (keep) {
alert(
`Yes, the person has a ${value}! Keep all people that have a ${value}.`
)
charactersInPlay = charactersInPlay.filter((person) => person[category].includes(value))
} else {
alert(
`No, the person doesn't have a ${value}! Remove all people that have a ${value}.`
)
charactersInPlay = charactersInPlay.filter((person) => !person[category].includes(value))
}
} else if (category === 'eyes') {
// Similar to the one above
if (keep) {
alert(
`Yes, the person has ${value} eyes! Keep all people that have ${value} eyes.`
)
charactersInPlay = charactersInPlay.filter((person) => person[category] === value)
} else {
alert(
`No, the person doesn't have ${value} eyes! Remove all people that have ${value} eyes.`
)
charactersInPlay = charactersInPlay.filter((person) => person[category] !== value)
}
} 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 has ${value} hair! Keep all people that have ${value} hair.`
)
charactersInPlay = charactersInPlay.filter((person) => person[category] === value)
Comment on lines +380 to +382

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From playing your game a few times, it looks like you did the filtering correctly. However, the values could be paid more attention to. None of the caracters match the value red hair, while there is no value for hidden hair or orange and purple hair. This makes it harder to filter away the wrong options.

} else {
// alert popup that says something like: "No, the person doesnt have yellow hair! Remove all people with yellow hair"
alert(
`No, the person doesn't have ${value} hair! Remove all people that have ${value} hair.`
)
charactersInPlay = charactersInPlay.filter((person) => person[category] !== value)
}
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stenli left this comment for me - I found it useful as I used the same approach as you, so passing it on:
the readability of your filterCharacters function can be improved by avoiding the repetition of alert messages. You can create a single alert message and use template literals to make the code cleaner.

like the following :

const filterCharacters = (keep) => {
const { category, value } = currentQuestion;
let message;

if (category === 'accessories') {
message = person wears ${value};
} else if (category === 'other') {
message = person wears ${value};
} else if (category === 'hair') {
message = person has ${value} hair;
} else if (category === 'eyes') {
message = person has ${value} eyes;
}

if (keep) {
alert(Yes, the ${message}! Keep all people who ${message});
} else {
alert(No, the ${message}! Remove all people who ${message});
}


// 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.
generateBoard()
}

// when clicking guess, the player first have to confirm that they want to make a 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() ?
const confirmation = confirm(`Are you sure you want to guess ${personToConfirm}?`)
// If the player wants to guess, invoke the checkMyGuess function.
if (confirmation === true) {
checkMyGuess(personToConfirm)
}
}

// 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
winOrLose.style.display = 'flex';
// 4. Hide the game board
board.style.display = 'none';

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you need both of these? I think display flex should overwrite the board

// 2. Set a Message to show in the win or lose section accordingly
if (secret.name === personToCheck) {
winOrLoseText.innerHTML = `You guessed right! Congratulations, you are a winner!`
} else {
winOrLoseText.innerHTML = `You lost! The correct person was ${secret.name}.`
}
}

// Invokes the start function when website is loaded
start()

// Show the correct alert message for different categories
alert(`You have ${charactersInPlay.length} characters left.`)

// All the event listeners
restartButton.addEventListener('click', start)

questions.addEventListener('change', selectQuestion)

findOutButton.addEventListener('click', checkQuestion)

playAgainButton.addEventListener('click', start)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see you mentioned some of this in your readme - the play again (after guessing) and the restart (while playting) buttons work well (you have the event listeners set up), however the game does not reset. The scroll down menu should go back to "please choose an option" and timer should start. The secret person does change however. Perhaps the end of my code can help you here - notice the start function being called once more:

//Restart game
restartButton.addEventListener('click', start)
//Play again from the win/ lose site
playAgainButton.addEventListener('click', () => {
winOrLose.style.display = "none"
start()





// 1. Check if the personToCheck is the same as the secret person's name
//secret.name is the name of the secret person
// 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