218 lines
7.3 KiB
Python
218 lines
7.3 KiB
Python
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
|
||
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)
|
||
selected = pool[:8]
|
||
ContestQuestion.objects.bulk_create(
|
||
[
|
||
ContestQuestion(
|
||
contest=contest,
|
||
question_version=version,
|
||
order=index,
|
||
points=100,
|
||
)
|
||
for index, version in enumerate(selected, 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)} 场比赛"
|
||
)
|
||
)
|