Files
Hulumath-Web/backend/contest/management/commands/seed_contests.py
T
Jacky 6c9f475ba5
CI / test (pull_request) Successful in 3m58s
PR合并自动部署 / release-check (pull_request) Successful in 13s
PR合并自动部署 / deploy (pull_request) Successful in 16s
release: prepare Hulumath v1.2.0
2026-08-10 01:23:30 +08:00

232 lines
7.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import random
from django.core.management.base import BaseCommand
from contest.models import Contest, ContestQuestion, Question, QuestionVersion
def _generate_beginner():
"""入门:100以内加减乘除,约 400 题。"""
questions = []
ops = [
("+", lambda a, b: a + b),
("-", lambda a, b: a - b),
("×", lambda a, b: a * b),
("÷", lambda a, b: a // b if b != 0 and a % b == 0 else None),
]
idx = 0
for a in range(2, 100):
for b in range(2, min(a + 1, 100)):
for sym, fn in ops:
if idx >= 400:
return questions
if sym == "÷" and (b == 0 or a % b != 0):
continue
if sym == "-" and a < b:
continue
result = fn(a, b)
if result is None or result < 0 or result > 10000:
continue
slug = f"b-auto-{idx:04d}"
prompt = f"{a} {sym} {b}"
answer = str(result)
if (slug, prompt, answer) not in questions:
questions.append((slug, prompt, answer))
idx += 1
return questions[:400]
def _generate_standard():
"""标准:两位数乘除、大数减法、平方,约 400 题。"""
questions = []
idx = 0
# 两位数乘法
for a in range(11, 100):
for b in range(11, min(a + 1, 100)):
if idx >= 150:
break
questions.append((f"s-auto-mul-{idx:04d}", f"{a} × {b}", str(a * b)))
idx += 1
# 三位数除法
for a in range(100, 1000, 7):
for b in range(11, 50):
if idx >= 300:
break
if a % b == 0:
questions.append((f"s-auto-div-{idx:04d}", f"{a} ÷ {b}", str(a // b)))
idx += 1
# 大数减法
for a in range(1000, 10000, 137):
b = random.randint(100, a - 1)
if idx >= 350:
break
questions.append((f"s-auto-sub-{idx:04d}", f"{a} - {b}", str(a - b)))
idx += 1
# 平方
for a in range(11, 100):
if idx >= 400:
break
questions.append((f"s-auto-sq-{idx:04d}", f"{a}²", str(a * a)))
idx += 1
return questions[:400]
def _generate_advanced():
"""进阶:模运算、组合数、高次幂、开方、等差数列求和,约 200 题。"""
questions = []
idx = 0
# 模运算
for base in range(2, 20):
for exp in range(2, 13):
for mod in range(3, 20):
if idx >= 50:
break
result = pow(base, exp, mod)
questions.append((f"a-auto-mod-{idx:04d}", f"{base}^{exp} ÷ {mod} 的余数", str(result)))
idx += 1
# 组合数 C(n,k)
for n in range(5, 31):
for k in range(2, min(n // 2 + 1, 8)):
if idx >= 100:
break
from math import comb
questions.append((f"a-auto-comb-{idx:04d}", f"C({n},{k})", str(comb(n, k))))
idx += 1
# 高次幂
for base in range(2, 10):
for exp in range(3, 10):
if idx >= 130:
break
result = base ** exp
if result > 10**8:
continue
questions.append((f"a-auto-pow-{idx:04d}", f"{base}^{exp}", str(result)))
idx += 1
# 开方(完全平方数)
for n in range(10, 200):
if idx >= 160:
break
r = int(n**0.5)
if r * r == n:
questions.append((f"a-auto-sqrt-{idx:04d}", f"√{n}", str(r)))
idx += 1
# 等差数列求和 1..n
for n in range(10, 201, 5):
if idx >= 200:
break
result = n * (n + 1) // 2
questions.append((f"a-auto-sum-{idx:04d}", f"1 到 {n} 的整数和", str(result)))
idx += 1
# 一元一次方程,补足稳定的 200 题池
for solution in range(2, 60):
if idx >= 200:
break
coefficient = solution % 7 + 2
offset = solution % 11 + 1
total = coefficient * solution + offset
questions.append(
(
f"a-auto-linear-{idx:04d}",
f"{coefficient}x + {offset} = {total},求 x",
str(solution),
)
)
idx += 1
return questions[:200]
QUESTIONS = {
Question.Track.BEGINNER: _generate_beginner(),
Question.Track.STANDARD: _generate_standard(),
Question.Track.ADVANCED: _generate_advanced(),
}
class Command(BaseCommand):
help = "创建分层题库(入门/标准/进阶)和实时 1v1、每日赛、单人练习"
def handle(self, *args, **options):
versions = {}
for track, questions in QUESTIONS.items():
versions[track] = []
for slug, prompt, answer in questions:
question, _ = Question.objects.update_or_create(
slug=slug,
defaults={"track": track, "tags": ["口算"], "is_active": True},
)
version, _ = QuestionVersion.objects.update_or_create(
question=question,
version=1,
defaults={
"prompt": prompt,
"answer": answer,
"explanation": f"答案为 {answer}",
},
)
versions[track].append(version)
contest_specs = []
for track, label in (
(Question.Track.BEGINNER, "入门"),
(Question.Track.STANDARD, "标准"),
(Question.Track.ADVANCED, "进阶"),
):
contest_specs.extend(
[
(
f"realtime-{track}",
f"{label}实时 1v1",
Contest.Kind.REALTIME,
track,
60,
),
(
f"daily-{track}",
f"{label}今日挑战",
Contest.Kind.DAILY,
track,
180,
),
(
f"practice-{track}",
f"{label}单人闯关",
Contest.Kind.PRACTICE,
track,
300,
),
]
)
for slug, title, kind, track, duration in contest_specs:
contest, _ = Contest.objects.update_or_create(
slug=slug,
defaults={
"title": title,
"kind": kind,
"track": track,
"duration_seconds": duration,
"status": Contest.Status.PUBLISHED,
},
)
ContestQuestion.objects.filter(contest=contest).delete()
pool = list(versions[track])
random.shuffle(pool)
ContestQuestion.objects.bulk_create(
[
ContestQuestion(
contest=contest,
question_version=version,
order=index,
points=100,
)
for index, version in enumerate(pool, start=1)
]
)
total = sum(map(len, QUESTIONS.values()))
self.stdout.write(
self.style.SUCCESS(
f"已创建 {total} 道题(入门{len(QUESTIONS[Question.Track.BEGINNER])}、"
f"标准{len(QUESTIONS[Question.Track.STANDARD])}、"
f"进阶{len(QUESTIONS[Question.Track.ADVANCED])})和 {len(contest_specs)} 场比赛"
)
)