Chuyển tới nội dung chính

Xây dựng Game Đoán Số Hoàn Chỉnh - Hướng dẫn Python từ A-Z

· 28 phút để đọc
Behitek
Software Developer & Educator

Chào các bạn! Hôm nay chúng ta sẽ cùng nhau xây dựng một Game Đoán Số hoàn chỉnh bằng Python. Đây là dự án tuyệt vời để các bạn mới học lập trình có thể thực hành và hiểu rõ các khái niệm cơ bản như vòng lặp, điều kiện, và xử lý input từ người dùng.

Game này không chỉ đơn giản là đoán một số ngẫu nhiên, mà chúng ta sẽ xây dựng một hệ thống hoàn chỉnh với nhiều tính năng thú vị như: hệ thống gợi ý thông minh, nhiều mức độ khó, hệ thống điểm số, và thống kê chi tiết!

Game Demo và Giới Thiệu

Trước khi bắt đầu code, hãy cùng xem game chúng ta sẽ xây dựng sẽ như thế nào:

🎯 CHÀO MỪNG ĐẾN VỚI GAME ĐOÁN SỐ HOÀN CHỈNH! 🎯

Chọn mức độ khó:
1. Dễ (1-10, tối đa 5 lần đoán)
2. Trung bình (1-50, tối đa 7 lần đoán)
3. Khó (1-100, tối đa 10 lần đoán)
4. Siêu khó (1-1000, tối đa 12 lần đoán)

Lựa chọn của bạn: 2

🎮 BẮT ĐẦU GAME - MỨC TRUNG BÌNH 🎮
Số bí mật nằm trong khoảng 1-50
Bạn có tối đa 7 lần đoán để tìm ra số đúng!

Lần đoán 1/7: 25
🔥 Rất gần rồi! Số cần tìm LỚN HỚN 25

Lần đoán 2/7: 35
❄️ Hơi xa rồi! Số cần tìm NHỎ HỚN 35

Lần đoán 3/7: 30
🎉 CHÚC MỪNG! Bạn đã đoán đúng số 30!

💯 KẾT QUẢ GAME 💯
- Số lần đoán: 3/7
- Điểm số: 850 điểm
- Độ chính xác: Tuyệt vời!

Bạn Sẽ Học Được Gì?

Qua bài hướng dẫn này, bạn sẽ nắm vững:

  • Tạo số ngẫu nhiên với module random
  • Xử lý input từ người dùng và validation
  • Vòng lặp và điều kiện trong game logic
  • Hệ thống menu và navigation đơn giản
  • Hệ thống điểm số và ranking
  • Lưu/đọc dữ liệu từ file để ghi nhớ thống kê
  • Xử lý lỗi và exception handling
  • Code organization và best practices

Yêu Cầu Trước Khi Bắt Đầu

  • Python cơ bản: Biết về biến, hàm, if/else, vòng lặp
  • Python 3.6+ đã cài đặt trên máy
  • Text editor hoặc IDE (VS Code, PyCharm, etc.)
  • Tinh thần học hỏi và sẵn sàng thử nghiệm! 🚀

Bước 1: Phân Tích Game - Hiểu Rõ Trước Khi Code

Trước khi nhảy vào code, chúng ta cần hiểu rõ game hoạt động như thế nào. Hãy cùng phân tích từng bước:

Game Flow - Luồng Chơi Game

Phân Tích Chi Tiết Các Thành Phần

1. Mức Độ Khó (Difficulty Levels)

  • Dễ: 1-10, 5 lần đoán, điểm cơ sở 300
  • Trung bình: 1-50, 7 lần đoán, điểm cơ sở 500
  • Khó: 1-100, 10 lần đoán, điểm cơ sở 700
  • Siêu khó: 1-1000, 12 lần đoán, điểm cơ sở 1000

2. Hệ Thống Gợi Ý Thông Minh

  • Tính khoảng cách giữa số đoán và số đúng
  • Đưa ra phản hồi phù hợp: "Rất gần", "Gần", "Xa", "Rất xa"
  • Sử dụng emoji để game sinh động hơn

3. Hệ Thống Điểm Số

Điểm = Điểm_Cơ_Sở × (Lượt_Còn_Lại / Tổng_Lượt) × Hệ_Số_Bonus

4. Lưu Trữ Dữ Liệu

  • Số game đã chơi
  • Số lần thắng/thua
  • Điểm cao nhất
  • Thống kê theo mức độ khó

Bước 2: Thiết Kế Kiến Trúc - Xây Dựng Cấu Trúc Code

Chúng ta sẽ tổ chức code thành các module riêng biệt để dễ quản lý và bảo trì:

Sơ Đồ Kiến Trúc

Cấu Trúc Files

guessing_game/
├── main.py # Entry point
├── game_manager.py # Game logic chính
├── guessing_game.py # Core game mechanics
├── ui_manager.py # User interface
├── stats_manager.py # Statistics handling
├── config.py # Game configuration
└── data/
└── stats.json # Lưu thống kê người chơi

Bước 3: Implementation - Xây Dựng Game Từng Bước

Bây giờ chúng ta sẽ bắt đầu code! Tôi sẽ giải thích từng dòng code một cách chi tiết.

3.1. Tạo File Cấu Hình (config.py)

Đầu tiên, chúng ta tạo file cấu hình để lưu các thông số game:

# config.py
"""
File cấu hình cho Game Đoán Số
Chứa tất cả các thông số có thể điều chỉnh
"""

# Cấu hình mức độ khó
DIFFICULTY_LEVELS = {
1: {
'name': 'Dễ',
'min_range': 1,
'max_range': 10,
'max_attempts': 5,
'base_score': 300,
'emoji': '😊'
},
2: {
'name': 'Trung bình',
'min_range': 1,
'max_range': 50,
'max_attempts': 7,
'base_score': 500,
'emoji': '🤔'
},
3: {
'name': 'Khó',
'min_range': 1,
'max_range': 100,
'max_attempts': 10,
'base_score': 700,
'emoji': '😤'
},
4: {
'name': 'Siêu khó',
'min_range': 1,
'max_range': 1000,
'max_attempts': 12,
'base_score': 1000,
'emoji': '🔥'
}
}

