Bootstrap

python实现答题游戏

有这样一个需求:使用python实现一个游戏,一共有10个问题,依次回答每个问题,每个用户可以输入问题的答案,但是互相不能看到,有一个管理员可以看到所有人的答案,并且当所有人都填写完成后可以公布所有人的答案,并且打分。在回答的下面有一个得分表,每打完一轮问题的得分则实时计算总得分和排名。限制一个IP只能登录一个用户。

下面给出一个基于 Flask 的简化示例,演示如何用 Python 实现这样一个游戏的核心功能,主要包括:

  1. 10 个问题的答题流程。
  2. 用户只能看到自己的答案,管理员可以看到所有人的答案。
  3. 当所有人都填写完成后,管理员可以公布所有人的答案并进行打分。
  4. 显示得分表,每轮更新后实时计算总得分和排名
  5. 限制同一个 IP 地址只能登录一个用户。

为演示方便,以下示例仅做最小可行的流程说明,使用字典存储用户和答案信息;在实际生产环境中,可以结合数据库、认证机制、前端模板等进行更完善的开发。


主要功能说明

  1. 注册/登录

    • 用户首次进入时,需要填写用户名进行注册并登录。
    • 同一个 IP 地址只允许注册一个用户,后续从同一 IP 再访问则直接视为该用户登录或禁止二次注册。
  2. 答题

    • 一共 10 个问题,这里演示时以简单的问卷形式呈现。
    • 每个用户在提交完所有答案后,才能等待管理员公布结果。
  3. 管理员查看与公布答案

    • 管理员可以查看所有用户答案。
    • 管理员可进行打分,并将所有用户答案与分数公示。
    • 每完成一轮题目即可查看实时总得分并进行排名。
  4. 得分统计与排名

    • 每一轮提交答案后,管理员打分并把分数汇总到用户总分中。
    • 根据所有用户的总分动态计算排名并显示。

代码示例

下面的示例使用 Flask 进行演示,提供了若干路由(URL)以完成相应功能。请先安装 Flask:

pip install flask

然后创建一个脚本文件(如 quiz_game.py),内容如下:

from flask import Flask, request, session, redirect, url_for, render_template_string
import functools

app = Flask(__name__)
app.secret_key = "your_secret_key"  # 用于 session 加密

# 记录哪些 IP 已经注册过用户
ip_to_user = {}

# 存储用户信息,包括用户名、答案、分数等
# 结构示例:
# users_data = {
#   'alice': {
#       'answers': ["ans1", "ans2", ...],  # 当前轮次的答案
#       'scores': [5, ...],               # 历史每一轮的得分
#       'total_score': 5
#   },
#   'bob': {...},
#   ...
# }
users_data = {}

# 定义题目,这里示例 10 个问题
QUESTIONS = [
    "问题1:你最喜欢的颜色是什么?",
    "问题2:你最喜欢的动物是什么?",
    "问题3:你的故乡在哪里?",
    "问题4:你最喜欢吃什么?",
    "问题5:你最喜欢哪个季节?",
    "问题6:你最喜欢做的运动是什么?",
    "问题7:你最喜欢的电影类型是什么?",
    "问题8:假期里你最想去哪里旅行?",
    "问题9:你希望学习哪项新技能?",
    "问题10:你最喜欢的音乐类型是什么?",
]

# 标记当前答题是否开放,如果轮次结束后可以由管理员统一切换
answer_open = True
# 标记这一轮是否已经公布答案
answers_revealed = False

# ========== 帮助函数 ==========

def login_required(func):
    """需要用户已登录,否则跳转到登录页面。"""
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        if 'username' not in session:
            return redirect(url_for('login'))
        return func(*args, **kwargs)
    return wrapper

def admin_required(func):
    """需要管理员权限,否则跳转到登录。此示例简单地将用户名为 'admin' 视为管理员。"""
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        if session.get('username') != 'admin':
            return "只有管理员能访问此页面。", 403
        return func(*args, **kwargs)
    return wrapper

def get_rankings():
    """
    根据 total_score 得到排行榜信息,返回 [(username, total_score), ...] 从高到低。
    """
    ranking = sorted(users_data.items(), key=lambda x: x[1].get('total_score', 0), reverse=True)
    # 转换成列表 [(用户名, 总分), ...]
    return [(user, info['total_score']) for user, info in ranking]

