quastt-show 0.1.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ from .game import Game
quastt_show/game.py ADDED
@@ -0,0 +1,388 @@
1
+ import tkinter as tk
2
+ from tkinter import messagebox
3
+ import requests
4
+ import html
5
+ import random
6
+ import json
7
+ import os
8
+ from tkinter import PhotoImage
9
+ # ------------------------------
10
+ # Constants and styles
11
+ # ------------------------------
12
+
13
+ BG_COLOR = "#f0f4f8"
14
+ PANEL_BG = "#ffffff"
15
+ ACCENT_COLOR = "#4a90e2"
16
+ TEXT_COLOR = "#333333"
17
+ CORRECT_COLOR = "#28a745"
18
+ WRONG_COLOR = "#dc3545"
19
+ FONT_LARGE = ("Segoe UI", 24, "bold")
20
+ FONT_MEDIUM = ("Segoe UI", 16)
21
+ FONT_SMALL = ("Segoe UI", 12)
22
+
23
+ STATS_FILE = os.path.join(os.path.expanduser("~"), ".trivia_stats.json")
24
+
25
+
26
+ # ------------------------------
27
+ # Rounded Button using Canvas
28
+ # ------------------------------
29
+
30
+ class RoundedButton(tk.Canvas):
31
+ def __init__(self, parent, text, command=None, width=200, height=50, radius=20, bg=ACCENT_COLOR, fg="white", font=FONT_MEDIUM):
32
+ super().__init__(parent, width=width, height=height, highlightthickness=0, bg=parent["bg"])
33
+ self.command = command
34
+ self.radius = radius
35
+ self.bg = bg
36
+ self.fg = fg
37
+ self.font = font
38
+ self.text = text
39
+ self.width = width
40
+ self.height = height
41
+
42
+ self.create_rounded_rect(0, 0, width, height, radius, fill=bg, outline="")
43
+ self.text_id = self.create_text(width // 2, height // 2, text=text, fill=fg, font=font)
44
+
45
+ self.bind("<Button-1>", self._on_click)
46
+ self.bind("<Enter>", self._on_enter)
47
+ self.bind("<Leave>", self._on_leave)
48
+
49
+ def create_rounded_rect(self, x1, y1, x2, y2, r, **kwargs):
50
+ points = [
51
+ x1+r, y1,
52
+ x2-r, y1,
53
+ x2, y1,
54
+ x2, y1+r,
55
+ x2, y2-r,
56
+ x2, y2,
57
+ x2-r, y2,
58
+ x1+r, y2,
59
+ x1, y2,
60
+ x1, y2-r,
61
+ x1, y1+r,
62
+ x1, y1,
63
+ ]
64
+ return self.create_polygon(points, smooth=True, **kwargs)
65
+
66
+ def _on_click(self, event):
67
+ if self.command:
68
+ self.command()
69
+
70
+ def _on_enter(self, event):
71
+ self.itemconfig(1, fill="#357ABD") # Slightly darker on hover
72
+
73
+ def _on_leave(self, event):
74
+ self.itemconfig(1, fill=self.bg)
75
+
76
+
77
+ # ------------------------------
78
+ # Main Game class
79
+ # ------------------------------
80
+
81
+ class Game:
82
+ def __init__(self, difficulty="medium"):
83
+ self.difficulty = difficulty
84
+ self.app = TriviaApp(difficulty)
85
+
86
+ def run(self):
87
+ self.app.mainloop()
88
+
89
+
90
+ # ------------------------------
91
+ # TriviaApp Main Tk window
92
+ # ------------------------------
93
+
94
+ class TriviaApp(tk.Tk):
95
+ def __init__(self, difficulty):
96
+ super().__init__()
97
+ self.title("QuasttShow")
98
+ self.geometry("1200x800")
99
+ self.configure(bg=BG_COLOR)
100
+ self.resizable(False, False)
101
+
102
+ self.difficulty = difficulty
103
+ self.questions = []
104
+ self.current_q_index = 0
105
+ self.score = 0
106
+ self.streak = 0
107
+ self.max_streak = 0
108
+ self.paused = False
109
+ self.ai_stats = {"correct_by_category": {}, "questions_answered": 0, "correct_answers": 0}
110
+
111
+ self.stats = self.load_stats()
112
+
113
+ self.container = tk.Frame(self, bg=BG_COLOR)
114
+ self.container.pack(expand=True, fill="both")
115
+
116
+ self.frames = {}
117
+ for F in (MenuFrame, GameFrame, StatsFrame, PauseOverlay):
118
+ frame = F(self.container, self)
119
+ self.frames[F.__name__] = frame
120
+ frame.place(relwidth=1, relheight=1)
121
+
122
+ self.show_frame("MenuFrame")
123
+
124
+ def show_frame(self, name):
125
+ frame = self.frames[name]
126
+ frame.tkraise()
127
+
128
+ def load_stats(self):
129
+ if os.path.exists(STATS_FILE):
130
+ try:
131
+ with open(STATS_FILE, "r") as f:
132
+ return json.load(f)
133
+ except Exception:
134
+ return {"games_played": 0, "total_score": 0, "max_score": 0, "max_streak": 0}
135
+ else:
136
+ return {"games_played": 0, "total_score": 0, "max_score": 0, "max_streak": 0}
137
+
138
+ def save_stats(self):
139
+ try:
140
+ with open(STATS_FILE, "w") as f:
141
+ json.dump(self.stats, f)
142
+ except Exception:
143
+ pass
144
+
145
+ def fetch_questions(self, amount=15, difficulty=None, qtype='multiple'):
146
+ difficulty = difficulty or self.difficulty
147
+ url = f"https://opentdb.com/api.php?amount={amount}&difficulty={difficulty}&type={qtype}"
148
+ try:
149
+ response = requests.get(url)
150
+ data = response.json()
151
+ if data["response_code"] != 0:
152
+ return []
153
+ return data["results"]
154
+ except Exception:
155
+ return []
156
+
157
+ def start_new_game(self):
158
+ self.questions = self.fetch_questions()
159
+ if not self.questions:
160
+ messagebox.showerror("Error", "Failed to fetch questions. Check your internet connection.")
161
+ self.show_frame("MenuFrame")
162
+ return
163
+ self.current_q_index = 0
164
+ self.score = 0
165
+ self.streak = 0
166
+ self.max_streak = 0
167
+ self.ai_stats = {"correct_by_category": {}, "questions_answered": 0, "correct_answers": 0}
168
+ self.paused = False
169
+ self.frames["GameFrame"].load_question()
170
+ self.show_frame("GameFrame")
171
+
172
+ def update_game_stats(self, correct, category):
173
+ self.ai_stats['questions_answered'] += 1
174
+ if correct:
175
+ self.ai_stats['correct_answers'] += 1
176
+ self.ai_stats['correct_by_category'][category] = \
177
+ self.ai_stats['correct_by_category'].get(category, 0) + 1
178
+
179
+ def end_game(self):
180
+ self.stats["games_played"] += 1
181
+ self.stats["total_score"] += self.score
182
+ self.stats["max_score"] = max(self.stats.get("max_score", 0), self.score)
183
+ self.stats["max_streak"] = max(self.stats.get("max_streak", 0), self.max_streak)
184
+ self.save_stats()
185
+ msg = f"Game Over!\nFinal Score: {self.score}\nMax Streak: {self.max_streak}"
186
+ messagebox.showinfo("Game Over", msg)
187
+ self.show_frame("MenuFrame")
188
+
189
+
190
+ # ------------------------------
191
+ # Frames: Menu, Game, Pause, Stats
192
+ # ------------------------------
193
+
194
+ class MenuFrame(tk.Frame):
195
+ def __init__(self, parent, controller):
196
+ super().__init__(parent, bg=BG_COLOR)
197
+ self.controller = controller
198
+
199
+ title = tk.Label(self, text="🎲Enter QuasttShow", font=FONT_LARGE, fg=ACCENT_COLOR, bg=BG_COLOR)
200
+ title.pack(pady=(80, 40))
201
+ btn_start = RoundedButton(self, "🏐Start Game", command=self.start_game, width=300, height=60)
202
+ btn_start.pack(pady=20)
203
+
204
+ btn_stats = RoundedButton(self, "✨Statistics", command=lambda: controller.show_frame("StatsFrame"), width=300, height=60)
205
+ btn_stats.pack(pady=20)
206
+
207
+ btn_exit = RoundedButton(self, "❌Exit", command=controller.destroy, width=300, height=60)
208
+ btn_exit.pack(pady=20)
209
+
210
+ def start_game(self):
211
+ self.controller.start_new_game()
212
+
213
+
214
+ class GameFrame(tk.Frame):
215
+ def __init__(self, parent, controller):
216
+ super().__init__(parent, bg=BG_COLOR)
217
+ self.controller = controller
218
+
219
+ # Question text
220
+ self.question_text = tk.Label(self, text="", font=FONT_MEDIUM, fg=TEXT_COLOR, bg=BG_COLOR, wraplength=1000, justify="center")
221
+ self.question_text.pack(pady=(60, 30))
222
+
223
+ # Answers frame (2 columns)
224
+ self.answers_frame = tk.Frame(self, bg=BG_COLOR)
225
+ self.answers_frame.pack(pady=20)
226
+
227
+ # Status bar (score, streak)
228
+ self.status_label = tk.Label(self, text="", font=FONT_SMALL, fg=TEXT_COLOR, bg=BG_COLOR)
229
+ self.status_label.pack(pady=10)
230
+
231
+ # AI prediction
232
+ self.ai_label = tk.Label(self, text="", font=FONT_SMALL, fg=ACCENT_COLOR, bg=BG_COLOR, justify="center")
233
+ self.ai_label.pack(pady=10)
234
+
235
+ # Pause button
236
+ self.pause_btn = RoundedButton(self, "Pause", command=self.show_pause, width=150, height=40)
237
+ self.pause_btn.pack(pady=15)
238
+
239
+ self.answer_buttons = []
240
+
241
+ def load_question(self):
242
+ self.clear_answer_buttons()
243
+ if self.controller.current_q_index >= len(self.controller.questions):
244
+ self.controller.end_game()
245
+ return
246
+
247
+ qdata = self.controller.questions[self.controller.current_q_index]
248
+ question = html.unescape(qdata["question"])
249
+ correct_answer = html.unescape(qdata["correct_answer"])
250
+ incorrect_answers = [html.unescape(ans) for ans in qdata["incorrect_answers"]]
251
+ options = incorrect_answers + [correct_answer]
252
+ random.shuffle(options)
253
+
254
+ self.correct_answer = correct_answer
255
+ self.current_category = qdata.get("category", "Unknown")
256
+
257
+ self.question_text.config(text=question)
258
+
259
+ # Create buttons in 2 columns
260
+ self.answer_buttons = []
261
+ for i, option in enumerate(options):
262
+ btn = RoundedButton(self.answers_frame, option, command=lambda opt=option: self.check_answer(opt), width=540, height=60)
263
+ self.answer_buttons.append(btn)
264
+ btn.grid(row=i//2, column=i%2, padx=20, pady=15)
265
+
266
+ self.update_status()
267
+ self.update_ai_prediction()
268
+
269
+ def clear_answer_buttons(self):
270
+ for btn in self.answer_buttons:
271
+ btn.destroy()
272
+ self.answer_buttons = []
273
+
274
+ def check_answer(self, choice):
275
+ if self.controller.paused:
276
+ return
277
+
278
+ correct = choice == self.correct_answer
279
+ if correct:
280
+ self.controller.score += 10
281
+ self.controller.streak += 1
282
+ if self.controller.streak > self.controller.max_streak:
283
+ self.controller.max_streak = self.controller.streak
284
+ else:
285
+ self.controller.streak = 0
286
+
287
+ # Update AI stats
288
+ self.controller.update_game_stats(correct, self.current_category)
289
+
290
+ # Flash question text color and disable buttons temporarily
291
+ self.flash_feedback(correct)
292
+
293
+ def flash_feedback(self, correct):
294
+ color = CORRECT_COLOR if correct else WRONG_COLOR
295
+ self.question_text.config(fg=color)
296
+ for btn in self.answer_buttons:
297
+ btn.unbind("<Button-1>")
298
+ self.after(1200, self.next_question)
299
+
300
+ def next_question(self):
301
+ self.question_text.config(fg=TEXT_COLOR)
302
+ self.controller.current_q_index += 1
303
+ self.load_question()
304
+
305
+ def update_status(self):
306
+ status = f"Score: {self.controller.score} Streak: {self.controller.streak} Max Streak: {self.controller.max_streak}"
307
+ self.status_label.config(text=status)
308
+
309
+ def update_ai_prediction(self):
310
+ stats = self.controller.ai_stats
311
+ answered = stats['questions_answered']
312
+ if answered == 0:
313
+ text = "AI Prediction: No data yet."
314
+ else:
315
+ acc = stats['correct_answers'] / answered
316
+ best_cat = max(stats['correct_by_category'], key=stats['correct_by_category'].get) if stats['correct_by_category'] else "N/A"
317
+ text = f"AI Prediction:\nAccuracy: {acc*100:.1f}%\nBest Category: {best_cat}"
318
+ self.ai_label.config(text=text)
319
+
320
+ def show_pause(self):
321
+ self.controller.paused = True
322
+ self.controller.show_frame("PauseOverlay")
323
+
324
+
325
+ class PauseOverlay(tk.Frame):
326
+ def __init__(self, parent, controller):
327
+ super().__init__(parent, bg="#000000") # semi-transparent black overlay
328
+ self.controller = controller
329
+
330
+ panel = tk.Frame(self, bg=PANEL_BG, width=400, height=300)
331
+ panel.place(relx=0.5, rely=0.5, anchor="center")
332
+
333
+ title = tk.Label(panel, text="Game Paused", font=FONT_LARGE, fg=TEXT_COLOR, bg=PANEL_BG)
334
+ title.pack(pady=(40, 30))
335
+
336
+ btn_resume = RoundedButton(panel, "Resume", command=self.resume_game)
337
+ btn_resume.pack(pady=15)
338
+
339
+ btn_menu = RoundedButton(panel, "Return to Menu", command=self.return_to_menu)
340
+ btn_menu.pack(pady=15)
341
+
342
+ def resume_game(self):
343
+ self.controller.paused = False
344
+ self.controller.show_frame("GameFrame")
345
+
346
+ def return_to_menu(self):
347
+ self.controller.paused = False
348
+ self.controller.show_frame("MenuFrame")
349
+
350
+
351
+ class StatsFrame(tk.Frame):
352
+ def __init__(self, parent, controller):
353
+ super().__init__(parent, bg=BG_COLOR)
354
+ self.controller = controller
355
+
356
+ title = tk.Label(self, text="Statistics", font=FONT_LARGE, fg=ACCENT_COLOR, bg=BG_COLOR)
357
+ title.pack(pady=(60, 40))
358
+
359
+ self.stats_text = tk.Label(self, text="", font=FONT_MEDIUM, fg=TEXT_COLOR, bg=BG_COLOR, justify="left")
360
+ self.stats_text.pack(pady=20)
361
+
362
+ btn_back = RoundedButton(self, "Back to Menu", command=lambda: controller.show_frame("MenuFrame"), width=300, height=60)
363
+ btn_back.pack(pady=40)
364
+
365
+ self.update_stats()
366
+
367
+ def update_stats(self):
368
+ s = self.controller.stats
369
+ games = s.get("games_played", 0)
370
+ avg_score = s.get("total_score", 0) / games if games > 0 else 0
371
+ max_score = s.get("max_score", 0)
372
+ max_streak = s.get("max_streak", 0)
373
+ stats_text = (
374
+ f"Games Played: {games}\n"
375
+ f"Average Score: {avg_score:.2f}\n"
376
+ f"Max Score: {max_score}\n"
377
+ f"Max Streak: {max_streak}\n"
378
+ )
379
+ self.stats_text.config(text=stats_text)
380
+
381
+
382
+ # ------------------------------
383
+ # Run if main
384
+ # ------------------------------
385
+
386
+ if __name__ == "__main__":
387
+ game = Game(difficulty="medium")
388
+ game.run()
@@ -0,0 +1,11 @@
1
+ Metadata-Version: 2.4
2
+ Name: quastt_show
3
+ Version: 0.1.2
4
+ Summary: A trivia game with AI stats.
5
+ Author: RVA
6
+ Requires-Python: >=3.7
7
+ Requires-Dist: requests
8
+ Dynamic: author
9
+ Dynamic: requires-dist
10
+ Dynamic: requires-python
11
+ Dynamic: summary
@@ -0,0 +1,7 @@
1
+ quastt_show/__init__.py,sha256=eMgN52YNlIhpHGDEdg7q2HV8d-bZBvoYQ4VY9Tk1BVY,24
2
+ quastt_show/game.py,sha256=jKPgruUttd0YTTqRNPeNmSNTqNadYZDL8vjCcmYTalU,13689
3
+ quastt_show-0.1.2.dist-info/METADATA,sha256=b_y5BTgojWawI6Kj4adP_LxVKJYXyUURPo6oCL-1LAU,244
4
+ quastt_show-0.1.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
5
+ quastt_show-0.1.2.dist-info/entry_points.txt,sha256=qLcSh8ANs-GHsS2TCzOciVdEyy-25IyVCujHZmG2S-I,54
6
+ quastt_show-0.1.2.dist-info/top_level.txt,sha256=BtMM1888oCrruk37yJGnahqbhyirtsL6d9ES0pK081g,12
7
+ quastt_show-0.1.2.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ quastt-show = quastt_show.game:main
@@ -0,0 +1 @@
1
+ quastt_show