├── src ├── images │ ├── 0.png │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ └── 6.png ├── App.test.js ├── components │ ├── streak.js │ ├── Next.js │ ├── Letter.js │ ├── Output.js │ ├── Answer.js │ ├── Letters.js │ ├── hangman-figure.js │ └── words.js ├── index.js ├── registerServiceWorker.js └── App.js ├── public ├── favicon.ico ├── manifest.json ├── index.html └── style │ └── style.css ├── .gitignore ├── package.json └── README.md /src/images/0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djbarnwal/hangman/HEAD/src/images/0.png -------------------------------------------------------------------------------- /src/images/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djbarnwal/hangman/HEAD/src/images/1.png -------------------------------------------------------------------------------- /src/images/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djbarnwal/hangman/HEAD/src/images/2.png -------------------------------------------------------------------------------- /src/images/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djbarnwal/hangman/HEAD/src/images/3.png -------------------------------------------------------------------------------- /src/images/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djbarnwal/hangman/HEAD/src/images/4.png -------------------------------------------------------------------------------- /src/images/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djbarnwal/hangman/HEAD/src/images/5.png -------------------------------------------------------------------------------- /src/images/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djbarnwal/hangman/HEAD/src/images/6.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djbarnwal/hangman/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | }); 9 | -------------------------------------------------------------------------------- /src/components/streak.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | 4 | const Output = (props) => { 5 | 6 | return ( 7 |
8 | Streak: {props.streak} 9 |
10 | ); 11 | }; 12 | 13 | export default Output; 14 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | import registerServiceWorker from './registerServiceWorker'; 5 | 6 | ReactDOM.render(, document.getElementById('root')); 7 | registerServiceWorker(); 8 | -------------------------------------------------------------------------------- /src/components/Next.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const Next = (props) => { 4 | if(props.gameStatus > 0 && props.answerList.length) { 5 | return ( 6 |
props.nextWord()} className='nextButton'> 7 | Next 8 |
9 | ); 10 | } 11 | return
; 12 | } 13 | 14 | export default Next; 15 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Hangman", 3 | "name": "Play Hangman", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#fefefe", 14 | "background_color": "#232222" 15 | } 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # See https://help.github.com/ignore-files/ for more about ignoring files. 3 | 4 | # dependencies 5 | /node_modules 6 | 7 | # testing 8 | /coverage 9 | 10 | # production 11 | /build 12 | 13 | # misc 14 | .DS_Store 15 | .env.local 16 | .env.development.local 17 | .env.test.local 18 | .env.production.local 19 | 20 | npm-debug.log* 21 | yarn-debug.log* 22 | yarn-error.log* 23 | *.db 24 | 25 | -------------------------------------------------------------------------------- /src/components/Letter.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const Letter = (props) => { 4 | if ( props.pickedArray.indexOf(props.alpha) > -1 || props.gameStatus > 0 ) { 5 | return ( 6 |
  • 7 | {props.alpha} 8 |
  • 9 | ); 10 | } 11 | return ( 12 |
  • props.addAlphas(props.alpha)}> 13 | {props.alpha} 14 |
  • 15 | ); 16 | }; 17 | 18 | export default Letter; 19 | -------------------------------------------------------------------------------- /src/components/Output.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | let output; 4 | const Output = (props) => { 5 | if(!props.gameStatus) output = "The Game is on let's play"; 6 | if(props.gameStatus === 1) output = "YOU WIN!!"; 7 | if(props.gameStatus === 2) { 8 | output = `YOU LOSE! The word was ${props.answer}`; 9 | } 10 | return ( 11 |
    12 | {output} 13 |
    14 | ); 15 | }; 16 | 17 | export default Output; 18 | -------------------------------------------------------------------------------- /src/components/Answer.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const Answer = (props) => { 4 | let answer = props.answer.word; 5 | const hint = props.answer.hint; 6 | let pick = props.pickedArray; 7 | 8 | let guess = pick.join(''); 9 | let regexp = new RegExp('[^' + guess + ']','g'); 10 | let underscore = answer.replace(regexp, '_'); 11 | 12 | 13 | return ( 14 |
    15 |
    16 | {underscore} 17 |
    18 |
    19 | {hint} 20 |
    21 |
    22 | ); 23 | }; 24 | 25 | export default Answer; 26 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hangman", 3 | "version": "0.1.0", 4 | "private": true, 5 | "homepage": "https://dhiraj161298.github.io/hangman", 6 | "dependencies": { 7 | "react": "^15.5.4", 8 | "react-dom": "^15.5.4" 9 | }, 10 | "devDependencies": { 11 | "gh-pages": "^1.0.0", 12 | "react-scripts": "1.0.7" 13 | }, 14 | "scripts": { 15 | "start": "react-scripts start", 16 | "build": "react-scripts build", 17 | "test": "react-scripts test --env=jsdom", 18 | "eject": "react-scripts eject", 19 | "predeploy": "npm run build", 20 | "deploy": "gh-pages -d build" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/components/Letters.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Letter from './Letter'; 3 | 4 | const Letters = (props) => { 5 | const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; 6 | let chars = characters.split(''); 7 | 8 | chars = chars.map((character, i) => { 9 | return ( 10 | 16 | ); 17 | }); 18 | 19 | return ( 20 |
    21 |
      22 | {chars} 23 |
    24 |
    25 | ); 26 | }; 27 | 28 | export default Letters; 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Play Hangman 2 | https://djbarnwal.github.io/hangman/ 3 | The traditional hangman puzzle game developed using ReactJS. 4 | 5 | ## Requirements for Development 6 | 7 | - `React` 8 | - `npm` 9 | 10 | ## Setup for Development 11 | 12 | - `git clone https://github.com/djbarnwal/hangman.git` 13 | - `cd hangman` 14 | - `npm install` 15 | - `npm start` 16 | 17 | ## Git Flow 18 | 19 | To submit a PR: 20 | - Create a feature branch in this repo (or your own fork if you don't have appropriate permissions) 21 | - Once the feature is ready, open a pull request against the `master` branch 22 | 23 | ## Things to Do 24 | 1. Add more categories 25 | 2. Add a better hangman figure 26 | 3. Add leaderboard 27 | 4. Make Hangman creator 28 | -------------------------------------------------------------------------------- /src/components/hangman-figure.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | 3 | //Import all images 4 | const images = importAll(require.context('../images', false, /\.(png|jpe?g|svg)$/)); 5 | 6 | let imgArray = []; 7 | 8 | class Hangman extends Component { 9 | 10 | componentWillMount() { 11 | for(let i=0; i<=6; i++) { 12 | imgArray.push( ); 13 | } 14 | } 15 | 16 | showHangman() { 17 | let output = imgArray.slice(0, this.props.incorrectPicks+1); 18 | return output; 19 | } 20 | 21 | render() { 22 | return ( 23 |
    24 | {this.showHangman()} 25 |
    26 | ); 27 | } 28 | } 29 | 30 | export default Hangman; 31 | 32 | function importAll(r) { 33 | let images = {}; 34 | r.keys().map((item, index) => { 35 | images[item.replace('./', '')] = r(item); 36 | return null; 37 | }); 38 | return images; 39 | } 40 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Play Hangman 12 | 13 | 14 | 17 |
    18 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/components/words.js: -------------------------------------------------------------------------------- 1 | export default [ 2 | {word: "Machaya", hint:"To do something great"}, 3 | {word: "Makhaya", hint:"To do a task poorly"}, 4 | {word: "Insti Top", hint:"Highest point of KGP"}, 5 | {word: "Gol C", hint:"Here stands BC Roy"}, 6 | {word: "Bhaat", hint:"A local meetup"}, 7 | {word: "Maggu", hint:"A special kind of species with just one goal"}, 8 | {word: "Rassa", hint:"They often conduct strikes in campus :P"}, 9 | {word: "Matka", hint:"Not a UG, not a scholar"}, 10 | {word: "Dassi", hint:"Academic gods"}, 11 | {word: "Peace", hint:"The most famous one"}, 12 | {word: "Illumination", hint:"Lights."}, 13 | {word: "Happa", hint:"Hall president"}, 14 | {word: "Tempo Shout", hint:"We don't scream we have ____"}, 15 | {word: "Chaggi", hint:"Probably the most common CG"}, 16 | {word: "Panji", hint:"He is a dude"}, 17 | {word: "Faccha", hint:"The noobs of KGP"}, 18 | {word: "Frustapa", hint:"Anxiety and annoyance for a KGPian"}, 19 | {word: "Bhajan", hint:"60% of DC searches"}, 20 | {word: "Funda", hint:"the basic principle behind something"}, 21 | {word: "Geez", hint:"eating someone's stuff as of its own"}, 22 | {word: "Load", hint:"To Have Tension"}, 23 | {word: "Fakka", hint:"An F-grade in any subject"}, 24 | {word: "Lodu", hint:"A person who takes lot of load"}, 25 | {word: "Banda", hint:"Any Boy"}, 26 | {word: "Bhaatu", hint:"A person who is very talkative"}, 27 | {word: "Archi", hint:"The Architecture Department"}, 28 | {word: "Taapna", hint:" Copying assignments or examination answer-scripts from any person"}, 29 | {word: "Makhana", hint:"To spoil a job"}, 30 | {word: "Stud", hint:"Any student who is good in academics as well as other fields like sports"}, 31 | {word: "Bandi", hint:"Any girl"}, 32 | {word: "Batti", hint:"The Electrical Engineering Department"}, 33 | {word: "Hathora", hint:"The Mechanical Engineering Department"}, 34 | {word: "Junta", hint:"The human population"}, 35 | {word: "Chhedis", hint:"A dhaba just outside campus"}, 36 | {word: "Atthi", hint:"A C.G.P.A. in between 8 and 9"}, 37 | {word: "Nehli", hint:"A C.G.P.A. greater than 9"}, 38 | {word: "Panji", hint:"A C.G.P.A. in between 5 and 6"}, 39 | {word: "Bhaat", hint:"Bekar Chat"}, 40 | {word: "Ghaasi", hint:"A student of Agriculture Engineering Department"}, 41 | ]; 42 | -------------------------------------------------------------------------------- /src/registerServiceWorker.js: -------------------------------------------------------------------------------- 1 | // In production, we register a service worker to serve assets from local cache. 2 | 3 | // This lets the app load faster on subsequent visits in production, and gives 4 | // it offline capabilities. However, it also means that developers (and users) 5 | // will only see deployed updates on the "N+1" visit to a page, since previously 6 | // cached resources are updated in the background. 7 | 8 | // To learn more about the benefits of this model, read https://goo.gl/KwvDNy. 9 | // This link also includes instructions on opting out of this behavior. 10 | 11 | export default function register() { 12 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 13 | window.addEventListener('load', () => { 14 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 15 | navigator.serviceWorker 16 | .register(swUrl) 17 | .then(registration => { 18 | registration.onupdatefound = () => { 19 | const installingWorker = registration.installing; 20 | installingWorker.onstatechange = () => { 21 | if (installingWorker.state === 'installed') { 22 | if (navigator.serviceWorker.controller) { 23 | // At this point, the old content will have been purged and 24 | // the fresh content will have been added to the cache. 25 | // It's the perfect time to display a "New content is 26 | // available; please refresh." message in your web app. 27 | console.log('New content is available; please refresh.'); 28 | } else { 29 | // At this point, everything has been precached. 30 | // It's the perfect time to display a 31 | // "Content is cached for offline use." message. 32 | console.log('Content is cached for offline use.'); 33 | } 34 | } 35 | }; 36 | }; 37 | }) 38 | .catch(error => { 39 | console.error('Error during service worker registration:', error); 40 | }); 41 | }); 42 | } 43 | } 44 | 45 | export function unregister() { 46 | if ('serviceWorker' in navigator) { 47 | navigator.serviceWorker.ready.then(registration => { 48 | registration.unregister(); 49 | }); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /public/style/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: #232222 none repeat scroll 0 0; 3 | color: #fefefe; 4 | font-family: Verdana; 5 | -moz-user-select: none; 6 | -webkit-user-select: none; 7 | } 8 | 9 | .container { 10 | text-align: center; 11 | } 12 | 13 | .figureWrapper { 14 | display: inline-block; 15 | } 16 | 17 | .answerWrapper { 18 | display: inline-block; 19 | width: 60%; 20 | } 21 | 22 | .title { 23 | font-size: 30px; 24 | margin: 2em 0; 25 | } 26 | 27 | .info { 28 | margin-top: 10px; 29 | color: #555; 30 | } 31 | 32 | .hangman { 33 | height: 350px; 34 | margin-top: 20px; 35 | position: relative; 36 | width: 350px; 37 | } 38 | 39 | .hangman img { 40 | position: absolute; 41 | left: 0; 42 | height: 350px; 43 | } 44 | 45 | .output { 46 | background: #fefefe; 47 | color: #232222; 48 | display: inline-block; 49 | padding: 4px 10px; 50 | } 51 | 52 | .answer-box { 53 | font-size: 50px; 54 | letter-spacing: 20px; 55 | margin-top: 30px; 56 | } 57 | 58 | .letters ul { 59 | margin: 0 auto; 60 | width: 75%; 61 | margin-top: 25px; 62 | padding: 0; 63 | } 64 | 65 | .letters ul li { 66 | background: #fbf9f9; 67 | border-radius: 3px; 68 | color: #222; 69 | cursor: pointer; 70 | display: inline-block; 71 | height: 20px; 72 | margin:3px; 73 | padding: 8px; 74 | width: 30px; 75 | transition: background-color ease 0.2s; 76 | } 77 | 78 | .letters ul li:hover { 79 | background: #F7DE31; 80 | } 81 | 82 | .letters ul .picked { 83 | background: #423F40; 84 | color: #212121; 85 | cursor: default; 86 | } 87 | 88 | .letters ul .picked:hover { 89 | background: #423F40; 90 | } 91 | 92 | .nextButton { 93 | border: 1px solid #eee; 94 | border-radius: 5px; 95 | display: inline-block; 96 | margin-top: 25px; 97 | padding: 10px 40px; 98 | transition: all 0.3s ease 0s; 99 | cursor: pointer; 100 | } 101 | 102 | .nextButton:hover { 103 | background: #fefefe; 104 | color: #222; 105 | } 106 | 107 | .hint, .streak { 108 | margin-top: 15px; 109 | } 110 | 111 | @media only screen and (max-width: 890px) { 112 | .title { 113 | margin: 0.3em 0; 114 | } 115 | .answerWrapper { 116 | width: 100%; 117 | margin-top: 2em; 118 | } 119 | .answer-box { 120 | font-size: 22px; 121 | letter-spacing: 10px; 122 | margin-top: 30px; 123 | } 124 | .hangman img, .hangman { 125 | height: 250px; 126 | } 127 | .hangman { 128 | width: 225px; 129 | } 130 | .letters ul { 131 | width: 90%; 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import Hangman from './components/hangman-figure'; 3 | import Answer from './components/Answer'; 4 | import Output from './components/Output'; 5 | import Letters from './components/Letters'; 6 | import Next from './components/Next'; 7 | import Streak from './components/streak'; 8 | import words from './components/words'; 9 | 10 | class App extends Component { 11 | constructor(props) { 12 | super(props); 13 | let answerList = words; 14 | // Shuffle the array 15 | answerList.sort(function() { return 0.5 - Math.random() }); 16 | const answer = answerList.pop(); 17 | answer.word = answer.word.toUpperCase(); 18 | 19 | this.state = { 20 | picked: [" "], 21 | incorrectPicks: 0, 22 | answerList, 23 | answer, 24 | gameStatus: 0, // 0 - play, 1 - won, 2 - lost 25 | streak: 0, 26 | }; 27 | 28 | this.addAlphas = this.addAlphas.bind(this); 29 | this.nextWord = this.nextWord.bind(this); 30 | } 31 | 32 | addAlphas(alpha) { 33 | let alphaList = this.state.picked; 34 | alphaList.push(alpha); 35 | this.setState({picked: alphaList}, () => { 36 | let word = this.state.answer.word.replace(new RegExp('[^' + this.state.picked + ']','g'), '-'); 37 | if(word.indexOf('-') === -1) { 38 | this.setState({ 39 | gameStatus: 1, 40 | streak: this.state.streak + 1 41 | }) 42 | } 43 | }); 44 | 45 | // Adding if statement and the regex as the else part in the above callback 46 | // will make the process slow. In the current setup we add regex for every 47 | // alphabet added increasing the number of cases. Find optimal way. 48 | 49 | if(this.state.answer.word.indexOf(alpha) === -1) { 50 | this.setState({incorrectPicks: (this.state.incorrectPicks + 1)}, () => { 51 | if(this.state.incorrectPicks === 6) { 52 | this.setState({ 53 | gameStatus: 2, 54 | streak: 0, 55 | }) 56 | } 57 | }); 58 | } 59 | } 60 | 61 | nextWord() { 62 | const answer = this.state.answerList.pop(); 63 | answer.word = answer.word.toUpperCase(); 64 | this.setState({ 65 | picked: [" "], 66 | incorrectPicks: 0, 67 | answer, 68 | gameStatus: 0, 69 | }); 70 | } 71 | 72 | render() { 73 | return ( 74 |
    75 |
    Hangman Game
    76 |
    77 | 80 |
    81 |
    82 | 86 | 90 |
    (All words are from KGP lingo)
    91 | 96 | 99 | 104 |
    105 |
    106 | ); 107 | } 108 | } 109 | 110 | export default App; 111 | --------------------------------------------------------------------------------