🎮 Quiz Game - Game đố vui thông minh
thông tin
Tạo một game quiz giống như "Ai Là Triệu Phú" mini - kiểm tra kiến thức và tranh tài cùng bạn bè!
🎯 Mục tiêu dự án
Xây dựng game quiz với các tính năng:
- 🧠 Nhiều chủ đề câu hỏi (Toán, Lịch sử, Khoa học, Python...)
- ⏱️ Hệ thống thời gian đếm ngược
- 🏆 Bảng xếp hạng điểm cao
- 💡 Gợi ý và trợ giúp
- 📊 Thống kê kết quả chi tiết
- 💾 Lưu tiến trình người chơi
- 🎨 Giao diện console đẹp mắt
🛠️ Chuẩn bị
Thư viện cần thiết
import json
import time
import random
import threading
from datetime import datetime, timedelta
from colorama import Fore, Back, Style, init
import os
# Khởi tạo colorama
init(autoreset=True)
🏗️ Xây dựng game
1. Class Question - Câu hỏi
class Question:
"""
Class đại diện cho một câu hỏi
Giống như một thẻ flashcard thông minh!
"""
def __init__(self, question_text, options, correct_answer, difficulty="medium",
category="general", explanation="", points=10):
self.question_text = question_text
self.options = options # List of 4 options
self.correct_answer = correct_answer # Index 0-3
self.difficulty = difficulty # easy, medium, hard
self.category = category
self.explanation = explanation
self.points = points
def is_correct(self, answer_index):
"""Kiểm tra đáp án có đúng không"""
return answer_index == self.correct_answer
def get_correct_text(self):
"""Lấy text đáp án đúng"""
return self.options[self.correct_answer]
def to_dict(self):
"""Chuyển thành dictionary để lưu file"""
return {
'question': self.question_text,
'options': self.options,
'correct': self.correct_answer,
'difficulty': self.difficulty,
'category': self.category,
'explanation': self.explanation,
'points': self.points
}
@classmethod
def from_dict(cls, data):
"""Tạo Question từ dictionary"""
return cls(
data['question'],
data['options'],
data['correct'],
data.get('difficulty', 'medium'),
data.get('category', 'general'),
data.get('explanation', ''),
data.get('points', 10)
)
2. Class QuestionBank - Ngân hàng câu hỏi
class QuestionBank:
"""
Quản lý tất cả câu hỏi
Giống như một thư viện câu hỏi khổng lồ!
"""
def __init__(self, data_file="questions.json"):
self.data_file = data_file
self.questions = []
self.load_questions()
def load_questions(self):
"""Tải câu hỏi từ file hoặc tạo mặc định"""
try:
if os.path.exists(self.data_file):
with open(self.data_file, 'r', encoding='utf-8') as f:
data = json.load(f)
self.questions = [Question.from_dict(q) for q in data]
else:
self.create_default_questions()
self.save_questions()
except Exception as e:
print(f"⚠️ Lỗi tải câu hỏi: {e}")
self.create_default_questions()
def create_default_questions(self):
"""Tạo bộ câu hỏi mặc định"""
default_questions = [
# Toán học
{
'question': "Kết quả của 7 × 8 là gì?",
'options': ["54", "56", "58", "64"],
'correct': 1,
'difficulty': "easy",
'category': "math",
'explanation': "7 × 8 = 56",
'points': 5
},
{
'question': "Căn bậc hai của 144 là?",
'options': ["11", "12", "13", "14"],
'correct': 1,
'difficulty': "medium",
'category': "math",
'explanation': "√144 = 12 vì 12² = 144",
'points': 10
},
# Lịch sử Việt Nam
{
'question': "Ai là vua sáng lập nhà Lý?",
'options': ["Lý Thái Tổ", "Lý Thái Tông", "Lý Thánh Tông", "Lý Nhân Tông"],
'correct': 0,
'difficulty': "medium",
'category': "history",
'explanation': "Lý Thái Tổ (Lý Công Uẩn) sáng lập nhà Lý năm 1009",
'points': 10
},
{
'question': "Chiến thắng Điện Biên Phủ diễn ra năm nào?",
'options': ["1953", "1954", "1955", "1956"],
'correct': 1,
'difficulty': "easy",
'category': "history",
'explanation': "Chiến thắng Điện Biên Phủ diễn ra ngày 7/5/1954",
'points': 5
},
# Khoa học
{
'question': "Công thức hóa học của nước là gì?",
'options': ["H2O", "CO2", "NaCl", "O2"],
'correct': 0,
'difficulty': "easy",
'category': "science",
'explanation': "H2O - 2 nguyên tử Hydrogen và 1 nguyên tử Oxygen",
'points': 5
},
{
'question': "Hành tinh nào gần Mặt Trời nhất?",
'options': ["Sao Kim", "Sao Thủy", "Trái Đất", "Sao Hỏa"],
'correct': 1,
'difficulty': "medium",
'category': "science",
'explanation': "Sao Thủy (Mercury) là hành tinh gần Mặt Trời nhất",
'points': 10
},
# Python Programming
{
'question': "Từ khóa nào dùng để tạo hàm trong Python?",
'options': ["function", "def", "func", "create"],
'correct': 1,
'difficulty': "easy",
'category': "python",
'explanation': "Dùng từ khóa 'def' để định nghĩa hàm trong Python",
'points': 5
},
{
'question': "Kết quả của len('Hello') trong Python?",
'options': ["4", "5", "6", "Error"],
'correct': 1,
'difficulty': "easy",
'category': "python",
'explanation': "len('Hello') = 5 vì chuỗi 'Hello' có 5 ký tự",
'points': 5
},
# Địa lý
{
'question': "Thủ đô của Nhật Bản là?",
'options': ["Kyoto", "Tokyo", "Osaka", "Hiroshima"],
'correct': 1,
'difficulty': "easy",
'category': "geography",
'explanation': "Tokyo là thủ đô của Nhật Bản",
'points': 5
},
{
'question': "Sông dài nhất thế giới là?",
'options': ["Amazon", "Nile", "Yangtze", "Mississippi"],
'correct': 1,
'difficulty': "medium",
'category': "geography",
'explanation': "Sông Nile dài 6.650km, là sông dài nhất thế giới",
'points': 10
}
]
self.questions = [Question.from_dict(q) for q in default_questions]
def save_questions(self):
"""Lưu câu hỏi ra file"""
try:
data = [q.to_dict() for q in self.questions]
with open(self.data_file, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
except Exception as e:
print(f"⚠️ Lỗi lưu câu hỏi: {e}")
def get_questions_by_category(self, category):
"""Lấy câu hỏi theo chủ đề"""
return [q for q in self.questions if q.category == category]
def get_questions_by_difficulty(self, difficulty):
"""Lấy câu hỏi theo độ khó"""
return [q for q in self.questions if q.difficulty == difficulty]
def get_random_questions(self, count=10, category=None, difficulty=None):
"""Lấy câu hỏi ngẫu nhiên"""
filtered_questions = self.questions.copy()
if category:
filtered_questions = [q for q in filtered_questions if q.category == category]
if difficulty:
filtered_questions = [q for q in filtered_questions if q.difficulty == difficulty]
if len(filtered_questions) < count:
count = len(filtered_questions)
return random.sample(filtered_questions, count)
def get_categories(self):
"""Lấy danh sách chủ đề"""
categories = set(q.category for q in self.questions)
return sorted(list(categories))
def add_question(self, question):
"""Thêm câu hỏi mới"""
self.questions.append(question)
self.save_questions()
3. Class Player - Người chơi
class Player:
"""
Thông tin người chơi
Giống như một thẻ người chơi trong game!
"""
def __init__(self, name):
self.name = name
self.total_score = 0
self.games_played = 0
self.questions_answered = 0
self.correct_answers = 0
self.best_score = 0
self.favorite_category = None
self.created_date = datetime.now().isoformat()
self.last_played = None
def update_stats(self, game_score, questions_count, correct_count, category):
"""Cập nhật thống kê người chơi"""
self.total_score += game_score
self.games_played += 1
self.questions_answered += questions_count
self.correct_answers += correct_count
self.last_played = datetime.now().isoformat()
if game_score > self.best_score:
self.best_score = game_score
# Cập nhật chủ đề yêu thích (đơn giản)
self.favorite_category = category
def get_accuracy(self):
"""Tính độ chính xác"""
if self.questions_answered == 0:
return 0
return round((self.correct_answers / self.questions_answered) * 100, 1)
def get_average_score(self):
"""Tính điểm trung bình mỗi game"""
if self.games_played == 0:
return 0
return round(self.total_score / self.games_played, 1)
def to_dict(self):
"""Chuyển thành dict để lưu file"""
return {
'name': self.name,
'total_score': self.total_score,
'games_played': self.games_played,
'questions_answered': self.questions_answered,
'correct_answers': self.correct_answers,
'best_score': self.best_score,
'favorite_category': self.favorite_category,
'created_date': self.created_date,
'last_played': self.last_played
}
@classmethod
def from_dict(cls, data):
"""Tạo Player từ dict"""
player = cls(data['name'])
player.total_score = data.get('total_score', 0)
player.games_played = data.get('games_played', 0)
player.questions_answered = data.get('questions_answered', 0)
player.correct_answers = data.get('correct_answers', 0)
player.best_score = data.get('best_score', 0)
player.favorite_category = data.get('favorite_category')
player.created_date = data.get('created_date', datetime.now().isoformat())
player.last_played = data.get('last_played')
return player
4. Class GameSession - Phiên chơi
class GameSession:
"""
Quản lý một phiên chơi game
Giống như một trọng tài điều khiển game!
"""
def __init__(self, player, questions, time_limit=30):
self.player = player
self.questions = questions
self.time_limit = time_limit
self.current_question = 0
self.score = 0
self.correct_count = 0
self.start_time = None
self.hints_used = 0
self.max_hints = 3
self.answers_log = [] # Lưu log đáp án
def start_game(self):
"""Bắt đầu game"""
self.start_time = datetime.now()
print(f"\n{Fore.GREEN}{Style.BRIGHT}🎮 BẮT ĐẦU GAME!")
print("=" * 50)
print(f"👤 Người chơi: {self.player.name}")
print(f"❓ Số câu hỏi: {len(self.questions)}")
print(f"⏰ Thời gian mỗi câu: {self.time_limit} giây")
print(f"💡 Gợi ý có thể dùng: {self.max_hints}")
print("=" * 50)
input(f"\n{Fore.YELLOW}📱 Nhấn Enter để bắt đầu...")
def play_question(self, question_index):
"""Chơi một câu hỏi"""
if question_index >= len(self.questions):
return False
question = self.questions[question_index]
self.current_question = question_index + 1
print(f"\n{Fore.CYAN}{Style.BRIGHT}")
print("=" * 60)
print(f"❓ CÂU {self.current_question}/{len(self.questions)} - {question.category.upper()}")
print("=" * 60)
# Hiển thị câu hỏi
print(f"{Fore.WHITE}{Style.BRIGHT}{question.question_text}")
print()
# Hiển thị lựa chọn
for i, option in enumerate(question.options):
print(f"{Fore.YELLOW} {chr(65+i)}. {option}")
print(f"\n{Fore.MAGENTA}💰 Điểm: {question.points} | 💡 Gợi ý còn: {self.max_hints - self.hints_used}")
# Đếm ngược thời gian
answer = self.get_timed_answer()
# Kiểm tra đáp án
is_correct = False
if answer is not None:
if answer == "hint":
return self.show_hint(question_index)
elif 0 <= answer <= 3:
is_correct = question.is_correct(answer)
# Xử lý kết quả
self.process_answer(question, answer, is_correct)
return True
def get_timed_answer(self):
"""Lấy đáp án với thời gian giới hạn"""
import select
import sys
print(f"\n{Fore.GREEN}⏰ Bạn có {self.time_limit} giây để trả lời!")
print(f"{Fore.CYAN}Nhập A, B, C, D hoặc 'hint' để gợi ý: ", end="", flush=True)
start_time = time.time()
answer = None
# Countdown timer trong thread riêng
def countdown():
for remaining in range(self.time_limit, 0, -1):
time.sleep(1)
if answer is not None:
break
print(f"\r{Fore.RED}⏰ Còn {remaining-1} giây... ", end="", flush=True)
# Bắt đầu countdown
timer_thread = threading.Thread(target=countdown)
timer_thread.daemon = True
timer_thread.start()
# Lấy input từ user
try:
user_input = input().strip().upper()
if user_input == "HINT":
answer = "hint"
elif user_input in ["A", "B", "C", "D"]:
answer = ord(user_input) - ord("A") # Chuyển A,B,C,D thành 0,1,2,3
else:
answer = -1 # Invalid
except:
answer = None
return answer
def show_hint(self, question_index):
"""Hiển thị gợi ý"""
if self.hints_used >= self.max_hints:
print(f"\n{Fore.RED}❌ Bạn đã hết lượt gợi ý!")
return self.play_question(question_index)
self.hints_used += 1
question = self.questions[question_index]
# Loại bỏ 2 đáp án sai
correct_index = question.correct_answer
wrong_indices = [i for i in range(4) if i != correct_index]
remove_indices = random.sample(wrong_indices, 2)
print(f"\n{Fore.YELLOW}💡 GỢI Ý: Loại bỏ 2 đáp án sai!")
for i, option in enumerate(question.options):
if i in remove_indices:
print(f"{Fore.RED} {chr(65+i)}. ❌ (Đã loại bỏ)")
else:
print(f"{Fore.YELLOW} {chr(65+i)}. {option}")
print(f"\n{Fore.MAGENTA}💡 Gợi ý còn: {self.max_hints - self.hints_used}")
# Tiếp tục nhận đáp án
answer = self.get_timed_answer()
if answer == "hint":
print(f"\n{Fore.RED}❌ Bạn đã dùng gợi ý rồi!")
return self.play_question(question_index)
is_correct = False
if answer is not None and 0 <= answer <= 3:
is_correct = question.is_correct(answer)
self.process_answer(question, answer, is_correct)
return True
def process_answer(self, question, answer, is_correct):
"""Xử lý kết quả đáp án"""
# Ghi log
log_entry = {
'question': question.question_text,
'user_answer': answer,
'correct_answer': question.correct_answer,
'is_correct': is_correct,
'points': question.points if is_correct else 0
}
self.answers_log.append(log_entry)
print(f"\n{'-' * 50}")
if answer is None:
print(f"{Fore.RED}⏰ HẾT THỜI GIAN!")
print(f"{Fore.YELLOW}💡 Đáp án đúng: {chr(65 + question.correct_answer)}. {question.get_correct_text()}")
elif answer == -1:
print(f"{Fore.RED}❌ ĐÁP ÁN KHÔNG HỢP LỆ!")
print(f"{Fore.YELLOW}💡 Đáp án đúng: {chr(65 + question.correct_answer)}. {question.get_correct_text()}")
elif is_correct:
points_earned = question.points
if self.hints_used > 0:
points_earned = max(1, points_earned // 2) # Giảm điểm khi dùng gợi ý
self.score += points_earned
self.correct_count += 1
print(f"{Fore.GREEN}✅ CHÍNH XÁC!")
print(f"{Fore.GREEN}🎉 +{points_earned} điểm")
print(f"{Fore.CYAN}💰 Tổng điểm: {self.score}")
else:
print(f"{Fore.RED}❌ SAI RỒI!")
print(f"{Fore.YELLOW}💡 Đáp án đúng: {chr(65 + question.correct_answer)}. {question.get_correct_text()}")
# Hiển thị giải thích nếu có
if question.explanation:
print(f"{Fore.BLUE}📖 Giải thích: {question.explanation}")
print(f"{'-' * 50}")
input(f"\n{Fore.YELLOW}📱 Nhấn Enter để tiếp tục...")
def end_game(self):
"""Kết thúc game"""
end_time = datetime.now()
duration = end_time - self.start_time
print(f"\n{Fore.GREEN}{Style.BRIGHT}🏁 KẾT THÚC GAME!")
print("=" * 50)
print(f"👤 Người chơi: {self.player.name}")
print(f"⏰ Thời gian chơi: {duration.seconds // 60}:{duration.seconds % 60:02d}")
print(f"❓ Tổng câu hỏi: {len(self.questions)}")
print(f"✅ Trả lời đúng: {self.correct_count}")
print(f"❌ Trả lời sai: {len(self.questions) - self.correct_count}")
print(f"💡 Gợi ý đã dùng: {self.hints_used}")
print(f"💰 Tổng điểm: {self.score}")
accuracy = (self.correct_count / len(self.questions)) * 100
print(f"🎯 Độ chính xác: {accuracy:.1f}%")
# Đánh giá
if accuracy >= 90:
print(f"{Fore.YELLOW}