Skip to content

Commit f0f0412

Browse files
Merge branch 'main' into feature/search-history-ui-redesign
2 parents a83a4fe + 7cdc45c commit f0f0412

41 files changed

Lines changed: 2432 additions & 2970 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/ISSUE_TEMPLATE/bug_report.md

Lines changed: 0 additions & 23 deletions
This file was deleted.

.github/ISSUE_TEMPLATE/feature_request.md

Lines changed: 0 additions & 13 deletions
This file was deleted.

.github/ISSUE_TEMPLATE/project_proposal.md

Lines changed: 0 additions & 13 deletions
This file was deleted.

.github/workflows/tests.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@ jobs:
2727
with:
2828
python-version: ${{ matrix.python-version }}
2929

30-
- name: Install dependencies
30+
- name: Install test dependencies
3131
run: |
3232
python -m pip install --upgrade pip
33-
pip install -r requirements.txt
33+
pip install pytest>=8.0.0
3434
3535
- name: Run tests
3636
run: pytest tests/ -v --tb=short

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ deactivate
265265

266266
## 👥 Contributors
267267

268-
We appreciate all contributions to recode hive! Thank you to everyone who has helped make this project better.
268+
We appreciate all contributions to the Python Mini Projects Collection! Thank you to everyone who has helped make this project better.
269269

270270
<a href="https://github.com/steam-bell-92/python-mini-project/graphs/contributors">
271271
<img src="https://contrib.rocks/image?repo=steam-bell-92/python-mini-project" />

games/Blackjack-21/Blackjack21_cli.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ def main():
2525
# Player draw
2626
card = deck.pop()
2727
player_cards.append(card)
28-
rank = card[:-2]
28+
rank = card.rstrip('♠️♥️♦️♣️\ufe0f').strip()
2929
if rank in ['Q','K','J']:
3030
player_hand.append(10)
3131
elif rank == 'A':
@@ -36,7 +36,7 @@ def main():
3636
# Dealer draw
3737
card = deck.pop()
3838
dealer_cards.append(card)
39-
rank = card[:-2]
39+
rank = card.rstrip('♠️♥️♦️♣️\ufe0f').strip()
4040
if rank in ['Q','K','J']:
4141
dealer_hand.append(10)
4242
elif rank == 'A':
@@ -62,7 +62,7 @@ def main():
6262
if choice in ['h', 'hit']:
6363
card = deck.pop()
6464
player_cards.append(card)
65-
rank = card[:-2]
65+
rank = card.rstrip('♠️♥️♦️♣️\ufe0f').strip()
6666
if rank in ['Q','K','J']:
6767
player_hand.append(10)
6868
elif rank == 'A':
@@ -101,7 +101,7 @@ def main():
101101
print("🃏 Dealer hits...")
102102
card = deck.pop()
103103
dealer_cards.append(card)
104-
rank = card[:-2]
104+
rank = card.rstrip('♠️♥️♦️♣️\ufe0f').strip()
105105
if rank in ['Q','K','J']:
106106
dealer_hand.append(10)
107107
elif rank == 'A':

games/Math-Quiz/Math-Quiz.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,9 @@ def generate_question(difficulty):
8686
a, b = random.randint(2, 15), random.randint(2, 15)
8787
return f"What is {a} x {b}?", a * b
8888
elif q_type == 'div':
89-
b = random.randint(2, 10)
90-
a = b * random.randint(2, 10)
91-
return f"What is {a} / {b}?", a // b
89+
b=random.randint(2,10)
90+
a=random.randint(2,100)
91+
return f"What is {a} / {b}?",round(a/b,2)
9292
elif q_type == 'negative':
9393
a = random.randint(-25, -1)
9494
b = random.randint(1, 30)
@@ -138,7 +138,10 @@ def generate_options(correct):
138138

139139
options = {correct}
140140
while len(options) < 4:
141-
fake = correct + random.randint(-15, 15)
141+
if isinstance(correct,float):
142+
fake=round(correct+random.uniform(-3,3),2)
143+
else:
144+
fake=correct+random.randint(-15,15)
142145
if fake != correct:
143146
options.add(fake)
144147
options = list(options)