# Cấu hình hệ thống gợi ý
HINT_SYSTEM = {
'very_close': {'threshold': 0.05, 'message': '🔥 Rất gần rồi!', 'emoji': '🔥'},
'close': {'threshold': 0.15, 'message': '👍 Gần rồi!', 'emoji': '👍'},
'far': {'threshold': 0.35, 'message': '🤏 Hơi xa!', 'emoji': '🤏'},
'very_far': {'threshold': 1.0, 'message': '❄️ Rất xa!', 'emoji': '❄️'}
}

# File lưu thống kê
STATS_FILE = 'data/stats.json'

# Các thông điệp trong game
MESSAGES = {
'welcome': '🎯 CHÀO MỪNG ĐẾN VỚI GAME ĐOÁN SỐ HOÀN CHỈNH! 🎯',
'goodbye': '👋 Cảm ơn bạn đã chơi! Hẹn gặp lại! 👋',
'invalid_input': '❌ Vui lòng nhập một số hợp lệ!',
'out_of_range': '⚠️ Số phải nằm trong khoảng {min}-{max}!',
'win': '🎉 CHÚC MỪNG! Bạn đã đoán đúng số {number}!',
'lose': '😢 Hết lượt rồi! Số đúng là {number}.',
'attempts_left': 'Còn lại {attempts} lần đoán.'
}

Giải thích chi tiết:

  • DIFFICULTY_LEVELS: Dictionary chứa thông tin của từng mức độ khó. Mỗi mức có:

    • name: Tên hiển thị
    • min_range, max_range: Khoảng số để đoán
    • max_attempts: Số lần đoán tối đa
    • base_score: Điểm cơ sở để tính điểm
    • emoji: Icon đại diện cho mức độ
  • HINT_SYSTEM: Hệ thống gợi ý dựa trên khoảng cách. threshold là ngưỡng tính theo tỷ lệ phần trăm của khoảng số.

3.2. Xây Dựng UI Manager (ui_manager.py)

# ui_manager.py
"""
Quản lý giao diện người dùng (User Interface)
Tất cả việc hiển thị và nhận input từ user đều ở đây
"""

import os
from config import DIFFICULTY_LEVELS, MESSAGES

class UIManager:
"""Lớp quản lý giao diện người dùng"""

def __init__(self):
self.separator = "=" * 50

def clear_screen(self):
"""Xóa màn hình console"""
os.system('cls' if os.name == 'nt' else 'clear')

def show_welcome(self):
"""Hiển thị màn hình chào mừng"""
self.clear_screen()
print(self.separator)
print(MESSAGES['welcome'])
print(self.separator)
print()

def show_main_menu(self):
"""Hiển thị menu chính"""
print("📋 MENU CHÍNH:")
print("1. 🎮 Chơi Game Mới")
print("2. 📊 Xem Thống Kê")
print("3. ❓ Hướng Dẫn")
print("4. 🚪 Thoát Game")
print()

def show_difficulty_menu(self):
"""Hiển thị menu chọn mức độ khó"""
print("🎯 CHỌN MỨC ĐỘ KHÓ:")
for key, level in DIFFICULTY_LEVELS.items():
print(f"{key}. {level['emoji']} {level['name']} "
f"({level['min_range']}-{level['max_range']}, "
f"tối đa {level['max_attempts']} lần đoán)")
print()

def show_game_header(self, difficulty_info, attempts_left):
"""Hiển thị header của game đang chơi"""
print(f"\n🎮 BẮT ĐẦU GAME - MỨC {difficulty_info['name'].upper()} 🎮")
print(f"Số bí mật nằm trong khoảng {difficulty_info['min_range']}-{difficulty_info['max_range']}")
print(f"Bạn có tối đa {difficulty_info['max_attempts']} lần đoán để tìm ra số đúng!")
print(f"Còn lại: {attempts_left} lần đoán")
print()

def get_user_input(self, prompt, input_type=int, valid_range=None):
"""
Nhận input từ người dùng với validation

Args:
prompt (str): Câu hỏi hiển thị
input_type (type): Kiểu dữ liệu mong muốn
valid_range (tuple): Khoảng giá trị hợp lệ (min, max)

Returns:
Giá trị đã được validate
"""
while True:
try:
user_input = input_type(input(prompt))

# Kiểm tra khoảng giá trị nếu có
if valid_range:
min_val, max_val = valid_range
if not (min_val <= user_input <= max_val):
if input_type == int:
print(MESSAGES['out_of_range'].format(
min=min_val, max=max_val))
continue

return user_input

except ValueError:
print(MESSAGES['invalid_input'])
except KeyboardInterrupt:
print("\n\n👋 Tạm biệt!")
exit()

def show_hint(self, hint_message, is_higher):
"""
Hiển thị gợi ý cho người chơi

Args:
hint_message (str): Thông điệp gợi ý
is_higher (bool): True nếu số cần tìm lớn hơn
"""
direction = "LỚN HỚN" if is_higher else "NHỎ HỚN"
print(f"{hint_message} Số cần tìm {direction} số bạn vừa đoán.")

def show_game_result(self, won, secret_number, attempts_used,
max_attempts, score=None):
"""
Hiển thị kết quả game

Args:
won (bool): True nếu thắng
secret_number (int): Số bí mật
attempts_used (int): Số lần đã đoán
max_attempts (int): Tổng số lần đoán cho phép
score (int): Điểm số đạt được
"""
print("\n" + "🎯" * 20)

if won:
print(MESSAGES['win'].format(number=secret_number))
accuracy = self._get_accuracy_level(attempts_used, max_attempts)
print(f"🏆 Độ chính xác: {accuracy}")
else:
print(MESSAGES['lose'].format(number=secret_number))

print(f"📈 Số lần đoán: {attempts_used}/{max_attempts}")

