Merge branch 'master' into master

This commit is contained in:
Kevin Engle
2019-04-24 15:21:01 -07:00
committed by GitHub
378 changed files with 11306 additions and 3888 deletions

1
.github/CODEOWNERS vendored Normal file
View File

@@ -0,0 +1 @@
* @exercism/python

1
.github/stale.yml vendored
View File

@@ -9,6 +9,7 @@ exemptLabels:
- epic
- enhancement
- beginner friendly
- awaiting review
# Label to use when marking an issue as stale
staleLabel: abandoned
# Comment to post when marking an issue as stale. Set to `false` to disable

View File

@@ -1,26 +1,32 @@
sudo: false
language: python
dist: xenial
python:
- 2.7
- 3.3
- 3.4
- 3.5
- 3.6
- 3.7
- nightly
matrix:
include:
# Python 3.4 not supported in xenial
- python: 3.4
dist: trusty
- env: JOB=HOUSEKEEPING
install: ./bin/fetch-configlet
before_script: ./bin/check-readmes.sh
script:
# May provide more useful output than configlet fmt
# if JSON is not valid or items are incomplete
- ./bin/configlet lint .
# Verify correct formatting in config.json
- ./bin/configlet fmt . && git diff --quiet
allow_failures:
- python: nightly
install:
- pip install -r requirements-travis.txt
before_script:
- flake8
- ./bin/fetch-configlet
- ./bin/configlet lint .
script:
script:
- ./test/check-exercises.py

View File