# ========== 路由视图 ==========

@app.route('/')
def index():
    """
    主页,如果已登录则跳转到答题页面;否则跳转到登录。
    """
    if 'username' in session:
        return redirect(url_for('quiz'))
    else:
        return redirect(url_for('login'))

@app.route('/login', methods=['GET', 'POST'])
def login():
    """
    用户登录/注册入口。
    同一个IP只能注册一个用户,若该IP已注册过,则自动成为该用户或禁止再次注册。
    """
    client_ip = request.remote_addr

    if request.method == 'POST':
        username = request.form.get('username', '').strip()
        if not username:
            return "用户名不能为空", 400
        
        # 如果该IP已经注册过其他用户,则禁止重复注册
        if client_ip in ip_to_user and ip_to_user[client_ip] != username:
            return f"该IP({client_ip})已经注册过用户 {ip_to_user[client_ip]},无法重复注册。", 403
        
        # 如果该IP没有注册用户或者与现有用户名匹配,则继续
        ip_to_user[client_ip] = username
        
        # 如果用户第一次注册,初始化信息
        if username not in users_data:
            users_data[username] = {
                "answers": [""] * len(QUESTIONS),
                "scores": [],
                "total_score": 0
            }
        session['username'] = username
        
        return redirect(url_for('quiz'))

    # GET 请求,返回一个简单的登录表单
    return render_template_string("""
    <h2>用户登录/注册</h2>
    <form method="post">
        用户名:<input type="text" name="username"><br>
        <input type="submit" value="登录">
    </form>
    """)

@app.route('/quiz', methods=['GET', 'POST'])
@login_required
def quiz():
    """
    答题页面。提交后保存答案。这里简单演示一次性提交 10 个问题。
    """
    global answer_open
    global answers_revealed

    username = session['username']

    if request.method == 'POST':
        if not answer_open:
            return "当前轮次答题已关闭,等待管理员开启下一轮或公布结果。", 403
        
        # 接收 10 个问题的答案
        new_answers = []
        for i in range(len(QUESTIONS)):
            ans = request.form.get(f'question_{i}', '').strip()
            new_answers.append(ans)
        
        # 保存新的答案
        users_data[username]['answers'] = new_answers
        
        return "提交成功!等待管理员公布结果。"

    # GET 请求时渲染一个简单的表单
    user_answers = users_data[username].get('answers', [""]*len(QUESTIONS))
    
    html_form = """<h2>答题页面</h2>
    {% if answer_open %}
        <form method="post">
            {% for i, question in enumerate(QUESTIONS) %}
                <div>
                    <label>{{ question }}</label><br>
                    <input type="text" name="question_{{ i }}" value="{{ user_answers[i] }}">
                </div>
                <br>
            {% endfor %}
            <input type="submit" value="提交答案">
        </form>
    {% else %}
        <p>答题已关闭,请等待管理员开启下一轮或查看最终结果。</p>
    {% endif %}
    """
    return render_template_string(
        html_form,
        QUESTIONS=QUESTIONS,
        user_answers=user_answers,
        answer_open=answer_open
    )

@app.route('/admin_view')
@admin_required
def admin_view():
    """
    管理员查看所有用户答案的页面(仅管理员可访问)。
    """
    html = "<h2>管理员查看所有答案</h2>"
    for user, info in users_data.items():
        html += f"<h3>用户:{user}</h3>"
        answers = info.get('answers', [])
        for i, ans in enumerate(answers):
            html += f"问题{i+1}: {ans}<br>"
    return html

@app.route('/admin_score', methods=['GET', 'POST'])
@admin_required
def admin_score():
    """
    管理员对本轮答案进行打分并公布。一次性给所有用户打分,然后系统更新总分、清空当前答案。
    """
    global answer_open
    global answers_revealed
    
    # GET:显示一个打分的页面
    if request.method == 'GET':
        html = """
        <h2>管理员打分</h2>
        <form method="post">
        """
        for user in users_data:
            html += f"<label>给用户 {user} 的本轮答案打分:</label>"
            html += f"<input type='number' name='score_{user}' value='0' min='0' max='100'><br><br>"
        html += "<input type='submit' value='提交分数'>"
        html += "</form>"
        return html
    
    # POST:接收管理员给每个用户的分数,更新 total_score
    for user in users_data:
        score_str = request.form.get(f'score_{user}', '0')
        try:
            score = int(score_str)
        except ValueError:
            score = 0
        
        # 更新每个用户的分数信息
        users_data[user]['scores'].append(score)
        users_data[user]['total_score'] += score

    answers_revealed = True
    answer_open = False  # 关闭当前轮次答题,等待管理员手动开启下一轮(如果有的话)

    return redirect(url_for('results'))

