From 57836dd212f4a029dd1ed2d8727972655c4b21f4 Mon Sep 17 00:00:00 2001 From: Kristin Faner Date: Tue, 22 Oct 2019 21:05:12 -0500 Subject: [PATCH 1/3] add hangman exercise --- exercises/hangman/.eslintrc | 26 +++++++++++ exercises/hangman/README.md | 52 +++++++++++++++++++++ exercises/hangman/babel.config.js | 14 ++++++ exercises/hangman/example.js | 63 +++++++++++++++++++++++++ exercises/hangman/hangman.js | 32 +++++++++++++ exercises/hangman/hangman.spec.js | 77 +++++++++++++++++++++++++++++++ exercises/hangman/package.json | 35 ++++++++++++++ 7 files changed, 299 insertions(+) create mode 100644 exercises/hangman/.eslintrc create mode 100644 exercises/hangman/README.md create mode 100644 exercises/hangman/babel.config.js create mode 100644 exercises/hangman/example.js create mode 100644 exercises/hangman/hangman.js create mode 100644 exercises/hangman/hangman.spec.js create mode 100644 exercises/hangman/package.json diff --git a/exercises/hangman/.eslintrc b/exercises/hangman/.eslintrc new file mode 100644 index 0000000000..2e5a5079a0 --- /dev/null +++ b/exercises/hangman/.eslintrc @@ -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" + } +} diff --git a/exercises/hangman/README.md b/exercises/hangman/README.md new file mode 100644 index 0000000000..ab541142b1 --- /dev/null +++ b/exercises/hangman/README.md @@ -0,0 +1,52 @@ +# Hello World + +The classical introductory exercise. Just say "Hello, World!". + +["Hello, World!"](http://en.wikipedia.org/wiki/%22Hello,_world!%22_program) is +the traditional first program for beginning programming in a new language +or environment. + +The objectives are simple: + +- Write a function that returns the string "Hello, World!". +- Run the test suite and make sure that it succeeds. +- Submit your solution and check it at the website. + +If everything goes well, you will be ready to fetch your first real exercise. + +## 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`. + +## Source + +This is an exercise to introduce users to using Exercism [http://en.wikipedia.org/wiki/%22Hello,_world!%22_program](http://en.wikipedia.org/wiki/%22Hello,_world!%22_program) + +## Submitting Incomplete Solutions + +It's possible to submit an incomplete solution so you can see how others have +completed the exercise. diff --git a/exercises/hangman/babel.config.js b/exercises/hangman/babel.config.js new file mode 100644 index 0000000000..9da4622b24 --- /dev/null +++ b/exercises/hangman/babel.config.js @@ -0,0 +1,14 @@ +module.exports = { + presets: [ + [ + '@babel/env', + { + targets: { + node: 'current', + }, + useBuiltIns: false, + }, + + ], + ], +}; diff --git a/exercises/hangman/example.js b/exercises/hangman/example.js new file mode 100644 index 0000000000..b55d6410c4 --- /dev/null +++ b/exercises/hangman/example.js @@ -0,0 +1,63 @@ +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 = '' + for (var i = 0; i < this.word.length; i++) { + this.maskedWord += '_' + } + } + + 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 + } +} \ No newline at end of file diff --git a/exercises/hangman/hangman.js b/exercises/hangman/hangman.js new file mode 100644 index 0000000000..2c9feaec38 --- /dev/null +++ b/exercises/hangman/hangman.js @@ -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"); + } +} \ No newline at end of file diff --git a/exercises/hangman/hangman.spec.js b/exercises/hangman/hangman.spec.js new file mode 100644 index 0000000000..4221d2d948 --- /dev/null +++ b/exercises/hangman/hangman.spec.js @@ -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) + }) + + test('Initially all letters are blanks and unguessed', () => { + let game = new Hangman('apple') + expect(game.getMaskedWord()).toEqual('_____') + }) + + test('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) + }) + + test('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_') + }) + + test('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) + }) + + test('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) + }) + + test('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) + }) +}); diff --git a/exercises/hangman/package.json b/exercises/hangman/package.json new file mode 100644 index 0000000000..893bf6fdd0 --- /dev/null +++ b/exercises/hangman/package.json @@ -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": {} +} From 7523ca76413b33f7f4d5ff28fa12326a7448d021 Mon Sep 17 00:00:00 2001 From: Kristin Faner Date: Tue, 22 Oct 2019 22:39:04 -0500 Subject: [PATCH 2/3] add config.json and readme.md, cleanup --- config.json | 14 ++++++++++++++ exercises/hangman/README.md | 27 +++++++++++++-------------- exercises/hangman/hangman.spec.js | 12 ++++++------ 3 files changed, 33 insertions(+), 20 deletions(-) diff --git a/config.json b/config.json index 6d40440a34..7cbe1f1c73 100644 --- a/config.json +++ b/config.json @@ -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", diff --git a/exercises/hangman/README.md b/exercises/hangman/README.md index ab541142b1..e204f8998b 100644 --- a/exercises/hangman/README.md +++ b/exercises/hangman/README.md @@ -1,18 +1,21 @@ -# Hello World +# Hangman -The classical introductory exercise. Just say "Hello, World!". +Implement the logic of the hangman game using functional reactive programming. -["Hello, World!"](http://en.wikipedia.org/wiki/%22Hello,_world!%22_program) is -the traditional first program for beginning programming in a new language -or environment. +Hangman is a simple word guessing game. -The objectives are simple: +[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." -- Write a function that returns the string "Hello, World!". -- Run the test suite and make sure that it succeeds. -- Submit your solution and check it at the website. +Implement the basic logic behind hangman using functional reactive +programming. You'll need to install an FRP library for this, this will +be described in the language/track specific files of the exercise. -If everything goes well, you will be ready to fetch your first real exercise. +[Hangman]: https://en.wikipedia.org/wiki/Hangman_%28game%29 +[frp]: https://en.wikipedia.org/wiki/Functional_reactive_programming ## Setup @@ -42,10 +45,6 @@ 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`. -## Source - -This is an exercise to introduce users to using Exercism [http://en.wikipedia.org/wiki/%22Hello,_world!%22_program](http://en.wikipedia.org/wiki/%22Hello,_world!%22_program) - ## Submitting Incomplete Solutions It's possible to submit an incomplete solution so you can see how others have diff --git a/exercises/hangman/hangman.spec.js b/exercises/hangman/hangman.spec.js index 4221d2d948..5cb224517e 100644 --- a/exercises/hangman/hangman.spec.js +++ b/exercises/hangman/hangman.spec.js @@ -7,12 +7,12 @@ describe('Hangman', () => { expect(game.getRemainingGuesses()).toEqual(9) }) - test('Initially all letters are blanks and unguessed', () => { + xtest('Initially all letters are blanks and unguessed', () => { let game = new Hangman('apple') expect(game.getMaskedWord()).toEqual('_____') }) - test('After 10 failures, the game is over', () => { + xtest('After 10 failures, the game is over', () => { let game = new Hangman('banana') let counter = 9 while (counter > 0) { @@ -29,7 +29,7 @@ describe('Hangman', () => { expect(game.getStatus()).toEqual(GAME_STATUS.LOSS) }) - test('Entering a correct letter removes correct underscores', () => { + xtest('Entering a correct letter removes correct underscores', () => { let game = new Hangman('orange') game.guess('g') expect(game.getMaskedWord()).toEqual('____g_') @@ -37,7 +37,7 @@ describe('Hangman', () => { expect(game.getMaskedWord()).toEqual('__a_g_') }) - test('Entering a correct letter twice counts as a failure', () => { + xtest('Entering a correct letter twice counts as a failure', () => { let game = new Hangman('pear') game.guess('e') expect(game.getMaskedWord()).toEqual('_e__') @@ -47,7 +47,7 @@ describe('Hangman', () => { expect(game.getRemainingGuesses()).toEqual(8) }) - test('Guessing all letters correctly results in a win', () => { + xtest('Guessing all letters correctly results in a win', () => { let game = new Hangman('peach') game.guess('p') expect(game.getMaskedWord()).toEqual('p____') @@ -62,7 +62,7 @@ describe('Hangman', () => { expect(game.getStatus()).toEqual(GAME_STATUS.WIN) }) - test('Winning on last guess still results in a win', () => { + xtest('Winning on last guess still results in a win', () => { let game = new Hangman('yyyyy') let counter = 9 while (counter > 0) { From c329ce5ba41eae5fb07904b244312b28157931c1 Mon Sep 17 00:00:00 2001 From: Kristin Faner Date: Sat, 28 Dec 2019 12:04:32 -0600 Subject: [PATCH 3/3] respond to code review --- exercises/hangman/README.md | 5 ++--- exercises/hangman/example.js | 7 ++----- exercises/hangman/hangman.js | 2 +- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/exercises/hangman/README.md b/exercises/hangman/README.md index e204f8998b..06a6910010 100644 --- a/exercises/hangman/README.md +++ b/exercises/hangman/README.md @@ -10,9 +10,8 @@ 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 using functional reactive -programming. You'll need to install an FRP library for this, this will -be described in the language/track specific files of the exercise. +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 diff --git a/exercises/hangman/example.js b/exercises/hangman/example.js index b55d6410c4..d710a6eab2 100644 --- a/exercises/hangman/example.js +++ b/exercises/hangman/example.js @@ -10,10 +10,7 @@ export class Hangman { this.status = GAME_STATUS.ONGOING this.word = word this.guesses = [] - this.maskedWord = '' - for (var i = 0; i < this.word.length; i++) { - this.maskedWord += '_' - } + this.maskedWord = '_'.repeat(this.word.length) } guess(char) { @@ -60,4 +57,4 @@ export class Hangman { getRemainingGuesses() { return this.remaining_guesses } -} \ No newline at end of file +} diff --git a/exercises/hangman/hangman.js b/exercises/hangman/hangman.js index 2c9feaec38..aa5faed92f 100644 --- a/exercises/hangman/hangman.js +++ b/exercises/hangman/hangman.js @@ -29,4 +29,4 @@ export class Hangman { getRemainingGuesses() { throw new Error("Remove this statement and implement this function"); } -} \ No newline at end of file +}