Python 经典项目实战:从零实现猜数字游戏(含完整源码解析)

从零开始用 Python 实现猜数字游戏:涵盖 random 模块、用户输入验证、条件判断、循环控制和异常处理。附带完整源码、逐步讲解和扩展挑战,是 Python 入门最佳练手项目。

猜数字游戏是编程入门的"Hello World+"——它足够简单,几分钟就能跑通;又足够丰富,可以练习条件判断、循环、异常处理和用户交互。跟着本文完成这个项目,你的 Python 基础会更扎实。


目录

  1. 项目目标与功能设计
  2. 核心知识点概览
  3. 基础版实现
  4. 增强版:加入难度选择
  5. 代码详解:逐行解析
  6. 常见错误与调试
  7. 扩展挑战
  8. 完整源码(可直接运行)
  9. 学习收获总结

1. 项目目标与功能设计

基本功能

  • 程序随机生成一个 1~100 之间的整数
  • 玩家每次输入一个猜测数字
  • 程序提示"太大了"或"太小了"
  • 猜中后显示恭喜信息和猜测次数
  • 支持"再玩一局"

扩展功能(增强版)

  • 难度选择:简单(150, 10次)、普通(1100, 7次)、困难(1~200, 5次)
  • 输入验证:非数字输入友好提示,不计入次数
  • 作弊模式:输入 hint 可获得范围提示
  • 排行榜:记录最少猜测次数

2. 核心知识点概览

在这个项目中,你将实践以下 Python 核心概念:

知识点在猜数字中的应用
random.randint()生成随机目标数字
input()获取用户输入
int()字符串转整数
if / elif / else比较猜测与目标数字
while 循环允许玩家反复猜测
try / except处理非数字输入的异常
函数定义 def代码模块化组织
字符串格式化 f-string友好的输出信息

3. 基础版实现

import random

def guess_number_game():
    """基础版猜数字游戏"""
    secret = random.randint(1, 100)
    attempts = 0
    max_attempts = 7
    
    print("=" * 40)
    print("🎮 欢迎来到猜数字游戏!")
    print("我已经想好了一个 1 到 100 之间的数字。")
    print(f"你有 {max_attempts} 次机会猜中它。")
    print("=" * 40)
    
    while attempts < max_attempts:
        attempts += 1
        
        # 获取用户输入
        guess_str = input(f"\n{attempts}/{max_attempts} 次猜测,请输入数字:")
        
        # 验证输入
        try:
            guess = int(guess_str)
        except ValueError:
            print("⚠️ 请输入有效的整数!")
            attempts -= 1   # 不计入次数
            continue
        
        # 检查范围
        if guess < 1 or guess > 100:
            print("⚠️ 数字必须在 1 到 100 之间!")
            attempts -= 1
            continue
        
        # 判断结果
        if guess == secret:
            print(f"\n🎉 恭喜你!猜对了!")
            print(f"🎯 目标数字就是 {secret}。")
            print(f"🏆 你用了 {attempts} 次就猜中了!")
            return True
        elif guess < secret:
            print("📈 太小了!再大一点。")
        else:
            print("📉 太大了!再小一点。")
        
        # 提示剩余次数
        remaining = max_attempts - attempts
        if remaining > 0:
            print(f"💡 还剩 {remaining} 次机会。")
    
    # 次数用完
    print(f"\n😢 很遗憾,次数用完了。")
    print(f"🎯 正确答案是 {secret}。")
    return False

if __name__ == "__main__":
    while True:
        guess_number_game()
        play_again = input("\n是否再玩一局?(y/n): ").strip().lower()
        if play_again != 'y':
            print("👋 感谢游玩,再见!")
            break

运行效果

========================================
🎮 欢迎来到猜数字游戏!
我已经想好了一个 1 到 100 之间的数字。
你有 7 次机会猜中它。
========================================

第 1/7 次猜测,请输入数字:50
📈 太小了!再大一点。
💡 还剩 6 次机会。

第 2/7 次猜测,请输入数字:75
📉 太大了!再小一点。
💡 还剩 5 次机会。

第 3/7 次猜测,请输入数字:62
📈 太小了!再大一点。
💡 还剩 4 次机会。

第 4/7 次猜测,请输入数字:68
🎉 恭喜你!猜对了!
🎯 目标数字就是 68。
🏆 你用了 4 次就猜中了!

是否再玩一局?(y/n): y
...

4. 增强版:加入难度选择

import random
import json
import os
from pathlib import Path

SAVE_FILE = Path.home() / ".guess_number_scores.json"