if score:
print(f"💯 Điểm số: {score} điểm")

print("🎯" * 20)

def _get_accuracy_level(self, attempts_used, max_attempts):
"""Tính mức độ chính xác dựa trên số lần đoán"""
ratio = attempts_used / max_attempts

if ratio <= 0.3:
return "Xuất sắc! 🌟"
elif ratio <= 0.5:
return "Tuyệt vời! 🎉"
elif ratio <= 0.7:
return "Tốt! 👍"
else:
return "Cần cải thiện! 💪"

def show_statistics(self, stats):
"""
Hiển thị thống kê chi tiết

Args:
stats (dict): Dữ liệu thống kê
"""
print("\n📊 THỐNG KÊ CHI TIẾT:")
print(self.separator)

print(f"🎮 Tổng số game đã chơi: {stats.get('total_games', 0)}")
print(f"🏆 Số lần thắng: {stats.get('wins', 0)}")
print(f"😢 Số lần thua: {stats.get('losses', 0)}")

# Tính tỷ lệ thắng
total = stats.get('total_games', 0)
if total > 0:
win_rate = (stats.get('wins', 0) / total) * 100
print(f"📈 Tỷ lệ thắng: {win_rate:.1f}%")

print(f"💯 Điểm cao nhất: {stats.get('best_score', 0)}")

# Thống kê theo mức độ khó
difficulty_stats = stats.get('difficulty_stats', {})
if difficulty_stats:
print("\n🎯 Thống kê theo mức độ khó:")
for diff_name, diff_stats in difficulty_stats.items():
print(f" {diff_name}: {diff_stats['wins']}/{diff_stats['total']} thắng")

print(self.separator)

def show_instructions(self):
"""Hiển thị hướng dẫn chơi game"""
print("\n❓ HƯỚNG DẪN CHƠI GAME:")
print(self.separator)

instructions = [
"1. Chọn mức độ khó phù hợp với trình độ",
"2. Game sẽ tạo ra một số bí mật trong khoảng đã chọn",
"3. Bạn đoán số và nhận được gợi ý:",
" 🔥 Rất gần: Chênh lệch < 5% khoảng số",
" 👍 Gần: Chênh lệch < 15% khoảng số",
" 🤏 Xa: Chênh lệch < 35% khoảng số",
" ❄️ Rất xa: Chênh lệch > 35% khoảng số",
"4. Đoán đúng trong ít lần nhất để có điểm cao!",
"5. Thống kê sẽ được lưu tự động"
]

for instruction in instructions:
print(instruction)

print(self.separator)

def ask_play_again(self):
"""Hỏi người chơi có muốn chơi tiếp không"""
choice = input("\n🔄 Bạn có muốn chơi lại không? (y/n): ").lower().strip()
return choice in ['y', 'yes', 'có', 'c']

def pause_for_user(self):
"""Tạm dừng để người dùng đọc thông tin"""
input("\n⏸️ Nhấn Enter để tiếp tục...")

Giải thích các phương thức quan trọng:

  • get_user_input(): Phương thức quan trọng nhất, xử lý việc nhận input từ user với validation đầy đủ. Có thể specify kiểu dữ liệu và khoảng giá trị hợp lệ.

  • show_hint(): Hiển thị gợi ý thông minh dựa trên khoảng cách giữa số đoán và số đúng.

  • _get_accuracy_level(): Phương thức private để đánh giá độ chính xác dựa trên tỷ lệ số lần đoán.

3.3. Xây Dựng Stats Manager (stats_manager.py)

# stats_manager.py
"""
Quản lý thống kê game
Lưu trữ và phân tích dữ liệu người chơi
"""

import json
import os
from datetime import datetime
from config import STATS_FILE

class StatsManager:
"""Lớp quản lý thống kê game"""

def __init__(self):
self.stats_file = STATS_FILE
self.stats = self.load_stats()

def load_stats(self):
"""
Tải thống kê từ file JSON

Returns:
dict: Dữ liệu thống kê hoặc dữ liệu mặc định
"""
# Tạo thư mục data nếu chưa có
os.makedirs(os.path.dirname(self.stats_file), exist_ok=True)

try:
with open(self.stats_file, 'r', encoding='utf-8') as f:
return json.load(f)
except (FileNotFoundError, json.JSONDecodeError):
# Trả về cấu trúc thống kê mặc định
return {
'total_games': 0,
'wins': 0,
'losses': 0,
'best_score': 0,
'difficulty_stats': {},
'last_played': None,
'creation_date': datetime.now().isoformat()
}

def save_stats(self):
"""Lưu thống kê vào file JSON"""
try:
# Cập nhật thời gian chơi cuối
self.stats['last_played'] = datetime.now().isoformat()

with open(self.stats_file, 'w', encoding='utf-8') as f:
json.dump(self.stats, f, ensure_ascii=False, indent=2)
except Exception as e:
print(f"⚠️ Không thể lưu thống kê: {e}")

def update_game_stats(self, won, difficulty_name, score, attempts_used):
"""
Cập nhật thống kê sau mỗi game

Args:
won (bool): True nếu thắng
difficulty_name (str): Tên mức độ khó
score (int): Điểm số đạt được
attempts_used (int): Số lần đã đoán
"""
# Cập nhật thống kê tổng thể
self.stats['total_games'] += 1

if won:
self.stats['wins'] += 1
# Cập nhật điểm cao nhất
if score > self.stats['best_score']:
self.stats['best_score'] = score
else:
self.stats['losses'] += 1

# Cập nhật thống kê theo mức độ khó
if difficulty_name not in self.stats['difficulty_stats']:
self.stats['difficulty_stats'][difficulty_name] = {
'total': 0,
'wins': 0,
'losses': 0,
'best_score': 0,
'total_attempts': 0,
'games_attempts': [] # Lưu số lần đoán của mỗi game
}

diff_stats = self.stats['difficulty_stats'][difficulty_name]
diff_stats['total'] += 1
diff_stats['total_attempts'] += attempts_used
diff_stats['games_attempts'].append(attempts_used)