games/Minesweeper/minesweeper.py

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
import random
2+
import sys
3+
4+
# Emojis for cell states
5+
UNOPENED = "⬜"
6+
FLAGGED = "🚩"
7+
MINE_EXPLODED = "💥"
8+
MINE = "💣"
9+
10+
NUMBERS = {
11+
0: "⬛",
12+
1: "1️⃣",
13+
2: "2️⃣",
14+
3: "3️⃣",
15+
4: "4️⃣",
16+
5: "5️⃣",
17+
6: "6️⃣",
18+
7: "7️⃣",
19+
8: "8️⃣"
20+
}
21+
22+
class Minesweeper:
23+
def __init__(self, size, num_mines):
24+
self.size = size
25+
self.num_mines = num_mines
26+
self.grid = [[0 for _ in range(size)] for _ in range(size)]
27+
self.visible = [[False for _ in range(size)] for _ in range(size)]
28+
self.flags = [[False for _ in range(size)] for _ in range(size)]
29+
self.first_click = True
30+
self.game_over = False
31+
self.victory = False
32+
33+
def place_mines(self, safe_r, safe_c):
34+
mines_placed = 0
35+
while mines_placed < self.num_mines:
36+
r = random.randint(0, self.size - 1)
37+
c = random.randint(0, self.size - 1)
38+
# Ensure not the first clicked cell and not already a mine
39+
if (r, c) != (safe_r, safe_c) and self.grid[r][c] != -1:
40+
self.grid[r][c] = -1
41+
mines_placed += 1
42+
43+
# Calculate numbers
44+
for r in range(self.size):
45+
for c in range(self.size):
46+
if self.grid[r][c] == -1:
47+
continue
48+
count = 0
49+
for dr in [-1, 0, 1]:
50+
for dc in [-1, 0, 1]:
51+
if dr == 0 and dc == 0:
52+
continue
53+
nr, nc = r + dr, c + dc
54+
if 0 <= nr < self.size and 0 <= nc < self.size and self.grid[nr][nc] == -1:
55+
count += 1
56+
self.grid[r][c] = count
57+
58+
def print_board(self, show_all=False):
59+
# Print column headers
60+
header = " "
61+
for c in range(self.size):
62+
if c < 10:
63+
header += f"{c} "
64+
else:
65+
header += f"{c} "
66+
print(header)
67+
68+
for r in range(self.size):
69+
row_str = f"{r:2} "
70+
for c in range(self.size):
71+
if show_all:
72+
if self.grid[r][c] == -1:
73+
if self.visible[r][c] and self.game_over:
74+
row_str += MINE_EXPLODED + " "
75+
else:
76+
row_str += MINE + " "
77+
else:
78+
row_str += NUMBERS[self.grid[r][c]] + " "
79+
else:
80+
if self.visible[r][c]:
81+
if self.grid[r][c] == -1:
82+
row_str += MINE_EXPLODED + " "
83+
else:
84+
row_str += NUMBERS[self.grid[r][c]] + " "
85+
elif self.flags[r][c]:
86+
row_str += FLAGGED + " "
87+
else:
88+
row_str += UNOPENED + " "
89+
print(row_str)
90+
91+
def dig(self, r, c):
92+
if self.flags[r][c] or self.visible[r][c]:
93+
return
94+
95+
if self.first_click:
96+
self.place_mines(r, c)
97+
self.first_click = False
98+
99+
self.visible[r][c] = True
100+
101+
if self.grid[r][c] == -1:
102+
self.game_over = True
103+
return
104+
105+
if self.grid[r][c] == 0:
106+
for dr in [-1, 0, 1]:
107+
for dc in [-1, 0, 1]:
108+
if dr == 0 and dc == 0:
109+
continue
110+
nr, nc = r + dr, c + dc
111+
if 0 <= nr < self.size and 0 <= nc < self.size:
112+
if not self.visible[nr][nc]:
113+
self.dig(nr, nc)
114+
115+
def flag(self, r, c):
116+
if not self.visible[r][c]:
117+
self.flags[r][c] = not self.flags[r][c]
118+
119+
def check_victory(self):
120+
for r in range(self.size):
121+
for c in range(self.size):
122+
if self.grid[r][c] != -1 and not self.visible[r][c]:
123+
return False
124+
return True
125+
126+
127+
def play_game():
128+
print("Welcome to CLI Minesweeper! 💣💥")
129+
print("Select Difficulty:")
130+
print("1. Easy (9x9, 10 mines)")
131+
print("2. Medium (16x16, 40 mines)")
132+
133+
while True:
134+
choice = input("Enter 1 or 2: ").strip()
135+
if choice == "1":
136+
size = 9
137+
mines = 10
138+
break
139+
elif choice == "2":
140+
size = 16
141+
mines = 40
142+
break
143+
else:
144+
print("Invalid choice.")
145+
146+
game = Minesweeper(size, mines)
147+
148+
while not game.game_over and not game.victory:
149+
print("\n" + "="*40 + "\n")
150+
game.print_board()
151+
print("\nCommands: d <row> <col> to Dig, f <row> <col> to Flag (e.g. 'd 0 5' or 'f 2 3')")
152+
print("Type 'quit' to exit.")
153+
cmd = input("Command: ").strip().lower().split()
154+
155+
if not cmd:
156+
continue
157+
if cmd[0] == 'quit':
158+
print("Exiting game. Bye!")
159+
sys.exit(0)
160+
161+
if len(cmd) != 3 or cmd[0] not in ('d', 'f'):
162+
print("Invalid command format.")
163+
continue
164+
165+
try:
166+
r = int(cmd[1])
167+
c = int(cmd[2])
168+
except ValueError:
169+
print("Row and column must be integers.")
170+
continue
171+
172+
if r < 0 or r >= size or c < 0 or c >= size:
173+
print("Coordinates out of bounds.")
174+
continue
175+
176+
if cmd[0] == 'd':
177+
game.dig(r, c)
178+
elif cmd[0] == 'f':
179+
game.flag(r, c)
180+
181+
if not game.game_over:
182+
game.victory = game.check_victory()
183+
184+
print("\n" + "="*40 + "\n")
185+
if game.game_over:
186+
print("💥 BOOM! You hit a mine. GAME OVER. 💥")
187+
game.print_board(show_all=True)
188+
elif game.victory:
189+
print("🎉 CONGRATULATIONS! You cleared the minefield! 🎉")
190+
game.print_board(show_all=True)
191+
192+
if __name__ == "__main__":
193+
try:
194+
play_game()
195+
except KeyboardInterrupt:
196+
print("\nGame interrupted. Exiting.")

games/Rock-Paper-Scissor/Rock-Paper-Scissor.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
import random
22
import os
3+
from pathlib import Path
34

4-
RESULTS_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "game_results.txt")
5+
RESULTS_FILE = Path(__file__).parent / "game_results.txt"
56

67

78
def parse_results():
89
"""Read game_results.txt and return a list of result dicts."""
910
records = []
10-
if not os.path.exists(RESULTS_FILE):
11+
if not RESULTS_FILE.exists():
1112
return records
1213
try:
13-
with open(RESULTS_FILE, "r") as f:
14+
with open(RESULTS_FILE, "r", encoding="utf-8") as f:
1415
for line in f:
1516
line = line.strip()
1617
if not line:
@@ -209,7 +210,7 @@ def main():
209210
f"(User-Computer), Rounds: {rounds_played}\n"
210211
)
211212
try:
212-
with open(RESULTS_FILE, "a") as f:
213+
with open(RESULTS_FILE, "a", encoding="utf-8") as f:
213214
f.write(result_string)
214215
print("Game results saved successfully.")
215216
except IOError:

0 commit comments

Comments
 (0)