class GuessNumberGame:
    """增强版猜数字游戏"""
    
    DIFFICULTIES = {
        "easy":   {"range": 50,  "max_attempts": 10, "name": "简单"},
        "normal": {"range": 100, "max_attempts": 7,  "name": "普通"},
        "hard":   {"range": 200, "max_attempts": 5,  "name": "困难"},
    }
    
    def __init__(self):
        self.scores = self.load_scores()
    
    def load_scores(self):
        """加载历史最高分"""
        if SAVE_FILE.exists():
            return json.loads(SAVE_FILE.read_text(encoding="utf-8"))
        return {}
    
    def save_scores(self):
        """保存最高分"""
        SAVE_FILE.write_text(json.dumps(self.scores, ensure_ascii=False, indent=2), encoding="utf-8")
    
    def select_difficulty(self):
        """选择难度"""
        print("\n请选择难度:")
        print("  1. 简单 (1~50, 10次机会)")
        print("  2. 普通 (1~100, 7次机会)")
        print("  3. 困难 (1~200, 5次机会)")
        
        while True:
            choice = input("输入 1/2/3: ").strip()
            mapping = {"1": "easy", "2": "normal", "3": "hard"}
            if choice in mapping:
                return mapping[choice]
            print("⚠️ 请输入 1、2 或 3")
    
    def get_hint(self, secret, min_guess, max_guess):
        """获取范围提示"""
        range_size = max_guess - min_guess
        if range_size <= 10:
            return f"非常接近了!范围 {min_guess} ~ {max_guess}"
        return f"当前范围:{min_guess} ~ {max_guess}"
    
    def play_round(self):
        """进行一局游戏"""
        difficulty = self.select_difficulty()
        config = self.DIFFICULTIES[difficulty]
        
        max_range = config["range"]
        max_attempts = config["max_attempts"]
        secret = random.randint(1, max_range)
        
        # 记录当前搜索范围(用于 hint)
        min_bound, max_bound = 1, max_range
        
        print(f"\n{'='*50}")
        print(f"🎮 难度:{config['name']} | 范围:1 ~ {max_range} | 机会:{max_attempts}")
        print(f"{'='*50}")
        
        for attempt in range(1, max_attempts + 1):
            guess_str = input(f"\n{attempt}/{max_attempts} 次,输入数字(或 hint 提示):").strip().lower()
            
            # hint 作弊模式
            if guess_str == "hint":
                print(f"💡 {self.get_hint(secret, min_bound, max_bound)}")
                attempt -= 1
                continue
            
            # 输入验证
            try:
                guess = int(guess_str)
            except ValueError:
                print("⚠️ 请输入有效的整数!")
                attempt -= 1
                continue
            
            # 范围检查
            if guess < 1 or guess > max_range:
                print(f"⚠️ 数字必须在 1 到 {max_range} 之间!")
                attempt -= 1
                continue
            
            # 更新搜索范围
            if guess < secret and guess > min_bound:
                min_bound = guess
            elif guess > secret and guess < max_bound:
                max_bound = guess
            
            # 判断
            if guess == secret:
                self.handle_win(difficulty, attempt, secret)
                return True
            elif guess < secret:
                print("📈 太小了!")
            else:
                print("📉 太大了!")
            
            remaining = max_attempts - attempt
            if remaining > 0:
                print(f"💡 还剩 {remaining} 次机会")
        
        # 失败
        print(f"\n😢 次数用尽!正确答案是 {secret}")
        return False
    
    def handle_win(self, difficulty, attempts, secret):
        """处理胜利"""
        print(f"\n🎉 恭喜你!猜对了!")
        print(f"🎯 数字就是 {secret}")
        print(f"🏆 难度:{self.DIFFICULTIES[difficulty]['name']} | 用了 {attempts} 次")
        
        # 更新最高分
        key = f"best_{difficulty}"
        prev_best = self.scores.get(key)
        
        if prev_best is None or attempts < prev_best:
            self.scores[key] = attempts
            self.save_scores()
            print("✨ 新纪录!")
        else:
            print(f"📊 历史最佳:{prev_best} 次")
    
    def show_stats(self):
        """显示统计"""
        if not self.scores:
            print("\n暂无记录。")
            return
        
        print("\n📈 个人最佳记录:")
        for key, value in self.scores.items():
            diff_name = key.replace("best_", "")
            print(f"  {self.DIFFICULTIES[diff_name]['name']}模式: {value} 次")
    
    def run(self):
        """主循环"""
        print("="*50)
        print("🎮 Python 猜数字游戏")
        print("="*50)
        
        while True:
            print("\n1. 开始游戏")
            print("2. 查看记录")
            print("3. 退出")
            
            choice = input("\n选择: ").strip()
            
            if choice == "1":
                self.play_round()
            elif choice == "2":
                self.show_stats()
            elif choice == "3":
                print("👋 再见!")
                break
            else:
                print("⚠️ 无效选择")

if __name__ == "__main__":
    game = GuessNumberGame()
    game.run()

5. 代码详解:逐行解析

5.1 random.randint() 详解

import random

# randint(a, b) 生成 [a, b] 闭区间的整数
secret = random.randint(1, 100)   # 可能是 1, 50, 100... 任意值

# 替代方案:
# randrange(1, 101) —— 半开区间 [1, 101),等价于 randint(1, 100)
# random.choice(range(1, 101)) —— 从序列中随机选择
# secrets.randbelow(100) + 1 —— 密码学安全随机(游戏不需要)

5.2 input() 与类型转换

# input() 始终返回字符串
user_input = input("输入:")
print(type(user_input))   # <class 'str'>

# 需要转为整数
try:
    number = int(user_input)
except ValueError:
    print("不是有效的数字!")