if won:
diff_stats['wins'] += 1
if score > diff_stats['best_score']:
diff_stats['best_score'] = score
else:
diff_stats['losses'] += 1

# Lưu thống kê
self.save_stats()

def get_stats(self):
"""
Lấy thống kê hiện tại

Returns:
dict: Dữ liệu thống kê
"""
return self.stats.copy()

def get_win_rate(self):
"""
Tính tỷ lệ thắng tổng thể

Returns:
float: Tỷ lệ thắng (0-100)
"""
total = self.stats['total_games']
if total == 0:
return 0.0
return (self.stats['wins'] / total) * 100

def get_average_attempts(self, difficulty_name=None):
"""
Tính số lần đoán trung bình

Args:
difficulty_name (str, optional): Tên mức độ khó cụ thể

Returns:
float: Số lần đoán trung bình
"""
if difficulty_name and difficulty_name in self.stats['difficulty_stats']:
diff_stats = self.stats['difficulty_stats'][difficulty_name]
if diff_stats['total'] > 0:
return diff_stats['total_attempts'] / diff_stats['total']

# Tính trung bình tổng thể
total_attempts = 0
total_games = 0

for diff_stats in self.stats['difficulty_stats'].values():
total_attempts += diff_stats['total_attempts']
total_games += diff_stats['total']

if total_games == 0:
return 0.0

return total_attempts / total_games

def get_difficulty_ranking(self):
"""
Xếp hạng các mức độ khó theo tỷ lệ thắng

Returns:
list: Danh sách (difficulty_name, win_rate) được sắp xếp
"""
rankings = []

for diff_name, diff_stats in self.stats['difficulty_stats'].items():
if diff_stats['total'] > 0:
win_rate = (diff_stats['wins'] / diff_stats['total']) * 100
rankings.append((diff_name, win_rate, diff_stats['total']))

# Sắp xếp theo tỷ lệ thắng giảm dần
rankings.sort(key=lambda x: x[1], reverse=True)
return rankings

def reset_stats(self):
"""Reset toàn bộ thống kê về mặc định"""
confirm = input("⚠️ Bạn có chắc muốn xóa toàn bộ thống kê? (yes/no): ").lower()
if confirm == 'yes':
self.stats = {
'total_games': 0,
'wins': 0,
'losses': 0,
'best_score': 0,
'difficulty_stats': {},
'last_played': None,
'creation_date': datetime.now().isoformat()
}
self.save_stats()
print("✅ Đã reset thống kê thành công!")
else:
print("❌ Hủy reset thống kê.")

Các tính năng nổi bật của StatsManager:

  • Lưu trữ JSON: Sử dụng JSON để lưu dữ liệu, dễ đọc và chỉnh sửa
  • Thống kê đa cấp: Theo dõi cả tổng thể và từng mức độ khó
  • Phân tích chi tiết: Tỷ lệ thắng, số lần đoán trung bình, xếp hạng
  • Error handling: Xử lý lỗi khi file không tồn tại hoặc corrupt

3.4. Xây Dựng Core Game Logic (guessing_game.py)

# guessing_game.py
"""
Core game mechanics - Logic chính của game đoán số
"""

import random
from config import DIFFICULTY_LEVELS, HINT_SYSTEM

class GuessingGame:
"""Lớp chính chứa logic game đoán số"""

def __init__(self, difficulty_level):
"""
Khởi tạo game với mức độ khó

Args:
difficulty_level (int): Mức độ khó (1-4)
"""
self.difficulty_info = DIFFICULTY_LEVELS[difficulty_level]
self.difficulty_name = self.difficulty_info['name']

# Thông số game
self.min_range = self.difficulty_info['min_range']
self.max_range = self.difficulty_info['max_range']
self.max_attempts = self.difficulty_info['max_attempts']
self.base_score = self.difficulty_info['base_score']

# Trạng thái game
self.secret_number = self._generate_secret_number()
self.current_attempt = 0
self.is_won = False
self.is_game_over = False
self.guess_history = [] # Lịch sử các lần đoán

def _generate_secret_number(self):
"""
Tạo số bí mật ngẫu nhiên

Returns:
int: Số bí mật trong khoảng cho phép
"""
return random.randint(self.min_range, self.max_range)

def make_guess(self, guess):
"""
Xử lý một lần đoán của người chơi

Args:
guess (int): Số người chơi đoán

Returns:
dict: Kết quả của lần đoán
"""
self.current_attempt += 1
self.guess_history.append(guess)

# Tạo result dictionary
result = {
'attempt_number': self.current_attempt,
'guess': guess,
'is_correct': False,
'is_game_over': False,
'hint': None,
'attempts_left': self.max_attempts - self.current_attempt
}

# Kiểm tra đoán đúng
if guess == self.secret_number:
result['is_correct'] = True
self.is_won = True
self.is_game_over = True
result['is_game_over'] = True
result['score'] = self.calculate_score()
else:
# Đưa ra gợi ý
result['hint'] = self._generate_hint(guess)

# Kiểm tra hết lượt
if self.current_attempt >= self.max_attempts:
self.is_game_over = True
result['is_game_over'] = True
result['secret_number'] = self.secret_number

return result

def _generate_hint(self, guess):
"""
Tạo gợi ý thông minh dựa trên khoảng cách

Args:
guess (int): Số người chơi đoán

Returns:
dict: Thông tin gợi ý
"""
distance = abs(guess - self.secret_number)
range_size = self.max_range - self.min_range + 1
distance_ratio = distance / range_size

# Xác định loại gợi ý
hint_type = 'very_far' # Mặc định
for hint_name, hint_config in HINT_SYSTEM.items():
if distance_ratio <= hint_config['threshold']:
hint_type = hint_name
break

hint_config = HINT_SYSTEM[hint_type]

return {
'type': hint_type,
'message': hint_config['message'],
'emoji': hint_config['emoji'],
'is_higher': self.secret_number > guess,
'distance': distance,
'distance_ratio': distance_ratio
}