@@ -14,7 +14,7 @@ Please see the [contributing guide](https://github.com/exercism/docs/blob/master
## Working on the Exercises
We welcome both improvements to the existing exercises and new exercises.
A list of missing exercise can be found here: http://exercism.io/languages/python/todo
A list of missing exercise can be found here: https://github.com/exercism/python/issues/417#issuecomment-366040062
### Conventions
@@ -23,13 +23,14 @@ A list of missing exercise can be found here: http://exercism.io/languages/pytho
- We use `unittest` (Python Standard Library) and no 3rd-party-framework.
- We use the parameter order `self.assertEqual(actual, expected)` ([#440](https://github.com/exercism/python/issues/440)).
- We use context managers (`with self.assertRaises(\<exception type\>):`) for testing for exceptions ([#477](https://github.com/exercism/python/issues/477)).
- We use an established utility method to confirm that expected exceptions contain a non-empty message. This method must be included for any test class with an exception-based test case ([#1080](https://github.com/exercism/python/issues/1080#issuecomment-442068539)).
- We use `assertIs(actual, True)` and `assertIs(actual, False)` rather than `assertTrue(actual)` or `assertFalse(actual)` ([#419](https://github.com/exercism/python/pull/419)).
- We use a comment string in the test file to reference the version of the exercise's `canonical-data.json` that tests were adapted from (wording can be found in: [#784](https://github.com/exercism/python/issues/784)).
### Testing
All exercises must be compatible with Python versions 2.7 and 3.3 upwards.
All exercises must be compatible with Python versions 2.7 and 3.4 upwards.
To test a single exercise (e.g., with Python 2.7):
```
@@ -71,9 +72,5 @@ If you're interested, Tim Pope even has an [entire blog post](http://tbaggery.co
If you're new to Git, take a look at [this short guide](https://github.com/exercism/docs/blob/master/contributing-to-language-tracks/README.md#git-basics).
## Python icon
The Python logo is an unregistered trademark. We are using a derived logo with the permission of the Python Software Foundation.
## License
This repository uses the [MIT License](/LICENSE).

22
bin/check-readmes.sh Executable file
View File

@@ -0,0 +1,22 @@
#!/bin/bash
get_timestamp()
{
path="$1"
git log -n1 --pretty=format:%ct -- "$path"
}
ret=0
for exercise in $(ls -d exercises/*/); do
meta_dir="${exercise}.meta"
if [ -d "$meta_dir" ]; then
meta_timestamp="$(get_timestamp "$meta_dir")"
readme_timestamp="$(get_timestamp "${exercise}README.md")"
if [ "$meta_timestamp" -gt "$readme_timestamp" ]; then
ret=1
echo "$(basename "$exercise"): .meta/ contents newer than README. Please regenerate it with configlet."
fi
fi
done
exit $ret

331
bin/check-test-version.py Executable file
View File

@@ -0,0 +1,331 @@
#!/usr/bin/env python
from __future__ import print_function
import argparse
import json
import os
import re
import sys
from glob import glob
from create_issue import GitHub
if sys.version_info[0] == 3:
FileNotFoundError = OSError
else:
FileNotFoundError = IOError
VERSION_PATTERN = r'(\d+\.\d+\.\d+)'
CANONICAL_PATTERN = (
'# Tests adapted from `?problem-specifications//canonical-data.json`? '
'@ v' + VERSION_PATTERN
)
rgx_version = re.compile(VERSION_PATTERN)
rgx_canonical = re.compile(CANONICAL_PATTERN)
DEFAULT_SPEC_PATH = os.path.join(
'..',
'problem-specifications'
)
gh = None
with open('config.json') as f:
config = json.load(f)
class CustomFormatter(
argparse.ArgumentDefaultsHelpFormatter,
argparse.RawDescriptionHelpFormatter
):
pass
class bcolors:
HEADER = '\033[95m'
OKBLUE = '\033[94m'
OKGREEN = '\033[92m'
WARNING = '\033[93m'
FAIL = '\033[91m'
ENDC = '\033[0m'
BOLD = '\033[1m'
UNDERLINE = '\033[4m'
def cjust(string, width, fillchar=' '):
while len(string) < width:
string = fillchar + string
if len(string) >= width:
break
string += fillchar
return string
def verify_spec_location(path):
with open(os.path.join(path, 'package.json')) as f:
data = json.load(f)
if data['name'] != 'problem-specifications':
raise ValueError(
'{} is not the problem-specifications directory'.format(path)
)
def get_test_file_path(exercise):
return os.path.join(
'exercises',
exercise,
exercise.replace('-', '_') + '_test.py'
)
def get_test_file_url(exercise):
return '/'.join([
'https://github.com',
'exercism',
'python',
'blob',
'master',
'exercises',
exercise,
exercise.replace('-', '_') + '_test.py'
])
def get_canonical_data_path(exercise, spec_path=DEFAULT_SPEC_PATH):
return os.path.join(
spec_path,
'exercises',
exercise,
'canonical-data.json'
)
def get_canonical_data_url(exercise):
return '/'.join([
'https://github.com',
'exercism',
'problem-specifications',
'blob',
'master',
'exercises',
exercise,
'canonical-data.json'
])
def get_referenced_version(exercise):
with open(get_test_file_path(exercise)) as f:
for line in f.readlines():
m = rgx_canonical.match(line)
if m is not None:
return m.group(1)
return '0.0.0'
def get_available_version(exercise, spec_path=DEFAULT_SPEC_PATH):
try:
with open(get_canonical_data_path(exercise, spec_path)) as f:
data = json.load(f)
m = rgx_version.match(data['version'])
return m.group(1)
except FileNotFoundError:
return '0.0.0'
def is_deprecated(exercise):
for e in config['exercises']:
if e['slug'] == exercise:
return e.get('deprecated', False)
return False
def create_issue_for_exercise(
exercise,
available_data_version,
extra_labels=None
):
title = '{}: update tests to v{}'.format(exercise, available_data_version)
body = (
'The [test suite]({}) for {} is out of date and needs updated to '
'conform to the [latest canonical data]({}).'
).format(
get_test_file_url(exercise),
exercise,
get_canonical_data_url(exercise),
)
labels = [
'beginner friendly',
'help wanted',
'enhancement',
]
if extra_labels is not None:
labels = list(set(labels + extra_labels))
issue = gh.create_issue(
'exercism',
'python',
title,
body=body,
labels=labels
)
return issue['number']
def check_test_version(
exercise,
spec=DEFAULT_SPEC_PATH,
no_color=True,
print_ok=True,
name_only=False,
has_data=False,
include_deprecated=False,
create_issue=False,
token=None,
extra_labels=None,
):
if not include_deprecated and is_deprecated(exercise):
return True
available = get_available_version(exercise, spec)
if available == '0.0.0' and has_data:
return True
referenced = get_referenced_version(exercise)
up_to_date = available == referenced
if up_to_date:
status, status_color = 'OK', bcolors.OKGREEN
else:
status, status_color = 'NOT OK', bcolors.FAIL
status = cjust(status, 8)
if not no_color:
status = status_color + status + bcolors.ENDC
if not up_to_date or print_ok:
if create_issue:
issue_number = create_issue_for_exercise(
exercise,
available,
extra_labels
)
issue_info = '(#{})'.format(issue_number)
else:
issue_info = ''
if name_only:
baseline = exercise
else:
baseline = '[{}] {}: {}{}{}'.format(
status,
exercise,
referenced,
'=' if up_to_date else '!=',
available
)
print(' '.join((baseline, issue_info)))
return up_to_date
if __name__ == '__main__':
parser = argparse.ArgumentParser(
formatter_class=CustomFormatter,
epilog=(
"Results are of the form:\n <exercise>: <referenced>!=<current>"
)
)
parser._optionals.title = 'options'
parser.add_argument(
'--version',
action='store_true',
help='Print version info.'
)
parser.add_argument(
'-o', '--only',
default='*',
metavar='<exercise>',
help='Check just the exercise specified (by the slug).',
)
parser.add_argument(
'--ignore',
action='append',
help='Check all except exercise[s] specified (by the slug).',
)
parser.add_argument(
'-p', '--spec-path',
default=DEFAULT_SPEC_PATH,
metavar='<path/to/spec>',
help='The location of the problem-specifications directory.'
)
g = parser.add_argument_group('output')
g.add_argument(
'-w', '--no-color',
action='store_true',
help='Disable colored output.'
)
g.add_argument(
'-s', '--has-data',
action='store_true',
help='Only print exercises with existing canonical data.'
)
g.add_argument(
'-d', '--include-deprecated',
action='store_true',
help='Include deprecated exercises'
)
mut_g = g.add_mutually_exclusive_group()
mut_g.add_argument(
'-v', '--verbose',
action='store_true',
help='Enable verbose output.'
)
mut_g.add_argument(
'-n', '--name-only',
action='store_true',
help='Print exercise names only.'
)
g = parser.add_argument_group('issue creation')
g.add_argument(
'--create-issue',
action='store_true',
help='Create issue for out-of-date exercises'
)
g.add_argument(
'-t', '--token',
help='GitHub personal access token (permissions: repo)'
)
g.add_argument(
'--labels',
nargs='+',
metavar='LABEL',
help=(
'additional issue labels ("beginner friendly", "enhancement", and '
'"help wanted" are always set)'
)
)
opts = parser.parse_args()
verify_spec_location(opts.spec_path)
if opts.create_issue:
if opts.token is None:
if os.path.isfile('.github.api_token'):
with open('.github.api_token') as f:
opts.token = f.read().strip()
if opts.token is not None:
gh = GitHub(api_token=opts.token)
else:
gh = GitHub()
kwargs = dict(
spec=opts.spec_path,
no_color=opts.no_color,
print_ok=opts.verbose,
name_only=opts.name_only,
has_data=opts.has_data,
create_issue=opts.create_issue,
extra_labels=opts.labels,
)
if opts.version:
print('check-test-version.py v1.1')
sys.exit(0)
result = True
for exercise in glob(os.path.join('exercises', opts.only)):
exercise = exercise.split(os.path.sep)[-1]
if opts.ignore and exercise in opts.ignore:
continue
if os.path.isdir(os.path.join('exercises', exercise)):
try:
result = check_test_version(exercise, **kwargs) and result
except FileNotFoundError as e:
print(str(e))
result = False
sys.exit(0 if result else 1)

38
bin/create_issue.py Normal file
View File

@@ -0,0 +1,38 @@
#!/usr/bin/env python
import requests
import json
# https://stackoverflow.com/a/17626704
class GitHub(object):
def __init__(self, **config_options):
self.__dict__.update(**config_options)
self.session = requests.Session()
if hasattr(self, 'api_token'):
self.session.headers['Authorization'] = 'token %s' % self.api_token
elif hasattr(self, 'username') and hasattr(self, 'password'):
self.session.auth = (self.username, self.password)
def create_issue(
self,
owner,
repo,
title,
body=None,
assignees=None,
labels=None
):
payload = dict(title=title)
if body is not None:
payload['body'] = body
if assignees is not None:
payload['assignees'] = assignees
if labels is not None:
payload['labels'] = labels
response = self.session.post(
'https://api.github.com/repos/{}/{}/issues'.format(owner, repo),
data=json.dumps(payload),
)
if response.status_code != 201:
raise ValueError('Failed to create issue: ' + response.content)
return json.loads(response.content)

File diff suppressed because it is too large Load Diff

View File

@@ -4,13 +4,52 @@
{{- with .Hints }}
{{ . }}
{{ end }}
{{- with .TrackInsert }}
{{ . }}
{{ end }}
{{- with .Spec.Credits -}}
## 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 {{ .Spec.SnakeCaseName }}_test.py`
- Python 3.4+: `pytest {{ .Spec.SnakeCaseName }}_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 {{ .Spec.SnakeCaseName }}_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/{{ .Spec.Slug }}` 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).
{{ with .Spec.Credits }}
## Source
{{ . }}
{{ end }}
## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

View File

@@ -1,45 +1,45 @@
{
"docs_url": "https://github.com/exercism/docs/blob/master/maintaining-a-track/maintainer-configuration.md",
"maintainers": [
{
"github_username": "behrtam",
"show_on_website": false,
"alumnus": false,
"show_on_website": false,
"name": null,
"bio": null,
"link_text": null,
"link_url": null,
"avatar_url": null
"avatar_url": null,
"bio": null
},
{
"github_username": "Dog",
"show_on_website": true,
"alumnus": false,
"show_on_website": true,
"name": "Dog",
"bio": "I can not only fetch JSON, but parse it too.",
"link_text": null,
"link_url": null,
"avatar_url": null
"avatar_url": null,
"bio": "I can not only fetch JSON, but parse it too."
},
{
"github_username": "m-a-ge",
"show_on_website": false,
"alumnus": false,
"show_on_website": false,
"name": null,
"bio": null,
"link_text": null,
"link_url": null,
"avatar_url": null
"avatar_url": null,
"bio": null
},
{
"github_username": "cmccandless",
"show_on_website": false,
"alumnus": false,
"name": null,
"bio": null,
"show_on_website": true,
"name": "Corey McCandless",
"link_text": null,
"link_url": null,
"avatar_url": null
"avatar_url": null,
"bio": "Big fan of homemade bread and reusable code."
}
],
"docs_url": "https://github.com/exercism/docs/blob/master/maintaining-a-track/maintainer-configuration.md"
]
}

View File

@@ -1,15 +1,15 @@
Python is a strong language for beginners.
Python is a strong language for beginners.
There are many resources available for programmers of all levels, the code is highly readable, and in many cases phrases are comparable to those in the English language.
Code can be written and executed from the command line, in an interactive IPython session, or in a [Jupyter](http://jupyter.org) (IPython) notebook.
The most common form of Python is compiled in C.
This is often invisible to the beginning programmer, but if there are uses for which exceptionally fast implementation is needed then C extensions can be written to optimize Python execution.
The most common form of Python is compiled in C; this is often invisible to the beginning programmer, but if there are uses for which exceptionally fast implementation is needed then C extensions can be written to optimize Python execution.
[Python is used extensively](https://www.python.org/about/apps/) in scientific computing, finance, games, networking, internet development, and in assembling pipelines of other programs.
Python was started by Guido van Rossum in 1989.
Its name is an homage to the comedy troupe Monty Python.
Python 2 is used widely but support [may end by 2020](https://www.python.org/dev/peps/pep-0373/#id2).
Python 3 was introduced in 2008 and is beginning to be adopted more widely.
They are similar, but users will encounter [some differences](http://blog.teamtreehouse.com/python-2-vs-python-3).
Python was started by Guido van Rossum in 1989; its name is an homage to the comedy troupe Monty Python.
Python 2 is used widely but support [may end by 2020](https://www.python.org/dev/peps/pep-0373/#id2); it is highly recommended that beginners use Python 3 (they are similar, but users will encounter [some differences](http://blog.teamtreehouse.com/python-2-vs-python-3)).
Python development is shepherded by [The Python Software Foundation](https://www.python.org/about/) and there are active community-based user groups worldwide.

View File

@@ -1,23 +0,0 @@
## 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/<exerciseName>` directory.
For example, if you're submitting `bob.py` for the Bob exercise, the submit command would be something like `exercism submit <path_to_exercism_dir>/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).

View File

@@ -2,4 +2,4 @@
If Python isn't already available on your system follow the instructions at [the Hitchhiker's Guide to Python](http://docs.python-guide.org/en/latest/starting/installation/) to install Python on your computer.
Exercism currently supports Python2.7 and Python 3.3+.
Exercism currently supports Python2.7 and Python 3.4+.

View File

@@ -1,4 +1,4 @@
# Learning Python From Scratch
## Learning Python From Scratch
Python is, as Wikipedia goes, a powerful *general-purpose high-level programming language*. It basically means that it can be used to write a wide variety of different kinds of software, from videogames to HTTP servers to command-line tools.
One of the main characteristics that differentiates Python from other programming languages is its strong emphasis on readability and code cleaness. In fact, differently from other languages like JavaScript or C++, in Python code indentation has a syntactical meaning and you are forced to chose and adhere to a writing style (e.g. don't mix *tabs* and *spaces* for identation; don't use two spaces where you should use four etc.). Yes, forced: the Python interpreter will raise SyntaxErrors if it recognize wrong indentation.

View File

@@ -1,4 +1,4 @@
# Installing `pytest`
## Installing `pytest`
We recommend you install [pytest](http://pytest.org/latest/) and
[pytest-cache](http://pythonhosted.org/pytest-cache/). `pytest` is a testing
@@ -44,9 +44,9 @@ cd exercism/python/bob
python bob_test.py
```
# Running the Tests
## Running the Tests
## Run All Tests
### Run All Tests
To run all tests for a specific exercise (we will take the `bob.py` exercise as
an example here), place yourself in the directory where that exercise has been
@@ -73,9 +73,9 @@ Ran 0 tests in 0.000s
OK
```
## More `pytest` Examples
### More `pytest` Examples
### Stop After First Failure
#### Stop After First Failure
The above will run all the tests, whether they fail or not. If you'd rather stop
the process and exit on the first failure, run:
@@ -83,7 +83,7 @@ the process and exit on the first failure, run:
pytest -x bob_test.py
```
### Failed Tests First
#### Failed Tests First
`pytest-cache` remembers which tests failed, and can run those tests first.
@@ -91,7 +91,7 @@ pytest -x bob_test.py
pytest --ff bob_test.py
```
### Running All Tests for All Exercises
#### Running All Tests for All Exercises
```bash
cd exercism/python/

View File

@@ -33,25 +33,43 @@ every exercise will require you to raise an exception, but for those that do, th
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:
`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 accumulate_test.py`
- Python 3.4+: `pytest accumulate_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 accumulate_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/python/<exerciseName>` directory.
Note that, when trying to submit an exercise, make sure the solution is in the `$EXERCISM_WORKSPACE/python/accumulate` directory.
For example, if you're submitting `bob.py` for the Bob exercise, the submit command would be something like `exercism submit <path_to_exercism_dir>/python/bob/bob.py`.
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 the [help page](http://exercism.io/languages/python).
please see [Running the Tests](http://exercism.io/tracks/python/tests).
## Source
Conversation with James Edward Gray II [https://twitter.com/jeg2](https://twitter.com/jeg2)
## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

View File

@@ -15,25 +15,43 @@ every exercise will require you to raise an exception, but for those that do, th
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:
`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 acronym_test.py`
- Python 3.4+: `pytest acronym_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 acronym_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/python/<exerciseName>` directory.
Note that, when trying to submit an exercise, make sure the solution is in the `$EXERCISM_WORKSPACE/python/acronym` directory.
For example, if you're submitting `bob.py` for the Bob exercise, the submit command would be something like `exercism submit <path_to_exercism_dir>/python/bob/bob.py`.
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 the [help page](http://exercism.io/languages/python).
please see [Running the Tests](http://exercism.io/tracks/python/tests).
## Source
Julien Vanier [https://github.com/monkbroc](https://github.com/monkbroc)
## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

View File

@@ -3,7 +3,7 @@ import unittest
from acronym import abbreviate
# Tests adapted from `problem-specifications//canonical-data.json` @ v1.1.0
# Tests adapted from `problem-specifications//canonical-data.json` @ v1.7.0
class AcronymTest(unittest.TestCase):
def test_basic(self):
@@ -16,15 +16,27 @@ class AcronymTest(unittest.TestCase):
self.assertEqual(abbreviate('First In, First Out'), 'FIFO')
def test_all_caps_words(self):
self.assertEqual(abbreviate('PHP: Hypertext Preprocessor'), 'PHP')
def test_non_acronym_all_caps_word(self):
self.assertEqual(abbreviate('GNU Image Manipulation Program'), 'GIMP')
def test_hyphenated(self):
def test_punctuation_without_whitespace(self):
self.assertEqual(
abbreviate('Complementary metal-oxide semiconductor'), 'CMOS')
def test_very_long_abbreviation(self):
self.assertEqual(
abbreviate("Rolling On The Floor Laughing So Hard That "
"My Dogs Came Over And Licked Me"), "ROTFLSHTMDCOALM")
def test_consecutive_delimiters(self):
self.assertEqual(
abbreviate('Something - I made up from thin air'), 'SIMUFTA')
def test_apostrophes(self):
self.assertEqual(abbreviate("Halley's Comet"), 'HC')
def test_underscore_emphasis(self):
self.assertEqual(abbreviate("The Road _Not_ Taken"), 'TRNT')
if __name__ == '__main__':
unittest.main()

View File

@@ -2,5 +2,5 @@ import re
def abbreviate(words):
regex = '[A-Z]+[a-z]*|[a-z]+'
regex = "[A-Z]+['a-z]*|['a-z]+"
return ''.join(word[0].upper() for word in re.findall(regex, words))

View File

@@ -0,0 +1,119 @@
# Affine Cipher
Create an implementation of the affine cipher,
an ancient encryption system created in the Middle East.
The affine cipher is a type of monoalphabetic substitution cipher.
Each character is mapped to its numeric equivalent, encrypted with
a mathematical function and then converted to the letter relating to
its new numeric value. Although all monoalphabetic ciphers are weak,
the affine cypher is much stronger than the atbash cipher,
because it has many more keys.
the encryption function is:
`E(x) = (ax + b) mod m`
- where `x` is the letter's index from 0 - length of alphabet - 1
- `m` is the length of the alphabet. For the roman alphabet `m == 26`.
- and `a` and `b` make the key
the decryption function is:
`D(y) = a^-1(y - b) mod m`
- where `y` is the numeric value of an encrypted letter, ie. `y = E(x)`
- it is important to note that `a^-1` is the modular multiplicative inverse
of `a mod m`
- the modular multiplicative inverse of `a` only exists if `a` and `m` are
coprime.
To find the MMI of `a`:
`an mod m = 1`
- where `n` is the modular multiplicative inverse of `a mod m`
More information regarding how to find a Modular Multiplicative Inverse
and what it means can be found [here.](https://en.wikipedia.org/wiki/Modular_multiplicative_inverse)
Because automatic decryption fails if `a` is not coprime to `m` your
program should return status 1 and `"Error: a and m must be coprime."`
if they are not. Otherwise it should encode or decode with the
provided key.
The Caesar (shift) cipher is a simple affine cipher where `a` is 1 and
`b` as the magnitude results in a static displacement of the letters.
This is much less secure than a full implementation of the affine cipher.
Ciphertext is written out in groups of fixed length, the traditional group
size being 5 letters, and punctuation is excluded. This is to make it
harder to guess things based on word boundaries.
## Examples
- Encoding `test` gives `ybty` with the key a=5 b=7
- Decoding `ybty` gives `test` with the key a=5 b=7
- Decoding `ybty` gives `lqul` with the wrong key a=11 b=7
- Decoding `kqlfd jzvgy tpaet icdhm rtwly kqlon ubstx`
- gives `thequickbrownfoxjumpsoverthelazydog` with the key a=19 b=13
- Encoding `test` with the key a=18 b=13
- gives `Error: a and m must be coprime.`
- because a and m are not relatively prime
### Examples of finding a Modular Multiplicative Inverse (MMI)
- simple example:
- `9 mod 26 = 9`
- `9 * 3 mod 26 = 27 mod 26 = 1`
- `3` is the MMI of `9 mod 26`
- a more complicated example:
- `15 mod 26 = 15`
- `15 * 7 mod 26 = 105 mod 26 = 1`
- `7` is the MMI of `15 mod 26`
## 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 affine_cipher_test.py`
- Python 3.4+: `pytest affine_cipher_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 affine_cipher_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/affine-cipher` 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
Wikipedia [http://en.wikipedia.org/wiki/Affine_cipher](http://en.wikipedia.org/wiki/Affine_cipher)
## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

View File

@@ -0,0 +1,6 @@
def encode(plain_text, a, b):
pass
def decode(ciphered_text, a, b):
pass

View File

@@ -0,0 +1,83 @@
import unittest
from affine_cipher import decode, encode
# Tests adapted from `problem-specifications//canonical-data.json` @ v2.0.0
class AffineCipherTest(unittest.TestCase):
def test_encode_yes(self):
self.assertEqual(encode("yes", 5, 7), "xbt")
def test_encode_no(self):
self.assertEqual(encode("no", 15, 18), "fu")
def test_encode_OMG(self):
self.assertEqual(encode("OMG", 21, 3), "lvz")
def test_encode_O_M_G(self):
self.assertEqual(encode("O M G", 25, 47), "hjp")
def test_encode_mindblowingly(self):
self.assertEqual(encode("mindblowingly", 11, 15), "rzcwa gnxzc dgt")
def test_encode_numbers(self):
self.assertEqual(encode("Testing,1 2 3, testing.", 3, 4),
"jqgjc rw123 jqgjc rw")
def test_encode_deep_thought(self):
self.assertEqual(encode("Truth is fiction.", 5, 17),
"iynia fdqfb ifje")
def test_encode_all_the_letters(self):
self.assertEqual(
encode("The quick brown fox jumps over the lazy dog.", 17, 33),
"swxtj npvyk lruol iejdc blaxk swxmh qzglf")
def test_encode_raises_meaningful_exception(self):
with self.assertRaisesWithMessage(ValueError):
encode("This is a test", 6, 17)
def test_decode_exercism(self):
self.assertEqual(decode("tytgn fjr", 3, 7), "exercism")
def test_decode_sentence(self):
self.assertEqual(
decode("qdwju nqcro muwhn odqun oppmd aunwd o", 19, 16),
"anobstacleisoftenasteppingstone")
def test_decode_numbers(self):
self.assertEqual(decode("odpoz ub123 odpoz ub", 25, 7),
"testing123testing")
def test_decode_all_the_letters(self):
self.assertEqual(
decode("swxtj npvyk lruol iejdc blaxk swxmh qzglf", 17, 33),
"thequickbrownfoxjumpsoverthelazydog")
def test_decode_with_no_spaces(self):
self.assertEqual(
decode("swxtjnpvyklruoliejdcblaxkswxmhqzglf", 17, 33),
"thequickbrownfoxjumpsoverthelazydog")
def test_decode_with_too_many_spaces(self):
self.assertEqual(
decode("vszzm cly yd cg qdp", 15, 16), "jollygreengiant")
def test_decode_raises_meaningful_exception(self):
with self.assertRaisesWithMessage(ValueError):
decode("Test", 13, 5)
# 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()

View File

@@ -0,0 +1,41 @@
BLKSZ = 5
ALPHSZ = 26
def modInverse(a, ALPHSZ):
a = a % ALPHSZ
for x in range(1, ALPHSZ):
if ((a * x) % ALPHSZ == 1):
return x
return 1
def translate(text, a, b, mode):
inv = modInverse(a, ALPHSZ)
if inv == 1:
raise ValueError("a and alphabet size must be coprime.")
chars = []
for c in text:
if c.isalnum():
orig = ord(c.lower()) - 97
if orig < 0:
chars.append(c)
continue
if mode == 0:
new = (a * orig + b) % ALPHSZ
elif mode == 1:
new = (inv * (orig - b)) % ALPHSZ
chars.append(chr(new + 97))
return ''.join(chars)
def encode(plain, a, b):
cipher = translate(plain, a, b, 0)
return " ".join([cipher[i:i + BLKSZ]
for i in range(0, len(cipher), BLKSZ)])
def decode(ciphered, a, b):
return translate(ciphered, a, b, 1)

View File

@@ -39,22 +39,39 @@ every exercise will require you to raise an exception, but for those that do, th
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:
`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 all_your_base_test.py`
- Python 3.4+: `pytest all_your_base_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 all_your_base_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/python/<exerciseName>` directory.
Note that, when trying to submit an exercise, make sure the solution is in the `$EXERCISM_WORKSPACE/python/all-your-base` directory.
For example, if you're submitting `bob.py` for the Bob exercise, the submit command would be something like `exercism submit <path_to_exercism_dir>/python/bob/bob.py`.
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 the [help page](http://exercism.io/languages/python).
please see [Running the Tests](http://exercism.io/tracks/python/tests).
## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

View File

@@ -1,2 +1,2 @@
def rebase(from_base, digits, to_base):
def rebase(input_base, digits, output_base):
pass

View File

@@ -3,9 +3,9 @@ import unittest
from all_your_base import rebase
# Tests adapted from `problem-specifications//canonical-data.json` @ v1.1.0
# Tests adapted from `problem-specifications//canonical-data.json` @ v2.3.0
class AllYourBaseTests(unittest.TestCase):
class AllYourBaseTest(unittest.TestCase):
def test_single_bit_to_one_decimal(self):
self.assertEqual(rebase(2, [1], 10), [1])
@@ -43,15 +43,15 @@ class AllYourBaseTests(unittest.TestCase):
def test_leading_zeros(self):
self.assertEqual(rebase(7, [0, 6, 0], 10), [4, 2])
def test_first_base_is_one(self):
def test_input_base_is_one(self):
with self.assertRaisesWithMessage(ValueError):
rebase(1, [], 10)
rebase(1, [0], 10)
def test_first_base_is_zero(self):
def test_input_base_is_zero(self):
with self.assertRaisesWithMessage(ValueError):
rebase(0, [], 10)
def test_first_base_is_negative(self):
def test_input_base_is_negative(self):
with self.assertRaisesWithMessage(ValueError):
rebase(-2, [1], 10)
@@ -63,15 +63,15 @@ class AllYourBaseTests(unittest.TestCase):
with self.assertRaisesWithMessage(ValueError):
rebase(2, [1, 2, 1, 0, 1, 0], 10)
def test_second_base_is_one(self):
def test_output_base_is_one(self):
with self.assertRaisesWithMessage(ValueError):
rebase(2, [1, 0, 1, 0, 1, 0], 1)
def test_second_base_is_zero(self):
def test_output_base_is_zero(self):
with self.assertRaisesWithMessage(ValueError):
rebase(10, [7], 0)
def test_second_base_is_negative(self):
def test_output_base_is_negative(self):
with self.assertRaisesWithMessage(ValueError):
rebase(2, [1], -7)

View File

@@ -37,25 +37,43 @@ every exercise will require you to raise an exception, but for those that do, th
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:
`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 allergies_test.py`
- Python 3.4+: `pytest allergies_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 allergies_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/python/<exerciseName>` directory.
Note that, when trying to submit an exercise, make sure the solution is in the `$EXERCISM_WORKSPACE/python/allergies` directory.
For example, if you're submitting `bob.py` for the Bob exercise, the submit command would be something like `exercism submit <path_to_exercism_dir>/python/bob/bob.py`.
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 the [help page](http://exercism.io/languages/python).
please see [Running the Tests](http://exercism.io/tracks/python/tests).
## Source
Jumpstart Lab Warm-up [http://jumpstartlab.com](http://jumpstartlab.com)
## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

View File

@@ -7,9 +7,9 @@ if not hasattr(unittest.TestCase, 'assertCountEqual'):
unittest.TestCase.assertCountEqual = unittest.TestCase.assertItemsEqual
# Tests adapted from `problem-specifications//canonical-data.json` @ v1.0.0
# Tests adapted from `problem-specifications//canonical-data.json` @ v1.2.0
class AllergiesTests(unittest.TestCase):
class AllergiesTest(unittest.TestCase):
def test_no_allergies_means_not_allergic(self):
allergies = Allergies(0)
self.assertIs(allergies.is_allergic_to('peanuts'), False)
@@ -25,6 +25,13 @@ class AllergiesTests(unittest.TestCase):
self.assertIs(allergies.is_allergic_to('shellfish'), True)
self.assertIs(allergies.is_allergic_to('strawberries'), False)
def test_allergic_to_strawberries_but_not_peanuts(self):
allergies = Allergies(9)
self.assertIs(allergies.is_allergic_to('eggs'), True)
self.assertIs(allergies.is_allergic_to('peanuts'), False)
self.assertIs(allergies.is_allergic_to('shellfish'), False)
self.assertIs(allergies.is_allergic_to('strawberries'), True)
def test_no_allergies_at_all(self):
self.assertEqual(Allergies(0).lst, [])
@@ -55,9 +62,6 @@ class AllergiesTests(unittest.TestCase):
'chocolate', 'pollen', 'cats'
])
def test_ignore_non_allergen_score_parts_only_eggs(self):
self.assertEqual(Allergies(257).lst, ['eggs'])
def test_ignore_non_allergen_score_parts(self):
self.assertCountEqual(
Allergies(509).lst, [

View File

@@ -39,22 +39,39 @@ every exercise will require you to raise an exception, but for those that do, th
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:
`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 alphametics_test.py`
- Python 3.4+: `pytest alphametics_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 alphametics_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/python/<exerciseName>` directory.
Note that, when trying to submit an exercise, make sure the solution is in the `$EXERCISM_WORKSPACE/python/alphametics` directory.
For example, if you're submitting `bob.py` for the Bob exercise, the submit command would be something like `exercism submit <path_to_exercism_dir>/python/bob/bob.py`.
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 the [help page](http://exercism.io/languages/python).
please see [Running the Tests](http://exercism.io/tracks/python/tests).
## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

View File

@@ -3,23 +3,30 @@ import unittest
from alphametics import solve
# Tests adapted from `problem-specifications//canonical-data.json` @ v1.0.0
# Tests adapted from `problem-specifications//canonical-data.json` @ v1.3.0
class TestAlphametics(unittest.TestCase):
def test_puzzle_with_03_letters(self):
class AlphameticsTest(unittest.TestCase):
def test_puzzle_with_three_letters(self):
self.assertEqual(solve("I + BB == ILL"), {"I": 1, "B": 9, "L": 0})
def test_invalid_solution_must_have_unique_value_for_each_letter(self):
def test_solution_must_have_unique_value_for_each_letter(self):
self.assertEqual(solve("A == B"), {})
def test_invalid_leading_zero_solution(self):
def test_leading_zero_solution_is_invalid(self):
self.assertEqual(solve("ACA + DD == BD"), {})
def test_puzzle_with_04_letters(self):
def test_puzzle_with_two_digits_final_carry(self):
self.assertEqual(
solve("A + A + A + A + A + A + A + A + A + A + A + B == BCC"),
{"A": 9,
"B": 1,
"C": 0})
def test_puzzle_with_four_letters(self):
self.assertEqual(
solve("AS + A == MOM"), {"A": 9, "S": 2, "M": 1, "O": 0})
def test_puzzle_with_06_letters(self):
def test_puzzle_with_six_letters(self):
self.assertEqual(
solve("NO + NO + TOO == LATE"),
{"N": 7,
@@ -29,7 +36,7 @@ class TestAlphametics(unittest.TestCase):
"A": 0,
"E": 2})
def test_puzzle_with_07_letters(self):
def test_puzzle_with_seven_letters(self):
self.assertEqual(
solve("HE + SEES + THE == LIGHT"),
{"E": 4,
@@ -40,7 +47,7 @@ class TestAlphametics(unittest.TestCase):
"S": 9,
"T": 7})
def test_puzzle_with_08_letters(self):
def test_puzzle_with_eight_letters(self):
self.assertEqual(
solve("SEND + MORE == MONEY"),
{"S": 9,
@@ -52,7 +59,7 @@ class TestAlphametics(unittest.TestCase):
"R": 8,
"Y": 2})
def test_puzzle_with_10_letters(self):
def test_puzzle_with_ten_letters(self):
self.assertEqual(
solve("AND + A + STRONG + OFFENSE + AS + A + GOOD == DEFENSE"),
{"A": 5,
@@ -66,6 +73,47 @@ class TestAlphametics(unittest.TestCase):
"S": 6,
"T": 9})
@unittest.skip("extra-credit")
def test_puzzle_with_ten_letters_and_199_addends(self):
self.assertEqual(
solve(
"THIS + A + FIRE + THEREFORE + FOR + ALL + HISTORIES + I + "
"TELL + A + TALE + THAT + FALSIFIES + ITS + TITLE + TIS + "
"A + LIE + THE + TALE + OF + THE + LAST + FIRE + HORSES + "
"LATE + AFTER + THE + FIRST + FATHERS + FORESEE + THE + "
"HORRORS + THE + LAST + FREE + TROLL + TERRIFIES + THE + "
"HORSES + OF + FIRE + THE + TROLL + RESTS + AT + THE + "
"HOLE + OF + LOSSES + IT + IS + THERE + THAT + SHE + STORES + "
"ROLES + OF + LEATHERS + AFTER + SHE + SATISFIES + HER + "
"HATE + OFF + THOSE + FEARS + A + TASTE + RISES + AS + SHE + "
"HEARS + THE + LEAST + FAR + HORSE + THOSE + FAST + HORSES + "
"THAT + FIRST + HEAR + THE + TROLL + FLEE + OFF + TO + THE + "
"FOREST + THE + HORSES + THAT + ALERTS + RAISE + THE + "
"STARES + OF + THE + OTHERS + AS + THE + TROLL + ASSAILS + "
"AT + THE + TOTAL + SHIFT + HER + TEETH + TEAR + HOOF + OFF + "
"TORSO + AS + THE + LAST + HORSE + FORFEITS + ITS + LIFE + "
"THE + FIRST + FATHERS + HEAR + OF + THE + HORRORS + THEIR + "
"FEARS + THAT + THE + FIRES + FOR + THEIR + FEASTS + ARREST + "
"AS + THE + FIRST + FATHERS + RESETTLE + THE + LAST + OF + "
"THE + FIRE + HORSES + THE + LAST + TROLL + HARASSES + THE + "
"FOREST + HEART + FREE + AT + LAST + OF + THE + LAST + "
"TROLL + ALL + OFFER + THEIR + FIRE + HEAT + TO + THE + "
"ASSISTERS + FAR + OFF + THE + TROLL + FASTS + ITS + LIFE + "
"SHORTER + AS + STARS + RISE + THE + HORSES + REST + SAFE + "
"AFTER + ALL + SHARE + HOT + FISH + AS + THEIR + AFFILIATES + "
"TAILOR + A + ROOFS + FOR + THEIR + SAFE == FORTRESSES"
),
{"A": 1,
"E": 0,
"F": 5,
"H": 8,
"I": 7,
"L": 2,
"O": 6,
"R": 3,
"S": 4,
"T": 9})
if __name__ == '__main__':
unittest.main()

View File

@@ -14,25 +14,43 @@ every exercise will require you to raise an exception, but for those that do, th
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:
`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 anagram_test.py`
- Python 3.4+: `pytest anagram_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 anagram_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/python/<exerciseName>` directory.
Note that, when trying to submit an exercise, make sure the solution is in the `$EXERCISM_WORKSPACE/python/anagram` directory.
For example, if you're submitting `bob.py` for the Bob exercise, the submit command would be something like `exercism submit <path_to_exercism_dir>/python/bob/bob.py`.
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 the [help page](http://exercism.io/languages/python).
please see [Running the Tests](http://exercism.io/tracks/python/tests).
## Source
Inspired by the Extreme Startup game [https://github.com/rchatley/extreme_startup](https://github.com/rchatley/extreme_startup)
## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

View File

@@ -1,2 +1,2 @@
def detect_anagrams(word, candidates):
def find_anagrams(word, candidates):
pass

View File

@@ -1,75 +1,62 @@
import unittest
from anagram import detect_anagrams
from anagram import find_anagrams
# Tests adapted from `problem-specifications//canonical-data.json` @ v1.0.1
# Tests adapted from `problem-specifications//canonical-data.json` @ v1.4.0
class AnagramTests(unittest.TestCase):
class AnagramTest(unittest.TestCase):
def test_no_matches(self):
candidates = ["hello", "world", "zombies", "pants"]
self.assertEqual(detect_anagrams("diaper", candidates), [])
def test_detects_simple_anagram(self):
candidates = ["tan", "stand", "at"]
self.assertEqual(detect_anagrams("ant", candidates), ["tan"])
def test_does_not_detect_false_positives(self):
self.assertEqual(detect_anagrams("galea", ["eagle"]), [])
self.assertEqual(find_anagrams("diaper", candidates), [])
def test_detects_two_anagrams(self):
candidates = ["stream", "pigeon", "maters"]
self.assertEqual(
detect_anagrams("master", candidates), ["stream", "maters"])
find_anagrams("master", candidates), ["stream", "maters"])
def test_does_not_detect_anagram_subsets(self):
self.assertEqual(detect_anagrams("good", ["dog", "goody"]), [])
self.assertEqual(find_anagrams("good", ["dog", "goody"]), [])
def test_detects_anagram(self):
candidates = ["enlists", "google", "inlets", "banana"]
self.assertEqual(detect_anagrams("listen", candidates), ["inlets"])
self.assertEqual(find_anagrams("listen", candidates), ["inlets"])
def test_detects_three_anagrams(self):
candidates = [
"gallery", "ballerina", "regally", "clergy", "largely", "leading"
]
self.assertEqual(
detect_anagrams("allergy", candidates),
find_anagrams("allergy", candidates),
["gallery", "regally", "largely"])
def test_does_not_detect_identical_words(self):
candidates = ["corn", "dark", "Corn", "rank", "CORN", "cron", "park"]
self.assertEqual(detect_anagrams("corn", candidates), ["cron"])
def test_does_not_detect_non_anagrams_with_identical_checksum(self):
self.assertEqual(detect_anagrams("mass", ["last"]), [])
self.assertEqual(find_anagrams("mass", ["last"]), [])
def test_detects_anagrams_case_insensitively(self):
candidates = ["cashregister", "Carthorse", "radishes"]
self.assertEqual(
detect_anagrams("Orchestra", candidates), ["Carthorse"])
find_anagrams("Orchestra", candidates), ["Carthorse"])
def test_detects_anagrams_using_case_insensitive_subjec(self):
def test_detects_anagrams_using_case_insensitive_subject(self):
candidates = ["cashregister", "carthorse", "radishes"]
self.assertEqual(
detect_anagrams("Orchestra", candidates), ["carthorse"])
find_anagrams("Orchestra", candidates), ["carthorse"])
def test_detects_anagrams_using_case_insensitive_possible_matches(self):
candidates = ["cashregister", "Carthorse", "radishes"]
self.assertEqual(
detect_anagrams("orchestra", candidates), ["Carthorse"])
def test_does_not_detect_a_word_as_its_own_anagram(self):
self.assertEqual(detect_anagrams("banana", ["Banana"]), [])
find_anagrams("orchestra", candidates), ["Carthorse"])
def test_does_not_detect_a_anagram_if_the_original_word_is_repeated(self):
self.assertEqual(detect_anagrams("go", ["go Go GO"]), [])
self.assertEqual(find_anagrams("go", ["go Go GO"]), [])
def test_anagrams_must_use_all_letters_exactly_once(self):
self.assertEqual(detect_anagrams("tapper", ["patter"]), [])
self.assertEqual(find_anagrams("tapper", ["patter"]), [])
def test_capital_word_is_not_own_anagram(self):
self.assertEqual(detect_anagrams("BANANA", ["Banana"]), [])
def test_words_are_not_anagrams_of_themselves_case_insensitive(self):
candidates = ["BANANA", "Banana", "banana"]
self.assertEqual(find_anagrams("BANANA", candidates), [])
if __name__ == '__main__':

View File

@@ -1,4 +1,4 @@
def detect_anagrams(word, candidates):
def find_anagrams(word, candidates):
return [candidate
for candidate in candidates
if _letters(candidate) == _letters(word)

View File

@@ -5,24 +5,57 @@ An [Armstrong number](https://en.wikipedia.org/wiki/Narcissistic_number) is a nu
For example:
- 9 is an Armstrong number, because `9 = 9^1 = 9`
- 10 is *not* an Armstrong number, because `10 != 1^2 + 0^2 = 2`
- 10 is *not* an Armstrong number, because `10 != 1^2 + 0^2 = 1`
- 153 is an Armstrong number, because: `153 = 1^3 + 5^3 + 3^3 = 1 + 125 + 27 = 153`
- 154 is *not* an Armstrong number, because: `154 != 1^3 + 5^3 + 4^3 = 1 + 125 + 64 = 190`
Write some code to determine whether a number is an Armstrong number.
## 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 armstrong_numbers_test.py`
- Python 3.4+: `pytest armstrong_numbers_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 armstrong_numbers_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/python/<exerciseName>` directory.
Note that, when trying to submit an exercise, make sure the solution is in the `$EXERCISM_WORKSPACE/python/armstrong-numbers` directory.
For example, if you're submitting `bob.py` for the Bob exercise, the submit command would be something like `exercism submit <path_to_exercism_dir>/python/bob/bob.py`.
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 the [help page](http://exercism.io/languages/python).
please see [Running the Tests](http://exercism.io/tracks/python/tests).
## Source
Wikipedia [https://en.wikipedia.org/wiki/Narcissistic_number](https://en.wikipedia.org/wiki/Narcissistic_number)
## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

View File

@@ -5,7 +5,7 @@ from armstrong_numbers import is_armstrong
# Tests adapted from `problem-specifications//canonical-data.json` @ v1.0.0
class ArmstrongTests(unittest.TestCase):
class ArmstrongNumbersTest(unittest.TestCase):
def test_single_digit_numbers_are_armstrong_numbers(self):
self.assertIs(is_armstrong(5), True)

View File

@@ -36,25 +36,43 @@ every exercise will require you to raise an exception, but for those that do, th
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:
`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 atbash_cipher_test.py`
- Python 3.4+: `pytest atbash_cipher_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 atbash_cipher_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/python/<exerciseName>` directory.
Note that, when trying to submit an exercise, make sure the solution is in the `$EXERCISM_WORKSPACE/python/atbash-cipher` directory.
For example, if you're submitting `bob.py` for the Bob exercise, the submit command would be something like `exercism submit <path_to_exercism_dir>/python/bob/bob.py`.
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 the [help page](http://exercism.io/languages/python).
please see [Running the Tests](http://exercism.io/tracks/python/tests).
## Source
Wikipedia [http://en.wikipedia.org/wiki/Atbash](http://en.wikipedia.org/wiki/Atbash)
## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

View File

@@ -3,7 +3,7 @@ import unittest
from atbash_cipher import decode, encode
# Tests adapted from `problem-specifications//canonical-data.json` @ v1.0.0
# Tests adapted from `problem-specifications//canonical-data.json` @ v1.2.0
class AtbashCipherTest(unittest.TestCase):
def test_encode_no(self):
@@ -51,6 +51,14 @@ class AtbashCipherTest(unittest.TestCase):
plaintext = "thequickbrownfoxjumpsoverthelazydog"
self.assertMultiLineEqual(decode(ciphertext), plaintext)
def test_decode_with_too_many_spaces(self):
self.assertMultiLineEqual(decode("vc vix r hn"), "exercism")
def test_decode_with_no_spaces(self):
ciphertext = "zmlyhgzxovrhlugvmzhgvkkrmthglmv"
plaintext = "anobstacleisoftenasteppingstone"
self.assertMultiLineEqual(decode(ciphertext), plaintext)
# additional track specific test
def test_encode_decode(self):
self.assertMultiLineEqual(

View File

@@ -0,0 +1,72 @@
# Bank Account
Simulate a bank account supporting opening/closing, withdrawals, and deposits
of money. Watch out for concurrent transactions!
A bank account can be accessed in multiple ways. Clients can make
deposits and withdrawals using the internet, mobile phones, etc. Shops
can charge against the account.
Create an account that can be accessed from multiple threads/processes
(terminology depends on your programming language).
It should be possible to close an account; operations against a closed
account must fail.
## Instructions
Run the test file, and fix each of the errors in turn. When you get the
first test to pass, go to the first pending or skipped test, and make
that pass as well. When all of the tests are passing, feel free to
submit.
Remember that passing code is just the first step. The goal is to work
towards a solution that is as readable and expressive as you can make
it.
Have fun!
## 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 bank_account_test.py`
- Python 3.4+: `pytest bank_account_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 bank_account_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/bank-account` 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).
## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

View File

@@ -0,0 +1,18 @@
class BankAccount(object):
def __init__(self):
pass
def get_balance(self):
pass
def open(self):
pass
def deposit(self, amount):
pass
def withdraw(self, amount):
pass
def close(self):
pass

View File

@@ -0,0 +1,153 @@
import sys
import threading
import time
import unittest
from bank_account import BankAccount
class BankAccountTest(unittest.TestCase):
def test_newly_opened_account_has_zero_balance(self):
account = BankAccount()
account.open()
self.assertEqual(account.get_balance(), 0)
def test_can_deposit_money(self):
account = BankAccount()
account.open()
account.deposit(100)
self.assertEqual(account.get_balance(), 100)
def test_can_deposit_money_sequentially(self):
account = BankAccount()
account.open()
account.deposit(100)
account.deposit(50)
self.assertEqual(account.get_balance(), 150)
def test_can_withdraw_money(self):
account = BankAccount()
account.open()
account.deposit(100)
account.withdraw(50)
self.assertEqual(account.get_balance(), 50)
def test_can_withdraw_money_sequentially(self):
account = BankAccount()
account.open()
account.deposit(100)
account.withdraw(20)
account.withdraw(80)
self.assertEqual(account.get_balance(), 0)
def test_checking_balance_of_closed_account_throws_error(self):
account = BankAccount()
account.open()
account.close()
with self.assertRaisesWithMessage(ValueError):
account.get_balance()
def test_deposit_into_closed_account(self):
account = BankAccount()
account.open()
account.close()
with self.assertRaisesWithMessage(ValueError):
account.deposit(50)
def test_withdraw_from_closed_account(self):
account = BankAccount()
account.open()
account.close()
with self.assertRaisesWithMessage(ValueError):
account.withdraw(50)
def test_close_already_closed_account(self):
account = BankAccount()
with self.assertRaisesWithMessage(ValueError):
account.close()
def test_open_already_opened_account(self):
account = BankAccount()
account.open()
with self.assertRaisesWithMessage(ValueError):
account.open()
def test_reopened_account_does_not_retain_balance(self):
account = BankAccount()
account.open()
account.deposit(50)
account.close()
account.open()
self.assertEqual(account.get_balance(), 0)
def test_cannot_withdraw_more_than_deposited(self):
account = BankAccount()
account.open()
account.deposit(25)
with self.assertRaises(ValueError):
account.withdraw(50)
def test_cannot_withdraw_negative(self):
account = BankAccount()
account.open()
account.deposit(100)
with self.assertRaisesWithMessage(ValueError):
account.withdraw(-50)
def test_cannot_deposit_negative(self):
account = BankAccount()
account.open()
with self.assertRaisesWithMessage(ValueError):
account.deposit(-50)
def test_can_handle_concurrent_transactions(self):
account = BankAccount()
account.open()
account.deposit(1000)
self.adjust_balance_concurrently(account)
self.assertEqual(account.get_balance(), 1000)
def adjust_balance_concurrently(self, account):
def transact():
account.deposit(5)
time.sleep(0.001)
account.withdraw(5)
# Greatly improve the chance of an operation being interrupted
# by thread switch, thus testing synchronization effectively
try:
sys.setswitchinterval(1e-12)
except AttributeError:
# For Python 2 compatibility
sys.setcheckinterval(1)
threads = [threading.Thread(target=transact) for _ in range(1000)]
for thread in threads:
thread.start()
for thread in threads:
thread.join()
# 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()

View File

@@ -0,0 +1,43 @@
import threading
class BankAccount(object):
def __init__(self):
self.is_open = False
self.balance = 0
self.lock = threading.Lock()
def get_balance(self):
with self.lock:
if not self.is_open:
raise ValueError('account not open')
return self.balance
def open(self):
if self.is_open:
raise ValueError('account already open')
self.is_open = True
self.balance = 0
def deposit(self, amount):
with self.lock:
if not self.is_open:
raise ValueError('account not open')
if amount <= 0:
raise ValueError('amount must be greater than 0')
self.balance += amount
def withdraw(self, amount):
with self.lock:
if not self.is_open:
raise ValueError('account not open')
if amount <= 0:
raise ValueError('amount must be greater than 0')
if amount > self.balance:
raise ValueError('amount must be less than balance')
self.balance -= amount
def close(self):
if not self.is_open:
raise ValueError('account not open')
self.is_open = False

View File

@@ -328,25 +328,43 @@ every exercise will require you to raise an exception, but for those that do, th
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:
`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 beer_song_test.py`
- Python 3.4+: `pytest beer_song_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 beer_song_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/python/<exerciseName>` directory.
Note that, when trying to submit an exercise, make sure the solution is in the `$EXERCISM_WORKSPACE/python/beer-song` directory.
For example, if you're submitting `bob.py` for the Bob exercise, the submit command would be something like `exercism submit <path_to_exercism_dir>/python/bob/bob.py`.
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 the [help page](http://exercism.io/languages/python).
please see [Running the Tests](http://exercism.io/tracks/python/tests).
## Source
Learn to Program by Chris Pine [http://pine.fm/LearnToProgram/?Chapter=06](http://pine.fm/LearnToProgram/?Chapter=06)
## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

View File

@@ -1,6 +1,2 @@
def verse(number):
pass
def song(number1, number2=0):
def recite(start, take=1):
pass

View File

@@ -1,70 +1,389 @@
import unittest
from beer_song import song, verse
from beer_song import recite
class BeerTest(unittest.TestCase):
def test_a_verse(self):
self.assertEqual(
verse(8),
"8 bottles of beer on the wall, 8 bottles of beer.\n"
"Take one down and pass it around, "
"7 bottles of beer on the wall.\n"
)
# Tests adapted from `problem-specifications//canonical-data.json` @ v2.1.0
def test_verse_1(self):
self.assertEqual(
verse(1),
"1 bottle of beer on the wall, 1 bottle of beer.\n"
"Take it down and pass it around, "
"no more bottles of beer on the wall.\n"
)
class BeerSongTest(unittest.TestCase):
def test_first_generic_verse(self):
expected = [
"99 bottles of beer on the wall, 99 bottles of beer.",
"Take one down and pass it around, 98 bottles of beer on the wall."
]
self.assertEqual(recite(start=99), expected)
def test_verse_2(self):
self.assertEqual(
verse(2),
"2 bottles of beer on the wall, 2 bottles of beer.\n"
"Take one down and pass it around, 1 bottle of beer on the wall.\n"
)
def test_last_generic_verse(self):
expected = [
"3 bottles of beer on the wall, 3 bottles of beer.",
"Take one down and pass it around, 2 bottles of beer on the wall."
]
self.assertEqual(recite(start=3), expected)
def test_verse_0(self):
self.assertEqual(
verse(0),
"No more bottles of beer on the wall, no more bottles of beer.\n"
"Go to the store and buy some more, "
"99 bottles of beer on the wall.\n"
)
def test_verse_with_two_bottles(self):
expected = [
"2 bottles of beer on the wall, 2 bottles of beer.",
"Take one down and pass it around, 1 bottle of beer on the wall."
]
self.assertEqual(recite(start=2), expected)
def test_songing_several_verses(self):
self.assertEqual(
song(8, 6),
"8 bottles of beer on the wall, 8 bottles of beer.\n"
"Take one down and pass it around, "
"7 bottles of beer on the wall.\n\n"
"7 bottles of beer on the wall, 7 bottles of beer.\n"
"Take one down and pass it around, "
"6 bottles of beer on the wall.\n\n"
"6 bottles of beer on the wall, 6 bottles of beer.\n"
"Take one down and pass it around, "
"5 bottles of beer on the wall.\n\n"
)
def test_verse_with_one_bottle(self):
expected = [
"1 bottle of beer on the wall, 1 bottle of beer.",
(
"Take it down and pass it around, "
"no more bottles of beer on the wall."
)
]
self.assertEqual(recite(start=1), expected)
def test_song_all_the_rest_of_the_verses(self):
self.assertEqual(
song(3),
"3 bottles of beer on the wall, 3 bottles of beer.\n"
"Take one down and pass it around, "
"2 bottles of beer on the wall.\n\n"
"2 bottles of beer on the wall, 2 bottles of beer.\n"
"Take one down and pass it around, "
"1 bottle of beer on the wall.\n\n"
"1 bottle of beer on the wall, 1 bottle of beer.\n"
"Take it down and pass it around, "
"no more bottles of beer on the wall.\n\n"
"No more bottles of beer on the wall, no more bottles of beer.\n"
"Go to the store and buy some more, "
"99 bottles of beer on the wall.\n\n"
)
def test_verse_with_zero_bottles(self):
expected = [
"No more bottles of beer on the wall, no more bottles of beer.",
(
"Go to the store and buy some more, "
"99 bottles of beer on the wall."
)
]
self.assertEqual(recite(start=0), expected)
def test_first_two_verses(self):
expected = [
"99 bottles of beer on the wall, 99 bottles of beer.",
(
"Take one down and pass it around, "
"98 bottles of beer on the wall."
),
"",
"98 bottles of beer on the wall, 98 bottles of beer.",
"Take one down and pass it around, 97 bottles of beer on the wall."
]
self.assertEqual(recite(start=99, take=2), expected)
def test_last_three_verses(self):
expected = [
"2 bottles of beer on the wall, 2 bottles of beer.",
"Take one down and pass it around, 1 bottle of beer on the wall.",
"",
"1 bottle of beer on the wall, 1 bottle of beer.",
(
"Take it down and pass it around, "
"no more bottles of beer on the wall."
),
"",
"No more bottles of beer on the wall, no more bottles of beer.",
(
"Go to the store and buy some more, "
"99 bottles of beer on the wall."
)
]
self.assertEqual(recite(start=2, take=3), expected)
def test_all_verses(self):
self.assertEqual(recite(start=99, take=100), SONG)
SONG = [
"99 bottles of beer on the wall, 99 bottles of beer.",
"Take one down and pass it around, 98 bottles of beer on the wall.",
"",
"98 bottles of beer on the wall, 98 bottles of beer.",
"Take one down and pass it around, 97 bottles of beer on the wall.",
"",
"97 bottles of beer on the wall, 97 bottles of beer.",
"Take one down and pass it around, 96 bottles of beer on the wall.",
"",
"96 bottles of beer on the wall, 96 bottles of beer.",
"Take one down and pass it around, 95 bottles of beer on the wall.",
"",
"95 bottles of beer on the wall, 95 bottles of beer.",
"Take one down and pass it around, 94 bottles of beer on the wall.",
"",
"94 bottles of beer on the wall, 94 bottles of beer.",
"Take one down and pass it around, 93 bottles of beer on the wall.",
"",
"93 bottles of beer on the wall, 93 bottles of beer.",
"Take one down and pass it around, 92 bottles of beer on the wall.",
"",
"92 bottles of beer on the wall, 92 bottles of beer.",
"Take one down and pass it around, 91 bottles of beer on the wall.",
"",
"91 bottles of beer on the wall, 91 bottles of beer.",
"Take one down and pass it around, 90 bottles of beer on the wall.",
"",
"90 bottles of beer on the wall, 90 bottles of beer.",
"Take one down and pass it around, 89 bottles of beer on the wall.",
"",
"89 bottles of beer on the wall, 89 bottles of beer.",
"Take one down and pass it around, 88 bottles of beer on the wall.",
"",
"88 bottles of beer on the wall, 88 bottles of beer.",
"Take one down and pass it around, 87 bottles of beer on the wall.",
"",
"87 bottles of beer on the wall, 87 bottles of beer.",
"Take one down and pass it around, 86 bottles of beer on the wall.",
"",
"86 bottles of beer on the wall, 86 bottles of beer.",
"Take one down and pass it around, 85 bottles of beer on the wall.",
"",
"85 bottles of beer on the wall, 85 bottles of beer.",
"Take one down and pass it around, 84 bottles of beer on the wall.",
"",
"84 bottles of beer on the wall, 84 bottles of beer.",
"Take one down and pass it around, 83 bottles of beer on the wall.",
"",
"83 bottles of beer on the wall, 83 bottles of beer.",
"Take one down and pass it around, 82 bottles of beer on the wall.",
"",
"82 bottles of beer on the wall, 82 bottles of beer.",
"Take one down and pass it around, 81 bottles of beer on the wall.",
"",
"81 bottles of beer on the wall, 81 bottles of beer.",
"Take one down and pass it around, 80 bottles of beer on the wall.",
"",
"80 bottles of beer on the wall, 80 bottles of beer.",
"Take one down and pass it around, 79 bottles of beer on the wall.",
"",
"79 bottles of beer on the wall, 79 bottles of beer.",
"Take one down and pass it around, 78 bottles of beer on the wall.",
"",
"78 bottles of beer on the wall, 78 bottles of beer.",
"Take one down and pass it around, 77 bottles of beer on the wall.",
"",
"77 bottles of beer on the wall, 77 bottles of beer.",
"Take one down and pass it around, 76 bottles of beer on the wall.",
"",
"76 bottles of beer on the wall, 76 bottles of beer.",
"Take one down and pass it around, 75 bottles of beer on the wall.",
"",
"75 bottles of beer on the wall, 75 bottles of beer.",
"Take one down and pass it around, 74 bottles of beer on the wall.",
"",
"74 bottles of beer on the wall, 74 bottles of beer.",
"Take one down and pass it around, 73 bottles of beer on the wall.",
"",
"73 bottles of beer on the wall, 73 bottles of beer.",
"Take one down and pass it around, 72 bottles of beer on the wall.",
"",
"72 bottles of beer on the wall, 72 bottles of beer.",
"Take one down and pass it around, 71 bottles of beer on the wall.",
"",
"71 bottles of beer on the wall, 71 bottles of beer.",
"Take one down and pass it around, 70 bottles of beer on the wall.",
"",
"70 bottles of beer on the wall, 70 bottles of beer.",
"Take one down and pass it around, 69 bottles of beer on the wall.",
"",
"69 bottles of beer on the wall, 69 bottles of beer.",
"Take one down and pass it around, 68 bottles of beer on the wall.",
"",
"68 bottles of beer on the wall, 68 bottles of beer.",
"Take one down and pass it around, 67 bottles of beer on the wall.",
"",
"67 bottles of beer on the wall, 67 bottles of beer.",
"Take one down and pass it around, 66 bottles of beer on the wall.",
"",
"66 bottles of beer on the wall, 66 bottles of beer.",
"Take one down and pass it around, 65 bottles of beer on the wall.",
"",
"65 bottles of beer on the wall, 65 bottles of beer.",
"Take one down and pass it around, 64 bottles of beer on the wall.",
"",
"64 bottles of beer on the wall, 64 bottles of beer.",
"Take one down and pass it around, 63 bottles of beer on the wall.",
"",
"63 bottles of beer on the wall, 63 bottles of beer.",
"Take one down and pass it around, 62 bottles of beer on the wall.",
"",
"62 bottles of beer on the wall, 62 bottles of beer.",
"Take one down and pass it around, 61 bottles of beer on the wall.",
"",
"61 bottles of beer on the wall, 61 bottles of beer.",
"Take one down and pass it around, 60 bottles of beer on the wall.",
"",
"60 bottles of beer on the wall, 60 bottles of beer.",
"Take one down and pass it around, 59 bottles of beer on the wall.",
"",
"59 bottles of beer on the wall, 59 bottles of beer.",
"Take one down and pass it around, 58 bottles of beer on the wall.",
"",
"58 bottles of beer on the wall, 58 bottles of beer.",
"Take one down and pass it around, 57 bottles of beer on the wall.",
"",
"57 bottles of beer on the wall, 57 bottles of beer.",
"Take one down and pass it around, 56 bottles of beer on the wall.",
"",
"56 bottles of beer on the wall, 56 bottles of beer.",
"Take one down and pass it around, 55 bottles of beer on the wall.",
"",
"55 bottles of beer on the wall, 55 bottles of beer.",
"Take one down and pass it around, 54 bottles of beer on the wall.",
"",
"54 bottles of beer on the wall, 54 bottles of beer.",
"Take one down and pass it around, 53 bottles of beer on the wall.",
"",
"53 bottles of beer on the wall, 53 bottles of beer.",
"Take one down and pass it around, 52 bottles of beer on the wall.",
"",
"52 bottles of beer on the wall, 52 bottles of beer.",
"Take one down and pass it around, 51 bottles of beer on the wall.",
"",
"51 bottles of beer on the wall, 51 bottles of beer.",
"Take one down and pass it around, 50 bottles of beer on the wall.",
"",
"50 bottles of beer on the wall, 50 bottles of beer.",
"Take one down and pass it around, 49 bottles of beer on the wall.",
"",
"49 bottles of beer on the wall, 49 bottles of beer.",
"Take one down and pass it around, 48 bottles of beer on the wall.",
"",
"48 bottles of beer on the wall, 48 bottles of beer.",
"Take one down and pass it around, 47 bottles of beer on the wall.",
"",
"47 bottles of beer on the wall, 47 bottles of beer.",
"Take one down and pass it around, 46 bottles of beer on the wall.",
"",
"46 bottles of beer on the wall, 46 bottles of beer.",
"Take one down and pass it around, 45 bottles of beer on the wall.",
"",
"45 bottles of beer on the wall, 45 bottles of beer.",
"Take one down and pass it around, 44 bottles of beer on the wall.",
"",
"44 bottles of beer on the wall, 44 bottles of beer.",
"Take one down and pass it around, 43 bottles of beer on the wall.",
"",
"43 bottles of beer on the wall, 43 bottles of beer.",
"Take one down and pass it around, 42 bottles of beer on the wall.",
"",
"42 bottles of beer on the wall, 42 bottles of beer.",
"Take one down and pass it around, 41 bottles of beer on the wall.",
"",
"41 bottles of beer on the wall, 41 bottles of beer.",
"Take one down and pass it around, 40 bottles of beer on the wall.",
"",
"40 bottles of beer on the wall, 40 bottles of beer.",
"Take one down and pass it around, 39 bottles of beer on the wall.",
"",
"39 bottles of beer on the wall, 39 bottles of beer.",
"Take one down and pass it around, 38 bottles of beer on the wall.",
"",
"38 bottles of beer on the wall, 38 bottles of beer.",
"Take one down and pass it around, 37 bottles of beer on the wall.",
"",
"37 bottles of beer on the wall, 37 bottles of beer.",
"Take one down and pass it around, 36 bottles of beer on the wall.",
"",
"36 bottles of beer on the wall, 36 bottles of beer.",
"Take one down and pass it around, 35 bottles of beer on the wall.",
"",
"35 bottles of beer on the wall, 35 bottles of beer.",
"Take one down and pass it around, 34 bottles of beer on the wall.",
"",
"34 bottles of beer on the wall, 34 bottles of beer.",
"Take one down and pass it around, 33 bottles of beer on the wall.",
"",
"33 bottles of beer on the wall, 33 bottles of beer.",
"Take one down and pass it around, 32 bottles of beer on the wall.",
"",
"32 bottles of beer on the wall, 32 bottles of beer.",
"Take one down and pass it around, 31 bottles of beer on the wall.",
"",
"31 bottles of beer on the wall, 31 bottles of beer.",
"Take one down and pass it around, 30 bottles of beer on the wall.",
"",
"30 bottles of beer on the wall, 30 bottles of beer.",
"Take one down and pass it around, 29 bottles of beer on the wall.",
"",
"29 bottles of beer on the wall, 29 bottles of beer.",
"Take one down and pass it around, 28 bottles of beer on the wall.",
"",
"28 bottles of beer on the wall, 28 bottles of beer.",
"Take one down and pass it around, 27 bottles of beer on the wall.",
"",
"27 bottles of beer on the wall, 27 bottles of beer.",
"Take one down and pass it around, 26 bottles of beer on the wall.",
"",
"26 bottles of beer on the wall, 26 bottles of beer.",
"Take one down and pass it around, 25 bottles of beer on the wall.",
"",
"25 bottles of beer on the wall, 25 bottles of beer.",
"Take one down and pass it around, 24 bottles of beer on the wall.",
"",
"24 bottles of beer on the wall, 24 bottles of beer.",
"Take one down and pass it around, 23 bottles of beer on the wall.",
"",
"23 bottles of beer on the wall, 23 bottles of beer.",
"Take one down and pass it around, 22 bottles of beer on the wall.",
"",
"22 bottles of beer on the wall, 22 bottles of beer.",
"Take one down and pass it around, 21 bottles of beer on the wall.",
"",
"21 bottles of beer on the wall, 21 bottles of beer.",
"Take one down and pass it around, 20 bottles of beer on the wall.",
"",
"20 bottles of beer on the wall, 20 bottles of beer.",
"Take one down and pass it around, 19 bottles of beer on the wall.",
"",
"19 bottles of beer on the wall, 19 bottles of beer.",
"Take one down and pass it around, 18 bottles of beer on the wall.",
"",
"18 bottles of beer on the wall, 18 bottles of beer.",
"Take one down and pass it around, 17 bottles of beer on the wall.",
"",
"17 bottles of beer on the wall, 17 bottles of beer.",
"Take one down and pass it around, 16 bottles of beer on the wall.",
"",
"16 bottles of beer on the wall, 16 bottles of beer.",
"Take one down and pass it around, 15 bottles of beer on the wall.",
"",
"15 bottles of beer on the wall, 15 bottles of beer.",
"Take one down and pass it around, 14 bottles of beer on the wall.",
"",
"14 bottles of beer on the wall, 14 bottles of beer.",
"Take one down and pass it around, 13 bottles of beer on the wall.",
"",
"13 bottles of beer on the wall, 13 bottles of beer.",
"Take one down and pass it around, 12 bottles of beer on the wall.",
"",
"12 bottles of beer on the wall, 12 bottles of beer.",
"Take one down and pass it around, 11 bottles of beer on the wall.",
"",
"11 bottles of beer on the wall, 11 bottles of beer.",
"Take one down and pass it around, 10 bottles of beer on the wall.",
"",
"10 bottles of beer on the wall, 10 bottles of beer.",
"Take one down and pass it around, 9 bottles of beer on the wall.",
"",
"9 bottles of beer on the wall, 9 bottles of beer.",
"Take one down and pass it around, 8 bottles of beer on the wall.",
"",
"8 bottles of beer on the wall, 8 bottles of beer.",
"Take one down and pass it around, 7 bottles of beer on the wall.",
"",
"7 bottles of beer on the wall, 7 bottles of beer.",
"Take one down and pass it around, 6 bottles of beer on the wall.",
"",
"6 bottles of beer on the wall, 6 bottles of beer.",
"Take one down and pass it around, 5 bottles of beer on the wall.",
"",
"5 bottles of beer on the wall, 5 bottles of beer.",
"Take one down and pass it around, 4 bottles of beer on the wall.",
"",
"4 bottles of beer on the wall, 4 bottles of beer.",
"Take one down and pass it around, 3 bottles of beer on the wall.",
"",
"3 bottles of beer on the wall, 3 bottles of beer.",
"Take one down and pass it around, 2 bottles of beer on the wall.",
"",
"2 bottles of beer on the wall, 2 bottles of beer.",
"Take one down and pass it around, 1 bottle of beer on the wall.",
"",
"1 bottle of beer on the wall, 1 bottle of beer.",
"Take it down and pass it around, no more bottles of beer on the wall.",
"",
"No more bottles of beer on the wall, no more bottles of beer.",
"Go to the store and buy some more, 99 bottles of beer on the wall."
]
if __name__ == '__main__':

View File

@@ -1,18 +1,23 @@
def song(first, last=0):
verses = ''
for number in reversed(range(last, first + 1)):
verses += verse(number) + '\n'
return verses
def recite(start, take=1):
results = []
for i in range(start, start - take, -1):
results.extend(verse(i))
if i > start - take + 1:
results.append('')
return results
def verse(number):
return ''.join([
"{} of beer on the wall, ".format(_bottles(number).capitalize()),
"{} of beer.\n".format(_bottles(number)),
_action(number),
_next_bottle(number),
])
return [
''.join([
"{} of beer on the wall, ".format(_bottles(number).capitalize()),
"{} of beer.".format(_bottles(number))
]),
''.join([
_action(number),
_next_bottle(number)
])
]
def _action(current_verse):
@@ -25,7 +30,7 @@ def _action(current_verse):
def _next_bottle(current_verse):
return "{} of beer on the wall.\n".format(
return "{} of beer on the wall.".format(
_bottles(_next_verse(current_verse)),
)

View File

@@ -61,25 +61,43 @@ every exercise will require you to raise an exception, but for those that do, th
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:
`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 binary_search_tree_test.py`
- Python 3.4+: `pytest binary_search_tree_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 binary_search_tree_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/python/<exerciseName>` directory.
Note that, when trying to submit an exercise, make sure the solution is in the `$EXERCISM_WORKSPACE/python/binary-search-tree` directory.
For example, if you're submitting `bob.py` for the Bob exercise, the submit command would be something like `exercism submit <path_to_exercism_dir>/python/bob/bob.py`.
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 the [help page](http://exercism.io/languages/python).
please see [Running the Tests](http://exercism.io/tracks/python/tests).
## Source
Josh Cheek [https://twitter.com/josh_cheek](https://twitter.com/josh_cheek)
## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

View File

@@ -1,14 +1,20 @@
class TreeNode(object):
def __init__(self, value):
self.value = value
def __init__(self, data, left, right):
self.data = None
self.left = None
self.right = None
def __str__(self):
fmt = 'TreeNode(data={}, left={}, right={})'
return fmt.format(self.data, self.left, self.right)
class BinarySearchTree(object):
def __init__(self):
def __init__(self, tree_data):
pass
def add(self, value):
def data(self):
pass
def search(self, value):
def sorted_data(self):
pass

View File

@@ -1,57 +1,98 @@
import unittest
from binary_search_tree import BinarySearchTree
from binary_search_tree import BinarySearchTree, TreeNode
class BinarySearchTreeTests(unittest.TestCase):
# Tests adapted from `problem-specifications//canonical-data.json` @ v1.0.0
def test_add_integer_numbers(self):
bst = BinarySearchTree()
bst.add(1)
bst.add(8)
bst.add(3)
bst.add(5)
bst.add(2)
self.assertEqual(list(bst.list()), [1, 2, 3, 5, 8])
class BinarySearchTreeTest(unittest.TestCase):
def test_add_float_numbers(self):
bst = BinarySearchTree()
bst.add(7.5)
bst.add(5.3)
bst.add(5.5)
bst.add(6.0)
bst.add(7.7)
self.assertEqual(list(bst.list()), [5.3, 5.5, 6.0, 7.5, 7.7])
def test_data_is_retained(self):
expected = TreeNode('4', None, None)
self.assertTreeEqual(BinarySearchTree(['4']).data(), expected)
def test_add_mixed_numbers(self):
bst = BinarySearchTree()
bst.add(1)
bst.add(8)
bst.add(7.5)
bst.add(5.3)
self.assertEqual(list(bst.list()), [1, 5.3, 7.5, 8])
# Test inserting data at proper node
def test_smaller_data_at_left_node(self):
expected = TreeNode('4', TreeNode('2', None, None), None)
self.assertTreeEqual(BinarySearchTree(['4', '2']).data(), expected)
def test_add_duplicated_numbers(self):
bst = BinarySearchTree()
bst.add(1)
bst.add(1)
bst.add(7.5)
bst.add(5.3)
self.assertEqual(list(bst.list()), [1, 1, 5.3, 7.5])
def test_same_number_at_left_node(self):
expected = TreeNode('4', TreeNode('4', None, None), None)
self.assertTreeEqual(BinarySearchTree(['4', '4']).data(), expected)
def test_search_existent_numbers(self):
bst = BinarySearchTree()
bst.add(1)
bst.add(7.5)
self.assertEqual(bst.search(1).value, 1)
self.assertEqual(bst.search(7.5).value, 7.5)
def test_greater_number_at_right_node(self):
expected = TreeNode('4', None, TreeNode('5', None, None))
self.assertTreeEqual(BinarySearchTree(['4', '5']).data(), expected)
def test_search_nonexistent_numbers(self):
bst = BinarySearchTree()
bst.add(1)
bst.add(7.5)
self.assertIs(bst.search(6), None)
self.assertIs(bst.search(8.8), None)
def test_can_create_complex_tree(self):
expected = TreeNode(
'4',
TreeNode(
'2',
TreeNode('1', None, None),
TreeNode('3', None, None)
),
TreeNode(
'6',
TreeNode('5', None, None),
TreeNode('7', None, None)
)
)
self.assertTreeEqual(
BinarySearchTree(['4', '2', '6', '1', '3', '5', '7']).data(),
expected
)
# Test can sort data
def test_can_sort_single_number(self):
self.assertEqual(BinarySearchTree(['2']).sorted_data(), ['2'])
def test_can_sort_if_second_number_is_smaller_than_first(self):
self.assertEqual(
BinarySearchTree(['2', '1']).sorted_data(), ['1', '2']
)
def test_can_sort_if_second_number_is_same_as_first(self):
self.assertEqual(
BinarySearchTree(['2', '2']).sorted_data(), ['2', '2']
)
def test_can_sort_if_second_number_is_greater_than_first(self):
self.assertEqual(
BinarySearchTree(['2', '3']).sorted_data(), ['2', '3']
)
def test_can_sort_complex_tree(self):
self.assertEqual(
BinarySearchTree(['2', '1', '3', '6', '7', '5']).sorted_data(),
['1', '2', '3', '5', '6', '7']
)
# Utilities
def assertTreeEqual(self, tree_one, tree_two):
try:
self.compare_tree(tree_one, tree_two)
except AssertionError:
raise AssertionError("{} != {}".format(tree_one, tree_two))
def compare_tree(self, tree_one, tree_two):
self.assertEqual(tree_one.data, tree_two.data)
# Compare left tree nodes
if tree_one.left and tree_two.left:
self.compare_tree(tree_one.left, tree_two.left)
elif tree_one.left is None and tree_two.left is None:
pass
else:
raise AssertionError
# Compare right tree nodes
if tree_one.right and tree_two.right:
self.compare_tree(tree_one.right, tree_two.right)
elif tree_one.right is None and tree_two.right is None:
pass
else:
raise AssertionError
if __name__ == '__main__':

View File

@@ -1,61 +1,51 @@
from collections import deque
class TreeNode(object):
def __init__(self, value):
self.value = value
self.left_node = None
self.right_node = None
def __init__(self, data, left, right):
self.data = data
self.left = left
self.right = right
def __str__(self):
return str(self.value)
fmt = 'TreeNode(data={}, left={}, right={})'
return fmt.format(self.data, self.left, self.right)
class BinarySearchTree(object):
def __init__(self):
def __init__(self, tree_data):
self.root = None
for data in tree_data:
self.add(data)
def add(self, value):
if(self.root is None):
self.root = TreeNode(value)
else:
inserted = False
cur_node = self.root
while not inserted:
if(value <= cur_node.value):
if(cur_node.left_node):
cur_node = cur_node.left_node
else:
cur_node.left_node = TreeNode(value)
inserted = True
elif(value > cur_node.value):
if(cur_node.right_node):
cur_node = cur_node.right_node
else:
cur_node.right_node = TreeNode(value)
inserted = True
def search(self, value):
def add(self, data):
if self.root is None:
self.root = TreeNode(data, None, None)
return
inserted = False
cur_node = self.root
found = False
while not found:
if(cur_node is None):
return None
elif(value < cur_node.value):
cur_node = cur_node.left_node
elif(value > cur_node.value):
cur_node = cur_node.right_node
elif(value == cur_node.value):
return cur_node
def list(self):
elements = deque()
self.trav_inorder(self.root, elements)
while not inserted:
if data <= cur_node.data:
if cur_node.left:
cur_node = cur_node.left
else:
cur_node.left = TreeNode(data, None, None)
inserted = True
elif data > cur_node.data:
if cur_node.right:
cur_node = cur_node.right
else:
cur_node.right = TreeNode(data, None, None)
inserted = True
def _inorder_traverse(self, node, elements):
if node is not None:
self._inorder_traverse(node.left, elements)
elements.append(node.data)
self._inorder_traverse(node.right, elements)
def data(self):
return self.root
def sorted_data(self):
elements = []
self._inorder_traverse(self.root, elements)
return elements
def trav_inorder(self, node, elements):
if(node is not None):
self.trav_inorder(node.left_node, elements)
elements.append(node.value)
self.trav_inorder(node.right_node, elements)

View File

@@ -42,25 +42,43 @@ every exercise will require you to raise an exception, but for those that do, th
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:
`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 binary_search_test.py`
- Python 3.4+: `pytest binary_search_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 binary_search_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/python/<exerciseName>` directory.
Note that, when trying to submit an exercise, make sure the solution is in the `$EXERCISM_WORKSPACE/python/binary-search` directory.
For example, if you're submitting `bob.py` for the Bob exercise, the submit command would be something like `exercism submit <path_to_exercism_dir>/python/bob/bob.py`.
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 the [help page](http://exercism.io/languages/python).
please see [Running the Tests](http://exercism.io/tracks/python/tests).
## Source
Wikipedia [http://en.wikipedia.org/wiki/Binary_search_algorithm](http://en.wikipedia.org/wiki/Binary_search_algorithm)
## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

View File

@@ -2,10 +2,10 @@ import unittest
from binary_search import binary_search
# Tests adapted from `problem-specifications//canonical-data.json` @ v1.3.0
# Tests adapted from `problem-specifications//canonical-data.json` @ v1.0.0
class BinarySearchTests(unittest.TestCase):
class BinarySearchTest(unittest.TestCase):
def test_finds_value_in_array_with_one_element(self):
self.assertEqual(binary_search([6], 6), 0)
@@ -44,6 +44,10 @@ class BinarySearchTests(unittest.TestCase):
with self.assertRaisesWithMessage(ValueError):
binary_search([], 1)
def test_nothing_is_found_when_left_and_right_bounds_cross(self):
with self.assertRaisesWithMessage(ValueError):
binary_search([1, 2], 0)
# Utility functions
def setUp(self):
try:

View File

@@ -38,25 +38,43 @@ every exercise will require you to raise an exception, but for those that do, th
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:
`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 binary_test.py`
- Python 3.4+: `pytest binary_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 binary_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/python/<exerciseName>` directory.
Note that, when trying to submit an exercise, make sure the solution is in the `$EXERCISM_WORKSPACE/python/binary` directory.
For example, if you're submitting `bob.py` for the Bob exercise, the submit command would be something like `exercism submit <path_to_exercism_dir>/python/bob/bob.py`.
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 the [help page](http://exercism.io/languages/python).
please see [Running the Tests](http://exercism.io/tracks/python/tests).
## Source
All of Computer Science [http://www.wolframalpha.com/input/?i=binary&a=*C.binary-_*MathWorld-](http://www.wolframalpha.com/input/?i=binary&a=*C.binary-_*MathWorld-)
## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

View File

@@ -9,7 +9,7 @@ import unittest
from binary import parse_binary
class BinaryTests(unittest.TestCase):
class BinaryTest(unittest.TestCase):
def test_binary_1_is_decimal_1(self):
self.assertEqual(parse_binary("1"), 1)

View File

@@ -6,6 +6,8 @@ Bob answers 'Sure.' if you ask him a question.
He answers 'Whoa, chill out!' if you yell at him.
He answers 'Calm down, I know what I'm doing!' if you yell a question at him.
He says 'Fine. Be that way!' if you address him without actually saying
anything.
@@ -19,25 +21,43 @@ every exercise will require you to raise an exception, but for those that do, th
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:
`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 bob_test.py`
- Python 3.4+: `pytest bob_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 bob_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/python/<exerciseName>` directory.
Note that, when trying to submit an exercise, make sure the solution is in the `$EXERCISM_WORKSPACE/python/bob` directory.
For example, if you're submitting `bob.py` for the Bob exercise, the submit command would be something like `exercism submit <path_to_exercism_dir>/python/bob/bob.py`.
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 the [help page](http://exercism.io/languages/python).
please see [Running the Tests](http://exercism.io/tracks/python/tests).
## Source
Inspired by the 'Deaf Grandma' exercise in Chris Pine's Learn to Program tutorial. [http://pine.fm/LearnToProgram/?Chapter=06](http://pine.fm/LearnToProgram/?Chapter=06)
## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

View File

@@ -1,100 +1,100 @@
import unittest
import bob
from bob import hey
# Tests adapted from `problem-specifications//canonical-data.json` @ v1.2.0
# Tests adapted from `problem-specifications//canonical-data.json` @ v1.4.0
class BobTests(unittest.TestCase):
class BobTest(unittest.TestCase):
def test_stating_something(self):
self.assertEqual(bob.hey("Tom-ay-to, tom-aaaah-to."), "Whatever.")
self.assertEqual(hey("Tom-ay-to, tom-aaaah-to."), "Whatever.")
def test_shouting(self):
self.assertEqual(bob.hey("WATCH OUT!"), "Whoa, chill out!")
self.assertEqual(hey("WATCH OUT!"), "Whoa, chill out!")
def test_shouting_gibberish(self):
self.assertEqual(bob.hey("FCECDFCAAB"), "Whoa, chill out!")
self.assertEqual(hey("FCECDFCAAB"), "Whoa, chill out!")
def test_asking_a_question(self):
self.assertEqual(
bob.hey("Does this cryogenic chamber make me look fat?"), "Sure.")
hey("Does this cryogenic chamber make me look fat?"), "Sure.")
def test_asking_a_numeric_question(self):
self.assertEqual(bob.hey("You are, what, like 15?"), "Sure.")
self.assertEqual(hey("You are, what, like 15?"), "Sure.")
def test_asking_gibberish(self):
self.assertEqual(bob.hey("fffbbcbeab?"), "Sure.")
self.assertEqual(hey("fffbbcbeab?"), "Sure.")
def test_talking_forcefully(self):
self.assertEqual(
bob.hey("Let's go make out behind the gym!"), "Whatever.")
hey("Let's go make out behind the gym!"), "Whatever.")
def test_using_acronyms_in_regular_speech(self):
self.assertEqual(
bob.hey("It's OK if you don't want to go to the DMV."),
hey("It's OK if you don't want to go to the DMV."),
"Whatever.")
def test_forceful_question(self):
self.assertEqual(
bob.hey("WHAT THE HELL WERE YOU THINKING?"),
hey("WHAT THE HELL WERE YOU THINKING?"),
"Calm down, I know what I'm doing!"
)
def test_shouting_numbers(self):
self.assertEqual(bob.hey("1, 2, 3 GO!"), "Whoa, chill out!")
self.assertEqual(hey("1, 2, 3 GO!"), "Whoa, chill out!")
def test_only_numbers(self):
self.assertEqual(bob.hey("1, 2, 3"), "Whatever.")
def test_no_letters(self):
self.assertEqual(hey("1, 2, 3"), "Whatever.")
def test_question_with_only_numbers(self):
self.assertEqual(bob.hey("4?"), "Sure.")
def test_question_with_no_letters(self):
self.assertEqual(hey("4?"), "Sure.")
def test_shouting_with_special_characters(self):
self.assertEqual(
bob.hey("ZOMG THE %^*@#$(*^ ZOMBIES ARE COMING!!11!!1!"),
hey("ZOMG THE %^*@#$(*^ ZOMBIES ARE COMING!!11!!1!"),
"Whoa, chill out!")
def test_shouting_with_no_exclamation_mark(self):
self.assertEqual(bob.hey("I HATE YOU"), "Whoa, chill out!")
self.assertEqual(hey("I HATE THE DMV"), "Whoa, chill out!")
def test_statement_containing_question_mark(self):
self.assertEqual(
bob.hey("Ending with ? means a question."), "Whatever.")
hey("Ending with ? means a question."), "Whatever.")
def test_non_letters_with_question(self):
self.assertEqual(bob.hey(":) ?"), "Sure.")
self.assertEqual(hey(":) ?"), "Sure.")
def test_prattling_on(self):
self.assertEqual(
bob.hey("Wait! Hang on. Are you going to be OK?"), "Sure.")
hey("Wait! Hang on. Are you going to be OK?"), "Sure.")
def test_silence(self):
self.assertEqual(bob.hey(""), "Fine. Be that way!")
self.assertEqual(hey(""), "Fine. Be that way!")
def test_prolonged_silence(self):
self.assertEqual(bob.hey(" "), "Fine. Be that way!")
self.assertEqual(hey(" "), "Fine. Be that way!")
def test_alternate_silence(self):
self.assertEqual(bob.hey("\t\t\t\t\t\t\t\t\t\t"), "Fine. Be that way!")
self.assertEqual(hey("\t\t\t\t\t\t\t\t\t\t"), "Fine. Be that way!")
def test_multiple_line_question(self):
self.assertEqual(
bob.hey("\nDoes this cryogenic chamber make me look fat?\nno"),
hey("\nDoes this cryogenic chamber make me look fat?\nNo."),
"Whatever.")
def test_starting_with_whitespace(self):
self.assertEqual(bob.hey(" hmmmmmmm..."), "Whatever.")
self.assertEqual(hey(" hmmmmmmm..."), "Whatever.")
def test_ending_with_whitespace(self):
self.assertEqual(
bob.hey("Okay if like my spacebar quite a bit? "), "Sure.")
hey("Okay if like my spacebar quite a bit? "), "Sure.")
def test_other_whitespace(self):
self.assertEqual(bob.hey("\n\r \t"), "Fine. Be that way!")
self.assertEqual(hey("\n\r \t"), "Fine. Be that way!")
def test_non_question_ending_with_whitespace(self):
self.assertEqual(
bob.hey("This is a statement ending with whitespace "),
hey("This is a statement ending with whitespace "),
"Whatever.")

View File

@@ -75,25 +75,43 @@ every exercise will require you to raise an exception, but for those that do, th
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:
`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 book_store_test.py`
- Python 3.4+: `pytest book_store_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 book_store_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/python/<exerciseName>` directory.
Note that, when trying to submit an exercise, make sure the solution is in the `$EXERCISM_WORKSPACE/python/book-store` directory.
For example, if you're submitting `bob.py` for the Bob exercise, the submit command would be something like `exercism submit <path_to_exercism_dir>/python/bob/bob.py`.
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 the [help page](http://exercism.io/languages/python).
please see [Running the Tests](http://exercism.io/tracks/python/tests).
## Source
Inspired by the harry potter kata from Cyber-Dojo. [http://cyber-dojo.org](http://cyber-dojo.org)
## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

View File

@@ -3,75 +3,57 @@ import unittest
from book_store import calculate_total
# Tests adapted from `problem-specifications//canonical-data.json` @ v1.1.0
# Tests adapted from `problem-specifications//canonical-data.json` @ v1.4.0
class BookStoreTests(unittest.TestCase):
class BookStoreTest(unittest.TestCase):
def test_only_a_single_book(self):
self.assertAlmostEqual(calculate_total([1]), 8.00,
places=2)
self.assertEqual(calculate_total([1]), 800)
def test_two_of_the_same_book(self):
self.assertAlmostEqual(calculate_total([2, 2]), 16.00,
places=2)
self.assertEqual(calculate_total([2, 2]), 1600)
def test_empty_basket(self):
self.assertAlmostEqual(calculate_total([]), 0.00,
places=2)
self.assertEqual(calculate_total([]), 0)
def test_two_different_books(self):
self.assertAlmostEqual(calculate_total([1, 2]), 15.20,
places=2)
self.assertEqual(calculate_total([1, 2]), 1520)
def test_three_different_books(self):
self.assertAlmostEqual(calculate_total([1, 2, 3]), 21.60,
places=2)
self.assertEqual(calculate_total([1, 2, 3]), 2160)
def test_four_different_books(self):
self.assertAlmostEqual(calculate_total([1, 2, 3, 4]), 25.60,
places=2)
self.assertEqual(calculate_total([1, 2, 3, 4]), 2560)
def test_five_different_books(self):
self.assertAlmostEqual(
calculate_total([1, 2, 3, 4, 5]), 30.00,
places=2)
self.assertEqual(calculate_total([1, 2, 3, 4, 5]), 3000)
def test_two_groups_of_4_is_cheaper_than_group_of_5_plus_group_of_3(self):
self.assertAlmostEqual(
calculate_total([1, 1, 2, 2, 3, 3, 4, 5]), 51.20,
places=2)
self.assertEqual(calculate_total([1, 1, 2, 2, 3, 3, 4, 5]), 5120)
def test_two_groups_of_4_is_cheaper_than_groups_of_5_and_3(self):
self.assertEqual(calculate_total([1, 1, 2, 3, 4, 4, 5, 5]), 5120)
def test_group_of_4_plus_group_of_2_is_cheaper_than_2_groups_of_3(self):
self.assertAlmostEqual(
calculate_total([1, 1, 2, 2, 3, 4]), 40.80,
places=2)
self.assertEqual(calculate_total([1, 1, 2, 2, 3, 4]), 4080)
def test_two_each_of_first_4_books_and_1_copy_each_of_rest(self):
self.assertAlmostEqual(
calculate_total([1, 1, 2, 2, 3, 3, 4, 4, 5]), 55.60,
places=2)
self.assertEqual(calculate_total([1, 1, 2, 2, 3, 3, 4, 4, 5]), 5560)
def test_two_copies_of_each_book(self):
self.assertAlmostEqual(
calculate_total([1, 1, 2, 2, 3, 3, 4, 4, 5, 5]), 60.00,
places=2)
self.assertEqual(calculate_total([1, 1, 2, 2, 3, 3, 4, 4, 5, 5]), 6000)
def test_three_copies_of_first_book_and_2_each_of_remaining(self):
self.assertAlmostEqual(
calculate_total([1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 1]),
68.00,
places=2)
self.assertEqual(
calculate_total([1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 1]), 6800)
def test_three_each_of_first_2_books_and_2_each_of_remaining_books(self):
self.assertAlmostEqual(
calculate_total([1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 1, 2]),
75.20,
places=2)
self.assertEqual(
calculate_total([1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 1, 2]), 7520)
def test_four_groups_of_4_are_cheaper_than_2_groups_each_of_5_and_3(self):
self.assertAlmostEqual(
self.assertEqual(
calculate_total([1, 1, 2, 2, 3, 3, 4, 5, 1, 1, 2, 2, 3, 3, 4, 5]),
102.40,
places=2)
10240)
if __name__ == '__main__':

View File

@@ -1,29 +1,49 @@
BOOK_PRICE = 8
def _group_price(size):
discounts = [0, .05, .1, .2, .25]
if not (0 < size <= 5):
raise ValueError('size must be in 1..' + len(discounts))
return BOOK_PRICE * size * (1 - discounts[size - 1])
def calculate_total(books, price_so_far=0.):
if not books:
return price_so_far
groups = list(set(books))
min_price = float('inf')
for i in range(len(groups)):
remaining_books = books[:]
for v in groups[:i + 1]:
remaining_books.remove(v)
price = calculate_total(remaining_books,
price_so_far + _group_price(i + 1))
min_price = min(min_price, price)
return min_price
from functools import reduce
BASE_COST = 800
discount = [1.0, 1.0, 0.95, 0.9, 0.8, 0.75]
def groupCost(g):
return len(g) * discount[len(g)]
class Grouping:
def __init__(self, groups=None):
self.groups = [set()] if groups is None else groups
def total(self):
return sum(map(groupCost, self.groups)) * BASE_COST
def dup(self):
return Grouping(list(map(set, self.groups)))
def add_to_valid(self, b):
"""Returns all possible groupings from current grouping adding book b
"""
other = self.dup()
other.groups.sort(key=lambda g: len(g))
results = []
for i, g in enumerate(other.groups):
if b not in g:
o2 = other.dup()
o2.groups[i].add(b)
results.append(o2)
if not results:
other.groups.append(set([b]))
return [other]
return results
def __lt__(self, other):
return self.total() < other.total()
def step(rs, b):
return [g for r in rs for g in r.add_to_valid(b)]
def calculate_total(books):
if len(books) == 0:
return 0
start = Grouping([{books[0]}])
return round(min(reduce(step, books[1:], [start])).total())

110
exercises/bowling/README.md Normal file
View File

@@ -0,0 +1,110 @@
# Bowling
Score a bowling game.
Bowling is a game where players roll a heavy ball to knock down pins
arranged in a triangle. Write code to keep track of the score
of a game of bowling.
## Scoring Bowling
The game consists of 10 frames. A frame is composed of one or two ball
throws with 10 pins standing at frame initialization. There are three
cases for the tabulation of a frame.
* An open frame is where a score of less than 10 is recorded for the
frame. In this case the score for the frame is the number of pins
knocked down.
* A spare is where all ten pins are knocked down by the second
throw. The total value of a spare is 10 plus the number of pins
knocked down in their next throw.
* A strike is where all ten pins are knocked down by the first
throw. The total value of a strike is 10 plus the number of pins
knocked down in the next two throws. If a strike is immediately
followed by a second strike, then the value of the first strike
cannot be determined until the ball is thrown one more time.
Here is a three frame example:
| Frame 1 | Frame 2 | Frame 3 |
| :-------------: |:-------------:| :---------------------:|
| X (strike) | 5/ (spare) | 9 0 (open frame) |
Frame 1 is (10 + 5 + 5) = 20
Frame 2 is (5 + 5 + 9) = 19
Frame 3 is (9 + 0) = 9
This means the current running total is 48.
The tenth frame in the game is a special case. If someone throws a
strike or a spare then they get a fill ball. Fill balls exist to
calculate the total of the 10th frame. Scoring a strike or spare on
the fill ball does not give the player more fill balls. The total
value of the 10th frame is the total number of pins knocked down.
For a tenth frame of X1/ (strike and a spare), the total value is 20.
For a tenth frame of XXX (three strikes), the total value is 30.
## Requirements
Write code to keep track of the score of a game of bowling. It should
support two operations:
* `roll(pins : int)` is called each time the player rolls a ball. The
argument is the number of pins knocked down.
* `score() : int` is called only at the very end of the game. It
returns the total score for that game.
## 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 bowling_test.py`
- Python 3.4+: `pytest bowling_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 bowling_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/bowling` 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
The Bowling Game Kata at but UncleBob [http://butunclebob.com/ArticleS.UncleBob.TheBowlingGameKata](http://butunclebob.com/ArticleS.UncleBob.TheBowlingGameKata)
## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

View File

@@ -1,5 +1,3 @@
class BowlingGame(object):
def __init__(self):
pass

View File

@@ -3,11 +3,9 @@ import unittest
from bowling import BowlingGame
# Tests adapted from `problem-specifications//canonical-data.json` @ v1.0.1
# Tests adapted from `problem-specifications//canonical-data.json` @ v1.2.0
class BowlingTests(unittest.TestCase):
def setUp(self):
self.game = BowlingGame()
class BowlingTest(unittest.TestCase):
def roll(self, rolls):
[self.game.roll(roll) for roll in rolls]
@@ -120,23 +118,27 @@ class BowlingTests(unittest.TestCase):
def test_rolls_cannot_score_negative_points(self):
self.assertRaises(ValueError, self.game.roll, -11)
with self.assertRaisesWithMessage(ValueError):
self.game.roll(-1)
def test_a_roll_cannot_score_more_than_10_points(self):
self.assertRaises(ValueError, self.game.roll, 11)
with self.assertRaisesWithMessage(ValueError):
self.game.roll(11)
def test_two_rolls_in_a_frame_cannot_score_more_than_10_points(self):
self.game.roll(5)
self.assertRaises(ValueError, self.game.roll, 6)
with self.assertRaisesWithMessage(ValueError):
self.game.roll(6)
def test_bonus_after_strike_in_last_frame_cannot_score_more_than_10(self):
rolls = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10]
self.roll(rolls)
self.assertRaises(ValueError, self.game.roll, 11)
with self.assertRaisesWithMessage(ValueError):
self.game.roll(11)
def test_bonus_aft_last_frame_strk_can_be_more_than_10_if_1_is_strk(self):
rolls = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10,
@@ -151,42 +153,76 @@ class BowlingTests(unittest.TestCase):
self.roll(rolls)
self.assertRaises(ValueError, self.game.roll, 10)
with self.assertRaisesWithMessage(ValueError):
self.game.roll(10)
def test_an_incomplete_game_cannot_be_scored(self):
rolls = [0, 0]
self.roll(rolls)
self.assertRaises(IndexError, self.game.score)
with self.assertRaisesWithMessage(IndexError):
self.game.score()
def test_cannot_roll_if_there_are_already_ten_frames(self):
rolls = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
self.roll(rolls)
self.assertRaises(IndexError, self.game.roll, 0)
with self.assertRaisesWithMessage(IndexError):
self.game.roll(0)
def test_bonus_rolls_for_strike_must_be_rolled_before_score_is_calc(self):
rolls = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10]
self.roll(rolls)
self.assertRaises(IndexError, self.game.score)
with self.assertRaisesWithMessage(IndexError):
self.game.score()
def test_both_bonuses_for_strike_must_be_rolled_before_score(self):
rolls = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10]
self.roll(rolls)
self.assertRaises(IndexError, self.game.score)
with self.assertRaisesWithMessage(IndexError):
self.game.score()
def test_bonus_rolls_for_spare_must_be_rolled_before_score_is_calc(self):
rolls = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 3]
self.roll(rolls)
self.assertRaises(IndexError, self.game.score)
with self.assertRaisesWithMessage(IndexError):
self.game.score()
def test_cannot_roll_after_bonus_roll_for_spare(self):
rolls = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 3, 2]
self.roll(rolls)
with self.assertRaisesWithMessage(IndexError):
self.game.roll(2)
def test_cannot_roll_after_bonus_rolls_for_strike(self):
rolls = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10,
3, 2]
self.roll(rolls)
with self.assertRaisesWithMessage(IndexError):
self.game.roll(2)
# Utility functions
def setUp(self):
self.game = BowlingGame()
try:
self.assertRaisesRegex
except AttributeError:
self.assertRaisesRegex = self.assertRaisesRegexp
def assertRaisesWithMessage(self, exception):
return self.assertRaisesRegex(exception, r".+")
if __name__ == '__main__':

View File

@@ -1,149 +1,99 @@
MAX_PINS = 10
NUM_FRAMES = 10
class BowlingGame(object):
"""This class manages the Bowling Game including the roll and score
methods"""
def __init__(self):
self.rolls = []
self.totalScore = 0
self.currentFrame = Frame()
self.bonusRollsAccrued = 0
self.bonusRollsSeen = 0
def roll(self, pins):
if self.isBonusRoll():
self.bonusRollsSeen += 1
# is the second roll valid based off the first?
if (self.currentFrame.isOpen() and
self.currentFrame.getFrame()[0] is not None):
if self.currentFrame.getFrame()[0] + pins > MAX_PINS:
raise ValueError("This roll will cause the current frame "
"to be getter than the max number of pins")
# open a new frame if the last one has been closed
if not self.currentFrame.isOpen():
self.currentFrame = Frame()
# valid roll between 0-10
if pins in range(MAX_PINS + 1):
# raise an error if the game is over and they try to roll again
if ((len(self.rolls) == NUM_FRAMES) and
self.bonusRollsAccrued == 0):
raise IndexError("Max Frames have been reached. Too many "
"rolls")
else:
self.currentFrame.roll(pins,
self.isBonusRoll(),
self.bonusRollsAccrued,
self.bonusRollsSeen)
# if we closed it add it to our rolls
if not self.currentFrame.isOpen():
self.rolls.append(self.currentFrame)
# If this is the last frame did we earn any bonus rolls?
if len(self.rolls) == NUM_FRAMES:
self.bonusRollsEarned()
else:
raise ValueError("Amount of pins rolled is greater than the max "
"number of pins")
def score(self):
frame_index = 0
while (frame_index <= NUM_FRAMES-1):
frame = self.rolls[frame_index].getFrame()
roll1 = frame[0]
roll2 = frame[1]
if self.isStrike(roll1):
self.totalScore += roll1 + self.stikeBonus(frame_index)
else:
if self.isSpare(roll1, roll2):
self.totalScore += roll1 + roll2 + \
self.spareBonus(frame_index)
else:
self.totalScore += roll1 + roll2
frame_index += 1
return self.totalScore
def isStrike(self, pins):
return True if pins == MAX_PINS else False
def isSpare(self, roll1, roll2):
return True if roll1 + roll2 == MAX_PINS else False
def stikeBonus(self, frame_index):
bonusroll1 = self.rolls[frame_index+1].getFrame()[0]
bonusroll2 = 0
# need to go further out if the next on is a strike
if bonusroll1 == 10:
bonusroll2 = self.rolls[frame_index+2].getFrame()[0]
else:
bonusroll2 = self.rolls[frame_index+1].getFrame()[1]
# edge case - if the last roll is a stike the bonus rolls needs to be
# validated
if (not self.isStrike(bonusroll1) and
(bonusroll1 + bonusroll2 > MAX_PINS)):
raise ValueError("The bonus rolls total to greater than the max "
"number of pins")
else:
return bonusroll1 + bonusroll2
def spareBonus(self, frame_index):
return self.rolls[frame_index+1].getFrame()[0]
def isLastFrame(self, frame_index):
return True if frame_index >= len(self.rolls)-1 else False
def bonusRollsEarned(self):
if len(self.rolls) == NUM_FRAMES:
lastFrame = self.rolls[NUM_FRAMES-1].getFrame()
if self.isStrike(lastFrame[0]):
self.bonusRollsAccrued = 2
elif self.isSpare(lastFrame[0], lastFrame[1]):
self.bonusRollsAccrued = 1
else:
self.bonusRollsAccrued = 0
return
def isBonusRoll(self):
# if we've already seen all
return True if len(self.rolls) >= NUM_FRAMES else False
MAX_FRAME = 10
class Frame(object):
"""This class is for internal use only. It divides up the array of
rolls into Frame objects"""
def __init__(self, idx):
self.idx = idx
self.throws = []
@property
def total_pins(self):
"""Total pins knocked down in a frame."""
return sum(self.throws)
def is_strike(self):
return self.total_pins == 10 and len(self.throws) == 1
def is_spare(self):
return self.total_pins == 10 and len(self.throws) == 2
def is_open(self):
return self.total_pins < 10 and len(self.throws) == 2
def is_closed(self):
"""Return whether a frame is over."""
return self.total_pins == 10 or len(self.throws) == 2
def throw(self, pins):
if self.total_pins + pins > 10:
raise ValueError("a frame's rolls cannot exceed 10")
self.throws.append(pins)
def score(self, next_throws):
result = self.total_pins
if self.is_strike():
result += sum(next_throws[:2])
elif self.is_spare():
result += sum(next_throws[:1])
return result
class BowlingGame(object):
def __init__(self):
self.rolls = [None, None]
self.open = True
self.current_frame_idx = 0
self.bonus_throws = []
self.frames = [Frame(idx) for idx in range(MAX_FRAME)]
def roll(self, roll, bonusRoll, accruedBonuses, seenBonuses):
# if it's a strike we close the frame
if roll == 10:
self.rolls[0] = 10
self.rolls[1] = 0
self.open = False
@property
def current_frame(self):
return self.frames[self.current_frame_idx]
def next_throws(self, frame_idx):
"""Return a frame's next throws in the form of a list."""
throws = []
for idx in range(frame_idx + 1, MAX_FRAME):
throws.extend(self.frames[idx].throws)
throws.extend(self.bonus_throws)
return throws
def roll_bonus(self, pins):
tenth_frame = self.frames[-1]
if tenth_frame.is_open():
raise IndexError("cannot throw bonus with an open tenth frame")
self.bonus_throws.append(pins)
# Check against invalid fill balls, e.g. [3, 10]
if (len(self.bonus_throws) == 2 and self.bonus_throws[0] != 10 and
sum(self.bonus_throws) > 10):
raise ValueError("invalid fill balls")
# Check if there are more bonuses than it should be
if tenth_frame.is_strike() and len(self.bonus_throws) > 2:
raise IndexError(
"wrong number of fill balls when the tenth frame is a strike")
elif tenth_frame.is_spare() and len(self.bonus_throws) > 1:
raise IndexError(
"wrong number of fill balls when the tenth frame is a spare")
def roll(self, pins):
if not 0 <= pins <= 10:
raise ValueError("invalid pins")
elif self.current_frame_idx == MAX_FRAME:
self.roll_bonus(pins)
else:
# first roll, but frame is still open
if self.rolls[0] is None:
self.rolls[0] = roll
# may need to close bonus roll frames before 2 have been seen
if bonusRoll and seenBonuses == accruedBonuses:
self.rolls[1] = 0
self.open = False
else:
# second roll, closes frame
self.rolls[1] = roll
self.open = False
self.current_frame.throw(pins)
if self.current_frame.is_closed():
self.current_frame_idx += 1
def isOpen(self):
return self.open
def getFrame(self):
return self.rolls
def score(self):
if self.current_frame_idx < MAX_FRAME:
raise IndexError("frame less than 10")
if self.frames[-1].is_spare() and len(self.bonus_throws) != 1:
raise IndexError(
"one bonus must be rolled when the tenth frame is spare")
if self.frames[-1].is_strike() and len(self.bonus_throws) != 2:
raise IndexError(
"two bonuses must be rolled when the tenth frame is strike")
return sum(frame.score(self.next_throws(frame.idx))
for frame in self.frames)

View File

@@ -1,35 +0,0 @@
# Bracket Push
Given a string containing brackets `[]`, braces `{}` and parentheses `()`,
verify that all the pairs are matched and nested correctly.
## 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/<exerciseName>` directory.
For example, if you're submitting `bob.py` for the Bob exercise, the submit command would be something like `exercism submit <path_to_exercism_dir>/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).
## Source
Ginna Baker
## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

View File

@@ -1,2 +0,0 @@
def check_brackets(input_string):
pass

View File

@@ -1,57 +0,0 @@
import unittest
from bracket_push import check_brackets
# Tests adapted from `problem-specifications//canonical-data.json` @ v1.1.0
class BracketPushTests(unittest.TestCase):
def test_paired_square_brackets(self):
self.assertEqual(check_brackets("[]"), True)
def test_empty_string(self):
self.assertEqual(check_brackets(""), True)
def test_unpaired_brackets(self):
self.assertEqual(check_brackets("[["), False)
def test_wrong_ordered_brackets(self):
self.assertEqual(check_brackets("}{"), False)
def test_wrong_closing_bracket(self):
self.assertEqual(check_brackets("{]"), False)
def test_paired_with_whitespace(self):
self.assertEqual(check_brackets("{ }"), True)
def test_simple_nested_brackets(self):
self.assertEqual(check_brackets("{[]}"), True)
def test_several_paired_brackets(self):
self.assertEqual(check_brackets("{}[]"), True)
def test_paired_and_nested_brackets(self):
self.assertEqual(check_brackets("([{}({}[])])"), True)
def test_unopened_closing_brackets(self):
self.assertEqual(check_brackets("{[)][]}"), False)
def test_unpaired_and_nested_brackets(self):
self.assertEqual(check_brackets("([{])"), False)
def test_paired_and_wrong_nested_brackets(self):
self.assertEqual(check_brackets("[({]})"), False)
def test_math_expression(self):
self.assertEqual(
check_brackets("(((185 + 223.85) * 15) - 543)/2"), True)
def test_complex_latex_expression(self):
self.assertEqual(
check_brackets(
("\\left(\\begin{array}{cc} \\frac{1}{3} & x\\\\ \\mathrm{e}^{"
"x} &... x^2 \\end{array}\\right)")), True)
if __name__ == '__main__':
unittest.main()

View File

@@ -6,9 +6,9 @@ that the sum of the coins' value would equal the correct amount of change.
## For example
- An input of 15 with [1, 5, 10, 25, 100] should return one nickel (5)
and one dime (10) or [0, 1, 1, 0, 0]
and one dime (10) or [5, 10]
- An input of 40 with [1, 5, 10, 25, 100] should return one nickel (5)
and one dime (10) and one quarter (25) or [0, 1, 1, 1, 0]
and one dime (10) and one quarter (25) or [5, 10, 25]
## Edge cases
@@ -24,25 +24,43 @@ every exercise will require you to raise an exception, but for those that do, th
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:
`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 change_test.py`
- Python 3.4+: `pytest change_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 change_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/python/<exerciseName>` directory.
Note that, when trying to submit an exercise, make sure the solution is in the `$EXERCISM_WORKSPACE/python/change` directory.
For example, if you're submitting `bob.py` for the Bob exercise, the submit command would be something like `exercism submit <path_to_exercism_dir>/python/bob/bob.py`.
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 the [help page](http://exercism.io/languages/python).
please see [Running the Tests](http://exercism.io/tracks/python/tests).
## Source
Software Craftsmanship - Coin Change Kata [https://web.archive.org/web/20130115115225/http://craftsmanship.sv.cmu.edu:80/exercises/coin-change-kata](https://web.archive.org/web/20130115115225/http://craftsmanship.sv.cmu.edu:80/exercises/coin-change-kata)
## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

View File

@@ -2,8 +2,8 @@ import unittest
from change import find_minimum_coins
# Tests adapted from `problem-specifications//canonical-data.json` @ v1.1.0
# Tests adapted from `problem-specifications//canonical-data.json` @ v1.3.0
class ChangeTest(unittest.TestCase):
def test_single_coin_change(self):
@@ -37,13 +37,26 @@ class ChangeTest(unittest.TestCase):
self.assertEqual(find_minimum_coins(0, [1, 5, 10, 21, 25]), [])
def test_error_testing_for_change_smaller_than_smallest_coin(self):
self.assertEqual(find_minimum_coins(3, [5, 10]), -1)
with self.assertRaisesWithMessage(ValueError):
find_minimum_coins(3, [5, 10])
def test_error_if_no_combination_can_add_up_to_target(self):
self.assertEqual(find_minimum_coins(94, [5, 10]), -1)
with self.assertRaisesWithMessage(ValueError):
find_minimum_coins(94, [5, 10])
def test_cannot_find_negative_change_values(self):
self.assertEqual(find_minimum_coins(-5, [1, 2, 5]), -1)
with self.assertRaisesWithMessage(ValueError):
find_minimum_coins(-5, [1, 2, 5])
# 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__":

View File

@@ -1,6 +1,6 @@
def find_minimum_coins(total_change, coins):
if total_change < 0:
return -1
raise ValueError("cannot find negative change values")
min_coins_required = [1e9] * (total_change + 1)
last_coin = [0]*(total_change + 1)
min_coins_required[0] = 0
@@ -15,7 +15,7 @@ def find_minimum_coins(total_change, coins):
last_coin[change] = change - coin
min_coins_required[change] = final_result
if min_coins_required[total_change] == 1e9:
return -1
raise ValueError("no combination can add up to target")
else:
last_coin_value = total_change
array = []

View File

@@ -58,25 +58,43 @@ every exercise will require you to raise an exception, but for those that do, th
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:
`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 circular_buffer_test.py`
- Python 3.4+: `pytest circular_buffer_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 circular_buffer_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/python/<exerciseName>` directory.
Note that, when trying to submit an exercise, make sure the solution is in the `$EXERCISM_WORKSPACE/python/circular-buffer` directory.
For example, if you're submitting `bob.py` for the Bob exercise, the submit command would be something like `exercism submit <path_to_exercism_dir>/python/bob/bob.py`.
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 the [help page](http://exercism.io/languages/python).
please see [Running the Tests](http://exercism.io/tracks/python/tests).
## Source
Wikipedia [http://en.wikipedia.org/wiki/Circular_buffer](http://en.wikipedia.org/wiki/Circular_buffer)
## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

View File

@@ -9,3 +9,15 @@ class BufferEmptyException(Exception):
class CircularBuffer(object):
def __init__(self, capacity):
pass
def read(self):
pass
def write(self, data):
pass
def overwrite(self, data):
pass
def clear(self):
pass

View File

@@ -7,7 +7,7 @@ from circular_buffer import (
)
# Tests adapted from `problem-specifications//canonical-data.json` @ v1.0.1
# Tests adapted from `problem-specifications//canonical-data.json` @ v1.1.0
class CircularBufferTest(unittest.TestCase):
def test_read_empty_buffer(self):
@@ -34,7 +34,7 @@ class CircularBufferTest(unittest.TestCase):
self.assertEqual(buf.read(), '1')
self.assertEqual(buf.read(), '2')
def test_full_buffer_cant_written(self):
def test_cant_write_to_full_buffer(self):
buf = CircularBuffer(1)
buf.write('1')
with self.assertRaisesWithMessage(BufferFullException):
@@ -70,7 +70,7 @@ class CircularBufferTest(unittest.TestCase):
buf.write('2')
self.assertEqual(buf.read(), '2')
def test_clear_does_nothin_empty_buffer(self):
def test_clear_does_nothing_on_empty_buffer(self):
buf = CircularBuffer(1)
buf.clear()
buf.write('1')
@@ -91,14 +91,7 @@ class CircularBufferTest(unittest.TestCase):
self.assertEqual(buf.read(), '2')
self.assertEqual(buf.read(), '3')
def test_write_full_buffer(self):
buf = CircularBuffer(2)
buf.write('1')
buf.write('2')
with self.assertRaisesWithMessage(BufferFullException):
buf.write('A')
def test_over_write_replaces_oldest_remaning_item(self):
def test_overwrite_replaces_oldest_remaining_item(self):
buf = CircularBuffer(3)
buf.write('1')
buf.write('2')

View File

@@ -14,25 +14,43 @@ every exercise will require you to raise an exception, but for those that do, th
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:
`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 clock_test.py`
- Python 3.4+: `pytest clock_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 clock_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/python/<exerciseName>` directory.
Note that, when trying to submit an exercise, make sure the solution is in the `$EXERCISM_WORKSPACE/python/clock` directory.
For example, if you're submitting `bob.py` for the Bob exercise, the submit command would be something like `exercism submit <path_to_exercism_dir>/python/bob/bob.py`.
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 the [help page](http://exercism.io/languages/python).
please see [Running the Tests](http://exercism.io/tracks/python/tests).
## Source
Pairing session with Erin Drummond [https://twitter.com/ebdrummond](https://twitter.com/ebdrummond)
## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

View File

@@ -2,5 +2,14 @@ class Clock(object):
def __init__(self, hour, minute):
pass
def __add__(self, other):
def __repr__(self):
pass
def __eq__(self, other):
pass
def __add__(self, minutes):
pass
def __sub__(self, minutes):
pass

View File

@@ -2,8 +2,8 @@ import unittest
from clock import Clock
# Tests adapted from `problem-specifications//canonical-data.json` @ v1.0.1
# Tests adapted from `problem-specifications//canonical-data.json` @ v2.4.0
class ClockTest(unittest.TestCase):
# Test creating a new clock with an initial time.
@@ -58,6 +58,9 @@ class ClockTest(unittest.TestCase):
def test_negative_minutes_roll_over_continuously(self):
self.assertEqual(str(Clock(1, -4820)), '16:40')
def test_negative_sixty_minutes_is_previous_hour(self):
self.assertEqual(str(Clock(2, -60)), '01:00')
def test_negative_hour_and_minutes_both_roll_over(self):
self.assertEqual(str(Clock(-25, -160)), '20:20')
@@ -90,28 +93,28 @@ class ClockTest(unittest.TestCase):
self.assertEqual(str(Clock(1, 1) + 3500), '11:21')
def test_subtract_minutes(self):
self.assertEqual(str(Clock(10, 3) + -3), '10:00')
self.assertEqual(str(Clock(10, 3) - 3), '10:00')
def test_subtract_to_previous_hour(self):
self.assertEqual(str(Clock(10, 3) + -3), '10:00')
self.assertEqual(str(Clock(10, 3) - 30), '09:33')
def test_subtract_more_than_an_hour(self):
self.assertEqual(str(Clock(10, 3) + -30), '09:33')
self.assertEqual(str(Clock(10, 3) - 70), '08:53')
def test_subtract_across_midnight(self):
self.assertEqual(str(Clock(10, 3) + -70), '08:53')
self.assertEqual(str(Clock(0, 3) - 4), '23:59')
def test_subtract_more_than_two_hours(self):
self.assertEqual(str(Clock(0, 0) + -160), '21:20')
self.assertEqual(str(Clock(0, 0) - 160), '21:20')
def test_subtract_more_than_two_hours_with_borrow(self):
self.assertEqual(str(Clock(6, 15) + -160), '03:35')
self.assertEqual(str(Clock(6, 15) - 160), '03:35')
def test_subtract_more_than_one_day(self):
self.assertEqual(str(Clock(5, 32) + -1500), '04:32')
self.assertEqual(str(Clock(5, 32) - 1500), '04:32')
def test_subtract_more_than_two_days(self):
self.assertEqual(str(Clock(2, 20) + -3000), '00:20')
self.assertEqual(str(Clock(2, 20) - 3000), '00:20')
# Construct two separate clocks, set times, test if they are equal.
def test_clocks_with_same_time(self):
@@ -159,6 +162,9 @@ class ClockTest(unittest.TestCase):
def test_clocks_with_negative_hours_and_minutes_that_wrap(self):
self.assertEqual(Clock(18, 7), Clock(-54, -11513))
def test_full_clock_and_zeroed_clock(self):
self.assertEqual(Clock(24, 0), Clock(0, 0))
if __name__ == '__main__':
unittest.main()

View File

@@ -1,4 +1,3 @@
class Clock(object):
'Clock that displays 24 hour clock that rollsover properly'
@@ -17,6 +16,10 @@ class Clock(object):
self.minute += minutes
return self.cleanup()
def __sub__(self, minutes):
self.minute -= minutes
return self.cleanup()
def cleanup(self):
self.hour += self.minute // 60
self.hour %= 24

View File

@@ -39,25 +39,43 @@ every exercise will require you to raise an exception, but for those that do, th
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:
`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 collatz_conjecture_test.py`
- Python 3.4+: `pytest collatz_conjecture_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 collatz_conjecture_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/python/<exerciseName>` directory.
Note that, when trying to submit an exercise, make sure the solution is in the `$EXERCISM_WORKSPACE/python/collatz-conjecture` directory.
For example, if you're submitting `bob.py` for the Bob exercise, the submit command would be something like `exercism submit <path_to_exercism_dir>/python/bob/bob.py`.
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 the [help page](http://exercism.io/languages/python).
please see [Running the Tests](http://exercism.io/tracks/python/tests).
## Source
An unsolved problem in mathematics named after mathematician Lothar Collatz [https://en.wikipedia.org/wiki/3x_%2B_1_problem](https://en.wikipedia.org/wiki/3x_%2B_1_problem)
## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

View File

@@ -2,10 +2,10 @@ import unittest
from collatz_conjecture import collatz_steps
# Tests adapted from `problem-specifications//canonical-data.json` @ v1.1.1
# Tests adapted from `problem-specifications//canonical-data.json` @ v1.2.1
class CollatzConjectureTests(unittest.TestCase):
class CollatzConjectureTest(unittest.TestCase):
def test_zero_steps_for_one(self):
self.assertEqual(collatz_steps(1), 0)
@@ -20,12 +20,22 @@ class CollatzConjectureTests(unittest.TestCase):
self.assertEqual(collatz_steps(1000000), 152)
def test_zero_is_invalid_input(self):
self.assertEqual(collatz_steps(0), None)
with self.assertRaisesWithMessage(ValueError):
collatz_steps(0)
def test_negative_number_is_invalid_input(self):
self.assertEqual(collatz_steps(-1), None)
with self.assertRaisesWithMessage(ValueError):
collatz_steps(-15)
self.assertEqual(collatz_steps(-15), None)
# 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__':

View File

@@ -1,6 +1,6 @@
def collatz_steps(n):
if n <= 0:
return
raise ValueError("input should be positive")
step_count = 0
while n > 1:

View File

@@ -35,6 +35,7 @@ Assume the programming language you are using does not have an implementation of
See [Emulating numeric types](https://docs.python.org/2/reference/datamodel.html#emulating-numeric-types) for help on operator overloading.
## Exception messages
Sometimes it is necessary to raise an exception. When you do this, you should include a meaningful error message to
@@ -43,25 +44,43 @@ every exercise will require you to raise an exception, but for those that do, th
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:
`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 complex_numbers_test.py`
- Python 3.4+: `pytest complex_numbers_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 complex_numbers_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/python/<exerciseName>` directory.
Note that, when trying to submit an exercise, make sure the solution is in the `$EXERCISM_WORKSPACE/python/complex-numbers` directory.
For example, if you're submitting `bob.py` for the Bob exercise, the submit command would be something like `exercism submit <path_to_exercism_dir>/python/bob/bob.py`.
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 the [help page](http://exercism.io/languages/python).
please see [Running the Tests](http://exercism.io/tracks/python/tests).
## Source
Wikipedia [https://en.wikipedia.org/wiki/Complex_number](https://en.wikipedia.org/wiki/Complex_number)
## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

View File

@@ -7,11 +7,40 @@ import math
from complex_numbers import ComplexNumber
# Tests adapted from `problem-specifications//canonical-data.json` @ v1.0.0
# Tests adapted from `problem-specifications//canonical-data.json` @ v1.3.0
class ComplexNumbersTest(unittest.TestCase):
def test_real_part_of_a_purely_real_number(self):
input_number = ComplexNumber(1, 0)
self.assertEqual(input_number.real, 1)
def test_real_part_of_a_purely_imaginary_number(self):
input_number = ComplexNumber(0, 1)
self.assertEqual(input_number.real, 0)
def test_real_part_of_a_number_with_real_and_imaginary_part(self):
input_number = ComplexNumber(1, 2)
self.assertEqual(input_number.real, 1)
def test_imaginary_part_of_a_purely_real_number(self):
input_number = ComplexNumber(1, 0)
self.assertEqual(input_number.imaginary, 0)
def test_imaginary_part_of_a_purely_imaginary_number(self):
input_number = ComplexNumber(0, 1)
self.assertEqual(input_number.imaginary, 1)
def test_imaginary_part_of_a_number_with_real_and_imaginary_part(self):
input_number = ComplexNumber(1, 2)
self.assertEqual(input_number.imaginary, 2)
def test_imaginary_unit(self):
first_input = ComplexNumber(0, 1)
second_input = ComplexNumber(0, 1)
expected = ComplexNumber(-1, 0)
self.assertEqual(first_input * second_input, expected)
def test_add_purely_real_numbers(self):
first_input = ComplexNumber(1, 0)
second_input = ComplexNumber(2, 0)
@@ -120,47 +149,33 @@ class ComplexNumbersTest(unittest.TestCase):
self.assertEqual(input_number.conjugate().imaginary,
expected.imaginary)
def test_real_part_of_a_purely_real_number(self):
input_number = ComplexNumber(1, 0)
self.assertEqual(input_number.real, 1)
def test_real_part_of_a_purely_imaginary_number(self):
input_number = ComplexNumber(0, 1)
self.assertEqual(input_number.real, 0)
def test_real_part_of_a_number_with_real_and_imaginary_part(self):
input_number = ComplexNumber(1, 2)
self.assertEqual(input_number.real, 1)
def test_imaginary_part_of_a_purely_real_number(self):
input_number = ComplexNumber(1, 0)
self.assertEqual(input_number.imaginary, 0)
def test_imaginary_part_of_a_purely_imaginary_number(self):
input_number = ComplexNumber(0, 1)
self.assertEqual(input_number.imaginary, 1)
def test_imaginary_part_of_a_number_with_real_and_imaginary_part(self):
input_number = ComplexNumber(1, 2)
self.assertEqual(input_number.imaginary, 2)
def test_eulers_identity_formula(self):
input_number = ComplexNumber(0, math.pi)
expected = ComplexNumber(-1, 0)
self.assertEqual(input_number.exp().real, expected.real)
self.assertEqual(input_number.exp().imaginary, expected.imaginary)
actual = input_number.exp()
self.assertAlmostEqual(actual.real, expected.real)
self.assertAlmostEqual(actual.imaginary, expected.imaginary)
def test_exponential_of_0(self):
input_number = ComplexNumber(0, 0)
expected = ComplexNumber(1, 0)
self.assertEqual(input_number.exp().real, expected.real)
self.assertEqual(input_number.exp().imaginary, expected.imaginary)
actual = input_number.exp()
self.assertAlmostEqual(actual.real, expected.real)
self.assertAlmostEqual(actual.imaginary, expected.imaginary)
def test_exponential_of_a_purely_real_number(self):
input_number = ComplexNumber(1, 0)
expected = ComplexNumber(math.e, 0)
self.assertEqual(input_number.exp().real, expected.real)
self.assertEqual(input_number.exp().imaginary, expected.imaginary)
actual = input_number.exp()
self.assertAlmostEqual(actual.real, expected.real)
self.assertAlmostEqual(actual.imaginary, expected.imaginary)
def test_exponential_of_a_number_with_real_and_imaginary_part(self):
input_number = ComplexNumber(math.log(2), math.pi)
expected = ComplexNumber(-2, 0)
actual = input_number.exp()
self.assertAlmostEqual(actual.real, expected.real)
self.assertAlmostEqual(actual.imaginary, expected.imaginary)
if __name__ == '__main__':

View File

@@ -40,6 +40,6 @@ class ComplexNumber(object):
return ComplexNumber(self.real, -1 * self.imaginary)
def exp(self):
r = round(math.cos(self.imaginary), 8) * math.exp(self.real)
i = round(math.sin(self.imaginary), 8) * math.exp(self.real)
r = math.cos(self.imaginary) * math.exp(self.real)
i = math.sin(self.imaginary) * math.exp(self.real)
return ComplexNumber(r, i)

View File

@@ -38,22 +38,39 @@ every exercise will require you to raise an exception, but for those that do, th
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:
`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 connect_test.py`
- Python 3.4+: `pytest connect_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 connect_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/python/<exerciseName>` directory.
Note that, when trying to submit an exercise, make sure the solution is in the `$EXERCISM_WORKSPACE/python/connect` directory.
For example, if you're submitting `bob.py` for the Bob exercise, the submit command would be something like `exercism submit <path_to_exercism_dir>/python/bob/bob.py`.
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 the [help page](http://exercism.io/languages/python).
please see [Running the Tests](http://exercism.io/tracks/python/tests).
## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

View File

@@ -12,11 +12,15 @@ regarded as forming a rectangle when printed with intervening newlines.
For example, the sentence
> If man was meant to stay on the ground, god would have given us roots.
```text
"If man was meant to stay on the ground, god would have given us roots."
```
is normalized to:
> ifmanwasmeanttostayonthegroundgodwouldhavegivenusroots
```text
"ifmanwasmeanttostayonthegroundgodwouldhavegivenusroots"
```
The plaintext should be organized in to a rectangle. The size of the
rectangle (`r x c`) should be decided by the length of the message,
@@ -27,13 +31,13 @@ Our normalized text is 54 characters long, dictating a rectangle with
`c = 8` and `r = 7`:
```text
ifmanwas
meanttos
tayonthe
groundgo
dwouldha
vegivenu
sroots
"ifmanwas"
"meanttos"
"tayonthe"
"groundgo"
"dwouldha"
"vegivenu"
"sroots "
```
The coded message is obtained by reading down the columns going left to
@@ -42,31 +46,30 @@ right.
The message above is coded as:
```text
imtgdvsfearwermayoogoanouuiontnnlvtwttddesaohghnsseoau
"imtgdvsfearwermayoogoanouuiontnnlvtwttddesaohghnsseoau"
```
Output the encoded text in chunks. Phrases that fill perfect rectangles
`(r X c)` should be output `c` chunks of `r` length, separated by spaces.
Phrases that do not fill perfect rectangles will have `n` empty spaces.
Those spaces should be distributed evenly, added to the end of the last
`n` chunks.
Output the encoded text in chunks that fill perfect rectangles `(r X c)`,
with `c` chunks of `r` length, separated by spaces. For phrases that are
`n` characters short of the perfect rectangle, pad each of the last `n`
chunks with a single trailing space.
```text
imtgdvs fearwer mayoogo anouuio ntnnlvt wttddes aohghn sseoau
"imtgdvs fearwer mayoogo anouuio ntnnlvt wttddes aohghn sseoau "
```
Notice that were we to stack these, we could visually decode the
cyphertext back in to the original message:
```text
imtgdvs
fearwer
mayoogo
anouuio
ntnnlvt
wttddes
aohghn
sseoau
"imtgdvs"
"fearwer"
"mayoogo"
"anouuio"
"ntnnlvt"
"wttddes"
"aohghn "
"sseoau "
```
## Exception messages
@@ -77,25 +80,43 @@ every exercise will require you to raise an exception, but for those that do, th
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:
`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 crypto_square_test.py`
- Python 3.4+: `pytest crypto_square_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 crypto_square_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/python/<exerciseName>` directory.
Note that, when trying to submit an exercise, make sure the solution is in the `$EXERCISM_WORKSPACE/python/crypto-square` directory.
For example, if you're submitting `bob.py` for the Bob exercise, the submit command would be something like `exercism submit <path_to_exercism_dir>/python/bob/bob.py`.
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 the [help page](http://exercism.io/languages/python).
please see [Running the Tests](http://exercism.io/tracks/python/tests).
## Source
J Dalbey's Programming Practice problems [http://users.csc.calpoly.edu/~jdalbey/103/Projects/ProgrammingPractice.html](http://users.csc.calpoly.edu/~jdalbey/103/Projects/ProgrammingPractice.html)
## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

View File

@@ -3,7 +3,7 @@ import unittest
from crypto_square import encode
# Tests adapted from `problem-specifications//canonical-data.json` @ v3.1.0
# Tests adapted from `problem-specifications//canonical-data.json` @ v3.2.0
class CryptoSquareTest(unittest.TestCase):
def test_empty_string(self):

View File

@@ -1,4 +1,4 @@
# Custom-Set
# Custom Set
Create a custom set type.
@@ -7,14 +7,47 @@ type, like a set. In this exercise you will define your own set. How it
works internally doesn't matter, as long as it behaves like a set of
unique elements.
## 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 custom_set_test.py`
- Python 3.4+: `pytest custom_set_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 custom_set_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/python/<exerciseName>` directory.
Note that, when trying to submit an exercise, make sure the solution is in the `$EXERCISM_WORKSPACE/python/custom-set` directory.
For example, if you're submitting `bob.py` for the Bob exercise, the submit command would be something like `exercism submit <path_to_exercism_dir>/python/bob/bob.py`.
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 the [help page](http://exercism.io/languages/python).
please see [Running the Tests](http://exercism.io/tracks/python/tests).
## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

View File

@@ -23,8 +23,8 @@ class CustomSet(object):
def intersection(self, other):
pass
def difference(self, other):
def __sub__(self, other):
pass
def union(self, other):
def __add__(self, other):
pass

View File

@@ -3,7 +3,7 @@ import unittest
from custom_set import CustomSet
# Tests adapted from `problem-specifications//canonical-data.json` @ v1.1.0
# Tests adapted from `problem-specifications//canonical-data.json` @ v1.3.0
class CustomSetTest(unittest.TestCase):
def test_sets_with_no_elements_are_empty(self):

65
exercises/darts/README.md Normal file
View File

@@ -0,0 +1,65 @@
# Darts
Write a function that returns the earned points in a single toss of a Darts game.
[Darts](https://en.wikipedia.org/wiki/Darts) is a game where players
throw darts to a [target](https://en.wikipedia.org/wiki/Darts#/media/File:Darts_in_a_dartboard.jpg).
In our particular instance of the game, the target rewards with 4 different amounts of points, depending on where the dart lands:
* If the dart lands outside the target, player earns no points (0 points).
* If the dart lands in the outer circle of the target, player earns 1 point.
* If the dart lands in the middle circle of the target, player earns 5 points.
* If the dart lands in the inner circle of the target, player earns 10 points.
The outer circle has a radius of 10 units (This is equivalent to the total radius for the entire target), the middle circle a radius of 5 units, and the inner circle a radius of 1. Of course, they are all centered to the same point (That is, the circles are [concentric](http://mathworld.wolfram.com/ConcentricCircles.html)) defined by the coordinates (0, 0).
Write a function that given a point in the target (defined by its `real` cartesian coordinates `x` and `y`), returns the correct amount earned by a dart landing in that point.
## 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 darts_test.py`
- Python 3.4+: `pytest darts_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 darts_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/darts` 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
Inspired by an excersie created by a professor Della Paolera in Argentina
## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

2
exercises/darts/darts.py Normal file
View File

@@ -0,0 +1,2 @@
def score(x, y):
pass

View File

@@ -0,0 +1,31 @@
import unittest
from darts import score
# Tests adapted from `problem-specifications//canonical-data.json` @ v1.1.0
class darts_test(unittest.TestCase):
def test_dart_lands_outside_target(self):
self.assertEqual(score(-9, 9), 0)
def test_dart_lands_just_in_border_of_target(self):
self.assertEqual(score(0, 10), 1)
def test_dart_lands_outer_circle(self):
self.assertEqual(score(4, 4), 1)
def test_dart_lands_border_between_outside_middle(self):
self.assertEqual(score(5, 0), 5)
def test_dart_lands_in_middle_circle(self):
self.assertEqual(score(0.8, -0.8), 5)
def test_dart_lands_border_betweeen_middle_inner(self):
self.assertEqual(score(0, -1), 10)
def test_dart_lands_inner_circle(self):
self.assertEqual(score(-0.1, -0.1), 10)
if __name__ == '__main__':
unittest.main()

View File

@@ -0,0 +1,14 @@
from math import sqrt
def score(x, y):
dart_location = sqrt(x * x + y * y)
if dart_location <= 1.0:
return 10
elif dart_location <= 5.0:
return 5
elif dart_location <= 10.0:
return 1
else:
return 0

View File

@@ -60,25 +60,43 @@ every exercise will require you to raise an exception, but for those that do, th
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:
`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 diamond_test.py`
- Python 3.4+: `pytest diamond_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 diamond_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/python/<exerciseName>` directory.
Note that, when trying to submit an exercise, make sure the solution is in the `$EXERCISM_WORKSPACE/python/diamond` directory.
For example, if you're submitting `bob.py` for the Bob exercise, the submit command would be something like `exercism submit <path_to_exercism_dir>/python/bob/bob.py`.
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 the [help page](http://exercism.io/languages/python).
please see [Running the Tests](http://exercism.io/tracks/python/tests).
## Source
Seb Rose [http://claysnow.co.uk/recycling-tests-in-tdd/](http://claysnow.co.uk/recycling-tests-in-tdd/)
## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

View File

@@ -3,9 +3,9 @@ import unittest
from diamond import make_diamond
# Tests adapted from `problem-specifications//canonical-data.json` @ v1.0.0
# Tests adapted from `problem-specifications//canonical-data.json` @ v1.1.0
class DiamondTests(unittest.TestCase):
class DiamondTest(unittest.TestCase):
def test_degenerate_case_with_a_single_row(self):
self.assertMultiLineEqual(make_diamond('A'), 'A\n')

View File

@@ -20,25 +20,43 @@ every exercise will require you to raise an exception, but for those that do, th
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:
`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 difference_of_squares_test.py`
- Python 3.4+: `pytest difference_of_squares_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 difference_of_squares_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/python/<exerciseName>` directory.
Note that, when trying to submit an exercise, make sure the solution is in the `$EXERCISM_WORKSPACE/python/difference-of-squares` directory.
For example, if you're submitting `bob.py` for the Bob exercise, the submit command would be something like `exercism submit <path_to_exercism_dir>/python/bob/bob.py`.
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 the [help page](http://exercism.io/languages/python).
please see [Running the Tests](http://exercism.io/tracks/python/tests).
## Source
Problem 6 at Project Euler [http://projecteuler.net/problem=6](http://projecteuler.net/problem=6)
## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

View File

@@ -3,7 +3,7 @@ import unittest
from difference_of_squares import difference, square_of_sum, sum_of_squares
# Tests adapted from `problem-specifications//canonical-data.json` @ v1.1.0
# Tests adapted from `problem-specifications//canonical-data.json` @ v1.2.0
class DifferenceOfSquaresTest(unittest.TestCase):
def test_square_of_sum_1(self):

View File

@@ -62,25 +62,43 @@ every exercise will require you to raise an exception, but for those that do, th
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:
`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 diffie_hellman_test.py`
- Python 3.4+: `pytest diffie_hellman_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 diffie_hellman_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/python/<exerciseName>` directory.
Note that, when trying to submit an exercise, make sure the solution is in the `$EXERCISM_WORKSPACE/python/diffie-hellman` directory.
For example, if you're submitting `bob.py` for the Bob exercise, the submit command would be something like `exercism submit <path_to_exercism_dir>/python/bob/bob.py`.
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 the [help page](http://exercism.io/languages/python).
please see [Running the Tests](http://exercism.io/tracks/python/tests).
## Source
Wikipedia, 1024 bit key from www.cryptopp.com/wiki. [http://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange](http://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange)
## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

View File

@@ -3,23 +3,25 @@ import unittest
import diffie_hellman
# Tests adapted from `problem-specifications//canonical-data.json` @ v1.0.0
class DiffieHellmanTest(unittest.TestCase):
def test_private_in_range(self):
def test_private_key_is_in_range(self):
primes = [5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]
for i in primes:
self.assertTrue(1 < diffie_hellman.private_key(i) < i)
# Can fail due to randomness, but most likely will not,
# due to pseudo-randomness and the large number chosen
def test_private_key_randomness(self):
def test_private_key_is_random(self):
p = 2147483647
private_keys = []
for i in range(5):
private_keys.append(diffie_hellman.private_key(p))
self.assertEqual(len(list(set(private_keys))), len(private_keys))
self.assertEqual(len(set(private_keys)), len(private_keys))
def test_public_key_correct(self):
def test_can_calculate_public_key_using_private_key(self):
p = 23
g = 5
private = 6
@@ -28,7 +30,7 @@ class DiffieHellmanTest(unittest.TestCase):
actual = diffie_hellman.public_key(p, g, private)
self.assertEqual(actual, expected)
def test_secret_key_correct(self):
def test_can_calculate_secret_using_other_party_s_public_key(self):
p = 23
public = 19
private = 6
@@ -37,50 +39,17 @@ class DiffieHellmanTest(unittest.TestCase):
actual = diffie_hellman.secret(p, public, private)
self.assertEqual(actual, expected)
def test_secret_key_correct_large_nums(self):
p = int("""120227323036150778550155526710966921740030662\
69457894729842354923526575959371158734103742634711454153\
30066288563005527069961435922404533456428692335628867529\
30249953227657883929905072620233073626594386072962776144\
69143365881426187411323246174903542571280506720291038940\
7991986070558964461330091797026762932543""".replace(
"\n", "").replace(" ", ""))
public = int("""7520544115435791944292554616920871123548\
58559049691782063133092992058683123990461493675163366079\
66149689640419216591714331722664409474612463910928128055\
99415792293044373353565984826436410603792531597409532111\
27577117569121441377056137760635413505489115127155125391\
86192176020596861210448363099541947258202188""".replace(
"\n", "").replace(" ", ""))
private = int("""248347939362593293991108130435688850515\
37971354473275017926961991904690152151776307586179022004\
17377685436170904594686456961202706692908603181062371925\
882""".replace("\n", "").replace(" ", ""))
expected = int("""70900735223964890815905879227737819348\
80851869892044649134650898046120174656773533145582564442\
98779465564310958207858354973848497783442169812282262526\
39932672153547963980483673419756271345828771971984887453\
01448857224581986445413661898091472983952358126388674082\
1363010486083940557620831348661126601106717071""".replace(
"\n", "").replace(" ", ""))
actual = diffie_hellman.secret(p, public, private)
self.assertEqual(actual, expected)
def test_exchange(self):
def test_key_exchange(self):
p = 23
g = 5
alice_private_key = diffie_hellman.private_key(p)
bob_private_key = diffie_hellman.private_key(p)
alice_public_key = diffie_hellman.public_key(p, g, alice_private_key)
bob_public_key = diffie_hellman.public_key(p, g, bob_private_key)
secret_a = diffie_hellman.secret(p, bob_public_key, alice_private_key)
secret_b = diffie_hellman.secret(p, alice_public_key, bob_private_key)
privateA = diffie_hellman.private_key(p)
privateB = diffie_hellman.private_key(p)
publicA = diffie_hellman.public_key(p, g, privateA)
publicB = diffie_hellman.public_key(p, g, privateB)
secretA = diffie_hellman.secret(p, publicB, privateA)
secretB = diffie_hellman.secret(p, publicA, privateB)
self.assertEqual(secretA, secretB)
self.assertEqual(secret_a, secret_b)
if __name__ == '__main__':

View File

@@ -0,0 +1,82 @@
# Dnd Character
For a game of [Dungeons & Dragons][DND], each player starts by generating a
character they can play with. This character has, among other things, six
abilities; strength, dexterity, constitution, intelligence, wisdom and
charisma. These six abilities have scores that are determined randomly. You
do this by rolling four 6-sided dice and record the sum of the largest three
dice. You do this six times, once for each ability.
Your character's initial hitpoints are 10 + your character's constitution
modifier. You find your character's constitution modifier by subtracting 10
from your character's constitution, divide by 2 and round down.
Write a random character generator that follows the rules above.
For example, the six throws of four dice may look like:
* 5, 3, 1, 6: You discard the 1 and sum 5 + 3 + 6 = 14, which you assign to strength.
* 3, 2, 5, 3: You discard the 2 and sum 3 + 5 + 3 = 11, which you assign to dexterity.
* 1, 1, 1, 1: You discard the 1 and sum 1 + 1 + 1 = 3, which you assign to constitution.
* 2, 1, 6, 6: You discard the 1 and sum 2 + 6 + 6 = 14, which you assign to intelligence.
* 3, 5, 3, 4: You discard the 3 and sum 5 + 3 + 4 = 12, which you assign to wisdom.
* 6, 6, 6, 6: You discard the 6 and sum 6 + 6 + 6 = 18, which you assign to charisma.
Because constitution is 3, the constitution modifier is -4 and the hitpoints are 6.
## Notes
Most programming languages feature (pseudo-)random generators, but few
programming languages are designed to roll dice. One such language is [Troll].
[DND]: https://en.wikipedia.org/wiki/Dungeons_%26_Dragons
[Troll]: http://hjemmesider.diku.dk/~torbenm/Troll/
## 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 dnd_character_test.py`
- Python 3.4+: `pytest dnd_character_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 dnd_character_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/dnd-character` 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
Simon Shine, Erik Schierboom [https://github.com/exercism/problem-specifications/issues/616#issuecomment-437358945](https://github.com/exercism/problem-specifications/issues/616#issuecomment-437358945)
## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

View File

@@ -0,0 +1,3 @@
class Character:
def __init__(self):
pass

Some files were not shown because too many files have changed in this diff Show More