1. Overview
Challenge & starter files: Advent of JS
Full codes: nilstarbb/advent-of-js/12-rock-paper-scissors
Live Demo: 12 - Rock, Paper, Scissors || Advent of JavaScript
Preview:
2. Details
Users should be able to:
- play the game and see the results
3. Coding
Setup React template
Please be aware: this approach is fine for learning and creating simple demos but is not suitable for production.
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
...
<link rel="stylesheet" href="./styles.css" />
<script src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div class="wrapper menu" id="wrapper"></div>
<script type="text/babel" src="./script.js"></script>
</body>
</html>
script.js:
const { useState, useEffect } = React;
const App = () => {
return (
<>
...
</>
);
};
ReactDOM.render(<App />, document.getElementById("wrapper"));
Add styles for tie
This game has three results: win, loss, and tie. The starter files only have styles for win and loss, so we’d better create one for the tie.
Add a title for tie:
<div class="your-pick">
<h1 class="you-win">you win</h1>
<h1 class="tie">tie</h1>
<img src="./images/rock.png" alt="Rock" />
</div>
Then use CSS style to hide those titles on different results:
/* Winner Page */
body.computer-wins h1.you-win,
body.computer-wins h1.tie,
body.you-win h1.computer-wins,
body.you-win h1.tie,
body.tie h1.you-win,
body.tie h1.computer-wins {
visibility: hidden;
}
Play rock, paper, scissors
Rock, paper, scissors is pretty simple. Here we create three options for the user and computer to pick, then compare the picks with the winning pairs to get our result.
const picks = ["rock", "paper", "scissors"];
const pickWins = {
rock: "scissors",
paper: "rock",
scissors: "paper",
};
...
const handlePick = (userPick) => {
const computerPick = picks[Math.floor(Math.random() * picks.length)];
const result = {
userPick: userPick,
computerPick: computerPick,
result:
userPick == computerPick
? "tie"
: pickWins[userPick] == computerPick
? "you-win"
: "computer-wins",
};
setPlayResult(result);
};
Build UI
Since the CSS file use body
classes outside components to change the appearance, we need to use document.body.classList.add()/remove()
to modify those classes.
You can use React Router here to switch between play and result, but as this is just a small practice project, we won’t go that far.
script.js:
const { useState, useEffect } = React;
const picks = ["rock", "paper", "scissors"];
const pickWins = {
rock: "scissors",
paper: "rock",
scissors: "paper",
};
const Winner = ({ result, handlePlayAgain }) => {
useEffect(() => {
switch (result.result) {
case "computer-wins":
document.body.classList.add("winner", "computer-wins");
document.body.classList.remove("you-win", "tie");
break;
case "you-win":
document.body.classList.add("winner", "you-win");
document.body.classList.remove("computer-wins", "tie");
break;
case "tie":
document.body.classList.add("winner", "tie");
document.body.classList.remove("computer-wins", "you-win");
default:
break;
}
}, []);
return (
<>
<div className="your-pick">
<h1 className="you-win">you win</h1>
<h1 className="tie">tie</h1>
<img
src={"./images/" + result.userPick + ".png"}
alt={result.userPick}
/>
</div>
<div className="computer-pick">
<h1 className="computer-wins">computer wins</h1>
<img
src={"./images/" + result.computerPick + ".png"}
alt={result.computerPick}
/>
</div>
<button className="play-again" onClick={() => handlePlayAgain()}>
play again?
</button>
</>
);
};
const PickOption = ({ option, handleClick }) => {
return (
<li className="pick-one">
<button onClick={() => handleClick(option)}>
<img src={"./images/" + option + ".png"} alt={option} />
{option}
</button>
</li>
);
};
const Pick = ({ handleClick }) => {
useEffect(
() => document.body.classList.remove("winner", "you-win", "computer-wins"),
[]
);
return (
<>
<h1>pick one</h1>
<ul>
{picks.map((pick) => (
<PickOption key={pick} option={pick} handleClick={handleClick} />
))}
</ul>
</>
);
};
const App = () => {
const [isPlaying, setIsPlaying] = useState(true);
const [playResult, setPlayResult] = useState(null);
useEffect(() => {
if (playResult) {
setIsPlaying(false);
}
}, [playResult]);
const handlePick = (userPick) => {
const computerPick = picks[Math.floor(Math.random() * picks.length)];
const result = {
userPick: userPick,
computerPick: computerPick,
result:
userPick == computerPick
? "tie"
: pickWins[userPick] == computerPick
? "you-win"
: "computer-wins",
};
setPlayResult(result);
};
const handlePlayAgain = () => {
setIsPlaying(true);
setPlayResult(null);
};
return (
<>
{isPlaying ? (
<Pick handleClick={handlePick} />
) : (
<Winner result={playResult} handlePlayAgain={handlePlayAgain} />
)}
</>
);
};
ReactDOM.render(<App />, document.getElementById("wrapper"));