def calculate_score(self):
"""
Tính điểm số dựa trên hiệu suất

Returns:
int: Điểm số đạt được
"""
if not self.is_won:
return 0

# Điểm cơ sở theo mức độ khó
base_score = self.base_score

# Hệ số dựa trên số lần đoán còn lại
attempts_left = self.max_attempts - self.current_attempt
efficiency_bonus = attempts_left / self.max_attempts

# Hệ số bonus đặc biệt
special_bonus = 1.0

# Bonus nếu đoán đúng ngay lần đầu
if self.current_attempt == 1:
special_bonus = 2.0
# Bonus nếu đoán đúng trong 3 lần đầu (trừ mức dễ)
elif self.current_attempt <= 3 and self.max_attempts > 5:
special_bonus = 1.5

# Tính điểm cuối
final_score = int(base_score * (1 + efficiency_bonus) * special_bonus)

return final_score

def get_game_summary(self):
"""
Lấy tóm tắt game hiện tại

Returns:
dict: Thông tin tóm tắt game
"""
return {
'difficulty_name': self.difficulty_name,
'difficulty_info': self.difficulty_info,
'secret_number': self.secret_number,
'current_attempt': self.current_attempt,
'max_attempts': self.max_attempts,
'is_won': self.is_won,
'is_game_over': self.is_game_over,
'guess_history': self.guess_history.copy(),
'score': self.calculate_score() if self.is_won else 0
}

def get_attempts_left(self):
"""Lấy số lần đoán còn lại"""
return self.max_attempts - self.current_attempt

def is_valid_guess(self, guess):
"""
Kiểm tra số đoán có hợp lệ không

Args:
guess (int): Số cần kiểm tra

Returns:
tuple: (is_valid, error_message)
"""
if not isinstance(guess, int):
return False, "Phải là số nguyên"

if guess < self.min_range or guess > self.max_range:
return False, f"Số phải nằm trong khoảng {self.min_range}-{self.max_range}"

if guess in self.guess_history:
return False, f"Bạn đã đoán số {guess} rồi!"

return True, ""

def get_smart_hint(self):
"""
Đưa ra gợi ý thông minh dựa trên lịch sử đoán

Returns:
str: Gợi ý thông minh
"""
if len(self.guess_history) < 2:
return "💡 Hãy thử đoán ở giữa khoảng!"

# Phân tích pattern đoán
last_guess = self.guess_history[-1]
previous_guess = self.guess_history[-2]

# Nếu đang tiến gần đến đáp án
if abs(last_guess - self.secret_number) < abs(previous_guess - self.secret_number):
return "💡 Bạn đang đi đúng hướng! Tiếp tục!"
else:
return "💡 Thử quay lại hướng trước đó xem!"

Highlights của GuessingGame class:

  • Hint System thông minh: Tính toán khoảng cách tương đối để đưa ra gợi ý phù hợp
  • Scoring Algorithm: Công thức tính điểm phức tạp dựa trên hiệu suất và bonus
  • Input Validation: Kiểm tra kỹ lưỡng input và tránh đoán trùng
  • Game State Management: Theo dõi đầy đủ trạng thái game

3.5. Xây Dựng Game Manager Chính (game_manager.py)

# game_manager.py
"""
Game Manager - Điều phối toàn bộ game
Kết nối tất cả các component lại với nhau
"""

from guessing_game import GuessingGame
from ui_manager import UIManager
from stats_manager import StatsManager
from config import DIFFICULTY_LEVELS

class GameManager:
"""Lớp quản lý chính của game"""

def __init__(self):
self.ui = UIManager()
self.stats = StatsManager()
self.current_game = None
self.is_running = True

def run(self):
"""Chạy game chính"""
self.ui.show_welcome()

while self.is_running:
self._show_main_menu()

def _show_main_menu(self):
"""Hiển thị và xử lý menu chính"""
self.ui.show_main_menu()

choice = self.ui.get_user_input(
"Lựa chọn của bạn (1-4): ",
int,
(1, 4)
)

if choice == 1:
self._start_new_game()
elif choice == 2:
self._show_statistics()
elif choice == 3:
self._show_instructions()
elif choice == 4:
self._exit_game()

def _start_new_game(self):
"""Bắt đầu game mới"""
# Chọn mức độ khó
self.ui.show_difficulty_menu()

difficulty = self.ui.get_user_input(
"Lựa chọn mức độ khó (1-4): ",
int,
(1, 4)
)

# Tạo game mới
self.current_game = GuessingGame(difficulty)

# Chạy game loop
self._run_game_loop()

def _run_game_loop(self):
"""Vòng lặp chính của game"""
game_summary = self.current_game.get_game_summary()

# Hiển thị header game
self.ui.show_game_header(
game_summary['difficulty_info'],
self.current_game.get_attempts_left()
)

# Vòng lặp đoán số
while not self.current_game.is_game_over:
# Nhận input từ người chơi
attempts_left = self.current_game.get_attempts_left()
prompt = f"Lần đoán {self.current_game.current_attempt + 1}/{self.current_game.max_attempts}: "

guess = self.ui.get_user_input(
prompt,
int,
(self.current_game.min_range, self.current_game.max_range)
)

# Kiểm tra tính hợp lệ của đoán
is_valid, error_msg = self.current_game.is_valid_guess(guess)
if not is_valid:
print(f"❌ {error_msg}")
continue

# Xử lý lần đoán
result = self.current_game.make_guess(guess)

# Hiển thị kết quả
if result['is_correct']:
# Thắng game
self.ui.show_game_result(
won=True,
secret_number=self.current_game.secret_number,
attempts_used=result['attempt_number'],
max_attempts=self.current_game.max_attempts,
score=result['score']
)
elif result['is_game_over']:
# Thua game
self.ui.show_game_result(
won=False,
secret_number=result['secret_number'],
attempts_used=result['attempt_number'],
max_attempts=self.current_game.max_attempts
)
else:
# Hiển thị gợi ý
hint = result['hint']
self.ui.show_hint(hint['message'], hint['is_higher'])

