From ad75f1cdfcc72086aeaef0d54e1c6b89fd22f397 Mon Sep 17 00:00:00 2001 From: Ankit Yadav Date: Fri, 6 Oct 2023 01:48:28 +0530 Subject: [PATCH 1/3] Developed hangman-game The game is developed and tested. It is working fine on the website. User have to guess the word. If user guess incorrect then parts of a hangman figure appears and if the hangman figure is fully formed then user loses. User can use the keyboard appearing on the screen or he/she can his own keyboard. --- .../src/examples/hangman-game.tsx | 577 ++++++++++++++++++ 1 file changed, 577 insertions(+) create mode 100644 packages/kitchen-sink/src/examples/hangman-game.tsx diff --git a/packages/kitchen-sink/src/examples/hangman-game.tsx b/packages/kitchen-sink/src/examples/hangman-game.tsx new file mode 100644 index 0000000000..1178a3fd1e --- /dev/null +++ b/packages/kitchen-sink/src/examples/hangman-game.tsx @@ -0,0 +1,577 @@ +import { useEffect, useState } from "react"; +import styled from "styled-components"; + +const words = [ + {"A large, flightless bird":"ostrich"}, + {"A nocturnal flying mammal":"bat"}, + {"A cold-blooded reptile often kept as a pet":"snake"}, + {"A four-legged, domesticated animal that barks":"dog"}, + {"A small, colorful insect that flies and collects nectar":"butterfly"}, + {"A large, long-necked animal native to Africa":"giraffe"}, + {"A popular citrus fruit":"orange"}, + {"A large, ferocious feline":"tiger"}, + {"A frozen dessert often served in a cone":"ice cream"}, + {"A yellow, crescent-shaped fruit":"banana"}, + {"A small, burrowing rodent":"mole"}, + {"A mythical creature with the body of a lion and the head of an eagle":"griffin"}, + {"A cold and creamy dairy product often used as a topping":"whipped cream"}, + {"A small, green vegetable often used in salads":"cucumber"}, + {"A fast-running bird with long legs":"ostrich"}, + {"A round, red fruit often associated with Valentine's Day":"strawberry"}, + {"A domesticated animal known for producing milk":"cow"}, + {"A venomous arachnid with eight legs":"spider"}, + {"A large, slow-moving mammal known for its long trunk":"elephant"}, + {"A popular seafood delicacy often served with butter":"lobster"}, + {"A fast, graceful mammal known for its long neck":"gazelle"}, + {"A hot, caffeinated beverage often served in the morning":"coffee"}, + {"A tropical fruit with a tough, spiky outer shell":"pineapple"}, + {"A sweet, sticky substance made by bees":"honey"}, + {"A large, striped big cat known for its roar":"lion"}, + {"A green vegetable used in salads and sandwiches":"lettuce"}, + {"A small, furry animal that hops":"rabbit"}, + {"A delicious, circular baked good often topped with icing":"doughnut"}, + {"A sour, yellow fruit often used in pies":"lemon"}, + {"A fast-swimming marine mammal with sharp teeth":"dolphin"}, + {"A small, red fruit often used in jams and jellies":"strawberry"}, + {"A cold, frozen dessert often served in a cup or cone":"ice cream"}, + {"A large, powerful bird of prey":"eagle"}, + {"A popular Italian pasta dish with tomato sauce":"spaghetti"}, + {"A long, thin pasta often used in Asian cuisine":"noodle"}, + {"A tiny, red fruit often used in salsa":"tomato"}, + {"A large, leafy green vegetable often used in salads":"spinach"}, + {"A small, furry rodent often kept as a pet":"hamster"}, + {"A sweet, sticky substance used to sweeten food and drinks":"sugar"}, + {"A popular breakfast food made from ground grains":"cereal"}, + {"A small, green vegetable often used in stir-fry":"pea"}, + {"A tropical fruit with a tough outer shell and sweet flesh":"coconut"}, + {"A round, orange fruit often associated with Halloween":"pumpkin"}, + {"A cold, fizzy beverage often served in cans or bottles":"soda"}, + {"A warm, comforting beverage often made from tea leaves":"chai"}, + {"A small, round fruit often used in pies and cobblers":"cherry"}, + {"A sweet, brown substance often used as a topping for pancakes":"syrup"}, + {"A delicious, creamy dairy product often used in desserts":"cream"} +] + +const tryAgainIcon = ( + + + +); + +function getRandomWord(arr: {}[]) { + const obj = arr[Math.floor(Math.random() * arr.length)] + // const word = Object.values(obj)[0] as string; + return obj; +} + +export default function App() { + const [wordToGuess, setWordToGuess] = useState(""); + const [hintToGuess, setHintToGuess ] = useState(""); + // const [wordToGuess, setWordToGuess] = useState(getRandomWord(words)); + const [guessedLetters, setGuessedLetters] = useState([]); + + const incorrectLetters = guessedLetters.filter( + (letter) => !wordToGuess.includes(letter) + ); + + const isLoser = incorrectLetters.length >= 6; + const isWinner = wordToGuess + .split("") + .every((letter) => guessedLetters.includes(letter)); + + const addGuessedLetter = (letter: string) => { + if (!guessedLetters.includes(letter)) { + setGuessedLetters((currentLetters) => [...currentLetters, letter]); + } + }; + + const restartGame = () => { + const obj = getRandomWord(words) as Object; + const word = Object.values(obj)[0] as string; + const question = Object.keys(obj)[0]; + setHintToGuess(question); + setWordToGuess(word); + setGuessedLetters([]); + }; + useEffect(()=>{ + restartGame(); + // eslint-disable-next-line + },[]); + return ( + + + Hangman + + {!(isWinner || isLoser) ? ( + + ) : ( + + {isWinner && "You are winner!"} + {isLoser && "Nice try..."} + + )} + {!(isWinner || isLoser) &&

+ Question: {hintToGuess} +

} + + + {(isWinner || isLoser) && ( + {tryAgainIcon} + )} + + + wordToGuess.includes(letter) + )} + incorrectLetters={incorrectLetters} + addGuessedLetter={addGuessedLetter} + /> +
+ +
+ ); +} + +const BigContainer = styled.div` + width: 100%; + height: 100%; + background: linear-gradient(to bottom right, #b3e0f2, #16a085); +` + +const Container = styled.div` + margin: 0 0.5rem; + display: flex; + flex-direction: column; + align-items: center; + justify-content: space-between; + gap: 2rem; + min-height: 100vh; + max-width: 800px; + + font-family: Inter, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, + Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; + position: relative; + padding-bottom:10px; + + @media (min-width: 768px) { + margin: 0 auto; + } +`; + +const Title = styled.h1` + padding: 15px; +`; + +const EndGame = styled.div` + display: flex; + flex-direction: column; + align-items: center; + gap: 1rem; + text-align: center; + font-size: 1.3rem; + font-weight: bold; + color: black; + @media (min-width: 768px) { + font-size: 2rem; + } +`; + +const TryAgainButton = styled.button` + display: flex; + align-items: center; + justify-content: center; + padding: 0.3rem; + border: 3px solid black; + border-radius: 1rem; + font-size: 2.5rem; + background: none; + color: black; + cursor: pointer; + + &:hover, + &:focus { + background: #16a085; + } + + @media (min-width: 768px) { + font-size: 2.3rem; + } +`; + + + + + +/**************************************************************** + *************************************** + Drawing Component + *************************************** +*****************************************************************/ + +const Head = styled.div` + width: 50px; + height: 50px; + border-radius: 100%; + border: 10px solid black; + position: absolute; + top: 40px; + right: -29px; + + @media (min-width: 768px) { + top: 50px; + } +` + +const Body = styled.div` + width: 10px; + height: 80px; + background: black; + position: absolute; + top: 90px; + right: 0; + + @media (min-width: 768px) { + height: 100px; + top: 119px; + } +` + +const RightArm = styled.div` + width: 70px; + height: 10px; + background: black; + position: absolute; + top: 110px; + right: -70px; + rotate: -30deg; + transform-origin: left bottom; + + @media (min-width: 768px) { + width: 100px; + top: 130px; + right: -100px; + } +` + +const LeftArm = styled.div` + width: 70px; + height: 10px; + background: black; + position: absolute; + top: 110px; + right: 10px; + rotate: 30deg; + transform-origin: right bottom; + + @media (min-width: 768px) { + width: 100px; + top: 130px; + } +` + +const RightLeg = styled.div` + width: 80px; + height: 10px; + background: black; + position: absolute; + top: 160px; + right: -70px; + rotate: 60deg; + transform-origin: left bottom; + + @media (min-width: 768px) { + width: 100px; + top: 209px; + right: -90px; + } +` + +const LeftLeg = styled.div` + width: 80px; + height: 10px; + background: black; + position: absolute; + top: 160px; + right: 0; + rotate: -60deg; + transform-origin: right bottom; + + @media (min-width: 768px) { + width: 100px; + top: 209px; + } +` + +const Element1 = styled.div` + height: 10px; + width: 120px; + background: black; + + @media (min-width: 768px) { + height: 10px; + width: 200px; + } +` + +const Element2 = styled.div` + margin-left: 60px; + height: 300px; + width: 10px; + background: black; + + @media (min-width: 768px) { + margin-left: 100px; + height: 320px; + width: 10px; + } +` + +const Element3 = styled.div` + margin-left: 60px; + height: 10px; + width: 150px; + background: black; + + @media (min-width: 768px) { + margin-left: 100px; + height: 10px; + width: 200px; + } +` + +const Element4 = styled.div` + position: absolute; + top: 0; + right: 0; + height: 40px; + width: 10px; + background: black; + + @media (min-width: 768px) { + height: 50px; + } +` + +const BodyParts = [Head, Body, RightArm, LeftArm, RightLeg, LeftLeg] +const Gallows = [Element4, Element3, Element2, Element1] + +interface Drawing { + numberOfGuesses: number +} + + function Drawing({ numberOfGuesses }:Drawing) { + return ( +
+ {BodyParts.slice(0, numberOfGuesses).map((Component, id) => )} + {Gallows.map((Component, id) => )} +
+ ) +} + +/**************************************************************** + *************************************** + Keyboard Component + *************************************** +*****************************************************************/ + + +const KEYS = [ + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z" +]; + +interface Keyboard { + correctLetters: string[]; + incorrectLetters: string[]; + addGuessedLetter: (letter: string) => void; + disabled?: boolean; +} + function Keyboard({ correctLetters, incorrectLetters, addGuessedLetter, disabled = false }: Keyboard) { + + useEffect(() => { + const handleKeyPress = (event: KeyboardEvent) => { + const key = event.key.toLowerCase(); + if (!disabled && KEYS.includes(key) && !correctLetters.includes(key) && !incorrectLetters.includes(key)) { + addGuessedLetter(key); + } + }; + + window.addEventListener('keydown', handleKeyPress); + + return () => { + window.removeEventListener('keydown', handleKeyPress); + }; + + // eslint-disable-next-line + }, [correctLetters, incorrectLetters, addGuessedLetter, disabled]); + + return ( + + + + {KEYS.map((key) => { + const active = correctLetters.includes(key); + const inActive = incorrectLetters.includes(key); + + return ( + addGuessedLetter(key)}> + {key} + + ); + })} + + + + ); +} + + +const KeyContainer = styled.div` + align-self: stretch; +`; + +const KeyboardGrid = styled.div` + display: grid; + grid-template-columns: repeat(auto-fit, minmax(50px, 1fr)); + gap: .5rem; + width: 100%; +` + +interface Key { + active: boolean, + inActive: boolean +} + + +const Key = styled.button` + aspect-ratio: 1/1; + width: 100%; + border: 3px solid black; + border-radius: 1rem; + font-size: 2rem; + font-family: unset; + font-family: monospace; + text-transform: uppercase; + font-weight: bold; + background: ${({ active }) => (active ? "#16A085" : "none")}; + color: ${({ active }) => (active ? "white" : "black")}; + opacity: ${({ inActive }) => (inActive ? ".3" : "1")}; + cursor: pointer; + + &:hover:not(:disabled), + &:focus:not(:disabled) { + background-color: #F4D03F; + } + + &:disabled { + cursor: not-allowed; + } + + @media (min-width: 768px) { + font-size: 2.3rem; + } +` +/**************************************************************** + *************************************** + Word Component + *************************************** +*****************************************************************/ + + +interface WordProps { + guessedLetters: string[]; + wordToGuess: string; + reveal?: boolean; +}; + + function Word({ guessedLetters, wordToGuess, reveal = false }: WordProps) { + return ( + + {wordToGuess.split("").map((letter, id) => ( + + + {letter} + + + ))} + + ); +} + +const WordContainer = styled.div` + padding: .5rem; + display: flex; + gap: 1rem; + max-width: 100vw; + font-size: 3rem; + font-weight: bold; + text-transform: uppercase; + font-family: monospace; + overflow-x: auto !important; + + @media (min-width: 768px) { + font-size: 5rem; + } +` + +const Border = styled.span` + border-bottom: .5rem solid black; +` + +interface Letter { + guessedLetters: string[], + letter: string, + reveal?: boolean +} + +const Letter = styled.span` + visibility: ${({ guessedLetters, letter, reveal }) => (guessedLetters.includes(letter) || reveal ? "visible" : "hidden")}; + color: ${({ guessedLetters, letter, reveal }) => (!guessedLetters.includes(letter) && reveal ? "#d30000" : "black")}; +` \ No newline at end of file From 849e10bc7a967795830d81db61b8ffd475b30ceb Mon Sep 17 00:00:00 2001 From: Ankit Yadav Date: Fri, 6 Oct 2023 02:23:03 +0530 Subject: [PATCH 2/3] Developed hangman-game and converted components to block components --- .../kitchen-sink/src/examples/hangman-game.tsx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/kitchen-sink/src/examples/hangman-game.tsx b/packages/kitchen-sink/src/examples/hangman-game.tsx index 1178a3fd1e..48933092a0 100644 --- a/packages/kitchen-sink/src/examples/hangman-game.tsx +++ b/packages/kitchen-sink/src/examples/hangman-game.tsx @@ -1,4 +1,5 @@ import { useEffect, useState } from "react"; +import { block } from 'million/react'; import styled from "styled-components"; const words = [ @@ -74,6 +75,7 @@ function getRandomWord(arr: {}[]) { return obj; } + export default function App() { const [wordToGuess, setWordToGuess] = useState(""); const [hintToGuess, setHintToGuess ] = useState(""); @@ -377,14 +379,14 @@ interface Drawing { numberOfGuesses: number } - function Drawing({ numberOfGuesses }:Drawing) { + const Drawing = block( ({ numberOfGuesses }:Drawing) => { return (
{BodyParts.slice(0, numberOfGuesses).map((Component, id) => )} {Gallows.map((Component, id) => )}
) -} +}) /**************************************************************** *************************************** @@ -428,7 +430,7 @@ interface Keyboard { addGuessedLetter: (letter: string) => void; disabled?: boolean; } - function Keyboard({ correctLetters, incorrectLetters, addGuessedLetter, disabled = false }: Keyboard) { + const Keyboard =block(({ correctLetters, incorrectLetters, addGuessedLetter, disabled = false }: Keyboard) => { useEffect(() => { const handleKeyPress = (event: KeyboardEvent) => { @@ -470,7 +472,7 @@ interface Keyboard { ); -} +}) const KeyContainer = styled.div` @@ -531,7 +533,7 @@ interface WordProps { reveal?: boolean; }; - function Word({ guessedLetters, wordToGuess, reveal = false }: WordProps) { + const Word = block(({ guessedLetters, wordToGuess, reveal = false }: WordProps) => { return ( {wordToGuess.split("").map((letter, id) => ( @@ -543,7 +545,7 @@ interface WordProps { ))} ); -} +}) const WordContainer = styled.div` padding: .5rem; From a9937df4977f77472d26d4338c540db0e6cca751 Mon Sep 17 00:00:00 2001 From: Ankit Yadav Date: Fri, 6 Oct 2023 02:38:59 +0530 Subject: [PATCH 3/3] Developed hangman-game and converted components to block components --- .../src/examples/hangman-game.tsx | 1052 +++++++++-------- 1 file changed, 543 insertions(+), 509 deletions(-) diff --git a/packages/kitchen-sink/src/examples/hangman-game.tsx b/packages/kitchen-sink/src/examples/hangman-game.tsx index 48933092a0..0b0990b53b 100644 --- a/packages/kitchen-sink/src/examples/hangman-game.tsx +++ b/packages/kitchen-sink/src/examples/hangman-game.tsx @@ -1,57 +1,62 @@ -import { useEffect, useState } from "react"; +import { useEffect, useState } from 'react'; import { block } from 'million/react'; -import styled from "styled-components"; +import styled from 'styled-components'; const words = [ - {"A large, flightless bird":"ostrich"}, - {"A nocturnal flying mammal":"bat"}, - {"A cold-blooded reptile often kept as a pet":"snake"}, - {"A four-legged, domesticated animal that barks":"dog"}, - {"A small, colorful insect that flies and collects nectar":"butterfly"}, - {"A large, long-necked animal native to Africa":"giraffe"}, - {"A popular citrus fruit":"orange"}, - {"A large, ferocious feline":"tiger"}, - {"A frozen dessert often served in a cone":"ice cream"}, - {"A yellow, crescent-shaped fruit":"banana"}, - {"A small, burrowing rodent":"mole"}, - {"A mythical creature with the body of a lion and the head of an eagle":"griffin"}, - {"A cold and creamy dairy product often used as a topping":"whipped cream"}, - {"A small, green vegetable often used in salads":"cucumber"}, - {"A fast-running bird with long legs":"ostrich"}, - {"A round, red fruit often associated with Valentine's Day":"strawberry"}, - {"A domesticated animal known for producing milk":"cow"}, - {"A venomous arachnid with eight legs":"spider"}, - {"A large, slow-moving mammal known for its long trunk":"elephant"}, - {"A popular seafood delicacy often served with butter":"lobster"}, - {"A fast, graceful mammal known for its long neck":"gazelle"}, - {"A hot, caffeinated beverage often served in the morning":"coffee"}, - {"A tropical fruit with a tough, spiky outer shell":"pineapple"}, - {"A sweet, sticky substance made by bees":"honey"}, - {"A large, striped big cat known for its roar":"lion"}, - {"A green vegetable used in salads and sandwiches":"lettuce"}, - {"A small, furry animal that hops":"rabbit"}, - {"A delicious, circular baked good often topped with icing":"doughnut"}, - {"A sour, yellow fruit often used in pies":"lemon"}, - {"A fast-swimming marine mammal with sharp teeth":"dolphin"}, - {"A small, red fruit often used in jams and jellies":"strawberry"}, - {"A cold, frozen dessert often served in a cup or cone":"ice cream"}, - {"A large, powerful bird of prey":"eagle"}, - {"A popular Italian pasta dish with tomato sauce":"spaghetti"}, - {"A long, thin pasta often used in Asian cuisine":"noodle"}, - {"A tiny, red fruit often used in salsa":"tomato"}, - {"A large, leafy green vegetable often used in salads":"spinach"}, - {"A small, furry rodent often kept as a pet":"hamster"}, - {"A sweet, sticky substance used to sweeten food and drinks":"sugar"}, - {"A popular breakfast food made from ground grains":"cereal"}, - {"A small, green vegetable often used in stir-fry":"pea"}, - {"A tropical fruit with a tough outer shell and sweet flesh":"coconut"}, - {"A round, orange fruit often associated with Halloween":"pumpkin"}, - {"A cold, fizzy beverage often served in cans or bottles":"soda"}, - {"A warm, comforting beverage often made from tea leaves":"chai"}, - {"A small, round fruit often used in pies and cobblers":"cherry"}, - {"A sweet, brown substance often used as a topping for pancakes":"syrup"}, - {"A delicious, creamy dairy product often used in desserts":"cream"} -] + { 'A large, flightless bird': 'ostrich' }, + { 'A nocturnal flying mammal': 'bat' }, + { 'A cold-blooded reptile often kept as a pet': 'snake' }, + { 'A four-legged, domesticated animal that barks': 'dog' }, + { 'A small, colorful insect that flies and collects nectar': 'butterfly' }, + { 'A large, long-necked animal native to Africa': 'giraffe' }, + { 'A popular citrus fruit': 'orange' }, + { 'A large, ferocious feline': 'tiger' }, + { 'A frozen dessert often served in a cone': 'ice cream' }, + { 'A yellow, crescent-shaped fruit': 'banana' }, + { 'A small, burrowing rodent': 'mole' }, + { + 'A mythical creature with the body of a lion and the head of an eagle': + 'griffin', + }, + { + 'A cold and creamy dairy product often used as a topping': 'whipped cream', + }, + { 'A small, green vegetable often used in salads': 'cucumber' }, + { 'A fast-running bird with long legs': 'ostrich' }, + { "A round, red fruit often associated with Valentine's Day": 'strawberry' }, + { 'A domesticated animal known for producing milk': 'cow' }, + { 'A venomous arachnid with eight legs': 'spider' }, + { 'A large, slow-moving mammal known for its long trunk': 'elephant' }, + { 'A popular seafood delicacy often served with butter': 'lobster' }, + { 'A fast, graceful mammal known for its long neck': 'gazelle' }, + { 'A hot, caffeinated beverage often served in the morning': 'coffee' }, + { 'A tropical fruit with a tough, spiky outer shell': 'pineapple' }, + { 'A sweet, sticky substance made by bees': 'honey' }, + { 'A large, striped big cat known for its roar': 'lion' }, + { 'A green vegetable used in salads and sandwiches': 'lettuce' }, + { 'A small, furry animal that hops': 'rabbit' }, + { 'A delicious, circular baked good often topped with icing': 'doughnut' }, + { 'A sour, yellow fruit often used in pies': 'lemon' }, + { 'A fast-swimming marine mammal with sharp teeth': 'dolphin' }, + { 'A small, red fruit often used in jams and jellies': 'strawberry' }, + { 'A cold, frozen dessert often served in a cup or cone': 'ice cream' }, + { 'A large, powerful bird of prey': 'eagle' }, + { 'A popular Italian pasta dish with tomato sauce': 'spaghetti' }, + { 'A long, thin pasta often used in Asian cuisine': 'noodle' }, + { 'A tiny, red fruit often used in salsa': 'tomato' }, + { 'A large, leafy green vegetable often used in salads': 'spinach' }, + { 'A small, furry rodent often kept as a pet': 'hamster' }, + { 'A sweet, sticky substance used to sweeten food and drinks': 'sugar' }, + { 'A popular breakfast food made from ground grains': 'cereal' }, + { 'A small, green vegetable often used in stir-fry': 'pea' }, + { 'A tropical fruit with a tough outer shell and sweet flesh': 'coconut' }, + { 'A round, orange fruit often associated with Halloween': 'pumpkin' }, + { 'A cold, fizzy beverage often served in cans or bottles': 'soda' }, + { 'A warm, comforting beverage often made from tea leaves': 'chai' }, + { 'A small, round fruit often used in pies and cobblers': 'cherry' }, + { 'A sweet, brown substance often used as a topping for pancakes': 'syrup' }, + { 'A delicious, creamy dairy product often used in desserts': 'cream' }, +]; const tryAgainIcon = ( + viewBox="0 0 16 16" + > ); -function getRandomWord(arr: {}[]) { - const obj = arr[Math.floor(Math.random() * arr.length)] - // const word = Object.values(obj)[0] as string; - return obj; -} +/**************************************************************** + *************************************** + Drawing Component + *************************************** +*****************************************************************/ +interface Drawing { + numberOfGuesses: number; +} -export default function App() { - const [wordToGuess, setWordToGuess] = useState(""); - const [hintToGuess, setHintToGuess ] = useState(""); - // const [wordToGuess, setWordToGuess] = useState(getRandomWord(words)); - const [guessedLetters, setGuessedLetters] = useState([]); +const Drawing = block(({ numberOfGuesses }: Drawing) => { + const Head = styled.div` + width: 50px; + height: 50px; + border-radius: 100%; + border: 10px solid black; + position: absolute; + top: 40px; + right: -29px; + + @media (min-width: 768px) { + top: 50px; + } + `; - const incorrectLetters = guessedLetters.filter( - (letter) => !wordToGuess.includes(letter) - ); + const Body = styled.div` + width: 10px; + height: 80px; + background: black; + position: absolute; + top: 90px; + right: 0; + + @media (min-width: 768px) { + height: 100px; + top: 119px; + } + `; - const isLoser = incorrectLetters.length >= 6; - const isWinner = wordToGuess - .split("") - .every((letter) => guessedLetters.includes(letter)); + const RightArm = styled.div` + width: 70px; + height: 10px; + background: black; + position: absolute; + top: 110px; + right: -70px; + rotate: -30deg; + transform-origin: left bottom; + + @media (min-width: 768px) { + width: 100px; + top: 130px; + right: -100px; + } + `; - const addGuessedLetter = (letter: string) => { - if (!guessedLetters.includes(letter)) { - setGuessedLetters((currentLetters) => [...currentLetters, letter]); + const LeftArm = styled.div` + width: 70px; + height: 10px; + background: black; + position: absolute; + top: 110px; + right: 10px; + rotate: 30deg; + transform-origin: right bottom; + + @media (min-width: 768px) { + width: 100px; + top: 130px; } - }; - - const restartGame = () => { - const obj = getRandomWord(words) as Object; - const word = Object.values(obj)[0] as string; - const question = Object.keys(obj)[0]; - setHintToGuess(question); - setWordToGuess(word); - setGuessedLetters([]); - }; - useEffect(()=>{ - restartGame(); - // eslint-disable-next-line - },[]); - return ( - - - Hangman - - {!(isWinner || isLoser) ? ( - - ) : ( - - {isWinner && "You are winner!"} - {isLoser && "Nice try..."} - - )} - {!(isWinner || isLoser) &&

- Question: {hintToGuess} -

} - - - {(isWinner || isLoser) && ( - {tryAgainIcon} - )} - - - wordToGuess.includes(letter) - )} - incorrectLetters={incorrectLetters} - addGuessedLetter={addGuessedLetter} - /> - - - - ); -} + `; -const BigContainer = styled.div` - width: 100%; - height: 100%; - background: linear-gradient(to bottom right, #b3e0f2, #16a085); -` - -const Container = styled.div` - margin: 0 0.5rem; - display: flex; - flex-direction: column; - align-items: center; - justify-content: space-between; - gap: 2rem; - min-height: 100vh; - max-width: 800px; - - font-family: Inter, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, - Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; - position: relative; - padding-bottom:10px; - - @media (min-width: 768px) { - margin: 0 auto; - } -`; - -const Title = styled.h1` - padding: 15px; -`; - -const EndGame = styled.div` - display: flex; - flex-direction: column; - align-items: center; - gap: 1rem; - text-align: center; - font-size: 1.3rem; - font-weight: bold; - color: black; - @media (min-width: 768px) { - font-size: 2rem; - } -`; - -const TryAgainButton = styled.button` - display: flex; - align-items: center; - justify-content: center; - padding: 0.3rem; - border: 3px solid black; - border-radius: 1rem; - font-size: 2.5rem; - background: none; - color: black; - cursor: pointer; - - &:hover, - &:focus { - background: #16a085; - } - - @media (min-width: 768px) { - font-size: 2.3rem; - } -`; + const RightLeg = styled.div` + width: 80px; + height: 10px; + background: black; + position: absolute; + top: 160px; + right: -70px; + rotate: 60deg; + transform-origin: left bottom; + + @media (min-width: 768px) { + width: 100px; + top: 209px; + right: -90px; + } + `; + const LeftLeg = styled.div` + width: 80px; + height: 10px; + background: black; + position: absolute; + top: 160px; + right: 0; + rotate: -60deg; + transform-origin: right bottom; + + @media (min-width: 768px) { + width: 100px; + top: 209px; + } + `; + const Element1 = styled.div` + height: 10px; + width: 120px; + background: black; + @media (min-width: 768px) { + height: 10px; + width: 200px; + } + `; + const Element2 = styled.div` + margin-left: 60px; + height: 300px; + width: 10px; + background: black; -/**************************************************************** - *************************************** - Drawing Component - *************************************** -*****************************************************************/ + @media (min-width: 768px) { + margin-left: 100px; + height: 320px; + width: 10px; + } + `; -const Head = styled.div` - width: 50px; - height: 50px; - border-radius: 100%; - border: 10px solid black; - position: absolute; - top: 40px; - right: -29px; - - @media (min-width: 768px) { - top: 50px; - } -` - -const Body = styled.div` - width: 10px; - height: 80px; - background: black; - position: absolute; - top: 90px; - right: 0; - - @media (min-width: 768px) { - height: 100px; - top: 119px; - } -` - -const RightArm = styled.div` - width: 70px; - height: 10px; - background: black; - position: absolute; - top: 110px; - right: -70px; - rotate: -30deg; - transform-origin: left bottom; - - @media (min-width: 768px) { - width: 100px; - top: 130px; - right: -100px; - } -` - -const LeftArm = styled.div` - width: 70px; - height: 10px; - background: black; - position: absolute; - top: 110px; - right: 10px; - rotate: 30deg; - transform-origin: right bottom; - - @media (min-width: 768px) { - width: 100px; - top: 130px; - } -` - -const RightLeg = styled.div` - width: 80px; - height: 10px; - background: black; - position: absolute; - top: 160px; - right: -70px; - rotate: 60deg; - transform-origin: left bottom; - - @media (min-width: 768px) { - width: 100px; - top: 209px; - right: -90px; - } -` - -const LeftLeg = styled.div` - width: 80px; - height: 10px; - background: black; - position: absolute; - top: 160px; - right: 0; - rotate: -60deg; - transform-origin: right bottom; - - @media (min-width: 768px) { - width: 100px; - top: 209px; - } -` - -const Element1 = styled.div` - height: 10px; - width: 120px; - background: black; - - @media (min-width: 768px) { + const Element3 = styled.div` + margin-left: 60px; height: 10px; - width: 200px; - } -` - -const Element2 = styled.div` - margin-left: 60px; - height: 300px; - width: 10px; - background: black; - - @media (min-width: 768px) { - margin-left: 100px; - height: 320px; - width: 10px; - } -` + width: 150px; + background: black; -const Element3 = styled.div` - margin-left: 60px; - height: 10px; - width: 150px; - background: black; + @media (min-width: 768px) { + margin-left: 100px; + height: 10px; + width: 200px; + } + `; - @media (min-width: 768px) { - margin-left: 100px; - height: 10px; - width: 200px; - } -` - -const Element4 = styled.div` - position: absolute; - top: 0; - right: 0; - height: 40px; - width: 10px; - background: black; - - @media (min-width: 768px) { - height: 50px; - } -` + const Element4 = styled.div` + position: absolute; + top: 0; + right: 0; + height: 40px; + width: 10px; + background: black; -const BodyParts = [Head, Body, RightArm, LeftArm, RightLeg, LeftLeg] -const Gallows = [Element4, Element3, Element2, Element1] + @media (min-width: 768px) { + height: 50px; + } + `; -interface Drawing { - numberOfGuesses: number -} + const BodyParts = [Head, Body, RightArm, LeftArm, RightLeg, LeftLeg]; + const Gallows = [Element4, Element3, Element2, Element1]; - const Drawing = block( ({ numberOfGuesses }:Drawing) => { return ( -
- {BodyParts.slice(0, numberOfGuesses).map((Component, id) => )} - {Gallows.map((Component, id) => )} +
+ {BodyParts.slice(0, numberOfGuesses).map((Component, id) => ( + + ))} + {Gallows.map((Component, id) => ( + + ))}
- ) -}) + ); +}); /**************************************************************** *************************************** @@ -394,186 +251,363 @@ interface Drawing { *************************************** *****************************************************************/ - -const KEYS = [ - "a", - "b", - "c", - "d", - "e", - "f", - "g", - "h", - "i", - "j", - "k", - "l", - "m", - "n", - "o", - "p", - "q", - "r", - "s", - "t", - "u", - "v", - "w", - "x", - "y", - "z" -]; - interface Keyboard { correctLetters: string[]; incorrectLetters: string[]; addGuessedLetter: (letter: string) => void; disabled?: boolean; } - const Keyboard =block(({ correctLetters, incorrectLetters, addGuessedLetter, disabled = false }: Keyboard) => { - - useEffect(() => { - const handleKeyPress = (event: KeyboardEvent) => { - const key = event.key.toLowerCase(); - if (!disabled && KEYS.includes(key) && !correctLetters.includes(key) && !incorrectLetters.includes(key)) { - addGuessedLetter(key); - } - }; - - window.addEventListener('keydown', handleKeyPress); - - return () => { - window.removeEventListener('keydown', handleKeyPress); - }; - - // eslint-disable-next-line - }, [correctLetters, incorrectLetters, addGuessedLetter, disabled]); - - return ( - - - - {KEYS.map((key) => { - const active = correctLetters.includes(key); - const inActive = incorrectLetters.includes(key); - - return ( - addGuessedLetter(key)}> - {key} - - ); - })} - - - - ); -}) +const Keyboard = block( + ({ + correctLetters, + incorrectLetters, + addGuessedLetter, + disabled = false, + }: Keyboard) => { + const KeyContainer = styled.div` + align-self: stretch; + `; + + const KeyboardGrid = styled.div` + display: grid; + grid-template-columns: repeat(auto-fit, minmax(50px, 1fr)); + gap: 0.5rem; + width: 100%; + `; + + interface Key { + active: boolean; + inActive: boolean; + } -const KeyContainer = styled.div` - align-self: stretch; -`; - -const KeyboardGrid = styled.div` - display: grid; - grid-template-columns: repeat(auto-fit, minmax(50px, 1fr)); - gap: .5rem; - width: 100%; -` + const Key = styled.button` + aspect-ratio: 1/1; + width: 100%; + border: 3px solid black; + border-radius: 1rem; + font-size: 2rem; + font-family: unset; + font-family: monospace; + text-transform: uppercase; + font-weight: bold; + background: ${({ active }) => (active ? '#16A085' : 'none')}; + color: ${({ active }) => (active ? 'white' : 'black')}; + opacity: ${({ inActive }) => (inActive ? '.3' : '1')}; + cursor: pointer; + + &:hover:not(:disabled), + &:focus:not(:disabled) { + background-color: #f4d03f; + } -interface Key { - active: boolean, - inActive: boolean -} + &:disabled { + cursor: not-allowed; + } + @media (min-width: 768px) { + font-size: 2.3rem; + } + `; + const KEYS = [ + 'a', + 'b', + 'c', + 'd', + 'e', + 'f', + 'g', + 'h', + 'i', + 'j', + 'k', + 'l', + 'm', + 'n', + 'o', + 'p', + 'q', + 'r', + 's', + 't', + 'u', + 'v', + 'w', + 'x', + 'y', + 'z', + ]; + + useEffect(() => { + const handleKeyPress = (event: KeyboardEvent) => { + const key = event.key.toLowerCase(); + if ( + !disabled && + KEYS.includes(key) && + !correctLetters.includes(key) && + !incorrectLetters.includes(key) + ) { + addGuessedLetter(key); + } + }; + + window.addEventListener('keydown', handleKeyPress); + + return () => { + window.removeEventListener('keydown', handleKeyPress); + }; + + // eslint-disable-next-line + }, [correctLetters, incorrectLetters, addGuessedLetter, disabled]); + + return ( + + + {KEYS.map((key) => { + const active = correctLetters.includes(key); + const inActive = incorrectLetters.includes(key); + + return ( + addGuessedLetter(key)} + > + {key} + + ); + })} + + + ); + }, +); -const Key = styled.button` - aspect-ratio: 1/1; - width: 100%; - border: 3px solid black; - border-radius: 1rem; - font-size: 2rem; - font-family: unset; - font-family: monospace; - text-transform: uppercase; - font-weight: bold; - background: ${({ active }) => (active ? "#16A085" : "none")}; - color: ${({ active }) => (active ? "white" : "black")}; - opacity: ${({ inActive }) => (inActive ? ".3" : "1")}; - cursor: pointer; - - &:hover:not(:disabled), - &:focus:not(:disabled) { - background-color: #F4D03F; - } - - &:disabled { - cursor: not-allowed; - } - - @media (min-width: 768px) { - font-size: 2.3rem; - } -` /**************************************************************** *************************************** Word Component *************************************** *****************************************************************/ - interface WordProps { guessedLetters: string[]; wordToGuess: string; reveal?: boolean; -}; - - const Word = block(({ guessedLetters, wordToGuess, reveal = false }: WordProps) => { - return ( - - {wordToGuess.split("").map((letter, id) => ( - - - {letter} - - - ))} - - ); -}) - -const WordContainer = styled.div` - padding: .5rem; - display: flex; - gap: 1rem; - max-width: 100vw; - font-size: 3rem; - font-weight: bold; - text-transform: uppercase; - font-family: monospace; - overflow-x: auto !important; - - @media (min-width: 768px) { - font-size: 5rem; - } -` - -const Border = styled.span` - border-bottom: .5rem solid black; -` - -interface Letter { - guessedLetters: string[], - letter: string, - reveal?: boolean } -const Letter = styled.span` - visibility: ${({ guessedLetters, letter, reveal }) => (guessedLetters.includes(letter) || reveal ? "visible" : "hidden")}; - color: ${({ guessedLetters, letter, reveal }) => (!guessedLetters.includes(letter) && reveal ? "#d30000" : "black")}; -` \ No newline at end of file +const Word = block( + ({ guessedLetters, wordToGuess, reveal = false }: WordProps) => { + const WordContainer = styled.div` + padding: 0.5rem; + display: flex; + gap: 1rem; + max-width: 100vw; + font-size: 3rem; + font-weight: bold; + text-transform: uppercase; + font-family: monospace; + overflow-x: auto !important; + + @media (min-width: 768px) { + font-size: 5rem; + } + `; + + const Border = styled.span` + border-bottom: 0.5rem solid black; + `; + + interface Letter { + guessedLetters: string[]; + letter: string; + reveal?: boolean; + } + + const Letter = styled.span` + visibility: ${({ guessedLetters, letter, reveal }) => + guessedLetters.includes(letter) || reveal ? 'visible' : 'hidden'}; + color: ${({ guessedLetters, letter, reveal }) => + !guessedLetters.includes(letter) && reveal ? '#d30000' : 'black'}; + `; + return ( + + {wordToGuess.split('').map((letter, id) => ( + + + {letter} + + + ))} + + ); + }, +); + + +/**************************************************************** + *************************************** + Main Component + *************************************** +*****************************************************************/ + +export default function App() { + const getRandomWord = (arr: {}[]) => { + const obj = arr[Math.floor(Math.random() * arr.length)]; + return obj; + }; + + const BigContainer = styled.div` + width: 100%; + height: 100%; + background: linear-gradient(to bottom right, #f0f6fc, #e1f1fe); + + `; + + const Container = styled.div` + margin: 0 0.5rem; + display: flex; + flex-direction: column; + align-items: center; + justify-content: space-between; + gap: 2rem; + min-height: 100vh; + max-width: 800px; + + font-family: Inter, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, + Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, + sans-serif; + position: relative; + padding-bottom: 10px; + + @media (min-width: 768px) { + margin: 0 auto; + } + `; + + const Title = styled.h1` + padding: 15px; + `; + + const EndGame = styled.div` + display: flex; + flex-direction: column; + align-items: center; + gap: 1rem; + text-align: center; + font-size: 1.3rem; + font-weight: bold; + color: black; + @media (min-width: 768px) { + font-size: 2rem; + } + `; + + const TryAgainButton = styled.button` + display: flex; + align-items: center; + justify-content: center; + padding: 0.3rem; + border: 3px solid black; + border-radius: 1rem; + font-size: 2.5rem; + background: none; + color: black; + cursor: pointer; + + &:hover, + &:focus { + background: #16a085; + } + + @media (min-width: 768px) { + font-size: 2.3rem; + } + `; + const WinnerSpan = styled.span` + color: green; + ` + const LoserSpan = styled.span` + color: red; + ` + const [wordToGuess, setWordToGuess] = useState(''); + const [hintToGuess, setHintToGuess] = useState(''); + const [guessedLetters, setGuessedLetters] = useState([]); + + const incorrectLetters = guessedLetters.filter( + (letter) => !wordToGuess.includes(letter), + ); + + const isLoser = incorrectLetters.length >= 6; + const isWinner = wordToGuess + .split('') + .every((letter) => guessedLetters.includes(letter)); + + const addGuessedLetter = (letter: string) => { + if (!guessedLetters.includes(letter)) { + setGuessedLetters((currentLetters) => [...currentLetters, letter]); + } + }; + + const restartGame = () => { + const obj = getRandomWord(words) as Object; + const word = Object.values(obj)[0] as string; + const question = Object.keys(obj)[0]; + setHintToGuess(question); + setWordToGuess(word); + setGuessedLetters([]); + }; + useEffect(() => { + restartGame(); + // eslint-disable-next-line + }, []); + return ( + + + Hangman + + {!(isWinner || isLoser) ? ( + + ) : ( + + {isWinner && 'You are winner!'} + {isLoser && 'Nice try...'} + + )} + {!(isWinner || isLoser) && ( +

+ Question: {hintToGuess} +

+ )} + + + {(isWinner || isLoser) && ( + {tryAgainIcon} + )} + + + wordToGuess.includes(letter), + )} + incorrectLetters={incorrectLetters} + addGuessedLetter={addGuessedLetter} + /> +
+
+ ); + } \ No newline at end of file