diff --git a/config.json b/config.json index 10fd1382513..98a0506b4d7 100644 --- a/config.json +++ b/config.json @@ -1270,6 +1270,18 @@ "object_oriented_programming" ] }, + { + "uuid": "adad6be5-855d-4d61-b14a-22e468ba5b44", + "slug": "hangman", + "core": false, + "unlocked_by": null, + "difficulty": 5, + "topics":[ + "events", + "reactive_programming", + "strings" + ] + }, { "uuid": "bb07c236-062c-2980-483a-a221e4724445dcd6f32", "slug": "custom-set", diff --git a/exercises/hangman/README.md b/exercises/hangman/README.md new file mode 100644 index 00000000000..54ae381cbb8 --- /dev/null +++ b/exercises/hangman/README.md @@ -0,0 +1,33 @@ +Hangman + +Implement the logic of the hangman game using functional reactive programming. + +Hangman is a simple word guessing game. + +Functional Reactive Programming 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 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. + +## Exception messages + +Sometimes it is necessary to raise an exception. When you do this, you should include a meaningful error message to +indicate what the source of the error is. This makes your code more readable and helps significantly with debugging. Not +every exercise will require you to raise an exception, but for those that do, the tests will only pass if you include +a message. + +To raise a message with an exception, just write it as an argument to the exception type. For example, instead of +`raise Exception`, you shold write: + +```python +raise Exception("Meaningful message indicating the source of the error") +``` + + +## Submitting Exercises + +Note that, when trying to submit an exercise, make sure the solution is in the `exercism/python/` directory. + +For example, if you're submitting `bob.py` for the Bob exercise, the submit command would be something like `exercism submit /python/bob/bob.py`. + +For more detailed information about running tests, code style and linting, +please see the [help page](http://exercism.io/languages/python). diff --git a/exercises/hangman/example.py b/exercises/hangman/example.py new file mode 100644 index 00000000000..326d0400959 --- /dev/null +++ b/exercises/hangman/example.py @@ -0,0 +1,42 @@ +class Hangman: + def __init__(self, word): + self.remainingGuesses = 9 + self.status = 'busy' + self.word = word + self.masked_word = '' + self.guesses = [] + for i in self.word: + self.masked_word += '_' + + def guess(self, char): + self.update_remaining_guesses(char) + self.update_masked_word() + self.update_status() + + def update_masked_word(self): + self.masked_word = '' + for i in self.word: + if i not in self.guesses: + self.masked_word += '_' + else: + self.masked_word += i + + def update_remaining_guesses(self, char): + if char not in self.word or char in self.guesses: + self.remainingGuesses -= 1 + else: + self.guesses.append(char) + + def update_status(self): + if self.masked_word == self.word: + self.status = 'win' + elif self.remainingGuesses < 0: + self.status = 'lose' + else: + self.status = 'busy' + + def get_masked_word(self): + return self.masked_word + + def get_status(self): + return self.status diff --git a/exercises/hangman/hangman.py b/exercises/hangman/hangman.py new file mode 100644 index 00000000000..4c316f0b701 --- /dev/null +++ b/exercises/hangman/hangman.py @@ -0,0 +1,13 @@ +class Hangman(object): + def __init__(self, word): + self.remainingGuesses = 9 + self.status = 'busy' + + def guess(self, char): + pass + + def get_masked_word(self): + pass + + def get_status(self): + pass diff --git a/exercises/hangman/hangman_test.py b/exercises/hangman/hangman_test.py new file mode 100644 index 00000000000..102e829fa7c --- /dev/null +++ b/exercises/hangman/hangman_test.py @@ -0,0 +1,82 @@ +import unittest + +from hangman import Hangman + + +# Tests adapted from csharp//hangman/HangmanTest.cs +class HangmanTests(unittest.TestCase): + def test_initially_9_failures_are_allowed(self): + game = Hangman('foo') + self.assertEqual(game.get_status(), 'busy') + self.assertEqual(game.remainingGuesses, 9) + + def test_initially_no_letters_are_guessed(self): + game = Hangman('foo') + + self.assertEqual(game.get_masked_word(), '___') + + def test_after_10_failures_the_game_is_over(self): + game = Hangman('foo') + + for i in range(10): + game.guess('x') + + self.assertEqual(game.get_status(), 'lose') + + def test_feeding_a_correct_letter_removes_underscores(self): + game = Hangman('foobar') + + game.guess('b') + self.assertEqual(game.get_status(), 'busy') + self.assertEqual(game.remainingGuesses, 9) + self.assertEqual(game.get_masked_word(), '___b__') + + game.guess('o') + self.assertEqual(game.get_status(), 'busy') + self.assertEqual(game.remainingGuesses, 9) + self.assertEqual(game.get_masked_word(), '_oob__') + + def test_feeding_a_correct_letter_twice_counts_as_a_failure(self): + game = Hangman('foobar') + + game.guess('b') + self.assertEqual(game.get_status(), 'busy') + self.assertEqual(game.remainingGuesses, 9) + self.assertEqual(game.get_masked_word(), '___b__') + + game.guess('b') + self.assertEqual(game.get_status(), 'busy') + self.assertEqual(game.remainingGuesses, 8) + self.assertEqual(game.get_masked_word(), '___b__') + + def test_getting_all_the_letters_right_makes_for_a_win(self): + game = Hangman('hello') + + game.guess('b') + self.assertEqual(game.get_status(), 'busy') + self.assertEqual(game.get_status(), 'busy') + self.assertEqual(game.remainingGuesses, 8) + self.assertEqual(game.get_masked_word(), '_____') + + game.guess('e') + self.assertEqual(game.get_status(), 'busy') + self.assertEqual(game.remainingGuesses, 8) + self.assertEqual(game.get_masked_word(), '_e___') + + game.guess('l') + self.assertEqual(game.get_status(), 'busy') + self.assertEqual(game.remainingGuesses, 8) + self.assertEqual(game.get_masked_word(), '_ell_') + + game.guess('o') + self.assertEqual(game.get_status(), 'busy') + self.assertEqual(game.remainingGuesses, 8) + self.assertEqual(game.get_masked_word(), '_ello') + + game.guess('h') + self.assertEqual(game.get_status(), 'win') + self.assertEqual(game.get_masked_word(), 'hello') + + +if __name__ == '__main__': + unittest.main()