161 lines
15 KiB
Python
161 lines
15 KiB
Python
from pathlib import Path
|
||
import json,html,csv,hashlib
|
||
root=Path(r'G:\项目\NumberSwipe-Variants');out=root/'design/demo-50';plan=json.loads((out/'demo-design.json').read_text(encoding='utf-8'));levels=plan['levels'];names={'big':'大格/异形','walls':'墙','direction':'方向','alternation':'双色','parity':'奇偶','rhythm-door':'步进门'}
|
||
def mech(l):return '+'.join(names[f] for f in l['features']) or '数字基础'
|
||
intro='''# NumberSwipe 50关DEMO设计与难度曲线
|
||
|
||
版本:2026-10-14 v1。面向首次接触的新玩家,目标是验证趣味性、规则理解与章节节奏。当前交付是关卡制作任务与目标曲线,没有具体坐标、作者解或50关可玩包。
|
||
|
||
## 研究如何适配本项目
|
||
|
||
研究正文来源:G:/下载/NumberSwipe_Research_H5.html。源文件SHA-256记录在demo-design.json;报告中的外部Zip统计未在本项目复算。采用有效分叉、区域风险、错误发现延迟、目标跨度、逻辑依赖和视觉操作负担的分层思路。公式权重、章节8/9/10节拍及报告首通指标均是待测假设。
|
||
|
||
本项目数字表示绝对访问步号,不能将Zip的编号升序直接迁移。每个拼块一步、每格恰好一次、共边移动、完整有序解唯一。按节点数量使用5–19格,不迁移研究报告6×6/7×7的面积规模。普通方格固定起点下目标格奇偶由天然二分图固定,奇偶优先放在大格或无双色的六边盘。双色严格交替时不叠加奇偶。方向通道仅用于单位方格整盘。步进门空盘关、起点也切换,出发前判门位、撤销恢复。单位方格门题主要判断通过方向;通过大格或含三角邻接的六边图才强调不同路线的到达状态。
|
||
|
||
保留当前五个原型及已认可的40关作为参考,不恢复相对步差、先后链、窗口或开关格。当前Variants没有奇偶判定,双色启用仍依赖mode名称:组合主线落地需独立增加features开关、奇偶判定/提示/求解器/必要性检查,并限制图示与实际规则一致。本轮不修改这些运行代码,不调整手机体验。
|
||
|
||
## 节拍选择
|
||
|
||
默认A:每10关第8关峰值、第9关释放、第10关收束。新机制采用演示→练习→变化→组合→巩固的五关单元;不要求每个10关章都新增规则。
|
||
|
||
B为对照:同一套题,把各章第8关移到第10关,9/10关前移为8/9。A/B拥有完全相同关卡ID和内容,只改变顺序。首次制作先校验A,再与B比较。不是随机动态调难。
|
||
|
||
前两关完成操作教学;大格与墙在1–10关;方向与双色在11–20关;奇偶与门在21–30关;六边迁移在31–40关;最后10关只深化组合。保留两次起点未知(37、38),不把起点未知作为所有关卡的固定难度旋钮。
|
||
|
||
目标分0–100是策划标尺,不是搜索节点数、通过概率或玩家实测难度。各章目标峰值40/46/50/58/72,最后一关54分收束,不在50关耗尽难度上限。释放题也必须有可解释的选择,避免机械蛇形或连续闭眼画。
|
||
|
||
## 五章目标
|
||
|
||
| 关卡 | 阶段 | 新内容 | 峰值关/目标分 |
|
||
|---|---|---|---|
|
||
| 1–10 | 覆盖、大格与墙 | 大格一步、永久墙 | 8/40 |
|
||
| 11–20 | 方向与双色 | 六种明确通道、换颜色 | 18/46 |
|
||
| 21–30 | 奇偶与步进门 | 步号奇偶、每步切换门 | 28/50 |
|
||
| 31–40 | 六边迁移与起点推理 | 六向邻接、未知起点 | 38/58 |
|
||
| 41–50 | 组合应用与收束 | 不新增规则 | 48/72 |
|
||
|
||
## 最新项目研究补充与桥接验证
|
||
|
||
已纳入原项目docs/一笔画竞品调研与关卡编排方案.md、2026-10-14补充的《一笔画深度调研-制作与难度证据.md》及45关结构审计JSON。该审计针对原项目26数字关+19六边关;本次另行读取Variants的40关,二者不能混为同一题库的验收。
|
||
|
||
技能依赖:覆盖/纠错→大格计步→枢纽出口→异形共边;基本共边→横竖端口→明确拐角→方向联合;精确步号与实际拼块图→单奇偶→双奇偶;步号与撤销→起步切换→通过方向→大格到达状态;熟悉墙→六向共边→旧规则迁移。未知起点依赖端点和步号推理,先小盘专项。
|
||
|
||
先制作并测试6道桥接样题,均计入50关:
|
||
|
||
|设计号|验证任务|控制变量|
|
||
|---|---|---|
|
||
|05|异形共边|只增加L形读盘,不叠奇偶或未知起点|
|
||
|13|拐角端口|只增加一种明确拐角,能解释反向通过|
|
||
|18|双色与墙联合|先掌握16/17,再测试异色出口保留|
|
||
|21|单奇偶|按节点计步,条件必要,不靠天然属性|
|
||
|24|双奇偶与区域|先掌握21–23,每条条件各自有效|
|
||
|28|大格与门位|先掌握26/27,检查实际步数关系|
|
||
|
||
教学测试按课程顺序。难度校准先让玩家学会对应规则,再交错代表题,避免把学习熟练或记忆同盘答案误判为题更容易。四层去重分别检查外观几何、真实邻接、约束配置、关键解法;几何相同且推理不同可以少量保留,四层等价仅限明确强化用途。
|
||
|
||
## 逐关制作任务
|
||
|
||
表中格数、约束预算与曲线为候选制作目标;盘面通过验证与试玩之前都不能标记为完成。格式是设计任务数据,不是游戏可直接读取的关卡JSON。
|
||
'''
|
||
md=intro
|
||
for ch in plan['chapters']:
|
||
md+=f"\n### 第{ch['id']}章 · {ch['title']}\n\n{ch['focus']}。\n\n|关|标题/功能|格数|机制|目标分|核心任务|\n|---:|---|---:|---|---:|---|\n"
|
||
for l in levels[(ch['id']-1)*10:ch['id']*10]:md+=f"|{l['displayOrder']}|{l['title']} · {l['role']}|{l['nodeCountTarget']}|{mech(l)}|{l['targetDifficulty']}|{l['teachingGoal']}|\n"
|
||
for l in levels[(ch['id']-1)*10:ch['id']*10]:
|
||
md+=f"\n#### {l['displayOrder']:02d} · {l['title']}\n\n- 盘面:{l['layoutIntent']}。\n- 条件预算:{l['constraintsBudget']}。\n- 顿悟:{l['teachingGoal']}。\n- 诱惑路线:{l['temptingRouteIntent']}。\n- 排除依据:{l['rejectionReason']}。\n- 目标:分值{l['targetDifficultyBand'][0]}–{l['targetDifficultyBand'][1]};有效关键选择{l['targetDecisionCount'][0]}–{l['targetDecisionCount'][1]}处;最长依赖约{l['targetDependencyDepth']}层;思考时间P50候选目标{l['thinkingSecondsP50Target'][0]}–{l['thinkingSecondsP50Target'][1]}秒。\n"
|
||
if l['sourceCandidates']:md+='- 原型参考:'+', '.join(l['sourceCandidates'])+';仅作构造参考,不能视为可直接复用。\n'
|
||
md+='''
|
||
## 51–100关及正式版衔接
|
||
|
||
50关验证后,先依据真人数据修改题与关序,再扩展51–100。优先用区域拓扑、空洞、对称/破对称、枢纽先后和线索位置开发新题,不要求增加规则或继续扩大盘面。每个新章先安排熟悉规则的热身,再形成局部峰谷;不规定第51关必须难于第50关。
|
||
|
||
优质DEMO题作为内容资产复用,正式首发前统一筛选重排。过难题可移入挑战包。上线后新增章节与主题包,旧内容只进行有依据的局部修正。levelId首次分配后保留;显示顺序由章节manifest决定。规则实质变化增加contentRevision,完全替换题目分配新ID。保存通关进度和分析记录时绑定ID与修订,不能只绑定第几关。
|
||
|
||
## 交付与状态
|
||
|
||
- index.html:离线规划页,筛选逐关任务、切换A/B曲线、查看详细设计。
|
||
- demo-design.json:50关任务、稳定ID、章节与A/B关序,所有任务标记design-only。
|
||
- difficulty-curve.csv:目标曲线与A/B位置;实测难度列留空。
|
||
- VALIDATION_PLAN.md:难度定义、机器人策略、机制必要性与真人测试标准。
|
||
- project-snapshot.json:现有40关的结构快照,只是几何指标,不是人类难度。
|
||
- plan-check.json:设计表一致性检查,不是50关唯一解验收。
|
||
|
||
本轮没有制作50个坐标盘面,没有给出真实作者解,没有完成真人难度或手机测试。
|
||
'''
|
||
(out/'DEMO_DESIGN.md').write_text(md,encoding='utf-8')
|
||
(out/'VALIDATION_PLAN.md').write_text('''# 制作与难度验证规划
|
||
|
||
## 1. 三类难度分开保存
|
||
|
||
- targetDifficulty:策划目标分,当前有值。
|
||
- staticFeatures / simulatedFeatures:盘面完成后采集,当前为空。
|
||
- humanDifficulty:真人数据校准后填写,当前为空。
|
||
|
||
报告权重0.25B+0.25C+0.20R+0.15S+0.10U+0.05V是候选,不直接作为排序真值;本轮不实现未经校准的综合评分器。所有指标保存规则/模型版本。不同机制的搜索节点数不可未经校准直接比较。
|
||
|
||
## 2. 指标的操作定义
|
||
|
||
|指标|测量办法|限制|
|
||
|---|---|---|
|
||
|有效决策数B|在固定策略机器人遇到至少两种未被其已掌握规则排除的移动时,记录一次决策|不是原始邻接度>=3的节点数量;不能用完整求解器提前认出唯一解再计数|
|
||
|强制比例F|固定策略机器人独立通过局部移动、固定步号及已掌握规则确定的追加数/总追加数|不能把唯一解中的所有步当成强制步;不能将度2比例视为F|
|
||
|封闭风险C|每个诱惑移动是否会切断剩余潜在图;记录局部可见或需跨区推断|门的潜在连通图保留未来可开的边,双色和永久墙按各自静态限制处理|
|
||
|错误发现延迟R|按约定诱惑策略,从首次偏离到出现可解释矛盾之间的追加数|分别记录自动冲突、局部死路、区域矛盾;不是撤销次数|
|
||
|数字跨度S|固定数字按步号排序的相邻差和最后尾段;同时保存最长未确定段|不使用空间距离;奇偶不等同固定数字|
|
||
|推理依赖U|人工说明中为确认关键选择需要连续使用的前提层数|先由策划标注,真人口述核对,不由机制数量替代|
|
||
|视觉操作V|路径转向、标记密度、边界歧义、触点与跨块连线可读性|与逻辑难度分开,手机优化留到玩法验证后|
|
||
|
||
## 3. 机器人方案(待实施)
|
||
|
||
A局部策略:固定数字与基本操作约束优先,倾向较近线索、少出口及边缘;不访问完整答案,记录诱惑选择与回退。策略、候选排序和随机种子固定,便于复现。
|
||
|
||
B规则策略:在A基础上使用端点/剩余区域连通/通道配对/拼块计步/奇偶/颜色/门位推理;逐项记录所用规则与推理次数。只评估定义范围内的推理,不能宣称等价人类。
|
||
|
||
C完整搜索:确认完整有序解唯一,解除约束生成反例,记录搜索规模与耗时。C的节点数只作计算诊断,不等于真人难度。
|
||
|
||
## 4. 盘面发布门槛
|
||
|
||
每关结构合法、完整解唯一;无1时搜所有起点;每条奇偶、每个方向通道、每堵墙、门、全局交替要求逐项解除后至少两解;辅助数字逐项删减,教学保留需写理由。约束预算是候选目标,不能为了凑墙数或标记数保留冗余。
|
||
|
||
主线支持features显式启用,必须由规则、操作、求解、提示共用判定。禁止把当前mode名称判双色的逻辑直接复制到组合主线。奇偶原始格不与数字同格;不可依靠天然方格或双色奇偶做无效装饰。方向只适用单位方格整盘;大格和六边不继承lr/du等端口判断。
|
||
|
||
步进门最多一道,空盘关,起点也切换;出发前判位、落格后切换;回退和重开恢复。单位方格固定起点的天然奇偶不因绕路改变,门题不写成任意绕一圈就能补拍;需要可变到达步号关系时用大格或三角邻接六边盘。
|
||
|
||
组合必须有明确的跨机制推理,并记录解除每一项后的完整反例;唯一解通过但主要靠枚举、长段机械填满的题仍须重做。
|
||
|
||
## 5. 玩家测试(待实施)
|
||
|
||
先找3–5名新玩家定性测试规则解释与明显枯燥点,再组织更大样本进行50关分会话测试。每关20–30人可暴露明显问题,不用于精确估计低频退出或A/B显著性;A/B需要根据实际方差和所需效应另算样本量。
|
||
|
||
记录实际开始、首次落格、首次偏离、冲突被发现、回退、重开、提示、完成、离开、下一关启动和下一会话返回。仅本地导出记录,不在本轮加入自动网络上传。
|
||
|
||
第一次尝试:从首次起手到首次重开、提示或结束;分别记录是否含回退。首次无提示且无回退完成、允许回退的无提示完成、最终完成分开统计,避免成功率口径混乱。
|
||
|
||
思考时间:从盘面可读到首次落格,加上绘制途中思考停顿的候选口径,后台时间剔除;另存开始到完成总耗时。暂停阈值需在原型事件采集前固定,当前设计时间目标不与总耗时混用。
|
||
|
||
单关统计P50/P75/P90时间、错误发现延迟、回退、提示、开始后退出、完成后下一关启动。退出分为卡关、无聊、正常结束会话、操作/显示问题,结合短口述或选择项;不能把退出全部解释成难度。
|
||
|
||
先以实际测量修正排序,不用本方案的秒数直接判定成败。教学关目标是理解规则;挑战关目标是可解释推理;释放关目标是减少负担并保持兴趣。重点检查8/18/28/38/48峰值,以及其后两关是否让玩家愿意继续。
|
||
|
||
A/B关序保持完全相同题库:A第8峰值、9/10释放;B把峰值移至章末。用户首次参与时固定分组,不在同一玩家每章切换,避免学习效应。比较完成后下一关启动和后续会话返回,同时检查新规则理解、提示与卡关退出;未达样本前只作定性观察,不宣布最优。
|
||
|
||
## 6. 数据契约(规划,不是运行关卡)
|
||
|
||
设计任务记录levelId、designKey、教学任务、候选几何/机制、目标分/目标带、目标关键选择与依赖、制作状态。具体cells/walls/doors及作者解只有制作完成才填写到独立关卡包。
|
||
|
||
关序manifest维护ID数组,A/B互为同集合排列。已发题重排不清空已通关进度;实质规则改变有revision;替换为新谜题分配新ID。
|
||
|
||
当前交付不承诺平台、网络、广告或周期上线日期。先做玩法曲线验证,再安排手机体验和release内容生产。
|
||
''',encoding='utf-8')
|
||
# Minimal static SVG: target scores only, no inference that it is a measured curve.
|
||
def curve(ids):
|
||
lookup={l['levelId']:l for l in levels};seq=[lookup[i]['targetDifficulty'] for i in ids];W,H=1060,320;L,T=50,20;pw,ph=990,245
|
||
parts=[f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {W} {H}" role="img" aria-label="50关策划目标难度曲线,非实测"><rect width="1060" height="320" fill="#101e33"/>']
|
||
for v in (0,20,40,60,80,100):y=T+ph*(1-v/100);parts+=[f'<line x1="{L}" y1="{y}" x2="1040" y2="{y}" stroke="#30435d"/><text x="40" y="{y+4}" fill="#becddd" font-size="12" text-anchor="end">{v}</text>']
|
||
for pos in (1,10,20,30,40,50):x=L+pw*(pos-1)/49;parts+=[f'<text x="{x}" y="290" fill="#becddd" font-size="13" text-anchor="middle">{pos}</text>']
|
||
for c in range(5):x=L+pw*(c*10)/49;parts+=[f'<line x1="{x}" y1="20" x2="{x}" y2="265" stroke="#37516f" stroke-dasharray="4 5"/>']
|
||
points=' '.join(f'{L+pw*i/49:.2f},{T+ph*(1-v/100):.2f}' for i,v in enumerate(seq));parts+=[f'<polyline points="{points}" fill="none" stroke="#73d6c3" stroke-width="3"/>']
|
||
parts+=['<text x="1040" y="310" fill="#becddd" font-size="12" text-anchor="end">目标分 · 非实测 · 关序位置</text></svg>'];return ''.join(parts)
|
||
(out/'curve-a.svg').write_text(curve(plan['orders']['A']['levelIds']),encoding='utf-8');(out/'curve-b.svg').write_text(curve(plan['orders']['B']['levelIds']),encoding='utf-8')
|
||
print('Markdown specification, validation plan and A/B SVG curves written.')
|