summaryrefslogtreecommitdiff
path: root/scripts/p66-target-report.fsx
diff options
context:
space:
mode:
authorSomhairle H. Marisol <[email protected]>2026-09-29 08:09:12 +0800
committerSomhairle H. Marisol <[email protected]>2026-09-29 08:09:12 +0800
commit247d0bc083dffc648f043176eabf9bd4036f19e7 (patch)
tree78c11e2f923c7b9322b64dc81ccae86fc5bbceea /scripts/p66-target-report.fsx
parentcf6672ee1d6cf7013472f3bd91e68990f23750e8 (diff)
downloadliving-village-247d0bc083dffc648f043176eabf9bd4036f19e7.tar.gz
p66: 每日任务目标具体化 TargetNpc/TargetTile 定点
- Kernel Occupation:TaskTargets + targetNpcOf/targetTileOf + dailyTaskOfWith/ refreshTodayWith/refreshStateWith(splitmix 同源定点,候选排序消除顺序依赖,禁随机) - 仅既有 satisfiesCompletion 会读目标的模板定点:帮工→TargetNpc、翻土→TargetTile; 其余 8 模板目标保持 None(不改完成语义) - Desktop WorldBootstrap:reachableTargets(世界 NPC + 样板菜地瓦片)+ occupationStateForWorld/refreshOccupationTodayWorld;Game 开局/跨日/证据路径切换 - 历史入口 dailyTaskOf/refreshState/occupationStateFor 保留为空候选通配包装(逐字节不变) - 红先 P66TaskTargetTests(8 例):同 seed 幂等、跨 seed 落在可达集合、候选顺序无关、 历史通配回归、帮工/翻土定点门控完成、跨日重定点、v3 round-trip + v2 基线 - scripts/p66-target-report.fsx + docs/evidence/p66-analysis.txt(PASS) - Desktop 328/328,Kernel 119/119;digest 953775FA…7F6C ×3 PASS;Sim.fs 未动
Diffstat (limited to 'scripts/p66-target-report.fsx')
-rw-r--r--scripts/p66-target-report.fsx87
1 files changed, 87 insertions, 0 deletions
diff --git a/scripts/p66-target-report.fsx b/scripts/p66-target-report.fsx
new file mode 100644
index 0000000..e360775
--- /dev/null
+++ b/scripts/p66-target-report.fsx
@@ -0,0 +1,87 @@
+// P66(每日任务目标具体化)证据报告:按 hash 在可达 NPC/瓦片内定点的命中数字 + 同 seed 复现断言。
+// 运行:dotnet fsi scripts/p66-target-report.fsx > docs/evidence/p66-analysis.txt
+// 前置:已 dotnet build LivingVillage.sln -c Release。
+#r "../src/LivingVillage.Kernel/bin/Release/net8.0/LivingVillage.Kernel.dll"
+#r "../src/LivingVillage.Desktop/bin/Release/net8.0/LivingVillage.Desktop.dll"
+
+open System
+open LivingVillage.Kernel
+open LivingVillage.Kernel.Sim
+open LivingVillage.Desktop
+
+let seed = 42UL
+let world = WorldBootstrap.initialWorldWithOccupation false seed Sim.npcCount None
+let reachable = WorldBootstrap.reachableTargets world
+
+let npcLabel (NpcId id) = string id
+let tileLabel (x, y) = sprintf "(%d,%d)" x y
+
+let stateAt (kind: Occupation.Kind) (day: int64) =
+ WorldBootstrap.occupationStateForWorld seed kind (day * Sim.ticksPerDay + 10L) world
+
+printfn "P66 每日任务目标具体化 — 证据报告"
+printfn "seed=%d npcs=%d reachable_npcs=%d reachable_tiles=%d (%s)"
+ (int seed) world.Npcs.Length reachable.Npcs.Length reachable.Tiles.Length
+ (reachable.Tiles |> List.map tileLabel |> String.concat ",")
+printfn ""
+
+let mutable totalTasks = 0
+let mutable npcTargeted = 0
+let mutable tileTargeted = 0
+let mutable wildcard = 0
+let mutable membershipOk = true
+let mutable reproduceOk = true
+let mutable concreteSamples = 0
+
+for kind in Occupation.all do
+ let mutable hits = 0
+ for day in 0L .. 59L do
+ let state = stateAt kind day
+ match state.Today with
+ | None -> ()
+ | Some task ->
+ totalTasks <- totalTasks + 1
+ let again = (stateAt kind day).Today.Value
+ if again <> task then reproduceOk <- false
+ match task.TemplateId with
+ | Occupation.HelpWork ->
+ npcTargeted <- npcTargeted + 1
+ hits <- hits + 1
+ match task.TargetNpc with
+ | Some npc when List.contains npc reachable.Npcs ->
+ concreteSamples <- concreteSamples + 1
+ if concreteSamples <= 12 then
+ printfn " %-4s day=%2d HelpWork TargetNpc=%s" (Occupation.saveToken kind) day (npcLabel npc)
+ | _ -> membershipOk <- false
+ | Occupation.TillSoil ->
+ tileTargeted <- tileTargeted + 1
+ hits <- hits + 1
+ match task.TargetTile with
+ | Some tile when List.contains tile reachable.Tiles ->
+ concreteSamples <- concreteSamples + 1
+ if concreteSamples <= 12 then
+ printfn " %-4s day=%2d TillSoil TargetTile=%s" (Occupation.saveToken kind) day (tileLabel tile)
+ | _ -> membershipOk <- false
+ | _ ->
+ wildcard <- wildcard + 1
+ if task.TargetNpc.IsSome || task.TargetTile.IsSome then membershipOk <- false
+ printfn " %-8s 60 天内定点模板命中=%d" (Occupation.nameOf kind) hits
+
+printfn ""
+printfn "统计(4 职业 × 60 天 = %d 条任务)" totalTasks
+printfn " 定点 NPC(帮工) = %d" npcTargeted
+printfn " 定点瓦片(翻土) = %d" tileTargeted
+printfn " 保持通配(其余) = %d" wildcard
+printfn " 目标 ∈ 可达集合 = %b" membershipOk
+printfn " 同输入两次逐字节一致 = %b" reproduceOk
+printfn ""
+
+// 空候选 → None(保留历史通配口径)。
+let legacyNone =
+ (Occupation.dailyTaskOf seed (Occupation.occupationSeedOf Occupation.Farmer) 3L Occupation.Farmer).Value
+printfn "历史口径 dailyTaskOf(空候选):TargetNpc=%b TargetTile=%b(应全 false)"
+ legacyNone.TargetNpc.IsSome legacyNone.TargetTile.IsSome
+
+let verdict = if membershipOk && reproduceOk then "PASS" else "FAIL"
+printfn ""
+printfn "verdict: %s" verdict