if result['attempts_left'] > 0:
print(f"Còn lại: {result['attempts_left']} lần đoán")

# Gợi ý thông minh nếu còn ít lượt
if result['attempts_left'] <= 2:
smart_hint = self.current_game.get_smart_hint()
print(smart_hint)

print() # Dòng trống cho dễ đọc

# Cập nhật thống kê
self._update_game_stats()

# Hỏi chơi lại
if self.ui.ask_play_again():
self._start_new_game()

def _update_game_stats(self):
"""Cập nhật thống kê sau khi game kết thúc"""
game_summary = self.current_game.get_game_summary()

self.stats.update_game_stats(
won=game_summary['is_won'],
difficulty_name=game_summary['difficulty_name'],
score=game_summary['score'],
attempts_used=game_summary['current_attempt']
)

def _show_statistics(self):
"""Hiển thị thống kê chi tiết"""
stats_data = self.stats.get_stats()
self.ui.show_statistics(stats_data)

# Hiển thị thống kê nâng cao
self._show_advanced_stats()

self.ui.pause_for_user()

def _show_advanced_stats(self):
"""Hiển thị thống kê nâng cao"""
print("\n🏆 THỐNG KÊ NÂNG CAO:")
print("=" * 50)

# Tỷ lệ thắng tổng thể
win_rate = self.stats.get_win_rate()
print(f"📊 Tỷ lệ thắng tổng thể: {win_rate:.1f}%")

# Số lượt đoán trung bình
avg_attempts = self.stats.get_average_attempts()
if avg_attempts > 0:
print(f"🎯 Số lần đoán trung bình: {avg_attempts:.1f}")

# Xếp hạng theo mức độ khó
rankings = self.stats.get_difficulty_ranking()
if rankings:
print("\n🥇 Xếp hạng theo mức độ khó (theo tỷ lệ thắng):")
for i, (diff_name, win_rate, total_games) in enumerate(rankings, 1):
medal = ["🥇", "🥈", "🥉"][i-1] if i <= 3 else "🏅"
print(f" {medal} {diff_name}: {win_rate:.1f}% ({total_games} game)")

# Mức độ khó được ưa thích
stats_data = self.stats.get_stats()
difficulty_stats = stats_data.get('difficulty_stats', {})
if difficulty_stats:
most_played = max(difficulty_stats.items(), key=lambda x: x[1]['total'])
print(f"\n❤️ Mức độ khó ưa thích: {most_played[0]} ({most_played[1]['total']} game)")

def _show_instructions(self):
"""Hiển thị hướng dẫn"""
self.ui.show_instructions()

# Demo mini game
print("\n🎮 DEMO NHANH:")
print("Hãy thử đoán số từ 1-10 (Demo mode):")

demo_game = GuessingGame(1) # Mức dễ
demo_secret = demo_game.secret_number

print(f"💡 (Gợi ý: Số bí mật là {demo_secret})")

guess = self.ui.get_user_input("Đoán số: ", int, (1, 10))
result = demo_game.make_guess(guess)

if result['is_correct']:
print("🎉 Đúng rồi! Bạn đã hiểu cách chơi!")
else:
hint = result['hint']
print(f"{hint['message']} Số đúng là {demo_secret}")

self.ui.pause_for_user()

def _exit_game(self):
"""Thoát game"""
stats_data = self.stats.get_stats()

if stats_data['total_games'] > 0:
print(f"\n📊 TỔNG KẾT PHIÊN CHƠI:")
print(f"🎮 Đã chơi: {stats_data['total_games']} game")
print(f"🏆 Thắng: {stats_data['wins']} game")
print(f"💯 Điểm cao nhất: {stats_data['best_score']}")

print("\n👋 Cảm ơn bạn đã chơi Game Đoán Số!")
print("🌟 Hẹn gặp lại bạn lần sau!")

self.is_running = False

3.6. File Main - Entry Point (main.py)

# main.py
"""
Entry point của Game Đoán Số Hoàn Chỉnh
Khởi động và chạy game
"""

import sys
import os

# Thêm current directory vào Python path
sys.path.append(os.path.dirname(os.path.abspath(__file__)))

from game_manager import GameManager

def main():
"""Hàm main - điểm khởi đầu của chương trình"""
try:
# Tạo và chạy game manager
game = GameManager()
game.run()

except KeyboardInterrupt:
print("\n\n👋 Game bị dừng bởi người dùng. Tạm biệt!")
except Exception as e:
print(f"\n❌ Có lỗi xảy ra: {e}")
print("🔧 Vui lòng khởi động lại game.")
finally:
print("\n🔚 Kết thúc chương trình.")

if __name__ == "__main__":
main()

Bước 4: Testing và Debugging - Kiểm Tra Game

Bây giờ chúng ta hãy test game để đảm bảo mọi thứ hoạt động đúng cách. Việc testing rất quan trọng để phát hiện lỗi sớm và đảm bảo user experience tốt.

4.1. Tạo Test Script

Tạo file test_game.py để kiểm tra các thành phần chính:

# test_game.py
"""
Test script để kiểm tra game logic
"""
import sys
sys.path.append('.')

from guessing_game import GuessingGame
from stats_manager import StatsManager
from ui_manager import UIManager

def test_guessing_game():
print("🧪 Testing GuessingGame...")

# Test tạo game mức dễ
game = GuessingGame(1) # Mức dễ
print(f"✅ Game created: Range {game.min_range}-{game.max_range}")
print(f" Secret number: {game.secret_number}")

# Test đoán sai
result = game.make_guess(1)
print(f"✅ Guess 1: {'Correct' if result['is_correct'] else 'Incorrect'}")
if not result['is_correct']:
hint = result['hint']
print(f" Hint: {hint['message']}")

# Test đoán đúng
result = game.make_guess(game.secret_number)
print(f"✅ Correct guess: Score {result.get('score', 0)}")

