feat(icon): 轨迹时序指纹修复 —— 1ms 图标 4/8→7/8=0,errorCode 语义与轨迹对拍收口
- 真因:dt 分布与真机严重不符(旧 50% 落 0~16ms,真机 79% 落 13~20ms)+ 点数 随机 57~65(真机 6/6 恒 61)。按 6 条真机轨迹重拟合 _sample_dt,新增 _fit_points 钉死 61 点(补齐用 [0,0,dt] 驻留点,不能复制位移点——净位移会被放大)。 - A/B:1ms 图标 0×4→0×7(前 7 连 0);残余偶发 12 为连发频控形态,低频使用即可。 - errorCode 语义标定:50=答案被否(容差实测 25~40px,我们点偏仅 2~3px), 12=包级拒答;判点位只看 50/0。 - 顺带修 build_plain:轨迹落末槽(k=37)时 chunk2 恒补前导逗号 → 非法 JSON。 - 新增 scripts/verify/send_campaign.py(批量真发+落图+统计)与 judge_icons.py (提示目标 vs 点击点并排判读);账本 #64~#74 + 专文 ICON2408.md。 复验:selftest 全绿;判读图 24/24 点位全对;mliev 图标 9/10=0。
This commit is contained in:
@@ -132,14 +132,74 @@ def test_offline_generate_and_roundtrip() -> None:
|
||||
len(plain["chunk2"]), len(plain["sd"]))
|
||||
|
||||
|
||||
def test_build_plain_trajectory_first() -> None:
|
||||
"""轨迹就在第 0 槽时,chunk1 里没有任何槽 —— 不能再补分隔逗号。
|
||||
|
||||
实测触发场景:某些构建的清单里轨迹模块排在第 0 位,那样会拼出 `{"cd":[,`
|
||||
—— 非法 JSON,服务端返回 errorCode=12。
|
||||
"""
|
||||
import json as _json
|
||||
|
||||
from tcaptcha.telemetry.values import build_plain, trajectory_index
|
||||
|
||||
rows = [{"name": "trajectory"}, {"name": "platform"}, {"name": "constant_98k"}]
|
||||
assert trajectory_index(rows) == 0
|
||||
cd = [[[1, 1, 12]], "MacIntel", "98k"]
|
||||
p = build_plain(cd, rows)
|
||||
assert p["chunk1"] == '{"cd":[', repr(p["chunk1"])
|
||||
assert _json.loads(p["chunk1"] + p["trajectory"] + p["chunk2"] + p["sd"])["cd"] == cd
|
||||
|
||||
# 轨迹在中间时,chunk1 仍必须以 ',' 结尾(真机实测口径)
|
||||
rows2 = [{"name": "platform"}, {"name": "trajectory"}, {"name": "constant_98k"}]
|
||||
p2 = build_plain(["MacIntel", [[1, 1, 12]], "98k"], rows2)
|
||||
assert p2["chunk1"].endswith(",") and p2["chunk2"].startswith(",")
|
||||
|
||||
|
||||
def test_build_plain_trajectory_last() -> None:
|
||||
"""轨迹就在**最后 1 槽**时,chunk2 里没有任何槽 —— 不能再补前导逗号。
|
||||
|
||||
实测触发场景(2026-10-12):38 槽里有构建把轨迹排在 k=37(最后),
|
||||
旧写法恒拼 `,],` -> 整段变成 `{"cd":[...,],` 非法 JSON。
|
||||
本题同时钉住「切分器必须接受 `],` 这种对称边界」。
|
||||
"""
|
||||
import base64 as _b64
|
||||
import json as _json
|
||||
|
||||
from tcaptcha.telemetry.crypto import DEFAULT_KEY
|
||||
from tcaptcha.telemetry.values import build_plain, trajectory_index
|
||||
|
||||
rows = [{"name": "platform"}, {"name": "constant_98k"}, {"name": "trajectory"}]
|
||||
assert trajectory_index(rows) == 2
|
||||
cd = ["MacIntel", "98k", [[1, 1, 12]]]
|
||||
p = build_plain(cd, rows)
|
||||
assert p["chunk2"] == "],", repr(p["chunk2"])
|
||||
whole = p["chunk1"] + p["trajectory"] + p["chunk2"] + p["sd"]
|
||||
assert _json.loads(whole)["cd"] == cd
|
||||
|
||||
# 旧写法(恒补前导逗号)拼出来的确实是非法 JSON —— 这正是当年 12 的成因之一
|
||||
bad = p["chunk1"] + p["trajectory"] + "," + p["chunk2"] + p["sd"]
|
||||
try:
|
||||
_json.loads(bad)
|
||||
raise AssertionError("旧写法应当拼出非法 JSON")
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
# 走完整加密 -> 解密 -> 切分:切分器要吃下 `],` 这种边界
|
||||
collect = build_collect(p["chunk1"], p["trajectory"], p["chunk2"], p["sd"], DEFAULT_KEY)
|
||||
dec = xtea_decrypt_bytes(_b64.b64decode(collect), DEFAULT_KEY).decode("latin1").rstrip("\x00")
|
||||
assert split_collect_segments(dec) == (p["chunk1"], p["trajectory"], p["chunk2"], p["sd"])
|
||||
|
||||
|
||||
def main() -> int:
|
||||
selfcheck()
|
||||
test_split_single_space_pad()
|
||||
test_split_ignores_space_inside_string()
|
||||
test_build_collect_roundtrip_single_space_pad()
|
||||
test_offline_generate_and_roundtrip()
|
||||
test_build_plain_trajectory_first()
|
||||
test_build_plain_trajectory_last()
|
||||
print("✅ crypto 纯算法自检通过 (XTEA 往返 + encrypt_string 一致性"
|
||||
" + 四段切分回归 + 纯离线闭环)")
|
||||
" + 四段切分回归 + 纯离线闭环 + 轨迹首/末槽边界)")
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user