diff --git a/python/grains/.exercism/metadata.json b/python/grains/.exercism/metadata.json new file mode 100644 index 0000000..29decb6 --- /dev/null +++ b/python/grains/.exercism/metadata.json @@ -0,0 +1 @@ +{"track":"python","exercise":"grains","id":"adff732869fb4477a4fff084db71a9c2","url":"https://exercism.io/my/solutions/adff732869fb4477a4fff084db71a9c2","handle":"Xevion","is_requester":true,"auto_approve":false} \ No newline at end of file diff --git a/python/grains/README.md b/python/grains/README.md new file mode 100644 index 0000000..39de18a --- /dev/null +++ b/python/grains/README.md @@ -0,0 +1,76 @@ +# Grains + +Calculate the number of grains of wheat on a chessboard given that the number +on each square doubles. + +There once was a wise servant who saved the life of a prince. The king +promised to pay whatever the servant could dream up. Knowing that the +king loved chess, the servant told the king he would like to have grains +of wheat. One grain on the first square of a chess board. Two grains on +the next. Four on the third, and so on. + +There are 64 squares on a chessboard. + +Write code that shows: +- how many grains were on each square, and +- the total number of grains + +## For bonus points + +Did you get the tests passing and the code clean? If you want to, these +are some additional things you could try: + +- Optimize for speed. +- Optimize for readability. + +Then please share your thoughts in a comment on the submission. Did this +experiment make the code better? Worse? Did you learn anything from it? + +## 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 should write: + +```python +raise Exception("Meaningful message indicating the source of the error") +``` + +## Running the tests + +To run the tests, run the appropriate command below ([why they are different](https://github.com/pytest-dev/pytest/issues/1629#issue-161422224)): + +- Python 2.7: `py.test grains_test.py` +- Python 3.4+: `pytest grains_test.py` + +Alternatively, you can tell Python to run the pytest module (allowing the same command to be used regardless of Python version): +`python -m pytest grains_test.py` + +### Common `pytest` options + +- `-v` : enable verbose output +- `-x` : stop running tests on first failure +- `--ff` : run failures from previous test before running other test cases + +For other options, see `python -m pytest -h` + +## Submitting Exercises + +Note that, when trying to submit an exercise, make sure the solution is in the `$EXERCISM_WORKSPACE/python/grains` directory. + +You can find your Exercism workspace by running `exercism debug` and looking for the line that starts with `Workspace`. + +For more detailed information about running tests, code style and linting, +please see [Running the Tests](http://exercism.io/tracks/python/tests). + +## Source + +JavaRanch Cattle Drive, exercise 6 [http://www.javaranch.com/grains.jsp](http://www.javaranch.com/grains.jsp) + +## Submitting Incomplete Solutions + +It's possible to submit an incomplete solution so you can see how others have completed the exercise. diff --git a/python/grains/grains.py b/python/grains/grains.py new file mode 100644 index 0000000..90d2a0f --- /dev/null +++ b/python/grains/grains.py @@ -0,0 +1,2 @@ +square = lambda number : exec('raise ValueError(\'Value {} is invalid ({}).\')'.format(number, 'too high' if number > 64 else 'too low')) if number > 64 or number < 1 else 2 ** (number - 1) +total = lambda number : exec('raise ValueError(\'Value {} is invalid (too low).\')'.format(number)) if number < 1 else sum([square(i) for i in range(1, number+1)]) \ No newline at end of file diff --git a/python/grains/grains_test.py b/python/grains/grains_test.py new file mode 100644 index 0000000..0f97d02 --- /dev/null +++ b/python/grains/grains_test.py @@ -0,0 +1,63 @@ +import unittest + +from grains import square, total + + +# Tests adapted from `problem-specifications//canonical-data.json` @ v1.2.0 + +class GrainsTest(unittest.TestCase): + def test_square_1(self): + self.assertEqual(square(1), 1) + + def test_square_2(self): + self.assertEqual(square(2), 2) + + def test_square_3(self): + self.assertEqual(square(3), 4) + + def test_square_4(self): + self.assertEqual(square(4), 8) + + def test_square_16(self): + self.assertEqual(square(16), 32768) + + def test_square_32(self): + self.assertEqual(square(32), 2147483648) + + def test_square_64(self): + self.assertEqual(square(64), 9223372036854775808) + + def test_square_0_raises_exception(self): + with self.assertRaisesWithMessage(ValueError): + square(0) + with self.assertRaisesWithMessage(ValueError): + total(0) + + def test_square_negative_raises_exception(self): + with self.assertRaisesWithMessage(ValueError): + square(-1) + with self.assertRaisesWithMessage(ValueError): + total(-1) + + def test_square_gt_64_raises_exception(self): + with self.assertRaisesWithMessage(ValueError): + square(65) + with self.assertRaisesWithMessage(ValueError): + total(65) + + def test_total(self): + self.assertEqual(total(64), 18446744073709551615) + + # Utility functions + def setUp(self): + try: + self.assertRaisesRegex + except AttributeError: + self.assertRaisesRegex = self.assertRaisesRegexp + + def assertRaisesWithMessage(self, exception): + return self.assertRaisesRegex(exception, r".+") + + +if __name__ == '__main__': + unittest.main()