@app.route('/results')
@login_required
def results():
    """
    公布所有用户本轮答案与分数(如果管理员已打分的话)。
    并显示当前排行榜。
    """
    if not session.get('username'):
        return redirect(url_for('login'))

    if not users_data:
        return "暂无答案"

    html = "<h2>当前轮次结果</h2>"
    # 只有在管理员打完分之后才公布
    if not all(users_data[user]['scores'] for user in users_data):
        html += "<p>管理员尚未打分,无法显示分数。</p>"
    else:
        # 显示最新一轮的分数(即 scores 列表中的最后一项)
        for user, info in users_data.items():
            latest_score = info['scores'][-1] if info['scores'] else 0
            answers = info.get('answers', [])
            html += f"<h3>用户:{user}</h3>"
            for i, ans in enumerate(answers):
                html += f"问题{i+1}: {ans}<br>"
            html += f"<strong>本轮得分: {latest_score}</strong><br>"
    
    # 显示排行榜
    html += "<h2>排行榜</h2>"
    ranking = get_rankings()
    rank_str = ""
    for idx, (user, total_score) in enumerate(ranking, start=1):
        rank_str += f"{idx}. {user}{total_score}分<br>"
    html += rank_str

    return html

@app.route('/admin_open_new_round')
@admin_required
def admin_open_new_round():
    """
    管理员开启新一轮答题:清空当前答案,answer_open = True, 并 answers_revealed = False。
    """
    global answer_open, answers_revealed

    # 把当前答案列表清空(如果希望保留可以改成其他逻辑)
    for user, info in users_data.items():
        info['answers'] = [""] * len(QUESTIONS)

    answer_open = True
    answers_revealed = False

    return "新一轮答题已开启!"

if __name__ == '__main__':
    # 启动Flask服务
    app.run(debug=True, host='0.0.0.0', port=5000)

功能说明

  1. ip_to_user 字典用于记录 IP → 用户名 的映射,确保同一 IP 无法注册多个账号。
  2. users_data 用于保存所有用户的状态,包括:
    • 当前轮的答案 answers
    • 每一轮的得分列表 scores
    • 总分 total_score
  3. answer_open 控制当前轮次是否允许答题,answers_revealed 标记本轮答案是否已公布。
  4. 提供了如下 路由
    • /login:用户登录/注册页面。
    • /quiz:用户答题页面(一次性答完 10 道题)。
    • /admin_view:管理员查看所有答案(仅管理员可访问)。
    • /admin_score:管理员对所有用户打分并公布结果(仅管理员可访问)。
    • /results:已打分后的结果页面,显示所有用户本轮答案、分数以及排行榜。
    • /admin_open_new_round:管理员开启下一轮答题,清空当前答案,重新开放答题。

使用方式

  1. 安装依赖并运行:
    pip install flask
    python quiz_game.py
    
  2. 浏览器访问 http://<服务器IP>:5000,若未登录则自动跳转到 /login
  3. 管理员可以使用特殊用户名(如 admin)登录,以访问管理员路由:
    • http://<服务器IP>:5000/admin_view 查看所有人的答案
    • http://<服务器IP>:5000/admin_score 给大家打分
    • http://<服务器IP>:5000/admin_open_new_round 开启下一轮
  4. 普通用户只能查看自己的答案并进行答题,分数公布后可查看总榜。

后续完善建议

  • 数据库:将用户数据、IP 映射等存入数据库(如 SQLite、MySQL、PostgreSQL),确保数据持久化。
  • 用户认证:对登录机制进行加固,比如密码验证、单点登录、令牌校验等,防止恶意用户冒名顶替。
  • 前端界面:可使用 Flask 的模板功能(或前端框架)美化页面,并提供更加友好易用的交互。
  • 并发/部署:考虑使用 uWSGI、Gunicorn 等部署在正式服务器上,提高并发能力。

这个示例主要演示了核心逻辑流程和数据结构,大家可以根据实际需求进行拓展和改进。祝你开发顺利!

;