Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -906,6 +906,20 @@
"difficulty": 2,
"topics": ["conditionals", "strings", "text_formatting", "transforming"]
},
{
"slug": "hangman",
"uuid": "1a7f8522-2c68-45af-a5a2-1519c03a823e",
"core": false,
"unlocked_by": "bob",
"difficulty": 4,
"topics": [
"arrays",
"conditionals",
"loops",
"parsing",
"strings"
]
},
{
"slug": "diffie-hellman",
"uuid": "127eccbd-3009-4a8f-95c1-7d8aeb608550",
Expand Down
26 changes: 26 additions & 0 deletions exercises/hangman/.eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"root": true,
"parser": "babel-eslint",
"parserOptions": {
"ecmaVersion": 7,
"sourceType": "module"
},
"env": {
"es6": true,
"node": true,
"jest": true
},
"extends": [
"eslint:recommended",
"plugin:import/errors",
"plugin:import/warnings"
],
"rules": {
"linebreak-style": "off",

"import/extensions": "off",
"import/no-default-export": "off",
"import/no-unresolved": "off",
"import/prefer-default-export": "off"
}
}
50 changes: 50 additions & 0 deletions exercises/hangman/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Hangman

Implement the logic of the hangman game using functional reactive programming.

Hangman is a simple word guessing game.

[Functional Reactive Programming][frp] is a way to write interactive
programs. It differs from the usual perspective in that instead of
saying "when the button is pressed increment the counter", you write
"the value of the counter is the sum of the number of times the button
is pressed."

Implement the basic logic behind hangman, optionally using functional
reactive programming. No additional library is necessary.

[Hangman]: https://en.wikipedia.org/wiki/Hangman_%28game%29
[frp]: https://en.wikipedia.org/wiki/Functional_reactive_programming

## Setup

Go through the setup instructions for Javascript to install the necessary
dependencies:

[https://exercism.io/tracks/javascript/installation](https://exercism.io/tracks/javascript/installation)

## Requirements

Install assignment dependencies:

```bash
$ npm install
```

## Making the test suite pass

Execute the tests with:

```bash
$ npm test
```

In the test suites all tests but the first have been skipped.

Once you get a test passing, you can enable the next one by changing `xtest` to
`test`.

## Submitting Incomplete Solutions

It's possible to submit an incomplete solution so you can see how others have
completed the exercise.
14 changes: 14 additions & 0 deletions exercises/hangman/babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module.exports = {
presets: [
[
'@babel/env',
{
targets: {
node: 'current',
},
useBuiltIns: false,
},

],
],
};
60 changes: 60 additions & 0 deletions exercises/hangman/example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
export const GAME_STATUS = {
WIN: 'win',
LOSS: 'loss',
ONGOING: 'ongoing'
}

export class Hangman {
constructor(word) {
this.remaining_guesses = 9
this.status = GAME_STATUS.ONGOING
this.word = word
this.guesses = []
this.maskedWord = '_'.repeat(this.word.length)
}

guess(char) {
if (this.status != GAME_STATUS.ONGOING) {
throw new Error(`Game is over, status: ${this.status}.`)
}
if (this.word.split('').indexOf(char) >= 0 && this.guesses.indexOf(char) < 0) {
this.guesses.push(char)
this.updateMaskedWord()
} else {
this.remaining_guesses -= 1
}
this.updateStatus()
}

updateMaskedWord() {
this.maskedWord = ''
for (var i = 0; i < this.word.length; i++) {
const letter = this.word[i]
if (this.guesses.indexOf(letter) >= 0) {
this.maskedWord += letter
} else {
this.maskedWord += '_'
}
}
}

updateStatus() {
if (this.remaining_guesses < 0) {
this.status = GAME_STATUS.LOSS
} else if (this.maskedWord === this.word) {
this.status = GAME_STATUS.WIN
}
}

getMaskedWord() {
return this.maskedWord
}

getStatus() {
return this.status
}

getRemainingGuesses() {
return this.remaining_guesses
}
}
32 changes: 32 additions & 0 deletions exercises/hangman/hangman.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//
// This is only a SKELETON file for the 'Scale Generator' exercise. It's been provided as a
// convenience to get you started writing code faster.
//

export const GAME_STATUS = {
WIN: 'win',
LOSS: 'loss',
ONGOING: 'ongoing'
}

export class Hangman {
constructor(word) {
throw new Error("Remove this statement and implement this function");
}

guess(char) {
throw new Error("Remove this statement and implement this function");
}

getMaskedWord() {
throw new Error("Remove this statement and implement this function");
}

getStatus() {
throw new Error("Remove this statement and implement this function");
}

getRemainingGuesses() {
throw new Error("Remove this statement and implement this function");
}
}
77 changes: 77 additions & 0 deletions exercises/hangman/hangman.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { Hangman, GAME_STATUS } from './hangman';

describe('Hangman', () => {
test('Initially 9 failures are allowed', () => {
let game = new Hangman('foo')
expect(game.getStatus()).toEqual(GAME_STATUS.ONGOING)
expect(game.getRemainingGuesses()).toEqual(9)
})

xtest('Initially all letters are blanks and unguessed', () => {
let game = new Hangman('apple')
expect(game.getMaskedWord()).toEqual('_____')
})

xtest('After 10 failures, the game is over', () => {
let game = new Hangman('banana')
let counter = 9
while (counter > 0) {
game.guess('z')
counter -= 1
}
expect(game.getMaskedWord()).toEqual('______')
try {
game.guess('z')
} catch (e) {
expect(e).toBe(new Error('Game is over, status: loss.'))
}
expect(game.getMaskedWord()).toEqual('______')
expect(game.getStatus()).toEqual(GAME_STATUS.LOSS)
})

xtest('Entering a correct letter removes correct underscores', () => {
let game = new Hangman('orange')
game.guess('g')
expect(game.getMaskedWord()).toEqual('____g_')
game.guess('a')
expect(game.getMaskedWord()).toEqual('__a_g_')
})

xtest('Entering a correct letter twice counts as a failure', () => {
let game = new Hangman('pear')
game.guess('e')
expect(game.getMaskedWord()).toEqual('_e__')
expect(game.getRemainingGuesses()).toEqual(9)
game.guess('e')
expect(game.getMaskedWord()).toEqual('_e__')
expect(game.getRemainingGuesses()).toEqual(8)
})

xtest('Guessing all letters correctly results in a win', () => {
let game = new Hangman('peach')
game.guess('p')
expect(game.getMaskedWord()).toEqual('p____')
game.guess('e')
expect(game.getMaskedWord()).toEqual('pe___')
game.guess('a')
expect(game.getMaskedWord()).toEqual('pea__')
game.guess('c')
expect(game.getMaskedWord()).toEqual('peac_')
game.guess('h')
expect(game.getMaskedWord()).toEqual('peach')
expect(game.getStatus()).toEqual(GAME_STATUS.WIN)
})

xtest('Winning on last guess still results in a win', () => {
let game = new Hangman('yyyyy')
let counter = 9
while (counter > 0) {
game.guess('h')
counter -= 1
}
expect(game.getMaskedWord()).toEqual('_____')
game.guess('y')
expect(game.getMaskedWord()).toEqual('yyyyy')
expect(game.getStatus()).toEqual(GAME_STATUS.WIN)
})
});
35 changes: 35 additions & 0 deletions exercises/hangman/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"name": "exercism-javascript",
"description": "Exercism exercises in Javascript.",
"author": "Katrina Owen",
"private": true,
"repository": {
"type": "git",
"url": "https://github.com/exercism/javascript"
},
"devDependencies": {
"@babel/cli": "^7.5.5",
"@babel/core": "^7.5.5",
"@babel/preset-env": "^7.5.5",
"@types/jest": "^24.0.16",
"@types/node": "^12.6.8",
"babel-eslint": "^10.0.2",
"babel-jest": "^24.8.0",
"eslint": "^6.1.0",
"eslint-plugin-import": "^2.18.2",
"jest": "^24.8.0"
},
"jest": {
"modulePathIgnorePatterns": [
"package.json"
]
},
"scripts": {
"test": "jest --no-cache ./*",
"watch": "jest --no-cache --watch ./*",
"lint": "eslint .",
"lint-test": "eslint . && jest --no-cache ./* "
},
"license": "MIT",
"dependencies": {}
}