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 }}
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..f886d3f321a
--- /dev/null
+++ b/Snake/README.md
@@ -0,0 +1,66 @@
+# 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. Linux or Mac - Curses comes pre-installed with python3
+
+2. Windows - Run
+ ```bash
+ pip3 install -r requirements.txt
+
+## Running the Game
+
+From the project root:
+
+ ```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).
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/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/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/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
-
+
+# 🥊 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.
+
-## 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.
+

-- **Sound Effects and Music**:
- - Immersive soundtracks and attack effects.
+[](https://python.org)
+[](https://pygame.org)
+[](LICENSE)
+[](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**
+
+
+**Master of Blades**
+- ⚔️ Devastating sword attacks
+- 🛡️ Heavy armor protection
+- 💪 High damage output
+- 🏃♂️ Agile combat moves
+
+ |
+
+
+### 🧙♂️ **The Wizard**
+
+
+**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)**
+
-[](https://github.com/AadityaPanda/Street_Fighter/releases/latest)
+| 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)**
+
-## 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**
+
+[](https://github.com/AadityaPanda/Street_Fighter/releases/latest)
+
+[](https://github.com/AadityaPanda/Street_Fighter/releases/download/v1.1/Game.zip)
+
+[](https://github.com/AadityaPanda/Street_Fighter/archive/refs/heads/main.zip)
+
+
+
+---
+
+## 📄 License
+
+
+
+This project is licensed under the **MIT License**
+
+[](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
+
+
+
+[](https://github.com/AadityaPanda/Street_Fighter/graphs/contributors)
+
+*Check [CONTRIBUTING.md](CONTRIBUTING.md) for detailed guidelines*
+
+
+
+---
+
+## 📧 Contact
+
+
+
+### 🌐 **Let's Connect!**
+
+[](mailto:aadityapanda23@gmail.com)
+[](https://github.com/AadityaPanda)
+[](https://www.linkedin.com/in/aadityapanda/)
+
+---
+
+### 💫 *"Try something new every day!"*
+
+

-## Contact
+
-- **Developer**: Aaditya Panda
-- **Email**: [aadityapanda23@gmail.com](mailto:aadityapanda23@gmail.com)
-- **GitHub**: [AadityaPanda](https://github.com/AadityaPanda)
+
+
-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()
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
+
diff --git a/dice_roller.py b/dice_roller.py
index de805c61646..e2bd77fcd5f 100644
--- a/dice_roller.py
+++ b/dice_roller.py
@@ -1,57 +1,62 @@
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...")
+
+ # 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 = separator.join(dice_art[line_index] for dice_art in dice_arts)
+ print(row_line)
+
+ print(divider)
+
+ # Output results and total sum
+ print(f"Individual Rolls: {rolls}")
+ print(f"Total Sum: {sum(rolls)}")
+ print("========================================")
+
+
+if __name__ == "__main__":
+ main()
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
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()
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
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")