# 更好的做法:封装验证函数
def get_valid_number(prompt, min_val, max_val):
    """获取范围内的有效整数"""
    while True:
        try:
            value = int(input(prompt))
            if min_val <= value <= max_val:
                return value
            print(f"请输入 {min_val}{max_val} 之间的数字")
        except ValueError:
            print("请输入有效的整数")

5.3 while 循环与条件判断

# 基础循环结构
attempts = 0
max_attempts = 7

while attempts < max_attempts:
    attempts += 1
    # ... 获取猜测 ...
    
    if guess == secret:
        print("赢了!")
        break       # 提前退出循环
    elif guess < secret:
        print("太小了")
    else:
        print("太大了")
else:
    # while 正常结束(没有 break)时执行
    print(f"次数用尽,答案是 {secret}")

5.4 文件持久化(JSON)

import json
from pathlib import Path

# 保存
scores = {"easy": 3, "normal": 5}
Path("scores.json").write_text(json.dumps(scores), encoding="utf-8")

# 加载
if Path("scores.json").exists():
    scores = json.loads(Path("scores.json").read_text(encoding="utf-8"))

6. 常见错误与调试

错误 1:TypeError: ‘<’ not supported between ‘str’ and ‘int’

# ❌ 错误:忘了转换 input 的结果
guess = input("输入:")
if guess < secret:    # TypeError!

# ✅ 正确
guess = int(input("输入:"))

错误 2:无限循环

# ❌ 错误:attempts 永远不会增加
attempts = 0
while attempts < 7:
    if guess != secret:
        print("错了")   # 没有 attempts += 1

# ✅ 正确
while attempts < 7:
    attempts += 1    # 确保计数器更新

错误 3:ValueError 未被捕获

# ❌ 错误:int() 转换在外层
num = int(input())
try:
    if num < 1:
        print("太小")
except ValueError:    # 不会捕获到!int() 在上面已经抛出了

# ✅ 正确
try:
    num = int(input())
except ValueError:
    print("请输入数字")

7. 扩展挑战

完成基础版本后,尝试以下进阶挑战:

挑战 1:二分查找提示

让程序在每次猜测后提示"你现在的最优策略是猜 X",基于二分查找算法。

挑战 2:GUI 版本

使用 tkinter 制作图形界面:

import tkinter as tk
from tkinter import messagebox

class GuessNumberGUI:
    def __init__(self):
        self.root = tk.Tk()
        self.root.title("猜数字游戏")
        self.secret = random.randint(1, 100)
        self.attempts = 0
        
        tk.Label(self.root, text="输入你的猜测 (1-100):").pack()
        self.entry = tk.Entry(self.root)
        self.entry.pack()
        
        tk.Button(self.root, text="猜测", command=self.check_guess).pack()
        self.result_label = tk.Label(self.root, text="")
        self.result_label.pack()
    
    def check_guess(self):
        try:
            guess = int(self.entry.get())
            # ... 判断逻辑 ...
        except ValueError:
            messagebox.showerror("错误", "请输入数字")
    
    def run(self):
        self.root.mainloop()

挑战 3:网络对战版

使用 Flask/FastAPI 制作 Web 版本,支持多人排行榜。参考 Python Web 框架指南

挑战 4:AI 对手

写一个 AI 玩家,让它用二分查找策略自动玩游戏,并统计平均需要几次:

def ai_player(min_val, max_val, secret):
    """AI 用二分查找猜数字"""
    attempts = 0
    low, high = min_val, max_val
    
    while True:
        attempts += 1
        guess = (low + high) // 2
        if guess == secret:
            return attempts
        elif guess < secret:
            low = guess + 1
        else:
            high = guess - 1

# 测试:1000 次游戏
results = [ai_player(1, 100, random.randint(1, 100)) for _ in range(1000)]
print(f"平均次数: {sum(results)/len(results):.2f}")    # ≈ 5.5 次
print(f"最多次数: {max(results)}")                      # ≤ 7 次

8. 完整源码(可直接运行)

[见上方"增强版"代码块]

保存为 guess_number.py,然后运行:

python3 guess_number.py

9. 学习收获总结

完成这个项目后,你应该能:

技能掌握程度
random 模块使用⭐⭐⭐⭐⭐
用户输入处理⭐⭐⭐⭐⭐
条件判断(if/elif/else)⭐⭐⭐⭐⭐
循环控制(while/for)⭐⭐⭐⭐⭐
异常处理(try/except)⭐⭐⭐⭐⭐
函数定义与组织⭐⭐⭐⭐⭐
文件读写(JSON序列化)⭐⭐⭐⭐
面向对象基础(class)⭐⭐⭐⭐

延伸阅读


💡 最佳学习方法:不要只是复制粘贴代码。先理解每行代码的作用,然后尝试修改规则(比如范围改成 1~1000,或减少猜测次数),看看会发生什么。动手改代码,才是最好的学习。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「python」更多文章

  1. Python 高级异步编程:Trio 结构化并发与 AnyIO 兼容层
  2. Python 数据工程与 ETL 管道实战
  3. Python 元编程与动态特性深度解析