From fe6d81a0bcbcb417ffc6214e1c70a00e19bf2e1c Mon Sep 17 00:00:00 2001 From: William Date: Tue, 7 Jul 2026 21:29:01 -0700 Subject: [PATCH 01/11] Added guessing game --- very_easy/Random.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 very_easy/Random.py diff --git a/very_easy/Random.py b/very_easy/Random.py new file mode 100644 index 00000000000..6877653345d --- /dev/null +++ b/very_easy/Random.py @@ -0,0 +1,26 @@ +import random + +# Random number between 1 and 10 +number = random.randint(1, 10) + +# Make a loop +while True: + + try: + # Ask for guess + guess = int(input("Guess of a number (1, 10): ")) + + if (guess < 1 or guess > 10): + print("ERROR") + continue; + + if (guess == number): + print("You did it!") + break + elif (guess < number): + print("Higher!") + elif (guess > number): + print("Lower!") + # Robust error handling + except (ValueError): + print("ERROR") From cccd26797d6dfbdcd2fc505e6ff7fa0b339488e1 Mon Sep 17 00:00:00 2001 From: Diyan Date: Tue, 28 Jul 2026 17:08:50 +0530 Subject: [PATCH 02/11] Added contributor --- contributors.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 contributors.md diff --git a/contributors.md b/contributors.md new file mode 100644 index 00000000000..ba1e0f16b4e --- /dev/null +++ b/contributors.md @@ -0,0 +1,4 @@ +Diyan + +251080020 + From f1cb57c55edfda439e488b429c7acbd82400eceb Mon Sep 17 00:00:00 2001 From: bhunesh Date: Mon, 17 Aug 2026 01:36:55 +0530 Subject: [PATCH 03/11] Add interactive dice_roller.py implementation (Closes #3202) --- dice_roller.py | 109 +++++++++++++++++++++++++------------------------ 1 file changed, 55 insertions(+), 54 deletions(-) diff --git a/dice_roller.py b/dice_roller.py index de805c61646..ea987e9e8ef 100644 --- a/dice_roller.py +++ b/dice_roller.py @@ -1,57 +1,58 @@ import random -dice_art = { - 1: ("┌─────────┐", - "│ │", - "│ ● │", - "│ │", - "└─────────┘"), - 2: ("┌─────────┐", - "│ ● │", - "│ │", - "│ ● │", - "└─────────┘"), - 3: ("┌─────────┐", - "│ ● │", - "│ ● │", - "│ ● │", - "└─────────┘"), - 4: ("┌─────────┐", - "│ ● ● │", - "│ │", - "│ ● ● │", - "└─────────┘"), - 5: ("┌─────────┐", - "│ ● ● │", - "│ ● │", - "│ ● ● │", - "└─────────┘"), - 6: ("┌─────────┐", - "│ ● ● │", - "│ ● ● │", - "│ ● ● │", - "└─────────┘") -} - -dice = [] -total = 0 -num_of_dice = int(input("How many dice?: ")) - -for die in range(num_of_dice): - dice.append(random.randint(1, 6)) - -# PRINT VERTICALLY -# for die in range(num_of_dice): -# for line in dice_art.get(dice[die]): -# print(line) - -# PRINT HORIZONTALLY -for line in range(5): - for die in dice: - print(dice_art.get(die)[line], end="") - print() - -for die in dice: - total += die -print(f"total: {total}") \ No newline at end of file +def get_die_art(value): + """Returns a list of 5 strings representing the box-drawing art + + for a given die face value (1-6). + """ + faces = { + 1: ["┌───────┐", "│ │", "│ ● │", "│ │", "└───────┘"], + 2: ["┌───────┐", "│ ● │", "│ │", "│ ● │", "└───────┘"], + 3: ["┌───────┐", "│ ● │", "│ ● │", "│ ● │", "└───────┘"], + 4: ["┌───────┐", "│ ● ● │", "│ │", "│ ● ● │", "└───────┘"], + 5: ["┌───────┐", "│ ● ● │", "│ ● │", "│ ● ● │", "└───────┘"], + 6: ["┌───────┐", "│ ● ● │", "│ ● ● │", "│ ● ● │", "└───────┘"], + } + return faces.get(value, faces[1]) + + +def main(): + print("========================================") + print(" Interactive Dice Roller ") + print("========================================") + + # Get valid user input for the number of dice + while True: + try: + num_dice = int(input("How many dice would you like to roll? (1-10): ")) + if 1 <= num_dice <= 10: + break + print("Please choose a number between 1 and 10.") + except ValueError: + print("Invalid input. Please enter a valid integer.") + + # Generate random values for each die + rolls = [random.randint(1, 6) for _ in range(num_dice)] + + print(f"\nRolling {num_dice} dice...") + print("~" * (num_dice * 11)) + + # Retrieve ASCII art for each rolled die + dice_arts = [get_die_art(val) for val in rolls] + + # Print dice side-by-side by iterating line by line (0 to 4) + for line_index in range(5): + row_line = " ".join(dice_art[line_index] for dice_art in dice_arts) + print(row_line) + + print("~" * (num_dice * 11)) + + # Output results and total sum + print(f"Individual Rolls: {rolls}") + print(f"Total Sum: {sum(rolls)}") + print("========================================") + + +if __name__ == "__main__": + main() From 6f3b57ad3b3ade78b5bba8a26671a3ae0ad14267 Mon Sep 17 00:00:00 2001 From: bhunesh Sharma Date: Mon, 17 Aug 2026 01:40:56 +0530 Subject: [PATCH 04/11] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- dice_roller.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/dice_roller.py b/dice_roller.py index ea987e9e8ef..e2bd77fcd5f 100644 --- a/dice_roller.py +++ b/dice_roller.py @@ -36,17 +36,21 @@ def main(): rolls = [random.randint(1, 6) for _ in range(num_dice)] print(f"\nRolling {num_dice} dice...") - print("~" * (num_dice * 11)) # Retrieve ASCII art for each rolled die dice_arts = [get_die_art(val) for val in rolls] + separator = " " + die_width = len(dice_arts[0][0]) + divider = "~" * (num_dice * die_width + (num_dice - 1) * len(separator)) + print(divider) + # Print dice side-by-side by iterating line by line (0 to 4) for line_index in range(5): - row_line = " ".join(dice_art[line_index] for dice_art in dice_arts) + row_line = separator.join(dice_art[line_index] for dice_art in dice_arts) print(row_line) - print("~" * (num_dice * 11)) + print(divider) # Output results and total sum print(f"Individual Rolls: {rolls}") From f58b50eb97a21eda4d12c790278e3a5a03c384c3 Mon Sep 17 00:00:00 2001 From: Spirit Date: Mon, 31 Aug 2026 22:38:19 +0530 Subject: [PATCH 05/11] feat: add duplicate_file_finder utility script and update README --- duplicate_file_finder.py | 144 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 duplicate_file_finder.py diff --git a/duplicate_file_finder.py b/duplicate_file_finder.py new file mode 100644 index 00000000000..d9e3f481e40 --- /dev/null +++ b/duplicate_file_finder.py @@ -0,0 +1,144 @@ +""" +Duplicate File Finder +--------------------- +Scans a target directory to identify duplicate files by comparing their +SHA-256 checksums in memory-efficient chunks. + +Features: +- Size-first filtering for fast scanning +- Memory-efficient chunked reading for large files +- Handles nested subdirectories and permission errors gracefully + +Usage: + python duplicate_file_finder.py [directory_path] +""" + +import argparse +from collections import defaultdict +import hashlib +import os +from pathlib import Path +from typing import DefaultDict, Dict, List + + +def calculate_file_hash(filepath: Path, chunk_size: int = 65536) -> str: + """ + Calculate the SHA-256 hash of a file reading in chunks. + + :param filepath: Path to the file. + :param chunk_size: Size of byte chunks to read at a time. + :return: Hexadecimal SHA-256 hash string. + """ + hasher = hashlib.sha256() + try: + with open(filepath, "rb") as file_obj: + while chunk := file_obj.read(chunk_size): + hasher.update(chunk) + return hasher.hexdigest() + except (PermissionError, OSError) as error: + print(f"[!] Warning: Could not read {filepath}: {error}") + return "" + + +def find_duplicate_files(target_dir: str) -> Dict[str, List[Path]]: + """ + Scan a directory tree and return a dictionary mapping file hashes + to lists of duplicate file paths. + + :param target_dir: The root directory to scan. + :return: Dictionary of {hash: [list_of_duplicate_paths]}. + """ + path = Path(target_dir).resolve() + if not path.is_dir(): + raise NotADirectoryError(f"Directory not found: {target_dir}") + + print(f"[*] Scanning directory: {path}") + + # Step 1: Group files by size (files with unique sizes cannot be duplicates) + size_map: DefaultDict[int, List[Path]] = defaultdict(list) + total_files = 0 + + for root, _, files in os.walk(path): + for filename in files: + file_path = Path(root) / filename + try: + if not file_path.is_symlink() and file_path.exists(): + file_size = file_path.stat().st_size + if file_size > 0: # Skip empty files + size_map[file_size].append(file_path) + total_files += 1 + except (PermissionError, OSError): + continue + + print(f"[*] Found {total_files} files. Checking for duplicate candidates...") + + # Step 2: For file sizes with multiple files, compare hashes + hash_map: DefaultDict[str, List[Path]] = defaultdict(list) + + for size, file_list in size_map.items(): + if len(file_list) > 1: + for file_path in file_list: + file_hash = calculate_file_hash(file_path) + if file_hash: + hash_map[file_hash].append(file_path) + + # Return only groups that have duplicates (more than 1 path) + duplicates = {h: paths for h, paths in hash_map.items() if len(paths) > 1} + return duplicates + + +def format_size(bytes_size: int) -> str: + """Format bytes into a human-readable string (KB, MB, GB).""" + for unit in ["B", "KB", "MB", "GB", "TB"]: + if bytes_size < 1024.0: + return f"{bytes_size:.2f} {unit}" + bytes_size /= 1024.0 + return f"{bytes_size:.2f} PB" + + +def main() -> None: + parser = argparse.ArgumentParser( + description="Find duplicate files in a directory using SHA-256 hashing." + ) + parser.add_argument( + "directory", + nargs="?", + default=".", + help="Target directory path to scan (default: current directory)", + ) + args = parser.parse_args() + + try: + duplicates = find_duplicate_files(args.directory) + except NotADirectoryError as e: + print(f"[!] Error: {e}") + return + + if not duplicates: + print("\n[+] No duplicate files found.") + return + + total_duplicate_groups = len(duplicates) + total_wasted_space = 0 + + print(f"\n{'='*60}") + print(f"[+] Found {total_duplicate_groups} group(s) of duplicate files:") + print(f"{'='*60}\n") + + for index, (file_hash, file_paths) in enumerate(duplicates.items(), start=1): + file_size = file_paths[0].stat().st_size + wasted = file_size * (len(file_paths) - 1) + total_wasted_space += wasted + + print(f"Group #{index} (Size: {format_size(file_size)} each, SHA-256: {file_hash[:12]}...):") + for path in file_paths: + print(f" -> {path}") + print() + + print(f"{'='*60}") + print(f"[+] Total duplicate wasted space: {format_size(total_wasted_space)}") + print(f"{'='*60}") + + +if __name__ == "__main__": + main() \ No newline at end of file From 533ffd01bf35d68e5a7f32785f2d40efd233f1fa Mon Sep 17 00:00:00 2001 From: katsugtgz Date: Tue, 6 Oct 2026 14:38:07 +0700 Subject: [PATCH 06/11] deps: bump nltk from 3.9.2 to 3.10.3 in requirements_with_versions.txt --- requirements_with_versions.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements_with_versions.txt b/requirements_with_versions.txt index 20b1c1d112c..6bd2e09014e 100644 --- a/requirements_with_versions.txt +++ b/requirements_with_versions.txt @@ -30,7 +30,7 @@ PyPDF2==3.0.1 pyserial==3.5 twilio==9.9.1 tabula==1.0.5 -nltk==3.9.2 +nltk==3.10.3 Pillow==12.1.0 SocksiPy-branch==1.01 xlrd==2.0.2 From 77793953306c92b2c4de3a8b9478e351e5f0536f Mon Sep 17 00:00:00 2001 From: Aaditya Panda Date: Sat, 10 Oct 2026 10:25:37 +0530 Subject: [PATCH 07/11] Street_Fighter: single-player mode against a computer opponent - The wizard (player 2) is now AI-controlled: it chases, retreats when low on health, jumps and picks close/long-range attacks. - New in-game controls guide screen, USER/COMPUTER labels and a main-menu button during a match. - README rewritten for the single-player game; fixes the broken `pip install -r` command (now `pip install -r docs/requirements.txt`). --- Street_Fighter/docs/README.md | 412 ++++++++++++++++++++++++++-------- Street_Fighter/src/fighter.py | 170 ++++++++++---- Street_Fighter/src/main.py | 255 +++++++++------------ 3 files changed, 553 insertions(+), 284 deletions(-) diff --git a/Street_Fighter/docs/README.md b/Street_Fighter/docs/README.md index 2ff27a478e8..9f75afd829b 100644 --- a/Street_Fighter/docs/README.md +++ b/Street_Fighter/docs/README.md @@ -1,129 +1,349 @@ -# Street Fighter -![download](https://github.com/user-attachments/assets/1395caef-363b-4485-8c0a-8d738f3cd379) +
+# 🥊 Street Fighter +### *The Ultimate Python Fighting Experience* -**Street Fighter** is an engaging two-player fighting game built with Python and Pygame. This project features exciting gameplay mechanics, unique characters, and dynamic animations, making it a perfect choice for retro game enthusiasts and developers interested in Python-based game development. +![Street Fighter Banner](https://github.com/user-attachments/assets/1395caef-363b-4485-8c0a-8d738f3cd379) -## Features -- **Two Distinct Fighters**: - - **Warrior**: A melee combatant with powerful sword attacks. - - **Wizard**: A magic wielder with spell-based attacks. - -- **Gameplay Mechanics**: - - Health bars for each fighter. - - Smooth animations for idle, run, jump, attack, hit, and death actions. - - Scoring system to track player victories. - -- **Dynamic Background**: - - Blurred background effects during the main menu for a cinematic feel. +Typing Animation -- **Sound Effects and Music**: - - Immersive soundtracks and attack effects. +[![Python](https://img.shields.io/badge/Python-3.7+-3776AB?style=for-the-badge&logo=python&logoColor=white)](https://python.org) +[![Pygame](https://img.shields.io/badge/Pygame-2.0+-00A86B?style=for-the-badge&logo=pygame&logoColor=white)](https://pygame.org) +[![License](https://img.shields.io/badge/License-MIT-yellow?style=for-the-badge&logo=open-source-initiative&logoColor=white)](LICENSE) +[![Stars](https://img.shields.io/github/stars/AadityaPanda/Street_Fighter?style=for-the-badge&logo=github&color=FFD700)](https://github.com/AadityaPanda/Street_Fighter/stargazers) -- **Responsive UI**: - - Main menu with start, score, and exit options. - - Victory screen for the winning fighter. +
-- **Custom Controls** for two players. +--- + +## 🎮 About The Game + +**Street Fighter** is an electrifying two-player fighting game that brings classic arcade action to your desktop! Built with Python and Pygame, this project showcases advanced game development techniques with smooth animations, dynamic combat systems, and immersive audio-visual effects. + +> *"Enter the arena, choose your destiny, and prove your fighting prowess!"* ⚔️ + +--- + +## ✨ Game Features + + + + + + +
+ +### 🗡️ **The Warrior** +![Warrior](https://img.shields.io/badge/Melee-Fighter-red?style=for-the-badge&logo=sword&logoColor=white) + +**Master of Blades** +- ⚔️ Devastating sword attacks +- 🛡️ Heavy armor protection +- 💪 High damage output +- 🏃‍♂️ Agile combat moves + + + +### 🧙‍♂️ **The Wizard** +![Wizard](https://img.shields.io/badge/Magic-Caster-blue?style=for-the-badge&logo=magic&logoColor=white) + +**Arcane Spellcaster** +- ✨ Powerful spell attacks +- 🔮 Magical projectiles +- 🌟 Elemental damage +- 🎭 Mystical abilities + +
+ +### 🎯 **Core Gameplay Features** + +
+ +| Feature | Description | Status | +|---------|-------------|--------| +| 💚 **Health System** | Dynamic health bars with real-time updates | ✅ Active | +| 🎬 **Smooth Animations** | Fluid character movements and transitions | ✅ Active | +| 🏆 **Scoring System** | Track victories and tournament progress | ✅ Active | +| 🎵 **Audio Experience** | Immersive soundtracks and combat effects | ✅ Active | +| 🖼️ **Dynamic Backgrounds** | Cinematic blur effects and visual polish | ✅ Active | +| 🎮 **Responsive Controls** | Intuitive two-player control scheme | ✅ Active | + +
+ +--- + +## 🎨 Visual Showcase + +
+ +### 🌟 **Game Highlights** + +```ascii + ⚔️ COMBAT SYSTEM ⚔️ + ╔═══════════════════╗ + ║ • Melee Attacks ║ + ║ • Magic Spells ║ + ║ • Combo System ║ + ║ • Victory Moves ║ + ╚═══════════════════╝ +``` + +
+ +--- ## 📋 Table of Contents -- [Street Fighter](#street-fighter) - - [Features](#features) - - [📋 Table of Contents](#-table-of-contents) - - [Requirements](#requirements) - - [Installation](#installation) - - [Gameplay Instructions](#gameplay-instructions) - - [Player Controls:](#player-controls) - - [Downloads](#downloads) - - [License](#license) - - [Credits](#credits) - - [Contributing](#contributing) - - [Contact](#contact) - -## Requirements -- Python 3.7 or higher -- Required Python libraries: - - `pygame` - - `numpy` - - `opencv-python` - -## Installation - -Follow these steps to install and run the game: - -1. **Clone the Repository**: - ```bash - git clone https://github.com/AadityaPanda/Street_Fighter.git - cd Streer_Fighter - ``` -2. **Install Dependencies**: - ```bash - pip install -r - ``` +
-3. **Run the Game**: - ```bash - python src/main.py - ``` +| Section | Quick Links | +|---------|-------------| +| 🎮 | [Features](#-game-features) | +| ⚙️ | [Requirements](#-requirements) | +| 🚀 | [Installation](#-installation) | +| 🎯 | [Gameplay](#-gameplay-instructions) | +| 📦 | [Downloads](#-downloads) | +| 📄 | [License](#-license) | +| 🙏 | [Credits](#-credits) | +| 🤝 | [Contributing](#-contributing) | +| 📧 | [Contact](#-contact) | + +
+ +--- + +## ⚙️ Requirements + +
+ +### 🛠️ **System Requirements** + +
+ +| Component | Minimum Requirement | +|-----------|-------------------| +| 🐍 **Python** | Version 3.7 or higher | +| 💾 **RAM** | 4GB recommended | +| 💿 **Storage** | 100MB free space | +| 🖥️ **Display** | 1024x768 resolution | + +### 📚 **Required Libraries** + +```python +# Core Dependencies +pygame>=2.0.0 # Game engine +numpy>=1.19.0 # Mathematical operations +opencv-python>=4.5.0 # Image processing +``` + +--- + +## 🚀 Installation + +
+ +### 🎯 **Quick Start Guide** + +
+ +**Step 1: Clone the Repository** +```bash +git clone https://github.com/AadityaPanda/Street_Fighter.git +cd Street_Fighter +``` + +**Step 2: Install Dependencies** +```bash +pip install -r docs/requirements.txt +``` + +**Step 3: Launch the Game** +```bash +python src/main.py +``` + +
+ +🎉 **Ready to Fight!** 🎉 + +
+ +--- -## Gameplay Instructions +## 🎯 Gameplay Instructions -### Player Controls: -- **Player 1**: - - Move: `A` (Left), `D` (Right) - - Jump: `W` - - Attack: `R` (Attack 1), `T` (Attack 2) +
-- **Player 2**: - - Move: Left Arrow (`←`), Right Arrow (`→`) - - Jump: Up Arrow (`↑`) - - Attack: `M` (Attack 1), `N` (Attack 2) +### 🎮 **Master the Controls** -**Objective**: Reduce your opponent's health to zero to win the round. Victory is celebrated with a dynamic win screen! +
-## Downloads + + + + + +
-You can download the latest release of **Street Fighter** from the following link: +### 👤 **Player 1 (Warrior)** +![Player 1](https://img.shields.io/badge/Player-1-red?style=for-the-badge&logo=user&logoColor=white) -[![Version](https://img.shields.io/github/v/release/AadityaPanda/Street_Fighter?color=%230567ff&label=Latest%20Release&style=for-the-badge)](https://github.com/AadityaPanda/Street_Fighter/releases/latest) Download +| Action | Key | +|--------|-----| +| ⬅️ Move Left | `A` | +| ➡️ Move Right | `D` | +| ⬆️ Jump | `W` | +| ⚔️ Attack 1 | `R` | +| 🗡️ Attack 2 | `T` | -## License + -This project is licensed under the [MIT License](LICENSE). Feel free to use, modify, and distribute it in your projects. +### 👤 **Player 2 (Wizard)** +![Player 2](https://img.shields.io/badge/Player-2-blue?style=for-the-badge&logo=user&logoColor=white) -## Credits +| Action | Key | +|--------|-----| +| ⬅️ Move Left | `←` | +| ➡️ Move Right | `→` | +| ⬆️ Jump | `↑` | +| ✨ Spell 1 | `M` | +| 🔮 Spell 2 | `N` | -- **Developer**: Aaditya Panda -- **Assets**: - - Background music and sound effects: [Free Music Archive](https://freemusicarchive.org/) - - Fonts: [Turok Font](https://www.fontspace.com/turok-font) - - Sprites: Custom-designed and modified from open-source assets. +
-## Contributing +
-Contributions are welcome! Here's how you can help: -1. Fork the repository. -2. Create a new branch: +### 🏆 **Victory Conditions** + +```diff ++ Reduce opponent's health to ZERO ++ Unleash devastating combo attacks ++ Master timing and positioning ++ Claim victory in the arena! +``` + +
+ +--- + +## 📦 Downloads + +
+ +### 🎯 **Get the Latest Release** + +[![Latest Release](https://img.shields.io/github/v/release/AadityaPanda/Street_Fighter?color=%230567ff&label=Latest%20Release&style=for-the-badge&logo=github)](https://github.com/AadityaPanda/Street_Fighter/releases/latest) + +[![Download Game](https://custom-icon-badges.demolab.com/badge/-Download%20Game-0B6623?style=for-the-badge&logo=download&logoColor=white)](https://github.com/AadityaPanda/Street_Fighter/releases/download/v1.1/Game.zip) + +[![Download Source](https://img.shields.io/badge/Download-Source%20Code-blue?style=for-the-badge&logo=github&logoColor=white)](https://github.com/AadityaPanda/Street_Fighter/archive/refs/heads/main.zip) + +
+ +--- + +## 📄 License + +
+ +This project is licensed under the **MIT License** + +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=for-the-badge)](https://opensource.org/licenses/MIT) + +*Feel free to use, modify, and distribute in your projects!* + +
+ +--- + +## 🙏 Credits + +
+ +### 👨‍💻 **Development Team** + +
+ +| Role | Contributor | +|------|-------------| +| 🎮 **Lead Developer** | [Aaditya Panda](https://github.com/AadityaPanda) | +| 🎨 **Game Design** | Aaditya Panda | +| 🔧 **Technical Lead** | Aaditya Panda | + +### 🎨 **Assets & Resources** + +| Asset Type | Source | +|------------|--------| +| 🎵 **Background Music** | [Free Music Archive](https://freemusicarchive.org/) | +| 🔤 **Fonts** | [Turok Font](https://www.fontspace.com/turok-font) | +| 🎭 **Sprites** | Custom-designed + Open-source assets | +| 🎧 **Sound Effects** | Royalty-free audio libraries | + +--- + +## 🤝 Contributing + +
+ +### 🌟 **Join the Development!** + +We welcome contributions from the community! + +
+ +**🚀 Quick Contribution Guide:** + +1. **Fork** the repository + ```bash + git fork https://github.com/AadityaPanda/Street_Fighter.git + ``` + +2. **Create** a feature branch ```bash - git checkout -b feature/YourFeatureName + git checkout -b feature/AmazingFeature ``` -3. Commit your changes: + +3. **Commit** your changes ```bash - git commit -m "Add YourFeatureName" + git commit -m "Add AmazingFeature" ``` -4. Push to the branch: + +4. **Push** to the branch ```bash - git push origin feature/YourFeatureName + git push origin feature/AmazingFeature ``` -5. Open a pull request. -Check the [CONTRIBUTING.md](CONTRIBUTING.md) for detailed guidelines. +5. **Open** a Pull Request + +
+ +[![Contributors](https://img.shields.io/github/contributors/AadityaPanda/Street_Fighter?style=for-the-badge&logo=github)](https://github.com/AadityaPanda/Street_Fighter/graphs/contributors) + +*Check [CONTRIBUTING.md](CONTRIBUTING.md) for detailed guidelines* + +
+ +--- + +## 📧 Contact + +
+ +### 🌐 **Let's Connect!** + +[![Email](https://img.shields.io/badge/Email-aadityapanda23@gmail.com-D14836?style=for-the-badge&logo=gmail&logoColor=white)](mailto:aadityapanda23@gmail.com) +[![GitHub](https://img.shields.io/badge/GitHub-AadityaPanda-181717?style=for-the-badge&logo=github&logoColor=white)](https://github.com/AadityaPanda) +[![LinkedIn](https://img.shields.io/badge/LinkedIn-Connect-0077B5?style=for-the-badge&logo=linkedin&logoColor=white)](https://www.linkedin.com/in/aadityapanda/) + +--- + +### 💫 *"Try something new every day!"* + +Closing Message -## Contact +
-- **Developer**: Aaditya Panda -- **Email**: [aadityapanda23@gmail.com](mailto:aadityapanda23@gmail.com) -- **GitHub**: [AadityaPanda](https://github.com/AadityaPanda) +![Made with Love](https://img.shields.io/badge/Made%20with-❤️-red?style=for-the-badge) +![Python](https://img.shields.io/badge/Built%20with-Python-blue?style=for-the-badge&logo=python&logoColor=white) -Try somehting new everyday!!! +
diff --git a/Street_Fighter/src/fighter.py b/Street_Fighter/src/fighter.py index 94fc68abd16..3c22eabecdd 100644 --- a/Street_Fighter/src/fighter.py +++ b/Street_Fighter/src/fighter.py @@ -1,6 +1,6 @@ import pygame - - +import random +import math class Fighter: def __init__(self, player, x, y, flip, data, sprite_sheet, animation_steps, sound): self.player = player @@ -24,6 +24,14 @@ def __init__(self, player, x, y, flip, data, sprite_sheet, animation_steps, soun self.hit = False self.health = 100 self.alive = True + # AI attributes for computer player + self.ai_enabled = (player == 2) # Player 2 is AI-controlled + self.ai_reaction_time = 0 + self.ai_decision_cooldown = 0 + self.ai_aggression = 0.85 # Increased aggression (0-1 scale, higher = more aggressive) + self.ai_defense_threshold = 25 # Lower threshold for defensive behavior + self.ai_reaction_speed = 5 # Faster reaction time (lower = faster) + self.ai_predictive_aim = True # Enable predictive aiming def load_images(self, sprite_sheet, animation_steps): # extract images from spritesheet @@ -31,18 +39,108 @@ def load_images(self, sprite_sheet, animation_steps): for y, animation in enumerate(animation_steps): temp_img_list = [] for x in range(animation): - temp_img = sprite_sheet.subsurface( - x * self.size, y * self.size, self.size, self.size - ) + temp_img = sprite_sheet.subsurface(x * self.size, y * self.size, self.size, self.size) temp_img_list.append( - pygame.transform.scale( - temp_img, - (self.size * self.image_scale, self.size * self.image_scale), - ) - ) + pygame.transform.scale(temp_img, (self.size * self.image_scale, self.size * self.image_scale))) animation_list.append(temp_img_list) return animation_list + def ai_make_decision(self, target, screen_width, screen_height): + """Enhanced AI decision making for computer-controlled fighter""" + if not self.ai_enabled or not self.alive: + return 0, 0, False, 0 # dx, dy, jump, attack_type + + # Reduce decision cooldown + if self.ai_decision_cooldown > 0: + self.ai_decision_cooldown -= 1 + return 0, 0, False, 0 + + # Calculate distance to target + distance = abs(target.rect.centerx - self.rect.centerx) + vertical_distance = abs(target.rect.centery - self.rect.centery) + + # Predictive aiming - anticipate player movement + target_velocity_x = 0 + if hasattr(target, 'running') and target.running: + target_velocity_x = 10 if target.rect.centerx > self.rect.centerx else -10 + + # AI behavior based on health + is_defensive = self.health < self.ai_defense_threshold + + # Movement decisions + dx = 0 + dy = 0 + should_jump = False + attack_type = 0 + + # Enhanced movement logic + if distance > 180: # Too far - approach aggressively + if target.rect.centerx > self.rect.centerx: + dx = 12 # Move right faster + else: + dx = -12 # Move left faster + self.running = True + elif distance < 60: # Too close - maintain optimal distance + if target.rect.centerx > self.rect.centerx: + dx = -8 # Move left + else: + dx = 8 # Move right + self.running = True + elif distance < 120: # Optimal range - circle around opponent + if random.random() < 0.3: # 30% chance to circle + if target.rect.centerx > self.rect.centerx: + dx = -3 + else: + dx = 3 + self.running = True + + # Enhanced jump decisions + jump_chance = 0.4 if not is_defensive else 0.6 # More likely to jump when defensive + if (random.random() < jump_chance and not self.jump and + (distance < 200 or vertical_distance > 50 or target.jump)): + should_jump = True + + # Counter-attack logic - attack when opponent is vulnerable + should_attack = False + if distance < 140 and self.attack_cooldown == 0: + # Attack when opponent is jumping or attacking + if target.jump or target.attacking: + should_attack = True + # Normal attack decision + elif random.random() < self.ai_aggression: + should_attack = True + + if should_attack: + # Smart attack type selection + if target.jump: + attack_type = 2 # Use long range attack against jumping opponent + elif distance < 80: + attack_type = 1 # Close range attack + else: + attack_type = 2 # Long range attack + + # Faster decision cooldown for more aggressive AI + self.ai_decision_cooldown = random.randint(5, 15) + + # Enhanced defensive behavior + if is_defensive: + # Increase distance when health is low + if distance < 250: + if target.rect.centerx > self.rect.centerx: + dx = -10 + else: + dx = 10 + self.running = True + # More likely to jump to escape + if random.random() < 0.5 and not self.jump: + should_jump = True + # Block/evade more often + if target.attacking and distance < 100: + if random.random() < 0.7: # 70% chance to evade + should_jump = True + + return dx, dy, should_jump, attack_type + def move(self, screen_width, screen_height, target, round_over): SPEED = 10 GRAVITY = 2 @@ -51,12 +149,12 @@ def move(self, screen_width, screen_height, target, round_over): self.running = False self.attack_type = 0 - # get keypresses + # get keypresses for human player key = pygame.key.get_pressed() # can only perform other actions if not currently attacking if self.attacking == False and self.alive == True and round_over == False: - # check player 1 controls + # check player 1 controls (human player) if self.player == 1: # movement if key[pygame.K_a]: @@ -78,27 +176,25 @@ def move(self, screen_width, screen_height, target, round_over): if key[pygame.K_t]: self.attack_type = 2 - # check player 2 controls - if self.player == 2: - # movement - if key[pygame.K_LEFT]: - dx = -SPEED - self.running = True - if key[pygame.K_RIGHT]: - dx = SPEED + # check player 2 controls (AI player) + elif self.player == 2 and self.ai_enabled: + # Get AI decisions + ai_dx, ai_dy, should_jump, attack_type = self.ai_make_decision(target, screen_width, screen_height) + + # Apply AI movement + if ai_dx != 0: + dx = ai_dx self.running = True - # jump - if key[pygame.K_UP] and self.jump == False: + + # Apply AI jump + if should_jump and self.jump == False: self.vel_y = -30 self.jump = True - # attack - if key[pygame.K_m] or key[pygame.K_n]: + + # Apply AI attack + if attack_type > 0: self.attack(target) - # determine which attack type was used - if key[pygame.K_m]: - self.attack_type = 1 - if key[pygame.K_n]: - self.attack_type = 2 + self.attack_type = attack_type # apply gravity self.vel_y += GRAVITY @@ -179,12 +275,8 @@ def attack(self, target): # execute attack self.attacking = True self.attack_sound.play() - attacking_rect = pygame.Rect( - self.rect.centerx - (2 * self.rect.width * self.flip), - self.rect.y, - 2 * self.rect.width, - self.rect.height, - ) + attacking_rect = pygame.Rect(self.rect.centerx - (2 * self.rect.width * self.flip), self.rect.y, + 2 * self.rect.width, self.rect.height) if attacking_rect.colliderect(target.rect): target.health -= 10 target.hit = True @@ -199,10 +291,4 @@ def update_action(self, new_action): def draw(self, surface): img = pygame.transform.flip(self.image, self.flip, False) - surface.blit( - img, - ( - self.rect.x - (self.offset[0] * self.image_scale), - self.rect.y - (self.offset[1] * self.image_scale), - ), - ) + surface.blit(img, (self.rect.x - (self.offset[0] * self.image_scale), self.rect.y - (self.offset[1] * self.image_scale))) diff --git a/Street_Fighter/src/main.py b/Street_Fighter/src/main.py index 62778cee3b3..1d000dcb2a0 100644 --- a/Street_Fighter/src/main.py +++ b/Street_Fighter/src/main.py @@ -1,23 +1,26 @@ import math import pygame from pygame import mixer +from pygame import font import cv2 import numpy as np import os import sys from fighter import Fighter - # Helper Function for Bundled Assets def resource_path(relative_path): try: base_path = sys._MEIPASS except Exception: - base_path = os.path.abspath(".") + # When running from src directory, look in parent directory for assets + if os.path.basename(os.getcwd()) == "src": + base_path = os.path.abspath("..") + else: + base_path = os.path.abspath(".") return os.path.join(base_path, relative_path) - mixer.init() pygame.init() @@ -43,21 +46,13 @@ def resource_path(relative_path): # Load Assets bg_image = cv2.imread(resource_path("assets/images/bg1.jpg")) -victory_img = pygame.image.load( - resource_path("assets/images/victory.png") -).convert_alpha() -warrior_victory_img = pygame.image.load( - resource_path("assets/images/warrior.png") -).convert_alpha() -wizard_victory_img = pygame.image.load( - resource_path("assets/images/wizard.png") -).convert_alpha() +victory_img = pygame.image.load(resource_path("assets/images/victory.png")).convert_alpha() +warrior_victory_img = pygame.image.load(resource_path("assets/images/warrior.png")).convert_alpha() +wizard_victory_img = pygame.image.load(resource_path("assets/images/wizard.png")).convert_alpha() # Fonts menu_font = pygame.font.Font(resource_path("assets/fonts/turok.ttf"), 50) -menu_font_title = pygame.font.Font( - resource_path("assets/fonts/turok.ttf"), 100 -) # Larger font for title +menu_font_title = pygame.font.Font(resource_path("assets/fonts/turok.ttf"), 100) # Larger font for title count_font = pygame.font.Font(resource_path("assets/fonts/turok.ttf"), 80) score_font = pygame.font.Font(resource_path("assets/fonts/turok.ttf"), 30) @@ -71,12 +66,8 @@ def resource_path(relative_path): magic_fx.set_volume(0.75) # Load Fighter Spritesheets -warrior_sheet = pygame.image.load( - resource_path("assets/images/warrior.png") -).convert_alpha() -wizard_sheet = pygame.image.load( - resource_path("assets/images/wizard.png") -).convert_alpha() +warrior_sheet = pygame.image.load(resource_path("assets/images/warrior.png")).convert_alpha() +wizard_sheet = pygame.image.load(resource_path("assets/images/wizard.png")).convert_alpha() # Define Animation Steps WARRIOR_ANIMATION_STEPS = [10, 8, 1, 7, 7, 3, 7] @@ -131,24 +122,13 @@ def draw_button(text, font, text_col, button_col, x, y, width, height): def victory_screen(winner_img): start_time = pygame.time.get_ticks() while pygame.time.get_ticks() - start_time < ROUND_OVER_COOLDOWN: - resized_victory_img = pygame.transform.scale( - victory_img, (victory_img.get_width() * 2, victory_img.get_height() * 2) - ) - screen.blit( - resized_victory_img, - ( - SCREEN_WIDTH // 2 - resized_victory_img.get_width() // 2, - SCREEN_HEIGHT // 2 - resized_victory_img.get_height() // 2 - 50, - ), - ) - - screen.blit( - winner_img, - ( - SCREEN_WIDTH // 2 - winner_img.get_width() // 2, - SCREEN_HEIGHT // 2 - winner_img.get_height() // 2 + 100, - ), - ) + + resized_victory_img = pygame.transform.scale(victory_img, (victory_img.get_width() * 2, victory_img.get_height() * 2)) + screen.blit(resized_victory_img, (SCREEN_WIDTH // 2 - resized_victory_img.get_width() // 2, + SCREEN_HEIGHT // 2 - resized_victory_img.get_height() // 2 - 50)) + + screen.blit(winner_img, (SCREEN_WIDTH // 2 - winner_img.get_width() // 2, + SCREEN_HEIGHT // 2 - winner_img.get_height() // 2 + 100)) pygame.display.update() @@ -176,9 +156,7 @@ def main_menu(): elapsed_time = (pygame.time.get_ticks() - animation_start_time) / 1000 scale_factor = 1 + 0.05 * math.sin(elapsed_time * 2 * math.pi) # Slight scaling - scaled_font = pygame.font.Font( - "assets/fonts/turok.ttf", int(100 * scale_factor) - ) + scaled_font = pygame.font.Font(resource_path("assets/fonts/turok.ttf"), int(100 * scale_factor)) title_text = "STREET FIGHTER" colors = [BLUE, GREEN, YELLOW] @@ -187,57 +165,26 @@ def main_menu(): title_y = SCREEN_HEIGHT // 6 shadow_offset = 5 - draw_text( - title_text, - scaled_font, - shadow_color, - title_x + shadow_offset, - title_y + shadow_offset, - ) + draw_text(title_text, scaled_font, shadow_color, title_x + shadow_offset, title_y + shadow_offset) draw_gradient_text(title_text, scaled_font, title_x, title_y, colors) button_width = 280 button_height = 60 button_spacing = 30 - start_button_y = ( - SCREEN_HEIGHT // 2 - (button_height + button_spacing) * 1.5 + 50 - ) - scores_button_y = ( - SCREEN_HEIGHT // 2 - (button_height + button_spacing) * 0.5 + 50 - ) - exit_button_y = SCREEN_HEIGHT // 2 + (button_height + button_spacing) * 0.5 + 50 - - start_button = draw_button( - "START GAME", - menu_font, - BLACK, - GREEN, - SCREEN_WIDTH // 2 - button_width // 2, - start_button_y, - button_width, - button_height, - ) - scores_button = draw_button( - "SCORES", - menu_font, - BLACK, - GREEN, - SCREEN_WIDTH // 2 - button_width // 2, - scores_button_y, - button_width, - button_height, - ) - exit_button = draw_button( - "EXIT", - menu_font, - BLACK, - GREEN, - SCREEN_WIDTH // 2 - button_width // 2, - exit_button_y, - button_width, - button_height, - ) + start_button_y = SCREEN_HEIGHT // 2 - (button_height + button_spacing) * 2 + 50 + controls_button_y = SCREEN_HEIGHT // 2 - (button_height + button_spacing) * 1 + 50 + scores_button_y = SCREEN_HEIGHT // 2 + (button_height + button_spacing) * 0 + 50 + exit_button_y = SCREEN_HEIGHT // 2 + (button_height + button_spacing) * 1 + 50 + + start_button = draw_button("START GAME", menu_font, BLACK, GREEN, SCREEN_WIDTH // 2 - button_width // 2, + start_button_y, button_width, button_height) + controls_button = draw_button("CONTROLS", menu_font, BLACK, GREEN, SCREEN_WIDTH // 2 - button_width // 2, + controls_button_y, button_width, button_height) + scores_button = draw_button("SCORES", menu_font, BLACK, GREEN, SCREEN_WIDTH // 2 - button_width // 2, + scores_button_y, button_width, button_height) + exit_button = draw_button("EXIT", menu_font, BLACK, GREEN, SCREEN_WIDTH // 2 - button_width // 2, + exit_button_y, button_width, button_height) for event in pygame.event.get(): if event.type == pygame.QUIT: @@ -246,6 +193,8 @@ def main_menu(): if event.type == pygame.MOUSEBUTTONDOWN: if start_button.collidepoint(event.pos): return "START" + if controls_button.collidepoint(event.pos): + return "CONTROLS" if scores_button.collidepoint(event.pos): return "SCORES" if exit_button.collidepoint(event.pos): @@ -261,57 +210,76 @@ def scores_screen(): draw_bg(bg_image) scores_title = "SCORES" - draw_text( - scores_title, - menu_font_title, - RED, - SCREEN_WIDTH // 2 - menu_font_title.size(scores_title)[0] // 2, - 50, - ) - - score_font_large = pygame.font.Font( - "assets/fonts/turok.ttf", 60 - ) # Increased size for scores + draw_text(scores_title, menu_font_title, RED, SCREEN_WIDTH // 2 - menu_font_title.size(scores_title)[0] // 2, 50) + + score_font_large = pygame.font.Font(resource_path("assets/fonts/turok.ttf"), 60) # Increased size for scores p1_text = f"P1: {score[0]}" p2_text = f"P2: {score[1]}" shadow_offset = 5 p1_text_x = SCREEN_WIDTH // 2 - score_font_large.size(p1_text)[0] // 2 p1_text_y = SCREEN_HEIGHT // 2 - 50 - draw_text( - p1_text, - score_font_large, - BLACK, - p1_text_x + shadow_offset, - p1_text_y + shadow_offset, - ) # Shadow - draw_gradient_text( - p1_text, score_font_large, p1_text_x, p1_text_y, [BLUE, GREEN] - ) # Gradient + draw_text(p1_text, score_font_large, BLACK, p1_text_x + shadow_offset, p1_text_y + shadow_offset) # Shadow + draw_gradient_text(p1_text, score_font_large, p1_text_x, p1_text_y, [BLUE, GREEN]) # Gradient p2_text_x = SCREEN_WIDTH // 2 - score_font_large.size(p2_text)[0] // 2 p2_text_y = SCREEN_HEIGHT // 2 + 50 - draw_text( - p2_text, - score_font_large, - BLACK, - p2_text_x + shadow_offset, - p2_text_y + shadow_offset, - ) # Shadow - draw_gradient_text( - p2_text, score_font_large, p2_text_x, p2_text_y, [RED, YELLOW] - ) # Gradient - - return_button = draw_button( - "RETURN TO MAIN MENU", - menu_font, - BLACK, - GREEN, - SCREEN_WIDTH // 2 - 220, - 700, - 500, - 50, - ) + draw_text(p2_text, score_font_large, BLACK, p2_text_x + shadow_offset, p2_text_y + shadow_offset) # Shadow + draw_gradient_text(p2_text, score_font_large, p2_text_x, p2_text_y, [RED, YELLOW]) # Gradient + + return_button = draw_button("RETURN TO MAIN MENU", menu_font, BLACK, GREEN, SCREEN_WIDTH // 2 - 220, 700, 500, 50) + + for event in pygame.event.get(): + if event.type == pygame.QUIT: + pygame.quit() + exit() + if event.type == pygame.MOUSEBUTTONDOWN: + if return_button.collidepoint(event.pos): + return + + pygame.display.update() + clock.tick(FPS) + + +def controls_screen(): + while True: + draw_bg(bg_image) + + # Title + controls_title = "CONTROLS GUIDE" + draw_text(controls_title, menu_font_title, GREEN, SCREEN_WIDTH // 2 - menu_font_title.size(controls_title)[0] // 2, 50) + + # Controls information + controls_font = pygame.font.Font(resource_path("assets/fonts/turok.ttf"), 35) + small_font = pygame.font.Font(resource_path("assets/fonts/turok.ttf"), 25) + + # Movement controls + draw_text("MOVEMENT:", controls_font, WHITE, 100, 150) + draw_text("A - Move Left", small_font, BLUE, 100, 190) + draw_text("D - Move Right", small_font, BLUE, 100, 220) + draw_text("W - Jump", small_font, BLUE, 100, 250) + + # Attack controls + draw_text("ATTACKS:", controls_font, WHITE, 100, 300) + draw_text("R - Close Range Attack", small_font, RED, 100, 340) + draw_text("T - Long Range Attack", small_font, RED, 100, 370) + + # Game info + draw_text("GAME INFO:", controls_font, WHITE, 500, 150) + draw_text("• You play as the WARRIOR (left)", small_font, YELLOW, 500, 190) + draw_text("• Computer plays as WIZARD (right)", small_font, (255, 100, 0), 500, 220) + draw_text("• Each attack deals 10 damage", small_font, WHITE, 500, 250) + draw_text("• First to lose all health loses", small_font, WHITE, 500, 280) + + # Tips + draw_text("TIPS:", controls_font, WHITE, 500, 330) + draw_text("• Jump to avoid attacks", small_font, GREEN, 500, 370) + draw_text("• Use close attacks when near", small_font, GREEN, 500, 400) + draw_text("• Use long attacks at distance", small_font, GREEN, 500, 430) + draw_text("• Watch for AI patterns!", small_font, GREEN, 500, 460) + + # Return button + return_button = draw_button("RETURN TO MAIN MENU", menu_font, BLACK, GREEN, SCREEN_WIDTH // 2 - 220, 550, 500, 50) for event in pygame.event.get(): if event.type == pygame.QUIT: @@ -327,19 +295,8 @@ def scores_screen(): def reset_game(): global fighter_1, fighter_2 - fighter_1 = Fighter( - 1, - 200, - 310, - False, - WARRIOR_DATA, - warrior_sheet, - WARRIOR_ANIMATION_STEPS, - sword_fx, - ) - fighter_2 = Fighter( - 2, 700, 310, True, WIZARD_DATA, wizard_sheet, WIZARD_ANIMATION_STEPS, magic_fx - ) + fighter_1 = Fighter(1, 200, 310, False, WARRIOR_DATA, warrior_sheet, WARRIOR_ANIMATION_STEPS, sword_fx) + fighter_2 = Fighter(2, 700, 310, True, WIZARD_DATA, wizard_sheet, WIZARD_ANIMATION_STEPS, magic_fx) def draw_health_bar(health, x, y): @@ -350,7 +307,7 @@ def draw_health_bar(health, x, y): def countdown(): - countdown_font = pygame.font.Font("assets/fonts/turok.ttf", 100) + countdown_font = pygame.font.Font(resource_path("assets/fonts/turok.ttf"), 100) countdown_texts = ["3", "2", "1", "FIGHT!"] for text in countdown_texts: @@ -380,12 +337,16 @@ def game_loop(): draw_text(f"P1: {score[0]}", score_font, RED, 20, 20) draw_text(f"P2: {score[1]}", score_font, RED, SCREEN_WIDTH - 220, 20) + + # Add player identification labels + player_label_font = pygame.font.Font(resource_path("assets/fonts/turok.ttf"), 25) + draw_text("USER", player_label_font, BLUE, 20, 85) + draw_text("COMPUTER", player_label_font, (255, 100, 0), SCREEN_WIDTH - 220, 85) + draw_health_bar(fighter_1.health, 20, 50) draw_health_bar(fighter_2.health, SCREEN_WIDTH - 220, 50) - exit_button = draw_button( - "MAIN MENU", menu_font, BLACK, YELLOW, SCREEN_WIDTH // 2 - 150, 20, 300, 50 - ) + exit_button = draw_button("MAIN MENU", menu_font, BLACK, YELLOW, SCREEN_WIDTH // 2 - 150, 20, 300, 50) if not round_over: fighter_1.move(SCREEN_WIDTH, SCREEN_HEIGHT, fighter_2, round_over) @@ -426,5 +387,7 @@ def game_loop(): if menu_selection == "START": game_loop() + elif menu_selection == "CONTROLS": + controls_screen() elif menu_selection == "SCORES": scores_screen() From c6e5bba0e3b494d4a59d53025a3e2650ced9899d Mon Sep 17 00:00:00 2001 From: katsugtgz Date: Fri, 16 Oct 2026 19:01:16 +0700 Subject: [PATCH 08/11] ci: update action versions to current majors python.yml: actions/checkout v4 -> v7, actions/setup-python v5 -> v7 Codeql.yml: actions/checkout v4 -> v7, github/codeql-action v3 -> v4 --- .github/workflows/Codeql.yml | 8 ++++---- .github/workflows/python.yml | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/Codeql.yml b/.github/workflows/Codeql.yml index 8888ce47d41..f0a31820d22 100644 --- a/.github/workflows/Codeql.yml +++ b/.github/workflows/Codeql.yml @@ -19,24 +19,24 @@ jobs: steps: # 1. 检出代码 - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v7 with: fetch-depth: 0 # 2. 初始化 CodeQL - name: Initialize CodeQL - uses: github/codeql-action/init@v3 + uses: github/codeql-action/init@v4 with: languages: python # 不指定 queries,Action 会默认跑安全 + 质量查询 # 3. 自动构建 - name: Autobuild - uses: github/codeql-action/autobuild@v3 + uses: github/codeql-action/autobuild@v4 # 4. 执行分析 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 + uses: github/codeql-action/analyze@v4 with: # 不指定 queries,Action 会自动跑安全 + 质量规则 upload: true \ No newline at end of file diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index a0fe5f85114..b91037ad10c 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -19,10 +19,10 @@ jobs: python-version: ['3.10', '3.11', '3.12'] steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 + uses: actions/setup-python@v7 with: python-version: ${{ matrix.python-version }} From d70de158ae54b8d3090d0cb4a18fce340de92ec7 Mon Sep 17 00:00:00 2001 From: Ayush Gupta Date: Tue, 27 Oct 2026 23:31:46 +0100 Subject: [PATCH 09/11] Add Snake Game --- Snake/.gitignore | 10 ++++++ Snake/.python-version | 1 + Snake/README.md | 76 +++++++++++++++++++++++++++++++++++++++++++ Snake/arena.py | 40 +++++++++++++++++++++++ Snake/constants.py | 18 ++++++++++ Snake/game.py | 69 +++++++++++++++++++++++++++++++++++++++ Snake/main.py | 42 ++++++++++++++++++++++++ Snake/menu.py | 45 +++++++++++++++++++++++++ Snake/over.py | 36 ++++++++++++++++++++ Snake/pause.py | 20 ++++++++++++ Snake/pyproject.toml | 7 ++++ Snake/snake.py | 51 +++++++++++++++++++++++++++++ Snake/utils.py | 12 +++++++ Snake/uv.lock | 8 +++++ 14 files changed, 435 insertions(+) create mode 100644 Snake/.gitignore create mode 100644 Snake/.python-version create mode 100644 Snake/README.md create mode 100644 Snake/arena.py create mode 100644 Snake/constants.py create mode 100644 Snake/game.py create mode 100644 Snake/main.py create mode 100644 Snake/menu.py create mode 100644 Snake/over.py create mode 100644 Snake/pause.py create mode 100644 Snake/pyproject.toml create mode 100644 Snake/snake.py create mode 100644 Snake/utils.py create mode 100644 Snake/uv.lock diff --git a/Snake/.gitignore b/Snake/.gitignore new file mode 100644 index 00000000000..505a3b1ca27 --- /dev/null +++ b/Snake/.gitignore @@ -0,0 +1,10 @@ +# Python-generated files +__pycache__/ +*.py[oc] +build/ +dist/ +wheels/ +*.egg-info + +# Virtual environments +.venv diff --git a/Snake/.python-version b/Snake/.python-version new file mode 100644 index 00000000000..24ee5b1be99 --- /dev/null +++ b/Snake/.python-version @@ -0,0 +1 @@ +3.13 diff --git a/Snake/README.md b/Snake/README.md new file mode 100644 index 00000000000..75ef0ba4fed --- /dev/null +++ b/Snake/README.md @@ -0,0 +1,76 @@ +# SnakePix + +SnakePix is a terminal-based clone of the classic Snake game built with Python and `curses`. + +## Description + +A polished terminal game with a fixed-size board, responsive controls, score tracking, and a main menu for choosing difficulty. The game renders a bordered arena, displays score live, and shows a game over screen with restart options. + +## Features + +- Fixed board size: `72 x 36` +- Live score display during gameplay +- Difficulty menu with three speeds: `EASY`, `MEDIUM`, `HARD` +- `WASD` and arrow key movement +- Auto-pause when terminal window is too small +- Simple snake body rendered as `@` +- Food rendered as `*` +- Game over screen with `MAIN MENU` and `QUIT` options +- No external dependencies beyond Python and `curses` + +## Requirements + +- Python 3.13+ +- Terminal with `curses` support + +## Installation + +1. Create and activate a virtual environment: + ```bash + python -m venv .venv + source .venv/bin/activate + ``` +2. Upgrade pip: + ```bash + python -m pip install --upgrade pip + ``` + +## Running the Game + +From the project root: + +- Recommended (if `uv` is installed): + ```bash + uv run main.py + ``` + +- Fallback option: + ```bash + python main.py + ``` + +## Controls + +- `W` or `Up Arrow`: move up +- `S` or `Down Arrow`: move down +- `A` or `Left Arrow`: move left +- `D` or `Right Arrow`: move right +- `Q`: quit from the main menu screen +- `Enter`: select menu options + +## Project Structure + +- `main.py` — application entry point and main loop +- `menu.py` — difficulty selection menu +- `game.py` — main game loop, snake movement, collision detection, and food handling +- `arena.py` — arena rendering, score display, and boundary drawing +- `snake.py` — snake body representation, movement, and collision logic +- `utils.py` — food generation and drawing utilities +- `pause.py` — resize handling when the terminal is too small +- `constants.py` — board dimensions and input key constants + +## Notes + +- The terminal should be at least `72` columns by `36` rows for the game to display correctly. +- The game will pause if the terminal is resized smaller than the required board dimensions. +- The snake cannot reverse directly into itself (up/down and left/right inversions are blocked). \ No newline at end of file diff --git a/Snake/arena.py b/Snake/arena.py new file mode 100644 index 00000000000..cc29a2d2ae7 --- /dev/null +++ b/Snake/arena.py @@ -0,0 +1,40 @@ +import curses +from constants import * +from pause import pause + +def arena(screen, score = 0): + height, width = screen.getmaxyx() + + if height < HEIGHT or width < WIDTH: + pause(screen, height, width) + screen.refresh() + else: + try: + for r in range(HEIGHT): + if r == 0: + screen.addch(r, 0, curses.ACS_ULCORNER) + for c in range(1, WIDTH - 1): + screen.addch(r, c, curses.ACS_HLINE) + screen.addch(r, WIDTH - 1, curses.ACS_URCORNER) + elif r == 1: + score_string = f"SCORE : {score}" + screen.addstr(r, 1, score_string) + for c in (0, WIDTH-1): + screen.addch(r, c, curses.ACS_VLINE) + elif r == HEIGHT - 1: + screen.addch(r, 0, curses.ACS_LLCORNER) + for c in range(1, WIDTH - 1): + screen.addch(r, c, curses.ACS_HLINE) + screen.addch(r, WIDTH - 1, curses.ACS_LRCORNER) + elif r == 2: + screen.addch(r, 0, curses.ACS_LTEE) + for c in range(1, WIDTH - 1): + screen.addch(r, c, curses.ACS_HLINE) + screen.addch(r, WIDTH - 1, curses.ACS_RTEE) + else: + screen.addch(r, 0, curses.ACS_VLINE) + for c in range(1, WIDTH - 1): + screen.addch(r, c, " ") + screen.addch(r, WIDTH - 1, curses.ACS_VLINE) + except curses.error: + pass diff --git a/Snake/constants.py b/Snake/constants.py new file mode 100644 index 00000000000..9ecc5f06504 --- /dev/null +++ b/Snake/constants.py @@ -0,0 +1,18 @@ +#board +WIDTH = 72 +HEIGHT = 36 + +# wasd movement + +W = ord('w') +A = ord('a') +S = ord('s') +D = ord('d') + +# directions +UP = (0, 1) +DW = (0, -1) +LT = (-1, 0) +RT = (1, 0) + + diff --git a/Snake/game.py b/Snake/game.py new file mode 100644 index 00000000000..34f8312581a --- /dev/null +++ b/Snake/game.py @@ -0,0 +1,69 @@ +import curses +from arena import arena +import constants +from utils import * +from collections import deque +from time import sleep +from pause import * +from snake import Snake + +""" +order of execution +1. check if the previous state produced a collision +2. if it does then do not render the next state and quit to over screen +3. if no collision, then render the state on the screen +""" + + +def game(screen, setting): + if setting is None: + return + + snake = Snake([(12, 12), (12, 11), (12, 10)]) + score = 0 + + screen.nodelay(True) + try: + food = gen_food(snake) + + while True: + + r, c = screen.getmaxyx() + if r < HEIGHT or c < WIDTH: + pause(screen, r, c) + screen.refresh() + sleep(0.1) + continue + + key = screen.getch() + snake.change_direction(key) + snake.move() + + if snake.head() != food: + snake.removeTail() + else: + score += 1 + food = gen_food(snake) + + if snake.is_collision(): + screen.clear() + break + + screen.clear() + arena(screen, score) + snake.draw(screen) + draw_food(screen, food) + + screen.refresh() + + sleep(setting) + finally: + screen.nodelay(False) + return score + + + + + + + diff --git a/Snake/main.py b/Snake/main.py new file mode 100644 index 00000000000..ed7347a910b --- /dev/null +++ b/Snake/main.py @@ -0,0 +1,42 @@ +import curses +from time import sleep +from menu import * +from arena import arena +from pause import * +from game import * +from over import * +from sys import exit + +def main(screen): + screen.keypad(True) + curses.noecho() + curses.curs_set(0) + try: + while True: + screen.clear() + + height, width = screen.getmaxyx() + + if height < HEIGHT or width < WIDTH: + pause(screen, height, width) + else: + arena(screen) + screen.refresh() + setting = menu(screen) + score = game(screen, setting) + selection = over(screen, score) + + if selection == "MAIN MENU": + continue + else: + exit(0) + + screen.refresh() + key = screen.getch() + if key == ord('q'): + break + finally: + screen.keypad(False) + +if __name__ == "__main__": + curses.wrapper(main) diff --git a/Snake/menu.py b/Snake/menu.py new file mode 100644 index 00000000000..f842405ce8d --- /dev/null +++ b/Snake/menu.py @@ -0,0 +1,45 @@ +import curses +from constants import * +from arena import * + +def menu(screen): + menu_options = ['EASY', 'MEDIUM', 'HARD'] + + settings = { + 'EASY': 0.20, + 'MEDIUM': 0.10, + 'HARD': 0.05 + } + + heading = "SNAKE PIX!" + selection = 0 + + while True: + screen.clear() + height, width = screen.getmaxyx() + arena(screen) + try: + + if height < HEIGHT or width < WIDTH: + pause(screen, height, width) + screen.refresh() + continue + + screen.addstr(HEIGHT//3 - 6, (WIDTH // 2) - len(heading)//2 , heading) + screen.addstr(HEIGHT//3 + 6 + selection * 2, (WIDTH // 2) - 10, ">>") + for i, option in enumerate(menu_options): + screen.addstr(HEIGHT//3 + 6 + i * 2, (WIDTH // 2) - len(option)//2, option) + + screen.refresh() + except curses.error: + return + key = screen.getch() + + if key in (S, curses.KEY_DOWN): + selection = selection + 1 if selection < 2 else selection + elif key in (W, curses.KEY_UP): + selection = selection - 1 if selection > 0 else selection + elif key == ord('\n'): + return settings[menu_options[selection]] + + diff --git a/Snake/over.py b/Snake/over.py new file mode 100644 index 00000000000..f9316020eca --- /dev/null +++ b/Snake/over.py @@ -0,0 +1,36 @@ +from constants import * +from arena import * + +def over(screen, score): + + heading = "GAME OVER!" + selection = 0 + menu_options = ['MAIN MENU', 'QUIT'] + + while True: + screen.clear() + height, width = screen.getmaxyx() + arena(screen, score) + try: + + if height < HEIGHT or width < WIDTH: + raise curses.error + + score_string = f"FINAL SCORE : {score}" + screen.addstr(HEIGHT//3 - 6, (WIDTH // 2) - len(heading)//2 , heading) + screen.addstr(HEIGHT//3 - 3, (WIDTH // 2) - len(score_string)//2 , score_string) + screen.addstr(HEIGHT//3 + 6 + selection * 2, (WIDTH // 2) - 10, ">>") + for i, option in enumerate(menu_options): + screen.addstr(HEIGHT//3 + 6 + i * 2, (WIDTH // 2) - len(option)//2, option) + + screen.refresh() + except curses.error: + return + key = screen.getch() + + if key in (S, curses.KEY_DOWN): + selection = selection + 1 if selection < 1 else selection + elif key in (W, curses.KEY_UP): + selection = selection - 1 if selection > 0 else selection + elif key == ord('\n'): + return menu_options[selection] diff --git a/Snake/pause.py b/Snake/pause.py new file mode 100644 index 00000000000..102ca5bece8 --- /dev/null +++ b/Snake/pause.py @@ -0,0 +1,20 @@ +import curses +from constants import * +from arena import * + +def pause(screen, r, c): + + screen.clear() + + message = "--FIX THE TERMINAL SIZE--" + + if len('Pause') < c < len(message): + message = "Pause" + + if c < len(message): + message = "P" + + try: + screen.addstr(r // 2, max(0, c // 2 - len(message)//2), message) + except curses.error: + pass \ No newline at end of file diff --git a/Snake/pyproject.toml b/Snake/pyproject.toml new file mode 100644 index 00000000000..e568b6fe42b --- /dev/null +++ b/Snake/pyproject.toml @@ -0,0 +1,7 @@ +[project] +name = "snakepix" +version = "0.1.0" +description = "Add your description here" +readme = "README.md" +requires-python = ">=3.13" +dependencies = [] diff --git a/Snake/snake.py b/Snake/snake.py new file mode 100644 index 00000000000..7a75198e810 --- /dev/null +++ b/Snake/snake.py @@ -0,0 +1,51 @@ +from constants import * +from collections import deque +import curses + +class Snake: + + def __init__(self, starting_positions): + self.__body = deque(starting_positions) + self.__direction = (0, 1) + + def is_part_of(self, point): + return point in self.__body + + def draw(self, screen): + for r, c in self.__body: + try: + screen.addstr(r, c, "@") + except curses.error: + pass + + def move(self): + r, c = self.__body[0] + new_head = (r + self.__direction[0], c + self.__direction[1]) + self.__body.appendleft(new_head) + + def removeTail(self): + self.__body.pop() + + def change_direction(self, key): + if key in (W, curses.KEY_UP ) and self.__direction != (1, 0): + self.__direction = (-1, 0) + elif key in (S, curses.KEY_DOWN ) and self.__direction != (-1, 0): + self.__direction = (1, 0) + elif key in (A, curses.KEY_LEFT ) and self.__direction != (0, 1): + self.__direction = (0, -1) + elif key in (D, curses.KEY_RIGHT) and self.__direction != (0, -1): + self.__direction = (0, 1) + + def head(self): + return self.__body[0] + + def is_collision(self): + head = self.__body[0] + r, c = head + if r == 2 or r == HEIGHT-1 or c == 0 or c == WIDTH - 1: + return True + + if head in list(self.__body)[1:]: + return True + + return False \ No newline at end of file diff --git a/Snake/utils.py b/Snake/utils.py new file mode 100644 index 00000000000..de541db4a69 --- /dev/null +++ b/Snake/utils.py @@ -0,0 +1,12 @@ +from constants import * +import random + +def gen_food(snake): + while True: + pos = (random.randint(3, HEIGHT-2), random.randint(2, WIDTH-2)) + if not snake.is_part_of(pos): + return pos + +def draw_food(screen, food): + r, c = food + screen.addstr(r, c, "*") \ No newline at end of file diff --git a/Snake/uv.lock b/Snake/uv.lock new file mode 100644 index 00000000000..381b666ed02 --- /dev/null +++ b/Snake/uv.lock @@ -0,0 +1,8 @@ +version = 1 +revision = 3 +requires-python = ">=3.13" + +[[package]] +name = "snakepix" +version = "0.1.0" +source = { virtual = "." } From 8a8fd696978606929a3c2aceda51a6d26ddcc7c5 Mon Sep 17 00:00:00 2001 From: Ayush Gupta Date: Tue, 3 Nov 2026 16:54:42 +0100 Subject: [PATCH 10/11] Game Working --- Snake/README.md | 20 +++++--------------- Snake/pyproject.toml | 7 ------- Snake/requirements.txt | 6 ++++++ Snake/uv.lock | 8 -------- 4 files changed, 11 insertions(+), 30 deletions(-) delete mode 100644 Snake/pyproject.toml create mode 100644 Snake/requirements.txt delete mode 100644 Snake/uv.lock diff --git a/Snake/README.md b/Snake/README.md index 75ef0ba4fed..f886d3f321a 100644 --- a/Snake/README.md +++ b/Snake/README.md @@ -25,26 +25,16 @@ A polished terminal game with a fixed-size board, responsive controls, score tra ## Installation -1. Create and activate a virtual environment: - ```bash - python -m venv .venv - source .venv/bin/activate - ``` -2. Upgrade pip: +1. Linux or Mac - Curses comes pre-installed with python3 + +2. Windows - Run ```bash - python -m pip install --upgrade pip - ``` + pip3 install -r requirements.txt ## Running the Game From the project root: -- Recommended (if `uv` is installed): - ```bash - uv run main.py - ``` - -- Fallback option: ```bash python main.py ``` @@ -73,4 +63,4 @@ From the project root: - The terminal should be at least `72` columns by `36` rows for the game to display correctly. - The game will pause if the terminal is resized smaller than the required board dimensions. -- The snake cannot reverse directly into itself (up/down and left/right inversions are blocked). \ No newline at end of file +- The snake cannot reverse directly into itself (up/down and left/right inversions are blocked). diff --git a/Snake/pyproject.toml b/Snake/pyproject.toml deleted file mode 100644 index e568b6fe42b..00000000000 --- a/Snake/pyproject.toml +++ /dev/null @@ -1,7 +0,0 @@ -[project] -name = "snakepix" -version = "0.1.0" -description = "Add your description here" -readme = "README.md" -requires-python = ">=3.13" -dependencies = [] diff --git a/Snake/requirements.txt b/Snake/requirements.txt new file mode 100644 index 00000000000..2a75d731103 --- /dev/null +++ b/Snake/requirements.txt @@ -0,0 +1,6 @@ +# Standard dependencies (if you have any) +# requests>=2.31.0 + +# Install curses wrapper ONLY if the target system is Windows +windows-curses; sys_platform == 'win32' + diff --git a/Snake/uv.lock b/Snake/uv.lock deleted file mode 100644 index 381b666ed02..00000000000 --- a/Snake/uv.lock +++ /dev/null @@ -1,8 +0,0 @@ -version = 1 -revision = 3 -requires-python = ">=3.13" - -[[package]] -name = "snakepix" -version = "0.1.0" -source = { virtual = "." } From 53f9ab21ea465d5da3be173b79cd5262dc39db71 Mon Sep 17 00:00:00 2001 From: Ayush Gupta Date: Thu, 5 Nov 2026 14:57:46 +0100 Subject: [PATCH 11/11] Fibonacci Script Complete --- fib.py | 105 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100755 fib.py diff --git a/fib.py b/fib.py new file mode 100755 index 00000000000..915aefbdd96 --- /dev/null +++ b/fib.py @@ -0,0 +1,105 @@ +#! /usr/bin/python3 + +""" +Author: + Name: Ayush Gupta + Email: in.ayushgupta@gmail.com + GitHub: inayushgupta + +Description: +The Fibonacci code avoid the extra work. +It not only utlizes dynamic programming and +avoid repreated calls for same calculations +but also it does saves that result for multiple +values. +Sorted list, for example [4, 9, 10] does not +have to calculate fibonacci for every numebr +-- 4 : 1, 2, 3, 4 +-- 9 : 1, 2, 3, 4, 5, 6, 7, 8, 9 +-- 10: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 + +It saves the result as it goes and avoid recalculation + +-- 4 : 1, 2, 3, 4 --> saves 3 and 4 +-- 9 : ( use 3 and 4 ) --> 5, 6, 7, 8, 9 --> saves 8 and 9 +-- 10: ( use 8 and 9 ) --> 10 + +saving so much work. +""" + +import sys + +# global state tracking +_last = 1 +_prev, _curr = 0, 1 + +# calculate the next fib using saved states +def fib(n): + global _last, _prev, _curr + + if n in (0, 1): + return n + + for _ in range(_last, n): + _curr, _prev = _prev + _curr, _curr + + _last = n + + return _curr + + +def interactive_input(): + + tries = 3 + while tries: + n = input("Enter your number: ") + if n.isdigit(): + return int(n) + print("INVALID INPUT: Please enter a valid number.") + tries -= 1 + print("MAXIMUM NUMBER OF TRIES: exiting") + sys.exit(1) + + +def process_args() -> list[int]: + + args = sys.argv + + if len(args) < 2: + number = interactive_input() + if not number: + sys.exit(1) + return [number] + + nums = [] + for i in range(1, len(args)): + if not args[i].isdigit(): + print("INVALID INPUT: one or more input numbers are not valid.") + sys.exit(1) + nums.append(int(args[i])) + + + return nums + +def main(): + + nums = process_args() + + # sort the number to cut some time off + nums.sort() + + # remove duplicates + sorted_nums = [nums[0]] + prev = nums[0] + for num in nums[1:]: + if num == prev: + continue + sorted_nums.append(num) + prev = num + + for num in sorted_nums: + print(f"{num}:{fib(num)}") + + sys.exit(0) + +main()