print("✅ GuessingGame tests passed!\n")

def test_stats_manager():
print("🧪 Testing StatsManager...")

stats = StatsManager()
print("✅ StatsManager created")

# Test update stats
stats.update_game_stats(won=True, difficulty_name="Dễ", score=500, attempts_used=3)
data = stats.get_stats()
print(f"✅ Stats: {data['total_games']} games, {data['wins']} wins")

print("✅ StatsManager tests passed!\n")

if __name__ == "__main__":
test_guessing_game()
test_stats_manager()
print("🎉 All tests passed! Game is ready!")

4.2. Chạy Game Thực Tế

Để chạy game, sử dụng lệnh:

python3 main.py

Demo gameplay thực tế:

🎯 CHÀO MỪNG ĐẾN VỚI GAME ĐOÁN SỐ HOÀN CHỈNH! 🎯

📋 MENU CHÍNH:
1. 🎮 Chơi Game Mới
2. 📊 Xem Thống Kê
3. ❓ Hướng Dẫn
4. 🚪 Thoát Game

Lựa chọn của bạn (1-4): 1

🎯 CHỌN MỨC ĐỘ KHÓ:
1. 😊 Dễ (1-10, tối đa 5 lần đoán)
2. 🤔 Trung bình (1-50, tối đa 7 lần đoán)
3. 😤 Khó (1-100, tối đa 10 lần đoán)
4. 🔥 Siêu khó (1-1000, tối đa 12 lần đoán)

Lựa chọn mức độ khó (1-4): 2

🎮 BẮT ĐẦU GAME - MỨC TRUNG BÌNH 🎮
Số bí mật nằm trong khoảng 1-50
Bạn có tối đa 7 lần đoán để tìm ra số đúng!

Lần đoán 1/7: 25
🤏 Hơi xa! Số cần tìm LỚN HỚN số bạn vừa đoán.
Còn lại: 6 lần đoán

Lần đoán 2/7: 35
👍 Gần rồi! Số cần tìm NHỎ HỚN số bạn vừa đoán.
Còn lại: 5 lần đoán

Lần đoán 3/7: 30
🎉 CHÚC MỪNG! Bạn đã đoán đúng số 30!
🏆 Độ chính xác: Tuyệt vời! 🎉
📈 Số lần đoán: 3/7
💯 Điểm số: 850 điểm

4.3. Common Issues và Solutions

Vấn đề 1: ModuleNotFoundError

# Solution: Thêm vào đầu main.py
import sys
import os
sys.path.append(os.path.dirname(os.path.abspath(__file__)))

Vấn đề 2: File stats.json không tạo được

# Solution: Kiểm tra quyền write và tạo thư mục
os.makedirs(os.path.dirname(self.stats_file), exist_ok=True)

Vấn đề 3: Input validation fails

# Solution: Sử dụng try-except đầy đủ
try:
user_input = int(input(prompt))
except ValueError:
print("❌ Vui lòng nhập một số hợp lệ!")
continue

4.4. Performance Optimization

Cải thiện Loading Time:

# Lazy import để tăng tốc khởi động
def _show_statistics(self):
if not hasattr(self, '_advanced_stats_loaded'):
# Load heavy computations chỉ khi cần
self._advanced_stats_loaded = True

Memory Management:

# Giới hạn lịch sử đoán để tiết kiệm memory
if len(self.guess_history) > 50: # Giữ tối đa 50 lần đoán
self.guess_history = self.guess_history[-25:]

Bước 5: Enhancements - Cải Tiến và Mở Rộng

Đây là phần thú vị nhất! Chúng ta sẽ thêm các tính năng nâng cao để game trở nên chuyên nghiệp hơn.

5.1. Thêm Sound Effects (Tùy chọn)

# sound_manager.py
"""
Quản lý âm thanh cho game
"""
import os

class SoundManager:
def __init__(self):
self.sounds_enabled = True

def play_sound(self, sound_type):
"""Phát âm thanh theo loại"""
if not self.sounds_enabled:
return

sound_map = {
'win': '🔔', # Có thể thay bằng pygame.mixer
'lose': '💀',
'hint': '💡',
'menu': '🎵'
}

# Đơn giản hóa với emoji thay vì âm thanh thực
print(f"{sound_map.get(sound_type, '🔊')}", end=' ')

5.2. Thêm Game Modes Mới

# Thêm vào config.py
GAME_MODES = {
'classic': {
'name': 'Kinh điển',
'description': 'Game đoán số thông thường'
},
'speedrun': {
'name': 'Đua tốc độ',
'description': 'Có thời gian giới hạn',
'time_limit': 60 # 60 giây
},
'reverse': {
'name': 'Ngược lại',
'description': 'Máy đoán số của bạn'
},
'multiplayer': {
'name': 'Nhiều người chơi',
'description': 'Thi đấu với bạn bè'
}
}

5.3. AI Opponent (Máy đoán số)

# ai_player.py
"""
AI Player cho reverse mode
"""
import random

class AIPlayer:
"""AI chơi game đoán số"""

def __init__(self, min_range, max_range):
self.min_range = min_range
self.max_range = max_range
self.possible_numbers = list(range(min_range, max_range + 1))

def make_guess(self):
"""AI đưa ra lần đoán"""
if len(self.possible_numbers) == 1:
return self.possible_numbers[0]

# Sử dụng binary search strategy
mid_index = len(self.possible_numbers) // 2
return self.possible_numbers[mid_index]

def update_from_hint(self, guess, is_higher):
"""Cập nhật dựa trên gợi ý"""
if is_higher:
# Loại bỏ tất cả số <= guess
self.possible_numbers = [n for n in self.possible_numbers if n > guess]
else:
# Loại bỏ tất cả số >= guess
self.possible_numbers = [n for n in self.possible_numbers if n < guess]

5.4. Leaderboard System

# leaderboard.py
"""
Hệ thống xếp hạng toàn cầu
"""
import json
from datetime import datetime

class Leaderboard:
"""Quản lý bảng xếp hạng"""

