diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index eba8a7be2..cc69ef8e0 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -17,10 +17,12 @@ about: A template PR for code review with a checklist ### Files - [ ] The file name describes the function's behavior +- [ ] There is a module header in the function file - [ ] There is a module docstring in the function file - [ ] The test file's name matches the function file name - `/tests/test_file_name.py` - [ ] There is a module docstring in the tests file +- [ ] There is a module header in the tests file ### Unit Tests @@ -54,7 +56,7 @@ 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_ + - _It's ok to have extra helper functions, like with mergesort_ - [ ] The function has correct type annotations - [ ] The function is not called at the top level of the function file - _Recursive solutions **can** call the function from **inside** the function body_ @@ -86,6 +88,6 @@ about: A template PR for code review with a checklist - [ ] The code follows the strategy as simply as possible - [ ] The implementation is as simple as possible given the strategy - [ ] There are no commented lines of code -- [ ] There are no `print` statements anywhere +- [ ] There are no `print` or `input` statements anywhere in the function or test files - [ ] The code includes defensive assertions - [ ] Defensive assertions include as little logic as possible diff --git a/README.md b/README.md index 5f59c8774..763333d10 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,133 @@ -# MIT Avatars 🌟 +# MIT Avatars -**Collaborate. Innovate. Illuminate.** +![Team Work](assets/team-work.jpg) -Welcome to the repository of **MIT Avatars**, representing -Group 17 in the MIT Emerging Talent Program (Foundations Track for Computer Science -and Data Science program). +Welcome to the **MIT Avatars** repository! This is where our group collaborates, +learns, and grows together by solving Python challenges, sharing knowledge, and +developing innovative coding solutions. + +--- + +## 👥 Meet the Team + +We’re a group of passionate and diverse learners united by a love for coding and +problem-solving. Here's a brief introduction to our team members: + +- **Aseel 🎯** + *Fun Fact:* "I love playing Sudoku—it’s like a workout for my brain!" + +- **Ameen 🏉** + *Fun Fact:* "I am a professional rugby player. I play professional rugby nationally + in Lebanon and represent Palestine internationally." + +- **Maria ✈️** + *Fun Fact:* "I love traveling as much as I love food – the only difference + is, one fills my passport and the other fills my stomach!" + +- **Muhannad 🧑🏻‍💻** + *Fun Fact:* "When I feel lost in the maze of learning, I just hit the 'code' button + because nothing finds the way like programming!" + +- **Maher 🛣️** + *Fun Fact:* "I really like playing rugby." + +- **Malak ❇️** + *Fun Fact:* "I look for purpose in almost everything I do." + +- **Abdulrahman 🕊** + *Fun Fact:* "I'm a medical student who codes and leads teams—blending science, + tech, and leadership in a unique way! I also love cats." + +- **Rouaa 🍲** + *Fun Fact:* "With all my responsibilities between home and study, I still find + the most fun in cooking up meals for my family—because good food is the best + way to bring everyone together!" + +--- + +## 📌 Purpose of this Repository + +This repository is our collaborative space for: + +- 📝 **Sharing Python challenges:** Post interesting problems and work together + on solutions. +- 💡 **Collaborating on innovative solutions:** Share diverse approaches and + refine them. +- 📚 **Documenting our learning journey:** Note lessons learned and best practices. + +--- + +## 🚀 Getting Started + +Here’s how to get involved and contribute: + +### 1️⃣ Fork the Repository + +Click the **Fork** button at the top-right of this page to create your copy of +the repository. + +### 2️⃣ Create an Issue + +Navigate to the **Issues** tab and submit an issue describing the challenge. + +### 3️⃣ Work on the Challenge + +Clone the repository to your local machine using: + +```bash +git clone https://github.com/YOUR-USERNAME/ET6-foundations-group-17.git +``` + +Create a new branch for your work: + +```bash +git checkout -b feature/your-feature-name +``` + +### 4️⃣ Submit Your Work + +Push your changes to the branch: + +```bash +git add . +git commit -m "Description of changes" +git push origin feature/your-feature-name +``` + +Finally, open a pull request and wait for feedback from your team. + +--- + +## 🌟 Code of Conduct + +To foster a welcoming and collaborative environment: + +- Be respectful and constructive in your feedback. +- Share knowledge and support each other. +- Embrace diversity in ideas and approaches. + +--- + +## 🛠️ Tech Stack + +This repository uses: + +- **Python:** Our primary programming language for solving challenges. +- **Git/GitHub:** For version control and collaboration. + +--- + +## 🎯 Goals + +As the MIT Avatars, we aim to: + +- Strengthen our problem-solving skills. +- Enhance our Python proficiency. +- Build a supportive and creative learning community. +- Make it to the MIT program as a team. + +🔗 **Group Repository:** +Check out our repository: +[MIT-Emerging-Talent/ET6-foundations-group-17](https://github.com/MIT-Emerging-Talent/ET6-foundations-group-17) + +![Contributors](https://img.shields.io/github/contributors/MIT-Emerging-Talent/ET6-foundations-group-17) diff --git a/assets/team-work.jpg b/assets/team-work.jpg new file mode 100644 index 000000000..83f556c56 Binary files /dev/null and b/assets/team-work.jpg differ diff --git a/collaboration/communication.md b/collaboration/communication.md index 8fea53570..04088a940 100644 --- a/collaboration/communication.md +++ b/collaboration/communication.md @@ -10,8 +10,8 @@ | Jan. 2nd ✅ | Thursday | Google Meet | Final meeting for the collaboration folder| | Jan. 4th ✅ | Saturday | Zoom Online Meeting | Problems Delivery | | Jan. 6th ✅ | Monday | GitHub | Review Session | -| Jan. 8th | Wednesday | GitHub | Challenges Delivery Deadline | -| Jan. 9th | Tuesday | GitHub | Reviews Deadline | +| Jan. 8th ✅| Wednesday | GitHub | Challenges Delivery Deadline | +| Jan. 9th ✅| Tuesday | GitHub | Reviews Deadline | | Jan. 11th | Saturday | Zoom Online Meeting | Retrospective | ## Communication Channels @@ -19,10 +19,10 @@ How often will we get in touch on each channel, and what we will discuss there: - **Issues**: Used as needed to track tasks, discuss challenges, or document ideas. - Team members should check for updates daily. + Team members should also check for updates on a daily basis. - **Pull Requests**: Created whenever code is ready for review. All team members should aim to review PRs within 24-48 hours. -- **Slack/WhatsApp**: Active daily for quick updates, questions, or informal discussions. +- **Slack/WhatsApp**: Active daily for quick updates, questions, and informal discussions. - **Video Calls**: Scheduled every 3 days to discuss progress, address blockers, and align on upcoming tasks. @@ -32,18 +32,20 @@ How often will we get in touch on each channel, and what we will discuss there: | Name/Day | Sunday | Monday | Tuesday | Wednesday| Thursday | Friday | Saturday| |--------------|--------|---------|----------|----------|--------|-------|------| -| Malak Battatt| 5-8 PM| 5-8 PM | 5-8 PM | 5-8 PM |3-6 PM | 2-4 PM| 2-5 PM | +| Malak Battatt| 5-8PM| 5-8PM | 5-8PM | 5-8PM |3-6PM | 2-4PM| 2-5PM | | Abdulrahman Alsir| 7-9PM| 2-5PM | 4-8PM | 7-9PM | 7-9PM | 6-8PM | 6-8PM | +| Ameen Agha| 2-6PM| 2-6PM | 2-6PM | 2-6PM | 2-6PM | 2-6PM| 2-6PM| | Aseel AbuKmail| 7-10PM| 2-5PM | 5-8PM | 5-7PM | 7-9PM | 6-10PM | 6-8PM | | Maher Assaf | 4-7PM| 4-7PM | 6-7PM | 7-8PM | 7-9PM | 6-8PM | 6-8PM | | Maria Roufail | 4-7PM| 4-7PM | 6-7PM | 7-8PM | 7-9PM | 6-8PM | 6-8PM | | Muhannad Assaf | 4-7PM| 4-7PM | 6-7PM | 7-8PM | 7-9PM | 6-8PM | 6-8PM | -| Rouaa Hamzah | 4-7 PM| 4-7 PM | 4-7 PM | 4-7 PM | 7-9PM | | | +| Rouaa Hamzah | 4-7PM| 4-7PM | 4-7PM | 4-7PM | 7-9PM | 7-9PM| 7-9PM| ### How many hours everyone has per day - Malak Battatt: _~2-4h_; - Abdulrahman Alsir: _~2-4h_; +- Ameen Agha: _~2-4h_; - Aseel AbuKmail: _~2-4h_; - Maher: _~2h_; - Maria: _~2h_; diff --git a/collaboration/constraints.md b/collaboration/constraints.md index ab8ac882d..ead4ac343 100644 --- a/collaboration/constraints.md +++ b/collaboration/constraints.md @@ -27,6 +27,8 @@ discussions. members may create bottlenecks for specific tasks. - **Health and Well-being:** Some members may have personal health issues or responsibilities that could affect their availability. +- **Knowledge Gaps:** Some team members may lack experience with certain tools + or frameworks required for the project, leading to delays. ## Internal: Voluntary diff --git a/collaboration/retrospective.md b/collaboration/retrospective.md index 74e18813b..322785d43 100644 --- a/collaboration/retrospective.md +++ b/collaboration/retrospective.md @@ -4,20 +4,429 @@ ## Stop Doing +Abdulrahman: + +- Waiting until the last minute to complete requirements. + +- Being inactive and not responding. + +- Not listening to feedback. + +- Giving up. + +Malak: + +- Not communicating circumstances with team members. + +- Not putting enough effort into understanding. + +Aseel: + +- Spending excessive time on minor details in discussions. + +- Overloading tasks without prioritization, leading to stress + +Maher: + +- Not giving up, even when things seem impossible. + +- Seeking help earlier rather than last minute. + +- Being less shy. + +Muhannad: + +- Not double-checking assignments/solutions/results. + +- Waiting until the last minute. + +Ameen: + +- Procrastinating. + +- Overthinking minor tasks. + ## Continue Doing +Abdulrahman: + +- Collaboration + +- Continuous communication + +- Support + +Maria: + +- Improving my skills + +- Supporting beyond what I can + +- Seeking solutions + +Malak: + +- Collaborating + +- Discussing + +- Communicating + +- Supporting one another + +Aseel: + +- Regular peer reviews and collaborative coding sessions to maintain quality. + +- Utilizing GitHub effectively for tracking and documentation alignment. + +Maher: + +- Communicating + +- Asking for help + +Muhannad: + +- Improving my skills in personal and professional ways + +- Communicating + +- Assisting + +Ameen: + +- Encouraging team morale + +- Researching solutions + +- Engaging in team meetings + +Rouaa: + +- Actively gathering feedback and suggestions from engaged team members. + +- Summarizing meetings to keep everyone aligned. + +- Supporting one another in applying concepts or tools. + ## Start Doing +Abdulrahman: + +- Communicating personal issues and obstacles early. + +- Putting effort into completing project goals and objectives. + +Maria: + +- Taking more courses regarding Python and coding. + +Malak: + +- Having frequent discussions and feedback meetings. + +- Planning ahead and outlining project steps. + +- Setting milestones for the project. + +Aseel: + +- Setting specific, measurable goals for better progress tracking. + +- Scheduling short check-ins to address blockers early. + +Maher: + +- Being more talkative and active. + +- Setting goals from day one for future projects. + +Muhannad: + +- Improving IT skills by taking more courses and seeking expert guidance. + +Ameen: + +- Skill development. + +- Setting clear milestones. + +- Initiating discussions and meetings. + ## Lessons Learned +Abdulrahman: + +- Ask for help when needed. + +- Set roles early in the project for better collaboration. + +- Be more elaborative with group norms. + +- Communicate and try to understand team members. + +- Be accountable for actions, as they affect the team. + +- Everything can be learned with effort. + +- Planning ahead saves trouble in the future. + +- Flexibility is essential. + +- Learn different approaches to problem-solving. + +- Patience + +Maria: + +- Get familiar with GitHub to avoid CI check failures. + +- Mistakes teach valuable lessons. + +Malak: + +- Python, testing, and documenting are learnable with time and effort. + +- Active discussions early in the project make things easier. + +- Assigning roles increases responsibility and contribution. + +- Sustainable project plans yield better outcomes. + +- Asking for help is okay. + +Aseel: + +- Communication is critical for team alignment and reducing confusion. + +- Flexibility ensures progress during unexpected challenges. + +- Collaboration improves outcomes by dividing tasks based on expertise. + +Maher: + +- Plan early to leave extra time for unexpected issues. + +Muhannad: + +- Try harder; you can always improve. + +- It’s okay to seek guidance when unsure. + +Ameen: + +- Start tasks early for better time management. + +- Asking for help saves time and reduces stress. + +- Adapting to team dynamics is crucial for success. + +Rouaa: + +- Work as a team and maintain commitment to deadlines. + +- Manage time effectively to balance tasks. + +- Collaborate using tools like VS Code and GitHub. + +- Cooperation and respect are vital for shared goals. + ______________________________________________________________________ ## Strategy vs. Board ### What parts of your plan went as expected? +Abdulrahman: + +- Helping those who needed help. + +- Preparing collaboration documents. + +- Productive team meetings. + +Maria: + +- Helping each other. + +- Being productive. + +- Maintaining good communication. + +- Being welcoming and supportive. + +Malak: + +- Collaborating effectively. + +- Supporting team members. + +- Solving challenges together. + +Aseel: + +- Peer reviews and GitHub collaboration were seamless due to clear +teamwork. + +- Foundational exercises stayed on schedule, showing effective planning. + +Maher: + +- The team was welcoming despite my late arrival. + +- Supporting one another. + +Muhannad: + +- Team members were very supportive. + +- Managed everything effectively, even at the last minute. + +Ameen: + +- Collaboration helped solve complex problems faster. + +- Open discussions kept everyone on the same page. + +- A supportive atmosphere encouraged self-confidence. + ### What parts of your plan did not work out? +Abdulrahman: + +- Not all solutions and test files met the requirements. + +- Some team members behaved individually, affecting others negatively. + +- Some members were inactive the entire time. + +- Last-minute completion lowered deliverable quality. + +Maria: + +- Unable to meet as a full team due to conflicting schedules. + +- Misunderstanding with a team member. + +- A team member remained inactive. + +- Deliverables submitted last-minute caused unnecessary stress. + +Malak: + +- The team could never meet as a whole. + +- Errors in test files were unresolved without external help. + +Aseel: + +- Time management was difficult during early sprints. + +- Tool onboarding took longer than expected, delaying progress. + +Maher: + +- The team could not meet as a whole. + +- Language barriers caused communication difficulties. + +Muhannad: + +- Some issues were solved last-minute, causing team stress. + +Ameen: + +- Testing delays caused last-minute fixes. + +- Unclear roles created confusion among members. + +- Scheduling conflicts made team meetings challenging. + +Rouaa: + +- Weak internet connectivity. + +- Busy schedules due to holidays and exams. + ### Did you need to add things that weren't in your strategy? -### Or remove extra steps? +Abdulrahman: + +- Following the project life cycle. + +- A project manager or coordinator. + +- Evaluation and QA techniques. + +Maria: + +- Assigned roles and tasks from day one. + +Malak: + +- A detailed project plan. + +- Assigned roles for each member. + +- Clear milestones for the project. + +- Performance monitoring and analytics. + +Aseel: + +- Additional troubleshooting sessions for blockers improved efficiency. + +Maher: + +- Clear assigned roles. + +- Focus on better performance. + +Muhannad: + +- Develop a better project plan. + +Ameen: + +- Use advanced project management tools for better tracking. + +- Schedule more progress reviews to resolve issues early. + +- Assign clear roles from the start. + +Rouaa: + +- Allocate dedicated time for better planning at the project's start. + +- Maintain mutual support and respect within the team. + +- Ensure clear communication to overcome scheduling issues. + +### Things to Be Removed + +Malak: + +- Extra branches that have no use. + +Aseel: + +- Consolidated reviews into a single GitHub thread instead of multiple +platforms. + +Ameen: + +- Stick to one communication platform to streamline discussions. + +- Avoid redundant features or extra branches that add no value. + +### Final thoughts + +**Rouaa:** Our team achieved remarkable success in terms of collaboration, +problem-solving, and mutual support. Each member contributed to creating a +positive and productive environment that allowed us to overcome challenges +and make progress together. I truly believe we were a great team, working as +one hand to achieve success. The stronger members supported the weaker ones, +and the more active members stepped in to help those who were unavailable. +We treated each other with respect and humility, which made our +collaboration both productive and enjoyable. diff --git a/notes/README.md b/notes/README.md index 17e0f0ded..71fbd38a5 100644 --- a/notes/README.md +++ b/notes/README.md @@ -1 +1,17 @@ # Notes + +## Abdulrahman Ali + +- For the number sort challenge, I uploaded the file via file upload because I +was facing electricity shortage and I had to use my phone to upload the file. +- All the recent updates were done via CLI since the power is partially back and +I have access to a laptop right now. +- The same goes for the other challenges I uploaded via file upload and when I +got my laptop back I updated the files via CLI. +- I updated the functions and test files of some of our team members because +they kept failing the CI checks and this affected all other team members and we +needed to complete our project on time but these issues were keeping us from +commiting any new changes to the repository because they will keep failing the +CI checks. +- We had to remove two challenges because they needed pandas and we weren't sure +we can edit the workflow file to add pandas. diff --git a/solutions/chicken_nugget_fun_solution.py b/solutions/chicken_nugget_fun_solution.py new file mode 100644 index 000000000..cfba29f48 --- /dev/null +++ b/solutions/chicken_nugget_fun_solution.py @@ -0,0 +1,19 @@ +import random + + +def chicken_nugget_fun(): + print("Welcome to the Chicken Nugget Fun Zone!") + + nugget_facts = [ + "Chicken nuggets were invented in the 1950s by Robert C. Baker!", + "The world record for eating chicken nuggets is 746 grams in 3 minutes!", + "McDonald's nuggets come in four shapes: bell, bow-tie, ball, and boot.", + "Try making homemade nuggets with chicken, breadcrumbs, and spices!", + "Some people dip chicken nuggets in honey—have you tried it?", + "Chicken nuggets are eaten by millions around the world every day!", + "Sweet chili sauce makes chicken nuggets extra tasty!", + "You can even make plant-based chicken nuggets now!", + ] + + random_fact = random.choice(nugget_facts) + print(f"Here's your nugget fun: {random_fact}") diff --git a/solutions/chocolate_distribution.py b/solutions/chocolate_distribution.py new file mode 100644 index 000000000..d4cbe0761 --- /dev/null +++ b/solutions/chocolate_distribution.py @@ -0,0 +1,38 @@ +def minimize_chocolate_difference(chocolates, k): + """ + Function to distribute chocolates to minimize the difference between the maximum + and minimum chocolates received. + + Parameters: + chocolates (list): List of integers representing chocolates in packets. + k (int): Number of students. + + Returns: + int: The minimized difference between the maximum and minimum chocolates received. + """ + if k == 0 or len(chocolates) == 0: + return 0 + + if len(chocolates) < k: + return -1 # Not enough packets for the students + + # Sort the packets + chocolates.sort() + + # Initialize the minimum difference + min_diff = float("inf") + + # Find the smallest difference for a group of k packets + for i in range(len(chocolates) - k + 1): + diff = chocolates[i + k - 1] - chocolates[i] + min_diff = min(min_diff, diff) + + return min_diff + + +# Example usage +if __name__ == "__main__": + chocolates = [12, 4, 7, 9, 2, 23, 25, 41, 30, 40, 28, 42, 30, 44, 48, 43, 50] + k = 7 + result = minimize_chocolate_difference(chocolates, k) + print(f"Minimum difference is {result}") diff --git a/solutions/find_pythagorean_triplets.py b/solutions/find_pythagorean_triplets.py new file mode 100644 index 000000000..0ad01b042 --- /dev/null +++ b/solutions/find_pythagorean_triplets.py @@ -0,0 +1,73 @@ +""" +This module finds all Pythagorean triplets (a, b, c) such that a^2 + b^2 = c^2 +within a given range. It also supports finding primitive Pythagorean triplets. + +Contents: +- `gcd`: Function to calculate the greatest common divisor of two integers. +- `find_primitive_pythagorean_triplets`: Function to find all primitive Pythagorean + triplets within a given range. + +Author: Malak Battatt +Date: January 8th, 2025 +""" + +from typing import List, Tuple + + +def gcd(x: int, y: int) -> int: + """ + Calculate the greatest common divisor of x and y using the Euclidean algorithm. + + Args: + x (int): The first integer + y (int): The second integer + + Returns: + int: The greatest common divisor of x and y + + Examples: + >>> gcd(48, 18) + 6 + >>> gcd(56, 98) + 14 + """ + while y: + x, y = y, x % y # Update x and y to proceed with the Euclidean algorithm + return x + + +def find_primitive_pythagorean_triplets(n: int) -> List[Tuple[int, int, int]]: + """ + Find all primitive Pythagorean triplets (a, b, c) within the range [1, n]. + + Args: + n (int): The upper limit of the range to find triplets + + Returns: + List[Tuple[int, int, int]]: A list of tuples representing primitive Pythagorean triplets + + Raises: + ValueError: If n is not a positive integer + + Examples: + >>> find_primitive_pythagorean_triplets(1) + [] + >>> find_primitive_pythagorean_triplets(10) + [(3, 4, 5)] + >>> find_primitive_pythagorean_triplets(20) + [(3, 4, 5), (5, 12, 13), (8, 15, 17)] + >>> find_primitive_pythagorean_triplets(50) + [(3, 4, 5), (5, 12, 13), (8, 15, 17), (7, 24, 25), (20, 21, 29)] + """ + if not isinstance(n, int) or n <= 0: + raise ValueError("The range must be a positive integer") + + triplets = [] + for a in range(1, n + 1): + for b in range(a, n + 1): + c_squared = a**2 + b**2 + c = int(c_squared**0.5) + if c**2 == c_squared and c <= n: + if gcd(a, b) == 1 and gcd(b, c) == 1 and gcd(a, c) == 1: + triplets.append((a, b, c)) + return triplets diff --git a/solutions/is_palindrome.py b/solutions/is_palindrome.py new file mode 100644 index 000000000..3356038a0 --- /dev/null +++ b/solutions/is_palindrome.py @@ -0,0 +1,37 @@ +""" +A module to check if a given string is a palindrome. + +Module contents: + - is_palindrome: checks if a string reads the same forwards and backwards. + +Created on 03-01-25 + +""" + + +def is_palindrome(input_string: str) -> bool: + """ + Checks if a string is a palindrome. + + Parameters: + input_string (str): The string to be checked. + + Returns: + bool: True if the string is a palindrome, False otherwise. + + Raises: + TypeError: If the input is not a string. + + Examples: + >>> is_palindrome("madam") + True + + >>> is_palindrome("hello") + False + + >>> is_palindrome("") + True + """ + if not isinstance(input_string, str): + raise TypeError("Input must be a string.") + return input_string == input_string[::-1] diff --git a/solutions/mirror_words_challenge.py b/solutions/mirror_words_challenge.py new file mode 100644 index 000000000..bd81759eb --- /dev/null +++ b/solutions/mirror_words_challenge.py @@ -0,0 +1,52 @@ +import re + + +def mirror_words(sentence: str) -> str: + """ + Reverses each word in a given sentence while maintaining the order of the words. + Punctuation marks at the end of words are correctly handled, ensuring they remain + in place. + + Args: + sentence (str): The sentence to be processed. It can contain words, punctuation, + spaces, numbers, and special characters. + Returns: + str: A new string where each word is reversed, but the word order, punctuation, + numbers, and special characters remain unchanged. + + Example: + >>> mirror_words("Hello world!") + 'olleH dlrow!' + + >>> mirror_words("Python is fun") + 'nohtyP si nuf' + + >>> mirror_words("Keep calm & code on.") + 'peeK mlac & edoc .no' + + Raises: + TypeError: If the input is not a string. + + Note: + This function processes each word individually, reversing only the characters + in the word while leaving punctuation, numbers, and special characters + unchanged. + + """ + if not isinstance(sentence, str): + raise TypeError("Input must be a string") + + # Split the sentence into words + words = sentence.split() + + mirrored_words_list = [] # List to store the mirrored words + for word in words: + # Match the word and any punctuation at the end + match = re.match(r"([a-zA-Z]+)([^a-zA-Z]*)", word) + if match: + # Reverse the word and keep punctuation at the end without reversing it + mirrored_words_list.append(match.group(1)[::-1] + match.group(2)) + else: + mirrored_words_list.append(word) + + return " ".join(mirrored_words_list) diff --git a/solutions/number_guessing_game.py b/solutions/number_guessing_game.py new file mode 100644 index 000000000..d20f58ec3 --- /dev/null +++ b/solutions/number_guessing_game.py @@ -0,0 +1,120 @@ +""" +This module implements a number guessing game where players select a difficulty +level and attempt to guess a randomly generated number within a limited number of attempts. +The difficulty levels determine the number of attempts allowed. + +Contents: +- `choose_difficulty`: Function to select a difficulty level based on provided choice. +- `number_guessing_game`: Function to simulate the number guessing game. + +Author: Malak Battatt +Date: January 7th, 2025 +""" + +import random + + +def choose_difficulty(choice: int) -> tuple[int, int, str]: + """ + Selects a difficulty level for the game based on the provided choice. + + Args: + choice (int): The user's choice for difficulty level (1-Easy, 2-Medium, 3-Hard). + + Returns: + tuple[int, int, str]: A tuple containing the randomly generated number, + the maximum number of attempts allowed, and a message indicating + the difficulty level selected. + + Examples: + >>> choose_difficulty(1) + (random number between 1 and 50, 15, 'Easy (1 to 50) - 15 attempts') + + >>> choose_difficulty(2) + (random number between 1 and 50, 10, 'Medium (1 to 50) - 10 attempts') + + >>> choose_difficulty(3) + (random number between 1 and 50, 5, 'Hard (1 to 50) - 5 attempts') + + >>> choose_difficulty(4) + (random number between 1 and 50, 10, 'Invalid choice! Defaulting to Medium (1 to 50) + with 10 attempts.') + """ + if choice == 1: + return random.randint(1, 50), 15, "Easy (1 to 50) - 15 attempts" + elif choice == 2: + return random.randint(1, 50), 10, "Medium (1 to 50) - 10 attempts" + elif choice == 3: + return random.randint(1, 50), 5, "Hard (1 to 50) - 5 attempts" + else: + return ( + random.randint(1, 50), + 10, + "Invalid choice! Defaulting to Medium (1 to 50) with 10 attempts.", + ) + + +def number_guessing_game(choice: int, guesses: list[int]) -> list[str]: + """ + Simulates a number guessing game where the player selects a difficulty + level and attempts to guess the randomly generated number within a + limited number of attempts. + + Args: + choice (int): The user's choice for difficulty level (1-Easy, 2-Medium, 3-Hard). + guesses (list[int]): A list of integers representing the player's guesses. + + Returns: + list[str]: A list of strings representing the game's messages. + + Examples: + >>> number_guessing_game(2, [25, 30, 40, 50, 45, 48, 50, 35, 20, 15]) + ['Welcome to the Number Guessing Game!', + "Let's choose a difficulty level:", + 'Medium (1 to 50) - 10 attempts', + '\\nYou have 10 attempts left.', + 'Too high! Try a much lower number.', + '\\nYou have 9 attempts left.', + ... + '🎉 Congratulations! You guessed the number in 6 attempts. 🎉'] + """ + messages = [ + "Welcome to the Number Guessing Game!", + "Let's choose a difficulty level:", + ] + + # Choose the difficulty level + number_to_guess, max_attempts, difficulty_message = choose_difficulty(choice) + messages.append(difficulty_message) + attempts = 0 + guessed_correctly = False + + for guess in guesses: + if attempts >= max_attempts: + break + messages.append(f"\nYou have {max_attempts - attempts} attempts left.") + attempts += 1 + + if guess < number_to_guess: + if number_to_guess - guess > 10: + messages.append("Too low! Try a much higher number.") + else: + messages.append("Too low, but you're close!") + elif guess > number_to_guess: + if guess - number_to_guess > 10: + messages.append("Too high! Try a much lower number.") + else: + messages.append("Too high, but you're close!") + else: + guessed_correctly = True + messages.append( + f"🎉 Congratulations! You guessed the number in {attempts} attempts. 🎉" + ) + break + + if not guessed_correctly: + messages.append( + f"You've run outta attempts. The number was {number_to_guess}. Better luck next time!" + ) + + return messages diff --git a/solutions/number_sort.py b/solutions/number_sort.py new file mode 100644 index 000000000..387be7559 --- /dev/null +++ b/solutions/number_sort.py @@ -0,0 +1,52 @@ +""" +A module for sorting a list of numbers using a custom implementation of the Bubble Sort algorithm. + +Module contents: + - sort: sorts a list of numbers in ascending order using the Bubble Sort algorithm. + - main loop: collects user input until 9 numbers are provided and sorts them. + +Created on 03-01-25 +@author: Abdulrahman Ali + Cody +""" + + +def sort(list_of_num: list) -> list: + """ + Sorts a list of numbers in ascending order using the Bubble Sort algorithm. + + Parameters: + list_of_num (list): A list of numbers to be sorted. + + Returns: + list: The sorted list of numbers. + + Raises: + TypeError: If the input is not a list. + ValueError: If any element in the list is not a number (int or float). + + Strategy: + The function iterates through the list of numbers and compares each element with the next. + If the current element is greater than the next, the elements are swapped. + This process is repeated until the list is sorted in ascending order. + + Examples: + >>> sort([4, 2, 7, 1]) + [1, 2, 4, 7] + + >>> sort([10, -5, 3, 0]) + [-5, 0, 3, 10] + + >>> sort([1, 2, 3, 4]) + [1, 2, 3, 4] + """ + if not isinstance(list_of_num, list): + raise TypeError("Input must be a list.") + + if not all(isinstance(x, (int, float)) for x in list_of_num): + raise ValueError("All elements in the list must be numbers.") + + for i in range(len(list_of_num) - 1): + for j in range(len(list_of_num) - 1 - i): + if list_of_num[j] > list_of_num[j + 1]: + list_of_num[j], list_of_num[j + 1] = list_of_num[j + 1], list_of_num[j] + return list_of_num diff --git a/solutions/rock_paper_scissor.py b/solutions/rock_paper_scissor.py index 8af5d4617..eda534b2b 100644 --- a/solutions/rock_paper_scissor.py +++ b/solutions/rock_paper_scissor.py @@ -3,7 +3,7 @@ Module contents: - play_round: Simulates a round of Rock-Paper-Scissors where the user competes - against the computer. + against the computer. Created on 31-12-24 Updated on 09-01-25 diff --git a/solutions/tests/test_chicken_nugget_fun.py b/solutions/tests/test_chicken_nugget_fun.py new file mode 100644 index 000000000..b49058269 --- /dev/null +++ b/solutions/tests/test_chicken_nugget_fun.py @@ -0,0 +1,50 @@ +import sys +import unittest +from io import StringIO +from pathlib import Path + +sys.path.append(str(Path(__file__).parent.parent)) + +from chicken_nugget_fun_solution import chicken_nugget_fun + + +class TestChickenNuggetFun(unittest.TestCase): + """Test cases for the `chicken_nugget_fun` function.""" + + def test_output(self): + """Test if the output contains a valid chicken nugget fact.""" + # Backup original stdout + original_stdout = sys.stdout + sys.stdout = StringIO() + + try: + # Run the function + chicken_nugget_fun() + + # Get the output + output = sys.stdout.getvalue() + + # Check if the output contains a nugget fact + self.assertTrue( + any( + fact in output + for fact in [ + "Chicken nuggets were invented", + "The world record for eating chicken nuggets", + "McDonald's nuggets come in four shapes", + "Try making homemade nuggets", + "Some people dip chicken nuggets in honey", + "Chicken nuggets are eaten by millions", + "Sweet chili sauce makes chicken nuggets extra tasty", + "You can even make plant-based chicken nuggets", + ] + ), + "Output did not contain a valid chicken nugget fact.", + ) + finally: + # Restore original stdout + sys.stdout = original_stdout + + +if __name__ == "__main__": + unittest.main() diff --git a/solutions/tests/test_chocolate_distribution.py b/solutions/tests/test_chocolate_distribution.py new file mode 100644 index 000000000..9e1a0ad4a --- /dev/null +++ b/solutions/tests/test_chocolate_distribution.py @@ -0,0 +1,54 @@ +import unittest +import sys +from pathlib import Path + +sys.path.append(str(Path(__file__).parent.parent)) +from chocolate_distribution import minimize_chocolate_difference + + +class TestMinimizeChocolateDifference(unittest.TestCase): + def test_valid_case(self): + """Test a valid case with enough packets and students.""" + chocolates = [12, 4, 7, 9, 2, 23, 25, 41, 30, 40, 28, 42, 30, 44, 48, 43, 50] + k = 7 + result = minimize_chocolate_difference(chocolates, k) + self.assertEqual(result, 10) + + def test_not_enough_packets(self): + """Test the case where there are fewer packets than students.""" + chocolates = [1, 2, 3] + k = 5 + result = minimize_chocolate_difference(chocolates, k) + self.assertEqual(result, -1) + + def test_empty_chocolates_list(self): + """Test the case where the chocolates list is empty.""" + chocolates = [] + k = 3 + result = minimize_chocolate_difference(chocolates, k) + self.assertEqual(result, 0) + + def test_zero_students(self): + """Test the case where the number of students is zero.""" + chocolates = [10, 20, 30] + k = 0 + result = minimize_chocolate_difference(chocolates, k) + self.assertEqual(result, 0) + + def test_single_packet(self): + """Test the case with only one packet and one student.""" + chocolates = [42] + k = 1 + result = minimize_chocolate_difference(chocolates, k) + self.assertEqual(result, 0) + + def test_minimized_difference(self): + """Test a generic case to validate the minimized difference calculation.""" + chocolates = [7, 3, 2, 4, 9, 12, 56] + k = 3 + result = minimize_chocolate_difference(chocolates, k) + self.assertEqual(result, 2) + + +if __name__ == "__main__": + unittest.main() diff --git a/solutions/tests/test_find_pythagorean_triplets.py b/solutions/tests/test_find_pythagorean_triplets.py new file mode 100644 index 000000000..b1518a6a3 --- /dev/null +++ b/solutions/tests/test_find_pythagorean_triplets.py @@ -0,0 +1,65 @@ +""" +Unit tests for the find_pythagorean_triplets module. + +This module contains unit tests for the number guessing game, specifically testing +the find_primitive_pythagorean_triplets function. + +Tests included: +- TestFindPrimitivePythagoreanTriplets: Tests for the find_primitive_pythagorean_triplets function. + +Author: Malak Battatt +Date: January 8th, 2025 +""" + +import os +import sys +import unittest + +from ..find_pythagorean_triplets import find_primitive_pythagorean_triplets + +# Add the parent directory to the system path +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + + +class TestFindPrimitivePythagoreanTriplets(unittest.TestCase): + """Unit tests for the `find_primitive_pythagorean_triplets` function.""" + + def test_valid_range(self): + """Test with a valid range value""" + self.assertEqual(find_primitive_pythagorean_triplets(10), [(3, 4, 5)]) + + def test_multiple_triplets(self): + """Test with a range that includes multiple triplets""" + self.assertEqual( + find_primitive_pythagorean_triplets(20), + [(3, 4, 5), (5, 12, 13), (8, 15, 17)], + ) + + def test_no_triplets(self): + """Test with a range that includes no triplets""" + self.assertEqual(find_primitive_pythagorean_triplets(2), []) + + def test_invalid_range_type(self): + """Test with an invalid range type""" + with self.assertRaises(ValueError): + find_primitive_pythagorean_triplets("not a number") + + def test_negative_range(self): + """Test with a negative range value""" + with self.assertRaises(ValueError): + find_primitive_pythagorean_triplets(-5) + + +# I had to comment this test because it was taking too long to run and I couldn't find a way to fix + +# def test_large_input(self): +# """Test with a very large range value to ensure performance""" +# result = find_primitive_pythagorean_triplets( +# 10**6 +# ) # Added test for large input +# # Checking the length to ensure it runs and returns results +# self.assertTrue(len(result) > 0) + + +if __name__ == "__main__": + unittest.main() diff --git a/solutions/tests/test_is_palindrome.py b/solutions/tests/test_is_palindrome.py new file mode 100644 index 000000000..29f9be49c --- /dev/null +++ b/solutions/tests/test_is_palindrome.py @@ -0,0 +1,50 @@ +""" +Test module for the is_palindrome function. + +Test categories: + - Standard cases: typical strings to verify palindrome status + - Edge cases: empty string, single-character strings + - Defensive tests: invalid input types + +Created on 03-01-25 +Updated on 10-01-25 + +""" + +import unittest +import sys +from pathlib import Path + +sys.path.append(str(Path(__file__).parent.parent)) +from is_palindrome import is_palindrome + + +class TestIsPalindrome(unittest.TestCase): + def test_palindrome(self): + """Test for a string that is a palindrome.""" + result = is_palindrome("madam") + self.assertTrue(result) + + def test_non_palindrome(self): + """Test for a string that is not a palindrome.""" + result = is_palindrome("hello") + self.assertFalse(result) + + def test_empty_string(self): + """Test for an empty string.""" + result = is_palindrome("") + self.assertTrue(result) + + def test_single_character(self): + """Test for a single-character string.""" + result = is_palindrome("a") + self.assertTrue(result) + + def test_invalid_input_type(self): + """Test for invalid input types.""" + with self.assertRaises(TypeError): + is_palindrome(12345) + + +if __name__ == "__main__": + unittest.main() diff --git a/solutions/tests/test_mirror_words_challenge.py b/solutions/tests/test_mirror_words_challenge.py new file mode 100644 index 000000000..59a0b0348 --- /dev/null +++ b/solutions/tests/test_mirror_words_challenge.py @@ -0,0 +1,60 @@ +import unittest + +from ..mirror_words_challenge import mirror_words + + +class TestMirrorWords(unittest.TestCase): + """Tests for mirror_words function""" + + # Standard test cases + def test_mirror_words_regular_sentence(self): + """It should mirror each word in a regular sentence while maintaining word order""" + actual = mirror_words("Hello world!") + expected = "olleH dlrow!" + self.assertEqual(actual, expected) + + def test_mirror_words_single_word(self): + """It should reverse a single word in a sentence""" + actual = mirror_words("Python") + expected = "nohtyP" + self.assertEqual(actual, expected) + + def test_mirror_words_with_punctuation(self): + """It should handle words with punctuation marks""" + actual = mirror_words("Python is fun") + expected = "nohtyP si nuf" + self.assertEqual(actual, expected) + + # Edge cases + def test_empty_sentence(self): + """It should return an empty string for an empty sentence""" + actual = mirror_words("") + expected = "" + self.assertEqual(actual, expected) + + def test_single_character(self): + """It should return the single character unchanged""" + actual = mirror_words("A") + expected = "A" + self.assertEqual(actual, expected) + + def test_sentence_with_special_characters(self): + """It should handle sentences with special characters correctly""" + actual = mirror_words("!@# $%^ &*()") + expected = "!@# $%^ &*()" + self.assertEqual(actual, expected) + + # Defensive test cases + def test_non_string_input(self): + """It should raise TypeError for non-string input""" + with self.assertRaises(TypeError): + mirror_words(12345) + + def test_input_with_mixed_data_types(self): + """It should raise TypeError for input with mixed types""" + with self.assertRaises(TypeError): + mirror_words(["Hello", "world!"]) + + +if __name__ == "__main__": + unittest.main() diff --git a/solutions/tests/test_number_guessing_game.py b/solutions/tests/test_number_guessing_game.py new file mode 100644 index 000000000..b146660b5 --- /dev/null +++ b/solutions/tests/test_number_guessing_game.py @@ -0,0 +1,86 @@ +""" +Unit tests for the choose_difficulty function from the number guessing game module. + +This module contains unit tests for the number guessing game, specifically testing +the choose_difficulty function and the number_guessing_game function. + +Tests included: +- TestChooseDifficulty: Tests for the choose_difficulty function. +- TestNumberGuessingGame: Tests for the number_guessing_game function. + +Author: Malak Battatt +Date: January 7th, 2025 +""" + +import os +import sys +import unittest + +# To make the tests pass, change to an absolute import path "from number_guessing_game.py" +from ..number_guessing_game import choose_difficulty, number_guessing_game + +# Add the parent directory to the system path +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + + +class TestChooseDifficulty(unittest.TestCase): + """Unit tests for the `choose_difficulty` function.""" + + def test_easy_difficulty(self): + """Test Easy difficulty with expected range and attempts.""" + number, attempts, message = choose_difficulty(1) + self.assertTrue(1 <= number <= 50) + self.assertEqual(attempts, 15) + self.assertEqual(message, "Easy (1 to 50) - 15 attempts") + + def test_medium_difficulty(self): + """Test Medium difficulty with expected range and attempts.""" + number, attempts, message = choose_difficulty(2) + self.assertTrue(1 <= number <= 50) + self.assertEqual(attempts, 10) + self.assertEqual(message, "Medium (1 to 50) - 10 attempts") + + def test_hard_difficulty(self): + """Test Hard difficulty with expected range and attempts.""" + number, attempts, message = choose_difficulty(3) + self.assertTrue(1 <= number <= 50) + self.assertEqual(attempts, 5) + self.assertEqual(message, "Hard (1 to 50) - 5 attempts") + + def test_invalid_input(self): + """Test defaulting to Medium on invalid input.""" + number, attempts, message = choose_difficulty(4) + self.assertTrue(1 <= number <= 50) + self.assertEqual(attempts, 10) + self.assertEqual( + message, + "Invalid choice! Defaulting to Medium (1 to 50) with 10 attempts.", + ) + + +class TestNumberGuessingGame(unittest.TestCase): + """Unit tests for the `number_guessing_game` function.""" + + def test_game_welcome_message(self): + """Test the welcome message.""" + messages = number_guessing_game(2, [25, 30, 40, 50, 45, 48, 50, 35, 20, 15]) + self.assertIn("Welcome to the Number Guessing Game!", messages) + + def test_game_choose_difficulty(self): + """Test the difficulty selection message.""" + messages = number_guessing_game(2, [25, 30, 40, 50, 45, 48, 50, 35, 20, 15]) + self.assertIn("Let's choose a difficulty level:", messages) + + def test_game_difficulty_message(self): + """Test the difficulty message.""" + messages = number_guessing_game(2, [25, 30, 40, 50, 45, 48, 50, 35, 20, 15]) + self.assertIn("Medium (1 to 50) - 10 attempts", messages) + + def test_game_attempts_message(self): + """Test the attempts message.""" + messages = number_guessing_game(2, [25, 30, 40, 50, 45, 48, 50, 35, 20, 15]) + self.assertIn("\nYou have 10 attempts left.", messages) + + +if __name__ == "__main__": + unittest.main() diff --git a/solutions/tests/test_number_sort.py b/solutions/tests/test_number_sort.py new file mode 100644 index 000000000..f1cb6aea8 --- /dev/null +++ b/solutions/tests/test_number_sort.py @@ -0,0 +1,112 @@ +""" +Test module for the sort function in the custom Bubble Sort implementation. +Contains tests for standard cases, edge cases, and defensive assertions. + +Test categories: + - Standard cases: typical lists of numbers to verify sorting correctness + - Edge cases: empty lists, single-element lists, duplicate values + - Defensive tests: invalid input types, assertions + +Created on 03-01-25 +Updated on 10-01-25 +Author: Cody (Reviewed By Abdulrahman) +""" + +import unittest +import sys +from pathlib import Path + +sys.path.append(str(Path(__file__).parent.parent)) +from number_sort import sort + + +class TestBubbleSort(unittest.TestCase): + """ + Test cases for the custom Bubble Sort function. + """ + + def test_standard_case(self): + """Test for a typical list of unsorted numbers.""" + result = sort([4, 2, 7, 1, 9, 5]) + expected = [1, 2, 4, 5, 7, 9] + self.assertEqual(result, expected) + + def test_reverse_order(self): + """Test for a list in reverse order.""" + result = sort([10, 9, 8, 7, 6, 5]) + expected = [5, 6, 7, 8, 9, 10] + self.assertEqual(result, expected) + + def test_sorted_input(self): + """Test for a list that is already sorted.""" + result = sort([1, 2, 3, 4, 5]) + expected = [1, 2, 3, 4, 5] + self.assertEqual(result, expected) + + def test_single_element(self): + """Test for a list with a single element.""" + result = sort([42]) + expected = [42] + self.assertEqual(result, expected) + + def test_empty_list(self): + """Test for an empty list.""" + result = sort([]) + expected = [] + self.assertEqual(result, expected) + + def test_duplicates(self): + """Test for a list with duplicate numbers.""" + result = sort([4, 2, 7, 2, 5, 4]) + expected = [2, 2, 4, 4, 5, 7] + self.assertEqual(result, expected) + + def test_negative_numbers(self): + """Test for a list with negative numbers.""" + result = sort([-3, -1, -4, -2]) + expected = [-4, -3, -2, -1] + self.assertEqual(result, expected) + + def test_mixed_numbers(self): + """Test for a list with a mix of positive and negative numbers.""" + result = sort([-2, 3, 0, -1, 4]) + expected = [-2, -1, 0, 3, 4] + self.assertEqual(result, expected) + + def test_invalid_input_type(self): + """Test for invalid input where the argument is not a list.""" + with self.assertRaises(TypeError): + sort("not_a_list") + + def test_non_numeric_elements(self): + """Test for a list with non-numeric elements.""" + with self.assertRaises(ValueError): + sort([1, "a", 3]) + + def test_large_numbers(self): + """Test for lists with very large numbers.""" + result = sort([10**6, -(10**6), 0]) + expected = [-(10**6), 0, 10**6] + self.assertEqual(result, expected) + + def test_very_large_numbers(self): + """Test sorting numbers in the millions range.""" + result = sort([2000000, 1000000, 3000000]) + expected = [1000000, 2000000, 3000000] + self.assertEqual(result, expected) + + def test_very_small_numbers(self): + """Test sorting very small decimal numbers.""" + result = sort([0.0001, 0.0003, 0.0002]) + expected = [0.0001, 0.0002, 0.0003] + self.assertEqual(result, expected) + + def test_wide_range_numbers(self): + """Test sorting numbers with very different magnitudes.""" + result = sort([1000000, 0.001, 1, 10000]) + expected = [0.001, 1, 10000, 1000000] + self.assertEqual(result, expected) + + +if __name__ == "__main__": + unittest.main() diff --git a/solutions/tests/test_tic_tac_toe_game.py b/solutions/tests/test_tic_tac_toe_game.py new file mode 100644 index 000000000..ebd44f198 --- /dev/null +++ b/solutions/tests/test_tic_tac_toe_game.py @@ -0,0 +1,53 @@ +import unittest +from unittest.mock import patch +from io import StringIO +import sys +from pathlib import Path + +sys.path.append(str(Path(__file__).parent.parent)) + +# Assume the main Tic-Tac-Toe functions are imported from the main file +from tic_tac_toe_game import check_winner, is_full, tic_tac_toe + + +class TestTicTacToe(unittest.TestCase): + def test_check_winner_rows(self): + board = [["X", "X", "X"], ["O", " ", "O"], [" ", " ", " "]] + self.assertEqual(check_winner(board), "X") + + def test_check_winner_columns(self): + board = [["X", "O", " "], ["X", "O", " "], ["X", " ", " "]] + self.assertEqual(check_winner(board), "X") + + def test_check_winner_diagonals(self): + board = [["X", "O", "O"], [" ", "X", " "], [" ", " ", "X"]] + self.assertEqual(check_winner(board), "X") + + def test_is_full_true(self): + board = [["X", "O", "X"], ["O", "X", "O"], ["O", "X", "O"]] + self.assertTrue(is_full(board)) + + def test_is_full_false(self): + board = [["X", "O", "X"], ["O", " ", "O"], ["O", "X", "O"]] + self.assertFalse(is_full(board)) + + @patch("builtins.input", side_effect=["0 0", "0 1", "1 1", "0 2", "2 2"]) + @patch("sys.stdout", new_callable=StringIO) + def test_tic_tac_toe_win(self, mock_stdout, mock_input): + tic_tac_toe() + output = mock_stdout.getvalue() + self.assertIn("Player X wins!", output) + + @patch( + "builtins.input", + side_effect=["0 0", "0 1", "0 2", "1 1", "1 0", "1 2", "2 1", "2 0", "2 2"], + ) + @patch("sys.stdout", new_callable=StringIO) + def test_tic_tac_toe_tie(self, mock_stdout, mock_input): + tic_tac_toe() + output = mock_stdout.getvalue() + self.assertIn("It's a tie!", output) + + +if __name__ == "__main__": + unittest.main() diff --git a/solutions/tests/test_travel_planner_fun.py b/solutions/tests/test_travel_planner_fun.py new file mode 100644 index 000000000..deb0925df --- /dev/null +++ b/solutions/tests/test_travel_planner_fun.py @@ -0,0 +1,33 @@ +import unittest +import sys +from pathlib import Path + +sys.path.append(str(Path(__file__).parent.parent)) +from travel_planner_fun_solution import TravelPlanner + + +class TestTravelPlanner(unittest.TestCase): + def setUp(self): + self.planner = TravelPlanner() + + def test_generate_destination(self): + destination = self.planner.generate_destination() + self.assertIn(destination, self.planner.destinations) + + def test_generate_activity(self): + activity = self.planner.generate_activity() + self.assertIn(activity, self.planner.activities) + + def test_generate_travel_tip(self): + tip = self.planner.generate_travel_tip() + self.assertIn(tip, self.planner.tips) + + def test_plan_trip(self): + trip = self.planner.plan_trip() + self.assertIn(trip["destination"], self.planner.destinations) + self.assertIn(trip["activity"], self.planner.activities) + self.assertIn(trip["tip"], self.planner.tips) + + +if __name__ == "__main__": + unittest.main() diff --git a/solutions/tic_tac_toe_game.py b/solutions/tic_tac_toe_game.py new file mode 100644 index 000000000..a214b119f --- /dev/null +++ b/solutions/tic_tac_toe_game.py @@ -0,0 +1,78 @@ +def print_board(board): + for row in board: + print(" | ".join(row)) + print("-" * 9) + + +def check_winner(board): + # Check rows + for row in board: + if row[0] == row[1] == row[2] and row[0] != " ": + return row[0] + + # Check columns + for col in range(3): + if board[0][col] == board[1][col] == board[2][col] and board[0][col] != " ": + return board[0][col] + + # Check diagonals + if board[0][0] == board[1][1] == board[2][2] and board[0][0] != " ": + return board[0][0] + if board[0][2] == board[1][1] == board[2][0] and board[0][2] != " ": + return board[0][2] + + return None + + +def is_full(board): + return all(cell != " " for row in board for cell in row) + + +def tic_tac_toe(): + board = [[" " for _ in range(3)] for _ in range(3)] + players = ["X", "O"] + turn = 0 + + print("Welcome to Tic-Tac-Toe!") + print_board(board) + + while True: + current_player = players[turn % 2] + print(f"Player {current_player}'s turn.") + + try: + row, col = map( + int, + input( + "Enter row and column (0, 1, or 2 separated by a space): " + ).split(), + ) + except ValueError: + print("Invalid input. Please enter two numbers separated by a space.") + continue + + if not (0 <= row < 3 and 0 <= col < 3): + print("Invalid position. Please enter numbers between 0 and 2.") + continue + + if board[row][col] != " ": + print("Position already taken. Choose another.") + continue + + board[row][col] = current_player + print_board(board) + + winner = check_winner(board) + if winner: + print(f"Player {winner} wins!") + break + + if is_full(board): + print("It's a tie!") + break + + turn += 1 + + +if __name__ == "__main__": + tic_tac_toe() diff --git a/solutions/travel_planner_fun_solution.py b/solutions/travel_planner_fun_solution.py new file mode 100644 index 000000000..f5cfb0e53 --- /dev/null +++ b/solutions/travel_planner_fun_solution.py @@ -0,0 +1,77 @@ +import random +from random import choice + + +class TravelPlanner: + def __init__(self): + random.seed() + self.destinations = [ + "Paris, France", + "Kyoto, Japan", + "Machu Picchu, Peru", + "Cape Town, South Africa", + "New York City, USA", + "Santorini, Greece", + "Cairo, Egypt", + "Sydney, Australia", + "Reykjavik, Iceland", + "Bali, Indonesia", + ] + self.activities = [ + "exploring ancient ruins", + "dining at a Michelin-star restaurant", + "taking a hot air balloon ride", + "relaxing on a pristine beach", + "hiking through breathtaking trails", + "shopping in local markets", + "attending a cultural festival", + "snorkeling with vibrant marine life", + "enjoying a scenic train ride", + "visiting world-class museums", + ] + self.tips = [ + "Always pack a portable charger!", + "Learn a few phrases in the local language to impress locals.", + "Carry a reusable water bottle to stay hydrated.", + "Don't forget travel insurance—it's a lifesaver!", + "Try street food—it's often the tastiest and cheapest option.", + "Keep a digital and physical copy of your passport.", + "Research local customs to avoid awkward situations.", + "Pack light—you'll thank yourself later.", + "Wake up early to enjoy tourist spots without crowds.", + "Always have some local currency on hand for small purchases.", + ] + + def generate_destination(self): + return choice(self.destinations) + + def generate_activity(self): + return choice(self.activities) + + def generate_travel_tip(self): + return choice(self.tips) + + def plan_trip(self): + destination = self.generate_destination() + activity = self.generate_activity() + tip = self.generate_travel_tip() + + return {"destination": destination, "activity": activity, "tip": tip} + + +def main(): + print("Welcome to the Automated Travel Planner! \U0001f30d") + print("Sit back and relax while we plan your next dream trip.\n") + + planner = TravelPlanner() + trip = planner.plan_trip() + + print(f"\U0001f4cd Destination: {trip['destination']}") + print(f"\U0001f3c3 Activity: {trip['activity']}") + print(f"\U0001f4d6 Travel Tip: {trip['tip']}\n") + + print("Your virtual getaway is ready! Safe travels! \U0001f30f") + + +if __name__ == "__main__": + main()