From 79e00f6ffe90ca2eb8e3a3072c161caec9f33940 Mon Sep 17 00:00:00 2001 From: Egor Kostan Date: Sat, 22 Nov 2025 20:42:48 -0800 Subject: [PATCH 1/5] Add Resistor Color Expert exercise Introduces the Resistor Color Expert exercise with implementation, tests, documentation, and configuration files. The exercise translates resistor color bands into human-readable labels including value, units, and tolerance. --- resistor-color-expert/.exercism/config.json | 20 +++ resistor-color-expert/.exercism/metadata.json | 1 + resistor-color-expert/HELP.md | 130 ++++++++++++++++++ resistor-color-expert/README.md | 113 +++++++++++++++ .../resistor_color_expert.py | 84 +++++++++++ .../resistor_color_expert_test.py | 63 +++++++++ 6 files changed, 411 insertions(+) create mode 100644 resistor-color-expert/.exercism/config.json create mode 100644 resistor-color-expert/.exercism/metadata.json create mode 100644 resistor-color-expert/HELP.md create mode 100644 resistor-color-expert/README.md create mode 100644 resistor-color-expert/resistor_color_expert.py create mode 100644 resistor-color-expert/resistor_color_expert_test.py diff --git a/resistor-color-expert/.exercism/config.json b/resistor-color-expert/.exercism/config.json new file mode 100644 index 0000000..edf50b4 --- /dev/null +++ b/resistor-color-expert/.exercism/config.json @@ -0,0 +1,20 @@ +{ + "authors": [ + "meatball133", + "bethanyg" + ], + "files": { + "solution": [ + "resistor_color_expert.py" + ], + "test": [ + "resistor_color_expert_test.py" + ], + "example": [ + ".meta/example.py" + ] + }, + "blurb": "Convert color codes as used on resistors with different bands to a human-readable label.", + "source": "Based on earlier resistor color exercises made by Erik Schierboom and Maud de Vries", + "source_url": "https://github.com/exercism/problem-specifications/issues/1464" +} diff --git a/resistor-color-expert/.exercism/metadata.json b/resistor-color-expert/.exercism/metadata.json new file mode 100644 index 0000000..2ae16b1 --- /dev/null +++ b/resistor-color-expert/.exercism/metadata.json @@ -0,0 +1 @@ +{"track":"python","exercise":"resistor-color-expert","id":"f217b52b157b4a4a956ce7f1aad6fe35","url":"https://exercism.org/tracks/python/exercises/resistor-color-expert","handle":"myFirstCode","is_requester":true,"auto_approve":false} \ No newline at end of file diff --git a/resistor-color-expert/HELP.md b/resistor-color-expert/HELP.md new file mode 100644 index 0000000..c2c28ac --- /dev/null +++ b/resistor-color-expert/HELP.md @@ -0,0 +1,130 @@ +# Help + +## Running the tests + +We use [pytest][pytest: Getting Started Guide] as our website test runner. +You will need to install `pytest` on your development machine if you want to run tests for the Python track locally. +You should also install the following `pytest` plugins: + +- [pytest-cache][pytest-cache] +- [pytest-subtests][pytest-subtests] + +Extended information can be found in our website [Python testing guide][Python track tests page]. + + +### Running Tests + +To run the included tests, navigate to the folder where the exercise is stored using `cd` in your terminal (_replace `{exercise-folder-location}` below with your path_). +Test files usually end in `_test.py`, and are the same tests that run on the website when a solution is uploaded. + +Linux/MacOS +```bash +$ cd {path/to/exercise-folder-location} +``` + +Windows +```powershell +PS C:\Users\foobar> cd {path\to\exercise-folder-location} +``` + +
+ +Next, run the `pytest` command in your terminal, replacing `{exercise_test.py}` with the name of the test file: + +Linux/MacOS +```bash +$ python3 -m pytest -o markers=task {exercise_test.py} +==================== 7 passed in 0.08s ==================== +``` + +Windows +```powershell +PS C:\Users\foobar> py -m pytest -o markers=task {exercise_test.py} +==================== 7 passed in 0.08s ==================== +``` + + +### Common options +- `-o` : override default `pytest.ini` (_you can use this to avoid marker warnings_) +- `-v` : enable verbose output. +- `-x` : stop running tests on first failure. +- `--ff` : run failures from previous test before running other test cases. + +For additional options, use `python3 -m pytest -h` or `py -m pytest -h`. + + +### Fixing warnings + +If you do not use `pytest -o markers=task` when invoking `pytest`, you might receive a `PytestUnknownMarkWarning` for tests that use our new syntax: + +```bash +PytestUnknownMarkWarning: Unknown pytest.mark.task - is this a typo? You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/mark.html +``` + +To avoid typing `pytest -o markers=task` for every test you run, you can use a `pytest.ini` configuration file. +We have made one that can be downloaded from the top level of the Python track directory: [pytest.ini][pytest.ini]. + +You can also create your own `pytest.ini` file with the following content: + +```ini +[pytest] +markers = + task: A concept exercise task. +``` + +Placing the `pytest.ini` file in the _root_ or _working_ directory for your Python track exercises will register the marks and stop the warnings. +More information on pytest marks can be found in the `pytest` documentation on [marking test functions][pytest: marking test functions with attributes] and the `pytest` documentation on [working with custom markers][pytest: working with custom markers]. + +Information on customizing pytest configurations can be found in the `pytest` documentation on [configuration file formats][pytest: configuration file formats]. + + +### Extending your IDE or Code Editor + +Many IDEs and code editors have built-in support for using `pytest` and other code quality tools. +Some community-sourced options can be found on our [Python track tools page][Python track tools page]. + +[Pytest: Getting Started Guide]: https://docs.pytest.org/en/latest/getting-started.html +[Python track tools page]: https://exercism.org/docs/tracks/python/tools +[Python track tests page]: https://exercism.org/docs/tracks/python/tests +[pytest-cache]:http://pythonhosted.org/pytest-cache/ +[pytest-subtests]:https://github.com/pytest-dev/pytest-subtests +[pytest.ini]: https://github.com/exercism/python/blob/main/pytest.ini +[pytest: configuration file formats]: https://docs.pytest.org/en/6.2.x/customize.html#configuration-file-formats +[pytest: marking test functions with attributes]: https://docs.pytest.org/en/6.2.x/mark.html#raising-errors-on-unknown-marks +[pytest: working with custom markers]: https://docs.pytest.org/en/6.2.x/example/markers.html#working-with-custom-markers + +## Submitting your solution + +You can submit your solution using the `exercism submit resistor_color_expert.py` command. +This command will upload your solution to the Exercism website and print the solution page's URL. + +It's possible to submit an incomplete solution which allows you to: + +- See how others have completed the exercise +- Request help from a mentor + +## Need to get help? + +If you'd like help solving the exercise, check the following pages: + +- The [Python track's documentation](https://exercism.org/docs/tracks/python) +- The [Python track's programming category on the forum](https://forum.exercism.org/c/programming/python) +- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5) +- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs) + +Should those resources not suffice, you could submit your (incomplete) solution to request mentoring. + +Below are some resources for getting help if you run into trouble: + +- [The PSF](https://www.python.org) hosts Python downloads, documentation, and community resources. +- [The Exercism Community on Discord](https://exercism.org/r/discord) +- [Python Community on Discord](https://pythondiscord.com/) is a very helpful and active community. +- [/r/learnpython/](https://www.reddit.com/r/learnpython/) is a subreddit designed for Python learners. +- [#python on Libera.chat](https://www.python.org/community/irc/) this is where the core developers for the language hang out and get work done. +- [Python Community Forums](https://discuss.python.org/) +- [Free Code Camp Community Forums](https://forum.freecodecamp.org/) +- [CodeNewbie Community Help Tag](https://community.codenewbie.org/t/help) +- [Pythontutor](http://pythontutor.com/) for stepping through small code snippets visually. + +Additionally, [StackOverflow](http://stackoverflow.com/questions/tagged/python) is a good spot to search for your problem/question to see if it has been answered already. + If not - you can always [ask](https://stackoverflow.com/help/how-to-ask) or [answer](https://stackoverflow.com/help/how-to-answer) someone else's question. \ No newline at end of file diff --git a/resistor-color-expert/README.md b/resistor-color-expert/README.md new file mode 100644 index 0000000..ea47196 --- /dev/null +++ b/resistor-color-expert/README.md @@ -0,0 +1,113 @@ +# Resistor Color Expert + +Welcome to Resistor Color Expert on Exercism's Python Track. +If you need help running the tests or submitting your code, check out `HELP.md`. + +## Introduction + +If you want to build something using a Raspberry Pi, you'll probably use _resistors_. +Like the previous [`Resistor Color Duo`][resistor-color-duo-exercise] and [`Resistor Color Trio`][resistor-color-trio-exercise] exercises, you will be translating resistor color bands to human-readable labels. + +- Each resistor has a resistance value. +- Resistors are small - so small in fact that if you printed the resistance value on them, it would be hard to read. + To get around this problem, manufacturers print color-coded bands onto the resistors to denote their resistance values. +- Each band acts as a digit of a number. + For example, if they printed a brown band (value 1) followed by a green band (value 5), it would translate to the number 15. + + +[resistor-color-duo-exercise]: https://exercism.org/tracks/python/exercises/resistor-color-duo +[resistor-color-trio-exercise]: https://exercism.org/tracks/python/exercises/resistor-color-trio + +## Instructions + +In this exercise, you are going to create a helpful program so that you don't have to remember the values of the bands. +The program will take 1, 4, or 5 colors as input and output the correct value in ohms. +The color bands are encoded as follows: + +- black: 0 +- brown: 1 +- red: 2 +- orange: 3 +- yellow: 4 +- green: 5 +- blue: 6 +- violet: 7 +- grey: 8 +- white: 9 + +In [`Resistor Color Trio`][resistor-color-trio-exercise] you decoded the first three color bands. +For instance: orange-orange-brown translated to the main value `330`. +In this exercise you will need to add _tolerance_ to the mix. +Tolerance is the maximum amount that a value can be above or below the main value. +For example, if the last band is green, the maximum tolerance will be `±0.5%`. + +The tolerance band will have one of these values: + +- grey - 0.05% +- violet - 0.1% +- blue - 0.25% +- green - 0.5% +- brown - 1% +- red - 2% +- gold - 5% +- silver - 10% + +The four-band resistor is built up like this: + +| Band_1 | Band_2 | Band_3 | band_4 | +| ------- | ------- | ---------- | --------- | +| Value_1 | Value_2 | Multiplier | Tolerance | + +Examples: + +- orange-orange-brown-green would be `330` ohms with a `±0.5%` tolerance. +- orange-orange-red-grey would be `3300` ohms with `±0.05%` tolerance. + +The difference between a four and five-band resistor is that the five-band resistor has an extra band to indicate a more precise value. + +| Band_1 | Band_2 | Band_3 | Band_4 | band_5 | +| ------- | ------- | ------- | ---------- | --------- | +| Value_1 | Value_2 | Value_3 | Multiplier | Tolerance | + +Examples: + +- orange-orange-orange-black-green would be `333` ohms with a `±0.5%` tolerance. +- orange-red-orange-blue-violet would be `323M` ohms with a `±0.10` tolerance. + +There are also one band resistors. +One band resistors only have the color black with a value of 0. + + +Your program should translate an input `list` of resistor band colors into a label: + +"... ohms ...%" + +So an input `list` of `["orange", "orange", "black", "green"]` should return: + +"33 ohms ±0.5%" + +When there are more than a thousand ohms, we say "kiloohms". + That's similar to saying "kilometer" for 1000 meters, and "kilograms" for 1000 grams. + +So an input `list` of `["orange", "orange", "orange", "grey"]` should return: + +"33 kiloohms ±0.05%" + +When there are more than a million ohms, we say "megaohms". + +So an input `list` of `["orange", "orange", "blue", "red"]` should return: + +"33 megaohms ±2%" + +[resistor-color-trio-exercise]: https://exercism.org/tracks/python/exercises/resistor-color-trio + +## Source + +### Created by + +- @meatball133 +- @bethanyg + +### Based on + +Based on earlier resistor color exercises made by Erik Schierboom and Maud de Vries - https://github.com/exercism/problem-specifications/issues/1464 \ No newline at end of file diff --git a/resistor-color-expert/resistor_color_expert.py b/resistor-color-expert/resistor_color_expert.py new file mode 100644 index 0000000..ac5933a --- /dev/null +++ b/resistor-color-expert/resistor_color_expert.py @@ -0,0 +1,84 @@ +""" +Resistor Color Expert. + +In this exercise you will need to add tolerance to the mix. +Tolerance is the maximum amount that a value can be above or +below the main value. For example, if the last band is green, +the maximum tolerance will be ±0.5%. +""" + +# Mapping of resistor color names to their corresponding digit values. +COLOR_VALUES: dict[str, int] = { + "black": 0, + "brown": 1, + "red": 2, + "orange": 3, + "yellow": 4, + "green": 5, + "blue": 6, + "violet": 7, + "grey": 8, + "white": 9, +} + +# Mapping of tolerance band color names to their maximum tolerance suffix. +MAX_TOLERANCE: dict[str, str] = { + "grey": " ±0.05%", + "violet": " ±0.1%", + "blue": " ±0.25%", + "green": " ±0.5%", + "brown": " ±1%", + "red": " ±2%", + "gold": " ±5%", + "silver": " ±10%", +} + + +def resistor_label(colors: list[str]) -> str: + """ + Return a human-readable resistor label from band colors. + + Scales to the largest whole unit (ohms, kiloohms, megaohms, gigaohms) + and appends maximum tolerance if present. + + :param colors: Band colors in order. Lengths: + 1 (value only); + 4 (two digits, multiplier, tolerance); + 5 (three digits, multiplier, tolerance). + :type colors: list[str] + :returns: Scaled value with units and optional tolerance (e.g., '4.7 kiloohms ±5%'). + :rtype: str + :raises KeyError: If an unknown color is provided. + """ + + prefix: str = "" + postfix: str = "" + max_tolerance: str = "" + + if len(colors) == 1: + prefix: str = f"{COLOR_VALUES[colors[0]]}" + + if len(colors) == 4: + prefix: str = f"{COLOR_VALUES[colors[0]]}{COLOR_VALUES[colors[1]]}" + postfix = f"{'0' * COLOR_VALUES[colors[2]]}" + max_tolerance = MAX_TOLERANCE[colors[3]] + + if len(colors) == 5: + prefix = f"{COLOR_VALUES[colors[0]]}{COLOR_VALUES[colors[1]]}{COLOR_VALUES[colors[2]]}" + postfix = f"{'0' * COLOR_VALUES[colors[3]]}" + max_tolerance = MAX_TOLERANCE[colors[4]] + + int_val: int = int(prefix + postfix) + + # Scale to the largest whole unit + # (ohms, kiloohms, megaohms, gigaohms) for readability + if 1000 <= int_val < 1000000: + return f"{float(int_val / 1000)}".rstrip(".0") + f" kiloohms{max_tolerance}" + + if 1000000 <= int_val < 1000000000: + return f"{int_val / 1000000} megaohms{max_tolerance}" + + if 1000000000 <= int_val: + return f"{int_val / 1000000000} gigaohms{max_tolerance}" + + return f"{int_val} ohms{max_tolerance}" diff --git a/resistor-color-expert/resistor_color_expert_test.py b/resistor-color-expert/resistor_color_expert_test.py new file mode 100644 index 0000000..20df637 --- /dev/null +++ b/resistor-color-expert/resistor_color_expert_test.py @@ -0,0 +1,63 @@ +# pylint: disable=C0301, C0114, C0115, C0116, R0904 + +import unittest + +from resistor_color_expert import ( + resistor_label, +) + +# Tests adapted from `problem-specifications//canonical-data.json` + + +class ResistorColorExpertTest(unittest.TestCase): + def test_orange_orange_black_and_red(self): + self.assertEqual(resistor_label(["orange", "orange", "black", "red"]), "33 ohms ±2%") + + def test_blue_grey_brown_and_violet(self): + self.assertEqual(resistor_label(["blue", "grey", "brown", "violet"]), "680 ohms ±0.1%") + + def test_red_black_red_and_green(self): + self.assertEqual(resistor_label(["red", "black", "red", "green"]), "2 kiloohms ±0.5%") + + def test_green_brown_orange_and_grey(self): + self.assertEqual( + resistor_label(["green", "brown", "orange", "grey"]), "51 kiloohms ±0.05%" + ) + + def test_one_black_band(self): + self.assertEqual(resistor_label(["black"]), "0 ohms") + + def test_orange_orange_yellow_black_and_brown(self): + self.assertEqual( + resistor_label(["orange", "orange", "yellow", "black", "brown"]), "334 ohms ±1%" + ) + + def test_red_green_yellow_yellow_and_brown(self): + self.assertEqual( + resistor_label(["red", "green", "yellow", "yellow", "brown"]), "2.54 megaohms ±1%" + ) + + def test_blue_grey_white_brown_and_brown(self): + self.assertEqual( + resistor_label(["blue", "grey", "white", "brown", "brown"]), "6.89 kiloohms ±1%" + ) + + def test_violet_orange_red_and_grey(self): + self.assertEqual( + resistor_label(["violet", "orange", "red", "grey"]), "7.3 kiloohms ±0.05%" + ) + + def test_brown_red_orange_green_and_blue(self): + self.assertEqual( + resistor_label(["brown", "red", "orange", "green", "blue"]), "12.3 megaohms ±0.25%" + ) + + def test_brown_black_brown_yellow_and_violet(self): + self.assertEqual( + resistor_label(["brown", "black", "brown", "yellow", "violet"]), "1.01 megaohms ±0.1%" + ) + + def test_brown_black_red_and_red(self): + self.assertEqual( + resistor_label(["brown", "black", "red", "red"]), "1 kiloohms ±2%" + ) From 18548ab7fbae5316ba93d9a9959a3ca71d976a89 Mon Sep 17 00:00:00 2001 From: Egor Kostan Date: Sat, 22 Nov 2025 20:54:30 -0800 Subject: [PATCH 2/5] Update resistor_color_expert.py --- resistor-color-expert/resistor_color_expert.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/resistor-color-expert/resistor_color_expert.py b/resistor-color-expert/resistor_color_expert.py index ac5933a..0cb85d0 100644 --- a/resistor-color-expert/resistor_color_expert.py +++ b/resistor-color-expert/resistor_color_expert.py @@ -59,12 +59,15 @@ def resistor_label(colors: list[str]) -> str: prefix: str = f"{COLOR_VALUES[colors[0]]}" if len(colors) == 4: - prefix: str = f"{COLOR_VALUES[colors[0]]}{COLOR_VALUES[colors[1]]}" + prefix: str = (f"{COLOR_VALUES[colors[0]]}" + f"{COLOR_VALUES[colors[1]]}") postfix = f"{'0' * COLOR_VALUES[colors[2]]}" max_tolerance = MAX_TOLERANCE[colors[3]] if len(colors) == 5: - prefix = f"{COLOR_VALUES[colors[0]]}{COLOR_VALUES[colors[1]]}{COLOR_VALUES[colors[2]]}" + prefix = (f"{COLOR_VALUES[colors[0]]}" + f"{COLOR_VALUES[colors[1]]}" + f"{COLOR_VALUES[colors[2]]}") postfix = f"{'0' * COLOR_VALUES[colors[3]]}" max_tolerance = MAX_TOLERANCE[colors[4]] From 8372b863162fbf2030f4f9316a2a734b6a2589ad Mon Sep 17 00:00:00 2001 From: Egor Kostan Date: Sat, 22 Nov 2025 20:55:39 -0800 Subject: [PATCH 3/5] Update resistor_color_expert.py --- resistor-color-expert/resistor_color_expert.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/resistor-color-expert/resistor_color_expert.py b/resistor-color-expert/resistor_color_expert.py index 0cb85d0..a2b5182 100644 --- a/resistor-color-expert/resistor_color_expert.py +++ b/resistor-color-expert/resistor_color_expert.py @@ -59,15 +59,16 @@ def resistor_label(colors: list[str]) -> str: prefix: str = f"{COLOR_VALUES[colors[0]]}" if len(colors) == 4: - prefix: str = (f"{COLOR_VALUES[colors[0]]}" - f"{COLOR_VALUES[colors[1]]}") + prefix: str = f"{COLOR_VALUES[colors[0]]}{COLOR_VALUES[colors[1]]}" postfix = f"{'0' * COLOR_VALUES[colors[2]]}" max_tolerance = MAX_TOLERANCE[colors[3]] if len(colors) == 5: - prefix = (f"{COLOR_VALUES[colors[0]]}" - f"{COLOR_VALUES[colors[1]]}" - f"{COLOR_VALUES[colors[2]]}") + prefix = ( + f"{COLOR_VALUES[colors[0]]}" + f"{COLOR_VALUES[colors[1]]}" + f"{COLOR_VALUES[colors[2]]}" + ) postfix = f"{'0' * COLOR_VALUES[colors[3]]}" max_tolerance = MAX_TOLERANCE[colors[4]] From 61354b68463736840c27ee2468671aaed63c9c0f Mon Sep 17 00:00:00 2001 From: Egor Kostan Date: Sat, 22 Nov 2025 20:58:03 -0800 Subject: [PATCH 4/5] Update resistor_color_expert.py --- resistor-color-expert/resistor_color_expert.py | 1 - 1 file changed, 1 deletion(-) diff --git a/resistor-color-expert/resistor_color_expert.py b/resistor-color-expert/resistor_color_expert.py index a2b5182..d40bf9a 100644 --- a/resistor-color-expert/resistor_color_expert.py +++ b/resistor-color-expert/resistor_color_expert.py @@ -50,7 +50,6 @@ def resistor_label(colors: list[str]) -> str: :rtype: str :raises KeyError: If an unknown color is provided. """ - prefix: str = "" postfix: str = "" max_tolerance: str = "" From ee00583f4b8c68370137bb5f173ce4b661b43fdd Mon Sep 17 00:00:00 2001 From: Egor Kostan Date: Sat, 22 Nov 2025 21:00:33 -0800 Subject: [PATCH 5/5] Add pylint R0801 disable to color mapping Disabled pylint R0801 (duplicate code) for COLOR_VALUES mapping in both resistor_color_expert.py and resistor_color_trio.py to suppress warnings about code duplication. --- resistor-color-expert/resistor_color_expert.py | 1 + resistor-color-trio/resistor_color_trio.py | 1 + 2 files changed, 2 insertions(+) diff --git a/resistor-color-expert/resistor_color_expert.py b/resistor-color-expert/resistor_color_expert.py index d40bf9a..d471381 100644 --- a/resistor-color-expert/resistor_color_expert.py +++ b/resistor-color-expert/resistor_color_expert.py @@ -8,6 +8,7 @@ """ # Mapping of resistor color names to their corresponding digit values. +# pylint: disable=R0801 COLOR_VALUES: dict[str, int] = { "black": 0, "brown": 1, diff --git a/resistor-color-trio/resistor_color_trio.py b/resistor-color-trio/resistor_color_trio.py index 804aa5f..60f049e 100644 --- a/resistor-color-trio/resistor_color_trio.py +++ b/resistor-color-trio/resistor_color_trio.py @@ -9,6 +9,7 @@ """ # Mapping of resistor color names to their corresponding digit values. +# pylint: disable=R0801 COLOR_VALUES: dict[str, int] = { "black": 0, "brown": 1,