def __init__(self):
self.leaderboard_file = 'data/leaderboard.json'
self.scores = self.load_leaderboard()

def add_score(self, player_name, score, difficulty, attempts):
"""Thêm điểm vào bảng xếp hạng"""
entry = {
'player': player_name,
'score': score,
'difficulty': difficulty,
'attempts': attempts,
'date': datetime.now().isoformat()
}

self.scores.append(entry)

# Sắp xếp theo điểm cao nhất
self.scores.sort(key=lambda x: x['score'], reverse=True)

# Giữ top 100
self.scores = self.scores[:100]

self.save_leaderboard()

def get_top_scores(self, limit=10):
"""Lấy top điểm cao nhất"""
return self.scores[:limit]

def get_player_rank(self, player_name):
"""Tìm thứ hạng của người chơi"""
for i, entry in enumerate(self.scores, 1):
if entry['player'] == player_name:
return i
return None

5.5. Tính Năng Hint Thông Minh Nâng Cao

# Thêm vào GuessingGame class
def get_advanced_hint(self):
"""Gợi ý nâng cao dựa trên pattern recognition"""
if len(self.guess_history) < 3:
return self.get_smart_hint()

# Phân tích pattern đoán
recent_guesses = self.guess_history[-3:]
distances = [abs(g - self.secret_number) for g in recent_guesses]

# Kiểm tra xu hướng
if distances[0] > distances[1] > distances[2]:
return "🎯 Tuyệt vời! Bạn đang tiến gần đến đáp án!"
elif distances[0] < distances[1] < distances[2]:
return "⚠️ Bạn đang đi xa khỏi đáp án rồi!"
else:
# Phân tích khoảng
low = min(recent_guesses)
high = max(recent_guesses)

if self.secret_number < low:
return f"💡 Đáp án nhỏ hơn {low}. Thử số thấp hơn!"
elif self.secret_number > high:
return f"💡 Đáp án lớn hơn {high}. Thử số cao hơn!"
else:
return f"💡 Đáp án nằm trong khoảng {low}-{high}!"

5.6. Save/Load Game State

# Thêm vào GameManager
def save_game_state(self):
"""Lưu trạng thái game để tiếp tục sau"""
if not self.current_game:
return

state = {
'game_summary': self.current_game.get_game_summary(),
'timestamp': datetime.now().isoformat()
}

with open('data/saved_game.json', 'w') as f:
json.dump(state, f, indent=2)

print("💾 Đã lưu game!")

def load_game_state(self):
"""Khôi phục game đã lưu"""
try:
with open('data/saved_game.json', 'r') as f:
state = json.load(f)

# Recreate game từ saved state
game_data = state['game_summary']
# ... logic để khôi phục game

print("📂 Đã tải game thành công!")
return True
except FileNotFoundError:
return False

Source Code Hoàn Chỉnh

Tất cả source code đã được tối ưu hóa và test kỹ lưỡng. Các file chính:

📁 Cấu trúc project:

guessing_game/
├── main.py # Entry point
├── config.py # Cấu hình game
├── game_manager.py # Game manager chính
├── guessing_game.py # Core game logic
├── ui_manager.py # User interface
├── stats_manager.py # Quản lý thống kê
├── test_game.py # Test script
└── data/
├── stats.json # Thống kê người chơi
└── saved_game.json # Game state (nếu có)

🎮 Để chạy game:

# Clone hoặc tải về source code
# Mở terminal tại thư mục game
python3 main.py

Thử Thách Cho Bạn

Sau khi hoàn thành game cơ bản, hãy thử những thử thách này:

  • Challenge 1: Thêm chế độ "Đoán số thập phân" (1.0 - 10.0)
  • Challenge 2: Tạo tournament mode cho nhiều người chơi
  • Challenge 3: Implement machine learning để AI học cách đoán tối ưu
  • Challenge 4: Thêm các power-ups (extra hint, skip turn, etc.)
  • Challenge 5: Tạo web interface với Flask/Django
  • Challenge 6: Multiplayer online với WebSocket
  • Challenge 7: Mobile app với Kivy hoặc React Native bridge

Bài Tiếp Theo

🎯 Bài tiếp theo: Phát triển game Tic-Tac-toe (Cờ caro 3x3) - Hướng dẫn AI cơ bản

Trong bài tiếp theo, chúng ta sẽ:

  • Xây dựng game Tic-Tac-toe hoàn chỉnh
  • Học về 2D array và game board representation
  • Implement AI opponent với Minimax algorithm
  • Tạo strategy patterns và game theory cơ bản

Kết Luận

Chúc mừng! 🎉 Bạn đã hoàn thành việc xây dựng một Game Đoán Số hoàn chỉnh với đầy đủ tính năng:

Những gì bạn đã học được:

  • Game Logic Design: Thiết kế luồng chơi và state management
  • Object-Oriented Programming: Tổ chức code theo classes và modules
  • File I/O: Lưu/đọc dữ liệu JSON cho thống kê
  • Error Handling: Xử lý exception và input validation
  • User Experience: Thiết kế interface thân thiện với emoji và colors
  • Testing: Viết test cases và debug systematically
  • Code Organization: Best practices cho dự án Python

Key Programming Concepts:

  • Random number generation và seed control
  • Loops và conditionals trong game logic
  • Dictionary và list manipulation cho data structures
  • Function design với clear inputs/outputs
  • Class inheritance và composition patterns
  • Configuration management với separate config files

Game này không chỉ là một project đơn giản mà là foundation để bạn phát triển các game phức tạp hơn. Những kiến thức về game loop, state management, và user interaction sẽ áp dụng được cho mọi loại game khác.

Hãy tiếp tục thử nghiệm, mở rộng game, và quan trọng nhất là enjoy the coding journey! 🚀


💡 Pro Tip: Lưu source code này làm template cho các game project tương lai. Pattern và structure ở đây có thể tái sử dụng cho nhiều loại game khác!

Happy Coding! 👨‍💻👩‍💻

Loading comments...