diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index 11b97531109..20b0f50af6b 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -21,7 +21,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v5 with: - python-version: '3.13.7' + python-version: '3.14.0' - name: Install all dependencies and tools run: | @@ -38,7 +38,7 @@ jobs: run: pytest || true - name: Run Ruff checks with ignored rules - run: ruff check . --ignore B904,B905,EM101,EXE001,G004,ISC001,PLC0415,PLC1901,PLW060,PLW1641,PLW2901,PT011,PT018,PT028,S101,S311,SIM905,SLF001 + run: ruff check . --ignore B904,B905,EM101,EXE001,G004,ISC001,PLC0415,PLC1901,PLW060,PLW1641,PLW2901,PT011,PT018,PT028,S101,S311,SIM905,SLF001,F405 - name: Run Mypy type checks run: mypy . --ignore-missing-imports || true \ No newline at end of file diff --git a/.gitignore b/.gitignore index 0f3717818e6..5d5134469d2 100644 --- a/.gitignore +++ b/.gitignore @@ -16,12 +16,10 @@ for i in string: odd+=i print(lower+upper+odd+even) +.vscode +__pycache__/ .venv -# operating system-related files -# file properties cache/storage on macOS *.DS_Store - -# thumbnail cache on Windows Thumbs.db bankmanaging.db \ No newline at end of file diff --git a/BoardGame-CLI/python.py b/BoardGame-CLI/python.py index 49929a775ce..5a28f5adf6f 100644 --- a/BoardGame-CLI/python.py +++ b/BoardGame-CLI/python.py @@ -87,4 +87,5 @@ def play_snakes_and_ladders(): # Start the game -play_snakes_and_ladders() +if __name__ == "__main__": + play_snakes_and_ladders() diff --git a/BoardGame-CLI/snakeLadder.py b/BoardGame-CLI/snakeLadder.py index ea605cab340..fd06dd64527 100644 --- a/BoardGame-CLI/snakeLadder.py +++ b/BoardGame-CLI/snakeLadder.py @@ -90,7 +90,7 @@ def play(): elif n == 2: players = {} # stores player ans their locations isReady = {} - current_loc = 0 # vaiable for iterating location + current_loc = 1 # reset starting location to 1 player_input() elif n == 3: @@ -170,4 +170,5 @@ def ladder(a, i): print("/" * 40) -player_input() +if __name__ == "__main__": + player_input() diff --git a/BoardGame-CLI/uno.py b/BoardGame-CLI/uno.py index f10f46c4ff8..dca5f1e10c1 100644 --- a/BoardGame-CLI/uno.py +++ b/BoardGame-CLI/uno.py @@ -1,16 +1,25 @@ # uno game # import random +from typing import List """ Generate the UNO deck of 108 cards. -Parameters: None -Return values: deck=>list + +Doctest examples: + +>>> deck = buildDeck() +>>> len(deck) +108 +>>> sum(1 for c in deck if 'Wild' in c) +8 + +Return: list of card strings (e.g. 'Red 7', 'Wild Draw Four') """ -def buildDeck(): - deck = [] +def buildDeck() -> List[str]: + deck: List[str] = [] # example card:Red 7,Green 8, Blue skip colours = ["Red", "Green", "Yellow", "Blue"] values = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "Draw Two", "Skip", "Reverse"] @@ -24,7 +33,6 @@ def buildDeck(): for i in range(4): deck.append(wilds[0]) deck.append(wilds[1]) - print(deck) return deck @@ -35,10 +43,9 @@ def buildDeck(): """ -def shuffleDeck(deck): - for cardPos in range(len(deck)): - randPos = random.randint(0, 107) - deck[cardPos], deck[randPos] = deck[randPos], deck[cardPos] +def shuffleDeck(deck: List[str]) -> List[str]: + # use Python's built-in shuffle which is efficient and correct + random.shuffle(deck) return deck @@ -48,10 +55,18 @@ def shuffleDeck(deck): """ -def drawCards(numCards): - cardsDrawn = [] +def drawCards(numCards: int) -> List[str]: + """ + Draw a number of cards from the top of the global `unoDeck`. + + Raises ValueError if the deck runs out of cards. + """ + cardsDrawn: List[str] = [] for x in range(numCards): - cardsDrawn.append(unoDeck.pop(0)) + try: + cardsDrawn.append(unoDeck.pop(0)) + except IndexError: + raise ValueError("The deck is empty; cannot draw more cards") return cardsDrawn @@ -62,7 +77,7 @@ def drawCards(numCards): """ -def showHand(player, playerHand): +def showHand(player: int, playerHand: List[str]) -> None: print("Player {}'s Turn".format(players_name[player])) print("Your Hand") print("------------------") @@ -80,7 +95,15 @@ def showHand(player, playerHand): """ -def canPlay(colour, value, playerHand): +def canPlay(colour: str, value: str, playerHand: List[str]) -> bool: + """ + Return True if any card in playerHand is playable on a discard with given colour and value. + + >>> canPlay('Red','5',['Red 3','Green 5']) + True + >>> canPlay('Blue','7',['Green 1']) + False + """ for card in playerHand: if "Wild" in card: return True @@ -89,101 +112,118 @@ def canPlay(colour, value, playerHand): return False +# --- Global deck and initial setup --- unoDeck = buildDeck() unoDeck = shuffleDeck(unoDeck) unoDeck = shuffleDeck(unoDeck) -discards = [] +discards: List[str] = [] -players_name = [] -players = [] +players_name: List[str] = [] +players: List[List[str]] = [] colours = ["Red", "Green", "Yellow", "Blue"] -numPlayers = int(input("How many players?")) -while numPlayers < 2 or numPlayers > 4: - numPlayers = int( - input("Invalid. Please enter a number between 2-4.\nHow many players?") - ) -for player in range(numPlayers): - players_name.append(input("Enter player {} name: ".format(player + 1))) - players.append(drawCards(5)) - - -playerTurn = 0 -playDirection = 1 -playing = True -discards.append(unoDeck.pop(0)) -splitCard = discards[0].split(" ", 1) -currentColour = splitCard[0] -if currentColour != "Wild": - cardVal = splitCard[1] -else: - cardVal = "Any" - -while playing: - showHand(playerTurn, players[playerTurn]) - print("Card on top of discard pile: {}".format(discards[-1])) - if canPlay(currentColour, cardVal, players[playerTurn]): - cardChosen = int(input("Which card do you want to play?")) - while not canPlay( - currentColour, cardVal, [players[playerTurn][cardChosen - 1]] - ): - cardChosen = int(input("Not a valid card. Which card do you want to play?")) - print("You played {}".format(players[playerTurn][cardChosen - 1])) - discards.append(players[playerTurn].pop(cardChosen - 1)) - - # cheak if player won - if len(players[playerTurn]) == 0: - playing = False - # winner = "Player {}".format(playerTurn+1) - winner = players_name[playerTurn] - else: - # cheak for special cards - splitCard = discards[-1].split(" ", 1) - currentColour = splitCard[0] - if len(splitCard) == 1: - cardVal = "Any" - else: - cardVal = splitCard[1] - if currentColour == "Wild": - for x in range(len(colours)): - print("{}) {}".format(x + 1, colours[x])) - newColour = int(input("What colour would you like to choose? ")) - while newColour < 1 or newColour > 4: - newColour = int( - input("Invalid option. What colour would you like to choose") - ) - currentColour = colours[newColour - 1] - if cardVal == "Reverse": - playDirection = playDirection * -1 - elif cardVal == "Skip": - playerTurn += playDirection - if playerTurn >= numPlayers: - playerTurn = 0 - elif playerTurn < 0: - playerTurn = numPlayers - 1 - elif cardVal == "Draw Two": - playerDraw = playerTurn + playDirection - if playerDraw == numPlayers: - playerDraw = 0 - elif playerDraw < 0: - playerDraw = numPlayers - 1 - players[playerDraw].extend(drawCards(2)) - elif cardVal == "Draw Four": - playerDraw = playerTurn + playDirection - if playerDraw == numPlayers: - playerDraw = 0 - elif playerDraw < 0: - playerDraw = numPlayers - 1 - players[playerDraw].extend(drawCards(4)) - print("") + + +def main() -> None: + """Run interactive UNO game (keeps original behavior). + + Note: main() is interactive and not exercised by doctest. + """ + global players_name, players, discards + + numPlayers = int(input("How many players?")) + while numPlayers < 2 or numPlayers > 4: + numPlayers = int( + input("Invalid. Please enter a number between 2-4.\nHow many players?") + ) + for player in range(numPlayers): + players_name.append(input("Enter player {} name: ".format(player + 1))) + players.append(drawCards(5)) + + playerTurn = 0 + playDirection = 1 + playing = True + discards.append(unoDeck.pop(0)) + splitCard = discards[0].split(" ", 1) + currentColour = splitCard[0] + if currentColour != "Wild": + cardVal = splitCard[1] else: - print("You can't play. You have to draw a card.") - players[playerTurn].extend(drawCards(1)) + cardVal = "Any" + + while playing: + showHand(playerTurn, players[playerTurn]) + print("Card on top of discard pile: {}".format(discards[-1])) + if canPlay(currentColour, cardVal, players[playerTurn]): + cardChosen = int(input("Which card do you want to play?")) + while not canPlay( + currentColour, cardVal, [players[playerTurn][cardChosen - 1]] + ): + cardChosen = int( + input("Not a valid card. Which card do you want to play?") + ) + print("You played {}".format(players[playerTurn][cardChosen - 1])) + discards.append(players[playerTurn].pop(cardChosen - 1)) + + # cheak if player won + if len(players[playerTurn]) == 0: + playing = False + # winner = "Player {}".format(playerTurn+1) + winner = players_name[playerTurn] + else: + # cheak for special cards + splitCard = discards[-1].split(" ", 1) + currentColour = splitCard[0] + if len(splitCard) == 1: + cardVal = "Any" + else: + cardVal = splitCard[1] + if currentColour == "Wild": + for x in range(len(colours)): + print("{}) {}".format(x + 1, colours[x])) + newColour = int(input("What colour would you like to choose? ")) + while newColour < 1 or newColour > 4: + newColour = int( + input( + "Invalid option. What colour would you like to choose" + ) + ) + currentColour = colours[newColour - 1] + if cardVal == "Reverse": + playDirection = playDirection * -1 + elif cardVal == "Skip": + playerTurn += playDirection + if playerTurn >= numPlayers: + playerTurn = 0 + elif playerTurn < 0: + playerTurn = numPlayers - 1 + elif cardVal == "Draw Two": + playerDraw = playerTurn + playDirection + if playerDraw == numPlayers: + playerDraw = 0 + elif playerDraw < 0: + playerDraw = numPlayers - 1 + players[playerDraw].extend(drawCards(2)) + elif cardVal == "Draw Four": + playerDraw = playerTurn + playDirection + if playerDraw == numPlayers: + playerDraw = 0 + elif playerDraw < 0: + playerDraw = numPlayers - 1 + players[playerDraw].extend(drawCards(4)) + print("") + else: + print("You can't play. You have to draw a card.") + players[playerTurn].extend(drawCards(1)) + + playerTurn += playDirection + if playerTurn >= numPlayers: + playerTurn = 0 + elif playerTurn < 0: + playerTurn = numPlayers - 1 + + print("Game Over") + print("{} is the Winner!".format(winner)) - playerTurn += playDirection - if playerTurn >= numPlayers: - playerTurn = 0 - elif playerTurn < 0: - playerTurn = numPlayers - 1 -print("Game Over") -print("{} is the Winner!".format(winner)) +if __name__ == "__main__": + main() diff --git a/Industrial_developed_hangman/tests/test_hangman/test_main.py b/Industrial_developed_hangman/tests/test_hangman/test_main.py index 9d377c7f9e9..6567e56b765 100644 --- a/Industrial_developed_hangman/tests/test_hangman/test_main.py +++ b/Industrial_developed_hangman/tests/test_hangman/test_main.py @@ -1,5 +1,3 @@ -import os -from pathlib import Path from typing import Callable, List import pytest @@ -8,7 +6,6 @@ from src.hangman.main import ( MainProcess, Source, - parse_word_from_local, parse_word_from_site, ) diff --git a/Password Manager Using Tkinter/PGV.py b/Password Manager Using Tkinter/PGV.py new file mode 100644 index 00000000000..045625ea650 --- /dev/null +++ b/Password Manager Using Tkinter/PGV.py @@ -0,0 +1,18 @@ +import json + +new_data = { + website_input.get():{ + "email": email_input.get(), + "password": passw_input.get() + } + } + +try: + with open("data.json", "r") as data_file: + data = json.load(data_file) +except FileNotFoundError: + with open("data.json", "w") as data_file: + pass +else: + with open("data.json", "w") as data_file: + json.dump(new_data, data_file, indent = 4) \ No newline at end of file diff --git a/Password Manager Using Tkinter/README.md b/Password Manager Using Tkinter/README.md new file mode 100644 index 00000000000..a31bf876550 --- /dev/null +++ b/Password Manager Using Tkinter/README.md @@ -0,0 +1,68 @@ +# My Personal Password Manager + +Hello there! Welcome to my Password Manager project. I created this application to provide a simple and secure way **to manage all my website login credentials in one place**. It's a desktop application **built with Python**, and I've focused on making it both functional and easy on the eyes. + +--- + +## What It Can Do + +- **Generate Strong Passwords**: One click = secure, random passwords (auto-copied to clipboard!). + +- **Save Credentials**: Store site name, email/username, and password safely. + +- **Quick Search**: Instantly find saved logins by website name. + +- **View All Passwords**: Unlock all saved entries with your master password — neatly organized in a table. + +- **One-Click Copy**: Copy email or password directly from the list for quick logins. + +--- + +## How I Built It + +- **Python**: Powers the core logic behind the app. + +- **Tkinter**: Handles the clean and simple user interface. + +- **ttkbootstrap**: Adds a modern, professional theme to the UI. + +- **JSON**: Stores all password data securely in a local data.json file. + +--- + +## How to Get Started + +1. Install Python 3 on your system. + +2. Download all project files (main.py, logo.png, requirements.txt). + +3. Open Terminal/CMD, navigate to the project folder, and run: +```python +pip install -r requirements.txt +``` + +4. Start the app with: +```python +python main.py +``` + +5. Enjoy! + +--- + +## Screenshots + + + + + + + + + + +
Main Window where from where you can add credentialsMain Window
Second window — where you can see all your saved passwords (but hey, you’ll need a password to see your passwords 😏). +Main Window
+ + + diff --git a/Password Manager Using Tkinter/data.json b/Password Manager Using Tkinter/data.json new file mode 100644 index 00000000000..399ea997575 --- /dev/null +++ b/Password Manager Using Tkinter/data.json @@ -0,0 +1,14 @@ +{ + "Amazon": { + "email": "prashant123Amazon@gmail.com", + "password": "1mD%#Z555rF$&2aRpI" + }, + "Facebook": { + "email": "prashant123Facebook@gmail.com", + "password": "qQ6#H7o&)i8S!3sO)c4" + }, + "Flipkart": { + "email": "prashant123Flipkart@gmail.com", + "password": "1!+7NqmUZbN18K(3A+" + } +} \ No newline at end of file diff --git a/Password Manager Using Tkinter/data.txt b/Password Manager Using Tkinter/data.txt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/Password Manager Using Tkinter/logo.png b/Password Manager Using Tkinter/logo.png new file mode 100644 index 00000000000..59b69165150 Binary files /dev/null and b/Password Manager Using Tkinter/logo.png differ diff --git a/Password Manager Using Tkinter/main.py b/Password Manager Using Tkinter/main.py new file mode 100644 index 00000000000..15d6fdc1d13 --- /dev/null +++ b/Password Manager Using Tkinter/main.py @@ -0,0 +1,207 @@ +import tkinter as tk +from tkinter import messagebox, simpledialog +import ttkbootstrap as ttk +from ttkbootstrap.constants import * +import pyperclip +import json +from random import choice, randint, shuffle + +# ---------------------------- CONSTANTS ------------------------------- # +FONT_NAME = "Helvetica" +# IMP: this is not a secure way to store a master password. +# in a real application, this should be changed and stored securely (e.g., hashed and salted). +MASTER_PASSWORD = "password123" + +# ---------------------------- PASSWORD GENERATOR ------------------------------- # +def generate_password(): + """generates a random strong password and copies it to clipboard.""" + letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'] + numbers = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'] + symbols = ['!', '#', '$', '%', '&', '(', ')', '*', '+'] + + password_letters = [choice(letters) for _ in range(randint(8, 10))] + password_symbols = [choice(symbols) for _ in range(randint(2, 4))] + password_numbers = [choice(numbers) for _ in range(randint(2, 4))] + + password_list = password_letters + password_symbols + password_numbers + shuffle(password_list) + + password = "".join(password_list) + password_entry.delete(0, tk.END) + password_entry.insert(0, password) + pyperclip.copy(password) + messagebox.showinfo(title="Password Generated", message="Password copied to clipboard!") + +# ---------------------------- SAVE PASSWORD ------------------------------- # +def save(): + """saves the website, email, and password to a JSON file.""" + website = website_entry.get() + email = email_entry.get() + password = password_entry.get() + new_data = { + website: { + "email": email, + "password": password, + } + } + + if not website or not password: + messagebox.showerror(title="Oops", message="Please don't leave any fields empty!") + return + + is_ok = messagebox.askokcancel(title=website, message=f"These are the details entered: \nEmail: {email} " + f"\nPassword: {password} \nIs it ok to save?") + if is_ok: + try: + with open("data.json", "r") as data_file: + data = json.load(data_file) + except (FileNotFoundError, json.JSONDecodeError): + data = {} + + data.update(new_data) + + with open("data.json", "w") as data_file: + json.dump(data, data_file, indent=4) + + website_entry.delete(0, tk.END) + password_entry.delete(0, tk.END) + +# ---------------------------- FIND PASSWORD ------------------------------- # +def find_password(): + """finds and displays password for a given website.""" + website = website_entry.get() + try: + with open("data.json", "r") as data_file: + data = json.load(data_file) + except (FileNotFoundError, json.JSONDecodeError): + messagebox.showerror(title="Error", message="No Data File Found.") + return + + if website in data: + email = data[website]["email"] + password = data[website]["password"] + messagebox.showinfo(title=website, message=f"Email: {email}\nPassword: {password}") + pyperclip.copy(password) + messagebox.showinfo(title="Copied", message="Password for {} copied to clipboard.".format(website)) + else: + messagebox.showerror(title="Error", message=f"No details for {website} exists.") + +# ---------------------------- VIEW ALL PASSWORDS ------------------------------- # +def view_all_passwords(): + """prompts for master password and displays all saved passwords if correct.""" + password = simpledialog.askstring("Master Password", "Please enter the master password:", show='*') + + if password == MASTER_PASSWORD: + show_passwords_window() + elif password is not None: # avoids error message if user clicks cancel + messagebox.showerror("Incorrect Password", "The master password you entered is incorrect.") + +def show_passwords_window(): + """creates a new window to display all passwords in a table.""" + all_passwords_window = tk.Toplevel(window) + all_passwords_window.title("All Saved Passwords") + all_passwords_window.config(padx=20, pady=20) + + # a frame for the treeview and scrollbar + tree_frame = ttk.Frame(all_passwords_window) + tree_frame.grid(row=0, column=0, columnspan=2, sticky='nsew') + + # a Treeview (table) + cols = ('Website', 'Email', 'Password') + tree = ttk.Treeview(tree_frame, columns=cols, show='headings') + + # column headings and widths + tree.heading('Website', text='Website') + tree.column('Website', width=150) + tree.heading('Email', text='Email/Username') + tree.column('Email', width=200) + tree.heading('Password', text='Password') + tree.column('Password', width=200) + + tree.grid(row=0, column=0, sticky='nsew') + + # a scrollbar + scrollbar = ttk.Scrollbar(tree_frame, orient=tk.VERTICAL, command=tree.yview) + tree.configure(yscroll=scrollbar.set) + scrollbar.grid(row=0, column=1, sticky='ns') + + # load data from JSON file + try: + with open("data.json", "r") as data_file: + data = json.load(data_file) + + # insert data into the treeview + for website, details in data.items(): + tree.insert("", "end", values=(website, details['email'], details['password'])) + + except (FileNotFoundError, json.JSONDecodeError): + # if file not found or empty, it will just show an empty table + pass + + def copy_selected_info(column_index, info_type): + """copies the email or password of the selected row.""" + selected_item = tree.focus() + if not selected_item: + messagebox.showwarning("No Selection", "Please select a row from the table first.", parent=all_passwords_window) + return + + item_values = tree.item(selected_item, 'values') + info_to_copy = item_values[column_index] + pyperclip.copy(info_to_copy) + messagebox.showinfo("Copied!", f"The {info_type.lower()} for '{item_values[0]}' has been copied to your clipboard.", parent=all_passwords_window) + + # a frame for the buttons + button_frame = ttk.Frame(all_passwords_window) + button_frame.grid(row=1, column=0, columnspan=2, pady=(10,0)) + + copy_email_button = ttk.Button(button_frame, text="Copy Email", style="success.TButton", command=lambda: copy_selected_info(1, "Email")) + copy_email_button.pack(side=tk.LEFT, padx=5) + + copy_password_button = ttk.Button(button_frame, text="Copy Password", style="success.TButton", command=lambda: copy_selected_info(2, "Password")) + copy_password_button.pack(side=tk.LEFT, padx=5) + + all_passwords_window.grab_set() # makes window modal + +# ---------------------------- UI SETUP ------------------------------- # +window = ttk.Window(themename="superhero") +window.title("Password Manager") +window.config(padx=50, pady=50) + +# logo +canvas = tk.Canvas(width=200, height=200, highlightthickness=0) +logo_img = tk.PhotoImage(file="logo.png") +canvas.create_image(100, 100, image=logo_img) +canvas.grid(row=0, column=1, pady=(0, 20)) + +# labels +website_label = ttk.Label(text="Website:", font=(FONT_NAME, 12)) +website_label.grid(row=1, column=0, sticky="W") +email_label = ttk.Label(text="Email/Username:", font=(FONT_NAME, 12)) +email_label.grid(row=2, column=0, sticky="W") +password_label = ttk.Label(text="Password:", font=(FONT_NAME, 12)) +password_label.grid(row=3, column=0, sticky="W") + +# entries +website_entry = ttk.Entry(width=32) +website_entry.grid(row=1, column=1, pady=5, sticky="EW") +website_entry.focus() +email_entry = ttk.Entry(width=50) +email_entry.grid(row=2, column=1, columnspan=2, pady=5, sticky="EW") +email_entry.insert(0, "example@email.com") +password_entry = ttk.Entry(width=32) +password_entry.grid(row=3, column=1, pady=5, sticky="EW") + +# buttons +search_button = ttk.Button(text="Search", width=14, command=find_password, style="info.TButton") +search_button.grid(row=1, column=2, sticky="EW", padx=(5,0)) +generate_password_button = ttk.Button(text="Generate Password", command=generate_password, style="success.TButton") +generate_password_button.grid(row=3, column=2, sticky="EW", padx=(5,0)) +add_button = ttk.Button(text="Add", width=43, command=save, style="primary.TButton") +add_button.grid(row=4, column=1, columnspan=2, pady=(10,0), sticky="EW") + +view_all_button = ttk.Button(text="View All Passwords", command=view_all_passwords, style="secondary.TButton") +view_all_button.grid(row=5, column=1, columnspan=2, pady=(10,0), sticky="EW") + + +window.mainloop() + diff --git a/Password Manager Using Tkinter/requirements.txt b/Password Manager Using Tkinter/requirements.txt new file mode 100644 index 00000000000..60ffacab8a4 --- /dev/null +++ b/Password Manager Using Tkinter/requirements.txt @@ -0,0 +1 @@ +ttkbootstrap \ No newline at end of file diff --git a/Tic-Tac-Toe Games/tic-tac-toe1.py b/Tic-Tac-Toe Games/tic-tac-toe1.py index f9825207079..4f87c72b7c6 100644 --- a/Tic-Tac-Toe Games/tic-tac-toe1.py +++ b/Tic-Tac-Toe Games/tic-tac-toe1.py @@ -28,11 +28,13 @@ def print_board(board: Board) -> None: def check_winner(board: Board, player: str) -> bool: """Return True if `player` has won.""" for i in range(3): - if all(board[i][j] == player for j in range(3)) or \ - all(board[j][i] == player for j in range(3)): + if all(board[i][j] == player for j in range(3)) or all( + board[j][i] == player for j in range(3) + ): return True - if all(board[i][i] == player for i in range(3)) or \ - all(board[i][2 - i] == player for i in range(3)): + if all(board[i][i] == player for i in range(3)) or all( + board[i][2 - i] == player for i in range(3) + ): return True return False @@ -86,5 +88,6 @@ def main() -> None: if __name__ == "__main__": import doctest + doctest.testmod() main() diff --git a/Tic-Tac-Toe Games/tic-tac-toe3.py b/Tic-Tac-Toe Games/tic-tac-toe3.py index f7a4fe3d5b7..92c60d494e6 100644 --- a/Tic-Tac-Toe Games/tic-tac-toe3.py +++ b/Tic-Tac-Toe Games/tic-tac-toe3.py @@ -21,11 +21,13 @@ def check_winner(board: Board, player: str) -> bool: """Check if `player` has a winning line on `board`.""" for i in range(3): - if all(board[i][j] == player for j in range(3)) or \ - all(board[j][i] == player for j in range(3)): + if all(board[i][j] == player for j in range(3)) or all( + board[j][i] == player for j in range(3) + ): return True - if all(board[i][i] == player for i in range(3)) or \ - all(board[i][2 - i] == player for i in range(3)): + if all(board[i][i] == player for i in range(3)) or all( + board[i][2 - i] == player for i in range(3) + ): return True return False @@ -116,16 +118,19 @@ def ai_move() -> None: # --- Initialize GUI --- root = ctk.CTk() root.title("Tic-Tac-Toe") -board: Board = [[" "]*3 for _ in range(3)] +board: Board = [[" "] * 3 for _ in range(3)] buttons: List[List[ctk.CTkButton]] = [] for i in range(3): row_buttons: List[ctk.CTkButton] = [] for j in range(3): btn = ctk.CTkButton( - root, text=" ", font=("normal", 30), - width=100, height=100, - command=lambda r=i, c=j: make_move(r, c) + root, + text=" ", + font=("normal", 30), + width=100, + height=100, + command=lambda r=i, c=j: make_move(r, c), ) btn.grid(row=i, column=j, padx=2, pady=2) row_buttons.append(btn) @@ -133,5 +138,6 @@ def ai_move() -> None: if __name__ == "__main__": import doctest + doctest.testmod() root.mainloop() diff --git a/Tic-Tac-Toe Games/tic-tac-toe6.py b/Tic-Tac-Toe Games/tic-tac-toe6.py index 329675be2c9..294f6fa0a17 100644 --- a/Tic-Tac-Toe Games/tic-tac-toe6.py +++ b/Tic-Tac-Toe Games/tic-tac-toe6.py @@ -62,9 +62,14 @@ def check_win(player_pos: Dict[str, List[int]], cur_player: str) -> bool: False """ soln = [ - [1, 2, 3], [4, 5, 6], [7, 8, 9], # Rows - [1, 4, 7], [2, 5, 8], [3, 6, 9], # Columns - [1, 5, 9], [3, 5, 7] # Diagonals + [1, 2, 3], + [4, 5, 6], + [7, 8, 9], # Rows + [1, 4, 7], + [2, 5, 8], + [3, 6, 9], # Columns + [1, 5, 9], + [3, 5, 7], # Diagonals ] return any(all(pos in player_pos[cur_player] for pos in combo) for combo in soln) diff --git a/Timetable_Operations.py b/Timetable_Operations.py index 18b9218f561..630ef597417 100644 --- a/Timetable_Operations.py +++ b/Timetable_Operations.py @@ -67,5 +67,6 @@ def calculate() -> None: if __name__ == "__main__": import doctest + doctest.testmod() root.mainloop() diff --git a/simple_calculator.py b/simple_calculator.py index 3464389ca3e..8864d9b2f60 100644 --- a/simple_calculator.py +++ b/simple_calculator.py @@ -14,6 +14,7 @@ 4.0 """ + def add(x: float, y: float) -> float: """Return the sum of x and y.""" return x + y @@ -41,17 +42,17 @@ def calculator() -> None: while True: choice: str = input("Enter choice (1/2/3/4): ").strip() - if choice in ('1', '2', '3', '4'): + if choice in ("1", "2", "3", "4"): num1: float = float(input("Enter first number: ")) num2: float = float(input("Enter second number: ")) - if choice == '1': + if choice == "1": print(f"{num1} + {num2} = {add(num1, num2)}") - elif choice == '2': + elif choice == "2": print(f"{num1} - {num2} = {subtract(num1, num2)}") - elif choice == '3': + elif choice == "3": print(f"{num1} * {num2} = {multiply(num1, num2)}") - elif choice == '4': + elif choice == "4": print(f"{num1} / {num2} = {divide(num1, num2)}") break else: @@ -60,5 +61,6 @@ def calculator() -> None: if __name__ == "__main__": import doctest + doctest.testmod() calculator() diff --git a/smart_file_organizer.py b/smart_file_organizer.py index 3150251678e..d9062c9fc93 100644 --- a/smart_file_organizer.py +++ b/smart_file_organizer.py @@ -68,7 +68,9 @@ def organize_files(base_path: str) -> None: Args: base_path: Path of the directory to organize. """ - files = [f for f in os.listdir(base_path) if os.path.isfile(os.path.join(base_path, f))] + files = [ + f for f in os.listdir(base_path) if os.path.isfile(os.path.join(base_path, f)) + ] if not files: print(f"[{datetime.now().strftime('%H:%M:%S')}] No files found in {base_path}") return @@ -82,9 +84,13 @@ def organize_files(base_path: str) -> None: try: shutil.move(source, os.path.join(target_folder, file_name)) - print(f"[{datetime.now().strftime('%H:%M:%S')}] Moved: {file_name} -> {category}/") + print( + f"[{datetime.now().strftime('%H:%M:%S')}] Moved: {file_name} -> {category}/" + ) except Exception as e: - print(f"[{datetime.now().strftime('%H:%M:%S')}] Error moving {file_name}: {e}") + print( + f"[{datetime.now().strftime('%H:%M:%S')}] Error moving {file_name}: {e}" + ) def main() -> None: @@ -92,16 +98,12 @@ def main() -> None: parser = argparse.ArgumentParser( description="Organize files in a directory into categorized subfolders." ) - parser.add_argument( - "--path", - required=True, - help="Directory path to organize." - ) + parser.add_argument("--path", required=True, help="Directory path to organize.") parser.add_argument( "--interval", type=int, default=0, - help="Interval (in minutes) to repeat automatically (0 = run once)." + help="Interval (in minutes) to repeat automatically (0 = run once).", ) args = parser.parse_args() diff --git a/time_delta.py b/time_delta.py index 9b46d34f79f..dc9d479303d 100644 --- a/time_delta.py +++ b/time_delta.py @@ -1,7 +1,7 @@ """ Time Delta Calculator -This module provides functionality to calculate the absolute difference +This module provides functionality to calculate the absolute difference in seconds between two timestamps in the format: Day dd Mon yyyy hh:mm:ss +xxxx """ # ----------------------------------------------------------------------------- @@ -31,7 +31,6 @@ # 88200 # ------------------------------------------------------------------------------ - import datetime from typing import List, Tuple @@ -39,10 +38,10 @@ def parse_timestamp(timestamp: str) -> datetime.datetime: """ Parse a timestamp string into a datetime object. - + Args: timestamp: String in the format "Day dd Mon yyyy hh:mm:ss +xxxx" - + Returns: A datetime object with timezone information """ @@ -54,18 +53,18 @@ def parse_timestamp(timestamp: str) -> datetime.datetime: def calculate_time_delta(t1: str, t2: str) -> int: """ Calculate the absolute time difference between two timestamps in seconds. - + Args: t1: First timestamp string t2: Second timestamp string - + Returns: Absolute time difference in seconds as an integer """ # Parse both timestamps dt1 = parse_timestamp(t1) dt2 = parse_timestamp(t2) - + # Calculate absolute difference and convert to seconds time_difference = abs(dt1 - dt2) return int(time_difference.total_seconds()) @@ -74,7 +73,7 @@ def calculate_time_delta(t1: str, t2: str) -> int: def read_test_cases() -> Tuple[int, List[Tuple[str, str]]]: """ Read test cases from standard input. - + Returns: A tuple containing: - Number of test cases @@ -83,12 +82,12 @@ def read_test_cases() -> Tuple[int, List[Tuple[str, str]]]: try: num_test_cases = int(input().strip()) test_cases = [] - + for _ in range(num_test_cases): timestamp1 = input().strip() timestamp2 = input().strip() test_cases.append((timestamp1, timestamp2)) - + return num_test_cases, test_cases except ValueError as e: raise ValueError("Invalid input format") from e @@ -100,11 +99,11 @@ def main() -> None: """ try: num_test_cases, test_cases = read_test_cases() - + for t1, t2 in test_cases: result = calculate_time_delta(t1, t2) print(result) - + except ValueError as e: print(f"Error: {e}") except Exception as e: diff --git a/to check leap year.py b/to check leap year.py index ece26a6ecca..c54b3719527 100644 --- a/to check leap year.py +++ b/to check leap year.py @@ -15,6 +15,7 @@ False """ + def is_leap_year(year: int) -> bool: """ Return True if year is a leap year, False otherwise. @@ -29,6 +30,7 @@ def is_leap_year(year: int) -> bool: if __name__ == "__main__": import doctest + doctest.testmod() year_input = input("Enter a year: ").strip()