diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 6e269b4ad..eba8a7be2 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -54,8 +54,10 @@ about: A template PR for code review with a checklist - [ ] The function's name describes it's behavior - [ ] The function's name matches the file name + - _It's ok to have extra helper functions if necessary, like with mergesort_ - [ ] The function has correct type annotations -- [ ] The function is not called in the function file +- [ ] The function is not called at the top level of the function file + - _Recursive solutions **can** call the function from **inside** the function body_ ## Strategy @@ -67,7 +69,7 @@ about: A template PR for code review with a checklist ### Don'ts -- [ ] The function's strategy _is not_ described in the documentation +- [ ] The function's strategy _is not_ described in any docstrings or tests - [ ] Comments explain the _strategy_, **not** the _implementation_ - [ ] The function _does not_ have more comments than code - If it does, consider finding a new strategy or a simpler implementation diff --git a/solutions/pine_tree_art.py b/solutions/pine_tree_art.py new file mode 100644 index 000000000..8bf95f0f2 --- /dev/null +++ b/solutions/pine_tree_art.py @@ -0,0 +1,54 @@ +""" +Module for generating and displaying ASCII art of a pine tree. + +This module provides a function to create a visual representation of a pine tree +with customizable height and trunk dimensions. + +Author: fevziismailsahin +Created: 01/09/2025 +""" + + +def pine_tree_art(height: int = 10, trunk_width: int = 3, trunk_height: int = 3) -> str: + """ + Generate an ASCII art representation of a pine tree. + + Parameters: + height (int): The height of the tree (default is 10). + trunk_width (int): The width of the tree trunk (default is 3). + trunk_height (int): The height of the tree trunk (default is 3). + + Returns: + str: The generated ASCII art as a string. + """ + + # Type checks + if ( + not isinstance(height, int) + or not isinstance(trunk_width, int) + or not isinstance(trunk_height, int) + ): + raise TypeError("Height, trunk_width, and trunk_height must be integers") + + # Defensive assertions + if height <= 0: + raise ValueError("Height must be a positive integer") + if trunk_width <= 0: + raise ValueError("Trunk width must be a positive integer") + if trunk_height <= 0: + raise ValueError("Trunk height must be a positive integer") + + # Create the foliage of the tree + tree = [] + for i in range(height): + spaces = " " * (height - i - 1) # Add spaces for alignment (center the stars) + stars = "*" * (2 * i + 1) # Create the stars for the current level + tree.append(spaces + stars) # Append to tree list with correct alignment + + # Create the trunk of the tree + trunk = " " * (height - trunk_width // 2 - 1) + "|" * trunk_width # Centered trunk + for _ in range(trunk_height): + tree.append(trunk) + + # Return the tree as a single string with an extra newline at the end + return "\n".join(tree) + "\n" diff --git a/solutions/tests/test_pine_tree_art.py b/solutions/tests/test_pine_tree_art.py new file mode 100644 index 000000000..cc91aee1d --- /dev/null +++ b/solutions/tests/test_pine_tree_art.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Unit test module for generating ASCII art of a pine tree. + +Test categories: + - Standard cases: correct inputs for height, trunk_width, trunk_height; + - Edge cases; + - Defensive tests: wrong input types, assertions; + +Created on 2025-01-11 +Author: fevziismailsahin +""" + +import unittest + +from ..pine_tree_art import pine_tree_art + + +class TestPineTreeArt(unittest.TestCase): + """Test suite for generating ASCII art of a pine tree.""" + + # Standard cases: correct inputs + def test_standard_case_1(self): + """Test with typical valid inputs for height, trunk_width, trunk_height.""" + expected_result = ( + " *\n" + " ***\n" + " *****\n" + " *******\n" + " *********\n" + " ***********\n" + " *************\n" + " ***************\n" + " *****************\n" + "*******************\n" + " |||\n" + " |||\n" + " |||\n" + ) + self.assertEqual(pine_tree_art(10, 3, 3), expected_result) + + def test_standard_case_2(self): + """Test with smaller tree height and trunk dimensions.""" + expected_result = ( + " *\n ***\n *****\n *******\n*********\n |||\n |||\n |||\n" + ) + self.assertEqual(pine_tree_art(5, 3, 3), expected_result) + + # Edge cases: testing zero or near-zero values + def test_zero_height(self): + """Test when height is zero, which should raise a ValueError.""" + with self.assertRaises(ValueError): + pine_tree_art(0, 3, 3) + + def test_zero_trunk_width(self): + """Test when trunk width is zero, which should raise a ValueError.""" + with self.assertRaises(ValueError): + pine_tree_art(10, 0, 3) + + def test_zero_trunk_height(self): + """Test when trunk height is zero, which should raise a ValueError.""" + with self.assertRaises(ValueError): + pine_tree_art(10, 3, 0) + + # Defensive tests: wrong input types, assertions + def test_invalid_height_type(self): + """Test when 'height' is not an integer, which should raise a TypeError.""" + with self.assertRaises(TypeError): + pine_tree_art("ten", 3, 3) + + def test_invalid_trunk_width_type(self): + """Test when 'trunk_width' is not an integer, which should raise a TypeError.""" + with self.assertRaises(TypeError): + pine_tree_art(10, "three", 3) + + def test_invalid_trunk_height_type(self): + """Test when 'trunk_height' is not an integer, which should raise a TypeError.""" + with self.assertRaises(TypeError): + pine_tree_art(10, 3, "three") + + +if __name__ == "__main__": + unittest.main()